diff --git a/src/apps.js b/src/apps.js index ba3a67184..112aa1888 100644 --- a/src/apps.js +++ b/src/apps.js @@ -101,7 +101,6 @@ exports = module.exports = { var appdb = require('./appdb.js'), appstore = require('./appstore.js'), - AppstoreError = require('./appstore.js').AppstoreError, appTaskManager = require('./apptaskmanager.js'), assert = require('assert'), async = require('async'), @@ -1487,14 +1486,7 @@ 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 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 BoxError(BoxError.DATABASE_ERROR, error)); + callback(error); }); }); } @@ -1609,10 +1601,6 @@ 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 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(error); const task = { diff --git a/src/appstore.js b/src/appstore.js index 9dbce5d89..30096eacc 100644 --- a/src/appstore.js +++ b/src/appstore.js @@ -19,14 +19,13 @@ exports = module.exports = { getAppUpdate: getAppUpdate, getBoxUpdate: getBoxUpdate, - createTicket: createTicket, - - AppstoreError: AppstoreError + createTicket: createTicket }; var apps = require('./apps.js'), assert = require('assert'), async = require('async'), + BoxError = require('./boxerror.js'), constants = require('./constants.js'), custom = require('./custom.js'), debug = require('debug')('box:appstore'), @@ -43,44 +42,14 @@ var apps = require('./apps.js'), users = require('./users.js'), util = require('util'); -function AppstoreError(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(AppstoreError, Error); -AppstoreError.INTERNAL_ERROR = 'Internal Error'; -AppstoreError.EXTERNAL_ERROR = 'External Error'; -AppstoreError.ALREADY_EXISTS = 'Already Exists'; -AppstoreError.ACCESS_DENIED = 'Access Denied'; -AppstoreError.NOT_FOUND = 'Not Found'; -AppstoreError.PLAN_LIMIT = 'Plan limit reached'; // upstream 402 (subsciption_expired and subscription_required) -AppstoreError.LICENSE_ERROR = 'License Error'; // upstream 422 (no license, invalid license) -AppstoreError.INVALID_TOKEN = 'Invalid token'; // upstream 401 (invalid token) -AppstoreError.NOT_REGISTERED = 'Not registered'; // upstream 412 (no token, not set yet) -AppstoreError.ALREADY_REGISTERED = 'Already registered'; - -var NOOP_CALLBACK = function (error) { if (error) debug(error); }; +const NOOP_CALLBACK = function (error) { if (error) debug(error); }; function getCloudronToken(callback) { assert.strictEqual(typeof callback, 'function'); settings.getCloudronToken(function (error, token) { - if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error)); - if (!token) return callback(new AppstoreError(AppstoreError.NOT_REGISTERED)); + if (error) return callback(error); + if (!token) return callback(new BoxError(BoxError.LICENSE_ERROR, 'Missing token')); callback(null, token); }); @@ -100,9 +69,9 @@ function login(email, password, totpToken, callback) { const url = settings.apiServerOrigin() + '/api/v1/login'; superagent.post(url).send(data).timeout(30 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); - if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.ACCESS_DENIED)); - if (result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, `login status code: ${result.statusCode}`)); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message)); + if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + if (result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `login status code: ${result.statusCode}`)); callback(null, result.body); // { userId, accessToken } }); @@ -120,9 +89,9 @@ function registerUser(email, password, callback) { const url = settings.apiServerOrigin() + '/api/v1/register_user'; superagent.post(url).send(data).timeout(30 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); - if (result.statusCode === 409) return callback(new AppstoreError(AppstoreError.ALREADY_EXISTS)); - if (result.statusCode !== 201) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, `register status code: ${result.statusCode}`)); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message)); + if (result.statusCode === 409) return callback(new BoxError(BoxError.ALREADY_EXISTS)); + if (result.statusCode !== 201) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `register status code: ${result.statusCode}`)); callback(null); }); @@ -136,11 +105,11 @@ function getSubscription(callback) { const url = settings.apiServerOrigin() + '/api/v1/subscription'; superagent.get(url).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); - if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN)); - if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR)); - if (result.statusCode === 502) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, `Stripe error: ${error.message}`)); - if (result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, `Unknown error: ${error.message}`)); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message)); + if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR)); + if (result.statusCode === 502) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `Stripe error: ${error.message}`)); + if (result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `Unknown error: ${error.message}`)); callback(null, result.body); // { email, subscription } }); @@ -164,13 +133,13 @@ function purchaseApp(data, callback) { const url = `${settings.apiServerOrigin()}/api/v1/cloudronapps`; superagent.post(url).send(data).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); - if (result.statusCode === 404) return callback(new AppstoreError(AppstoreError.NOT_FOUND)); // appstoreId does not exist - if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN)); - if (result.statusCode === 402) return callback(new AppstoreError(AppstoreError.PLAN_LIMIT, result.body.message)); - if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message)); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message)); + if (result.statusCode === 404) return callback(new BoxError(BoxError.NOT_FOUND)); // appstoreId does not exist + if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + if (result.statusCode === 402) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message)); + if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message)); // 200 if already purchased, 201 is newly purchased - if (result.statusCode !== 201 && result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App purchase failed. %s %j', result.status, result.body))); + if (result.statusCode !== 201 && result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('App purchase failed. %s %j', result.status, result.body))); callback(null); }); @@ -189,16 +158,16 @@ function unpurchaseApp(appId, data, callback) { const url = `${settings.apiServerOrigin()}/api/v1/cloudronapps/${appId}`; superagent.get(url).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message)); if (result.statusCode === 404) return callback(null); // was never purchased - if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN)); - if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message)); - if (result.statusCode !== 201 && result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App unpurchase failed. %s %j', result.status, result.body))); + if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message)); + if (result.statusCode !== 201 && result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('App unpurchase failed. %s %j', result.status, result.body))); superagent.del(url).send(data).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error)); - if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN)); - if (result.statusCode !== 204) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App unpurchase failed. %s %j', result.status, result.body))); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error)); + if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + if (result.statusCode !== 204) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('App unpurchase failed. %s %j', result.status, result.body))); callback(null); }); @@ -214,42 +183,42 @@ function sendAliveStatus(callback) { async.series([ function (callback) { settings.getAll(function (error, result) { - if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error)); + if (error) return callback(error); allSettings = result; callback(); }); }, function (callback) { domains.getAll(function (error, result) { - if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error)); + if (error) return callback(error); allDomains = result; callback(); }); }, function (callback) { mail.getDomains(function (error, result) { - if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error)); + if (error) return callback(error); mailDomains = result; callback(); }); }, function (callback) { eventlog.getAllPaged([ eventlog.ACTION_USER_LOGIN ], null, 1, 1, function (error, result) { - if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error)); + if (error) return callback(error); loginEvents = result; callback(); }); }, function (callback) { users.count(function (error, result) { - if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error)); + if (error) return callback(error); userCount = result; callback(); }); }, function (callback) { groups.count(function (error, result) { - if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error)); + if (error) return callback(error); groupCount = result; callback(); }); @@ -298,11 +267,11 @@ function sendAliveStatus(callback) { const url = `${settings.apiServerOrigin()}/api/v1/alive`; superagent.post(url).send(data).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error)); - if (result.statusCode === 404) return callback(new AppstoreError(AppstoreError.NOT_FOUND)); - if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN)); - if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message)); - if (result.statusCode !== 201) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Sending alive status failed. %s %j', result.status, result.body))); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error)); + if (result.statusCode === 404) return callback(new BoxError(BoxError.NOT_FOUND)); + if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message)); + if (result.statusCode !== 201) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Sending alive status failed. %s %j', result.status, result.body))); callback(null); }); @@ -319,25 +288,25 @@ function getBoxUpdate(callback) { const url = `${settings.apiServerOrigin()}/api/v1/boxupdate`; superagent.get(url).query({ accessToken: token, boxVersion: constants.VERSION }).timeout(10 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); - if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN)); - if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message)); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message)); + if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message)); if (result.statusCode === 204) return callback(null); // no update - if (result.statusCode !== 200 || !result.body) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text))); + if (result.statusCode !== 200 || !result.body) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text))); var updateInfo = result.body; if (!semver.valid(updateInfo.version) || semver.gt(constants.VERSION, updateInfo.version)) { - return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Invalid update version: %s %s', result.statusCode, result.text))); + return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Invalid update version: %s %s', result.statusCode, result.text))); } // updateInfo: { version, changelog, sourceTarballUrl, sourceTarballSigUrl, boxVersionsUrl, boxVersionsSigUrl } - if (!updateInfo.version || typeof updateInfo.version !== 'string') return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad version): %s %s', result.statusCode, result.text))); - if (!updateInfo.changelog || !Array.isArray(updateInfo.changelog)) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad version): %s %s', result.statusCode, result.text))); - if (!updateInfo.sourceTarballUrl || typeof updateInfo.sourceTarballUrl !== 'string') return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad sourceTarballUrl): %s %s', result.statusCode, result.text))); - if (!updateInfo.sourceTarballSigUrl || typeof updateInfo.sourceTarballSigUrl !== 'string') return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad sourceTarballSigUrl): %s %s', result.statusCode, result.text))); - if (!updateInfo.boxVersionsUrl || typeof updateInfo.boxVersionsUrl !== 'string') return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad boxVersionsUrl): %s %s', result.statusCode, result.text))); - if (!updateInfo.boxVersionsSigUrl || typeof updateInfo.boxVersionsSigUrl !== 'string') return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response (bad boxVersionsSigUrl): %s %s', result.statusCode, result.text))); + if (!updateInfo.version || typeof updateInfo.version !== 'string') return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad version): %s %s', result.statusCode, result.text))); + if (!updateInfo.changelog || !Array.isArray(updateInfo.changelog)) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad version): %s %s', result.statusCode, result.text))); + if (!updateInfo.sourceTarballUrl || typeof updateInfo.sourceTarballUrl !== 'string') return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad sourceTarballUrl): %s %s', result.statusCode, result.text))); + if (!updateInfo.sourceTarballSigUrl || typeof updateInfo.sourceTarballSigUrl !== 'string') return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad sourceTarballSigUrl): %s %s', result.statusCode, result.text))); + if (!updateInfo.boxVersionsUrl || typeof updateInfo.boxVersionsUrl !== 'string') return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad boxVersionsUrl): %s %s', result.statusCode, result.text))); + if (!updateInfo.boxVersionsSigUrl || typeof updateInfo.boxVersionsSigUrl !== 'string') return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response (bad boxVersionsSigUrl): %s %s', result.statusCode, result.text))); callback(null, updateInfo); }); @@ -354,11 +323,11 @@ function getAppUpdate(app, callback) { const url = `${settings.apiServerOrigin()}/api/v1/appupdate`; superagent.get(url).query({ accessToken: token, boxVersion: constants.VERSION, appId: app.appStoreId, appVersion: app.manifest.version }).timeout(10 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error)); - if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN)); - if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message)); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error)); + if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message)); if (result.statusCode === 204) return callback(null); // no update - if (result.statusCode !== 200 || !result.body) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text))); + if (result.statusCode !== 200 || !result.body) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text))); const updateInfo = result.body; @@ -368,7 +337,7 @@ function getAppUpdate(app, callback) { // do some sanity checks if (!safe.query(updateInfo, 'manifest.version') || semver.gt(curAppVersion, safe.query(updateInfo, 'manifest.version'))) { debug('Skipping malformed update of app %s version: %s. got %j', app.id, curAppVersion, updateInfo); - return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Malformed update: %s %s', result.statusCode, result.text))); + return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Malformed update: %s %s', result.statusCode, result.text))); } // { id, creationDate, manifest } @@ -384,20 +353,20 @@ function registerCloudron(data, callback) { const url = `${settings.apiServerOrigin()}/api/v1/register_cloudron`; superagent.post(url).send(data).timeout(30 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); - if (result.statusCode !== 201) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, `Unable to register cloudron: ${error.message}`)); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message)); + if (result.statusCode !== 201) return callback(new BoxError(BoxError.EXTERNAL_ERROR, `Unable to register cloudron: ${error.message}`)); // cloudronId, token, licenseKey - if (!result.body.cloudronId) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, 'Invalid response - no cloudron id')); - if (!result.body.cloudronToken) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, 'Invalid response - no token')); - if (!result.body.licenseKey) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, 'Invalid response - no license')); + if (!result.body.cloudronId) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Invalid response - no cloudron id')); + if (!result.body.cloudronToken) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Invalid response - no token')); + if (!result.body.licenseKey) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Invalid response - no license')); async.series([ settings.setCloudronId.bind(null, result.body.cloudronId), settings.setCloudronToken.bind(null, result.body.cloudronToken), settings.setLicenseKey.bind(null, result.body.licenseKey), ], function (error) { - if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error)); + if (error) return callback(error); debug(`registerCloudron: Cloudron registered with id ${result.body.cloudronId}`); @@ -412,7 +381,7 @@ function registerWithLicense(license, domain, callback) { assert.strictEqual(typeof callback, 'function'); getCloudronToken(function (error, token) { - if (token) return callback(new AppstoreError(AppstoreError.ALREADY_REGISTERED)); + if (token) return callback(new BoxError(BoxError.CONFLICT)); registerCloudron({ license, domain }, callback); }); @@ -429,7 +398,7 @@ function registerWithLoginCredentials(options, callback) { } getCloudronToken(function (error, token) { - if (token) return callback(new AppstoreError(AppstoreError.ALREADY_REGISTERED)); + if (token) return callback(new BoxError(BoxError.CONFLICT)); maybeSignup(function (error) { if (error) return callback(error); @@ -469,10 +438,10 @@ function createTicket(info, callback) { info.supportEmail = custom.spec().support.email; // destination address for tickets superagent.post(url).query({ accessToken: token }).send(info).timeout(10 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); - if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN)); - if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message)); - if (result.statusCode !== 201) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text))); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message)); + if (result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message)); + if (result.statusCode !== 201) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text))); callback(null); }); @@ -487,14 +456,15 @@ function getApps(callback) { if (error) return callback(error); settings.getUnstableAppsConfig(function (error, unstable) { - if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error)); + if (error) return callback(error); + const url = `${settings.apiServerOrigin()}/api/v1/apps`; superagent.get(url).query({ accessToken: token, boxVersion: constants.VERSION, unstable: unstable }).timeout(10 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); - if (result.statusCode === 403 || result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN)); - if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message)); - if (result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App listing failed. %s %j', result.status, result.body))); - if (!result.body.apps) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text))); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message)); + if (result.statusCode === 403 || result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message)); + if (result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('App listing failed. %s %j', result.status, result.body))); + if (!result.body.apps) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text))); callback(null, result.body.apps); }); @@ -514,11 +484,11 @@ function getAppVersion(appId, version, callback) { if (version !== 'latest') url += `/versions/${version}`; superagent.get(url).query({ accessToken: token }).timeout(10 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); - if (result.statusCode === 403 || result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN)); - if (result.statusCode === 404) return callback(new AppstoreError(AppstoreError.NOT_FOUND)); - if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message)); - if (result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App fetch failed. %s %j', result.status, result.body))); + if (error && !error.response) return callback(new BoxError(BoxError.NETWORK_ERROR, error.message)); + if (result.statusCode === 403 || result.statusCode === 401) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); + if (result.statusCode === 404) return callback(new BoxError(BoxError.NOT_FOUND)); + if (result.statusCode === 422) return callback(new BoxError(BoxError.LICENSE_ERROR, result.body.message)); + if (result.statusCode !== 200) return callback(new BoxError(BoxError.EXTERNAL_ERROR, util.format('App fetch failed. %s %j', result.status, result.body))); callback(null, result.body); }); diff --git a/src/provision.js b/src/provision.js index e008bde68..edbddcb83 100644 --- a/src/provision.js +++ b/src/provision.js @@ -10,7 +10,6 @@ exports = module.exports = { }; var appstore = require('./appstore.js'), - AppstoreError = require('./appstore.js').AppstoreError, assert = require('assert'), async = require('async'), backups = require('./backups.js'), @@ -67,7 +66,7 @@ function autoRegister(domain, callback) { debug('Auto-registering cloudron'); appstore.registerWithLicense(license.trim(), domain, function (error) { - if (error && error.reason !== AppstoreError.ALREADY_REGISTERED) { + if (error && error.reason !== BoxError.CONFLICT) { // not already registered debug('Failed to auto-register cloudron', error); return callback(new BoxError(BoxError.LICENSE_ERROR, 'Failed to auto-register Cloudron with license. Please contact support@cloudron.io')); } diff --git a/src/routes/apps.js b/src/routes/apps.js index 0915ddbe4..26a698acd 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -61,9 +61,11 @@ function toHttpError(error) { return new HttpError(409, error); case BoxError.BAD_FIELD: return new HttpError(400, error); - case BoxError.PLAN_LIMIT: + case BoxError.LICENSE_ERROR: return new HttpError(402, error); case BoxError.EXTERNAL_ERROR: + case BoxError.NETWORK_ERROR: + case BoxError.FS_ERROR: return new HttpError(424, error); case BoxError.INTERNAL_ERROR: default: diff --git a/src/routes/appstore.js b/src/routes/appstore.js index 96fdef869..15d89ef7b 100644 --- a/src/routes/appstore.js +++ b/src/routes/appstore.js @@ -10,12 +10,34 @@ exports = module.exports = { }; var appstore = require('../appstore.js'), - AppstoreError = appstore.AppstoreError, assert = require('assert'), + BoxError = require('../boxerror.js'), custom = require('../custom.js'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess; +function toHttpError(error) { + switch (error.reason) { + case BoxError.LICENSE_ERROR: + case BoxError.INVALID_CREDENTIALS: + return new HttpError(402, error); + case BoxError.NOT_FOUND: + return new HttpError(404, error); + case BoxError.ALREADY_EXISTS: + case BoxError.CONFLICT: + return new HttpError(409, error); + case BoxError.ACCESS_DENIED: + return new HttpError(412, error); + case BoxError.EXTERNAL_ERROR: + case BoxError.NETWORK_ERROR: + case BoxError.FS_ERROR: + return new HttpError(424, error); + case BoxError.INTERNAL_ERROR: + default: + return new HttpError(500, error); + } +} + function isAppAllowed(appstoreId) { if (custom.spec().appstore.blacklist.includes(appstoreId)) return false; @@ -27,10 +49,7 @@ function isAppAllowed(appstoreId) { function getApps(req, res, next) { appstore.getApps(function (error, apps) { - if (error && error.reason === AppstoreError.INVALID_TOKEN) return next(new HttpError(402, error.message)); - if (error && error.reason === AppstoreError.LICENSE_ERROR) return next(new HttpError(402, error.message)); - if (error && error.reason === AppstoreError.NOT_REGISTERED) return next(new HttpError(412, error.message)); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); let filteredApps = apps.filter((app) => !custom.spec().appstore.blacklist.includes(app.id)); if (custom.spec().appstore.whitelist) filteredApps = filteredApps.filter((app) => app.id in custom.spec().appstore.whitelist); @@ -45,11 +64,7 @@ function getApp(req, res, next) { if (!isAppAllowed(req.params.appstoreId)) return next(new HttpError(405, 'feature disabled by admin')); appstore.getApp(req.params.appstoreId, function (error, app) { - if (error && error.reason === AppstoreError.NOT_FOUND) return next(new HttpError(404, 'No such app')); - if (error && error.reason === AppstoreError.INVALID_TOKEN) return next(new HttpError(402, error.message)); - if (error && error.reason === AppstoreError.LICENSE_ERROR) return next(new HttpError(402, error.message)); - if (error && error.reason === AppstoreError.NOT_REGISTERED) return next(new HttpError(412, error.message)); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(200, app)); }); @@ -62,11 +77,7 @@ function getAppVersion(req, res, next) { if (!isAppAllowed(req.params.appstoreId)) return next(new HttpError(405, 'feature disabled by admin')); appstore.getAppVersion(req.params.appstoreId, req.params.versionId, function (error, manifest) { - if (error && error.reason === AppstoreError.NOT_FOUND) return next(new HttpError(404, 'No such app or version')); - if (error && error.reason === AppstoreError.INVALID_TOKEN) return next(new HttpError(402, error.message)); - if (error && error.reason === AppstoreError.LICENSE_ERROR) return next(new HttpError(402, error.message)); - if (error && error.reason === AppstoreError.NOT_REGISTERED) return next(new HttpError(412, error.message)); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(200, manifest)); }); @@ -81,11 +92,7 @@ function registerCloudron(req, res, next) { if (typeof req.body.signup !== 'boolean') return next(new HttpError(400, 'signup must be a boolean')); appstore.registerWithLoginCredentials(req.body, function (error) { - if (error && error.reason === AppstoreError.ALREADY_EXISTS) return next(new HttpError(409, error.message)); - if (error && error.reason === AppstoreError.ACCESS_DENIED) return next(new HttpError(412, error.message)); - if (error && error.reason === AppstoreError.ALREADY_REGISTERED) return next(new HttpError(422, error.message)); - if (error && error.reason === AppstoreError.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(201, {})); }); @@ -95,10 +102,7 @@ function getSubscription(req, res, next) { assert.strictEqual(typeof req.body, 'object'); appstore.getSubscription(function (error, result) { - if (error && error.reason === AppstoreError.INVALID_TOKEN) return next(new HttpError(402, error.message)); - if (error && error.reason === AppstoreError.NOT_REGISTERED) return next(new HttpError(412, error.message)); - if (error && error.reason === AppstoreError.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, result)); // { email, cloudronId, plan, cancel_at, status } }); diff --git a/src/routes/test/appstore-test.js b/src/routes/test/appstore-test.js index 40970069b..451b96d98 100644 --- a/src/routes/test/appstore-test.js +++ b/src/routes/test/appstore-test.js @@ -63,7 +63,7 @@ describe('Appstore Apps API', function () { superagent.get(SERVER_URL + '/api/v1/appstore/apps') .query({ access_token: token }) .end(function (error, result) { - expect(result.statusCode).to.equal(412); // not registered yet + expect(result.statusCode).to.equal(402); // not registered yet; invalid credentials done(); }); }); @@ -72,7 +72,7 @@ describe('Appstore Apps API', function () { superagent.get(SERVER_URL + '/api/v1/appstore/apps/org.wordpress.cloudronapp') .query({ access_token: token }) .end(function (error, result) { - expect(result.statusCode).to.equal(412); // not registered yet + expect(result.statusCode).to.equal(402); // not registered yet; invalid credentials done(); }); }); @@ -170,7 +170,7 @@ describe('Subscription API - no signup', function () { .send({ email: 'test@cloudron.io', password: 'secret', signup: false }) .query({ access_token: token }) .end(function (error, result) { - expect(result.statusCode).to.equal(422); + expect(result.statusCode).to.equal(409); done(); }); }); diff --git a/src/test/appstore-test.js b/src/test/appstore-test.js index f1efe9ce6..b48f553ab 100644 --- a/src/test/appstore-test.js +++ b/src/test/appstore-test.js @@ -9,7 +9,7 @@ var async = require('async'), appstore = require('../appstore.js'), - AppstoreError = appstore.AppstoreError, + BoxError = require('../boxerror.js'), database = require('../database.js'), expect = require('expect.js'), nock = require('nock'), @@ -52,7 +52,7 @@ describe('Appstore', function () { it('cannot send alive status without registering', function (done) { appstore.sendAliveStatus(function (error) { expect(error).to.be.ok(); - expect(error.reason).to.equal(AppstoreError.NOT_REGISTERED); + expect(error.reason).to.equal(BoxError.LICENSE_ERROR); // missing token done(); }); });