diff --git a/src/apps.js b/src/apps.js index bcfc46484..7d7cd4934 100644 --- a/src/apps.js +++ b/src/apps.js @@ -1,8 +1,6 @@ 'use strict'; exports = module.exports = { - AppsError: AppsError, - hasAccessTo: hasAccessTo, removeInternalFields: removeInternalFields, removeRestrictedFields: removeRestrictedFields, @@ -138,38 +136,6 @@ var appdb = require('./appdb.js'), validator = require('validator'), _ = require('underscore'); -// http://dustinsenos.com/articles/customErrorsInNode -// http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi -function AppsError(reason, errorOrMessage, details) { - assert.strictEqual(typeof reason, 'string'); - assert(errorOrMessage instanceof Error || typeof errorOrMessage === 'string' || typeof errorOrMessage === 'undefined'); - assert(typeof details === 'object' || typeof details === 'undefined'); - - Error.call(this); - Error.captureStackTrace(this, this.constructor); - - this.name = this.constructor.name; - this.reason = reason; - if (typeof errorOrMessage === 'undefined') { - this.message = reason; - } else if (typeof errorOrMessage === 'string') { - this.message = errorOrMessage; - } else { - this.message = 'Internal error'; - this.nestedError = errorOrMessage; - } - - this.details = details || {}; -} -util.inherits(AppsError, Error); -AppsError.INTERNAL_ERROR = 'Internal Error'; -AppsError.EXTERNAL_ERROR = 'External Error'; -AppsError.ALREADY_EXISTS = 'Already Exists'; -AppsError.NOT_FOUND = 'Not Found'; -AppsError.BAD_FIELD = 'Bad Field'; -AppsError.BAD_STATE = 'Bad State'; -AppsError.PLAN_LIMIT = 'Plan Limit'; - const NOOP_CALLBACK = function (error) { if (error) debug(error); }; // validate the port bindings @@ -209,12 +175,12 @@ function validatePortBindings(portBindings, manifest) { if (!portBindings) return null; for (let portName in portBindings) { - if (!/^[a-zA-Z0-9_]+$/.test(portName)) return new AppsError(AppsError.BAD_FIELD, `${portName} is not a valid environment variable`, { field: 'portBindings', portName: portName }); + if (!/^[a-zA-Z0-9_]+$/.test(portName)) return new BoxError(BoxError.BAD_FIELD, `${portName} is not a valid environment variable`, { field: 'portBindings', portName: portName }); const hostPort = portBindings[portName]; - if (!Number.isInteger(hostPort)) return new AppsError(AppsError.BAD_FIELD, `${hostPort} is not an integer`, { field: 'portBindings', portName: portName }); - if (RESERVED_PORTS.indexOf(hostPort) !== -1) return new AppsError(AppsError.BAD_FIELD, `Port ${hostPort} is reserved.`, { field: 'portBindings', portName: portName }); - if (hostPort <= 1023 || hostPort > 65535) return new AppsError(AppsError.BAD_FIELD, `${hostPort} is not in permitted range`, { field: 'portBindings', portName: portName }); + if (!Number.isInteger(hostPort)) return new BoxError(BoxError.BAD_FIELD, `${hostPort} is not an integer`, { field: 'portBindings', portName: portName }); + if (RESERVED_PORTS.indexOf(hostPort) !== -1) return new BoxError(BoxError.BAD_FIELD, `Port ${hostPort} is reserved.`, { field: 'portBindings', portName: portName }); + if (hostPort <= 1023 || hostPort > 65535) return new BoxError(BoxError.BAD_FIELD, `${hostPort} is not in permitted range`, { field: 'portBindings', portName: portName }); } // it is OK if there is no 1-1 mapping between values in manifest.tcpPorts and portBindings. missing values implies @@ -222,7 +188,7 @@ function validatePortBindings(portBindings, manifest) { const tcpPorts = manifest.tcpPorts || { }; const udpPorts = manifest.udpPorts || { }; for (let portName in portBindings) { - if (!(portName in tcpPorts) && !(portName in udpPorts)) return new AppsError(AppsError.BAD_FIELD, `Invalid portBindings ${portName}`, { field: 'portBindings', portName: portName }); + if (!(portName in tcpPorts) && !(portName in udpPorts)) return new BoxError(BoxError.BAD_FIELD, `Invalid portBindings ${portName}`, { field: 'portBindings', portName: portName }); } return null; @@ -250,13 +216,13 @@ function validateAccessRestriction(accessRestriction) { if (accessRestriction === null) return null; if (accessRestriction.users) { - if (!Array.isArray(accessRestriction.users)) return new AppsError(AppsError.BAD_FIELD, 'users array property required'); - if (!accessRestriction.users.every(function (e) { return typeof e === 'string'; })) return new AppsError(AppsError.BAD_FIELD, 'All users have to be strings'); + if (!Array.isArray(accessRestriction.users)) return new BoxError(BoxError.BAD_FIELD, 'users array property required'); + if (!accessRestriction.users.every(function (e) { return typeof e === 'string'; })) return new BoxError(BoxError.BAD_FIELD, 'All users have to be strings'); } if (accessRestriction.groups) { - if (!Array.isArray(accessRestriction.groups)) return new AppsError(AppsError.BAD_FIELD, 'groups array property required'); - if (!accessRestriction.groups.every(function (e) { return typeof e === 'string'; })) return new AppsError(AppsError.BAD_FIELD, 'All groups have to be strings'); + if (!Array.isArray(accessRestriction.groups)) return new BoxError(BoxError.BAD_FIELD, 'groups array property required'); + if (!accessRestriction.groups.every(function (e) { return typeof e === 'string'; })) return new BoxError(BoxError.BAD_FIELD, 'All groups have to be strings'); } // TODO: maybe validate if the users and groups actually exist @@ -277,8 +243,8 @@ function validateMemoryLimit(manifest, memoryLimit) { // a special value that indicates unlimited memory if (memoryLimit === -1) return null; - if (memoryLimit < min) return new AppsError(AppsError.BAD_FIELD, 'memoryLimit too small'); - if (memoryLimit > max) return new AppsError(AppsError.BAD_FIELD, 'memoryLimit too large'); + if (memoryLimit < min) return new BoxError(BoxError.BAD_FIELD, 'memoryLimit too small'); + if (memoryLimit > max) return new BoxError(BoxError.BAD_FIELD, 'memoryLimit too large'); return null; } @@ -287,8 +253,8 @@ function validateDebugMode(debugMode) { assert.strictEqual(typeof debugMode, 'object'); if (debugMode === null) return null; - if ('cmd' in debugMode && debugMode.cmd !== null && !Array.isArray(debugMode.cmd)) return new AppsError(AppsError.BAD_FIELD, 'debugMode.cmd must be an array or null' ); - if ('readonlyRootfs' in debugMode && typeof debugMode.readonlyRootfs !== 'boolean') return new AppsError(AppsError.BAD_FIELD, 'debugMode.readonlyRootfs must be a boolean' ); + if ('cmd' in debugMode && debugMode.cmd !== null && !Array.isArray(debugMode.cmd)) return new BoxError(BoxError.BAD_FIELD, 'debugMode.cmd must be an array or null' ); + if ('readonlyRootfs' in debugMode && typeof debugMode.readonlyRootfs !== 'boolean') return new BoxError(BoxError.BAD_FIELD, 'debugMode.readonlyRootfs must be a boolean' ); return null; } @@ -297,7 +263,7 @@ function validateRobotsTxt(robotsTxt) { if (robotsTxt === null) return null; // this is the nginx limit on inline strings. if we really hit this, we have to generate a file - if (robotsTxt.length > 4096) return new AppsError(AppsError.BAD_FIELD, 'robotsTxt must be less than 4096', { field: 'robotsTxt' }); + if (robotsTxt.length > 4096) return new BoxError(BoxError.BAD_FIELD, 'robotsTxt must be less than 4096', { field: 'robotsTxt' }); // TODO: validate the robots file? we escape the string when templating the nginx config right now @@ -307,9 +273,9 @@ function validateRobotsTxt(robotsTxt) { function validateCsp(csp) { if (csp === null) return null; - if (csp.length > 4096) return new AppsError(AppsError.BAD_FIELD, 'CSP must be less than 4096', { field: 'csp' }); + if (csp.length > 4096) return new BoxError(BoxError.BAD_FIELD, 'CSP must be less than 4096', { field: 'csp' }); - if (csp.includes('"')) return new AppsError(AppsError.BAD_FIELD, 'CSP cannot contains double quotes', { field: 'csp' }); + if (csp.includes('"')) return new BoxError(BoxError.BAD_FIELD, 'CSP cannot contains double quotes', { field: 'csp' }); return null; } @@ -319,31 +285,31 @@ function validateBackupFormat(format) { if (format === 'tgz' || format == 'rsync') return null; - return new AppsError(AppsError.BAD_FIELD, 'Invalid backup format'); + return new BoxError(BoxError.BAD_FIELD, 'Invalid backup format'); } function validateLabel(label) { if (label === null) return null; - if (label.length > 128) return new AppsError(AppsError.BAD_FIELD, 'label must be less than 128', { field: 'label' }); + if (label.length > 128) return new BoxError(BoxError.BAD_FIELD, 'label must be less than 128', { field: 'label' }); return null; } function validateTags(tags) { - if (tags.length > 64) return new AppsError(AppsError.BAD_FIELD, 'Can only set up to 64 tags', { field: 'tags' }); + if (tags.length > 64) return new BoxError(BoxError.BAD_FIELD, 'Can only set up to 64 tags', { field: 'tags' }); - if (tags.some(tag => tag.length == 0)) return new AppsError(AppsError.BAD_FIELD, 'tag cannot be empty', { field: 'tags' }); - if (tags.some(tag => tag.length > 128)) return new AppsError(AppsError.BAD_FIELD, 'tag must be less than 128', { field: 'tags' }); + if (tags.some(tag => tag.length == 0)) return new BoxError(BoxError.BAD_FIELD, 'tag cannot be empty', { field: 'tags' }); + if (tags.some(tag => tag.length > 128)) return new BoxError(BoxError.BAD_FIELD, 'tag must be less than 128', { field: 'tags' }); return null; } function validateEnv(env) { for (let key in env) { - if (key.length > 512) return new AppsError(AppsError.BAD_FIELD, 'Max env var key length is 512', { field: 'env', env: env }); + if (key.length > 512) return new BoxError(BoxError.BAD_FIELD, 'Max env var key length is 512', { field: 'env', env: env }); // http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html - if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) return new AppsError(AppsError.BAD_FIELD, `"${key}" is not a valid environment variable`, { field: 'env', env: env }); + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) return new BoxError(BoxError.BAD_FIELD, `"${key}" is not a valid environment variable`, { field: 'env', env: env }); } return null; @@ -352,23 +318,23 @@ function validateEnv(env) { function validateDataDir(dataDir) { if (dataDir === null) return null; - if (path.resolve(dataDir) !== dataDir) return new AppsError(AppsError.BAD_FIELD, 'dataDir must be an absolute path', { field: 'dataDir' }); + if (path.resolve(dataDir) !== dataDir) return new BoxError(BoxError.BAD_FIELD, 'dataDir must be an absolute path', { field: 'dataDir' }); // nfs shares will have the directory mounted already let stat = safe.fs.lstatSync(dataDir); if (stat) { - if (!stat.isDirectory()) return new AppsError(AppsError.BAD_FIELD, `dataDir ${dataDir} is not a directory`, { field: 'dataDir' }); + if (!stat.isDirectory()) return new BoxError(BoxError.BAD_FIELD, `dataDir ${dataDir} is not a directory`, { field: 'dataDir' }); let entries = safe.fs.readdirSync(dataDir); - if (!entries) return new AppsError(AppsError.BAD_FIELD, `dataDir ${dataDir} could not be listed`, { field: 'dataDir' }); - if (entries.length !== 0) return new AppsError(AppsError.BAD_FIELD, `dataDir ${dataDir} is not empty`, { field: 'dataDir' }); + if (!entries) return new BoxError(BoxError.BAD_FIELD, `dataDir ${dataDir} could not be listed`, { field: 'dataDir' }); + if (entries.length !== 0) return new BoxError(BoxError.BAD_FIELD, `dataDir ${dataDir} is not empty`, { field: 'dataDir' }); } // backup logic relies on paths not overlapping (because it recurses) - if (dataDir.startsWith(paths.APPS_DATA_DIR)) return new AppsError(AppsError.BAD_FIELD, `dataDir ${dataDir} cannot be inside apps data`, { field: 'dataDir' }); + if (dataDir.startsWith(paths.APPS_DATA_DIR)) return new BoxError(BoxError.BAD_FIELD, `dataDir ${dataDir} cannot be inside apps data`, { field: 'dataDir' }); // if we made it this far, it cannot start with any of these realistically const fhs = [ '/bin', '/boot', '/etc', '/lib', '/lib32', '/lib64', '/proc', '/run', '/sbin', '/tmp', '/usr' ]; - if (fhs.some((p) => dataDir.startsWith(p))) return new AppsError(AppsError.BAD_FIELD, `dataDir ${dataDir} cannot be placed inside this location`, { field: 'dataDir' }); + if (fhs.some((p) => dataDir.startsWith(p))) return new BoxError(BoxError.BAD_FIELD, `dataDir ${dataDir} cannot be placed inside this location`, { field: 'dataDir' }); return null; } @@ -382,7 +348,7 @@ function getDuplicateErrorDetails(errorMessage, locations, domainObjectMap, port var match = errorMessage.match(/ER_DUP_ENTRY: Duplicate entry '(.*)' for key '(.*)'/); if (!match) { debug('Unexpected SQL error message.', errorMessage); - return new AppsError(AppsError.INTERNAL_ERROR, new Error(errorMessage)); + return new BoxError(BoxError.INTERNAL_ERROR, new Error(errorMessage)); } // check if a location conflicts @@ -391,20 +357,20 @@ function getDuplicateErrorDetails(errorMessage, locations, domainObjectMap, port const { subdomain, domain } = locations[i]; if (match[1] !== `${subdomain}-${domain}`) continue; - return new AppsError(AppsError.ALREADY_EXISTS, `Domain '${domains.fqdn(subdomain, domainObjectMap[domain])}' is in use`, { subdomain, domain }); + return new BoxError(BoxError.ALREADY_EXISTS, `Domain '${domains.fqdn(subdomain, domainObjectMap[domain])}' is in use`, { subdomain, domain }); } } // check if any of the port bindings conflict for (let portName in portBindings) { - if (portBindings[portName] === parseInt(match[1])) return new AppsError(AppsError.ALREADY_EXISTS, `Port ${match[1]} is reserved`, { portName }); + if (portBindings[portName] === parseInt(match[1])) return new BoxError(BoxError.ALREADY_EXISTS, `Port ${match[1]} is reserved`, { portName }); } if (match[2] === 'dataDir') { - return new AppsError(AppsError.BAD_FIELD, `Data directory ${match[1]} is in use`, { field: 'dataDir' }); + return new BoxError(BoxError.BAD_FIELD, `Data directory ${match[1]} is in use`, { field: 'dataDir' }); } - return new AppsError(AppsError.ALREADY_EXISTS, `${match[2]} '${match[1]}' is in use`); + return new BoxError(BoxError.ALREADY_EXISTS, `${match[2]} '${match[1]}' is in use`); } function getDataDir(app, dataDir) { @@ -454,7 +420,7 @@ function getIconPath(appId, options, callback) { const appstoreIconPath = `${paths.APP_ICONS_DIR}/${appId}.png`; if (safe.fs.existsSync(appstoreIconPath)) return callback(null, appstoreIconPath); - callback(new AppsError(AppsError.NOT_FOUND, 'No icon')); + callback(new BoxError(BoxError.NOT_FOUND, 'No icon')); } function postProcess(app, domainObjectMap) { @@ -492,7 +458,7 @@ function getDomainObjectMap(callback) { assert.strictEqual(typeof callback, 'function'); domaindb.getAll(function (error, domainObjects) { - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error) return callback(error); let domainObjectMap = {}; for (let d of domainObjects) { domainObjectMap[d.domain] = d; } @@ -509,8 +475,8 @@ function get(appId, callback) { if (error) return callback(error); appdb.get(appId, function (error, app) { - 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)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, 'No such app')); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); postProcess(app, domainObjectMap); @@ -527,8 +493,8 @@ function getByContainerId(containerId, callback) { if (error) return callback(error); appdb.getByContainerId(containerId, function (error, app) { - 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)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, 'No such app')); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); postProcess(app, domainObjectMap); @@ -546,13 +512,13 @@ function getByIpAddress(ip, callback) { if (constants.TEST && exports._MOCK_GET_BY_IP_APP_ID) return get(exports._MOCK_GET_BY_IP_APP_ID, callback); docker.getContainerIdByIp(ip, function (error, containerId) { - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error) return callback(error); docker.inspect(containerId, function (error, result) { - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error) return callback(error); const appId = safe.query(result, 'Config.Labels.appId', null); - if (!appId) return callback(new AppsError(AppsError.NOT_FOUND, 'No such app')); + if (!appId) return callback(new BoxError(BoxError.NOT_FOUND, 'No such app')); get(appId, callback); }); @@ -567,7 +533,7 @@ function getByFqdn(fqdn, callback) { if (error) return callback(error); var app = result.find(function (a) { return a.fqdn === fqdn; }); - if (!app) return callback(new AppsError(AppsError.NOT_FOUND, 'No such app')); + if (!app) return callback(new BoxError(BoxError.NOT_FOUND, 'No such app')); callback(null, app); }); @@ -580,7 +546,7 @@ function getAll(callback) { if (error) return callback(error); appdb.getAll(function (error, apps) { - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); apps.forEach((app) => postProcess(app, domainObjectMap)); @@ -603,7 +569,7 @@ function getAllByUser(user, callback) { } function downloadManifest(appStoreId, manifest, callback) { - if (!appStoreId && !manifest) return callback(new AppsError(AppsError.BAD_FIELD, 'Neither manifest nor appStoreId provided')); + if (!appStoreId && !manifest) return callback(new BoxError(BoxError.BAD_FIELD, 'Neither manifest nor appStoreId provided')); if (!appStoreId) return callback(null, '', manifest); @@ -614,9 +580,9 @@ function downloadManifest(appStoreId, manifest, callback) { debug('downloading manifest from %s', url); superagent.get(url).timeout(30 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppsError(AppsError.EXTERNAL_ERROR, 'Network error downloading manifest:' + error.message)); + if (error && !error.response) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Network error downloading manifest:' + error.message)); - if (result.statusCode !== 200) return callback(new AppsError(AppsError.NOT_FOUND, util.format('Failed to get app info from store.', result.statusCode, result.text))); + if (result.statusCode !== 200) return callback(new BoxError(BoxError.NOT_FOUND, util.format('Failed to get app info from store.', result.statusCode, result.text))); callback(null, parts[0], result.body.manifest); }); @@ -662,12 +628,12 @@ function addTask(appId, installationState, task, callback) { const requireNullTaskId = 'requireNullTaskId' in task ? task.requireNullTaskId : true; tasks.add(tasks.TASK_APP, [ appId, args ], function (error, taskId) { - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error) return callback(error); appdb.setTask(appId, _.extend({ installationState, taskId, error: null }, values), { requiredState, requireNullTaskId }, function (error) { - if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(new AppsError(AppsError.ALREADY_EXISTS, error.message)); - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.BAD_STATE, 'Another task is scheduled for this app')); // could be because app went away OR a taskId exists - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(new BoxError(BoxError.ALREADY_EXISTS, error.message)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.BAD_STATE, 'Another task is scheduled for this app')); // could be because app went away OR a taskId exists + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); if (scheduleNow) scheduleTask(appId, installationState, taskId, NOOP_CALLBACK); @@ -680,10 +646,10 @@ function checkAppState(app, state) { assert.strictEqual(typeof app, 'object'); assert.strictEqual(typeof state, 'string'); - if (app.taskId) return new AppsError(AppsError.BAD_STATE, `Not allowed in this app state : ${app.installationState} / ${app.runState}`); + if (app.taskId) return new BoxError(BoxError.BAD_STATE, `Not allowed in this app state : ${app.installationState} / ${app.runState}`); if (app.installationState === exports.ISTATE_ERROR) { - if (state !== exports.ISTATE_PENDING_UNINSTALL) return new AppsError(AppsError.BAD_STATE, 'Not allowed in error state'); + if (state !== exports.ISTATE_PENDING_UNINSTALL) return new BoxError(BoxError.BAD_STATE, 'Not allowed in error state'); } return null; @@ -697,10 +663,10 @@ function validateLocations(locations, callback) { if (error) return callback(error); for (let location of locations) { - if (!(location.domain in domainObjectMap)) return callback(new AppsError(AppsError.BAD_FIELD, 'No such domain', { field: 'location', domain: location.domain, subdomain: location.subdomain })); + if (!(location.domain in domainObjectMap)) return callback(new BoxError(BoxError.BAD_FIELD, 'No such domain', { field: 'location', domain: location.domain, subdomain: location.subdomain })); error = domains.validateHostname(location.subdomain, domainObjectMap[location.domain]); - if (error) return callback(new AppsError(AppsError.BAD_FIELD, 'Bad location: ' + error.message, { field: 'location', domain: location.domain, subdomain: location.subdomain })); + if (error) return callback(new BoxError(BoxError.BAD_FIELD, 'Bad location: ' + error.message, { field: 'location', domain: location.domain, subdomain: location.subdomain })); } callback(null, domainObjectMap); @@ -738,7 +704,7 @@ function install(data, user, auditSource, callback) { if (error) return callback(error); error = manifestFormat.parse(manifest); - if (error) return callback(new AppsError(AppsError.BAD_FIELD, 'Manifest error: ' + error.message)); + if (error) return callback(new BoxError(BoxError.BAD_FIELD, 'Manifest error: ' + error.message)); error = checkManifestConstraints(manifest); if (error) return callback(error); @@ -761,7 +727,7 @@ function install(data, user, auditSource, callback) { error = validateTags(tags); if (error) return callback(error); - if ('sso' in data && !('optionalSso' in manifest)) return callback(new AppsError(AppsError.BAD_FIELD, 'sso can only be specified for apps with optionalSso')); + if ('sso' in data && !('optionalSso' in manifest)) return callback(new BoxError(BoxError.BAD_FIELD, 'sso can only be specified for apps with optionalSso')); // if sso was unspecified, enable it by default if possible if (sso === null) sso = !!manifest.addons['ldap'] || !!manifest.addons['oauth']; @@ -770,7 +736,7 @@ function install(data, user, auditSource, callback) { if (mailboxName) { error = mail.validateName(mailboxName); - if (error) return callback(new AppsError(AppsError.BAD_FIELD, error.message, { field: 'mailboxName' })); + if (error) return callback(new BoxError(BoxError.BAD_FIELD, error.message, { field: 'mailboxName' })); } else { mailboxName = mailboxNameForLocation(location, manifest); } @@ -778,10 +744,10 @@ function install(data, user, auditSource, callback) { var appId = uuid.v4(); if (icon) { - if (!validator.isBase64(icon)) return callback(new AppsError(AppsError.BAD_FIELD, 'icon is not base64', { field: 'icon' })); + if (!validator.isBase64(icon)) return callback(new BoxError(BoxError.BAD_FIELD, 'icon is not base64', { field: 'icon' })); if (!safe.fs.writeFileSync(path.join(paths.APP_ICONS_DIR, appId + '.png'), Buffer.from(icon, 'base64'))) { - return callback(new AppsError(AppsError.INTERNAL_ERROR, 'Error saving icon:' + safe.error.message)); + return callback(new BoxError(BoxError.FS_ERROR, 'Error saving icon:' + safe.error.message)); } } @@ -791,7 +757,7 @@ function install(data, user, auditSource, callback) { if (cert && key) { error = reverseProxy.validateCertificate(location, domainObjectMap[domain], { cert, key }); - if (error) return callback(new AppsError(AppsError.BAD_FIELD, error.message, { field: 'cert' })); + if (error) return callback(new BoxError(BoxError.BAD_FIELD, error.message, { field: 'cert' })); } debug('Will install app with id : ' + appId); @@ -814,8 +780,8 @@ function install(data, user, auditSource, callback) { appdb.add(appId, appStoreId, manifest, location, domain, translatePortBindings(portBindings, manifest), data, function (error) { if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(getDuplicateErrorDetails(error.message, locations, domainObjectMap, portBindings)); - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, error.message)); - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, error.message)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); purchaseApp({ appId: appId, appstoreId: appStoreId, manifestId: manifest.id }, function (error) { if (error) return callback(error); @@ -823,7 +789,7 @@ function install(data, user, auditSource, callback) { // save cert to boxdata/certs if (cert && key) { let error = reverseProxy.setAppCertificateSync(location, domainObjectMap[domain], { cert, key }); - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, 'Error setting cert: ' + error.message)); + if (error) return callback(error); } const task = { @@ -862,8 +828,8 @@ function setAccessRestriction(appId, accessRestriction, auditSource, callback) { 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)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, 'No such app')); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, accessRestriction: accessRestriction }); @@ -885,8 +851,8 @@ function setLabel(appId, label, auditSource, callback) { 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)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, 'No such app')); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, label: label }); @@ -908,8 +874,8 @@ function setTags(appId, tags, auditSource, callback) { 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)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, 'No such app')); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, tags: tags }); @@ -928,10 +894,10 @@ function setIcon(appId, icon, auditSource, callback) { 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 (!validator.isBase64(icon)) return callback(new BoxError(BoxError.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)); + return callback(new BoxError(BoxError.FS_ERROR, 'Error saving icon:' + safe.error.message)); } } else { safe.fs.unlinkSync(path.join(paths.APP_ICONS_DIR, appId + '.user.png')); @@ -1044,7 +1010,7 @@ function setMailbox(appId, mailboxName, auditSource, callback) { if (mailboxName) { error = mail.validateName(mailboxName); - if (error) return callback(new AppsError(AppsError.BAD_FIELD, error.message, { field: 'mailboxName' })); + if (error) return callback(new BoxError(BoxError.BAD_FIELD, error.message, { field: 'mailboxName' })); } else { mailboxName = mailboxNameForLocation(app.location, app.manifest); } @@ -1073,8 +1039,8 @@ function setAutomaticBackup(appId, enable, auditSource, callback) { 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)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, 'No such app')); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, enableBackup: enable }); @@ -1093,8 +1059,8 @@ function setAutomaticUpdate(appId, enable, auditSource, callback) { 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)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, 'No such app')); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, enableAutomaticUpdate: enable }); @@ -1121,11 +1087,11 @@ function setReverseProxyConfig(appId, reverseProxyConfig, auditSource, callback) if (error) return callback(error); reverseProxy.writeAppConfig(_.extend({}, app, { reverseProxyConfig }), function (error) { - if (error) return callback(new AppsError(AppsError.EXTERNAL_ERROR, 'Error writing nginx config')); + if (error) return callback(error); appdb.update(appId, { reverseProxyConfig }, 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)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, 'No such app')); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, reverseProxyConfig }); @@ -1145,16 +1111,16 @@ function setCertificate(appId, bundle, auditSource, callback) { if (error) return callback(error); domains.get(app.domain, function (error, domainObject) { - if (error && error.reason === BoxError.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 (error && error.reason === BoxError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, 'No such domain')); + if (error) return callback(new BoxError(BoxError.DATABASE_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' })); + if (error) return callback(new BoxError(BoxError.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)); + if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: appId, app: app, cert: bundle.cert, key: bundle.key }); @@ -1210,7 +1176,7 @@ function setLocation(appId, data, auditSource, callback) { values }; addTask(appId, exports.ISTATE_PENDING_LOCATION_CHANGE, task, function (error, result) { - if (error && error.reason === AppsError.ALREADY_EXISTS) error = getDuplicateErrorDetails(error.message, locations, domainObjectMap, data.portBindings); + if (error && error.reason === BoxError.ALREADY_EXISTS) error = getDuplicateErrorDetails(error.message, locations, domainObjectMap, data.portBindings); if (error) return callback(error); values.fqdn = domains.fqdn(values.location, domainObjectMap[values.domain]); @@ -1273,7 +1239,7 @@ function update(appId, data, auditSource, callback) { if (error) return callback(error); error = manifestFormat.parse(manifest); - if (error) return callback(new AppsError(AppsError.BAD_FIELD, 'Manifest error:' + error.message)); + if (error) return callback(new BoxError(BoxError.BAD_FIELD, 'Manifest error:' + error.message)); error = checkManifestConstraints(manifest); if (error) return callback(error); @@ -1283,7 +1249,7 @@ function update(appId, data, auditSource, callback) { // prevent user from installing a app with different manifest id over an existing app // this allows cloudron install -f --app for an app installed from the appStore if (app.manifest.id !== updateConfig.manifest.id) { - if (!data.force) return callback(new AppsError(AppsError.BAD_FIELD, 'manifest id does not match. force to override')); + if (!data.force) return callback(new BoxError(BoxError.BAD_FIELD, 'manifest id does not match. force to override')); // clear appStoreId so that this app does not get updates anymore updateConfig.appStoreId = ''; } @@ -1292,15 +1258,15 @@ function update(appId, data, auditSource, callback) { const currentVersion = semver.prerelease(app.manifest.version) ? app.manifest.version : `${app.manifest.version}-0`; const updateVersion = semver.prerelease(updateConfig.manifest.version) ? updateConfig.manifest.version : `${updateConfig.manifest.version}-0`; if (app.appStoreId !== '' && semver.lte(updateVersion, currentVersion)) { - if (!data.force) return callback(new AppsError(AppsError.BAD_FIELD, 'Downgrades are not permitted for apps installed from AppStore. force to override')); + if (!data.force) return callback(new BoxError(BoxError.BAD_FIELD, 'Downgrades are not permitted for apps installed from AppStore. force to override')); } if ('icon' in data) { if (data.icon) { - if (!validator.isBase64(data.icon)) return callback(new AppsError(AppsError.BAD_FIELD, 'icon is not base64', { field: 'icon' })); + if (!validator.isBase64(data.icon)) return callback(new BoxError(BoxError.BAD_FIELD, 'icon is not base64', { field: 'icon' })); if (!safe.fs.writeFileSync(path.join(paths.APP_ICONS_DIR, appId + '.user.png'), Buffer.from(data.icon, 'base64'))) { - return callback(new AppsError(AppsError.INTERNAL_ERROR, 'Error saving icon:' + safe.error.message)); + return callback(new BoxError(BoxError.FS_ERROR, 'Error saving icon:' + safe.error.message)); } } else { safe.fs.unlinkSync(path.join(paths.APP_ICONS_DIR, appId + '.user.png')); @@ -1308,7 +1274,7 @@ function update(appId, data, auditSource, callback) { } // do not update apps in debug mode - if (app.debugMode && !data.force) return callback(new AppsError(AppsError.BAD_STATE, 'debug mode enabled. force to override')); + if (app.debugMode && !data.force) return callback(new BoxError(BoxError.BAD_STATE, 'debug mode enabled. force to override')); // Ensure we update the memory limit in case the new app requires more memory as a minimum // 0 and -1 are special updateConfig for memory limit indicating unset and unlimited @@ -1420,7 +1386,7 @@ function repair(appId, data, auditSource, callback) { // create a new task instead of updating the old one, since it helps tracking addTask(appId, newState, { args, values, requiredState: null }, function (error, result) { - if (error && error.reason === AppsError.ALREADY_EXISTS) error = getDuplicateErrorDetails(error.message, locations, domainObjectMap, { /* portBindings */}); + if (error && error.reason === BoxError.ALREADY_EXISTS) error = getDuplicateErrorDetails(error.message, locations, domainObjectMap, { /* portBindings */}); if (error) return callback(error); eventlog.add(eventlog.ACTION_APP_REPAIR, auditSource, { taskId: result.taskId, app, newState }); @@ -1450,11 +1416,11 @@ function restore(appId, data, auditSource, callback) { var func = data.backupId ? backups.get.bind(null, data.backupId) : function (next) { return next(null, { manifest: app.manifest }); }; func(function (error, backupInfo) { - if (error && error.reason === BoxError.NOT_FOUND) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); - if (error && error.reason === BoxError.EXTERNAL_ERROR) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error && error.reason === BoxError.NOT_FOUND) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); + if (error && error.reason === BoxError.EXTERNAL_ERROR) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); - if (!backupInfo.manifest) callback(new AppsError(AppsError.EXTERNAL_ERROR, 'Could not get restore manifest')); + if (!backupInfo.manifest) callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Could not get restore manifest')); // re-validate because this new box version may not accept old configs error = checkManifestConstraints(backupInfo.manifest); @@ -1530,14 +1496,14 @@ function purchaseApp(data, callback) { appdb.del(data.appId, function (delError) { if (delError) debug('install: Failed to rollback app installation.', delError); - if (error.reason === AppstoreError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, error.message)); - if (error && error.reason === AppstoreError.PLAN_LIMIT) return callback(new AppsError(AppsError.PLAN_LIMIT, error.message)); - if (error && error.reason === AppstoreError.INVALID_TOKEN) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); - if (error && error.reason === AppstoreError.LICENSE_ERROR) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); - if (error && error.reason === AppstoreError.NOT_REGISTERED) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); - if (error && error.reason === AppstoreError.EXTERNAL_ERROR) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); + if (error.reason === AppstoreError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, error.message)); + if (error && error.reason === AppstoreError.PLAN_LIMIT) return callback(new BoxError(BoxError.PLAN_LIMIT, error.message)); + if (error && error.reason === AppstoreError.INVALID_TOKEN) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); + if (error && error.reason === AppstoreError.LICENSE_ERROR) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); + if (error && error.reason === AppstoreError.NOT_REGISTERED) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); + if (error && error.reason === AppstoreError.EXTERNAL_ERROR) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); - callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + callback(new BoxError(BoxError.DATABASE_ERROR, error)); }); }); } @@ -1567,11 +1533,11 @@ function clone(appId, data, user, auditSource, callback) { if (error) return callback(error); backups.get(backupId, function (error, backupInfo) { - if (error && error.reason === BoxError.EXTERNAL_ERROR) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); - if (error && error.reason === BoxError.NOT_FOUND) return callback(new AppsError(AppsError.EXTERNAL_ERROR, 'Backup not found')); - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error && error.reason === BoxError.EXTERNAL_ERROR) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); + if (error && error.reason === BoxError.NOT_FOUND) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Backup not found')); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); - if (!backupInfo.manifest) callback(new AppsError(AppsError.EXTERNAL_ERROR, 'Could not get restore config')); + if (!backupInfo.manifest) callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Could not get restore config')); const manifest = backupInfo.manifest, appStoreId = app.appStoreId; @@ -1584,7 +1550,7 @@ function clone(appId, data, user, auditSource, callback) { if (mailboxName) { error = mail.validateName(mailboxName); - if (error) return callback(new AppsError(AppsError.BAD_FIELD, error.message, { field: 'mailboxName' })); + if (error) return callback(new BoxError(BoxError.BAD_FIELD, error.message, { field: 'mailboxName' })); } else { mailboxName = mailboxNameForLocation(location, manifest); } @@ -1610,7 +1576,7 @@ function clone(appId, data, user, auditSource, callback) { appdb.add(newAppId, appStoreId, manifest, location, domain, translatePortBindings(portBindings, manifest), data, function (error) { if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(getDuplicateErrorDetails(error.message, locations, domainObjectMap, portBindings)); - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); purchaseApp({ appId: newAppId, appstoreId: app.appStoreId, manifestId: manifest.id }, function (error) { if (error) return callback(error); @@ -1652,11 +1618,11 @@ function uninstall(appId, auditSource, callback) { if (error) return callback(error); appstore.unpurchaseApp(appId, { appstoreId: app.appStoreId, manifestId: app.manifest.id }, function (error) { - if (error && error.reason === AppstoreError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND)); - if (error && error.reason === AppstoreError.INVALID_TOKEN) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); - if (error && error.reason === AppstoreError.LICENSE_ERROR) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); - if (error && error.reason === AppstoreError.EXTERNAL_ERROR) return callback(new AppsError(AppsError.EXTERNAL_ERROR, error.message)); - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error && error.reason === AppstoreError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error && error.reason === AppstoreError.INVALID_TOKEN) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); + if (error && error.reason === AppstoreError.LICENSE_ERROR) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); + if (error && error.reason === AppstoreError.EXTERNAL_ERROR) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error.message)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); const task = { args: {}, @@ -1725,16 +1691,16 @@ function stop(appId, callback) { function checkManifestConstraints(manifest) { assert(manifest && typeof manifest === 'object'); - if (manifest.manifestVersion > 2) return new AppsError(AppsError.BAD_FIELD, 'Manifest version must be <= 2'); + if (manifest.manifestVersion > 2) return new BoxError(BoxError.BAD_FIELD, 'Manifest version must be <= 2'); - if (!manifest.dockerImage) return new AppsError(AppsError.BAD_FIELD, 'Missing dockerImage'); // dockerImage is optional in manifest + if (!manifest.dockerImage) return new BoxError(BoxError.BAD_FIELD, 'Missing dockerImage'); // dockerImage is optional in manifest if (semver.valid(manifest.maxBoxVersion) && semver.gt(constants.VERSION, manifest.maxBoxVersion)) { - return new AppsError(AppsError.BAD_FIELD, 'Box version exceeds Apps maxBoxVersion'); + return new BoxError(BoxError.BAD_FIELD, 'Box version exceeds Apps maxBoxVersion'); } if (semver.valid(manifest.minBoxVersion) && semver.gt(manifest.minBoxVersion, constants.VERSION)) { - return new AppsError(AppsError.BAD_FIELD, 'App version requires a new platform version'); + return new BoxError(BoxError.BAD_FIELD, 'App version requires a new platform version'); } return null; @@ -1752,7 +1718,7 @@ function exec(appId, options, callback) { if (error) return callback(error); if (app.installationState !== exports.ISTATE_INSTALLED || app.runState !== exports.RSTATE_RUNNING) { - return callback(new AppsError(AppsError.BAD_STATE, 'App not installed or running')); + return callback(new BoxError(BoxError.BAD_STATE, 'App not installed or running')); } var container = docker.connection.getContainer(app.containerId); @@ -1770,9 +1736,9 @@ function exec(appId, options, callback) { }; container.exec(execOptions, function (error, exec) { - if (error && error.statusCode === 409) return callback(new AppsError(AppsError.BAD_STATE, error.message)); // container restarting/not running + if (error && error.statusCode === 409) return callback(new BoxError(BoxError.BAD_STATE, error.message)); // container restarting/not running + if (error) return callback(error); - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); var startOptions = { Detach: false, Tty: options.tty, @@ -1788,7 +1754,7 @@ function exec(appId, options, callback) { stderr: true }; exec.start(startOptions, function(error, stream /* in hijacked mode, this is a net.socket */) { - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error) return callback(error); if (options.rows && options.columns) { // there is a race where resizing too early results in a 404 "no such exec" @@ -1884,11 +1850,11 @@ function listBackups(page, perPage, appId, callback) { assert.strictEqual(typeof callback, 'function'); appdb.exists(appId, function (error, exists) { - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); - if (!exists) return callback(new AppsError(AppsError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); + if (!exists) return callback(new BoxError(BoxError.NOT_FOUND)); backups.getByAppIdPaged(page, perPage, appId, function (error, results) { - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error) return callback(error); callback(null, results); }); @@ -1899,7 +1865,8 @@ function restoreInstalledApps(callback) { assert.strictEqual(typeof callback, 'function'); getAll(function (error, apps) { - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error) return callback(error); + apps = apps.filter(app => app.installationState !== exports.ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand apps = apps.filter(app => app.installationState !== exports.ISTATE_PENDING_RESTORE); // safeguard against tasks being created non-stop if we crash on startup @@ -1938,7 +1905,8 @@ function configureInstalledApps(callback) { assert.strictEqual(typeof callback, 'function'); getAll(function (error, apps) { - if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); + if (error) return callback(error); + apps = apps.filter(app => app.installationState !== exports.ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand apps = apps.filter(app => app.installationState !== exports.ISTATE_PENDING_CONFIGURE); // safeguard against tasks being created non-stop if we crash on startup @@ -1998,7 +1966,7 @@ function downloadFile(appId, filePath, callback) { stream.on('data', function (d) { data += d; }); stream.on('end', function () { var parts = data.split('-'); - if (parts.length !== 2) return callback(new AppsError(AppsError.NOT_FOUND, 'file does not exist')); + if (parts.length !== 2) return callback(new BoxError(BoxError.NOT_FOUND, 'file does not exist')); var type = parts[0], filename, cmd, size; @@ -2006,13 +1974,13 @@ function downloadFile(appId, filePath, callback) { cmd = [ 'cat', filePath ]; size = parseInt(parts[1], 10); filename = path.basename(filePath); - if (isNaN(size)) return callback(new AppsError(AppsError.NOT_FOUND, 'file does not exist')); + if (isNaN(size)) return callback(new BoxError(BoxError.NOT_FOUND, 'file does not exist')); } else if (type === 'directory') { cmd = ['tar', 'zcf', '-', '-C', filePath, '.']; filename = path.basename(filePath) + '.tar.gz'; size = 0; // unknown } else { - return callback(new AppsError(AppsError.NOT_FOUND, 'only files or dirs can be downloaded')); + return callback(new BoxError(BoxError.NOT_FOUND, 'only files or dirs can be downloaded')); } exec(appId, { cmd: cmd , tty: false }, function (error, stream) { diff --git a/src/backups.js b/src/backups.js index d425cd577..c1365989a 100644 --- a/src/backups.js +++ b/src/backups.js @@ -39,7 +39,6 @@ exports = module.exports = { var addons = require('./addons.js'), apps = require('./apps.js'), - AppsError = require('./apps.js').AppsError, async = require('async'), assert = require('assert'), backupdb = require('./backupdb.js'), @@ -1170,7 +1169,7 @@ function cleanupSnapshots(backupConfig, callback) { delete info.box; async.eachSeries(Object.keys(info), function (appId, iteratorDone) { apps.get(appId, function (error /*, app */) { - if (!error || error.reason !== AppsError.NOT_FOUND) return iteratorDone(); + if (!error || error.reason !== BoxError.NOT_FOUND) return iteratorDone(); function done(/* ignoredError */) { safe.fs.unlinkSync(path.join(paths.BACKUP_INFO_DIR, `${appId}.sync.cache`)); diff --git a/src/dockerproxy.js b/src/dockerproxy.js index 9148faab9..e0aeaff2b 100644 --- a/src/dockerproxy.js +++ b/src/dockerproxy.js @@ -6,8 +6,8 @@ exports = module.exports = { }; var apps = require('./apps.js'), - AppsError = apps.AppsError, assert = require('assert'), + BoxError = require('./boxerror.js'), constants = require('./constants.js'), express = require('express'), debug = require('debug')('box:dockerproxy'), @@ -35,7 +35,7 @@ function authorizeApp(req, res, next) { } apps.getByIpAddress(req.connection.remoteAddress, function (error, app) { - if (error && error.reason === AppsError.NOT_FOUND) return next(new HttpError(401, 'Unauthorized')); + if (error && error.reason === BoxError.NOT_FOUND) return next(new HttpError(401, 'Unauthorized')); if (error) return next(new HttpError(500, error)); if (!('docker' in app.manifest.addons)) return next(new HttpError(401, 'Unauthorized')); @@ -67,6 +67,7 @@ function attachDockerRequest(req, res, next) { next(); } +// eslint-disable-next-line no-unused-vars function containersCreate(req, res, next) { safe.set(req.body, 'HostConfig.NetworkMode', 'cloudron'); // overwrite the network the container lives in safe.set(req.body, 'NetworkingConfig', {}); // drop any custom network configs @@ -97,6 +98,7 @@ function containersCreate(req, res, next) { req.dockerRequest.end(plainBody); } +// eslint-disable-next-line no-unused-vars function process(req, res, next) { // we have to rebuild the body since we consumed in in the parser if (Object.keys(req.body).length !== 0) { @@ -139,6 +141,7 @@ function start(callback) { debug(`startDockerProxy: started proxy on port ${constants.DOCKER_PROXY_PORT}`); + // eslint-disable-next-line no-unused-vars gHttpServer.on('upgrade', function (req, client, head) { // Create a new tcp connection to the TCP server var remote = net.connect('/var/run/docker.sock', function () { diff --git a/src/routes/apps.js b/src/routes/apps.js index c6ee76866..0915ddbe4 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -42,9 +42,9 @@ exports = module.exports = { }; var apps = require('../apps.js'), - AppsError = apps.AppsError, assert = require('assert'), auditSource = require('../auditsource.js'), + BoxError = require('../boxerror.js'), debug = require('debug')('box:routes/apps'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess, @@ -52,22 +52,22 @@ var apps = require('../apps.js'), util = require('util'), WebSocket = require('ws'); -function toHttpError(appError) { - switch (appError.reason) { - case AppsError.NOT_FOUND: - return new HttpError(404, appError); - case AppsError.ALREADY_EXISTS: - case AppsError.BAD_STATE: - return new HttpError(409, appError); - case AppsError.BAD_FIELD: - return new HttpError(400, appError); - case AppsError.PLAN_LIMIT: - return new HttpError(402, appError); - case AppsError.EXTERNAL_ERROR: - return new HttpError(424, appError); - case AppsError.INTERNAL_ERROR: +function toHttpError(error) { + switch (error.reason) { + case BoxError.NOT_FOUND: + return new HttpError(404, error); + case BoxError.ALREADY_EXISTS: + case BoxError.BAD_STATE: + return new HttpError(409, error); + case BoxError.BAD_FIELD: + return new HttpError(400, error); + case BoxError.PLAN_LIMIT: + return new HttpError(402, error); + case BoxError.EXTERNAL_ERROR: + return new HttpError(424, error); + case BoxError.INTERNAL_ERROR: default: - return new HttpError(500, appError); + return new HttpError(500, error); } } diff --git a/src/test/apps-test.js b/src/test/apps-test.js index 19027547b..81e5cec6a 100644 --- a/src/test/apps-test.js +++ b/src/test/apps-test.js @@ -7,8 +7,8 @@ var appdb = require('../appdb.js'), apps = require('../apps.js'), - AppsError = apps.AppsError, async = require('async'), + BoxError = require('../boxerror.js'), database = require('../database.js'), domains = require('../domains.js'), expect = require('expect.js'), @@ -217,7 +217,7 @@ describe('Apps', function () { it('cannot get invalid app', function (done) { apps.get('nope', function (error) { expect(error).to.be.ok(); - expect(error.reason).to.be(AppsError.NOT_FOUND); + expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); });