archive: implement unarchive

made a separate route instead of reusing install route. this was
because we want to copy over all the old app config as much as
possible.
This commit is contained in:
Girish Ramakrishnan
2024-12-10 14:46:30 +01:00
parent e168be6d97
commit 0e181cdc82
9 changed files with 173 additions and 90 deletions

View File

@@ -268,18 +268,18 @@ angular.module('Application').controller('BackupsController', ['$scope', '$locat
}
};
$scope.listArchives = {
$scope.archiveList = {
ready: false,
archives: [],
fetch: function () {
Client.listArchives(function (error, archives) {
if (error) Client.error(error);
$scope.listArchives.archives = archives;
$scope.listArchives.ready = true;
$scope.archiveList.archives = archives;
$scope.archiveList.ready = true;
// ensure we use the full api oprigin
$scope.listArchives.archives.forEach(a => {
$scope.archiveList.archives.forEach(a => {
a.iconUrl = window.cloudronApiOrigin + a.iconUrl;
});
});
@@ -290,11 +290,13 @@ angular.module('Application').controller('BackupsController', ['$scope', '$locat
busy: false,
error: {},
archive: null,
app: null, // just for simpler access . it's a fake app object!
ask: function (archive) {
$scope.archiveDelete.busy = false;
$scope.archiveDelete.error = {};
$scope.archiveDelete.archive = archive;
$scope.archiveDelete.app = archive.appConfig;
$('#archiveDeleteModal').modal('show');
},
@@ -305,18 +307,20 @@ angular.module('Application').controller('BackupsController', ['$scope', '$locat
Client.deleteArchive($scope.archiveDelete.archive.id, function (error) {
$scope.archiveDelete.busy = false;
if (error) return console.error('Unable to delete archive.', error.statusCode, error.message);
$scope.listArchives.fetch();
$scope.archiveList.fetch();
$('#archiveDeleteModal').modal('hide');
});
}
};
// keep in sync with app.js
$scope.clone = {
$scope.archiveRestore = {
busy: false,
error: {},
archive: null,
app: null, // just for simpler access . it's a fake app object!
subdomain: '',
domain: null,
secondaryDomains: {},
@@ -325,6 +329,7 @@ angular.module('Application').controller('BackupsController', ['$scope', '$locat
ports: {},
portsEnabled: {},
portInfo: {},
accessRestriction: { users: [], groups: [] },
init: function () {
Client.getDomains(function (error, domains) {
@@ -334,61 +339,69 @@ angular.module('Application').controller('BackupsController', ['$scope', '$locat
},
show: function (archive) {
$scope.clone.error = {};
$scope.clone.archive = archive;
const app = archive.appConfig;
$scope.archiveRestore.error = {};
$scope.archiveRestore.archive = archive;
const manifest = archive.appConfig.manifest;
$scope.clone.domain = $scope.domains.find(function (d) { return app.domain === d.domain; }); // pre-select the app's domain
$scope.clone.needsOverwrite = false;
$scope.clone.overwriteDns = false;
$scope.archiveRestore.app = archive.appConfig;
$scope.archiveRestore.subdomain = $scope.archiveRestore.app.subdomain;
$scope.archiveRestore.domain = $scope.domains.find(function (d) { return $scope.archiveRestore.app.domain === d.domain; }); // try to pre-select the app's domain
$scope.clone.secondaryDomains = {};
$scope.archiveRestore.needsOverwrite = false;
$scope.archiveRestore.overwriteDns = false;
$scope.archiveRestore.secondaryDomains = {};
var httpPorts = manifest.httpPorts || {};
for (var env2 in httpPorts) {
$scope.clone.secondaryDomains[env2] = {
$scope.archiveRestore.secondaryDomains[env2] = {
subdomain: httpPorts[env2].defaultValue || '',
domain: $scope.clone.domain
domain: $scope.archiveRestore.domain
};
}
$scope.clone.portInfo = angular.extend({}, manifest.tcpPorts, manifest.udpPorts); // Portbinding map only for information
$scope.archiveRestore.portInfo = angular.extend({}, manifest.tcpPorts, manifest.udpPorts); // Portbinding map only for information
// set default ports
for (var env in $scope.clone.portInfo) {
$scope.clone.ports[env] = $scope.clone.portInfo[env].defaultValue || 0;
$scope.clone.portsEnabled[env] = true;
for (var env in $scope.archiveRestore.portInfo) {
if ($scope.archiveRestore.app.portBindings[env]) { // was enabled in the app
$scope.archiveRestore.ports[env] = $scope.archiveRestore.app.portBindings[env].hostPort;
$scope.archiveRestore.portsEnabled[env] = true;
} else {
$scope.archiveRestore.ports[env] = $scope.archiveRestore.portInfo[env].defaultValue || 0;
$scope.archiveRestore.portsEnabled[env] = false;
}
}
$('#appCloneModal').modal('show');
$('#restoreArchiveModal').modal('show');
},
submit: function () {
$scope.clone.busy = true;
$scope.archiveRestore.busy = true;
const app = $scope.archiveRestore.archive.appConfig;
var secondaryDomains = {};
for (var env2 in $scope.clone.secondaryDomains) {
for (var env2 in $scope.archiveRestore.secondaryDomains) {
secondaryDomains[env2] = {
subdomain: $scope.clone.secondaryDomains[env2].subdomain,
domain: $scope.clone.secondaryDomains[env2].domain.domain
subdomain: $scope.archiveRestore.secondaryDomains[env2].subdomain,
domain: $scope.archiveRestore.secondaryDomains[env2].domain.domain
};
}
// only use enabled ports
var finalPorts = {};
for (var env in $scope.clone.ports) {
if ($scope.clone.portsEnabled[env]) {
finalPorts[env] = $scope.clone.ports[env];
for (var env in $scope.archiveRestore.ports) {
if ($scope.archiveRestore.portsEnabled[env]) {
finalPorts[env] = $scope.archiveRestore.ports[env];
}
}
var data = {
subdomain: $scope.clone.subdomain,
domain: $scope.clone.domain.domain,
subdomain: $scope.archiveRestore.subdomain,
domain: $scope.archiveRestore.domain.domain,
secondaryDomains: secondaryDomains,
ports: finalPorts,
backupId: $scope.clone.archive.backupId,
overwriteDns: $scope.clone.overwriteDns
overwriteDns: $scope.archiveRestore.overwriteDns,
};
var allDomains = [{ domain: data.domain, subdomain: data.subdomain }].concat(Object.keys(secondaryDomains).map(function (k) {
@@ -398,7 +411,7 @@ angular.module('Application').controller('BackupsController', ['$scope', '$locat
};
}));
async.eachSeries(allDomains, function (domain, callback) {
if ($scope.clone.overwriteDns) return callback();
if ($scope.archiveRestore.overwriteDns) return callback();
Client.checkDNSRecords(domain.domain, domain.subdomain, function (error, result) {
if (error) return callback(error);
@@ -410,8 +423,8 @@ angular.module('Application').controller('BackupsController', ['$scope', '$locat
return callback({ type: 'provider', fqdn: fqdn, message: result.error.message });
}
if (result.needsOverwrite) {
$scope.clone.needsOverwrite = true;
$scope.clone.overwriteDns = true;
$scope.archiveRestore.needsOverwrite = true;
$scope.archiveRestore.overwriteDns = true;
return callback({ type: 'externally_exists', fqdn: fqdn, message: 'DNS Record already exists. Confirm that the domain is not in use for services external to Cloudron' });
}
@@ -420,29 +433,27 @@ angular.module('Application').controller('BackupsController', ['$scope', '$locat
}, function (error) {
if (error) {
if (error.type) {
$scope.clone.error.location = error;
$scope.clone.busy = false;
$scope.archiveRestore.error.location = error;
$scope.archiveRestore.busy = false;
} else {
Client.error(error);
}
$scope.clone.error.location = error;
$scope.clone.busy = false;
$scope.archiveRestore.error.location = error;
$scope.archiveRestore.busy = false;
return;
}
const app = $scope.clone.archive.appConfig;
Client.installApp(app.manifest.id, app.manifest, data, function (error/*, clonedApp */) {
$scope.clone.busy = false;
Client.unarchiveApp($scope.archiveRestore.archive.id, data, function (error/*, newApp */) {
$scope.archiveRestore.busy = false;
if (error) {
var errorMessage = error.message.toLowerCase();
if (errorMessage.indexOf('port') !== -1) {
$scope.clone.error.port = error.message;
$scope.archiveRestore.error.port = error.message;
} else if (error.message.indexOf('location') !== -1 || error.message.indexOf('subdomain') !== -1) {
// TODO extract fqdn from error message, currently we just set it always to the main location
$scope.clone.error.location = { type: 'internally_exists', fqdn: data.subdomain + '.' + data.domain, message: error.message };
$scope.archiveRestore.error.location = { type: 'internally_exists', fqdn: data.subdomain + '.' + data.domain, message: error.message };
$('#cloneLocationInput').focus();
} else {
Client.error(error);
@@ -450,7 +461,7 @@ angular.module('Application').controller('BackupsController', ['$scope', '$locat
return;
}
$('#appCloneModal').modal('hide');
$('#restoreArchiveModal').modal('hide');
$location.path('/apps');
});
@@ -1049,7 +1060,7 @@ angular.module('Application').controller('BackupsController', ['$scope', '$locat
fetchBackups();
getBackupConfig();
$scope.listArchives.fetch();
$scope.archiveList.fetch();
$scope.manualBackupApps = Client.getInstalledApps().filter(function (app) { return app.type !== APP_TYPES.LINK && !app.enableBackup; });
@@ -1057,7 +1068,7 @@ angular.module('Application').controller('BackupsController', ['$scope', '$locat
$scope.createBackup.init();
$scope.cleanupBackups.init();
$scope.backupPolicy.init();
$scope.clone.init();
$scope.archiveRestore.init();
getBackupTasks();
getCleanupTasks();