Files
cloudron-box/src/appdb.js
T

615 lines
27 KiB
JavaScript
Raw Normal View History

'use strict';
exports = module.exports = {
get: get,
getByHttpPort: getByHttpPort,
2015-09-14 17:01:04 -07:00
getByContainerId: getByContainerId,
add: add,
exists: exists,
del: del,
update: update,
getAll: getAll,
getPortBindings: getPortBindings,
2017-10-23 22:05:43 +02:00
delPortBinding: delPortBinding,
setAddonConfig: setAddonConfig,
getAddonConfig: getAddonConfig,
getAddonConfigByAppId: getAddonConfigByAppId,
2017-03-26 19:06:36 -07:00
getAddonConfigByName: getAddonConfigByName,
unsetAddonConfig: unsetAddonConfig,
unsetAddonConfigByAppId: unsetAddonConfigByAppId,
2018-12-06 21:08:19 -08:00
getAppIdByAddonConfigValue: getAppIdByAddonConfigValue,
setHealth: setHealth,
setTask: setTask,
getAppStoreIds: getAppStoreIds,
2018-06-29 11:04:14 +02:00
// subdomain table types
SUBDOMAIN_TYPE_PRIMARY: 'primary',
SUBDOMAIN_TYPE_REDIRECT: 'redirect',
_clear: clear
};
var assert = require('assert'),
async = require('async'),
BoxError = require('./boxerror.js'),
database = require('./database.js'),
safe = require('safetydance'),
util = require('util');
2019-08-30 09:45:43 -07:00
var APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.installationState', 'apps.errorJson', 'apps.runState',
2018-06-29 22:25:34 +02:00
'apps.health', 'apps.containerId', 'apps.manifestJson', 'apps.httpPort', 'subdomains.subdomain AS location', 'subdomains.domain',
2020-01-28 21:30:35 -08:00
'apps.accessRestrictionJson', 'apps.memoryLimit', 'apps.cpuShares',
2020-10-27 22:39:05 -07:00
'apps.label', 'apps.tagsJson', 'apps.taskId', 'apps.reverseProxyConfigJson', 'apps.servicesConfigJson',
2019-10-13 18:22:03 -07:00
'apps.sso', 'apps.debugModeJson', 'apps.enableBackup',
2019-11-14 21:43:14 -08:00
'apps.creationTime', 'apps.updateTime', 'apps.mailboxName', 'apps.mailboxDomain', 'apps.enableAutomaticUpdate',
2019-02-12 16:03:12 -08:00
'apps.dataDir', 'apps.ts', 'apps.healthTime' ].join(',');
2018-08-12 22:08:19 -07:00
var PORT_BINDINGS_FIELDS = [ 'hostPort', 'type', 'environmentVariable', 'appId' ].join(',');
2018-08-24 10:39:59 -07:00
const SUBDOMAIN_FIELDS = [ 'appId', 'domain', 'subdomain', 'type' ].join(',');
function postProcess(result) {
assert.strictEqual(typeof result, 'object');
assert(result.manifestJson === null || typeof result.manifestJson === 'string');
result.manifest = safe.JSON.parse(result.manifestJson);
delete result.manifestJson;
2019-03-22 07:48:31 -07:00
assert(result.tagsJson === null || typeof result.tagsJson === 'string');
2019-04-15 14:42:17 +02:00
result.tags = safe.JSON.parse(result.tagsJson) || [];
2019-03-22 07:48:31 -07:00
delete result.tagsJson;
2019-10-13 18:22:03 -07:00
assert(result.reverseProxyConfigJson === null || typeof result.reverseProxyConfigJson === 'string');
result.reverseProxyConfig = safe.JSON.parse(result.reverseProxyConfigJson) || {};
delete result.reverseProxyConfigJson;
assert(result.hostPorts === null || typeof result.hostPorts === 'string');
assert(result.environmentVariables === null || typeof result.environmentVariables === 'string');
result.portBindings = { };
2018-08-12 22:08:19 -07:00
let hostPorts = result.hostPorts === null ? [ ] : result.hostPorts.split(',');
let environmentVariables = result.environmentVariables === null ? [ ] : result.environmentVariables.split(',');
let portTypes = result.portTypes === null ? [ ] : result.portTypes.split(',');
delete result.hostPorts;
delete result.environmentVariables;
2018-08-12 22:08:19 -07:00
delete result.portTypes;
2018-10-11 14:07:43 -07:00
for (let i = 0; i < environmentVariables.length; i++) {
2018-08-12 22:08:19 -07:00
result.portBindings[environmentVariables[i]] = { hostPort: parseInt(hostPorts[i], 10), type: portTypes[i] };
}
2015-10-13 12:29:40 +02:00
assert(result.accessRestrictionJson === null || typeof result.accessRestrictionJson === 'string');
result.accessRestriction = safe.JSON.parse(result.accessRestrictionJson);
if (result.accessRestriction && !result.accessRestriction.users) result.accessRestriction.users = [];
delete result.accessRestrictionJson;
2016-07-15 11:08:11 +02:00
2016-11-11 10:53:41 +05:30
result.sso = !!result.sso; // make it bool
2017-08-16 14:12:07 -07:00
result.enableBackup = !!result.enableBackup; // make it bool
2018-12-07 09:03:28 -08:00
result.enableAutomaticUpdate = !!result.enableAutomaticUpdate; // make it bool
assert(result.debugModeJson === null || typeof result.debugModeJson === 'string');
result.debugMode = safe.JSON.parse(result.debugModeJson);
delete result.debugModeJson;
2020-05-03 17:38:15 -07:00
assert(result.servicesConfigJson === null || typeof result.servicesConfigJson === 'string');
result.servicesConfig = safe.JSON.parse(result.servicesConfigJson) || {};
delete result.servicesConfigJson;
2020-04-28 14:34:17 -07:00
result.alternateDomains = result.alternateDomains || [];
result.alternateDomains.forEach(function (d) {
delete d.appId;
delete d.type;
});
2018-10-11 14:07:43 -07:00
let envNames = JSON.parse(result.envNames), envValues = JSON.parse(result.envValues);
delete result.envNames;
delete result.envValues;
result.env = {};
for (let i = 0; i < envNames.length; i++) { // NOTE: envNames is [ null ] when env of an app is empty
if (envNames[i]) result.env[envNames[i]] = envValues[i];
}
2018-12-20 14:33:29 -08:00
2020-10-27 22:39:05 -07:00
let volumeIds = JSON.parse(result.volumeIds);
delete result.volumeIds;
let volumeReadOnlys = JSON.parse(result.volumeReadOnlys);
delete result.volumeReadOnlys;
result.volumes = volumeIds[0] === null ? [] : volumeIds.map((v, idx) => { return { id: v, readOnly: volumeReadOnlys[idx] }; }); // NOTE: volumeIds is [null] when volumes of an app is empty
2019-09-23 09:13:43 -07:00
result.error = safe.JSON.parse(result.errorJson);
2019-08-30 09:45:43 -07:00
delete result.errorJson;
2019-08-26 15:28:29 -07:00
result.taskId = result.taskId ? String(result.taskId) : null;
}
function get(id, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('SELECT ' + APPS_FIELDS_PREFIXED + ','
2018-10-11 14:07:43 -07:00
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes, '
2020-10-27 22:39:05 -07:00
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues,'
+ 'JSON_ARRAYAGG(appVolumes.volumeId) AS volumeIds, JSON_ARRAYAGG(appVolumes.readOnly) AS volumeReadOnlys '
2018-06-29 11:04:14 +02:00
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
2018-10-11 14:07:43 -07:00
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
2018-08-12 22:08:19 -07:00
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
2020-10-27 22:39:05 -07:00
+ ' LEFT OUTER JOIN appVolumes ON apps.id = appVolumes.appId'
2018-06-29 11:04:14 +02:00
+ ' WHERE apps.id = ? GROUP BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY, id ], function (error, result) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
2019-10-24 20:48:38 -07:00
if (result.length === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
2018-08-24 10:39:59 -07:00
database.query('SELECT ' + SUBDOMAIN_FIELDS + ' FROM subdomains WHERE appId = ? AND type = ?', [ id, exports.SUBDOMAIN_TYPE_REDIRECT ], function (error, alternateDomains) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
result[0].alternateDomains = alternateDomains;
postProcess(result[0]);
callback(null, result[0]);
2018-10-11 14:07:43 -07:00
});
});
}
function getByHttpPort(httpPort, callback) {
assert.strictEqual(typeof httpPort, 'number');
assert.strictEqual(typeof callback, 'function');
database.query('SELECT ' + APPS_FIELDS_PREFIXED + ','
2018-10-11 14:07:43 -07:00
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,'
2020-10-27 22:39:05 -07:00
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues,'
+ 'JSON_ARRAYAGG(appVolumes.volumeId) AS volumeIds, JSON_ARRAYAGG(appVolumes.readOnly) AS volumeReadOnlys '
2018-06-29 11:04:14 +02:00
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
2018-10-11 14:07:43 -07:00
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
2018-08-12 22:08:19 -07:00
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
2020-10-27 22:39:05 -07:00
+ ' LEFT OUTER JOIN appVolumes ON apps.id = appVolumes.appId'
2018-06-29 11:04:14 +02:00
+ ' WHERE httpPort = ? GROUP BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY, httpPort ], function (error, result) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
2019-10-24 20:48:38 -07:00
if (result.length === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
2018-08-24 10:39:59 -07:00
database.query('SELECT ' + SUBDOMAIN_FIELDS + ' FROM subdomains WHERE appId = ? AND type = ?', [ result[0].id, exports.SUBDOMAIN_TYPE_REDIRECT ], function (error, alternateDomains) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
result[0].alternateDomains = alternateDomains;
postProcess(result[0]);
callback(null, result[0]);
});
});
}
2015-09-14 17:01:04 -07:00
function getByContainerId(containerId, callback) {
assert.strictEqual(typeof containerId, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('SELECT ' + APPS_FIELDS_PREFIXED + ','
2018-10-11 14:07:43 -07:00
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,'
2020-10-27 22:39:05 -07:00
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues,'
+ 'JSON_ARRAYAGG(appVolumes.volumeId) AS volumeIds, JSON_ARRAYAGG(appVolumes.readOnly) AS volumeReadOnlys '
2018-06-29 11:04:14 +02:00
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
2018-10-11 14:07:43 -07:00
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
2018-08-12 22:08:19 -07:00
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
2020-10-27 22:39:05 -07:00
+ ' LEFT OUTER JOIN appVolumes ON apps.id = appVolumes.appId'
2018-06-29 11:04:14 +02:00
+ ' WHERE containerId = ? GROUP BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY, containerId ], function (error, result) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
2019-10-24 20:48:38 -07:00
if (result.length === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
2015-09-14 17:01:04 -07:00
2018-08-24 10:39:59 -07:00
database.query('SELECT ' + SUBDOMAIN_FIELDS + ' FROM subdomains WHERE appId = ? AND type = ?', [ result[0].id, exports.SUBDOMAIN_TYPE_REDIRECT ], function (error, alternateDomains) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
result[0].alternateDomains = alternateDomains;
postProcess(result[0]);
2015-09-14 17:01:04 -07:00
callback(null, result[0]);
});
2015-09-14 17:01:04 -07:00
});
}
function getAll(callback) {
assert.strictEqual(typeof callback, 'function');
database.query('SELECT ' + APPS_FIELDS_PREFIXED + ','
2018-10-11 14:07:43 -07:00
+ 'GROUP_CONCAT(CAST(appPortBindings.hostPort AS CHAR(6))) AS hostPorts, GROUP_CONCAT(appPortBindings.environmentVariable) AS environmentVariables, GROUP_CONCAT(appPortBindings.type) AS portTypes,'
2020-10-27 22:39:05 -07:00
+ 'JSON_ARRAYAGG(appEnvVars.name) AS envNames, JSON_ARRAYAGG(appEnvVars.value) AS envValues,'
+ 'JSON_ARRAYAGG(appVolumes.volumeId) AS volumeIds, JSON_ARRAYAGG(appVolumes.readOnly) AS volumeReadOnlys '
2018-06-29 11:04:14 +02:00
+ ' FROM apps'
+ ' LEFT OUTER JOIN appPortBindings ON apps.id = appPortBindings.appId'
2018-10-11 14:07:43 -07:00
+ ' LEFT OUTER JOIN appEnvVars ON apps.id = appEnvVars.appId'
2018-08-12 22:08:19 -07:00
+ ' LEFT OUTER JOIN subdomains ON apps.id = subdomains.appId AND subdomains.type = ?'
2020-10-27 22:39:05 -07:00
+ ' LEFT OUTER JOIN appVolumes ON apps.id = appVolumes.appId'
2018-06-29 11:04:14 +02:00
+ ' GROUP BY apps.id ORDER BY apps.id', [ exports.SUBDOMAIN_TYPE_PRIMARY ], function (error, results) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
2018-08-24 10:39:59 -07:00
database.query('SELECT ' + SUBDOMAIN_FIELDS + ' FROM subdomains WHERE type = ?', [ exports.SUBDOMAIN_TYPE_REDIRECT ], function (error, alternateDomains) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
alternateDomains.forEach(function (d) {
var domain = results.find(function (a) { return d.appId === a.id; });
if (!domain) return;
domain.alternateDomains = domain.alternateDomains || [];
domain.alternateDomains.push(d);
});
results.forEach(postProcess);
callback(null, results);
});
});
}
2019-07-02 20:22:17 -07:00
function add(id, appStoreId, manifest, location, domain, portBindings, data, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof appStoreId, 'string');
assert(manifest && typeof manifest === 'object');
assert.strictEqual(typeof manifest.version, 'string');
assert.strictEqual(typeof location, 'string');
2017-11-02 22:16:42 +01:00
assert.strictEqual(typeof domain, 'string');
assert.strictEqual(typeof portBindings, 'object');
2016-06-17 16:43:35 -05:00
assert(data && typeof data === 'object');
assert.strictEqual(typeof callback, 'function');
portBindings = portBindings || { };
var manifestJson = JSON.stringify(manifest);
2016-06-17 16:43:35 -05:00
2019-03-22 07:48:31 -07:00
const accessRestriction = data.accessRestriction || null;
const accessRestrictionJson = JSON.stringify(accessRestriction);
const memoryLimit = data.memoryLimit || 0;
2020-01-28 21:30:35 -08:00
const cpuShares = data.cpuShares || 512;
2019-09-23 17:23:38 -07:00
const installationState = data.installationState;
const runState = data.runState;
2019-03-22 07:48:31 -07:00
const sso = 'sso' in data ? data.sso : null;
const debugModeJson = data.debugMode ? JSON.stringify(data.debugMode) : null;
const env = data.env || {};
const label = data.label || null;
const tagsJson = data.tags ? JSON.stringify(data.tags) : null;
2019-03-22 14:26:25 -07:00
const mailboxName = data.mailboxName || null;
2019-11-14 21:43:14 -08:00
const mailboxDomain = data.mailboxDomain || null;
const reverseProxyConfigJson = data.reverseProxyConfig ? JSON.stringify(data.reverseProxyConfig) : null;
var queries = [];
2018-06-29 11:04:14 +02:00
queries.push({
2020-01-28 21:30:35 -08:00
query: 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuShares, '
2019-11-14 21:43:14 -08:00
+ 'sso, debugModeJson, mailboxName, mailboxDomain, label, tagsJson, reverseProxyConfigJson) '
2020-01-28 21:30:35 -08:00
+ ' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
args: [ id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuShares,
2019-11-14 21:43:14 -08:00
sso, debugModeJson, mailboxName, mailboxDomain, label, tagsJson, reverseProxyConfigJson ]
});
2018-06-29 11:04:14 +02:00
queries.push({
query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)',
args: [ id, domain, location, exports.SUBDOMAIN_TYPE_PRIMARY ]
});
Object.keys(portBindings).forEach(function (env) {
queries.push({
2018-08-12 22:08:19 -07:00
query: 'INSERT INTO appPortBindings (environmentVariable, hostPort, type, appId) VALUES (?, ?, ?, ?)',
args: [ env, portBindings[env].hostPort, portBindings[env].type, id ]
});
});
2018-10-11 14:07:43 -07:00
Object.keys(env).forEach(function (name) {
queries.push({
query: 'INSERT INTO appEnvVars (appId, name, value) VALUES (?, ?, ?)',
args: [ id, name, env[name] ]
});
});
if (data.alternateDomains) {
data.alternateDomains.forEach(function (d) {
queries.push({
query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)',
args: [ id, d.domain, d.subdomain, exports.SUBDOMAIN_TYPE_REDIRECT ]
});
});
}
database.transaction(queries, function (error) {
if (error && error.code === 'ER_DUP_ENTRY') return callback(new BoxError(BoxError.ALREADY_EXISTS, error.message));
if (error && error.code === 'ER_NO_REFERENCED_ROW_2') return callback(new BoxError(BoxError.NOT_FOUND, 'no such domain'));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
callback(null);
});
}
function exists(id, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('SELECT 1 FROM apps WHERE id=?', [ id ], function (error, result) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
return callback(null, result.length !== 0);
});
}
function getPortBindings(id, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('SELECT ' + PORT_BINDINGS_FIELDS + ' FROM appPortBindings WHERE appId = ?', [ id ], function (error, results) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
var portBindings = { };
for (var i = 0; i < results.length; i++) {
2018-08-12 22:08:19 -07:00
portBindings[results[i].environmentVariable] = { hostPort: results[i].hostPort, type: results[i].type };
}
callback(null, portBindings);
});
}
2018-08-12 22:08:19 -07:00
function delPortBinding(hostPort, type, callback) {
2017-10-23 22:05:43 +02:00
assert.strictEqual(typeof hostPort, 'number');
2018-08-12 22:08:19 -07:00
assert.strictEqual(typeof type, 'string');
2017-10-23 22:05:43 +02:00
assert.strictEqual(typeof callback, 'function');
2018-08-12 22:08:19 -07:00
database.query('DELETE FROM appPortBindings WHERE hostPort=? AND type=?', [ hostPort, type ], function (error, result) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
2019-10-24 20:48:38 -07:00
if (result.affectedRows !== 1) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
2017-10-23 22:05:43 +02:00
callback(null);
});
}
function del(id, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof callback, 'function');
var queries = [
2018-06-29 11:04:14 +02:00
{ query: 'DELETE FROM subdomains WHERE appId = ?', args: [ id ] },
{ query: 'DELETE FROM appPortBindings WHERE appId = ?', args: [ id ] },
2018-10-11 14:07:43 -07:00
{ query: 'DELETE FROM appEnvVars WHERE appId = ?', args: [ id ] },
2020-01-31 15:28:42 -08:00
{ query: 'DELETE FROM appPasswords WHERE identifier = ?', args: [ id ] },
2020-10-27 22:39:05 -07:00
{ query: 'DELETE FROM appVolumes WHERE appId = ?', args: [ id ] },
{ query: 'DELETE FROM apps WHERE id = ?', args: [ id ] }
];
database.transaction(queries, function (error, results) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
2020-10-27 22:39:05 -07:00
if (results[5].affectedRows !== 1) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
callback(null);
});
}
function clear(callback) {
assert.strictEqual(typeof callback, 'function');
async.series([
2018-06-29 11:04:14 +02:00
database.query.bind(null, 'DELETE FROM subdomains'),
database.query.bind(null, 'DELETE FROM appPortBindings'),
database.query.bind(null, 'DELETE FROM appAddonConfigs'),
2018-10-11 14:07:43 -07:00
database.query.bind(null, 'DELETE FROM appEnvVars'),
database.query.bind(null, 'DELETE FROM apps')
], function (error) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
return callback(null);
});
}
function update(id, app, callback) {
updateWithConstraints(id, app, '', callback);
}
function updateWithConstraints(id, app, constraints, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof constraints, 'string');
assert.strictEqual(typeof callback, 'function');
assert(!('portBindings' in app) || typeof app.portBindings === 'object');
assert(!('accessRestriction' in app) || typeof app.accessRestriction === 'object' || app.accessRestriction === '');
2018-06-29 16:50:09 +02:00
assert(!('alternateDomains' in app) || Array.isArray(app.alternateDomains));
2019-04-11 17:18:11 +02:00
assert(!('tags' in app) || Array.isArray(app.tags));
2018-10-11 14:07:43 -07:00
assert(!('env' in app) || typeof app.env === 'object');
var queries = [ ];
if ('portBindings' in app) {
var portBindings = app.portBindings || { };
// replace entries by app id
queries.push({ query: 'DELETE FROM appPortBindings WHERE appId = ?', args: [ id ] });
Object.keys(portBindings).forEach(function (env) {
2018-08-12 22:08:19 -07:00
var values = [ portBindings[env].hostPort, portBindings[env].type, env, id ];
queries.push({ query: 'INSERT INTO appPortBindings (hostPort, type, environmentVariable, appId) VALUES(?, ?, ?, ?)', args: values });
});
}
2018-10-11 14:07:43 -07:00
if ('env' in app) {
queries.push({ query: 'DELETE FROM appEnvVars WHERE appId = ?', args: [ id ] });
Object.keys(app.env).forEach(function (name) {
queries.push({
query: 'INSERT INTO appEnvVars (appId, name, value) VALUES (?, ?, ?)',
args: [ id, name, app.env[name] ]
});
});
}
if ('location' in app && 'domain' in app) { // must be updated together as they are unique together
queries.push({ query: 'DELETE FROM subdomains WHERE appId = ?', args: [ id ]}); // all locations of an app must be updated together
queries.push({ query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, app.domain, app.location, exports.SUBDOMAIN_TYPE_PRIMARY ]});
2018-06-29 11:04:14 +02:00
if ('alternateDomains' in app) {
app.alternateDomains.forEach(function (d) {
queries.push({ query: 'INSERT INTO subdomains (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, exports.SUBDOMAIN_TYPE_REDIRECT ]});
});
}
}
2020-10-27 22:39:05 -07:00
if ('volumes' in app) {
queries.push({ query: 'DELETE FROM appVolumes WHERE appId = ?', args: [ id ]});
app.volumes.forEach(function (vid) {
queries.push({ query: 'INSERT INTO appVolumes (appId, volumeId) VALUES (?, ?)', args: [ id, vid ]});
});
}
var fields = [ ], values = [ ];
for (var p in app) {
2020-10-27 22:39:05 -07:00
if (p === 'manifest' || p === 'tags' || p === 'accessRestriction' || p === 'debugMode' || p === 'error' || p === 'reverseProxyConfig' || p === 'servicesConfig') {
fields.push(`${p}Json = ?`);
2017-01-20 09:40:11 -08:00
values.push(JSON.stringify(app[p]));
2020-04-27 22:55:43 -07:00
} else if (p !== 'portBindings' && p !== 'location' && p !== 'domain' && p !== 'alternateDomains' && p !== 'env') {
fields.push(p + ' = ?');
values.push(app[p]);
}
}
if (values.length !== 0) {
values.push(id);
queries.push({ query: 'UPDATE apps SET ' + fields.join(', ') + ' WHERE id = ? ' + constraints, args: values });
}
database.transaction(queries, function (error, results) {
if (error && error.code === 'ER_DUP_ENTRY') return callback(new BoxError(BoxError.ALREADY_EXISTS, error.message));
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
2019-10-24 20:48:38 -07:00
if (results[results.length - 1].affectedRows !== 1) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
return callback(null);
});
}
2019-02-12 16:03:12 -08:00
function setHealth(appId, health, healthTime, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof health, 'string');
2019-02-12 16:03:12 -08:00
assert(util.isDate(healthTime));
assert.strictEqual(typeof callback, 'function');
2019-02-12 16:03:12 -08:00
var values = { health, healthTime };
updateWithConstraints(appId, values, '', callback);
}
2019-09-24 20:29:01 -07:00
function setTask(appId, values, options, callback) {
assert.strictEqual(typeof appId, 'string');
2019-08-29 09:10:39 -07:00
assert.strictEqual(typeof values, 'object');
2019-09-24 20:29:01 -07:00
assert.strictEqual(typeof options, 'object');
2019-08-29 09:10:39 -07:00
assert.strictEqual(typeof callback, 'function');
2019-09-24 20:29:01 -07:00
if (!options.requireNullTaskId) return updateWithConstraints(appId, values, '', callback);
if (options.requiredState === null) {
updateWithConstraints(appId, values, 'AND taskId IS NULL', callback);
2019-09-21 19:45:55 -07:00
} else {
2019-09-24 20:29:01 -07:00
updateWithConstraints(appId, values, `AND taskId IS NULL AND installationState = "${options.requiredState}"`, callback);
2019-09-21 19:45:55 -07:00
}
}
function getAppStoreIds(callback) {
assert.strictEqual(typeof callback, 'function');
database.query('SELECT id, appStoreId FROM apps', function (error, results) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
callback(null, results);
});
}
function setAddonConfig(appId, addonId, env, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof addonId, 'string');
assert(util.isArray(env));
assert.strictEqual(typeof callback, 'function');
unsetAddonConfig(appId, addonId, function (error) {
if (error) return callback(error);
if (env.length === 0) return callback(null);
2017-03-25 14:14:57 -07:00
var query = 'INSERT INTO appAddonConfigs(appId, addonId, name, value) VALUES ';
var args = [ ], queryArgs = [ ];
for (var i = 0; i < env.length; i++) {
2017-03-25 14:14:57 -07:00
args.push(appId, addonId, env[i].name, env[i].value);
queryArgs.push('(?, ?, ?, ?)');
}
database.query(query + queryArgs.join(','), args, function (error) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
return callback(null);
});
});
}
function unsetAddonConfig(appId, addonId, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof addonId, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('DELETE FROM appAddonConfigs WHERE appId = ? AND addonId = ?', [ appId, addonId ], function (error) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
callback(null);
});
}
function unsetAddonConfigByAppId(appId, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof callback, 'function');
database.query('DELETE FROM appAddonConfigs WHERE appId = ?', [ appId ], function (error) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
callback(null);
});
}
function getAddonConfig(appId, addonId, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof addonId, 'string');
assert.strictEqual(typeof callback, 'function');
2017-03-25 14:14:57 -07:00
database.query('SELECT name, value FROM appAddonConfigs WHERE appId = ? AND addonId = ?', [ appId, addonId ], function (error, results) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
2017-03-25 14:14:57 -07:00
callback(null, results);
});
}
function getAddonConfigByAppId(appId, callback) {
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof callback, 'function');
2017-03-25 14:14:57 -07:00
database.query('SELECT name, value FROM appAddonConfigs WHERE appId = ?', [ appId ], function (error, results) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
2017-03-25 14:14:57 -07:00
callback(null, results);
});
}
2017-03-26 19:06:36 -07:00
2019-06-17 11:13:59 -07:00
function getAppIdByAddonConfigValue(addonId, namePattern, value, callback) {
2018-12-06 21:08:19 -08:00
assert.strictEqual(typeof addonId, 'string');
2019-06-17 11:13:59 -07:00
assert.strictEqual(typeof namePattern, 'string');
2018-12-06 21:08:19 -08:00
assert.strictEqual(typeof value, 'string');
assert.strictEqual(typeof callback, 'function');
2019-06-17 11:13:59 -07:00
database.query('SELECT appId FROM appAddonConfigs WHERE addonId = ? AND name LIKE ? AND value = ?', [ addonId, namePattern, value ], function (error, results) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
2019-10-24 20:48:38 -07:00
if (results.length === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
2018-12-06 21:08:19 -08:00
callback(null, results[0].appId);
});
}
function getAddonConfigByName(appId, addonId, namePattern, callback) {
2017-03-26 19:06:36 -07:00
assert.strictEqual(typeof appId, 'string');
assert.strictEqual(typeof addonId, 'string');
assert.strictEqual(typeof namePattern, 'string');
2017-03-26 19:06:36 -07:00
assert.strictEqual(typeof callback, 'function');
database.query('SELECT value FROM appAddonConfigs WHERE appId = ? AND addonId = ? AND name LIKE ?', [ appId, addonId, namePattern ], function (error, results) {
if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error));
2019-10-24 20:48:38 -07:00
if (results.length === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'App not found'));
2017-03-26 19:06:36 -07:00
callback(null, results[0].value);
});
}