diff --git a/src/boxerror.js b/src/boxerror.js index 11dcfdd48..484515871 100644 --- a/src/boxerror.js +++ b/src/boxerror.js @@ -44,6 +44,7 @@ BoxError.DOCKER_ERROR = 'Docker Error'; BoxError.EXTERNAL_ERROR = 'External Error'; // use this for external API errors BoxError.FS_ERROR = 'FileSystem Error'; BoxError.INTERNAL_ERROR = 'Internal Error'; +BoxError.LICENSE_ERROR = 'License Error'; BoxError.LOGROTATE_ERROR = 'Logrotate Error'; BoxError.NETWORK_ERROR = 'Network Error'; BoxError.NGINX_ERROR = 'Nginx Error'; diff --git a/src/provision.js b/src/provision.js index 0ace74275..e008bde68 100644 --- a/src/provision.js +++ b/src/provision.js @@ -6,9 +6,7 @@ exports = module.exports = { activate: activate, getStatus: getStatus, - autoRegister: autoRegister, - - ProvisionError: ProvisionError + autoRegister: autoRegister }; var appstore = require('./appstore.js'), @@ -32,9 +30,7 @@ var appstore = require('./appstore.js'), superagent = require('superagent'), sysinfo = require('./sysinfo.js'), users = require('./users.js'), - UsersError = users.UsersError, tld = require('tldjs'), - util = require('util'), _ = require('underscore'); const NOOP_CALLBACK = function (error) { if (error) debug(error); }; @@ -53,33 +49,6 @@ let gProvisionStatus = { } }; -function ProvisionError(reason, errorOrMessage) { - assert.strictEqual(typeof reason, 'string'); - assert(errorOrMessage instanceof Error || typeof errorOrMessage === 'string' || typeof errorOrMessage === '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; - } -} -util.inherits(ProvisionError, Error); -ProvisionError.BAD_FIELD = 'Field error'; -ProvisionError.BAD_STATE = 'Bad State'; -ProvisionError.ALREADY_SETUP = 'Already Setup'; -ProvisionError.INTERNAL_ERROR = 'Internal Error'; -ProvisionError.EXTERNAL_ERROR = 'External Error'; -ProvisionError.LICENSE_ERROR = 'License Error'; -ProvisionError.ALREADY_PROVISIONED = 'Already Provisioned'; - function setProgress(task, message, callback) { debug(`setProgress: ${task} - ${message}`); gProvisionStatus[task].message = message; @@ -93,14 +62,14 @@ function autoRegister(domain, callback) { if (!fs.existsSync(paths.LICENSE_FILE)) return callback(); const license = safe.fs.readFileSync(paths.LICENSE_FILE, 'utf8'); - if (!license) return callback(new ProvisionError(ProvisionError.EXTERNAL_ERROR, 'Cannot read license')); + if (!license) return callback(new BoxError(BoxError.LICENSE_ERROR, 'Cannot read license')); debug('Auto-registering cloudron'); appstore.registerWithLicense(license.trim(), domain, function (error) { if (error && error.reason !== AppstoreError.ALREADY_REGISTERED) { debug('Failed to auto-register cloudron', error); - return callback(new ProvisionError(ProvisionError.LICENSE_ERROR, 'Failed to auto-register Cloudron with license. Please contact support@cloudron.io')); + return callback(new BoxError(BoxError.LICENSE_ERROR, 'Failed to auto-register Cloudron with license. Please contact support@cloudron.io')); } callback(); @@ -127,7 +96,7 @@ function setup(dnsConfig, backupConfig, auditSource, callback) { assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof callback, 'function'); - if (gProvisionStatus.setup.active || gProvisionStatus.restore.active) return callback(new ProvisionError(ProvisionError.BAD_STATE, 'Already setting up or restoring')); + if (gProvisionStatus.setup.active || gProvisionStatus.restore.active) return callback(new BoxError(BoxError.BAD_STATE, 'Already setting up or restoring')); gProvisionStatus.setup = { active: true, errorMessage: '', message: 'Adding domain' }; @@ -138,11 +107,11 @@ function setup(dnsConfig, backupConfig, auditSource, callback) { } users.isActivated(function (error, activated) { - if (error) return done(new ProvisionError(ProvisionError.INTERNAL_ERROR, error)); - if (activated) return done(new ProvisionError(ProvisionError.ALREADY_SETUP)); + if (error) return done(new BoxError(BoxError.DATABASE_ERROR, error)); + if (activated) return done(new BoxError(BoxError.CONFLICT, 'Already activated', { activate: true })); unprovision(function (error) { - if (error) return done(new ProvisionError(ProvisionError.INTERNAL_ERROR, error)); + if (error) return done(error); const domain = dnsConfig.domain.toLowerCase(); const zoneName = dnsConfig.zoneName ? dnsConfig.zoneName : (tld.getDomain(domain) || domain); @@ -158,9 +127,7 @@ function setup(dnsConfig, backupConfig, auditSource, callback) { }; domains.add(domain, data, auditSource, function (error) { - if (error && error.reason === BoxError.BAD_FIELD) return done(new ProvisionError(ProvisionError.BAD_FIELD, error.message)); - if (error && error.reason === BoxError.ALREADY_EXISTS) return done(new ProvisionError(ProvisionError.BAD_FIELD, error.message)); - if (error) return done(new ProvisionError(ProvisionError.INTERNAL_ERROR, error)); + if (error) return done(error); callback(); // now that args are validated run the task in the background @@ -220,12 +187,10 @@ function activate(username, password, email, displayName, ip, auditSource, callb setTimeZone(ip, function () { }); // TODO: get this from user. note that timezone is detected based on the browser location and not the cloudron region users.createOwner(username, password, email, displayName, auditSource, function (error, userObject) { - if (error && error.reason === UsersError.ALREADY_EXISTS) return callback(new ProvisionError(ProvisionError.ALREADY_PROVISIONED, 'Already activated')); - if (error && error.reason === UsersError.BAD_FIELD) return callback(new ProvisionError(ProvisionError.BAD_FIELD, error.message)); - if (error) return callback(new ProvisionError(ProvisionError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.CONFLICT, 'Already activated', { activated: true })); clients.addTokenByUserId('cid-webadmin', userObject.id, Date.now() + constants.DEFAULT_TOKEN_EXPIRATION, {}, function (error, result) { - if (error) return callback(new ProvisionError(ProvisionError.INTERNAL_ERROR, error)); + if (error) return callback(error); eventlog.add(eventlog.ACTION_ACTIVATE, auditSource, { }); @@ -247,10 +212,10 @@ function restore(backupConfig, backupId, version, auditSource, callback) { assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof callback, 'function'); - if (!semver.valid(version)) return callback(new ProvisionError(ProvisionError.BAD_STATE, 'version is not a valid semver')); - if (semver.major(constants.VERSION) !== semver.major(version) || semver.minor(constants.VERSION) !== semver.minor(version)) return callback(new ProvisionError(ProvisionError.BAD_STATE, `Run cloudron-setup with --version ${version} to restore from this backup`)); + if (!semver.valid(version)) return callback(new BoxError(BoxError.BAD_FIELD, 'version is not a valid semver', { field: 'version' })); + if (semver.major(constants.VERSION) !== semver.major(version) || semver.minor(constants.VERSION) !== semver.minor(version)) return callback(new BoxError(BoxError.BAD_STATE, `Run cloudron-setup with --version ${version} to restore from this backup`)); - if (gProvisionStatus.setup.active || gProvisionStatus.restore.active) return callback(new ProvisionError(ProvisionError.BAD_STATE, 'Already setting up or restoring')); + if (gProvisionStatus.setup.active || gProvisionStatus.restore.active) return callback(new BoxError(BoxError.BAD_STATE, 'Already setting up or restoring')); gProvisionStatus.restore = { active: true, errorMessage: '', message: 'Testing backup config' }; @@ -261,13 +226,11 @@ function restore(backupConfig, backupId, version, auditSource, callback) { } users.isActivated(function (error, activated) { - if (error) return done(new ProvisionError(ProvisionError.INTERNAL_ERROR, error)); - if (activated) return done(new ProvisionError(ProvisionError.ALREADY_PROVISIONED, 'Already activated. Restore with a fresh Cloudron installation.')); + if (error) return done(error); + if (activated) return done(new BoxError(BoxError.CONFLICT, 'Already activated. Restore with a fresh Cloudron installation.')); backups.testConfig(backupConfig, function (error) { - if (error && error.reason === BoxError.BAD_FIELD) return done(new ProvisionError(ProvisionError.BAD_FIELD, error.message)); - if (error && error.reason === BoxError.EXTERNAL_ERROR) return done(new ProvisionError(ProvisionError.EXTERNAL_ERROR, error.message)); - if (error) return done(new ProvisionError(ProvisionError.INTERNAL_ERROR, error)); + if (error) return done(error); debug(`restore: restoring from ${backupId} from provider ${backupConfig.provider} with format ${backupConfig.format}`); @@ -293,10 +256,10 @@ function getStatus(callback) { assert.strictEqual(typeof callback, 'function'); users.isActivated(function (error, activated) { - if (error) return callback(new ProvisionError(ProvisionError.INTERNAL_ERROR, error)); + if (error) return callback(error); settings.getCloudronName(function (error, cloudronName) { - if (error) return callback(new ProvisionError(ProvisionError.INTERNAL_ERROR, error)); + if (error) return callback(error); callback(null, _.extend({ version: constants.VERSION, diff --git a/src/routes/provision.js b/src/routes/provision.js index 0865eaad8..b8772ac3f 100644 --- a/src/routes/provision.js +++ b/src/routes/provision.js @@ -9,15 +9,31 @@ exports = module.exports = { }; var assert = require('assert'), - auditSource = require('../auditsource'), + auditSource = require('../auditsource.js'), + BoxError = require('../boxerror.js'), debug = require('debug')('box:routes/setup'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess, provision = require('../provision.js'), - ProvisionError = require('../provision.js').ProvisionError, sysinfo = require('../sysinfo.js'), superagent = require('superagent'); +function toHttpError(error) { + switch (error.reason) { + case BoxError.EXTERNAL_ERROR: + return new HttpError(424, error); + case BoxError.NOT_FOUND: + return new HttpError(404, error); + case BoxError.CONFLICT: + return new HttpError(409, error); + case BoxError.BAD_FIELD: + return new HttpError(400, error); + case BoxError.INTERNAL_ERROR: + default: + return new HttpError(500, error); + } +} + function providerTokenAuth(req, res, next) { assert.strictEqual(typeof req.body, 'object'); @@ -59,10 +75,7 @@ function setup(req, res, next) { req.clearTimeout(); provision.setup(dnsConfig, req.body.backupConfig || null, auditSource.fromRequest(req), function (error) { - if (error && error.reason === ProvisionError.ALREADY_SETUP) return next(new HttpError(409, error.message)); - if (error && error.reason === ProvisionError.BAD_FIELD) return next(new HttpError(400, error.message)); - if (error && error.reason === ProvisionError.BAD_STATE) return next(new HttpError(409, error.message)); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(200, {})); }); @@ -85,9 +98,7 @@ function activate(req, res, next) { debug('activate: username:%s ip:%s', username, ip); provision.activate(username, password, email, displayName, ip, auditSource.fromRequest(req), function (error, info) { - if (error && error.reason === ProvisionError.ALREADY_PROVISIONED) return next(new HttpError(409, 'Already setup')); - if (error && error.reason === ProvisionError.BAD_FIELD) return next(new HttpError(400, error.message)); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(201, info)); }); @@ -108,11 +119,7 @@ function restore(req, res, next) { if (typeof req.body.version !== 'string') return next(new HttpError(400, 'version must be a string')); provision.restore(backupConfig, req.body.backupId, req.body.version, auditSource.fromRequest(req), function (error) { - if (error && error.reason === ProvisionError.ALREADY_SETUP) return next(new HttpError(409, error.message)); - if (error && error.reason === ProvisionError.BAD_FIELD) return next(new HttpError(400, error.message)); - if (error && error.reason === ProvisionError.BAD_STATE) return next(new HttpError(409, error.message)); - if (error && error.reason === ProvisionError.EXTERNAL_ERROR) return next(new HttpError(424, error.message)); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(200, {})); }); @@ -120,7 +127,7 @@ function restore(req, res, next) { function getStatus(req, res, next) { provision.getStatus(function (error, status) { - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(200, status)); });