diff --git a/migrations/20170816211111-appdb-add-enableBackup.js.js b/migrations/20170816211111-appdb-add-enableBackup.js.js new file mode 100644 index 000000000..ce01d78ff --- /dev/null +++ b/migrations/20170816211111-appdb-add-enableBackup.js.js @@ -0,0 +1,16 @@ +'use strict'; + +exports.up = function(db, callback) { + db.runSql('ALTER TABLE apps ADD COLUMN enableBackup BOOLEAN DEFAULT 1', function (error) { + if (error) console.error(error); + callback(error); + }); +}; + +exports.down = function(db, callback) { + db.runSql('ALTER TABLE apps DROP COLUMN enableBackup', function (error) { + if (error) console.error(error); + callback(error); + }); +}; + diff --git a/migrations/schema.sql b/migrations/schema.sql index a4ad763b8..887f96340 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -69,6 +69,7 @@ CREATE TABLE IF NOT EXISTS apps( sso BOOLEAN DEFAULT 1, // whether user chose to enable SSO debugModeJson TEXT, // options for development mode robotsTxt TEXT, + enableBackup BOOLEAN DEFAULT 1, // the following fields do not belong here, they can be removed when we use a queue for apptask lastBackupId VARCHAR(128), // used to pass backupId to restore from to apptask diff --git a/src/appdb.js b/src/appdb.js index e7cea43bd..b5d63f45d 100644 --- a/src/appdb.js +++ b/src/appdb.js @@ -60,7 +60,7 @@ var assert = require('assert'), var APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.installationState', 'apps.installationProgress', 'apps.runState', 'apps.health', 'apps.containerId', 'apps.manifestJson', 'apps.httpPort', 'apps.location', 'apps.dnsRecordId', 'apps.accessRestrictionJson', 'apps.lastBackupId', 'apps.oldConfigJson', 'apps.memoryLimit', 'apps.altDomain', - 'apps.xFrameOptions', 'apps.sso', 'apps.debugModeJson', 'apps.robotsTxt' ].join(','); + 'apps.xFrameOptions', 'apps.sso', 'apps.debugModeJson', 'apps.robotsTxt', 'apps.enableBackup' ].join(','); var PORT_BINDINGS_FIELDS = [ 'hostPort', 'environmentVariable', 'appId' ].join(','); @@ -98,6 +98,7 @@ function postProcess(result) { result.xFrameOptions = result.xFrameOptions || 'SAMEORIGIN'; result.sso = !!result.sso; // make it bool + result.enableBackup = !!result.enableBackup; // make it bool assert(result.debugModeJson === null || typeof result.debugModeJson === 'string'); result.debugMode = safe.JSON.parse(result.debugModeJson); diff --git a/src/apps.js b/src/apps.js index ba21e98d3..d37a006ea 100644 --- a/src/apps.js +++ b/src/apps.js @@ -415,6 +415,7 @@ function install(data, auditSource, callback) { sso = 'sso' in data ? data.sso : null, debugMode = data.debugMode || null, robotsTxt = data.robotsTxt || null, + enableBackup = 'enableBackup' in data ? data.enableBackup : true, backupId = data.backupId || null; assert(data.appStoreId || data.manifest); // atleast one of them is required @@ -484,7 +485,8 @@ function install(data, auditSource, callback) { sso: sso, debugMode: debugMode, mailboxName: (location ? location : manifest.title.toLowerCase().replace(/[^a-zA-Z0-9]/g, '')) + '.app', - lastBackupId: backupId + lastBackupId: backupId, + enableBackup: enableBackup }; appdb.add(appId, appStoreId, manifest, location, portBindings, data, function (error) { @@ -583,6 +585,8 @@ function configure(appId, data, auditSource, callback) { } } + if ('enableBackup' in data) values.enableBackup = data.enableBackup; + values.oldConfig = getAppConfig(app); debug('Will configure app with id:%s values:%j', appId, values); diff --git a/src/backups.js b/src/backups.js index a24a5b989..c1ac1f2c4 100644 --- a/src/backups.js +++ b/src/backups.js @@ -370,6 +370,11 @@ function backupBoxAndApps(auditSource, callback) { ++processed; + if (!app.enableBackup) { + progress.set(progress.BACKUP, step * processed, 'Skipped backup ' + (app.altDomain || config.appFqdn(app.location))); + return iteratorCallback(null, app.lastBackupId); // just use the last backup + } + backupApp(app, app.manifest, prefix, function (error, backupId) { if (error && error.reason !== BackupsError.BAD_STATE) { debugApp(app, 'Unable to backup', error); diff --git a/src/routes/apps.js b/src/routes/apps.js index 65a74e20f..4ec2c4974 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -58,7 +58,8 @@ function removeInternalAppFields(app) { xFrameOptions: app.xFrameOptions, sso: app.sso, debugMode: app.debugMode, - robotsTxt: app.robotsTxt + robotsTxt: app.robotsTxt, + enableBackup: app.enableBackup }; } @@ -130,6 +131,7 @@ function installApp(req, res, next) { if (data.xFrameOptions && typeof data.xFrameOptions !== 'string') return next(new HttpError(400, 'xFrameOptions must be a string')); if ('sso' in data && typeof data.sso !== 'boolean') return next(new HttpError(400, 'sso must be a boolean')); + if ('enableBackup' in data && typeof data.enableBackup !== 'boolean') return next(new HttpError(400, 'enableBackup must be a boolean')); if (('debugMode' in data) && typeof data.debugMode !== 'object') return next(new HttpError(400, 'debugMode must be an object')); @@ -171,6 +173,8 @@ function configureApp(req, res, next) { if (data.altDomain && typeof data.altDomain !== 'string') return next(new HttpError(400, 'altDomain must be a string')); if (data.xFrameOptions && typeof data.xFrameOptions !== 'string') return next(new HttpError(400, 'xFrameOptions must be a string')); + if ('enableBackup' in data && typeof data.enableBackup !== 'boolean') return next(new HttpError(400, 'enableBackup must be a boolean')); + if (('debugMode' in data) && typeof data.debugMode !== 'object') return next(new HttpError(400, 'debugMode must be an object')); if (data.robotsTxt && typeof data.robotsTxt !== 'string') return next(new HttpError(400, 'robotsTxt must be a string')); diff --git a/webadmin/src/js/client.js b/webadmin/src/js/client.js index 344c12e8a..a687bb7a8 100644 --- a/webadmin/src/js/client.js +++ b/webadmin/src/js/client.js @@ -338,7 +338,8 @@ angular.module('Application').service('Client', ['$http', 'md5', 'Notification', memoryLimit: config.memoryLimit, altDomain: config.altDomain || null, xFrameOptions: config.xFrameOptions, - robotsTxt: config.robotsTxt || null + robotsTxt: config.robotsTxt || null, + enableBackup: config.enableBackup }; post('/api/v1/apps/' + id + '/configure', data).success(function (data, status) { diff --git a/webadmin/src/views/apps.html b/webadmin/src/views/apps.html index be58b677d..8bbd080a0 100644 --- a/webadmin/src/views/apps.html +++ b/webadmin/src/views/apps.html @@ -133,6 +133,11 @@ +
+ + +
+
{{ appConfigure.error.cert }}
diff --git a/webadmin/src/views/apps.js b/webadmin/src/views/apps.js index f01e66ae9..8bd42848f 100644 --- a/webadmin/src/views/apps.js +++ b/webadmin/src/views/apps.js @@ -122,6 +122,7 @@ angular.module('Application').controller('AppsController', ['$scope', '$location $scope.appConfigure.xFrameOptions = ''; $scope.appConfigure.customAuth = false; $scope.appConfigure.robotsTxt = ''; + $scope.appConfigure.enableBackup = true; $scope.appConfigureForm.$setPristine(); $scope.appConfigureForm.$setUntouched(); @@ -215,6 +216,7 @@ angular.module('Application').controller('AppsController', ['$scope', '$location $scope.appConfigure.xFrameOptions = app.xFrameOptions.indexOf('ALLOW-FROM') === 0 ? app.xFrameOptions.split(' ')[1] : ''; $scope.appConfigure.customAuth = !(app.manifest.addons['ldap'] || app.manifest.addons['oauth']); $scope.appConfigure.robotsTxt = app.robotsTxt; + $scope.appConfigure.enableBackup = app.enableBackup; // create ticks starting from manifest memory limit $scope.appConfigure.memoryTicks = [ @@ -268,7 +270,8 @@ angular.module('Application').controller('AppsController', ['$scope', '$location key: $scope.appConfigure.keyFile, xFrameOptions: $scope.appConfigure.xFrameOptions ? ('ALLOW-FROM ' + $scope.appConfigure.xFrameOptions) : 'SAMEORIGIN', memoryLimit: $scope.appConfigure.memoryLimit === $scope.appConfigure.memoryTicks[0] ? 0 : $scope.appConfigure.memoryLimit, - robotsTxt: $scope.appConfigure.robotsTxt + robotsTxt: $scope.appConfigure.robotsTxt, + enableBackup: $scope.appConfigure.enableBackup }; Client.configureApp($scope.appConfigure.app.id, $scope.appConfigure.password, data, function (error) {