Compare commits
8 Commits
docker_tests
...
v5.6.2
| Author | SHA1 | Date | |
|---|---|---|---|
| cdae1f0d06 | |||
| 96468dd931 | |||
| a8949649a8 | |||
| a3fc7e9990 | |||
| c749842eab | |||
| 503497dcc7 | |||
| 516a822cd8 | |||
| 75eb8992a9 |
@@ -2112,6 +2112,7 @@
|
|||||||
* postgresql: set collation order explicity when creating database to C.UTF-8 (for confluence)
|
* postgresql: set collation order explicity when creating database to C.UTF-8 (for confluence)
|
||||||
* rsync: fix error while goes missing when syncing
|
* rsync: fix error while goes missing when syncing
|
||||||
* Pre-select app domain by default in the redirection drop down
|
* Pre-select app domain by default in the redirection drop down
|
||||||
|
* robots: preseve leading and trailing whitespaces/newlines
|
||||||
|
|
||||||
[6.0.0]
|
[6.0.0]
|
||||||
* Focal support
|
* Focal support
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ fi
|
|||||||
|
|
||||||
# Only --help works with mismatched ubuntu
|
# Only --help works with mismatched ubuntu
|
||||||
ubuntu_version=$(lsb_release -rs)
|
ubuntu_version=$(lsb_release -rs)
|
||||||
if [[ "${ubuntu_version}" != "16.04" && "${ubuntu_version}" != "18.04" && "${ubuntu_version}" != "20.04" ]]; then
|
if [[ "${ubuntu_version}" != "16.04" && "${ubuntu_version}" != "18.04" ]]; then
|
||||||
echo "Cloudron requires Ubuntu 16.04, 18.04 or 20.04" > /dev/stderr
|
echo "Cloudron requires Ubuntu 16.04 or 18.04" > /dev/stderr
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -423,14 +423,14 @@ function updateWithConstraints(id, app, constraints, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ('location' in app && 'domain' in app) { // must be updated together as they are unique together
|
if ('location' in app && 'domain' in app) { // must be updated together as they are unique together
|
||||||
queries.push({ query: 'UPDATE subdomains SET subdomain = ?, domain = ? WHERE appId = ? AND type = ?', args: [ app.location, app.domain, id, exports.SUBDOMAIN_TYPE_PRIMARY ]});
|
queries.push({ query: 'DELETE FROM subdomains WHERE appId = ?', args: [ id ]}); // all locations of an app must be updated together
|
||||||
}
|
queries.push({ query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, app.domain, app.location, exports.SUBDOMAIN_TYPE_PRIMARY ]});
|
||||||
|
|
||||||
if ('alternateDomains' in app) {
|
if ('alternateDomains' in app) {
|
||||||
queries.push({ query: 'DELETE FROM subdomains WHERE appId = ? AND type = ?', args: [ id, exports.SUBDOMAIN_TYPE_REDIRECT ]});
|
app.alternateDomains.forEach(function (d) {
|
||||||
app.alternateDomains.forEach(function (d) {
|
queries.push({ query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, exports.SUBDOMAIN_TYPE_REDIRECT ]});
|
||||||
queries.push({ query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, exports.SUBDOMAIN_TYPE_REDIRECT ]});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fields = [ ], values = [ ];
|
var fields = [ ], values = [ ];
|
||||||
|
|||||||
+15
-4
@@ -41,6 +41,7 @@ exports = module.exports = {
|
|||||||
backup: backup,
|
backup: backup,
|
||||||
listBackups: listBackups,
|
listBackups: listBackups,
|
||||||
|
|
||||||
|
getLocalLogfilePaths: getLocalLogfilePaths,
|
||||||
getLogs: getLogs,
|
getLogs: getLogs,
|
||||||
|
|
||||||
start: start,
|
start: start,
|
||||||
@@ -1365,6 +1366,19 @@ function update(app, data, auditSource, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLocalLogfilePaths(app) {
|
||||||
|
assert.strictEqual(typeof app, 'object');
|
||||||
|
|
||||||
|
const appId = app.id;
|
||||||
|
|
||||||
|
var filePaths = [];
|
||||||
|
filePaths.push(path.join(paths.LOG_DIR, appId, 'apptask.log'));
|
||||||
|
filePaths.push(path.join(paths.LOG_DIR, appId, 'app.log'));
|
||||||
|
if (app.manifest.addons && app.manifest.addons.redis) filePaths.push(path.join(paths.LOG_DIR, `redis-${appId}/app.log`));
|
||||||
|
|
||||||
|
return filePaths;
|
||||||
|
}
|
||||||
|
|
||||||
function getLogs(app, options, callback) {
|
function getLogs(app, options, callback) {
|
||||||
assert.strictEqual(typeof app, 'object');
|
assert.strictEqual(typeof app, 'object');
|
||||||
assert(options && typeof options === 'object');
|
assert(options && typeof options === 'object');
|
||||||
@@ -1384,11 +1398,8 @@ function getLogs(app, options, callback) {
|
|||||||
|
|
||||||
var args = [ '--lines=' + lines ];
|
var args = [ '--lines=' + lines ];
|
||||||
if (follow) args.push('--follow', '--retry', '--quiet'); // same as -F. to make it work if file doesn't exist, --quiet to not output file headers, which are no logs
|
if (follow) args.push('--follow', '--retry', '--quiet'); // same as -F. to make it work if file doesn't exist, --quiet to not output file headers, which are no logs
|
||||||
args.push(path.join(paths.LOG_DIR, appId, 'apptask.log'));
|
|
||||||
args.push(path.join(paths.LOG_DIR, appId, 'app.log'));
|
|
||||||
if (app.manifest.addons && app.manifest.addons.redis) args.push(path.join(paths.LOG_DIR, `redis-${appId}/app.log`));
|
|
||||||
|
|
||||||
var cp = spawn('/usr/bin/tail', args);
|
var cp = spawn('/usr/bin/tail', args.concat(getLocalLogfilePaths(app)));
|
||||||
|
|
||||||
var transformStream = split(function mapper(line) {
|
var transformStream = split(function mapper(line) {
|
||||||
if (format !== 'json') return line + '\n';
|
if (format !== 'json') return line + '\n';
|
||||||
|
|||||||
+42
-11
@@ -32,11 +32,13 @@ var apps = require('./apps.js'),
|
|||||||
constants = require('./constants.js'),
|
constants = require('./constants.js'),
|
||||||
debug = require('debug')('box:appstore'),
|
debug = require('debug')('box:appstore'),
|
||||||
eventlog = require('./eventlog.js'),
|
eventlog = require('./eventlog.js'),
|
||||||
|
path = require('path'),
|
||||||
paths = require('./paths.js'),
|
paths = require('./paths.js'),
|
||||||
safe = require('safetydance'),
|
safe = require('safetydance'),
|
||||||
semver = require('semver'),
|
semver = require('semver'),
|
||||||
settings = require('./settings.js'),
|
settings = require('./settings.js'),
|
||||||
superagent = require('superagent'),
|
superagent = require('superagent'),
|
||||||
|
support = require('./support.js'),
|
||||||
util = require('util');
|
util = require('util');
|
||||||
|
|
||||||
// These are the default options and will be adjusted once a subscription state is obtained
|
// These are the default options and will be adjusted once a subscription state is obtained
|
||||||
@@ -406,26 +408,55 @@ function createTicket(info, auditSource, callback) {
|
|||||||
apps.get(info.appId, callback);
|
apps.get(info.appId, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function enableSshIfNeeded(callback) {
|
||||||
|
if (!info.enableSshSupport) return callback();
|
||||||
|
|
||||||
|
support.enableRemoteSupport(true, auditSource, function (error) {
|
||||||
|
// ensure we can at least get the ticket through
|
||||||
|
if (error) console.error('Unable to enable SSH support.', error);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getCloudronToken(function (error, token) {
|
getCloudronToken(function (error, token) {
|
||||||
if (error) return callback(error);
|
if (error) return callback(error);
|
||||||
|
|
||||||
collectAppInfoIfNeeded(function (error, result) {
|
enableSshIfNeeded(function (error) {
|
||||||
if (error) return callback(error);
|
if (error) return callback(error);
|
||||||
if (result) info.app = result;
|
|
||||||
|
|
||||||
let url = settings.apiServerOrigin() + '/api/v1/ticket';
|
collectAppInfoIfNeeded(function (error, app) {
|
||||||
|
if (error) return callback(error);
|
||||||
|
if (app) info.app = app;
|
||||||
|
|
||||||
info.supportEmail = constants.SUPPORT_EMAIL; // destination address for tickets
|
info.supportEmail = constants.SUPPORT_EMAIL; // destination address for tickets
|
||||||
|
|
||||||
superagent.post(url).query({ accessToken: token }).send(info).timeout(10 * 1000).end(function (error, result) {
|
var req = superagent.post(`${settings.apiServerOrigin()}/api/v1/ticket`)
|
||||||
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
|
.query({ accessToken: token })
|
||||||
if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
|
.timeout(20 * 1000);
|
||||||
if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message));
|
|
||||||
if (result.statusCode !== 201) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
|
|
||||||
|
|
||||||
eventlog.add(eventlog.ACTION_SUPPORT_TICKET, auditSource, info);
|
// either send as JSON through body or as multipart, depending on attachments
|
||||||
|
if (info.app) {
|
||||||
|
req.field('infoJSON', JSON.stringify(info));
|
||||||
|
|
||||||
callback(null, { message: `An email for sent to ${constants.SUPPORT_EMAIL}. We will get back shortly!` });
|
apps.getLocalLogfilePaths(info.app).forEach(function (filePath) {
|
||||||
|
var logs = safe.child_process.execSync(`tail --lines=1000 ${filePath}`);
|
||||||
|
if (logs) req.attach(path.basename(filePath), logs, path.basename(filePath));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
req.send(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.end(function (error, result) {
|
||||||
|
if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message));
|
||||||
|
if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS));
|
||||||
|
if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message));
|
||||||
|
if (result.statusCode !== 201) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
|
||||||
|
|
||||||
|
eventlog.add(eventlog.ACTION_SUPPORT_TICKET, auditSource, info);
|
||||||
|
|
||||||
|
callback(null, { message: `An email for sent to ${constants.SUPPORT_EMAIL}. We will get back shortly!` });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+7
-5
@@ -78,15 +78,17 @@ function onActivated(callback) {
|
|||||||
// 1. mail bounces can now be sent to the cloudron owner
|
// 1. mail bounces can now be sent to the cloudron owner
|
||||||
// 2. the restore code path can run without sudo (since mail/ is non-root)
|
// 2. the restore code path can run without sudo (since mail/ is non-root)
|
||||||
async.series([
|
async.series([
|
||||||
(done) => reverseProxy.writeDefaultConfig({ activated :true }, done), // update IP based nginx config once user is created
|
|
||||||
platform.start,
|
platform.start,
|
||||||
cron.startJobs,
|
cron.startJobs,
|
||||||
function checkBackupConfiguration(callback) {
|
function checkBackupConfiguration(done) {
|
||||||
backups.checkConfiguration(function (error, message) {
|
backups.checkConfiguration(function (error, message) {
|
||||||
if (error) return callback(error);
|
if (error) return done(error);
|
||||||
notifications.alert(notifications.ALERT_BACKUP_CONFIG, 'Backup configuration is unsafe', message, callback);
|
notifications.alert(notifications.ALERT_BACKUP_CONFIG, 'Backup configuration is unsafe', message, done);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
// disable responding to api calls via IP to not leak domain info. this is carefully placed as the last item, so it buys
|
||||||
|
// the UI some time to query the dashboard domain in the restore code path
|
||||||
|
(done) => setTimeout(() => reverseProxy.writeDefaultConfig({ activated :true }, done), 30000)
|
||||||
], callback);
|
], callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ function createTicket(req, res, next) {
|
|||||||
if (typeof req.body.description !== 'string' || !req.body.description) return next(new HttpError(400, 'description must be string'));
|
if (typeof req.body.description !== 'string' || !req.body.description) return next(new HttpError(400, 'description must be string'));
|
||||||
if (req.body.appId && typeof req.body.appId !== 'string') return next(new HttpError(400, 'appId must be string'));
|
if (req.body.appId && typeof req.body.appId !== 'string') return next(new HttpError(400, 'appId must be string'));
|
||||||
if (req.body.altEmail && typeof req.body.altEmail !== 'string') return next(new HttpError(400, 'altEmail must be string'));
|
if (req.body.altEmail && typeof req.body.altEmail !== 'string') return next(new HttpError(400, 'altEmail must be string'));
|
||||||
|
if (req.body.enableSshSupport && typeof req.body.enableSshSupport !== 'boolean') return next(new HttpError(400, 'enableSshSupport must be a boolean'));
|
||||||
|
|
||||||
settings.getSupportConfig(function (error, supportConfig) {
|
settings.getSupportConfig(function (error, supportConfig) {
|
||||||
if (error) return next(new HttpError(503, `Error getting support config: ${error.message}`));
|
if (error) return next(new HttpError(503, `Error getting support config: ${error.message}`));
|
||||||
|
|||||||
Reference in New Issue
Block a user