Files
duplicati/Duplicati/Server/webroot/ngax/scripts/controllers/RestoreController.js
T

411 lines
15 KiB
JavaScript

backupApp.controller('RestoreController', function ($rootScope, $scope, $routeParams, $location, AppService, AppUtils, SystemInfo, ServerStatus) {
$scope.SystemInfo = SystemInfo.watch($scope);
$scope.AppUtils = AppUtils;
$scope.restore_step = 0;
$scope.connecting = false;
$scope.isTemporaryBackup = true;
$scope.HideFolderBrowser = true;
$scope.RestoreLocation = 'direct';
$scope.RestoreMode = 'overwrite';
var filesetsBuilt = {};
var filesetsRepaired = {};
var filesetStamps = {};
var inProgress = {};
$scope.filesetStamps = filesetStamps;
$scope.treedata = {};
$scope.Selected = [];
function createGroupLabel(dt) {
var dateStamp = function(a) { return a.getFullYear() * 10000 + a.getMonth() * 100 + a.getDate(); }
var now = new Date();
var today = dateStamp(now);
var yesterday = dateStamp(new Date(new Date().setDate(now.getDate() - 1)));
var week = dateStamp(new Date(new Date().setDate(now.getDate() - 7)));
var thismonth = dateStamp(new Date(new Date().setMonth(now.getMonth() - 1)));
var lastmonth = dateStamp(new Date(new Date().setMonth(now.getMonth() - 2)));
var dateBuckets = [
{text:'Today', stamp: today},
{text: 'Yesterday', stamp: yesterday},
{text: 'This week', stamp: week},
{text: 'This month', stamp: thismonth},
{text: 'Last month', stamp: lastmonth}
];
var stamp = dateStamp(dt);
for(var t in dateBuckets)
if (stamp >= dateBuckets[t].stamp)
return dateBuckets[t].text;
return dt.getFullYear() + '';
};
$scope.parseBackupTimesData = function() {
for(var n in filesetStamps)
delete filesetStamps[n];
for(var n in $scope.Filesets) {
var item = $scope.Filesets[n];
item.DisplayLabel = item.Version + ': ' + AppUtils.toDisplayDateAndTime(AppUtils.parseDate(item.Time));
item.GroupLabel = n == 0 ? 'Latests' : createGroupLabel(AppUtils.parseDate(item.Time));
filesetStamps[item.Version + ''] = item.Time;
}
$scope.RestoreVersion = 0;
};
$scope.fetchBackupTimes = function() {
$scope.connecting = true;
var qp = '';
if ($scope.IsBackupTemporary)
qp += '?from-remote-only=true';
AppService.get('/backup/' + $scope.BackupID + '/filesets' + qp).then(
function(resp) {
$scope.Filesets = resp.data;
$scope.parseBackupTimesData();
$scope.fetchPathInformation();
},
function(resp) {
var message = resp.statusText;
if (resp.data != null && resp.data.Message != null)
message = resp.data.Message;
$scope.connecting = false;
$scope.ConnectionProgress = '';
alert('Failed to connect: ' + message);
}
);
};
$scope.fetchPathInformation = function() {
var version = $scope.RestoreVersion + '';
if (inProgress[version] || $scope.restore_step != 0)
return;
if (!$scope.IsBackupTemporary && $scope.temporaryDB == null) {
// TODO: Register a temporary db here
}
function handleError(resp) {
delete inProgress[version];
$scope.connecting = false;
$scope.ConnectionProgress = '';
var message = resp.statusText;
if (resp.data != null && resp.data.Message != null)
message = resp.data.Message;
alert('Failed to fetch path information: ' + message);
};
if (filesetsBuilt[version] == null) {
if ($scope.isTemporaryBackup && filesetsRepaired[version] == null) {
$scope.connecting = true;
$scope.ConnectionProgress = 'Fetching path information ...';
inProgress[version] = true;
AppService.post('/backup/' + $scope.BackupID + '/repairupdate', { 'only-paths': true, 'time': filesetStamps[version + '']}).then(
function(resp) {
var taskid = resp.data.ID;
inProgress[version] = taskid;
$scope.taskid = taskid;
ServerStatus.callWhenTaskCompletes(taskid, function() {
AppService.get('/task/' + taskid).then(function(resp) {
delete inProgress[version];
$scope.connecting = false;
$scope.ConnectionProgress = '';
if (resp.data.Status == 'Completed')
{
filesetsRepaired[version] = true;
$scope.fetchPathInformation();
}
else
{
alert('Failed to fetch path information: ' + resp.data.ErrorMessage);
}
}, handleError);
});
}, handleError);
} else {
var stamp = filesetStamps[version];
// In case the times are not loaded yet
if (stamp == null)
return;
$scope.connecting = true;
$scope.ConnectionProgress = 'Fetching path information ...';
inProgress[version] = true;
AppService.get('/backup/' + $scope.BackupID + '/files/*?prefix-only=true&folder-contents=false&time=' + encodeURIComponent(stamp)).then(
function(resp) {
delete inProgress[version];
$scope.connecting = false;
$scope.ConnectionProgress = '';
filesetsBuilt[version] = resp.data.Files;
$scope.Paths = filesetsBuilt[version];
}, handleError);
}
} else {
$scope.Paths = filesetsBuilt[version];
}
};
$scope.$watch('RestoreVersion', function() { $scope.fetchPathInformation(); });
$scope.$watch('RestorePath', function() {
if (($scope.RestorePath || '').trim().length == 0)
$scope.RestoreLocation = 'direct';
else
$scope.RestoreLocation = 'custom';
});
$scope.onClickNext = function() {
var results = $scope.Selected;
if (results.length == 0) {
alert('No items to restore, please select one or more items');
return;
} else {
$scope.restore_step = 1;
}
};
$scope.onClickBack = function() {
$location.path('/restoredirect')
};
$scope.clearSearch = function() {
$scope.InSearchMode = false;
$scope.fetchPathInformation();
};
$scope.doSearch = function() {
if ($scope.Searching || $scope.restore_step != 1)
return;
if (($scope.SearchFilter || '').trim().length == 0) {
$scope.clearSearch();
return;
}
$scope.Searching = true;
var version = $scope.RestoreVersion + '';
var stamp = filesetStamps[version];
AppService.get('/backup/' + $scope.BackupID + '/files/*' + $scope.SearchFilter + '*?prefix-only=false&time=' + encodeURIComponent(stamp)).then(
function(resp) {
$scope.Searching = false;
var searchNodes = [];
var dirsep = $scope.SystemInfo.DirectorySeparator || '/';
function compareablePath(path) {
return $scope.SystemInfo.CaseSensitiveFilesystem ? path : path.toLowerCase();
};
for(var i in filesetsBuilt[version])
searchNodes[i] = { Path: filesetsBuilt[version][i].Path };
var files = resp.data.Files;
for(var i in files) {
var p = files[i].Path;
var cp = compareablePath(p);
var isdir = p[p.length - 1] == dirsep;
for(var j in searchNodes) {
var sn = searchNodes[j];
if (cp.indexOf(compareablePath(sn.Path)) == 0) {
var curpath = sn.Path;
var parts = p.substr(sn.Path.length).split(dirsep);
var col = sn;
for(var k in parts) {
var found = false;
curpath += parts[k];
if (isdir || k != parts.length - 1)
curpath += dirsep;
if (!col.Children)
col.Children = [];
for(var m in col.Children) {
if (compareablePath(col.Children[m].Path) == compareablePath(curpath)) {
found = true;
col = col.Children[m];
}
}
if (!found) {
var n = { Path: curpath, expanded: true };
if (!isdir && k == parts.length - 1) {
n.iconCls = 'x-tree-icon-leaf';
n.leaf = true;
}
col.Children.push(n);
col = n;
}
}
break;
}
}
}
$scope.Paths = searchNodes;
$scope.InSearchMode = true;
},
function(resp) {
$scope.Searching = false;
var message = resp.statusText;
if (resp.data != null && resp.data.Message != null)
message = resp.data.Message;
$scope.connecting = false;
$scope.ConnectionProgress = '';
alert('Failed to connect: ' + message);
}
);
};
$scope.onStartRestore = function() {
var version = $scope.RestoreVersion + '';
var stamp = filesetStamps[version];
var dirsep = $scope.SystemInfo.DirectorySeparator || '/';
var pathSep = $scope.SystemInfo.PathSeparator || '/';
function handleError(resp) {
var message = resp.statusText;
if (resp.data != null && resp.data.Message != null)
message = resp.data.Message;
$scope.connecting = false;
$scope.ConnectionProgress = '';
alert('Failed to connect: ' + message);
};
var p = {
'time': stamp,
'restore-path': $scope.RestoreLocation == 'custom' ? $scope.RestorePath : null,
'overwrite': $scope.RestoreMode == 'overwrite',
'permissions': $scope.RestorePermissions == null ? false : $scope.RestorePermissions
};
var paths = [];
for(var n in $scope.Selected) {
var item = $scope.Selected[n];
if (item.substr(item.length - 1) == dirsep)
paths.push(item + '*');
else
paths.push(item);
}
if (paths.length > 0)
p.paths = paths.join(pathSep);
if ($scope.IsBackupTemporary) {
$scope.connecting = true;
$scope.ConnectionProgress = 'Creating temporary backup ...';
AppService.post('/backup/' + $scope.BackupID + '/copytotemp').then(function(resp) {
var backupid = resp.data.ID;
$scope.ConnectionProgress = 'Building partial temporary database ...';
AppService.post('/backup/' + backupid + '/repair', p).then(function(resp) {
var taskid = $scope.taskid = resp.data.ID;
ServerStatus.callWhenTaskCompletes(taskid, function() {
AppService.get('/task/' + taskid).then(function(resp) {
$scope.ConnectionProgress = 'Starting the restore process ...';
if (resp.data.Status == 'Completed')
{
AppService.post('/backup/' + backupid + '/restore', p).then(function(resp) {
$scope.ConnectionProgress = 'Restoring files ...';
var t2 = $scope.taskid = resp.data.TaskID;
ServerStatus.callWhenTaskCompletes(t2, function() { $scope.onRestoreComplete(t2); });
}, handleError);
}
else
{
alert('Failed to build temporary database: ' + resp.data.ErrorMessage);
$scope.connecting = false;
$scope.ConnectionProgress = '';
}
}, handleError);
});
}, handleError);
}, handleError);
} else {
$scope.ConnectionProgress = 'Starting the restore process ...';
AppService.post('/backup/' + $scope.BackupID + '/restore', p).then(function(resp) {
$scope.ConnectionProgress = 'Restoring files ...';
var t2 = $scope.taskid = resp.data.TaskID;
ServerStatus.callWhenTaskCompletes(t2, function() { $scope.onRestoreComplete(t2); });
}, handleError);
}
};
$scope.onRestoreComplete = function(taskid) {
AppService.get('/task/' + taskid).then(function(resp) {
$scope.connecting = false;
$scope.ConnectionProgress = '';
if (resp.data.Status == 'Completed')
{
$scope.restore_step = 2;
}
else
{
alert('Failed to build restore files: ' + resp.data.ErrorMessage);
}
}, function(resp) {
var message = resp.statusText;
if (resp.data != null && resp.data.Message != null)
message = resp.data.Message;
$scope.connecting = false;
$scope.ConnectionProgress = '';
alert('Failed to connect: ' + message);
});
};
$scope.onClickComplete = function () {
$location.path('/');
}
$scope.BackupID = $routeParams.backupid;
$scope.IsBackupTemporary = parseInt($scope.BackupID) != $scope.BackupID;
// We pass in the filelist through a global variable
// ... bit ugly, but we do not want to do two remote queries,
// ... nor do we want to pass the information through the url
if ($scope.IsBackupTemporary && $rootScope.filesets && $rootScope.filesets[$scope.BackupID]) {
$scope.Filesets = $rootScope.filesets[$scope.BackupID];
$scope.parseBackupTimesData();
$scope.fetchPathInformation();
} else {
$scope.fetchBackupTimes();
}
});