2018-06-07 14:22:48 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
2022-12-24 14:48:46 +01:00
|
|
|
/* global $, angular, TASK_TYPES, SECRET_PLACEHOLDER, STORAGE_PROVIDERS, BACKUP_FORMATS, APP_TYPES */
|
2022-09-27 13:31:13 +02:00
|
|
|
/* global REGIONS_S3, REGIONS_WASABI, REGIONS_DIGITALOCEAN, REGIONS_EXOSCALE, REGIONS_SCALEWAY, REGIONS_LINODE, REGIONS_OVH, REGIONS_IONOS, REGIONS_UPCLOUD, REGIONS_VULTR */
|
2019-01-22 10:54:03 +01:00
|
|
|
|
|
|
|
|
angular.module('Application').controller('BackupsController', ['$scope', '$location', '$rootScope', '$timeout', 'Client', function ($scope, $location, $rootScope, $timeout, Client) {
|
2020-02-24 12:56:13 +01:00
|
|
|
Client.onReady(function () { if (!Client.getUserInfo().isAtLeastAdmin) $location.path('/'); });
|
2018-06-07 14:22:48 +02:00
|
|
|
|
2020-11-14 11:01:46 +01:00
|
|
|
$scope.SECRET_PLACEHOLDER = SECRET_PLACEHOLDER;
|
2021-10-26 09:24:15 -07:00
|
|
|
$scope.MIN_MEMORY_LIMIT = 800 * 1024 * 1024;
|
2020-11-14 11:01:46 +01:00
|
|
|
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.config = Client.getConfig();
|
2018-09-05 14:37:54 -07:00
|
|
|
$scope.user = Client.getUserInfo();
|
2020-09-10 00:07:12 -07:00
|
|
|
$scope.memory = null; // { memory, swap }
|
2018-09-05 14:37:54 -07:00
|
|
|
|
2020-04-03 10:36:51 -07:00
|
|
|
$scope.manualBackupApps = [];
|
|
|
|
|
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.backupConfig = {};
|
|
|
|
|
$scope.backups = [];
|
|
|
|
|
|
2022-09-27 13:31:13 +02:00
|
|
|
$scope.s3Regions = REGIONS_S3;
|
|
|
|
|
$scope.wasabiRegions = REGIONS_WASABI;
|
|
|
|
|
$scope.doSpacesRegions = REGIONS_DIGITALOCEAN;
|
|
|
|
|
$scope.exoscaleSosRegions = REGIONS_EXOSCALE;
|
|
|
|
|
$scope.scalewayRegions = REGIONS_SCALEWAY;
|
|
|
|
|
$scope.linodeRegions = REGIONS_LINODE;
|
|
|
|
|
$scope.ovhRegions = REGIONS_OVH;
|
|
|
|
|
$scope.ionosRegions = REGIONS_IONOS;
|
|
|
|
|
$scope.upcloudRegions = REGIONS_UPCLOUD;
|
|
|
|
|
$scope.vultrRegions = REGIONS_VULTR;
|
|
|
|
|
|
2022-12-24 11:10:04 +01:00
|
|
|
$scope.storageProviders = STORAGE_PROVIDERS.concat([
|
2020-06-05 12:48:27 +02:00
|
|
|
{ name: 'No-op (Only for testing)', value: 'noop' }
|
2022-09-27 13:31:13 +02:00
|
|
|
]);
|
2018-06-07 14:22:48 +02:00
|
|
|
|
2020-05-14 16:27:06 -07:00
|
|
|
$scope.retentionPolicies = [
|
2020-05-14 16:45:52 -07:00
|
|
|
{ name: '2 days', value: { keepWithinSecs: 2 * 24 * 60 * 60 }},
|
|
|
|
|
{ name: '1 week', value: { keepWithinSecs: 7 * 24 * 60 * 60 }}, // default
|
|
|
|
|
{ name: '1 month', value: { keepWithinSecs: 30 * 24 * 60 * 60 }},
|
2022-05-26 08:38:18 -07:00
|
|
|
{ name: '3 months', value: { keepWithinSecs: 3 * 30 * 24 * 60 * 60 }},
|
2020-05-21 14:09:06 -07:00
|
|
|
{ name: '2 daily, 4 weekly', value: { keepDaily: 2, keepWeekly: 4 }},
|
2020-05-14 21:36:22 -07:00
|
|
|
{ name: '3 daily, 4 weekly, 6 monthly', value: { keepDaily: 3, keepWeekly: 4, keepMonthly: 6 }},
|
|
|
|
|
{ name: '7 daily, 4 weekly, 12 monthly', value: { keepDaily: 7, keepWeekly: 4, keepMonthly: 12 }},
|
2020-05-14 16:45:52 -07:00
|
|
|
{ name: 'Forever', value: { keepWithinSecs: -1 }}
|
2018-06-07 14:22:48 +02:00
|
|
|
];
|
|
|
|
|
|
2020-07-28 21:48:24 -07:00
|
|
|
// values correspond to cron days
|
2020-07-29 15:09:06 -07:00
|
|
|
$scope.cronDays = [
|
2020-07-28 21:48:24 -07:00
|
|
|
{ name: 'Sunday', value: 0 },
|
|
|
|
|
{ name: 'Monday', value: 1 },
|
|
|
|
|
{ name: 'Tuesday', value: 2 },
|
|
|
|
|
{ name: 'Wednesday', value: 3 },
|
|
|
|
|
{ name: 'Thursday', value: 4 },
|
|
|
|
|
{ name: 'Friday', value: 5 },
|
|
|
|
|
{ name: 'Saturday', value: 6 },
|
|
|
|
|
];
|
|
|
|
|
|
2021-03-31 14:42:10 +02:00
|
|
|
// generates 24h time sets (instead of american 12h) to avoid having to translate everything to locales eg. 12:00
|
|
|
|
|
$scope.cronHours = Array.from({ length: 24 }).map(function (v, i) { return { name: (i < 10 ? '0' : '') + i + ':00', value: i }; });
|
2018-08-13 22:40:02 -07:00
|
|
|
|
2022-09-27 13:31:13 +02:00
|
|
|
$scope.formats = BACKUP_FORMATS;
|
2018-06-07 14:22:48 +02:00
|
|
|
|
|
|
|
|
$scope.prettyProviderName = function (provider) {
|
|
|
|
|
switch (provider) {
|
|
|
|
|
case 'caas': return 'Managed Cloudron';
|
|
|
|
|
default: return provider;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-28 21:48:24 -07:00
|
|
|
$scope.prettyBackupSchedule = function (pattern) {
|
|
|
|
|
if (!pattern) return '';
|
|
|
|
|
var tmp = pattern.split(' ');
|
|
|
|
|
var hours = tmp[2].split(','), days = tmp[5].split(',');
|
|
|
|
|
var prettyDay;
|
|
|
|
|
if (days.length === 7 || days[0] === '*') {
|
|
|
|
|
prettyDay = 'Everyday';
|
|
|
|
|
} else {
|
2020-07-29 15:09:06 -07:00
|
|
|
prettyDay = days.map(function (day) { return $scope.cronDays[parseInt(day, 10)].name.substr(0, 3); }).join(',');
|
2020-07-28 21:48:24 -07:00
|
|
|
}
|
|
|
|
|
|
2020-07-29 15:09:06 -07:00
|
|
|
var prettyHour = hours.map(function (hour) { return $scope.cronHours[parseInt(hour, 10)].name; }).join(',');
|
2020-07-28 21:48:24 -07:00
|
|
|
|
|
|
|
|
return prettyDay + ' at ' + prettyHour;
|
2020-05-19 16:13:20 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.prettyBackupRetentionPolicy = function (retentionPolicy) {
|
|
|
|
|
var tmp = $scope.retentionPolicies.find(function (p) { return angular.equals(p.value, retentionPolicy); });
|
|
|
|
|
return tmp ? tmp.name : '';
|
|
|
|
|
};
|
|
|
|
|
|
2021-10-11 17:45:55 +02:00
|
|
|
$scope.remount = {
|
|
|
|
|
busy: false,
|
|
|
|
|
error: null,
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
if (!$scope.mountlike($scope.backupConfig.provider)) return;
|
|
|
|
|
|
|
|
|
|
$scope.remount.busy = true;
|
|
|
|
|
$scope.remount.error = null;
|
|
|
|
|
|
|
|
|
|
Client.remountBackupStorage(function (error) {
|
|
|
|
|
if (error) {
|
|
|
|
|
console.error('Failed to remount backup storage.', error);
|
|
|
|
|
$scope.remount.error = error.message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// give the backend some time
|
|
|
|
|
$timeout(function () {
|
|
|
|
|
$scope.remount.busy = false;
|
|
|
|
|
getBackupConfig();
|
|
|
|
|
}, 2000);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.createBackup = {
|
|
|
|
|
busy: false,
|
|
|
|
|
percent: 0,
|
|
|
|
|
message: '',
|
|
|
|
|
errorMessage: '',
|
2018-12-08 21:45:49 -08:00
|
|
|
taskId: '',
|
2020-09-01 16:36:07 +02:00
|
|
|
taskType: TASK_TYPES.TASK_BACKUP,
|
2018-06-07 14:22:48 +02:00
|
|
|
|
2018-12-08 20:21:11 -08:00
|
|
|
checkStatus: function () {
|
2020-09-01 16:36:07 +02:00
|
|
|
// TODO support both task types TASK_BACKUP and TASK_CLEAN_BACKUPS
|
2020-05-14 22:42:41 -07:00
|
|
|
Client.getLatestTaskByType($scope.createBackup.taskType, function (error, task) {
|
2018-12-08 20:21:11 -08:00
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
|
|
|
|
if (!task) return;
|
|
|
|
|
|
2018-12-08 21:45:49 -08:00
|
|
|
$scope.createBackup.taskId = task.id;
|
|
|
|
|
$scope.createBackup.updateStatus();
|
2018-12-08 20:21:11 -08:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2018-12-08 21:45:49 -08:00
|
|
|
updateStatus: function () {
|
|
|
|
|
Client.getTask($scope.createBackup.taskId, function (error, data) {
|
|
|
|
|
if (error) return window.setTimeout($scope.createBackup.updateStatus, 5000);
|
2018-06-07 14:22:48 +02:00
|
|
|
|
2018-11-29 23:13:58 -08:00
|
|
|
if (!data.active) {
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.createBackup.busy = false;
|
|
|
|
|
$scope.createBackup.message = '';
|
|
|
|
|
$scope.createBackup.percent = 100; // indicates that 'result' is valid
|
2019-08-30 14:24:59 -07:00
|
|
|
$scope.createBackup.errorMessage = data.success ? '' : data.error.message;
|
2018-06-07 14:22:48 +02:00
|
|
|
|
|
|
|
|
return fetchBackups();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.createBackup.busy = true;
|
2018-11-19 17:34:14 -08:00
|
|
|
$scope.createBackup.percent = data.percent;
|
|
|
|
|
$scope.createBackup.message = data.message;
|
2019-04-03 11:45:56 -07:00
|
|
|
window.setTimeout($scope.createBackup.updateStatus, 3000);
|
2018-06-07 14:22:48 +02:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2018-11-17 20:04:41 -08:00
|
|
|
startBackup: function () {
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.createBackup.busy = true;
|
|
|
|
|
$scope.createBackup.percent = 0;
|
|
|
|
|
$scope.createBackup.message = '';
|
|
|
|
|
$scope.createBackup.errorMessage = '';
|
2020-09-01 16:36:07 +02:00
|
|
|
$scope.createBackup.taskType = TASK_TYPES.TASK_BACKUP;
|
2018-06-07 14:22:48 +02:00
|
|
|
|
2018-12-08 20:21:11 -08:00
|
|
|
Client.startBackup(function (error, taskId) {
|
2018-06-07 14:22:48 +02:00
|
|
|
if (error) {
|
|
|
|
|
if (error.statusCode === 409 && error.message.indexOf('full_backup') !== -1) {
|
|
|
|
|
$scope.createBackup.errorMessage = 'Backup already in progress. Please retry later.';
|
|
|
|
|
} else if (error.statusCode === 409) {
|
|
|
|
|
$scope.createBackup.errorMessage = 'App task is currently in progress. Please retry later.';
|
|
|
|
|
} else {
|
|
|
|
|
console.error(error);
|
|
|
|
|
$scope.createBackup.errorMessage = error.message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.createBackup.busy = false;
|
|
|
|
|
$('#createBackupFailedModal').modal('show');
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-08 21:45:49 -08:00
|
|
|
$scope.createBackup.taskId = taskId;
|
|
|
|
|
$scope.createBackup.updateStatus();
|
2018-06-07 14:22:48 +02:00
|
|
|
});
|
2018-11-17 20:04:41 -08:00
|
|
|
},
|
|
|
|
|
|
2020-07-28 21:48:24 -07:00
|
|
|
cleanupBackups: function () {
|
2020-07-15 15:10:37 -07:00
|
|
|
$('#cleanupBackupsModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
2020-05-14 22:42:41 -07:00
|
|
|
startCleanup: function () {
|
|
|
|
|
$scope.createBackup.busy = true;
|
|
|
|
|
$scope.createBackup.percent = 0;
|
|
|
|
|
$scope.createBackup.message = '';
|
|
|
|
|
$scope.createBackup.errorMessage = '';
|
2020-09-01 16:36:07 +02:00
|
|
|
$scope.createBackup.taskType = TASK_TYPES.TASK_CLEAN_BACKUPS;
|
2020-05-14 22:42:41 -07:00
|
|
|
|
2020-07-15 15:10:37 -07:00
|
|
|
$('#cleanupBackupsModal').modal('hide');
|
|
|
|
|
|
2020-05-14 22:42:41 -07:00
|
|
|
Client.cleanupBackups(function (error, taskId) {
|
|
|
|
|
if (error) console.error(error);
|
|
|
|
|
|
|
|
|
|
$scope.createBackup.taskId = taskId;
|
|
|
|
|
$scope.createBackup.updateStatus();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
stopTask: function () {
|
2019-01-24 15:55:21 -08:00
|
|
|
Client.stopTask($scope.createBackup.taskId, function (error) {
|
2018-11-17 20:04:41 -08:00
|
|
|
if (error) {
|
|
|
|
|
if (error.statusCode === 409) {
|
|
|
|
|
$scope.createBackup.errorMessage = 'No backup is currently in progress';
|
|
|
|
|
} else {
|
|
|
|
|
console.error(error);
|
|
|
|
|
$scope.createBackup.errorMessage = error.message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.createBackup.busy = false;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
});
|
2018-06-07 14:22:48 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-14 22:42:41 -07:00
|
|
|
$scope.listBackups = {
|
|
|
|
|
};
|
|
|
|
|
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.s3like = function (provider) {
|
2019-04-12 10:04:26 -07:00
|
|
|
return provider === 's3' || provider === 'minio' || provider === 's3-v4-compat'
|
|
|
|
|
|| provider === 'exoscale-sos' || provider === 'digitalocean-spaces'
|
2022-09-27 19:40:58 +02:00
|
|
|
|| provider === 'scaleway-objectstorage' || provider === 'wasabi' || provider === 'backblaze-b2' || provider === 'cloudflare-r2'
|
2021-09-27 10:01:09 -07:00
|
|
|
|| provider === 'linode-objectstorage' || provider === 'ovh-objectstorage' || provider === 'ionos-objectstorage'
|
2023-01-10 11:16:09 +01:00
|
|
|
|| provider === 'vultr-objectstorage' || provider === 'upcloud-objectstorage' || provider === 'idrive-e2';
|
2018-06-07 14:22:48 +02:00
|
|
|
};
|
|
|
|
|
|
2020-06-22 15:51:18 +02:00
|
|
|
$scope.mountlike = function (provider) {
|
2022-06-08 10:38:54 -07:00
|
|
|
return provider === 'sshfs' || provider === 'cifs' || provider === 'nfs' || provider === 'mountpoint' || provider === 'ext4' || provider === 'xfs';
|
2020-06-22 15:51:18 +02:00
|
|
|
};
|
|
|
|
|
|
2020-04-20 18:21:35 +02:00
|
|
|
// https://stackoverflow.com/questions/3665115/how-to-create-a-file-in-memory-for-user-to-download-but-not-through-server#18197341
|
|
|
|
|
function download(filename, text) {
|
|
|
|
|
var element = document.createElement('a');
|
|
|
|
|
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
|
|
|
|
|
element.setAttribute('download', filename);
|
|
|
|
|
|
|
|
|
|
element.style.display = 'none';
|
|
|
|
|
document.body.appendChild(element);
|
|
|
|
|
|
|
|
|
|
element.click();
|
|
|
|
|
|
|
|
|
|
document.body.removeChild(element);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-14 22:42:41 -07:00
|
|
|
$scope.downloadConfig = function (backup) {
|
2020-05-14 23:19:17 +02:00
|
|
|
// secrets and tokens already come with placeholder characters we remove them
|
|
|
|
|
var tmp = {
|
2022-04-05 09:41:09 -07:00
|
|
|
remotePath: backup.remotePath,
|
2020-05-14 23:19:17 +02:00
|
|
|
encrypted: !!$scope.backupConfig.password // we add this just to help the import UI
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Object.keys($scope.backupConfig).forEach(function (k) {
|
|
|
|
|
if ($scope.backupConfig[k] !== SECRET_PLACEHOLDER) tmp[k] = $scope.backupConfig[k];
|
|
|
|
|
});
|
|
|
|
|
|
2022-02-22 16:25:43 +01:00
|
|
|
var filename = 'cloudron-backup-config-' + (new Date()).toISOString().replace(/:|T/g,'-').replace(/\..*/,'') + '.json';
|
2022-02-22 16:28:14 +01:00
|
|
|
download(filename, JSON.stringify(tmp, null, 4));
|
2020-04-20 18:21:35 +02:00
|
|
|
};
|
|
|
|
|
|
2022-04-05 14:41:41 +02:00
|
|
|
$scope.editBackup = {
|
|
|
|
|
busy: false,
|
|
|
|
|
error: null,
|
|
|
|
|
backup: null,
|
|
|
|
|
|
|
|
|
|
label: '',
|
2022-04-07 15:31:41 +02:00
|
|
|
persist: false,
|
2022-04-05 14:41:41 +02:00
|
|
|
|
|
|
|
|
show: function (backup) {
|
|
|
|
|
$scope.editBackup.backup = backup;
|
|
|
|
|
$scope.editBackup.label = backup.label;
|
2022-04-07 15:31:41 +02:00
|
|
|
$scope.editBackup.persist = backup.preserveSecs === -1;
|
2022-04-05 14:41:41 +02:00
|
|
|
$scope.editBackup.error = null;
|
|
|
|
|
$scope.editBackup.busy = false;
|
|
|
|
|
|
|
|
|
|
$('#editBackupModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.editBackup.error = null;
|
|
|
|
|
$scope.editBackup.busy = true;
|
|
|
|
|
|
2022-04-07 15:31:41 +02:00
|
|
|
Client.editBackup($scope.editBackup.backup.id, $scope.editBackup.label, $scope.editBackup.persist ? -1 : 0, function (error) {
|
2022-04-05 14:41:41 +02:00
|
|
|
$scope.editBackup.busy = false;
|
|
|
|
|
if (error) return $scope.editBackup.error = error.message;
|
|
|
|
|
|
|
|
|
|
fetchBackups();
|
|
|
|
|
|
|
|
|
|
$('#editBackupModal').modal('hide');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-22 13:48:26 +02:00
|
|
|
$scope.backupDetails = {
|
|
|
|
|
backup: null,
|
|
|
|
|
|
|
|
|
|
show: function (backup) {
|
|
|
|
|
$scope.backupDetails.backup = backup;
|
|
|
|
|
$('#backupDetailsModal').modal('show');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-19 16:13:20 +02:00
|
|
|
$scope.configureScheduleAndRetention = {
|
|
|
|
|
busy: false,
|
|
|
|
|
error: {},
|
|
|
|
|
|
|
|
|
|
retentionPolicy: $scope.retentionPolicies[0],
|
2020-07-28 21:48:24 -07:00
|
|
|
days: [],
|
|
|
|
|
hours: [],
|
2020-05-19 16:13:20 +02:00
|
|
|
|
|
|
|
|
show: function () {
|
|
|
|
|
$scope.configureScheduleAndRetention.error = {};
|
|
|
|
|
$scope.configureScheduleAndRetention.busy = false;
|
2020-06-05 14:35:34 +02:00
|
|
|
|
|
|
|
|
var selectedPolicy = $scope.retentionPolicies.find(function (x) { return angular.equals(x.value, $scope.backupConfig.retentionPolicy); });
|
|
|
|
|
if (!selectedPolicy) selectedPolicy = $scope.retentionPolicies[0];
|
|
|
|
|
|
|
|
|
|
$scope.configureScheduleAndRetention.retentionPolicy = selectedPolicy.value;
|
2020-07-28 21:48:24 -07:00
|
|
|
|
|
|
|
|
var tmp = $scope.backupConfig.schedulePattern.split(' ');
|
|
|
|
|
var hours = tmp[2].split(','), days = tmp[5].split(',');
|
2020-07-29 09:27:55 -07:00
|
|
|
if (days[0] === '*') {
|
2020-07-29 15:09:06 -07:00
|
|
|
$scope.configureScheduleAndRetention.days = angular.copy($scope.cronDays, []);
|
2020-07-29 09:27:55 -07:00
|
|
|
} else {
|
2020-07-29 15:09:06 -07:00
|
|
|
$scope.configureScheduleAndRetention.days = days.map(function (day) { return $scope.cronDays[parseInt(day, 10)]; });
|
2020-07-29 09:27:55 -07:00
|
|
|
}
|
2020-07-29 15:09:06 -07:00
|
|
|
$scope.configureScheduleAndRetention.hours = hours.map(function (hour) { return $scope.cronHours[parseInt(hour, 10)]; });
|
2020-05-19 16:13:20 +02:00
|
|
|
|
|
|
|
|
$('#configureScheduleAndRetentionModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.configureScheduleAndRetention.error = {};
|
|
|
|
|
$scope.configureScheduleAndRetention.busy = true;
|
|
|
|
|
|
|
|
|
|
// start with the full backupConfig since the api requires all fields
|
|
|
|
|
var backupConfig = $scope.backupConfig;
|
|
|
|
|
backupConfig.retentionPolicy = $scope.configureScheduleAndRetention.retentionPolicy;
|
2020-07-28 21:48:24 -07:00
|
|
|
|
|
|
|
|
var daysPattern;
|
|
|
|
|
if ($scope.configureScheduleAndRetention.days.length === 7) daysPattern = '*';
|
|
|
|
|
else daysPattern = $scope.configureScheduleAndRetention.days.map(function (d) { return d.value; });
|
|
|
|
|
|
|
|
|
|
var hoursPattern;
|
|
|
|
|
if ($scope.configureScheduleAndRetention.hours.length === 24) hoursPattern = '*';
|
|
|
|
|
else hoursPattern = $scope.configureScheduleAndRetention.hours.map(function (d) { return d.value; });
|
|
|
|
|
|
2020-07-29 12:01:00 -07:00
|
|
|
backupConfig.schedulePattern ='00 00 ' + hoursPattern + ' * * ' + daysPattern;
|
2020-05-19 16:13:20 +02:00
|
|
|
|
|
|
|
|
Client.setBackupConfig(backupConfig, function (error) {
|
|
|
|
|
$scope.configureScheduleAndRetention.busy = false;
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
if (error.statusCode === 424) {
|
|
|
|
|
$scope.configureScheduleAndRetention.error.generic = error.message;
|
|
|
|
|
} else if (error.statusCode === 400) {
|
|
|
|
|
$scope.configureScheduleAndRetention.error.generic = error.message;
|
|
|
|
|
} else {
|
|
|
|
|
console.error('Unable to change schedule or retention.', error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$('#configureScheduleAndRetentionModal').modal('hide');
|
|
|
|
|
|
|
|
|
|
getBackupConfig();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.configureBackup = {
|
|
|
|
|
busy: false,
|
|
|
|
|
error: {},
|
|
|
|
|
|
|
|
|
|
provider: '',
|
|
|
|
|
bucket: '',
|
|
|
|
|
prefix: '',
|
|
|
|
|
accessKeyId: '',
|
|
|
|
|
secretAccessKey: '',
|
|
|
|
|
gcsKey: { keyFileName: '', content: '' },
|
|
|
|
|
region: '',
|
|
|
|
|
endpoint: '',
|
|
|
|
|
backupFolder: '',
|
2020-06-05 12:47:33 +02:00
|
|
|
mountPoint: '',
|
2018-06-07 14:22:48 +02:00
|
|
|
acceptSelfSignedCerts: false,
|
|
|
|
|
useHardlinks: true,
|
2022-02-16 11:48:57 -08:00
|
|
|
chown: true,
|
2018-06-07 14:22:48 +02:00
|
|
|
format: 'tgz',
|
2020-05-12 10:54:15 -07:00
|
|
|
password: '',
|
2020-11-14 11:01:46 +01:00
|
|
|
passwordRepeat: '',
|
2022-06-27 09:02:44 -07:00
|
|
|
encryptedFilenames: true,
|
2020-08-10 23:01:09 -07:00
|
|
|
advancedVisible: false,
|
|
|
|
|
|
|
|
|
|
memoryTicks: [],
|
2021-10-26 09:24:15 -07:00
|
|
|
memoryLimit: $scope.MIN_MEMORY_LIMIT,
|
2020-08-19 14:39:41 -07:00
|
|
|
uploadPartSizeTicks: [],
|
|
|
|
|
uploadPartSize: 50 * 1024 * 1024,
|
2020-08-10 23:01:09 -07:00
|
|
|
copyConcurrency: '',
|
|
|
|
|
downloadConcurrency: '',
|
|
|
|
|
syncConcurrency: '', // sort of similar to upload
|
2018-06-07 14:22:48 +02:00
|
|
|
|
2021-05-17 15:24:19 -07:00
|
|
|
mountOptions: {
|
|
|
|
|
host: '',
|
|
|
|
|
remoteDir: '',
|
|
|
|
|
username: '',
|
|
|
|
|
password: '',
|
2021-05-27 15:31:37 -07:00
|
|
|
diskPath: '',
|
2022-01-10 14:28:25 +01:00
|
|
|
seal: false,
|
2021-05-27 15:31:37 -07:00
|
|
|
user: '',
|
|
|
|
|
port: 22,
|
|
|
|
|
privateKey: ''
|
2021-05-17 15:24:19 -07:00
|
|
|
},
|
|
|
|
|
|
2020-05-12 21:41:35 -07:00
|
|
|
clearProviderFields: function () {
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.configureBackup.bucket = '';
|
|
|
|
|
$scope.configureBackup.prefix = '';
|
|
|
|
|
$scope.configureBackup.accessKeyId = '';
|
|
|
|
|
$scope.configureBackup.secretAccessKey = '';
|
|
|
|
|
$scope.configureBackup.gcsKey.keyFileName = '';
|
|
|
|
|
$scope.configureBackup.gcsKey.content = '';
|
|
|
|
|
$scope.configureBackup.endpoint = '';
|
|
|
|
|
$scope.configureBackup.region = '';
|
|
|
|
|
$scope.configureBackup.backupFolder = '';
|
2020-06-05 12:47:33 +02:00
|
|
|
$scope.configureBackup.mountPoint = '';
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.configureBackup.acceptSelfSignedCerts = false;
|
|
|
|
|
$scope.configureBackup.useHardlinks = true;
|
2022-02-16 11:48:57 -08:00
|
|
|
$scope.configureBackup.chown = true;
|
2021-10-26 09:24:15 -07:00
|
|
|
$scope.configureBackup.memoryLimit = $scope.MIN_MEMORY_LIMIT;
|
2020-08-10 23:01:09 -07:00
|
|
|
|
2020-08-19 14:39:41 -07:00
|
|
|
// scaleway only supports 1000 parts per object (https://www.scaleway.com/en/docs/s3-multipart-upload/)
|
|
|
|
|
$scope.configureBackup.uploadPartSize = $scope.configureBackup.provider === 'scaleway-objectstorage' ? 100 * 1024 * 1024 : 10 * 1024 * 1024;
|
2020-08-11 16:51:02 -07:00
|
|
|
$scope.configureBackup.downloadConcurrency = $scope.configureBackup.provider === 's3' ? 30 : 10;
|
|
|
|
|
$scope.configureBackup.syncConcurrency = $scope.configureBackup.provider === 's3' ? 20 : 10;
|
|
|
|
|
$scope.configureBackup.copyConcurrency = $scope.configureBackup.provider === 's3' ? 500 : 10;
|
2021-05-17 15:24:19 -07:00
|
|
|
|
2022-01-10 14:28:25 +01:00
|
|
|
$scope.configureBackup.mountOptions = { host: '', remoteDir: '', username: '', password: '', diskPath: '', seal: false, user: '', port: 22, privateKey: '' };
|
2018-06-07 14:22:48 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
show: function () {
|
|
|
|
|
$scope.configureBackup.error = {};
|
|
|
|
|
$scope.configureBackup.busy = false;
|
|
|
|
|
|
2020-08-11 17:03:49 -07:00
|
|
|
$scope.configureBackup.advancedVisible = false;
|
|
|
|
|
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.configureBackup.provider = $scope.backupConfig.provider;
|
|
|
|
|
$scope.configureBackup.bucket = $scope.backupConfig.bucket;
|
|
|
|
|
$scope.configureBackup.prefix = $scope.backupConfig.prefix;
|
|
|
|
|
$scope.configureBackup.region = $scope.backupConfig.region;
|
|
|
|
|
$scope.configureBackup.accessKeyId = $scope.backupConfig.accessKeyId;
|
|
|
|
|
$scope.configureBackup.secretAccessKey = $scope.backupConfig.secretAccessKey;
|
|
|
|
|
if ($scope.backupConfig.provider === 'gcs') {
|
|
|
|
|
$scope.configureBackup.gcsKey.keyFileName = $scope.backupConfig.credentials.client_email;
|
|
|
|
|
$scope.configureBackup.gcsKey.content = JSON.stringify({
|
|
|
|
|
project_id: $scope.backupConfig.projectId,
|
|
|
|
|
client_email: $scope.backupConfig.credentials.client_email,
|
|
|
|
|
private_key: $scope.backupConfig.credentials.private_key,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
$scope.configureBackup.endpoint = $scope.backupConfig.endpoint;
|
2020-11-17 21:12:27 -08:00
|
|
|
$scope.configureBackup.password = $scope.backupConfig.password || '';
|
2020-11-14 11:01:46 +01:00
|
|
|
$scope.configureBackup.passwordRepeat = '';
|
2022-06-27 09:02:44 -07:00
|
|
|
$scope.configureBackup.encryptedFilenames = 'encryptedFilenames' in $scope.backupConfig ? $scope.backupConfig.encryptedFilenames : true;
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.configureBackup.backupFolder = $scope.backupConfig.backupFolder;
|
2020-06-05 12:47:33 +02:00
|
|
|
$scope.configureBackup.mountPoint = $scope.backupConfig.mountPoint;
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.configureBackup.format = $scope.backupConfig.format;
|
|
|
|
|
$scope.configureBackup.acceptSelfSignedCerts = !!$scope.backupConfig.acceptSelfSignedCerts;
|
|
|
|
|
$scope.configureBackup.useHardlinks = !$scope.backupConfig.noHardlinks;
|
2022-02-16 11:48:57 -08:00
|
|
|
$scope.configureBackup.chown = $scope.backupConfig.chown;
|
2018-06-07 14:22:48 +02:00
|
|
|
|
2020-08-10 23:01:09 -07:00
|
|
|
$scope.configureBackup.memoryLimit = $scope.backupConfig.memoryLimit;
|
|
|
|
|
|
2020-08-19 14:39:41 -07:00
|
|
|
$scope.configureBackup.uploadPartSize = $scope.backupConfig.uploadPartSize || ($scope.configureBackup.provider === 'scaleway-objectstorage' ? 100 * 1024 * 1024 : 10 * 1024 * 1024);
|
2020-08-11 16:51:02 -07:00
|
|
|
$scope.configureBackup.downloadConcurrency = $scope.backupConfig.downloadConcurrency || ($scope.backupConfig.provider === 's3' ? 30 : 10);
|
|
|
|
|
$scope.configureBackup.syncConcurrency = $scope.backupConfig.syncConcurrency || ($scope.backupConfig.provider === 's3' ? 20 : 10);
|
|
|
|
|
$scope.configureBackup.copyConcurrency = $scope.backupConfig.copyConcurrency || ($scope.backupConfig.provider === 's3' ? 500 : 10);
|
2020-08-10 23:01:09 -07:00
|
|
|
|
2020-09-10 00:07:12 -07:00
|
|
|
var totalMemory = Math.max(($scope.memory.memory + $scope.memory.swap) * 1.5, 2 * 1024 * 1024);
|
2021-10-26 09:24:15 -07:00
|
|
|
$scope.configureBackup.memoryTicks = [ $scope.MIN_MEMORY_LIMIT ];
|
|
|
|
|
for (var i = 1024; i <= totalMemory/1024/1024; i *= 2) {
|
2020-08-10 23:01:09 -07:00
|
|
|
$scope.configureBackup.memoryTicks.push(i * 1024 * 1024);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-10 00:07:12 -07:00
|
|
|
$scope.configureBackup.uploadPartSizeTicks = [ 5 * 1024 * 1024 ];
|
2020-09-10 08:30:54 -07:00
|
|
|
for (var j = 32; j <= 1 * 1024; j *= 2) { // 5 GB is max for s3. but let's keep things practical for now. we upload 3 parts in parallel
|
2020-08-19 14:39:41 -07:00
|
|
|
$scope.configureBackup.uploadPartSizeTicks.push(j * 1024 * 1024);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 15:24:19 -07:00
|
|
|
var mountOptions = $scope.backupConfig.mountOptions || {};
|
2021-05-27 13:19:13 -07:00
|
|
|
$scope.configureBackup.mountOptions = {
|
2021-05-17 15:24:19 -07:00
|
|
|
host: mountOptions.host || '',
|
|
|
|
|
remoteDir: mountOptions.remoteDir || '',
|
|
|
|
|
username: mountOptions.username || '',
|
|
|
|
|
password: mountOptions.password || '',
|
2021-05-27 15:31:37 -07:00
|
|
|
diskPath: mountOptions.diskPath || '',
|
2022-01-10 14:28:25 +01:00
|
|
|
seal: mountOptions.seal,
|
2021-05-27 15:31:37 -07:00
|
|
|
user: mountOptions.user || '',
|
|
|
|
|
port: mountOptions.port || 22,
|
|
|
|
|
privateKey: mountOptions.privateKey || ''
|
2021-05-17 15:24:19 -07:00
|
|
|
};
|
2020-08-19 14:39:41 -07:00
|
|
|
|
2018-06-07 14:22:48 +02:00
|
|
|
$('#configureBackupModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.configureBackup.error = {};
|
|
|
|
|
$scope.configureBackup.busy = true;
|
|
|
|
|
|
|
|
|
|
var backupConfig = {
|
|
|
|
|
provider: $scope.configureBackup.provider,
|
2020-05-19 16:13:20 +02:00
|
|
|
format: $scope.configureBackup.format,
|
2020-08-10 23:01:09 -07:00
|
|
|
memoryLimit: $scope.configureBackup.memoryLimit,
|
2020-05-19 16:13:20 +02:00
|
|
|
// required for api call to provide all fields
|
2020-07-28 21:48:24 -07:00
|
|
|
schedulePattern: $scope.backupConfig.schedulePattern,
|
2020-05-19 16:13:20 +02:00
|
|
|
retentionPolicy: $scope.backupConfig.retentionPolicy
|
2018-06-07 14:22:48 +02:00
|
|
|
};
|
2022-06-27 09:02:44 -07:00
|
|
|
if ($scope.configureBackup.password) {
|
|
|
|
|
backupConfig.password = $scope.configureBackup.password;
|
|
|
|
|
backupConfig.encryptedFilenames = $scope.configureBackup.encryptedFilenames; // ignored with tgz format
|
|
|
|
|
}
|
2018-06-07 14:22:48 +02:00
|
|
|
|
|
|
|
|
// only set provider specific fields, this will clear them in the db
|
|
|
|
|
if ($scope.s3like(backupConfig.provider)) {
|
|
|
|
|
backupConfig.bucket = $scope.configureBackup.bucket;
|
|
|
|
|
backupConfig.prefix = $scope.configureBackup.prefix;
|
|
|
|
|
backupConfig.accessKeyId = $scope.configureBackup.accessKeyId;
|
|
|
|
|
backupConfig.secretAccessKey = $scope.configureBackup.secretAccessKey;
|
|
|
|
|
|
|
|
|
|
if ($scope.configureBackup.endpoint) backupConfig.endpoint = $scope.configureBackup.endpoint;
|
|
|
|
|
|
|
|
|
|
if (backupConfig.provider === 's3') {
|
|
|
|
|
if ($scope.configureBackup.region) backupConfig.region = $scope.configureBackup.region;
|
2018-07-30 07:29:20 -07:00
|
|
|
delete backupConfig.endpoint;
|
2018-06-07 14:22:48 +02:00
|
|
|
} else if (backupConfig.provider === 'minio' || backupConfig.provider === 's3-v4-compat') {
|
2020-06-15 16:51:56 +02:00
|
|
|
backupConfig.region = $scope.configureBackup.region || 'us-east-1';
|
2018-06-07 14:22:48 +02:00
|
|
|
backupConfig.acceptSelfSignedCerts = $scope.configureBackup.acceptSelfSignedCerts;
|
2020-05-27 17:48:51 -07:00
|
|
|
backupConfig.s3ForcePathStyle = true; // might want to expose this in the UI
|
2018-06-07 14:22:48 +02:00
|
|
|
} else if (backupConfig.provider === 'exoscale-sos') {
|
|
|
|
|
backupConfig.region = 'us-east-1';
|
|
|
|
|
backupConfig.signatureVersion = 'v4';
|
2019-07-22 16:34:16 -07:00
|
|
|
} else if (backupConfig.provider === 'wasabi') {
|
2020-05-19 14:52:40 +02:00
|
|
|
backupConfig.region = $scope.wasabiRegions.find(function (x) { return x.value === $scope.configureBackup.endpoint; }).region;
|
2019-07-22 16:34:16 -07:00
|
|
|
backupConfig.signatureVersion = 'v4';
|
2019-04-12 10:04:26 -07:00
|
|
|
} else if (backupConfig.provider === 'scaleway-objectstorage') {
|
|
|
|
|
backupConfig.region = $scope.scalewayRegions.find(function (x) { return x.value === $scope.configureBackup.endpoint; }).region;
|
|
|
|
|
backupConfig.signatureVersion = 'v4';
|
2020-02-26 09:08:34 -08:00
|
|
|
} else if (backupConfig.provider === 'linode-objectstorage') {
|
|
|
|
|
backupConfig.region = $scope.linodeRegions.find(function (x) { return x.value === $scope.configureBackup.endpoint; }).region;
|
|
|
|
|
backupConfig.signatureVersion = 'v4';
|
2020-04-29 12:54:19 -07:00
|
|
|
} else if (backupConfig.provider === 'ovh-objectstorage') {
|
|
|
|
|
backupConfig.region = $scope.ovhRegions.find(function (x) { return x.value === $scope.configureBackup.endpoint; }).region;
|
|
|
|
|
backupConfig.signatureVersion = 'v4';
|
2021-02-04 10:14:42 -08:00
|
|
|
} else if (backupConfig.provider === 'ionos-objectstorage') {
|
|
|
|
|
backupConfig.region = $scope.ionosRegions.find(function (x) { return x.value === $scope.configureBackup.endpoint; }).region;
|
|
|
|
|
backupConfig.signatureVersion = 'v4';
|
2021-06-16 22:35:46 -07:00
|
|
|
} else if (backupConfig.provider === 'vultr-objectstorage') {
|
|
|
|
|
backupConfig.region = $scope.vultrRegions.find(function (x) { return x.value === $scope.configureBackup.endpoint; }).region;
|
|
|
|
|
backupConfig.signatureVersion = 'v4';
|
2021-09-27 10:01:09 -07:00
|
|
|
} else if (backupConfig.provider === 'upcloud-objectstorage') { // the UI sets region and endpoint
|
|
|
|
|
var m = /^.*\.(.*)\.upcloudobjects.com$/.exec(backupConfig.endpoint);
|
|
|
|
|
backupConfig.region = m ? m[1] : 'us-east-1'; // let it fail in validation phase if m is not valid
|
|
|
|
|
backupConfig.signatureVersion = 'v4';
|
2018-06-07 14:22:48 +02:00
|
|
|
} else if (backupConfig.provider === 'digitalocean-spaces') {
|
|
|
|
|
backupConfig.region = 'us-east-1';
|
|
|
|
|
}
|
|
|
|
|
} else if (backupConfig.provider === 'gcs') {
|
|
|
|
|
backupConfig.bucket = $scope.configureBackup.bucket;
|
|
|
|
|
backupConfig.prefix = $scope.configureBackup.prefix;
|
|
|
|
|
try {
|
|
|
|
|
var serviceAccountKey = JSON.parse($scope.configureBackup.gcsKey.content);
|
|
|
|
|
backupConfig.projectId = serviceAccountKey.project_id;
|
|
|
|
|
backupConfig.credentials = {
|
|
|
|
|
client_email: serviceAccountKey.client_email,
|
|
|
|
|
private_key: serviceAccountKey.private_key
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!backupConfig.projectId || !backupConfig.credentials || !backupConfig.credentials.client_email || !backupConfig.credentials.private_key) {
|
|
|
|
|
throw 'fields_missing';
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
$scope.configureBackup.error.generic = 'Cannot parse Google Service Account Key: ' + e.message;
|
|
|
|
|
$scope.configureBackup.error.gcsKeyInput = true;
|
|
|
|
|
$scope.configureBackup.busy = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-05-17 15:24:19 -07:00
|
|
|
} else if ($scope.mountlike(backupConfig.provider)) {
|
2020-06-05 12:47:33 +02:00
|
|
|
backupConfig.prefix = $scope.configureBackup.prefix;
|
|
|
|
|
backupConfig.noHardlinks = !$scope.configureBackup.useHardlinks;
|
2021-05-17 15:24:19 -07:00
|
|
|
backupConfig.mountOptions = {};
|
|
|
|
|
|
|
|
|
|
if (backupConfig.provider === 'cifs' || backupConfig.provider === 'sshfs' || backupConfig.provider === 'nfs') {
|
|
|
|
|
backupConfig.mountOptions.host = $scope.configureBackup.mountOptions.host;
|
|
|
|
|
backupConfig.mountOptions.remoteDir = $scope.configureBackup.mountOptions.remoteDir;
|
|
|
|
|
|
|
|
|
|
if (backupConfig.provider === 'cifs') {
|
|
|
|
|
backupConfig.mountOptions.username = $scope.configureBackup.mountOptions.username;
|
|
|
|
|
backupConfig.mountOptions.password = $scope.configureBackup.mountOptions.password;
|
2022-01-10 14:28:25 +01:00
|
|
|
backupConfig.mountOptions.seal = $scope.configureBackup.mountOptions.seal;
|
2021-05-27 15:31:37 -07:00
|
|
|
} else if (backupConfig.provider === 'sshfs') {
|
|
|
|
|
backupConfig.mountOptions.user = $scope.configureBackup.mountOptions.user;
|
|
|
|
|
backupConfig.mountOptions.port = $scope.configureBackup.mountOptions.port;
|
|
|
|
|
backupConfig.mountOptions.privateKey = $scope.configureBackup.mountOptions.privateKey;
|
2021-05-17 15:24:19 -07:00
|
|
|
}
|
2022-06-08 10:38:54 -07:00
|
|
|
} else if (backupConfig.provider === 'ext4' || backupConfig.provider === 'xfs') {
|
2021-06-18 23:03:18 -07:00
|
|
|
backupConfig.mountOptions.diskPath = $scope.configureBackup.mountOptions.diskPath;
|
2021-06-21 22:08:19 -07:00
|
|
|
} else if (backupConfig.provider === 'mountpoint') {
|
|
|
|
|
backupConfig.mountPoint = $scope.configureBackup.mountPoint;
|
2022-02-16 11:48:57 -08:00
|
|
|
backupConfig.chown = $scope.configureBackup.chown;
|
2021-06-22 14:29:51 -07:00
|
|
|
backupConfig.preserveAttributes = true;
|
2021-05-17 15:24:19 -07:00
|
|
|
}
|
2021-06-21 22:08:19 -07:00
|
|
|
} else if (backupConfig.provider === 'filesystem') {
|
2018-06-07 14:22:48 +02:00
|
|
|
backupConfig.backupFolder = $scope.configureBackup.backupFolder;
|
|
|
|
|
backupConfig.noHardlinks = !$scope.configureBackup.useHardlinks;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-19 14:39:41 -07:00
|
|
|
backupConfig.uploadPartSize = $scope.configureBackup.uploadPartSize;
|
|
|
|
|
|
2020-08-10 23:01:09 -07:00
|
|
|
if (backupConfig.format === 'rsync') {
|
|
|
|
|
backupConfig.downloadConcurrency = $scope.configureBackup.downloadConcurrency;
|
|
|
|
|
backupConfig.syncConcurrency = $scope.configureBackup.syncConcurrency;
|
|
|
|
|
backupConfig.copyConcurrency = $scope.configureBackup.copyConcurrency;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-07 14:22:48 +02:00
|
|
|
Client.setBackupConfig(backupConfig, function (error) {
|
|
|
|
|
$scope.configureBackup.busy = false;
|
|
|
|
|
|
|
|
|
|
if (error) {
|
2018-09-10 10:36:32 -07:00
|
|
|
if (error.statusCode === 424) {
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.configureBackup.error.generic = error.message;
|
|
|
|
|
|
|
|
|
|
if (error.message.indexOf('AWS Access Key Id') !== -1) {
|
|
|
|
|
$scope.configureBackup.error.accessKeyId = true;
|
|
|
|
|
$scope.configureBackup.accessKeyId = '';
|
|
|
|
|
$scope.configureBackupForm.accessKeyId.$setPristine();
|
|
|
|
|
$('#inputConfigureBackupAccessKeyId').focus();
|
|
|
|
|
} else if (error.message.indexOf('not match the signature') !== -1 ) {
|
|
|
|
|
$scope.configureBackup.error.secretAccessKey = true;
|
|
|
|
|
$scope.configureBackup.secretAccessKey = '';
|
|
|
|
|
$scope.configureBackupForm.secretAccessKey.$setPristine();
|
|
|
|
|
$('#inputConfigureBackupSecretAccessKey').focus();
|
|
|
|
|
} else if (error.message.toLowerCase() === 'access denied') {
|
|
|
|
|
$scope.configureBackup.error.bucket = true;
|
|
|
|
|
$scope.configureBackup.bucket = '';
|
|
|
|
|
$scope.configureBackupForm.bucket.$setPristine();
|
|
|
|
|
$('#inputConfigureBackupBucket').focus();
|
|
|
|
|
} else if (error.message.indexOf('ECONNREFUSED') !== -1) {
|
|
|
|
|
$scope.configureBackup.error.generic = 'Unknown region';
|
|
|
|
|
$scope.configureBackup.error.region = true;
|
|
|
|
|
$scope.configureBackupForm.region.$setPristine();
|
|
|
|
|
$('#inputConfigureBackupDORegion').focus();
|
|
|
|
|
} else if (error.message.toLowerCase() === 'wrong region') {
|
|
|
|
|
$scope.configureBackup.error.generic = 'Wrong S3 Region';
|
|
|
|
|
$scope.configureBackup.error.region = true;
|
|
|
|
|
$scope.configureBackupForm.region.$setPristine();
|
|
|
|
|
$('#inputConfigureBackupS3Region').focus();
|
|
|
|
|
} else {
|
|
|
|
|
$('#inputConfigureBackupBucket').focus();
|
|
|
|
|
}
|
|
|
|
|
} else if (error.statusCode === 400) {
|
|
|
|
|
$scope.configureBackup.error.generic = error.message;
|
|
|
|
|
|
2020-05-12 10:54:15 -07:00
|
|
|
if (error.message.indexOf('password') !== -1) {
|
|
|
|
|
$scope.configureBackup.error.password = true;
|
|
|
|
|
$scope.configureBackupForm.password.$setPristine();
|
|
|
|
|
} else if ($scope.configureBackup.provider === 'filesystem') {
|
2018-06-07 14:22:48 +02:00
|
|
|
$scope.configureBackup.error.backupFolder = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
console.error('Unable to change provider.', error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// $scope.configureBackup.reset();
|
|
|
|
|
$('#configureBackupModal').modal('hide');
|
|
|
|
|
|
|
|
|
|
getBackupConfig();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function fetchBackups() {
|
|
|
|
|
Client.getBackups(function (error, backups) {
|
|
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
|
|
|
|
$scope.backups = backups;
|
2020-05-15 12:48:54 -07:00
|
|
|
$scope.backups = $scope.backups.slice(0, 20); // only show 20 since we don't have pagination
|
2018-06-07 14:22:48 +02:00
|
|
|
|
2020-05-16 09:46:57 -07:00
|
|
|
// add contents property
|
2021-05-08 17:18:26 -07:00
|
|
|
var appsById = {}, appsByFqdn = {};
|
|
|
|
|
Client.getInstalledApps().forEach(function (app) {
|
|
|
|
|
appsById[app.id] = app;
|
|
|
|
|
appsByFqdn[app.fqdn] = app;
|
|
|
|
|
});
|
2020-05-16 09:46:57 -07:00
|
|
|
|
|
|
|
|
$scope.backups.forEach(function (backup) {
|
|
|
|
|
backup.contents = [];
|
|
|
|
|
backup.dependsOn.forEach(function (appBackupId) {
|
|
|
|
|
let match = appBackupId.match(/app_(.*?)_.*/); // *? means non-greedy
|
2021-05-08 17:18:26 -07:00
|
|
|
if (!match) return;
|
|
|
|
|
if (match[1].indexOf('.') !== -1) { // newer backups have fqdn in them
|
|
|
|
|
if (appsByFqdn[match[1]]) backup.contents.push(appsByFqdn[match[1]]);
|
|
|
|
|
} else {
|
|
|
|
|
if (appsById[match[1]]) backup.contents.push(appsById[match[1]]);
|
|
|
|
|
}
|
2020-05-16 09:46:57 -07:00
|
|
|
});
|
|
|
|
|
});
|
2018-06-07 14:22:48 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getBackupConfig() {
|
|
|
|
|
Client.getBackupConfig(function (error, backupConfig) {
|
|
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
|
|
|
|
$scope.backupConfig = backupConfig;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Client.onReady(function () {
|
2020-09-10 00:07:12 -07:00
|
|
|
Client.memory(function (error, memory) {
|
|
|
|
|
if (error) console.error(error);
|
2018-06-07 14:22:48 +02:00
|
|
|
|
2020-09-10 00:07:12 -07:00
|
|
|
$scope.memory = memory;
|
2020-04-03 10:36:51 -07:00
|
|
|
|
2020-09-10 00:07:12 -07:00
|
|
|
fetchBackups();
|
|
|
|
|
getBackupConfig();
|
|
|
|
|
|
2022-09-28 12:13:32 +02:00
|
|
|
$scope.manualBackupApps = Client.getInstalledApps().filter(function (app) { return app.type !== APP_TYPES.LINK && !app.enableBackup; });
|
2020-09-10 00:07:12 -07:00
|
|
|
|
|
|
|
|
// show backup status
|
|
|
|
|
$scope.createBackup.checkStatus();
|
|
|
|
|
});
|
2018-06-07 14:22:48 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function readFileLocally(obj, file, fileName) {
|
|
|
|
|
return function (event) {
|
|
|
|
|
$scope.$apply(function () {
|
|
|
|
|
obj[file] = null;
|
|
|
|
|
obj[fileName] = event.target.files[0].name;
|
|
|
|
|
|
|
|
|
|
var reader = new FileReader();
|
|
|
|
|
reader.onload = function (result) {
|
|
|
|
|
if (!result.target || !result.target.result) return console.error('Unable to read local file');
|
|
|
|
|
obj[file] = result.target.result;
|
|
|
|
|
};
|
|
|
|
|
reader.readAsText(event.target.files[0]);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.getElementById('gcsKeyFileInput').onchange = readFileLocally($scope.configureBackup.gcsKey, 'content', 'keyFileName');
|
|
|
|
|
|
|
|
|
|
// setup all the dialog focus handling
|
2022-04-05 14:41:41 +02:00
|
|
|
['configureBackupModal', 'editBackupModal'].forEach(function (id) {
|
2018-06-07 14:22:48 +02:00
|
|
|
$('#' + id).on('shown.bs.modal', function () {
|
|
|
|
|
$(this).find("[autofocus]:first").focus();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('.modal-backdrop').remove();
|
|
|
|
|
}]);
|