Migrate codebase from CommonJS to ES Modules
- Convert all require()/module.exports to import/export across 260+ files - Add "type": "module" to package.json to enable ESM by default - Add migrations/package.json with "type": "commonjs" to keep db-migrate compatible - Convert eslint.config.js to ESM with sourceType: "module" - Replace __dirname/__filename with import.meta.dirname/import.meta.filename - Replace require.main === module with process.argv[1] === import.meta.filename - Remove 'use strict' directives (implicit in ESM) - Convert dynamic require() in switch statements to static import lookup maps (dns.js, domains.js, backupformats.js, backupsites.js, network.js) - Extract self-referencing exports.CONSTANT patterns into standalone const declarations (apps.js, services.js, locks.js, users.js, mail.js, etc.) - Lazify SERVICES object in services.js to avoid circular dependency TDZ issues - Add clearMailQueue() to mailer.js for ESM-safe queue clearing in tests - Add _setMockApp() to ldapserver.js for ESM-safe test mocking - Add _setMockResolve() wrapper to dig.js for ESM-safe DNS mocking in tests - Convert backupupload.js to use dynamic imports so --check exits before loading the module graph (which requires BOX_ENV) - Update check-install to use ESM import for infra_version.js - Convert scripts/ (hotfix, release, remote_hotfix.js, find-unused-translations) - All 1315 tests passing Migration stats (AI-assisted using Cursor with Claude): - Wall clock time: ~3-4 hours - Assistant completions: ~80-100 - Estimated token usage: ~1-2M tokens Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
348
src/apps.js
348
src/apps.js
@@ -1,6 +1,75 @@
|
||||
'use strict';
|
||||
import * as appTaskManager from './apptaskmanager.js';
|
||||
import * as appstore from './appstore.js';
|
||||
import * as archives from './archives.js';
|
||||
import assert from 'node:assert';
|
||||
import backups from './backups.js';
|
||||
import * as backupSites from './backupsites.js';
|
||||
import BoxError from './boxerror.js';
|
||||
import constants from './constants.js';
|
||||
import crypto from 'node:crypto';
|
||||
import { CronTime } from 'cron';
|
||||
import * as dashboard from './dashboard.js';
|
||||
import * as database from './database.js';
|
||||
import debugModule from 'debug';
|
||||
import * as dns from './dns.js';
|
||||
import * as docker from './docker.js';
|
||||
import * as domains from './domains.js';
|
||||
import eventlog from './eventlog.js';
|
||||
import fs from 'node:fs';
|
||||
import Location from './location.js';
|
||||
import locks from './locks.js';
|
||||
import * as logs from './logs.js';
|
||||
import * as mail from './mail.js';
|
||||
import manifestFormat from '@cloudron/manifest-format';
|
||||
import mysql from 'mysql2';
|
||||
import * as notifications from './notifications.js';
|
||||
import once from './once.js';
|
||||
import path from 'node:path';
|
||||
import paths from './paths.js';
|
||||
import { PassThrough } from 'node:stream';
|
||||
import * as reverseProxy from './reverseproxy.js';
|
||||
import safe from 'safetydance';
|
||||
import semver from 'semver';
|
||||
import services from './services.js';
|
||||
import shellModule from './shell.js';
|
||||
import tasks from './tasks.js';
|
||||
import { Transform as TransformStream } from 'node:stream';
|
||||
import * as users from './users.js';
|
||||
import util from 'node:util';
|
||||
import * as volumes from './volumes.js';
|
||||
import * as _ from './underscore.js';
|
||||
|
||||
exports = module.exports = {
|
||||
const debug = debugModule('box:apps');
|
||||
const shell = shellModule('apps');
|
||||
|
||||
const PORT_TYPE_TCP = 'tcp';
|
||||
const PORT_TYPE_UDP = 'udp';
|
||||
const ISTATE_PENDING_INSTALL = 'pending_install';
|
||||
const ISTATE_PENDING_CLONE = 'pending_clone';
|
||||
const ISTATE_PENDING_CONFIGURE = 'pending_configure';
|
||||
const ISTATE_PENDING_RECREATE_CONTAINER = 'pending_recreate_container';
|
||||
const ISTATE_PENDING_LOCATION_CHANGE = 'pending_location_change';
|
||||
const ISTATE_PENDING_SERVICES_CHANGE = 'pending_services_change';
|
||||
const ISTATE_PENDING_DATA_DIR_MIGRATION = 'pending_data_dir_migration';
|
||||
const ISTATE_PENDING_RESIZE = 'pending_resize';
|
||||
const ISTATE_PENDING_DEBUG = 'pending_debug';
|
||||
const ISTATE_PENDING_UNINSTALL = 'pending_uninstall';
|
||||
const ISTATE_PENDING_RESTORE = 'pending_restore';
|
||||
const ISTATE_PENDING_IMPORT = 'pending_import';
|
||||
const ISTATE_PENDING_UPDATE = 'pending_update';
|
||||
const ISTATE_PENDING_START = 'pending_start';
|
||||
const ISTATE_PENDING_STOP = 'pending_stop';
|
||||
const ISTATE_PENDING_RESTART = 'pending_restart';
|
||||
const ISTATE_ERROR = 'error';
|
||||
const ISTATE_INSTALLED = 'installed';
|
||||
const RSTATE_RUNNING = 'running';
|
||||
const RSTATE_STOPPED = 'stopped';
|
||||
const ACCESS_LEVEL_ADMIN = 'admin';
|
||||
const ACCESS_LEVEL_OPERATOR = 'operator';
|
||||
const ACCESS_LEVEL_USER = 'user';
|
||||
const ACCESS_LEVEL_NONE = '';
|
||||
|
||||
export default {
|
||||
canAccess,
|
||||
isOperator,
|
||||
accessLevel,
|
||||
@@ -99,32 +168,32 @@ exports = module.exports = {
|
||||
|
||||
canBackupApp,
|
||||
|
||||
PORT_TYPE_TCP: 'tcp',
|
||||
PORT_TYPE_UDP: 'udp',
|
||||
PORT_TYPE_TCP,
|
||||
PORT_TYPE_UDP,
|
||||
|
||||
// task codes - the installation state is now a misnomer (keep in sync in UI)
|
||||
ISTATE_PENDING_INSTALL: 'pending_install', // installs and fresh reinstalls
|
||||
ISTATE_PENDING_CLONE: 'pending_clone', // clone
|
||||
ISTATE_PENDING_CONFIGURE: 'pending_configure', // infra update
|
||||
ISTATE_PENDING_RECREATE_CONTAINER: 'pending_recreate_container', // env change or addon change
|
||||
ISTATE_PENDING_LOCATION_CHANGE: 'pending_location_change',
|
||||
ISTATE_PENDING_SERVICES_CHANGE: 'pending_services_change',
|
||||
ISTATE_PENDING_DATA_DIR_MIGRATION: 'pending_data_dir_migration',
|
||||
ISTATE_PENDING_RESIZE: 'pending_resize',
|
||||
ISTATE_PENDING_DEBUG: 'pending_debug',
|
||||
ISTATE_PENDING_UNINSTALL: 'pending_uninstall', // uninstallation
|
||||
ISTATE_PENDING_RESTORE: 'pending_restore', // restore to previous backup or on upgrade
|
||||
ISTATE_PENDING_IMPORT: 'pending_import', // import from external backup
|
||||
ISTATE_PENDING_UPDATE: 'pending_update', // update from installed state preserving data
|
||||
ISTATE_PENDING_START: 'pending_start',
|
||||
ISTATE_PENDING_STOP: 'pending_stop',
|
||||
ISTATE_PENDING_RESTART: 'pending_restart',
|
||||
ISTATE_ERROR: 'error', // error executing last pending_* command
|
||||
ISTATE_INSTALLED: 'installed', // app is installed
|
||||
ISTATE_PENDING_INSTALL,
|
||||
ISTATE_PENDING_CLONE,
|
||||
ISTATE_PENDING_CONFIGURE,
|
||||
ISTATE_PENDING_RECREATE_CONTAINER,
|
||||
ISTATE_PENDING_LOCATION_CHANGE,
|
||||
ISTATE_PENDING_SERVICES_CHANGE,
|
||||
ISTATE_PENDING_DATA_DIR_MIGRATION,
|
||||
ISTATE_PENDING_RESIZE,
|
||||
ISTATE_PENDING_DEBUG,
|
||||
ISTATE_PENDING_UNINSTALL,
|
||||
ISTATE_PENDING_RESTORE,
|
||||
ISTATE_PENDING_IMPORT,
|
||||
ISTATE_PENDING_UPDATE,
|
||||
ISTATE_PENDING_START,
|
||||
ISTATE_PENDING_STOP,
|
||||
ISTATE_PENDING_RESTART,
|
||||
ISTATE_ERROR,
|
||||
ISTATE_INSTALLED,
|
||||
|
||||
// run states
|
||||
RSTATE_RUNNING: 'running',
|
||||
RSTATE_STOPPED: 'stopped', // app stopped by us
|
||||
RSTATE_RUNNING,
|
||||
RSTATE_STOPPED,
|
||||
|
||||
// health states (keep in sync in UI)
|
||||
HEALTH_HEALTHY: 'healthy',
|
||||
@@ -133,10 +202,10 @@ exports = module.exports = {
|
||||
HEALTH_DEAD: 'dead',
|
||||
|
||||
// app access levels
|
||||
ACCESS_LEVEL_ADMIN: 'admin',
|
||||
ACCESS_LEVEL_OPERATOR: 'operator',
|
||||
ACCESS_LEVEL_USER: 'user',
|
||||
ACCESS_LEVEL_NONE: '',
|
||||
ACCESS_LEVEL_ADMIN,
|
||||
ACCESS_LEVEL_OPERATOR,
|
||||
ACCESS_LEVEL_USER,
|
||||
ACCESS_LEVEL_NONE,
|
||||
|
||||
// exported for testing
|
||||
_checkForPortBindingConflict: checkForPortBindingConflict,
|
||||
@@ -148,47 +217,6 @@ exports = module.exports = {
|
||||
_clear: clear
|
||||
};
|
||||
|
||||
const appTaskManager = require('./apptaskmanager.js'),
|
||||
appstore = require('./appstore.js'),
|
||||
archives = require('./archives.js'),
|
||||
assert = require('node:assert'),
|
||||
backups = require('./backups.js'),
|
||||
backupSites = require('./backupsites.js'),
|
||||
BoxError = require('./boxerror.js'),
|
||||
constants = require('./constants.js'),
|
||||
crypto = require('node:crypto'),
|
||||
{ CronTime } = require('cron'),
|
||||
dashboard = require('./dashboard.js'),
|
||||
database = require('./database.js'),
|
||||
debug = require('debug')('box:apps'),
|
||||
dns = require('./dns.js'),
|
||||
docker = require('./docker.js'),
|
||||
domains = require('./domains.js'),
|
||||
eventlog = require('./eventlog.js'),
|
||||
fs = require('node:fs'),
|
||||
Location = require('./location.js'),
|
||||
locks = require('./locks.js'),
|
||||
logs = require('./logs.js'),
|
||||
mail = require('./mail.js'),
|
||||
manifestFormat = require('@cloudron/manifest-format'),
|
||||
mysql = require('mysql2'),
|
||||
notifications = require('./notifications.js'),
|
||||
once = require('./once.js'),
|
||||
path = require('node:path'),
|
||||
paths = require('./paths.js'),
|
||||
PassThrough = require('node:stream').PassThrough,
|
||||
reverseProxy = require('./reverseproxy.js'),
|
||||
safe = require('safetydance'),
|
||||
semver = require('semver'),
|
||||
services = require('./services.js'),
|
||||
shell = require('./shell.js')('apps'),
|
||||
tasks = require('./tasks.js'),
|
||||
TransformStream = require('node:stream').Transform,
|
||||
users = require('./users.js'),
|
||||
util = require('node:util'),
|
||||
volumes = require('./volumes.js'),
|
||||
_ = require('./underscore.js');
|
||||
|
||||
// NOTE: when adding fields here, update the clone and unarchive logic as well
|
||||
const APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.versionsUrl', 'apps.installationState', 'apps.errorJson', 'apps.runState',
|
||||
'apps.health', 'apps.containerId', 'apps.manifestJson', 'apps.accessRestrictionJson', 'apps.memoryLimit', 'apps.cpuQuota',
|
||||
@@ -201,7 +229,7 @@ const APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.versionsUrl',
|
||||
// const PORT_BINDINGS_FIELDS = [ 'hostPort', 'type', 'environmentVariable', 'appId', 'count' ].join(',');
|
||||
const LOCATION_FIELDS = [ 'appId', 'subdomain', 'domain', 'type', 'certificateJson' ];
|
||||
|
||||
const CHECKVOLUME_CMD = path.join(__dirname, 'scripts/checkvolume.sh');
|
||||
const CHECKVOLUME_CMD = path.join(import.meta.dirname, 'scripts/checkvolume.sh');
|
||||
|
||||
// ports is a map of envvar -> hostPort
|
||||
function validatePorts(ports, manifest) {
|
||||
@@ -280,7 +308,7 @@ function translateToPortBindings(ports, manifest) {
|
||||
const tcpPorts = manifest.tcpPorts || {};
|
||||
|
||||
for (const portName in ports) {
|
||||
const portType = portName in tcpPorts ? exports.PORT_TYPE_TCP : exports.PORT_TYPE_UDP;
|
||||
const portType = portName in tcpPorts ? PORT_TYPE_TCP : PORT_TYPE_UDP;
|
||||
const portCount = portName in tcpPorts ? tcpPorts[portName].portCount : manifest.udpPorts[portName].portCount; // since count is optional, this can be undefined
|
||||
portBindings[portName] = { hostPort: ports[portName], type: portType, count: portCount || 1 };
|
||||
}
|
||||
@@ -587,10 +615,10 @@ function pickFields(app, accessLevel) {
|
||||
assert.strictEqual(typeof app, 'object');
|
||||
assert.strictEqual(typeof accessLevel, 'string');
|
||||
|
||||
if (accessLevel === exports.ACCESS_LEVEL_NONE) return null; // cannot happen!
|
||||
if (accessLevel === ACCESS_LEVEL_NONE) return null; // cannot happen!
|
||||
|
||||
let result;
|
||||
if (accessLevel === exports.ACCESS_LEVEL_USER) {
|
||||
if (accessLevel === ACCESS_LEVEL_USER) {
|
||||
result = _.pick(app, [
|
||||
'id', 'appStoreId', 'versionsUrl', 'installationState', 'error', 'runState', 'health', 'taskId', 'accessRestriction',
|
||||
'secondaryDomains', 'redirectDomains', 'aliasDomains', 'sso', 'subdomain', 'domain', 'fqdn', 'certificate',
|
||||
@@ -784,7 +812,7 @@ function canAutoupdateAppSync(app, updateInfo) {
|
||||
return { code: false, reason: 'Major package version requires review of breaking changes' }; // major changes are blocking
|
||||
}
|
||||
|
||||
if (app.runState === exports.RSTATE_STOPPED) return { code: false, reason: 'Stopped apps cannot run migration scripts' };
|
||||
if (app.runState === RSTATE_STOPPED) return { code: false, reason: 'Stopped apps cannot run migration scripts' };
|
||||
|
||||
const newTcpPorts = manifest.tcpPorts || {};
|
||||
const newUdpPorts = manifest.udpPorts || {};
|
||||
@@ -849,9 +877,9 @@ function canAccess(app, user) {
|
||||
}
|
||||
|
||||
function accessLevel(app, user) {
|
||||
if (isAdmin(user)) return exports.ACCESS_LEVEL_ADMIN;
|
||||
if (isOperator(app, user)) return exports.ACCESS_LEVEL_OPERATOR;
|
||||
return canAccess(app, user) ? exports.ACCESS_LEVEL_USER : exports.ACCESS_LEVEL_NONE;
|
||||
if (isAdmin(user)) return ACCESS_LEVEL_ADMIN;
|
||||
if (isOperator(app, user)) return ACCESS_LEVEL_OPERATOR;
|
||||
return canAccess(app, user) ? ACCESS_LEVEL_USER : ACCESS_LEVEL_NONE;
|
||||
}
|
||||
|
||||
async function checkForPortBindingConflict(portBindings, options) {
|
||||
@@ -1243,10 +1271,10 @@ async function onTaskFinished(error, appId, installationState, taskId, auditSour
|
||||
if (!app || !task) return;
|
||||
|
||||
switch (installationState) {
|
||||
case exports.ISTATE_PENDING_DATA_DIR_MIGRATION:
|
||||
case ISTATE_PENDING_DATA_DIR_MIGRATION:
|
||||
if (success) await safe(services.rebuildService('sftp', auditSource), { debug });
|
||||
break;
|
||||
case exports.ISTATE_PENDING_UPDATE: {
|
||||
case ISTATE_PENDING_UPDATE: {
|
||||
const fromManifest = success ? task.args[1].updateConfig.manifest : app.manifest;
|
||||
const toManifest = success ? app.manifest : task.args[1].updateConfig.manifest;
|
||||
|
||||
@@ -1264,11 +1292,11 @@ async function scheduleTask(appId, installationState, taskId, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
let memoryLimit = 400;
|
||||
if (installationState === exports.ISTATE_PENDING_CLONE || installationState === exports.ISTATE_PENDING_RESTORE
|
||||
|| installationState === exports.ISTATE_PENDING_IMPORT || installationState === exports.ISTATE_PENDING_UPDATE) {
|
||||
if (installationState === ISTATE_PENDING_CLONE || installationState === ISTATE_PENDING_RESTORE
|
||||
|| installationState === ISTATE_PENDING_IMPORT || installationState === ISTATE_PENDING_UPDATE) {
|
||||
const sites = await backupSites.listByContentForUpdates(appId);
|
||||
memoryLimit = sites.reduce((acc, cur) => cur.limits?.memoryLimit ? Math.max(cur.limits.memoryLimit/1024/1024, acc) : acc, 400);
|
||||
} else if (installationState === exports.ISTATE_PENDING_DATA_DIR_MIGRATION) {
|
||||
} else if (installationState === ISTATE_PENDING_DATA_DIR_MIGRATION) {
|
||||
memoryLimit = 1024; // cp takes more memory than we think
|
||||
}
|
||||
|
||||
@@ -1286,8 +1314,8 @@ async function scheduleTask(appId, installationState, taskId, auditSource) {
|
||||
taskId,
|
||||
installationState
|
||||
};
|
||||
await safe(update(appId, { installationState: exports.ISTATE_ERROR, error: appError, taskId: null }), { debug });
|
||||
} else if (!(installationState === exports.ISTATE_PENDING_UNINSTALL && !error)) { // clear out taskId except for successful uninstall
|
||||
await safe(update(appId, { installationState: ISTATE_ERROR, error: appError, taskId: null }), { debug });
|
||||
} else if (!(installationState === ISTATE_PENDING_UNINSTALL && !error)) { // clear out taskId except for successful uninstall
|
||||
await safe(update(appId, { taskId: null }), { debug });
|
||||
}
|
||||
|
||||
@@ -1303,7 +1331,7 @@ async function addTask(appId, installationState, task, auditSource) {
|
||||
|
||||
const { args, values } = task;
|
||||
// TODO: match the SQL logic to match checkAppState. this means checking the error.installationState and installationState. Unfortunately, former is JSON right now
|
||||
const requiredState = null; // 'requiredState' in task ? task.requiredState : exports.ISTATE_INSTALLED;
|
||||
const requiredState = null; // 'requiredState' in task ? task.requiredState : ISTATE_INSTALLED;
|
||||
const scheduleNow = 'scheduleNow' in task ? task.scheduleNow : true;
|
||||
const requireNullTaskId = 'requireNullTaskId' in task ? task.requireNullTaskId : true;
|
||||
|
||||
@@ -1324,17 +1352,17 @@ function checkAppState(app, state) {
|
||||
|
||||
if (app.taskId) return new BoxError(BoxError.BAD_STATE, `Locked by task ${app.taskId} : ${app.installationState} / ${app.runState}`);
|
||||
|
||||
if (app.installationState === exports.ISTATE_ERROR) {
|
||||
if (app.installationState === ISTATE_ERROR) {
|
||||
// allow task to be called again if that was the errored task
|
||||
if (app.error.installationState === state) return null;
|
||||
|
||||
// allow uninstall from any state
|
||||
if (state !== exports.ISTATE_PENDING_UNINSTALL && state !== exports.ISTATE_PENDING_RESTORE && state !== exports.ISTATE_PENDING_IMPORT) return new BoxError(BoxError.BAD_STATE, 'Not allowed in error state');
|
||||
if (state !== ISTATE_PENDING_UNINSTALL && state !== ISTATE_PENDING_RESTORE && state !== ISTATE_PENDING_IMPORT) return new BoxError(BoxError.BAD_STATE, 'Not allowed in error state');
|
||||
}
|
||||
|
||||
if (app.runState === exports.RSTATE_STOPPED) {
|
||||
if (app.runState === RSTATE_STOPPED) {
|
||||
// can't backup or restore since app addons are down. can't update because migration scripts won't run
|
||||
if (state === exports.ISTATE_PENDING_UPDATE || state === exports.ISTATE_PENDING_RESTORE || state === exports.ISTATE_PENDING_IMPORT) return new BoxError(BoxError.BAD_STATE, 'Not allowed in stopped state');
|
||||
if (state === ISTATE_PENDING_UPDATE || state === ISTATE_PENDING_RESTORE || state === ISTATE_PENDING_IMPORT) return new BoxError(BoxError.BAD_STATE, 'Not allowed in stopped state');
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -1521,8 +1549,8 @@ async function install(data, auditSource) {
|
||||
enableRedis,
|
||||
notes,
|
||||
crontab,
|
||||
runState: exports.RSTATE_RUNNING,
|
||||
installationState: exports.ISTATE_PENDING_INSTALL
|
||||
runState: RSTATE_RUNNING,
|
||||
installationState: ISTATE_PENDING_INSTALL
|
||||
};
|
||||
|
||||
const [addError] = await safe(add(appId, appStoreId, versionsUrl, manifest, subdomain, domain, portBindings, app));
|
||||
@@ -1681,7 +1709,7 @@ async function setMemoryLimit(app, memoryLimit, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_RESIZE);
|
||||
let error = checkAppState(app, ISTATE_PENDING_RESIZE);
|
||||
if (error) throw error;
|
||||
|
||||
error = validateMemoryLimit(app.manifest, memoryLimit);
|
||||
@@ -1691,7 +1719,7 @@ async function setMemoryLimit(app, memoryLimit, auditSource) {
|
||||
args: {},
|
||||
values: { memoryLimit }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_RESIZE, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_RESIZE, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, memoryLimit, taskId });
|
||||
|
||||
@@ -1704,7 +1732,7 @@ async function setCpuQuota(app, cpuQuota, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_RESIZE);
|
||||
let error = checkAppState(app, ISTATE_PENDING_RESIZE);
|
||||
if (error) throw error;
|
||||
|
||||
error = validateCpuQuota(cpuQuota);
|
||||
@@ -1714,7 +1742,7 @@ async function setCpuQuota(app, cpuQuota, auditSource) {
|
||||
args: {},
|
||||
values: { cpuQuota }
|
||||
};
|
||||
const taskId = await safe(addTask(appId, exports.ISTATE_PENDING_RESIZE, task, auditSource));
|
||||
const taskId = await safe(addTask(appId, ISTATE_PENDING_RESIZE, task, auditSource));
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, cpuQuota, taskId });
|
||||
|
||||
@@ -1727,14 +1755,14 @@ async function setMounts(app, mounts, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
const error = checkAppState(app, exports.ISTATE_PENDING_RECREATE_CONTAINER);
|
||||
const error = checkAppState(app, ISTATE_PENDING_RECREATE_CONTAINER);
|
||||
if (error) throw error;
|
||||
|
||||
const task = {
|
||||
args: {},
|
||||
values: { mounts }
|
||||
};
|
||||
const [taskError, taskId] = await safe(addTask(appId, exports.ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource));
|
||||
const [taskError, taskId] = await safe(addTask(appId, ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource));
|
||||
if (taskError && taskError.reason === BoxError.ALREADY_EXISTS) throw new BoxError(BoxError.CONFLICT, 'Duplicate mount points');
|
||||
if (taskError) throw taskError;
|
||||
|
||||
@@ -1749,7 +1777,7 @@ async function setDevices(app, devices, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_RECREATE_CONTAINER);
|
||||
let error = checkAppState(app, ISTATE_PENDING_RECREATE_CONTAINER);
|
||||
if (error) throw error;
|
||||
|
||||
error = validateDevices(devices);
|
||||
@@ -1759,7 +1787,7 @@ async function setDevices(app, devices, auditSource) {
|
||||
args: {},
|
||||
values: { devices }
|
||||
};
|
||||
const [taskError, taskId] = await safe(addTask(appId, exports.ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource));
|
||||
const [taskError, taskId] = await safe(addTask(appId, ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource));
|
||||
if (taskError) throw taskError;
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, devices, taskId });
|
||||
@@ -1773,7 +1801,7 @@ async function setEnvironment(app, env, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_RECREATE_CONTAINER);
|
||||
let error = checkAppState(app, ISTATE_PENDING_RECREATE_CONTAINER);
|
||||
if (error) throw error;
|
||||
|
||||
error = validateEnv(env);
|
||||
@@ -1783,7 +1811,7 @@ async function setEnvironment(app, env, auditSource) {
|
||||
args: {},
|
||||
values: { env }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_RECREATE_CONTAINER, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, env, taskId });
|
||||
|
||||
@@ -1796,7 +1824,7 @@ async function setDebugMode(app, debugMode, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_DEBUG);
|
||||
let error = checkAppState(app, ISTATE_PENDING_DEBUG);
|
||||
if (error) throw error;
|
||||
|
||||
error = validateDebugMode(debugMode);
|
||||
@@ -1806,7 +1834,7 @@ async function setDebugMode(app, debugMode, auditSource) {
|
||||
args: {},
|
||||
values: { debugMode }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_DEBUG, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_DEBUG, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, debugMode, taskId });
|
||||
|
||||
@@ -1823,7 +1851,7 @@ async function setMailbox(app, data, auditSource) {
|
||||
const enableMailbox = data.enable;
|
||||
|
||||
const appId = app.id;
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_SERVICES_CHANGE);
|
||||
let error = checkAppState(app, ISTATE_PENDING_SERVICES_CHANGE);
|
||||
if (error) throw error;
|
||||
|
||||
if (!app.manifest.addons?.sendmail) throw new BoxError(BoxError.BAD_FIELD, 'App does not use sendmail');
|
||||
@@ -1854,7 +1882,7 @@ async function setMailbox(app, data, auditSource) {
|
||||
args: {},
|
||||
values: { enableMailbox, mailboxName, mailboxDomain, mailboxDisplayName }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_SERVICES_CHANGE, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_SERVICES_CHANGE, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, mailboxName, mailboxDomain, mailboxDisplayName, taskId });
|
||||
|
||||
@@ -1870,7 +1898,7 @@ async function setInbox(app, data, auditSource) {
|
||||
|
||||
const enableInbox = data.enable;
|
||||
const appId = app.id;
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_SERVICES_CHANGE);
|
||||
let error = checkAppState(app, ISTATE_PENDING_SERVICES_CHANGE);
|
||||
if (error) throw error;
|
||||
|
||||
if (!app.manifest.addons?.recvmail) throw new BoxError(BoxError.BAD_FIELD, 'App does not use recvmail addon');
|
||||
@@ -1889,7 +1917,7 @@ async function setInbox(app, data, auditSource) {
|
||||
args: {},
|
||||
values: { enableInbox, inboxName, inboxDomain }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_SERVICES_CHANGE, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_SERVICES_CHANGE, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, enableInbox, inboxName, inboxDomain, taskId });
|
||||
|
||||
@@ -1902,7 +1930,7 @@ async function setTurn(app, enableTurn, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
const error = checkAppState(app, exports.ISTATE_PENDING_SERVICES_CHANGE);
|
||||
const error = checkAppState(app, ISTATE_PENDING_SERVICES_CHANGE);
|
||||
if (error) throw error;
|
||||
|
||||
if (!app.manifest.addons?.turn) throw new BoxError(BoxError.BAD_FIELD, 'App does not use turn addon');
|
||||
@@ -1912,7 +1940,7 @@ async function setTurn(app, enableTurn, auditSource) {
|
||||
args: {},
|
||||
values: { enableTurn }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_SERVICES_CHANGE, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_SERVICES_CHANGE, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, enableTurn, taskId });
|
||||
|
||||
@@ -1925,7 +1953,7 @@ async function setRedis(app, enableRedis, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
const error = checkAppState(app, exports.ISTATE_PENDING_SERVICES_CHANGE);
|
||||
const error = checkAppState(app, ISTATE_PENDING_SERVICES_CHANGE);
|
||||
if (error) throw error;
|
||||
|
||||
if (!app.manifest.addons?.redis) throw new BoxError(BoxError.BAD_FIELD, 'App does not use redis addon');
|
||||
@@ -1935,7 +1963,7 @@ async function setRedis(app, enableRedis, auditSource) {
|
||||
args: {},
|
||||
values: { enableRedis }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_SERVICES_CHANGE, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_SERVICES_CHANGE, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, enableRedis, taskId });
|
||||
|
||||
@@ -2020,7 +2048,7 @@ async function setLocation(app, data, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_LOCATION_CHANGE);
|
||||
let error = checkAppState(app, ISTATE_PENDING_LOCATION_CHANGE);
|
||||
if (error) throw error;
|
||||
|
||||
const values = {
|
||||
@@ -2073,7 +2101,7 @@ async function setLocation(app, data, auditSource) {
|
||||
},
|
||||
values
|
||||
};
|
||||
const [taskError, taskId] = await safe(addTask(appId, exports.ISTATE_PENDING_LOCATION_CHANGE, task, auditSource));
|
||||
const [taskError, taskId] = await safe(addTask(appId, ISTATE_PENDING_LOCATION_CHANGE, task, auditSource));
|
||||
if (taskError && taskError.reason !== BoxError.ALREADY_EXISTS) throw taskError;
|
||||
if (taskError && taskError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(taskError.message, locations, values.portBindings);
|
||||
|
||||
@@ -2094,7 +2122,7 @@ async function setStorage(app, volumeId, volumePrefix, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
const error = checkAppState(app, exports.ISTATE_PENDING_DATA_DIR_MIGRATION);
|
||||
const error = checkAppState(app, ISTATE_PENDING_DATA_DIR_MIGRATION);
|
||||
if (error) throw error;
|
||||
|
||||
if (volumeId) {
|
||||
@@ -2107,7 +2135,7 @@ async function setStorage(app, volumeId, volumePrefix, auditSource) {
|
||||
args: { newStorageVolumeId: volumeId, newStorageVolumePrefix: volumePrefix },
|
||||
values: {}
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_DATA_DIR_MIGRATION, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_DATA_DIR_MIGRATION, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, volumeId, volumePrefix, taskId });
|
||||
|
||||
@@ -2123,9 +2151,9 @@ async function updateApp(app, data, auditSource) {
|
||||
const skipBackup = !!data.skipBackup, appId = app.id, manifest = data.manifest;
|
||||
const values = { updateInfo: null }; // clear update indicator immediately
|
||||
|
||||
if (app.runState === exports.RSTATE_STOPPED) throw new BoxError(BoxError.BAD_STATE, 'Stopped apps cannot be updated');
|
||||
if (app.runState === RSTATE_STOPPED) throw new BoxError(BoxError.BAD_STATE, 'Stopped apps cannot be updated');
|
||||
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_UPDATE);
|
||||
let error = checkAppState(app, ISTATE_PENDING_UPDATE);
|
||||
if (error) throw error;
|
||||
|
||||
error = manifestFormat.parse(manifest);
|
||||
@@ -2192,7 +2220,7 @@ async function updateApp(app, data, auditSource) {
|
||||
args: { updateConfig },
|
||||
values
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_UPDATE, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_UPDATE, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_UPDATE, auditSource, { appId, app, skipBackup, toManifest: manifest, fromManifest: app.manifest, force: data.force, taskId });
|
||||
|
||||
@@ -2268,7 +2296,7 @@ async function repair(app, data, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
let errorState = (app.error && app.error.installationState) || exports.ISTATE_PENDING_CONFIGURE;
|
||||
let errorState = (app.error && app.error.installationState) || ISTATE_PENDING_CONFIGURE;
|
||||
|
||||
const task = {
|
||||
args: {},
|
||||
@@ -2277,7 +2305,7 @@ async function repair(app, data, auditSource) {
|
||||
};
|
||||
|
||||
// maybe split this into a separate route like reinstall?
|
||||
if (errorState === exports.ISTATE_PENDING_INSTALL || errorState === exports.ISTATE_PENDING_CLONE) {
|
||||
if (errorState === ISTATE_PENDING_INSTALL || errorState === ISTATE_PENDING_CLONE) {
|
||||
task.args = { skipDnsSetup: false, overwriteDns: true };
|
||||
if (data.manifest) {
|
||||
let error = manifestFormat.parse(data.manifest);
|
||||
@@ -2297,7 +2325,7 @@ async function repair(app, data, auditSource) {
|
||||
task.args.oldManifest = app.manifest;
|
||||
}
|
||||
} else {
|
||||
errorState = exports.ISTATE_PENDING_CONFIGURE;
|
||||
errorState = ISTATE_PENDING_CONFIGURE;
|
||||
if (data.dockerImage) {
|
||||
const newManifest = Object.assign({}, app.manifest, { dockerImage: data.dockerImage });
|
||||
task.values.manifest = newManifest;
|
||||
@@ -2318,7 +2346,7 @@ async function restore(app, backupId, auditSource) {
|
||||
|
||||
const appId = app.id;
|
||||
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_RESTORE);
|
||||
let error = checkAppState(app, ISTATE_PENDING_RESTORE);
|
||||
if (error) throw error;
|
||||
|
||||
// for empty or null backupId, use existing manifest to mimic a reinstall
|
||||
@@ -2358,7 +2386,7 @@ async function restore(app, backupId, auditSource) {
|
||||
values
|
||||
};
|
||||
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_RESTORE, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_RESTORE, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_RESTORE, auditSource, { app, backupId: backup.id, remotePath: backup.remotePath, fromManifest: app.manifest, toManifest: manifest, taskId });
|
||||
|
||||
@@ -2372,7 +2400,7 @@ async function importApp(app, data, auditSource) {
|
||||
|
||||
const appId = app.id;
|
||||
|
||||
const error = checkAppState(app, exports.ISTATE_PENDING_IMPORT);
|
||||
const error = checkAppState(app, ISTATE_PENDING_IMPORT);
|
||||
if (error) throw error;
|
||||
|
||||
let restoreConfig;
|
||||
@@ -2401,7 +2429,7 @@ async function importApp(app, data, auditSource) {
|
||||
},
|
||||
values: {}
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_IMPORT, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_IMPORT, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_IMPORT, auditSource, { app, remotePath: data.remotePath, inPlace: data.inPlace, taskId });
|
||||
|
||||
@@ -2479,8 +2507,8 @@ async function clone(app, data, user, auditSource) {
|
||||
if (!manifest.addons?.recvmail) dolly.inboxDomain = null; // needed because we are cloning _current_ app settings with old manifest
|
||||
|
||||
const obj = Object.assign(dolly, {
|
||||
installationState: exports.ISTATE_PENDING_CLONE,
|
||||
runState: exports.RSTATE_RUNNING,
|
||||
installationState: ISTATE_PENDING_CLONE,
|
||||
runState: RSTATE_RUNNING,
|
||||
mailboxName,
|
||||
mailboxDomain,
|
||||
secondaryDomains,
|
||||
@@ -2497,9 +2525,9 @@ async function clone(app, data, user, auditSource) {
|
||||
const task = {
|
||||
args: { restoreConfig, overwriteDns, skipDnsSetup, oldManifest: null },
|
||||
values: {},
|
||||
requiredState: exports.ISTATE_PENDING_CLONE
|
||||
requiredState: ISTATE_PENDING_CLONE
|
||||
};
|
||||
const taskId = await addTask(newAppId, exports.ISTATE_PENDING_CLONE, task, auditSource);
|
||||
const taskId = await addTask(newAppId, ISTATE_PENDING_CLONE, task, auditSource);
|
||||
|
||||
const newApp = Object.assign({}, _.omit(obj, ['icon']), { appStoreId, versionsUrl, manifest, subdomain, domain, portBindings });
|
||||
newApp.fqdn = dns.fqdn(newApp.subdomain, newApp.domain);
|
||||
@@ -2558,8 +2586,8 @@ async function unarchive(archive, data, auditSource) {
|
||||
redirectDomains: [],
|
||||
aliasDomains: [],
|
||||
mailboxDomain: data.domain, // archive's mailboxDomain may not exist
|
||||
runState: exports.RSTATE_RUNNING,
|
||||
installationState: exports.ISTATE_PENDING_INSTALL,
|
||||
runState: RSTATE_RUNNING,
|
||||
installationState: ISTATE_PENDING_INSTALL,
|
||||
sso: backup.appConfig ? backup.appConfig.sso : true // when no appConfig take a blind guess
|
||||
});
|
||||
obj.icon = (await archives.getIcons(archive.id))?.icon;
|
||||
@@ -2592,7 +2620,7 @@ async function uninstall(app, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
const error = checkAppState(app, exports.ISTATE_PENDING_UNINSTALL);
|
||||
const error = checkAppState(app, ISTATE_PENDING_UNINSTALL);
|
||||
if (error) throw error;
|
||||
|
||||
const task = {
|
||||
@@ -2600,7 +2628,7 @@ async function uninstall(app, auditSource) {
|
||||
values: {},
|
||||
requiredState: null // can run in any state, as long as no task is active
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_UNINSTALL, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_UNINSTALL, task, auditSource);
|
||||
await eventlog.add(eventlog.ACTION_APP_UNINSTALL, auditSource, { appId, app, taskId });
|
||||
|
||||
return { taskId };
|
||||
@@ -2628,14 +2656,14 @@ async function start(app, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
const error = checkAppState(app, exports.ISTATE_PENDING_START);
|
||||
const error = checkAppState(app, ISTATE_PENDING_START);
|
||||
if (error) throw error;
|
||||
|
||||
const task = {
|
||||
args: {},
|
||||
values: { runState: exports.RSTATE_RUNNING }
|
||||
values: { runState: RSTATE_RUNNING }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_START, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_START, task, auditSource);
|
||||
await eventlog.add(eventlog.ACTION_APP_START, auditSource, { appId, app, taskId });
|
||||
return { taskId };
|
||||
}
|
||||
@@ -2645,14 +2673,14 @@ async function stop(app, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
const error = checkAppState(app, exports.ISTATE_PENDING_STOP);
|
||||
const error = checkAppState(app, ISTATE_PENDING_STOP);
|
||||
if (error) throw error;
|
||||
|
||||
const task = {
|
||||
args: {},
|
||||
values: { runState: exports.RSTATE_STOPPED }
|
||||
values: { runState: RSTATE_STOPPED }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_STOP, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_STOP, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_STOP, auditSource, { appId, app, taskId });
|
||||
|
||||
@@ -2664,14 +2692,14 @@ async function restart(app, auditSource) {
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const appId = app.id;
|
||||
const error = checkAppState(app, exports.ISTATE_PENDING_RESTART);
|
||||
const error = checkAppState(app, ISTATE_PENDING_RESTART);
|
||||
if (error) throw error;
|
||||
|
||||
const task = {
|
||||
args: {},
|
||||
values: { runState: exports.RSTATE_RUNNING }
|
||||
values: { runState: RSTATE_RUNNING }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_RESTART, task, auditSource);
|
||||
const taskId = await addTask(appId, ISTATE_PENDING_RESTART, task, auditSource);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_RESTART, auditSource, { appId, app, taskId });
|
||||
|
||||
@@ -2706,7 +2734,7 @@ async function createExec(app, options) {
|
||||
const cmd = options.cmd || [ '/bin/bash' ];
|
||||
assert(Array.isArray(cmd) && cmd.length > 0);
|
||||
|
||||
if (app.installationState !== exports.ISTATE_INSTALLED || app.runState !== exports.RSTATE_RUNNING) {
|
||||
if (app.installationState !== ISTATE_INSTALLED || app.runState !== RSTATE_RUNNING) {
|
||||
throw new BoxError(BoxError.BAD_STATE, 'App not installed or running');
|
||||
}
|
||||
|
||||
@@ -2735,7 +2763,7 @@ async function startExec(app, execId, options) {
|
||||
assert.strictEqual(typeof execId, 'string');
|
||||
assert(options && typeof options === 'object');
|
||||
|
||||
if (app.installationState !== exports.ISTATE_INSTALLED || app.runState !== exports.RSTATE_RUNNING) {
|
||||
if (app.installationState !== ISTATE_INSTALLED || app.runState !== RSTATE_RUNNING) {
|
||||
throw new BoxError(BoxError.BAD_STATE, 'App not installed or running');
|
||||
}
|
||||
|
||||
@@ -2778,13 +2806,13 @@ function canBackupApp(app) {
|
||||
// only backup apps that are installed or specific pending states
|
||||
|
||||
// stopped apps cannot be backed up because addons might be down (redis)
|
||||
if (app.runState === exports.RSTATE_STOPPED) return false;
|
||||
if (app.runState === RSTATE_STOPPED) return false;
|
||||
|
||||
// we used to check the health here but that doesn't work for stopped apps. it's better to just fail
|
||||
// and inform the user if the backup fails and the app addons have not been setup yet.
|
||||
return app.installationState === exports.ISTATE_INSTALLED ||
|
||||
app.installationState === exports.ISTATE_PENDING_CONFIGURE ||
|
||||
app.installationState === exports.ISTATE_PENDING_UPDATE; // called from apptask
|
||||
return app.installationState === ISTATE_INSTALLED ||
|
||||
app.installationState === ISTATE_PENDING_CONFIGURE ||
|
||||
app.installationState === ISTATE_PENDING_UPDATE; // called from apptask
|
||||
}
|
||||
|
||||
async function backup(app, backupSiteId, auditSource) {
|
||||
@@ -2872,18 +2900,18 @@ async function restoreApps(apps, options, auditSource) {
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
apps = apps.filter(app => app.installationState !== exports.ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand
|
||||
apps = apps.filter(app => app.installationState !== exports.ISTATE_PENDING_RESTORE); // safeguard against tasks being created non-stop if we crash on startup
|
||||
apps = apps.filter(app => app.installationState !== ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand
|
||||
apps = apps.filter(app => app.installationState !== ISTATE_PENDING_RESTORE); // safeguard against tasks being created non-stop if we crash on startup
|
||||
|
||||
for (const app of apps) {
|
||||
const [error, results] = await safe(backups.getByIdentifierAndStatePaged(app.id, backups.BACKUP_STATE_NORMAL, 1, 1));
|
||||
let installationState, restoreConfig;
|
||||
if (!error && results.length) {
|
||||
installationState = exports.ISTATE_PENDING_RESTORE;
|
||||
installationState = ISTATE_PENDING_RESTORE;
|
||||
// intentionally ignore any backupSite provided during restore by the user because the site may not have all the apps
|
||||
restoreConfig = { backupId: results[0].id };
|
||||
} else {
|
||||
installationState = exports.ISTATE_PENDING_INSTALL;
|
||||
installationState = ISTATE_PENDING_INSTALL;
|
||||
restoreConfig = null;
|
||||
}
|
||||
|
||||
@@ -2907,8 +2935,8 @@ async function configureApps(apps, options, auditSource) {
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
apps = apps.filter(app => app.installationState !== exports.ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand
|
||||
apps = apps.filter(app => app.installationState !== exports.ISTATE_PENDING_CONFIGURE); // safeguard against tasks being created non-stop if we crash on startup
|
||||
apps = apps.filter(app => app.installationState !== ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand
|
||||
apps = apps.filter(app => app.installationState !== ISTATE_PENDING_CONFIGURE); // safeguard against tasks being created non-stop if we crash on startup
|
||||
|
||||
const scheduleNow = !!options.scheduleNow;
|
||||
|
||||
@@ -2922,7 +2950,7 @@ async function configureApps(apps, options, auditSource) {
|
||||
requireNullTaskId: false // ignore existing stale taskId
|
||||
};
|
||||
|
||||
const [addTaskError, taskId] = await safe(addTask(app.id, exports.ISTATE_PENDING_CONFIGURE, task, auditSource));
|
||||
const [addTaskError, taskId] = await safe(addTask(app.id, ISTATE_PENDING_CONFIGURE, task, auditSource));
|
||||
if (addTaskError) debug(`configureApps: error marking ${app.fqdn} for configure: ${JSON.stringify(addTaskError)}`);
|
||||
else debug(`configureApps: marked ${app.id} for re-configure with taskId ${taskId}`);
|
||||
}
|
||||
@@ -2935,23 +2963,23 @@ async function restartAppsUsingAddons(changedAddons, auditSource) {
|
||||
let apps = await list();
|
||||
// TODO: This ends up restarting apps that have optional redis
|
||||
apps = apps.filter(app => app.manifest.addons && _.intersection(Object.keys(app.manifest.addons), changedAddons).length !== 0);
|
||||
apps = apps.filter(app => app.installationState !== exports.ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand
|
||||
apps = apps.filter(app => app.installationState !== exports.ISTATE_PENDING_RESTART); // safeguard against tasks being created non-stop restart if we crash on startup
|
||||
apps = apps.filter(app => app.runState !== exports.RSTATE_STOPPED); // don't start stopped apps
|
||||
apps = apps.filter(app => app.installationState !== ISTATE_ERROR); // remove errored apps. let them be 'repaired' by hand
|
||||
apps = apps.filter(app => app.installationState !== ISTATE_PENDING_RESTART); // safeguard against tasks being created non-stop restart if we crash on startup
|
||||
apps = apps.filter(app => app.runState !== RSTATE_STOPPED); // don't start stopped apps
|
||||
|
||||
for (const app of apps) {
|
||||
debug(`restartAppsUsingAddons: marking ${app.fqdn} for restart`);
|
||||
|
||||
const task = {
|
||||
args: {},
|
||||
values: { runState: exports.RSTATE_RUNNING }
|
||||
values: { runState: RSTATE_RUNNING }
|
||||
};
|
||||
|
||||
// stop apps before updating the databases because postgres will "lock" them preventing import
|
||||
const [stopError] = await safe(docker.stopContainers(app.id));
|
||||
if (stopError) debug(`restartAppsUsingAddons: error stopping ${app.fqdn}`, stopError);
|
||||
|
||||
const [addTaskError, taskId] = await safe(addTask(app.id, exports.ISTATE_PENDING_RESTART, task, auditSource));
|
||||
const [addTaskError, taskId] = await safe(addTask(app.id, ISTATE_PENDING_RESTART, task, auditSource));
|
||||
if (addTaskError) debug(`restartAppsUsingAddons: error marking ${app.fqdn} for restart: ${JSON.stringify(addTaskError)}`);
|
||||
else debug(`restartAppsUsingAddons: marked ${app.id} for restart with taskId ${taskId}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user