diff --git a/docs/references/api.md b/docs/references/api.md index 947134b27..55f955096 100644 --- a/docs/references/api.md +++ b/docs/references/api.md @@ -117,6 +117,7 @@ Request: cert: , // pem encoded TLS cert key: , // pem encoded TLS key memoryLimit: , // memory constraint in bytes + backupId: , // initialize the app from this backup altDomain: , // alternate domain from which this app can be reached xFrameOptions: // set X-Frame-Options header, to control which websites can embed this app } @@ -153,6 +154,8 @@ If `altDomain` is set, the app can be accessed from `https://`. `memoryLimit` is the maximum memory this app can use (in bytes) including swap. If set to 0, the app uses the `memoryLimit` value set in the manifest. If set to -1, the app gets unlimited memory. +If `backupId` is provided the app will be initialized with the data from the backup. + Read more about the options at [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options). Response (200): diff --git a/src/apps.js b/src/apps.js index f10a3b8c3..65ab1c7d4 100644 --- a/src/apps.js +++ b/src/apps.js @@ -494,7 +494,8 @@ function install(data, auditSource, callback) { altDomain = data.altDomain || null, xFrameOptions = data.xFrameOptions || 'SAMEORIGIN', sso = 'sso' in data ? data.sso : null, - debugMode = data.debugMode || null; + debugMode = data.debugMode || null, + backupId = data.backupId || null; assert(data.appStoreId || data.manifest); // atleast one of them is required @@ -556,7 +557,8 @@ function install(data, auditSource, callback) { xFrameOptions: xFrameOptions, sso: sso, debugMode: debugMode, - mailboxName: (location ? location : manifest.title.toLowerCase().replace(/[^a-zA-Z0-9]/g, '')) + '.app' + mailboxName: (location ? location : manifest.title.toLowerCase().replace(/[^a-zA-Z0-9]/g, '')) + '.app', + lastBackupId: backupId }; appdb.add(appId, appStoreId, manifest, location, portBindings, data, function (error) { @@ -571,7 +573,7 @@ function install(data, auditSource, callback) { taskmanager.restartAppTask(appId); - eventlog.add(eventlog.ACTION_APP_INSTALL, auditSource, { appId: appId, location: location, manifest: manifest }); + eventlog.add(eventlog.ACTION_APP_INSTALL, auditSource, { appId: appId, location: location, manifest: manifest, backupId: backupId }); callback(null, { id : appId }); }); diff --git a/src/apptask.js b/src/apptask.js index 23cc4754c..173783062 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -343,6 +343,8 @@ function install(app, callback) { assert.strictEqual(typeof app, 'object'); assert.strictEqual(typeof callback, 'function'); + var backupId = app.lastBackupId; + async.series([ verifyManifest.bind(null, app), @@ -373,6 +375,15 @@ function install(app, callback) { updateApp.bind(null, app, { installationProgress: '60, Setting up addons' }), addons.setupAddons.bind(null, app, app.manifest.addons), + function restoreFromBackup(next) { + if (!backupId) return next(); + + async.series([ + updateApp.bind(null, app, { installationProgress: '65, Download backup and restore addons' }), + backups.restoreApp.bind(null, app, app.manifest.addons, backupId), + ], next); + }, + updateApp.bind(null, app, { installationProgress: '70, Creating container' }), createContainer.bind(null, app), diff --git a/src/routes/apps.js b/src/routes/apps.js index 3a0782001..767ac2aa8 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -113,6 +113,8 @@ function installApp(req, res, next) { if (('portBindings' in data) && typeof data.portBindings !== 'object') return next(new HttpError(400, 'portBindings must be an object')); if ('icon' in data && typeof data.icon !== 'string') return next(new HttpError(400, 'icon is not a string')); + if (data.backupId && typeof data.backupId !== 'string') return next(new HttpError(400, 'backupId must be string or null')); + // falsy values in cert and key unset the cert if (data.key && typeof data.cert !== 'string') return next(new HttpError(400, 'cert must be a string')); if (data.cert && typeof data.key !== 'string') return next(new HttpError(400, 'key must be a string'));