Merge pull request #65 from nextcloud/enh/39/allow-to-select-archive

allow to select the archive to restore from
This commit is contained in:
Simon L 2022-02-16 14:43:10 +01:00 committed by GitHub
commit 8a6de8f63c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 64 additions and 16 deletions

View file

@ -1,13 +1,9 @@
#!/bin/bash
# Variables
BORG_BACKUP_DIRECTORY="/mnt/borgbackup/borg"
# Functions
get_start_time(){
START_TIME=$(date +%s)
CURRENT_DATE=$(date --date @"$START_TIME" +"%Y%m%d_%H%M%S")
CURRENT_DATE_READABLE=$(date --date @"$START_TIME" +"%d.%m.%Y - %H:%M:%S")
}
get_expiration_time() {
END_TIME=$(date +%s)
@ -150,9 +146,6 @@ if [ "$BORG_MODE" = backup ]; then
# Remove the update skip file because the backup was successful
rm -f "/nextcloud_aio_volumes/nextcloud_aio_nextcloud_data/skip.update"
echo "$CURRENT_DATE,$CURRENT_DATE_READABLE" >> "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
chmod +r "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
# Prune options
BORG_PRUNE_OPTS=(--stats --progress --keep-within=7d --keep-weekly=4 --keep-monthly=6 "$BORG_BACKUP_DIRECTORY")
@ -172,17 +165,20 @@ fi
# Do the restore
if [ "$BORG_MODE" = restore ]; then
get_start_time
echo "Restoring the last backup..."
# Perform the restore
FIRST_ARCHIVE="$(borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | awk -F " " '{print $1}' | sort -r | head -1)"
if [ -n "$SELECTED_RESTORE_TIME" ]; then
SELECTED_ARCHIVE"$(borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | grep "$SELECTED_RESTORE_TIME" | awk -F " " '{print $1}' | head -1)"
else
SELECTED_ARCHIVE="$(borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | awk -F " " '{print $1}' | sort -r | head -1)"
fi
echo "Restoring '$SELECTED_ARCHIVE'..."
mkdir -p /tmp/borg
if ! borg mount "$BORG_BACKUP_DIRECTORY::$FIRST_ARCHIVE" /tmp/borg; then
if ! borg mount "$BORG_BACKUP_DIRECTORY::$SELECTED_ARCHIVE" /tmp/borg; then
echo "Could not mount the backup!"
exit 1
fi
if ! rsync --stats --archive --human-readable -vv --delete \
--exclude "nextcloud_aio_mastercontainer/data/backup_archives.list" \
--exclude "nextcloud_aio_mastercontainer/session/"** \
--exclude "nextcloud_aio_mastercontainer/certs/"** \
/tmp/borg/nextcloud_aio_volumes/ /nextcloud_aio_volumes; then

View file

@ -1,5 +1,8 @@
#!/bin/bash
# Variables
export BORG_BACKUP_DIRECTORY="/mnt/borgbackup/borg"
# Validate BORG_PASSWORD
if [ -z "$BORG_PASSWORD" ]; then
echo "BORG_PASSWORD is not allowed to be empty."
@ -24,6 +27,12 @@ fi
# Remove lockfile
rm -f "/nextcloud_aio_volumes/nextcloud_aio_database_dump/backup-is-running"
# Get a list of all available borg archives
set -x
borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | awk -F " " '{print $1","$3,$4}' > "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
chmod +r "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
set +x
if [ -n "$FAILED" ]; then
if [ "$BORG_MODE" = backup ]; then
# Add file to Nextcloud container so that it skips any update the next time

View file

@ -194,7 +194,8 @@
"internalPorts": [],
"environmentVariables": [
"BORG_PASSWORD=%BORGBACKUP_PASSWORD%",
"BORG_MODE=%BORGBACKUP_MODE%"
"BORG_MODE=%BORGBACKUP_MODE%",
"SELECTED_RESTORE_TIME=%SELECTED_RESTORE_TIME%"
],
"volumes": [
{

View file

@ -87,6 +87,7 @@ $app->get('/containers', function ($request, $response, $args) use ($container)
'was_start_button_clicked' => $configurationManager->wasStartButtonClicked(),
'has_update_available' => $dockerActionManger->isAnyUpdateAvailable(),
'last_backup_time' => $configurationManager->GetLastBackupTime(),
'backup_times' => $configurationManager->GetBackupTimes(),
]);
})->setName('profile');
$app->get('/login', function ($request, $response, $args) use ($container) {

View file

@ -83,6 +83,7 @@ class DockerController
public function StartBackupContainerRestore(Request $request, Response $response, $args) : Response {
$config = $this->configurationManager->GetConfig();
$config['backup-mode'] = 'restore';
$config['selected-restore-time'] = $request->getParsedBody()['selected_restore_time'];
$this->configurationManager->WriteConfig($config);
$id = self::TOP_CONTAINER;

View file

@ -64,7 +64,7 @@ class ConfigurationManager
}
$content = file_get_contents(DataConst::GetBackupArchivesList());
if ($content === "") {
if (count($content) === 0) {
return '';
}
@ -83,6 +83,30 @@ class ConfigurationManager
return $lastBackupTime;
}
public function GetBackupTimes() : array {
if (!file_exists(DataConst::GetBackupArchivesList())) {
return $array[] = '';
}
$content = file_get_contents(DataConst::GetBackupArchivesList());
if (count($content) === 0) {
return $array[] = '';
}
$backupLines = explode('\n', $content);
$backupTimes = [];
foreach($backupLines as $lines) {
$backupTimesTemp = explode(',', $lines);
$backupTimes[] = $backupTimesTemp[1];
}
if (!is_array($backupTimes)) {
return $array[] = '';
}
return $backupTimes;
}
public function wasStartButtonClicked() : bool {
if (isset($this->GetConfig()['wasStartButtonClicked'])) {
return true;
@ -152,6 +176,15 @@ class ConfigurationManager
return $config['backup-mode'];
}
public function GetSelectedRestoreTime() : string {
$config = $this->GetConfig();
if(!isset($config['selected-restore-time'])) {
$config['selected-restore-time'] = '';
}
return $config['selected-restore-time'];
}
public function GetAIOURL() : string {
$config = $this->GetConfig();
if(!isset($config['AIO_URL'])) {

View file

@ -212,6 +212,8 @@ class DockerActionManager
$replacements[1] = $this->configurationManager->GetBackupMode();
} elseif ($out[1] === 'AIO_URL') {
$replacements[1] = $this->configurationManager->GetAIOURL();
} elseif ($out[1] === 'SELECTED_RESTORE_TIME') {
$replacements[1] = $this->configurationManager->GetSelectedRestoreTime();
} else {
$replacements[1] = $this->configurationManager->GetSecret($out[1]);
}

View file

@ -206,11 +206,16 @@
<input class="button" type="submit" value="Check backup integrity" onclick="return confirm('Check backup integrity? Are you sure that you want to check the backup? This can take a long time depending on the size of your backup.')" /><br/>
</form>
Click on the button below to restore the last backup from {{ last_backup_time }}. This will overwrite all your files with the state of the backup. It makes sense to run an integrity check before restoring your files but is not mandatory since it shouldn't be needed in most situations.<br><br>
<form method="POST" action="/api/docker/restore" class="xhr">
Choose the backup that you want to restore and click on the button below to restore the selected backup. This will overwrite all your files with the state of the backup so you should consider creating a backup first. It also makes sense to run an integrity check before restoring your files but is not mandatory since it shouldn't be needed in most situations.<br><br>
<form method="POST" action="/api/docker/restore" class="xhr" id="restore_selection">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input class="button" type="submit" value="Restore last backup" onclick="return confirm('Restore last backup? Are you sure that you want to restore the last backup? This will stop all running containers and restore the last backup from {{ last_backup_time }}. You might want to check the backup integrity first.')" />
<select id="selected_restore_time" name="selected_restore_time" form="restore_selection">
{% for restore_time in backup_times %}
<option value="{{ restore_time }}">{{ restore_time }}"</option>
{% endfor %}
</select>
<input class="button" type="submit" value="Restore selected backup" onclick="return confirm('Restore the selected backup? Are you sure that you want to restore the selected backup? This will stop all running containers and restore the selected backup. It is recommended to create a backup first. You might also want to check the backup integrity.')" />
</form>
{% endif %}
{% endif %}