Files
cloudron-box/src/appstore.js
T

514 lines
25 KiB
JavaScript
Raw Normal View History

2017-04-13 00:42:44 -07:00
'use strict';
exports = module.exports = {
2019-05-04 11:45:03 -07:00
getApps: getApps,
getApp: getApp,
getAppVersion: getAppVersion,
2019-05-07 14:16:51 -07:00
registerWithLoginCredentials: registerWithLoginCredentials,
2019-05-07 14:15:35 -07:00
registerWithLicense: registerWithLicense,
2019-05-05 13:00:45 -07:00
2019-05-06 14:29:56 -07:00
purchaseApp: purchaseApp,
unpurchaseApp: unpurchaseApp,
2017-04-13 00:52:02 -07:00
2017-06-21 22:17:32 -07:00
getSubscription: getSubscription,
isFreePlan: isFreePlan,
2017-06-21 22:17:32 -07:00
2017-04-13 01:11:20 -07:00
sendAliveStatus: sendAliveStatus,
2017-04-13 01:23:11 -07:00
getAppUpdate: getAppUpdate,
2017-04-13 01:31:22 -07:00
getBoxUpdate: getBoxUpdate,
2017-04-13 01:23:11 -07:00
2019-05-07 11:36:08 -07:00
createTicket: createTicket,
2017-11-14 20:34:25 -08:00
2017-04-13 01:11:20 -07:00
AppstoreError: AppstoreError
2017-04-13 00:42:44 -07:00
};
var apps = require('./apps.js'),
2018-03-05 17:03:54 +01:00
assert = require('assert'),
async = require('async'),
2017-04-13 00:42:44 -07:00
config = require('./config.js'),
2019-05-07 11:36:08 -07:00
custom = require('./custom.js'),
2017-04-13 00:52:02 -07:00
debug = require('debug')('box:appstore'),
domains = require('./domains.js'),
eventlog = require('./eventlog.js'),
mail = require('./mail.js'),
2017-04-13 01:07:07 -07:00
os = require('os'),
2018-01-25 09:35:06 -08:00
safe = require('safetydance'),
semver = require('semver'),
2017-04-13 00:42:44 -07:00
settings = require('./settings.js'),
superagent = require('superagent'),
util = require('util');
2017-04-13 01:07:07 -07:00
function AppstoreError(reason, errorOrMessage) {
assert.strictEqual(typeof reason, 'string');
assert(errorOrMessage instanceof Error || typeof errorOrMessage === 'string' || typeof errorOrMessage === 'undefined');
2017-04-13 00:42:44 -07:00
2017-04-13 01:07:07 -07:00
Error.call(this);
Error.captureStackTrace(this, this.constructor);
2017-04-13 00:42:44 -07:00
2017-04-13 01:07:07 -07:00
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;
2017-04-13 00:42:44 -07:00
}
2017-04-13 01:07:07 -07:00
}
util.inherits(AppstoreError, Error);
AppstoreError.INTERNAL_ERROR = 'Internal Error';
AppstoreError.EXTERNAL_ERROR = 'External Error';
2019-05-03 16:27:47 -07:00
AppstoreError.ALREADY_EXISTS = 'Already Exists';
AppstoreError.ACCESS_DENIED = 'Access Denied';
2019-05-06 02:45:16 -07:00
AppstoreError.NOT_FOUND = 'Not Found';
2019-05-06 15:00:22 -07:00
AppstoreError.PLAN_LIMIT = 'Plan limit reached'; // upstream 402 (subsciption_expired and subscription_required)
2019-05-05 10:31:42 -07:00
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)
2019-05-06 19:46:42 -07:00
AppstoreError.ALREADY_REGISTERED = 'Already registered';
2017-04-13 00:42:44 -07:00
2017-04-13 01:07:07 -07:00
var NOOP_CALLBACK = function (error) { if (error) debug(error); };
2019-05-03 16:01:20 -07:00
function getCloudronToken(callback) {
2019-04-30 23:35:49 -07:00
assert.strictEqual(typeof callback, 'function');
2019-05-03 16:01:20 -07:00
settings.getCloudronToken(function (error, token) {
2019-04-30 23:35:49 -07:00
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
if (!token) return callback(new AppstoreError(AppstoreError.NOT_REGISTERED));
2019-04-30 23:35:49 -07:00
callback(null, token);
});
}
2019-05-03 16:27:47 -07:00
function login(email, password, totpToken, callback) {
assert.strictEqual(typeof email, 'string');
assert.strictEqual(typeof password, 'string');
assert.strictEqual(typeof totpToken, 'string');
assert.strictEqual(typeof callback, 'function');
var data = {
email: email,
password: password,
totpToken: totpToken
};
const url = config.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}`));
callback(null, result.body); // { userId, accessToken }
});
}
function registerUser(email, password, callback) {
assert.strictEqual(typeof email, 'string');
assert.strictEqual(typeof password, 'string');
assert.strictEqual(typeof callback, 'function');
var data = {
email: email,
password: password,
};
const url = config.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));
2019-05-03 20:25:44 -07:00
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}`));
2019-05-03 16:27:47 -07:00
callback(null);
});
}
2017-06-21 22:17:32 -07:00
function getSubscription(callback) {
assert.strictEqual(typeof callback, 'function');
2019-05-03 20:17:40 -07:00
getCloudronToken(function (error, token) {
2017-06-21 22:17:32 -07:00
if (error) return callback(error);
2019-05-03 20:17:40 -07:00
const url = config.apiServerOrigin() + '/api/v1/subscription';
superagent.get(url).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) {
2017-06-21 22:17:32 -07:00
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
2019-05-06 15:00:22 -07:00
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}`));
2017-06-21 22:17:32 -07:00
2019-05-05 10:31:42 -07:00
callback(null, result.body); // { email, subscription }
2017-06-21 22:17:32 -07:00
});
});
}
function isFreePlan(subscription) {
return !subscription || subscription.plan.id === 'free';
}
// See app.js install it will create a db record first but remove it again if appstore purchase fails
2019-05-06 14:29:56 -07:00
function purchaseApp(data, callback) {
2019-05-03 14:00:21 -07:00
assert.strictEqual(typeof data, 'object'); // { appstoreId, manifestId, appId }
2018-10-18 14:15:18 -07:00
assert(data.appstoreId || data.manifestId);
2019-05-03 14:00:21 -07:00
assert.strictEqual(typeof data.appId, 'string');
2017-04-13 01:07:07 -07:00
assert.strictEqual(typeof callback, 'function');
2019-05-03 16:01:20 -07:00
getCloudronToken(function (error, token) {
2017-04-13 01:07:07 -07:00
if (error) return callback(error);
2019-05-03 14:00:21 -07:00
const url = `${config.apiServerOrigin()}/api/v1/cloudronapps`;
2018-05-26 18:26:17 +02:00
2019-05-03 14:00:21 -07:00
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));
2019-05-06 15:00:22 -07:00
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));
2019-05-05 10:31:42 -07:00
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
2019-05-06 15:00:22 -07:00
// 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)));
2017-04-13 01:07:07 -07:00
callback(null);
2017-04-13 01:07:07 -07:00
});
});
}
2019-05-06 14:29:56 -07:00
function unpurchaseApp(appId, data, callback) {
2017-04-13 00:42:44 -07:00
assert.strictEqual(typeof appId, 'string');
2019-04-30 23:35:49 -07:00
assert.strictEqual(typeof data, 'object'); // { appstoreId, manifestId }
2018-10-18 14:15:18 -07:00
assert(data.appstoreId || data.manifestId);
2017-04-13 00:42:44 -07:00
assert.strictEqual(typeof callback, 'function');
2019-05-03 16:01:20 -07:00
getCloudronToken(function (error, token) {
2017-04-13 01:07:07 -07:00
if (error) return callback(error);
2017-04-13 00:42:44 -07:00
2019-05-03 14:00:21 -07:00
const url = `${config.apiServerOrigin()}/api/v1/cloudronapps/${appId}`;
2017-04-13 00:42:44 -07:00
2019-05-03 14:00:21 -07:00
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));
2017-04-13 00:42:44 -07:00
if (result.statusCode === 404) return callback(null); // was never purchased
2019-05-06 15:00:22 -07:00
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
2019-05-05 10:31:42 -07:00
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
2018-02-18 22:43:11 -08:00
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)));
2017-04-13 00:42:44 -07:00
2019-05-03 14:00:21 -07:00
superagent.del(url).send(data).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) {
2017-04-13 01:07:07 -07:00
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error));
2019-05-06 15:00:22 -07:00
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
2017-04-13 01:07:07 -07:00
if (result.statusCode !== 204) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App unpurchase failed. %s %j', result.status, result.body)));
2017-04-13 00:42:44 -07:00
callback(null);
});
});
2017-04-13 01:07:07 -07:00
});
2017-04-13 00:42:44 -07:00
}
2017-04-13 00:52:02 -07:00
function sendAliveStatus(callback) {
2017-04-13 01:07:07 -07:00
callback = callback || NOOP_CALLBACK;
var allSettings, allDomains, mailDomains, loginEvents;
async.series([
function (callback) {
settings.getAll(function (error, result) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
allSettings = result;
callback();
});
},
function (callback) {
domains.getAll(function (error, result) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
allDomains = result;
callback();
});
},
function (callback) {
2018-04-03 14:37:52 -07:00
mail.getDomains(function (error, result) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
mailDomains = result;
callback();
});
},
function (callback) {
2018-03-05 17:53:18 +01:00
eventlog.getAllPaged([ eventlog.ACTION_USER_LOGIN ], null, 1, 1, function (error, result) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
loginEvents = result;
callback();
});
}
], function (error) {
if (error) return callback(error);
var backendSettings = {
backupConfig: {
provider: allSettings[settings.BACKUP_CONFIG_KEY].provider,
hardlinks: !allSettings[settings.BACKUP_CONFIG_KEY].noHardlinks
},
domainConfig: {
count: allDomains.length,
domains: Array.from(new Set(allDomains.map(function (d) { return { domain: d.domain, provider: d.provider }; })))
},
mailConfig: {
outboundCount: mailDomains.length,
inboundCount: mailDomains.filter(function (d) { return d.enabled; }).length,
catchAllCount: mailDomains.filter(function (d) { return d.catchAll.length !== 0; }).length,
relayProviders: Array.from(new Set(mailDomains.map(function (d) { return d.relay.provider; })))
},
appAutoupdatePattern: allSettings[settings.APP_AUTOUPDATE_PATTERN_KEY],
boxAutoupdatePattern: allSettings[settings.BOX_AUTOUPDATE_PATTERN_KEY],
timeZone: allSettings[settings.TIME_ZONE_KEY],
};
var data = {
version: config.version(),
adminFqdn: config.adminFqdn(),
provider: config.provider(),
backendSettings: backendSettings,
machine: {
cpus: os.cpus(),
totalmem: os.totalmem()
},
events: {
lastLogin: loginEvents[0] ? (new Date(loginEvents[0].creationTime).getTime()) : 0
}
};
2019-05-03 16:01:20 -07:00
getCloudronToken(function (error, token) {
if (error) return callback(error);
2019-04-30 23:35:49 -07:00
const url = `${config.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));
2019-05-06 15:00:22 -07:00
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
2019-05-05 10:31:42 -07:00
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)));
callback(null);
2017-04-13 01:07:07 -07:00
});
2017-04-13 00:52:02 -07:00
});
2017-04-13 01:07:07 -07:00
});
2017-04-13 00:52:02 -07:00
}
2017-04-13 01:23:11 -07:00
2017-04-13 01:31:22 -07:00
function getBoxUpdate(callback) {
assert.strictEqual(typeof callback, 'function');
2019-05-03 16:01:20 -07:00
getCloudronToken(function (error, token) {
2017-04-13 01:31:22 -07:00
if (error) return callback(error);
2019-04-30 23:06:37 -07:00
const url = `${config.apiServerOrigin()}/api/v1/boxupdate`;
2017-04-13 01:31:22 -07:00
2019-04-30 23:06:37 -07:00
superagent.get(url).query({ accessToken: token, boxVersion: config.version() }).timeout(10 * 1000).end(function (error, result) {
2018-07-31 10:46:35 -07:00
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
2019-05-06 15:00:22 -07:00
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
2019-05-05 10:31:42 -07:00
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
2017-04-13 01:31:22 -07:00
if (result.statusCode === 204) return callback(null); // no update
2018-01-25 09:35:06 -08:00
if (result.statusCode !== 200 || !result.body) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
var updateInfo = result.body;
if (!semver.valid(updateInfo.version) || semver.gt(config.version(), updateInfo.version)) {
2018-08-03 09:46:22 -07:00
return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Invalid update version: %s %s', result.statusCode, result.text)));
2018-01-25 09:35:06 -08:00
}
2017-04-13 01:31:22 -07:00
2019-02-20 16:18:47 -08:00
// 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)));
2018-01-25 09:35:06 -08:00
callback(null, updateInfo);
2017-04-13 01:31:22 -07:00
});
});
}
2017-04-13 01:23:11 -07:00
function getAppUpdate(app, callback) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof callback, 'function');
2019-05-03 16:01:20 -07:00
getCloudronToken(function (error, token) {
2017-04-13 01:23:11 -07:00
if (error) return callback(error);
2019-04-30 23:06:37 -07:00
const url = `${config.apiServerOrigin()}/api/v1/appupdate`;
2017-04-13 01:23:11 -07:00
2019-04-30 23:06:37 -07:00
superagent.get(url).query({ accessToken: token, boxVersion: config.version(), appId: app.appStoreId, appVersion: app.manifest.version }).timeout(10 * 1000).end(function (error, result) {
2017-04-13 01:23:11 -07:00
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error));
2019-05-06 15:00:22 -07:00
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
2019-05-05 10:31:42 -07:00
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
2017-04-13 01:23:11 -07:00
if (result.statusCode === 204) return callback(null); // no update
2018-01-25 09:35:06 -08:00
if (result.statusCode !== 200 || !result.body) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
const updateInfo = result.body;
// for the appstore, x.y.z is the same as x.y.z-0 but in semver, x.y.z > x.y.z-0
const curAppVersion = semver.prerelease(app.manifest.version) ? app.manifest.version : `${app.manifest.version}-0`;
2018-01-25 09:35:06 -08:00
// 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);
2018-01-25 09:35:06 -08:00
return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Malformed update: %s %s', result.statusCode, result.text)));
}
2017-04-13 01:23:11 -07:00
2017-04-13 01:31:22 -07:00
// { id, creationDate, manifest }
2018-01-25 09:35:06 -08:00
callback(null, updateInfo);
2017-04-13 01:23:11 -07:00
});
});
}
2017-09-19 21:07:59 +02:00
2019-05-07 14:25:27 -07:00
function registerCloudron(data, callback) {
assert.strictEqual(typeof data, 'object');
2019-02-25 17:29:42 -08:00
assert.strictEqual(typeof callback, 'function');
2019-05-03 16:27:47 -07:00
const url = `${config.apiServerOrigin()}/api/v1/register_cloudron`;
2019-02-25 17:29:42 -08:00
2019-05-07 14:25:27 -07:00
superagent.post(url).send(data).timeout(30 * 1000).end(function (error, result) {
2019-02-25 17:29:42 -08:00
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
2019-05-06 15:00:22 -07:00
if (result.statusCode !== 201) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, `Unable to register cloudron: ${error.message}`));
2019-02-25 17:29:42 -08:00
2019-05-03 16:27:47 -07:00
// 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'));
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));
debug(`registerCloudron: Cloudron registered with id ${result.body.cloudronId}`);
callback();
});
});
}
2019-05-07 14:15:35 -07:00
function registerWithLicense(license, callback) {
assert.strictEqual(typeof license, 'string');
assert.strictEqual(typeof callback, 'function');
getCloudronToken(function (error, token) {
if (token) return callback(new AppstoreError(AppstoreError.ALREADY_REGISTERED));
registerCloudron({ license }, callback);
});
}
2019-05-07 14:16:51 -07:00
function registerWithLoginCredentials(options, callback) {
2019-05-03 16:27:47 -07:00
assert.strictEqual(typeof options, 'object');
assert.strictEqual(typeof callback, 'function');
function maybeSignup(done) {
if (!options.signup) return done();
registerUser(options.email, options.password, done);
}
2019-02-25 17:29:42 -08:00
2019-05-07 14:15:35 -07:00
getCloudronToken(function (error, token) {
if (token) return callback(new AppstoreError(AppstoreError.ALREADY_REGISTERED));
2019-05-03 16:27:47 -07:00
2019-05-06 19:46:42 -07:00
maybeSignup(function (error) {
2019-05-03 16:27:47 -07:00
if (error) return callback(error);
2019-02-25 17:29:42 -08:00
2019-05-06 19:46:42 -07:00
login(options.email, options.password, options.totpToken || '', function (error, result) {
if (error) return callback(error);
2019-05-07 14:25:27 -07:00
registerCloudron({ domain: config.adminDomain(), accessToken: result.accessToken }, callback);
2019-05-06 19:46:42 -07:00
});
2019-05-03 16:27:47 -07:00
});
2019-02-25 17:29:42 -08:00
});
}
2019-05-07 11:36:08 -07:00
function createTicket(info, callback) {
2017-11-14 20:34:25 -08:00
assert.strictEqual(typeof info, 'object');
assert.strictEqual(typeof info.email, 'string');
assert.strictEqual(typeof info.displayName, 'string');
assert.strictEqual(typeof info.type, 'string');
assert.strictEqual(typeof info.subject, 'string');
assert.strictEqual(typeof info.description, 'string');
assert.strictEqual(typeof callback, 'function');
2018-03-05 17:03:54 +01:00
function collectAppInfoIfNeeded(callback) {
if (!info.appId) return callback();
apps.get(info.appId, callback);
}
2019-05-03 16:01:20 -07:00
getCloudronToken(function (error, token) {
2019-04-30 23:35:49 -07:00
if (error) return callback(error);
2017-11-14 20:34:25 -08:00
2018-03-05 17:03:54 +01:00
collectAppInfoIfNeeded(function (error, result) {
if (error) console.error('Unable to get app info', error);
if (result) info.app = result;
2017-11-14 20:34:25 -08:00
2019-05-07 11:36:08 -07:00
let url = config.apiServerOrigin() + '/api/v1/ticket';
2019-05-10 15:53:34 -07:00
info.supportEmail = custom.spec().support.email; // destination address for tickets
2017-11-14 20:34:25 -08:00
2019-04-30 22:57:43 -07:00
superagent.post(url).query({ accessToken: token }).send(info).timeout(10 * 1000).end(function (error, result) {
2019-05-04 11:45:03 -07:00
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
2019-05-06 15:00:22 -07:00
if (result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
2019-05-05 10:31:42 -07:00
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
2018-03-05 17:03:54 +01:00
if (result.statusCode !== 201) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('Bad response: %s %s', result.statusCode, result.text)));
callback(null);
});
2017-11-14 20:34:25 -08:00
});
});
}
2019-05-04 11:45:03 -07:00
function getApps(callback) {
assert.strictEqual(typeof callback, 'function');
getCloudronToken(function (error, token) {
if (error) return callback(error);
settings.getUnstableAppsConfig(function (error, unstable) {
if (error) return callback(new AppstoreError(AppstoreError.INTERNAL_ERROR, error));
const url = `${config.apiServerOrigin()}/api/v1/apps`;
superagent.get(url).query({ accessToken: token, boxVersion: config.version(), unstable: unstable }).timeout(10 * 1000).end(function (error, result) {
if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message));
2019-05-05 10:31:42 -07:00
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));
2019-05-04 11:45:03 -07:00
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)));
callback(null, result.body.apps);
});
});
});
}
function getAppVersion(appId, version, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof version, 'string');
assert.strictEqual(typeof callback, 'function');
getCloudronToken(function (error, token) {
if (error) return callback(error);
let url = `${config.apiServerOrigin()}/api/v1/apps/${appId}`;
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));
2019-05-05 10:31:42 -07:00
if (result.statusCode === 403 || result.statusCode === 401) return callback(new AppstoreError(AppstoreError.INVALID_TOKEN));
2019-05-06 02:45:16 -07:00
if (result.statusCode === 404) return callback(new AppstoreError(AppstoreError.NOT_FOUND));
2019-05-05 10:31:42 -07:00
if (result.statusCode === 422) return callback(new AppstoreError(AppstoreError.LICENSE_ERROR, result.body.message));
2019-05-04 11:45:03 -07:00
if (result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App fetch failed. %s %j', result.status, result.body)));
callback(null, result.body);
});
});
}
function getApp(appId, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof callback, 'function');
getAppVersion(appId, 'latest', callback);
}