Break down the configure route

This commit is contained in:
Girish Ramakrishnan
2019-09-08 16:57:08 -07:00
parent 45a2d3745c
commit 801ca7eda1
6 changed files with 1765 additions and 839 deletions
+371
View File
@@ -17,6 +17,21 @@ exports = module.exports = {
configure: configure,
uninstall: uninstall,
setAccessRestriction: setAccessRestriction,
setLabel: setLabel,
setIcon: setIcon,
setTags: setTags,
setMemoryLimit: setMemoryLimit,
setAutomaticBackup: setAutomaticBackup,
setAutomaticUpdate: setAutomaticUpdate,
setRobotsTxt: setRobotsTxt,
setCertificate: setCertificate,
setDebugMode: setDebugMode,
setEnvironment: setEnvironment,
setMailbox: setMailbox,
setLocation: setLocation,
setDataDir: setDataDir,
restore: restore,
clone: clone,
@@ -58,6 +73,9 @@ exports = module.exports = {
ISTATE_PENDING_INSTALL: 'pending_install', // installs and fresh reinstalls
ISTATE_PENDING_CLONE: 'pending_clone', // clone
ISTATE_PENDING_CONFIGURE: 'pending_configure', // config (location, port) changes and on infra update
ISTATE_PENDING_CREATE_CONTAINER: 'pending_create_container',
ISTATE_PENDING_LOCATION_CHANGE: 'pending_location_change',
ISTATE_PENDING_DATA_DIR_MIGRATION: 'pending_data_dir_migration',
ISTATE_PENDING_UNINSTALL: 'pending_uninstall', // uninstallation
ISTATE_PENDING_RESTORE: 'pending_restore', // restore to previous backup or on upgrade
ISTATE_PENDING_UPDATE: 'pending_update', // update from installed state preserving data
@@ -803,6 +821,359 @@ function install(data, user, auditSource, callback) {
});
}
function setAccessRestriction(appId, accessRestriction, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof accessRestriction, 'object');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
error = validateAccessRestriction(accessRestriction);
if (error) return callback(error);
appdb.update(appId, { accessRestriction: accessRestriction }, function (error) {
if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, 'No such app'));
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error));
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, accessRestriction: accessRestriction });
callback();
});
});
}
function setLabel(appId, label, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof label, 'string');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
error = validateLabel(label);
if (error) return callback(error);
appdb.update(appId, { label: label }, function (error) {
if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, 'No such app'));
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error));
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, label: label });
callback();
});
});
}
function setTags(appId, tags, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert(Array.isArray(tags));
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
error = validateTags(tags);
if (error) return callback(error);
appdb.update(appId, { tags: tags }, function (error) {
if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, 'No such app'));
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error));
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, tags: tags });
callback();
});
});
}
function setIcon(appId, icon, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert(icon === null || typeof icon === 'string');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
if (icon) {
if (!validator.isBase64(icon)) return callback(new AppsError(AppsError.BAD_FIELD, 'icon is not base64', { field: 'icon' }));
if (!safe.fs.writeFileSync(path.join(paths.APP_ICONS_DIR, appId + '.user.png'), Buffer.from(icon, 'base64'))) {
return callback(new AppsError(AppsError.INTERNAL_ERROR, 'Error saving icon:' + safe.error.message));
}
} else {
safe.fs.unlinkSync(path.join(paths.APP_ICONS_DIR, appId + '.user.png'));
}
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, icon: icon });
callback();
});
}
function setMemoryLimit(appId, memoryLimit, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof memoryLimit, 'number');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
error = validateMemoryLimit(app.manifest, memoryLimit);
if (error) return callback(error);
scheduleTask(appId, {}, { installationState: exports.ISTATE_PENDING_CREATE_CONTAINER, memoryLimit: memoryLimit }, function (error, result) {
if (error) return callback(error);
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, memoryLimit: memoryLimit, taskId: result.taskId });
callback(null, { taskId: result.taskId });
});
});
}
function setEnvironment(appId, env, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof env, 'object');
assert.strictEqual(typeof memoryLimit, 'number');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
error = validateEnv(env);
if (error) return callback(error);
scheduleTask(appId, {}, { installationState: exports.ISTATE_PENDING_CREATE, env: env }, function (error, result) {
if (error) return callback(error);
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, env: env, taskId: result.taskId });
callback(null, { taskId: result.taskId });
});
});
}
function setDebugMode(appId, debugMode, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof debugMode, 'object');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
error = validateDebugMode(debugMode);
if (error) return callback(error);
scheduleTask(appId, {}, { installationState: exports.ISTATE_PENDING_CREATE, debugMode: debugMode }, function (error, result) {
if (error) return callback(error);
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, debugMode: debugMode, taskId: result.taskId });
callback(null, { taskId: result.taskId });
});
});
}
function setMailbox(appId, mailboxName, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert(!mailboxName || typeof mailboxName === 'string');
assert.strictEqual(typeof memoryLimit, 'number');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
if (mailboxName) {
error = mail.validateName(mailboxName);
if (error) return callback(new AppsError(AppsError.BAD_FIELD, error.message, { field: 'mailboxName' }));
} else {
mailboxName = mailboxNameForLocation(app.location, app.manifest);
}
scheduleTask(appId, {}, { installationState: exports.ISTATE_PENDING_CREATE, mailboxName: mailboxName }, function (error, result) {
if (error) return callback(error);
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, mailboxName: mailboxName, taskId: result.taskId });
callback(null, { taskId: result.taskId });
});
});
}
function setAutomaticBackup(appId, enable, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof enable, 'boolean');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
appdb.update(appId, { enableBackup: enable }, function (error) {
if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, 'No such app'));
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error));
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, enableBackup: enable });
callback();
});
});
}
function setAutomaticUpdate(appId, enable, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof enable, 'boolean');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
appdb.update(appId, { enableAutomaticUpdate: enable }, function (error) {
if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, 'No such app'));
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error));
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, enableAutomaticUpdate: enable });
callback();
});
});
}
function setRobotsTxt(appId, robotsTxt, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert(!robotsTxt || typeof robotsTxt === 'string');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
error = validateRobotsTxt(robotsTxt);
if (error) return callback(error);
appdb.update(appId, { robotsTxt: robotsTxt }, function (error) {
if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, 'No such app'));
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error));
// TODO: call reverseProxy config re-write here
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, robotsTxt: robotsTxt });
callback();
});
});
}
function setCertificate(appId, bundle, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert(bundle && typeof bundle === 'object');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
domains.get(app.domain, function (error, domainObject) {
if (error && error.reason === DomainsError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, 'No such domain'));
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, 'Could not get domain info:' + error.message));
if (bundle.cert && bundle.key) {
error = reverseProxy.validateCertificate(app.location, domainObject, { cert: bundle.cert, key: bundle.key });
if (error) return callback(new AppsError(AppsError.BAD_FIELD, error.message, { field: 'cert' }));
}
error = reverseProxy.setAppCertificateSync(app.location, domainObject, { cert: bundle.cert, key: bundle.key });
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, 'Error setting cert: ' + error.message));
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, cert: bundle.cert, key: bundle.key });
callback();
});
});
}
function setLocation(appId, data, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof data, 'object');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
let values = {
installationState: exports.ISTATE_PENDING_LOCATION_CHANGE,
// these are intentionally reset, if not set
portBindings: null,
alternateDomains: []
};
values.location = data.location.toLowerCase();
values.domain = data.domain.toLowerCase();
if ('portBindings' in data) {
error = validatePortBindings(data.portBindings, app.manifest);
if (error) return callback(error);
values.portBindings = translatePortBindings(data.portBindings || null, app.manifest);
}
if ('alternateDomains' in data) {
// TODO validate all subdomains [{ domain: '', subdomain: ''}]
values.alternateDomains = data.alternateDomains;
}
domains.get(values.domain, function (error, domainObject) {
if (error && error.reason === DomainsError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, 'No such domain'));
if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, 'Could not get domain info:' + error.message));
error = domains.validateHostname(values.location, domainObject);
if (error) return callback(new AppsError(AppsError.BAD_FIELD, 'Bad location: ' + error.message, { field: 'location' }));
scheduleTask(appId, { oldConfig: getAppConfig(app) }, values, function (error, result) {
if (error && error.reason === AppsError.ALREADY_EXISTS) error = getDuplicateErrorDetails(error.message, values.location, domainObject, data.portBindings, app.alternateDomains);
if (error) return callback(error);
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, config: values, taskId: result.taskId });
callback(null, { taskId: result.taskId });
});
});
});
}
function setDataDir(appId, dataDir, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof dataDir, 'string');
assert.strictEqual(typeof auditSource, 'object');
assert.strictEqual(typeof callback, 'function');
get(appId, function (error, app) {
if (error) return callback(error);
error = validateDataDir(dataDir);
if (error) return callback(error);
scheduleTask(appId, { oldConfig: getAppConfig(app) }, { installationState: exports.ISTATE_PENDING_DATA_DIR_MIGRATION, dataDir: dataDir }, function (error, result) {
if (error) return callback(error);
eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, dataDir: dataDir, taskId: result.taskId });
callback(null, { taskId: result.taskId });
});
});
}
function configure(appId, data, user, auditSource, callback) {
assert.strictEqual(typeof appId, 'string');
assert(data && typeof data === 'object');
+128 -4
View File
@@ -438,7 +438,7 @@ function waitForDnsPropagation(app, callback) {
});
}
function migrateDataDir(app, sourceDir, callback) {
function moveDataDir(app, sourceDir, callback) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof sourceDir, 'string');
assert.strictEqual(typeof callback, 'function');
@@ -446,9 +446,9 @@ function migrateDataDir(app, sourceDir, callback) {
let resolvedSourceDir = apps.getDataDir(app, sourceDir);
let resolvedTargetDir = apps.getDataDir(app, app.dataDir);
debug(`migrateDataDir: migrating data from ${resolvedSourceDir} to ${resolvedTargetDir}`);
debug(`moveDataDir: migrating data from ${resolvedSourceDir} to ${resolvedTargetDir}`);
shell.sudo('migrateDataDir', [ MV_VOLUME_CMD, resolvedSourceDir, resolvedTargetDir ], {}, function (error) {
shell.sudo('moveDataDir', [ MV_VOLUME_CMD, resolvedSourceDir, resolvedTargetDir ], {}, function (error) {
if (error) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `Error migrating data directory: ${error.message}`));
callback(null);
@@ -607,6 +607,127 @@ function backup(app, progressCallback, callback) {
});
}
function create(app, progressCallback, callback) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof progressCallback, 'function');
assert.strictEqual(typeof callback, 'function');
async.series([
progressCallback.bind(null, { percent: 10, message: 'Cleaning up old install' }),
stopApp.bind(null, app, progressCallback),
deleteContainers.bind(null, app, { managedOnly: true }),
progressCallback.bind(null, { percent: 60, message: 'Creating container' }),
createContainer.bind(null, app),
progressCallback.bind(null, { percent: 80, message: 'Starting app' }),
runApp.bind(null, app, progressCallback),
progressCallback.bind(null, { percent: 100, message: 'Done' }),
updateApp.bind(null, app, { installationState: apps.ISTATE_INSTALLED, error: null, health: null })
], function seriesDone(error) {
if (error) {
debugApp(app, 'error creating : %s', error);
return updateApp(app, { installationState: apps.ISTATE_ERROR, error: error.toPlainObject ? error.toPlainObject() : error.message }, callback.bind(null, error));
}
callback(null);
});
}
function changeLocation(app, oldConfig, progressCallback, callback) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof oldConfig, 'object');
assert.strictEqual(typeof progressCallback, 'function');
assert.strictEqual(typeof callback, 'function');
const locationChanged = oldConfig.fqdn !== app.fqdn;
async.series([
progressCallback.bind(null, { percent: 10, message: 'Cleaning up old install' }),
unconfigureReverseProxy.bind(null, app),
stopApp.bind(null, app, progressCallback),
deleteContainers.bind(null, app, { managedOnly: true }),
function (next) {
let obsoleteDomains = oldConfig.alternateDomains.filter(function (o) {
return !app.alternateDomains.some(function (n) { return n.subdomain === o.subdomain && n.domain === o.domain; });
});
if (locationChanged) obsoleteDomains.push({ subdomain: oldConfig.location, domain: oldConfig.domain });
if (obsoleteDomains.length === 0) return next();
unregisterSubdomains(app, obsoleteDomains, next);
},
progressCallback.bind(null, { percent: 30, message: 'Registering subdomains' }),
registerSubdomains.bind(null, app, !locationChanged /* overwrite */), // if location changed, do not overwrite to detect conflicts
// re-setup addons since they rely on the app's fqdn (e.g oauth)
progressCallback.bind(null, { percent: 50, message: 'Setting up addons' }),
addons.setupAddons.bind(null, app, app.manifest.addons),
progressCallback.bind(null, { percent: 60, message: 'Creating container' }),
createContainer.bind(null, app),
runApp.bind(null, app, progressCallback),
progressCallback.bind(null, { percent: 80, message: 'Waiting for DNS propagation' }),
exports._waitForDnsPropagation.bind(null, app),
progressCallback.bind(null, { percent: 90, message: 'Configuring reverse proxy' }),
configureReverseProxy.bind(null, app),
progressCallback.bind(null, { percent: 100, message: 'Done' }),
updateApp.bind(null, app, { installationState: apps.ISTATE_INSTALLED, error: null, health: null })
], function seriesDone(error) {
if (error) {
debugApp(app, 'error reconfiguring : %s', error);
return updateApp(app, { installationState: apps.ISTATE_ERROR, error: error.toPlainObject ? error.toPlainObject() : error.message }, callback.bind(null, error));
}
callback(null);
});
}
function migrateDataDir(app, oldConfig, progressCallback, callback) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof oldConfig, 'object');
assert.strictEqual(typeof progressCallback, 'function');
assert.strictEqual(typeof callback, 'function');
const dataDirChanged = oldConfig.dataDir !== app.dataDir;
async.series([
progressCallback.bind(null, { percent: 10, message: 'Cleaning up old install' }),
stopApp.bind(null, app, progressCallback),
deleteContainers.bind(null, app, { managedOnly: true }),
progressCallback.bind(null, { percent: 45, message: 'Ensuring app data directory' }),
createAppDir.bind(null, app),
// migrate dataDir
function (next) {
if (!dataDirChanged) return next();
moveDataDir(app, oldConfig.dataDir, next);
},
progressCallback.bind(null, { percent: 60, message: 'Creating container' }),
createContainer.bind(null, app),
progressCallback.bind(null, { percent: 60, message: 'Starting app' }),
runApp.bind(null, app, progressCallback),
progressCallback.bind(null, { percent: 100, message: 'Done' }),
updateApp.bind(null, app, { installationState: apps.ISTATE_INSTALLED, error: null, health: null })
], function seriesDone(error) {
if (error) {
debugApp(app, 'error reconfiguring : %s', error);
return updateApp(app, { installationState: apps.ISTATE_ERROR, error: error.toPlainObject ? error.toPlainObject() : error.message }, callback.bind(null, error));
}
callback(null);
});
}
// note that configure is called after an infra update as well
function configure(app, oldConfig, progressCallback, callback) {
assert.strictEqual(typeof app, 'object');
@@ -658,7 +779,7 @@ function configure(app, oldConfig, progressCallback, callback) {
function (next) {
if (!dataDirChanged) return next();
migrateDataDir(app, oldConfig.dataDir, next);
moveDataDir(app, oldConfig.dataDir, next);
},
progressCallback.bind(null, { percent: 60, message: 'Creating container' }),
@@ -894,6 +1015,9 @@ function run(appId, args, progressCallback, callback) {
switch (app.installationState) {
case apps.ISTATE_PENDING_INSTALL: return install(app, args.restoreConfig || {}, progressCallback, callback);
case apps.ISTATE_PENDING_CONFIGURE: return configure(app, args.oldConfig, progressCallback, callback);
case apps.ISTATE_PENDING_CREATE_CONTAINER: return create(app, progressCallback, callback);
case apps.ISTATE_PENDING_LOCATION_CHANGE: return changeLocation(app, args.oldConfig, progressCallback, callback);
case apps.ISTATE_PENDING_DATA_DIR_MIGRATION: return migrateDataDir(app, args.oldConfig, progressCallback, callback);
case apps.ISTATE_PENDING_UNINSTALL: return uninstall(app, progressCallback, callback);
case apps.ISTATE_PENDING_CLONE: return install(app, args.restoreConfig || {}, progressCallback, callback);
case apps.ISTATE_PENDING_RESTORE: return install(app, args.restoreConfig || {}, progressCallback, callback);
+216 -4
View File
@@ -14,6 +14,21 @@ exports = module.exports = {
getLogStream: getLogStream,
listBackups: listBackups,
setAccessRestriction: setAccessRestriction,
setLabel: setLabel,
setTags: setTags,
setIcon: setIcon,
setMemoryLimit: setMemoryLimit,
setAutomaticBackup: setAutomaticBackup,
setAutomaticUpdate: setAutomaticUpdate,
setRobotsTxt: setRobotsTxt,
setCertificate: setCertificate,
setDebugMode: setDebugMode,
setEnvironment: setEnvironment,
setMailbox: setMailbox,
setLocation: setLocation,
setDataDir: setDataDir,
stopApp: stopApp,
startApp: startApp,
exec: exec,
@@ -146,6 +161,203 @@ function installApp(req, res, next) {
});
}
function setAccessRestriction(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (typeof req.body.accessRestriction !== 'object') return next(new HttpError(400, 'accessRestriction must be an object'));
apps.setAccessRestriction(req.params.id, req.body.accessRestriction, auditSource.fromRequest(req), function (error) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(200, {}));
});
}
function setLabel(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (typeof req.body.label !== 'string') return next(new HttpError(400, 'label must be a string'));
apps.setLabel(req.params.id, req.body.label, auditSource.fromRequest(req), function (error) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(200, {}));
});
}
function setTags(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (!Array.isArray(req.body.tags)) return next(new HttpError(400, 'tags must be an array'));
if (req.body.tags.some((t) => typeof t !== 'string')) return next(new HttpError(400, 'tags array must contain strings'));
apps.setTags(req.params.id, req.body.tags, auditSource.fromRequest(req), function (error) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(200, {}));
});
}
function setIcon(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (req.body.icon !== null && typeof req.body.icon !== 'string') return next(new HttpError(400, 'icon is null or a base-64 image string'));
apps.setIcon(req.params.id, req.body.icon, auditSource.fromRequest(req), function (error) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(200, {}));
});
}
function setMemoryLimit(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (typeof req.body.memoryLimit !== 'number') return next(new HttpError(400, 'memoryLimit is not a number'));
apps.setMemoryLimit(req.params.id, req.body.memoryLimit, auditSource.fromRequest(req), function (error, result) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(202, { taskId: result.taskId }));
});
}
function setAutomaticBackup(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (typeof req.body.enable !== 'boolean') return next(new HttpError(400, 'enable must be a boolean'));
apps.setAutomaticBackup(req.params.id, req.body.enable, auditSource.fromRequest(req), function (error) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(200, {}));
});
}
function setAutomaticUpdate(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (typeof req.body.enable !== 'boolean') return next(new HttpError(400, 'enable must be a boolean'));
apps.setAutomaticUpdate(req.params.id, req.body.enable, auditSource.fromRequest(req), function (error) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(200, {}));
});
}
function setRobotsTxt(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (req.body.robotsTxt !== null && typeof req.body.robotsTxt !== 'string') return next(new HttpError(400, 'robotsTxt is not a string'));
apps.setRobotsTxt(req.params.id, req.body.robotsTxt, auditSource.fromRequest(req), function (error) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(200, {}));
});
}
function setCertificate(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (req.body.key !== null && typeof req.body.cert !== 'string') return next(new HttpError(400, 'cert must be a string'));
if (req.body.cert !== null && typeof req.body.key !== 'string') return next(new HttpError(400, 'key must be a string'));
if (req.body.cert && !req.body.key) return next(new HttpError(400, 'key must be provided'));
if (!req.body.cert && req.body.key) return next(new HttpError(400, 'cert must be provided'));
apps.setCertificate(req.params.id, req.body, auditSource.fromRequest(req), function (error) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(200, {}));
});
}
function setEnvironment(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (!req.body.env || typeof req.body.env !== 'object') return next(new HttpError(400, 'env must be an object'));
if (Object.keys(req.body.env).some(function (key) { return typeof req.body.env[key] !== 'string'; })) return next(new HttpError(400, 'env must contain values as strings'));
apps.setEnvironment(req.params.id, req.body.env, auditSource.fromRequest(req), function (error, result) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(202, { taskId: result.taskId }));
});
}
function setDebugMode(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (req.body.debugMode !== null && typeof req.body.debugMode !== 'object') return next(new HttpError(400, 'debugMode must be an object'));
apps.setDebugMode(req.params.id, req.body.debugMode, auditSource.fromRequest(req), function (error, result) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(202, { taskId: result.taskId }));
});
}
function setMailbox(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (req.body.mailboxName !== null && typeof req.body.mailboxName !== 'string') return next(new HttpError(400, 'mailboxName must be a string'));
apps.setMailbox(req.params.id, req.body.mailboxName, auditSource.fromRequest(req), function (error, result) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(202, { taskId: result.taskId }));
});
}
function setLocation(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (!req.body.location) return next(new HttpError(400, 'location is required'));
if (typeof req.body.location !== 'string') return next(new HttpError(400, 'location must be string'));
if (!req.body.domain) return next(new HttpError(400, 'domain is required'));
if (typeof req.body.domain !== 'string') return next(new HttpError(400, 'domain must be string'));
if ('portBindings' in req.body && typeof req.body.portBindings !== 'object') return next(new HttpError(400, 'portBindings must be an object'));
if ('alternateDomains' in req.body) {
if (!Array.isArray(req.body.alternateDomains)) return next(new HttpError(400, 'alternateDomains must be an array'));
if (req.body.alternateDomains.some(function (d) { return (typeof d.domain !== 'string' || typeof d.subdomain !== 'string'); })) return next(new HttpError(400, 'alternateDomains array must contain objects with domain and subdomain strings'));
}
apps.setLocation(req.params.id, req.body, auditSource.fromRequest(req), function (error, result) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(202, { taskId: result.taskId }));
});
}
function setDataDir(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
if (typeof req.body.dataDir !== 'string') return next(new HttpError(400, 'dataDir must be a string'));
apps.setDataDir(req.params.id, req.body.dataDir, auditSource.fromRequest(req), function (error, result) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(202, { taskId: result.taskId }));
});
}
function configureApp(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
assert.strictEqual(typeof req.params.id, 'string');
@@ -268,10 +480,10 @@ function startApp(req, res, next) {
debug('Start app id:%s', req.params.id);
apps.start(req.params.id, function (error) {
apps.start(req.params.id, function (error, result) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(202, { }));
next(new HttpSuccess(202, { taskId: result.taskId }));
});
}
@@ -280,10 +492,10 @@ function stopApp(req, res, next) {
debug('Stop app id:%s', req.params.id);
apps.stop(req.params.id, function (error) {
apps.stop(req.params.id, function (error, result) {
if (error) return next(toHttpError(error));
next(new HttpSuccess(202, { }));
next(new HttpSuccess(202, { taskId: result.taskId }));
});
}
+1030 -831
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -226,7 +226,23 @@ function initializeExpressSync() {
router.post('/api/v1/apps/install', appsManageScope, routes.apps.installApp);
router.post('/api/v1/apps/:id/uninstall', appsManageScope, routes.apps.uninstallApp);
router.post('/api/v1/apps/:id/configure', appsManageScope, routes.apps.configureApp);
router.post('/api/v1/apps/:id/configure/access_restriction', appsManageScope, routes.apps.setAccessRestriction);
router.post('/api/v1/apps/:id/configure/label', appsManageScope, routes.apps.setLabel);
router.post('/api/v1/apps/:id/configure/tags', appsManageScope, routes.apps.setTags);
router.post('/api/v1/apps/:id/configure/icon', appsManageScope, routes.apps.setIcon);
router.post('/api/v1/apps/:id/configure/memory_limit', appsManageScope, routes.apps.setMemoryLimit);
router.post('/api/v1/apps/:id/configure/automatic_backup', appsManageScope, routes.apps.setAutomaticBackup);
router.post('/api/v1/apps/:id/configure/automatic_update', appsManageScope, routes.apps.setAutomaticUpdate);
router.post('/api/v1/apps/:id/configure/robots_txt', appsManageScope, routes.apps.setRobotsTxt);
router.post('/api/v1/apps/:id/configure/cert', appsManageScope, routes.apps.setCertificate);
router.post('/api/v1/apps/:id/configure/debug_mode', appsManageScope, routes.apps.setDebugMode);
router.post('/api/v1/apps/:id/configure/mailbox', appsManageScope, routes.apps.setMailbox);
router.post('/api/v1/apps/:id/configure/env', appsManageScope, routes.apps.setEnvironment);
router.post('/api/v1/apps/:id/configure/data_dir', appsManageScope, routes.apps.setDataDir);
router.post('/api/v1/apps/:id/configure/location', appsManageScope, routes.apps.setLocation);
router.post('/api/v1/apps/:id/update', appsManageScope, routes.apps.updateApp);
router.post('/api/v1/apps/:id/restore', appsManageScope, routes.apps.restoreApp);
router.post('/api/v1/apps/:id/backup', appsManageScope, routes.apps.backupApp);