merge backupdb into backups.js
This commit is contained in:
212
src/apps.js
212
src/apps.js
@@ -1491,55 +1491,53 @@ function repair(app, data, auditSource, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function restore(app, backupId, auditSource, callback) {
|
||||
async function restore(app, backupId, auditSource) {
|
||||
assert.strictEqual(typeof app, 'object');
|
||||
assert.strictEqual(typeof backupId, 'string');
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
const appId = app.id;
|
||||
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_RESTORE);
|
||||
if (error) return callback(error);
|
||||
if (error) throw error;
|
||||
|
||||
// for empty or null backupId, use existing manifest to mimic a reinstall
|
||||
var func = backupId ? backups.get.bind(null, backupId) : function (next) { return next(null, { manifest: app.manifest }); };
|
||||
const backupInfo = backupId ? await backups.get(backupId) : { manifest: app.manifest };
|
||||
|
||||
func(function (error, backupInfo) {
|
||||
if (error) return callback(error);
|
||||
if (!backupInfo.manifest) throw new BoxError(BoxError.EXTERNAL_ERROR, 'Could not get restore manifest');
|
||||
if (backupInfo.encryptionVersion === 1) throw new BoxError(BoxError.BAD_FIELD, 'This encrypted backup was created with an older Cloudron version and has to be restored using the CLI tool');
|
||||
|
||||
if (!backupInfo.manifest) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Could not get restore manifest'));
|
||||
if (backupInfo.encryptionVersion === 1) return callback(new BoxError(BoxError.BAD_FIELD, 'This encrypted backup was created with an older Cloudron version and has to be restored using the CLI tool'));
|
||||
// re-validate because this new box version may not accept old configs
|
||||
error = checkManifestConstraints(backupInfo.manifest);
|
||||
if (error) throw error;
|
||||
|
||||
// re-validate because this new box version may not accept old configs
|
||||
error = checkManifestConstraints(backupInfo.manifest);
|
||||
if (error) return callback(error);
|
||||
let values = { manifest: backupInfo.manifest };
|
||||
if (!hasMailAddon(backupInfo.manifest)) { // clear if restore removed addon
|
||||
values.mailboxName = values.mailboxDomain = null;
|
||||
} else if (!app.mailboxName || app.mailboxName.endsWith('.app')) { // allocate since restore added the addon
|
||||
values.mailboxName = mailboxNameForLocation(app.location, backupInfo.manifest);
|
||||
values.mailboxDomain = app.domain;
|
||||
}
|
||||
|
||||
let values = { manifest: backupInfo.manifest };
|
||||
if (!hasMailAddon(backupInfo.manifest)) { // clear if restore removed addon
|
||||
values.mailboxName = values.mailboxDomain = null;
|
||||
} else if (!app.mailboxName || app.mailboxName.endsWith('.app')) { // allocate since restore added the addon
|
||||
values.mailboxName = mailboxNameForLocation(app.location, backupInfo.manifest);
|
||||
values.mailboxDomain = app.domain;
|
||||
}
|
||||
const restoreConfig = { backupId, backupFormat: backupInfo.format };
|
||||
|
||||
const restoreConfig = { backupId, backupFormat: backupInfo.format };
|
||||
const task = {
|
||||
args: {
|
||||
restoreConfig,
|
||||
oldManifest: app.manifest,
|
||||
skipDnsSetup: !!backupId, // if this is a restore, just skip dns setup. only re-installs should setup dns
|
||||
overwriteDns: true
|
||||
},
|
||||
values
|
||||
};
|
||||
|
||||
const task = {
|
||||
args: {
|
||||
restoreConfig,
|
||||
oldManifest: app.manifest,
|
||||
skipDnsSetup: !!backupId, // if this is a restore, just skip dns setup. only re-installs should setup dns
|
||||
overwriteDns: true
|
||||
},
|
||||
values
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
addTask(appId, exports.ISTATE_PENDING_RESTORE, task, function (error, result) {
|
||||
if (error) return callback(error);
|
||||
if (error) return reject(error);
|
||||
|
||||
eventlog.add(eventlog.ACTION_APP_RESTORE, auditSource, { app: app, backupId: backupInfo.id, fromManifest: app.manifest, toManifest: backupInfo.manifest, taskId: result.taskId });
|
||||
|
||||
callback(null, { taskId: result.taskId });
|
||||
resolve({ taskId: result.taskId });
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1656,9 +1654,13 @@ function clone(app, data, user, auditSource, callback) {
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
assert.strictEqual(typeof portBindings, 'object');
|
||||
|
||||
backups.get(backupId, function (error, backupInfo) {
|
||||
const locations = [{ subdomain: location, domain, type: 'primary' }];
|
||||
validateLocations(locations, async function (error, domainObjectMap) {
|
||||
if (error) return callback(error);
|
||||
|
||||
const [backupsError, backupInfo] = await safe(backups.get(backupId));
|
||||
if (backupsError) return callback(backupsError);
|
||||
|
||||
if (!backupInfo.manifest) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Could not get restore config'));
|
||||
if (backupInfo.encryptionVersion === 1) return callback(new BoxError(BoxError.BAD_FIELD, 'This encrypted backup was created with an older Cloudron version and cannot be cloned'));
|
||||
|
||||
@@ -1675,62 +1677,57 @@ function clone(app, data, user, auditSource, callback) {
|
||||
let mailboxName = hasMailAddon(manifest) ? mailboxNameForLocation(location, manifest) : null;
|
||||
let mailboxDomain = hasMailAddon(manifest) ? domain : null;
|
||||
|
||||
const locations = [{ subdomain: location, domain, type: 'primary' }];
|
||||
validateLocations(locations, function (error, domainObjectMap) {
|
||||
const newAppId = uuid.v4();
|
||||
|
||||
appdb.getIcons(app.id, function (error, icons) {
|
||||
if (error) return callback(error);
|
||||
|
||||
const newAppId = uuid.v4();
|
||||
const data = {
|
||||
installationState: exports.ISTATE_PENDING_CLONE,
|
||||
runState: exports.RSTATE_RUNNING,
|
||||
memoryLimit: app.memoryLimit,
|
||||
cpuShares: app.cpuShares,
|
||||
accessRestriction: app.accessRestriction,
|
||||
sso: !!app.sso,
|
||||
mailboxName: mailboxName,
|
||||
mailboxDomain: mailboxDomain,
|
||||
enableBackup: app.enableBackup,
|
||||
reverseProxyConfig: app.reverseProxyConfig,
|
||||
env: app.env,
|
||||
alternateDomains: [],
|
||||
aliasDomains: [],
|
||||
servicesConfig: app.servicesConfig,
|
||||
label: app.label ? `${app.label}-clone` : '',
|
||||
tags: app.tags,
|
||||
enableAutomaticUpdate: app.enableAutomaticUpdate,
|
||||
icon: icons.icon,
|
||||
enableMailbox: app.enableMailbox
|
||||
};
|
||||
|
||||
appdb.getIcons(app.id, function (error, icons) {
|
||||
appdb.add(newAppId, appStoreId, manifest, location, domain, translatePortBindings(portBindings, manifest), data, function (error) {
|
||||
if (error && error.reason === BoxError.ALREADY_EXISTS) return callback(getDuplicateErrorDetails(error.message, locations, domainObjectMap, portBindings));
|
||||
if (error) return callback(error);
|
||||
|
||||
const data = {
|
||||
installationState: exports.ISTATE_PENDING_CLONE,
|
||||
runState: exports.RSTATE_RUNNING,
|
||||
memoryLimit: app.memoryLimit,
|
||||
cpuShares: app.cpuShares,
|
||||
accessRestriction: app.accessRestriction,
|
||||
sso: !!app.sso,
|
||||
mailboxName: mailboxName,
|
||||
mailboxDomain: mailboxDomain,
|
||||
enableBackup: app.enableBackup,
|
||||
reverseProxyConfig: app.reverseProxyConfig,
|
||||
env: app.env,
|
||||
alternateDomains: [],
|
||||
aliasDomains: [],
|
||||
servicesConfig: app.servicesConfig,
|
||||
label: app.label ? `${app.label}-clone` : '',
|
||||
tags: app.tags,
|
||||
enableAutomaticUpdate: app.enableAutomaticUpdate,
|
||||
icon: icons.icon,
|
||||
enableMailbox: app.enableMailbox
|
||||
};
|
||||
|
||||
appdb.add(newAppId, appStoreId, manifest, location, domain, translatePortBindings(portBindings, manifest), data, function (error) {
|
||||
if (error && error.reason === BoxError.ALREADY_EXISTS) return callback(getDuplicateErrorDetails(error.message, locations, domainObjectMap, portBindings));
|
||||
purchaseApp({ appId: newAppId, appstoreId: app.appStoreId, manifestId: manifest.id || 'customapp' }, function (error) {
|
||||
if (error) return callback(error);
|
||||
|
||||
purchaseApp({ appId: newAppId, appstoreId: app.appStoreId, manifestId: manifest.id || 'customapp' }, function (error) {
|
||||
const restoreConfig = { backupId: backupId, backupFormat: backupInfo.format };
|
||||
const task = {
|
||||
args: { restoreConfig, overwriteDns, skipDnsSetup, oldManifest: null },
|
||||
values: {},
|
||||
requiredState: exports.ISTATE_PENDING_CLONE
|
||||
};
|
||||
addTask(newAppId, exports.ISTATE_PENDING_CLONE, task, function (error, result) {
|
||||
if (error) return callback(error);
|
||||
|
||||
const restoreConfig = { backupId: backupId, backupFormat: backupInfo.format };
|
||||
const task = {
|
||||
args: { restoreConfig, overwriteDns, skipDnsSetup, oldManifest: null },
|
||||
values: {},
|
||||
requiredState: exports.ISTATE_PENDING_CLONE
|
||||
};
|
||||
addTask(newAppId, exports.ISTATE_PENDING_CLONE, task, function (error, result) {
|
||||
if (error) return callback(error);
|
||||
const newApp = _.extend({}, _.omit(data, 'icon'), { appStoreId, manifest, location, domain, portBindings });
|
||||
newApp.fqdn = domains.fqdn(newApp.location, domainObjectMap[newApp.domain]);
|
||||
newApp.alternateDomains.forEach(function (ad) { ad.fqdn = domains.fqdn(ad.subdomain, domainObjectMap[ad.domain]); });
|
||||
newApp.aliasDomains.forEach(function (ad) { ad.fqdn = domains.fqdn(ad.subdomain, domainObjectMap[ad.domain]); });
|
||||
|
||||
const newApp = _.extend({}, _.omit(data, 'icon'), { appStoreId, manifest, location, domain, portBindings });
|
||||
newApp.fqdn = domains.fqdn(newApp.location, domainObjectMap[newApp.domain]);
|
||||
newApp.alternateDomains.forEach(function (ad) { ad.fqdn = domains.fqdn(ad.subdomain, domainObjectMap[ad.domain]); });
|
||||
newApp.aliasDomains.forEach(function (ad) { ad.fqdn = domains.fqdn(ad.subdomain, domainObjectMap[ad.domain]); });
|
||||
eventlog.add(eventlog.ACTION_APP_CLONE, auditSource, { appId: newAppId, oldAppId: appId, backupId: backupId, oldApp: app, newApp: newApp, taskId: result.taskId });
|
||||
|
||||
eventlog.add(eventlog.ACTION_APP_CLONE, auditSource, { appId: newAppId, oldAppId: appId, backupId: backupId, oldApp: app, newApp: newApp, taskId: result.taskId });
|
||||
|
||||
callback(null, { id: newAppId, taskId: result.taskId });
|
||||
});
|
||||
callback(null, { id: newAppId, taskId: result.taskId });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1976,59 +1973,54 @@ function backup(app, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function listBackups(app, page, perPage, callback) {
|
||||
async function listBackups(app, page, perPage) {
|
||||
assert.strictEqual(typeof app, 'object');
|
||||
assert(typeof page === 'number' && page > 0);
|
||||
assert(typeof perPage === 'number' && perPage > 0);
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
backups.getByIdentifierAndStatePaged(app.id, backups.BACKUP_STATE_NORMAL, page, perPage, function (error, results) {
|
||||
if (error) return callback(error);
|
||||
|
||||
callback(null, results);
|
||||
});
|
||||
return await backups.getByIdentifierAndStatePaged(app.id, backups.BACKUP_STATE_NORMAL, page, perPage);
|
||||
}
|
||||
|
||||
function restoreInstalledApps(options, callback) {
|
||||
assert.strictEqual(typeof options, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
getAll(function (error, apps) {
|
||||
const addTaskAsync = util.promisify(addTask);
|
||||
|
||||
getAll(async function (error, apps) {
|
||||
if (error) return callback(error);
|
||||
|
||||
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
|
||||
|
||||
async.eachSeries(apps, function (app, iteratorDone) {
|
||||
backups.getByIdentifierAndStatePaged(app.id, backups.BACKUP_STATE_NORMAL, 1, 1, function (error, results) {
|
||||
let installationState, restoreConfig, oldManifest;
|
||||
if (!error && results.length) {
|
||||
installationState = exports.ISTATE_PENDING_RESTORE;
|
||||
restoreConfig = { backupId: results[0].id, backupFormat: results[0].format };
|
||||
oldManifest = app.manifest;
|
||||
} else {
|
||||
installationState = exports.ISTATE_PENDING_INSTALL;
|
||||
restoreConfig = null;
|
||||
oldManifest = null;
|
||||
}
|
||||
for (const app of apps) {
|
||||
const [error, results] = await safe(backups.getByIdentifierAndStatePaged(app.id, backups.BACKUP_STATE_NORMAL, 1, 1));
|
||||
let installationState, restoreConfig, oldManifest;
|
||||
if (!error && results.length) {
|
||||
installationState = exports.ISTATE_PENDING_RESTORE;
|
||||
restoreConfig = { backupId: results[0].id, backupFormat: results[0].format };
|
||||
oldManifest = app.manifest;
|
||||
} else {
|
||||
installationState = exports.ISTATE_PENDING_INSTALL;
|
||||
restoreConfig = null;
|
||||
oldManifest = null;
|
||||
}
|
||||
|
||||
const task = {
|
||||
args: { restoreConfig, skipDnsSetup: options.skipDnsSetup, overwriteDns: true, oldManifest },
|
||||
values: {},
|
||||
scheduleNow: false, // task will be scheduled by autoRestartTasks when platform is ready
|
||||
requireNullTaskId: false // ignore existing stale taskId
|
||||
};
|
||||
const task = {
|
||||
args: { restoreConfig, skipDnsSetup: options.skipDnsSetup, overwriteDns: true, oldManifest },
|
||||
values: {},
|
||||
scheduleNow: false, // task will be scheduled by autoRestartTasks when platform is ready
|
||||
requireNullTaskId: false // ignore existing stale taskId
|
||||
};
|
||||
|
||||
debug(`restoreInstalledApps: marking ${app.fqdn} for restore using restore config ${JSON.stringify(restoreConfig)}`);
|
||||
debug(`restoreInstalledApps: marking ${app.fqdn} for restore using restore config ${JSON.stringify(restoreConfig)}`);
|
||||
|
||||
addTask(app.id, installationState, task, function (error, result) {
|
||||
if (error) debug(`restoreInstalledApps: error marking ${app.fqdn} for restore: ${JSON.stringify(error)}`);
|
||||
else debug(`restoreInstalledApps: marked ${app.id} for restore with taskId ${result.taskId}`);
|
||||
const [addTaskError, result] = await safe(addTaskAsync(app.id, installationState, task));
|
||||
if (addTaskError) debug(`restoreInstalledApps: error marking ${app.fqdn} for restore: ${JSON.stringify(addTaskError)}`);
|
||||
else debug(`restoreInstalledApps: marked ${app.id} for restore with taskId ${result.taskId}`);
|
||||
}
|
||||
|
||||
iteratorDone(); // ignore error
|
||||
});
|
||||
});
|
||||
}, callback);
|
||||
callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user