diff --git a/Containers/borgbackup/backupscript.sh b/Containers/borgbackup/backupscript.sh
index b975cc05..c30fdbc0 100644
--- a/Containers/borgbackup/backupscript.sh
+++ b/Containers/borgbackup/backupscript.sh
@@ -173,6 +173,17 @@ if [ "$BORG_MODE" = restore ]; then
echo "Could not mount the backup!"
exit 1
fi
+
+ # Save current aio password
+ AIO_PASSWORD="$(grep -oP '"password":"[a-zA-Z0-9 ]+"' /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json)"
+ AIO_PASSWORD="${AIO_PASSWORD##\"password\":\"}"
+ AIO_PASSWORD="${AIO_PASSWORD%\"}"
+
+ # Save current path
+ BORG_LOCATION="$(grep -oP '"borg_backup_host_location":"[\\/a-zA-Z0-9 ]+"' /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json)"
+ BORG_LOCATION="${BORG_LOCATION##\"borg_backup_host_location\":\"}"
+ BORG_LOCATION="${BORG_LOCATION%\"}"
+
if ! rsync --stats --archive --human-readable -vv --delete \
--exclude "nextcloud_aio_mastercontainer/session/"** \
--exclude "nextcloud_aio_mastercontainer/certs/"** \
@@ -183,8 +194,6 @@ if [ "$BORG_MODE" = restore ]; then
fi
umount /tmp/borg
- # TODO: reset fetchtimes in configuration.json so that it doesn't get the latest directly...
-
# Inform user
get_expiration_time
echo "Restore finished successfully on $END_DATE_READABLE ($DURATION_READABLE)"
@@ -196,6 +205,22 @@ if [ "$BORG_MODE" = restore ]; then
# Set backup-mode to restore since it was a restore
sed -i 's/"backup-mode":"[a-z]\+"/"backup-mode":"restore"/g' /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json
+ # Reset the AIO password to the currently used one
+ sed -i "s/\"password\":\"[a-zA-Z0-9 ]\+\"/\"password\":\"$AIO_PASSWORD\"/g" /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json
+
+ # Reset the backup path to the currently used one
+ if [ -n "$BORG_LOCATION" ]; then
+ # shellcheck disable=SC2143
+ if [ -n "$(grep -oP '"borg_backup_host_location":"[\\/a-zA-Z0-9 ]+"' /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json)" ]; then
+ sed -i "s/\"borg_backup_host_location\":\"[\\/a-zA-Z0-9 ]\+\"/\"borg_backup_host_location\":\"$BORG_LOCATION\"/g" /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json
+ else
+ echo "Could not set the borg_backup_host_location as it was empty."
+ echo "Probably the regex did not match."
+ fi
+ else
+ echo "Could not get the borg_backup_host_location as it was empty."
+ echo "Probably the regex did not match."
+ fi
exit 0
fi
@@ -215,3 +240,23 @@ if [ "$BORG_MODE" = check ]; then
echo "Check finished successfully on $END_DATE_READABLE ($DURATION_READABLE)"
exit 0
fi
+
+# Do the backup test
+if [ "$BORG_MODE" = test ]; then
+ if ! [ -d "$BORG_BACKUP_DIRECTORY" ]; then
+ echo "No 'borg' directory in the given backup directory found!"
+ echo "Please adjust the directory so that the borg archive is positioned in a folder named 'borg' inside the given directory!"
+ exit 1
+ elif ! [ -f "$BORG_BACKUP_DIRECTORY/config" ]; then
+ echo "A 'borg' directory was found but could not find the borg archive."
+ echo "It must be positioned directly in the 'borg' subfolder."
+ exit 1
+ elif ! borg list "$BORG_BACKUP_DIRECTORY"; then
+ echo "The entered path seems to be valid but could not open the backup archive."
+ echo "Most likely the entered password was wrong so please adjust it accordingly!"
+ exit 1
+ else
+ echo "Everything looks fine so feel free to continue!"
+ exit 0
+ fi
+fi
diff --git a/Containers/borgbackup/start.sh b/Containers/borgbackup/start.sh
index 7464cc6e..9b561036 100644
--- a/Containers/borgbackup/start.sh
+++ b/Containers/borgbackup/start.sh
@@ -4,19 +4,23 @@
export BORG_BACKUP_DIRECTORY="/mnt/borgbackup/borg"
# Validate BORG_PASSWORD
-if [ -z "$BORG_PASSWORD" ]; then
- echo "BORG_PASSWORD is not allowed to be empty."
+if [ -z "$BORG_PASSWORD" ] && [ -z "$BACKUP_RESTORE_PASSWORD" ]; then
+ echo "Neither BORG_PASSWORD nor BACKUP_RESTORE_PASSWORD are set."
exit 1
fi
-# Export defaults
-export BORG_PASSPHRASE="$BORG_PASSWORD"
+# Export defaults
+if [ -n "$BACKUP_RESTORE_PASSWORD" ]; then
+ export BORG_PASSPHRASE="$BACKUP_RESTORE_PASSWORD"
+else
+ export BORG_PASSPHRASE="$BORG_PASSWORD"
+fi
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
# Validate BORG_MODE
-if [ "$BORG_MODE" != backup ] && [ "$BORG_MODE" != restore ] && [ "$BORG_MODE" != check ]; then
- echo "No correct BORG_MODE mode applied. Valid are 'backup' and 'restore'."
+if [ "$BORG_MODE" != backup ] && [ "$BORG_MODE" != restore ] && [ "$BORG_MODE" != check ] && [ "$BORG_MODE" != test ]; then
+ echo "No correct BORG_MODE mode applied. Valid are 'backup', 'check', 'restore' and 'test'."
exit 1
fi
diff --git a/php/containers.json b/php/containers.json
index 182bcecd..25ee579e 100644
--- a/php/containers.json
+++ b/php/containers.json
@@ -211,7 +211,8 @@
"environmentVariables": [
"BORG_PASSWORD=%BORGBACKUP_PASSWORD%",
"BORG_MODE=%BORGBACKUP_MODE%",
- "SELECTED_RESTORE_TIME=%SELECTED_RESTORE_TIME%"
+ "SELECTED_RESTORE_TIME=%SELECTED_RESTORE_TIME%",
+ "BACKUP_RESTORE_PASSWORD=%BACKUP_RESTORE_PASSWORD%"
],
"volumes": [
{
diff --git a/php/psalm-baseline.xml b/php/psalm-baseline.xml
index 05c5a4e7..29c158ac 100644
--- a/php/psalm-baseline.xml
+++ b/php/psalm-baseline.xml
@@ -18,21 +18,10 @@
$args
-
- $request->getParsedBody()['borg_backup_host_location']
- $request->getParsedBody()['domain']
-
-
- $request->getParsedBody()['borg_backup_host_location']
- $request->getParsedBody()['domain']
-
-
- $request->getParsedBody()['borg_backup_host_location']
- $request->getParsedBody()['domain']
-
-
+
+ $args$args$args$args
diff --git a/php/public/index.php b/php/public/index.php
index bb6aaf20..db9cf821 100644
--- a/php/public/index.php
+++ b/php/public/index.php
@@ -52,6 +52,7 @@ $app->get('/api/docker/getwatchtower', AIO\Controller\DockerController::class .
$app->post('/api/docker/start', AIO\Controller\DockerController::class . ':StartContainer');
$app->post('/api/docker/backup', AIO\Controller\DockerController::class . ':StartBackupContainerBackup');
$app->post('/api/docker/backup-check', AIO\Controller\DockerController::class . ':StartBackupContainerCheck');
+$app->post('/api/docker/backup-test', AIO\Controller\DockerController::class . ':StartBackupContainerTest');
$app->post('/api/docker/restore', AIO\Controller\DockerController::class . ':StartBackupContainerRestore');
$app->post('/api/docker/stop', AIO\Controller\DockerController::class . ':StopContainer');
$app->get('/api/docker/logs', AIO\Controller\DockerController::class . ':GetLogs');
@@ -90,6 +91,7 @@ $app->get('/containers', function ($request, $response, $args) use ($container)
'is_onlyoffice_enabled' => $configurationManager->isOnlyofficeEnabled(),
'is_collabora_enabled' => $configurationManager->isCollaboraEnabled(),
'is_talk_enabled' => $configurationManager->isTalkEnabled(),
+ 'borg_restore_password' => $configurationManager->GetBorgRestorePassword(),
]);
})->setName('profile');
$app->get('/login', function ($request, $response, $args) use ($container) {
diff --git a/php/src/Controller/ConfigurationController.php b/php/src/Controller/ConfigurationController.php
index d17db27d..dbb67ffa 100644
--- a/php/src/Controller/ConfigurationController.php
+++ b/php/src/Controller/ConfigurationController.php
@@ -22,7 +22,8 @@ class ConfigurationController
public function SetConfig(Request $request, Response $response, $args) : Response {
try {
if (isset($request->getParsedBody()['domain'])) {
- $this->configurationManager->SetDomain($request->getParsedBody()['domain']);
+ $domain = $request->getParsedBody()['domain'] ?? '';
+ $this->configurationManager->SetDomain($domain);
}
if (isset($request->getParsedBody()['current-master-password']) || isset($request->getParsedBody()['new-master-password'])) {
@@ -32,7 +33,14 @@ class ConfigurationController
}
if (isset($request->getParsedBody()['borg_backup_host_location'])) {
- $this->configurationManager->SetBorgBackupHostLocation($request->getParsedBody()['borg_backup_host_location']);
+ $location = $request->getParsedBody()['borg_backup_host_location'] ?? '';
+ $this->configurationManager->SetBorgBackupHostLocation($location);
+ }
+
+ if (isset($request->getParsedBody()['borg_restore_host_location']) || isset($request->getParsedBody()['borg_restore_password'])) {
+ $restoreLocation = $request->getParsedBody()['borg_restore_host_location'] ?? '';
+ $borgPassword = $request->getParsedBody()['borg_restore_password'] ?? '';
+ $this->configurationManager->SetBorgRestoreHostLocationAndPassword($restoreLocation, $borgPassword);
}
if (isset($request->getParsedBody()['options-form'])) {
diff --git a/php/src/Controller/DockerController.php b/php/src/Controller/DockerController.php
index fa240b1a..6afbfdd4 100644
--- a/php/src/Controller/DockerController.php
+++ b/php/src/Controller/DockerController.php
@@ -109,6 +109,20 @@ class DockerController
return $response->withStatus(201)->withHeader('Location', '/');
}
+ public function StartBackupContainerTest(Request $request, Response $response, $args) : Response {
+ $config = $this->configurationManager->GetConfig();
+ $config['backup-mode'] = 'test';
+ $this->configurationManager->WriteConfig($config);
+
+ $id = self::TOP_CONTAINER;
+ $this->PerformRecursiveContainerStop($id);
+
+ $id = 'nextcloud-aio-borgbackup';
+ $this->PerformRecursiveContainerStart($id);
+
+ return $response->withStatus(201)->withHeader('Location', '/');
+ }
+
public function StartContainer(Request $request, Response $response, $args) : Response
{
$uri = $request->getUri();
diff --git a/php/src/Data/ConfigurationManager.php b/php/src/Data/ConfigurationManager.php
index b208ad74..e13e1e67 100644
--- a/php/src/Data/ConfigurationManager.php
+++ b/php/src/Data/ConfigurationManager.php
@@ -237,6 +237,8 @@ class ConfigurationManager
// Write domain
$config = $this->GetConfig();
$config['domain'] = $domain;
+ // Reset the borg restore password when setting the domain
+ $config['borg_restore_password'] = '';
$this->WriteConfig($config);
}
@@ -308,6 +310,33 @@ class ConfigurationManager
$this->WriteConfig($config);
}
+ /**
+ * @throws InvalidSettingConfigurationException
+ */
+ public function SetBorgRestoreHostLocationAndPassword(string $location, string $password) : void {
+ if ($location === '') {
+ throw new InvalidSettingConfigurationException("Please enter a path!");
+ }
+
+ $isValidPath = false;
+ if (str_starts_with($location, '/') && !str_ends_with($location, '/')) {
+ $isValidPath = true;
+ }
+
+ if(!$isValidPath) {
+ throw new InvalidSettingConfigurationException("The path may start with '/mnt/', '/media/' or '/host_mnt/' or may be equal to '/var/backups'.");
+ }
+
+ if ($password === '') {
+ throw new InvalidSettingConfigurationException("Please enter the password!");
+ }
+
+ $config = $this->GetConfig();
+ $config['borg_backup_host_location'] = $location;
+ $config['borg_restore_password'] = $password;
+ $this->WriteConfig($config);
+ }
+
/**
* @throws InvalidSettingConfigurationException
*/
@@ -384,6 +413,15 @@ class ConfigurationManager
return $config['borg_backup_host_location'];
}
+ public function GetBorgRestorePassword() : string {
+ $config = $this->GetConfig();
+ if(!isset($config['borg_restore_password'])) {
+ $config['borg_restore_password'] = '';
+ }
+
+ return $config['borg_restore_password'];
+ }
+
public function GetBorgBackupMode() : string {
$config = $this->GetConfig();
if(!isset($config['backup-mode'])) {
diff --git a/php/src/Docker/DockerActionManager.php b/php/src/Docker/DockerActionManager.php
index c84279ea..77da1206 100644
--- a/php/src/Docker/DockerActionManager.php
+++ b/php/src/Docker/DockerActionManager.php
@@ -241,6 +241,8 @@ class DockerActionManager
$replacements[1] = $this->configurationManager->GetApachePort();
} elseif ($out[1] === 'NEXTCLOUD_MOUNT') {
$replacements[1] = $this->configurationManager->GetNextcloudMount();
+ } elseif ($out[1] === 'BACKUP_RESTORE_PASSWORD') {
+ $replacements[1] = $this->configurationManager->GetBorgRestorePassword();
} elseif ($out[1] === 'CLAMAV_ENABLED') {
if ($this->configurationManager->isClamavEnabled()) {
$replacements[1] = 'yes';
diff --git a/php/templates/containers.twig b/php/templates/containers.twig
index bf7d0152..0dd0b88a 100644
--- a/php/templates/containers.twig
+++ b/php/templates/containers.twig
@@ -66,7 +66,9 @@
{% else %}
- Please type in the domain that will be used for Nextcloud:
+ Nextcloud AIO stands for Nextcloud All In One and provides easy deployment and maintenance with most features included in this one Nextcloud instance.
+
New AIO instance
+ Please type in the domain that will be used for Nextcloud if you want to create a new instance:
Make sure that this server is reachable on Port 443 and you've correctly set up the DNS config for the domain that you enter.
- If you have a dynamic IP-address, you can use e.g. DDclient with a compatible domain provider for DNS updates.
+ If you have a dynamic IP-address, you can use e.g. DDclient with a compatible domain provider for DNS updates.
+
+
Restore AIO instance from backup
+ You can alternatively restore an AIO instance from backup.
+
+ {% if borg_backup_host_location == '' or borg_restore_password == '' or borg_backup_mode not in ['test', 'check'] or backup_exit_code != 0 %}
+ Please enter the location of the backup archive on your host and the password of the backup archive below:
+
+ The folder path that you enter may start with /mnt/, /media/ or /host_mnt/ or may be equal to /var/backups. So e.g. /mnt/backup on Linux and macOS or /host_mnt/c/backup/directory on Windows. (This Windows example would be equivalent to 'C:\backup\directory' on the Windows host. So you need to translate the path that you want to use into the correct format.)
+ ⚠ Note that the backup archive must be located in a subfolder of the folder that you enter here and the subfolder which contains the archive must be named 'borg'. Otherwise will the backup container not find the backup archive!
+ {% endif %}
+
+ {% if borg_backup_host_location != '' and borg_restore_password != '' %}
+ {% if borg_backup_mode not in ['test', 'check'] or backup_exit_code != 0 %}
+ Everything set! Click on the button below to test the path and password:
+
+ {% endif %}
+ {% if borg_backup_mode in ['test', 'check'] %}
+ {% if backup_exit_code > 0 %}
+ Last {{ backup_exit_code }} failed! (Logs)
+ {% if borg_backup_mode == 'test' %}
+ Please adjust the path and/or password in order to make it work!
+ {% elseif borg_backup_mode == 'check' %}
+ The backup archive seems to be corrupt. Please try to use a different intact backup archive or try to fix it by following this documentation
+ {% endif %}
+ {% elseif backup_exit_code == 0 %}
+ Last {{ borg_backup_mode }} successful! (Logs)
+ {% if borg_backup_mode == 'test' %}
+ Feel free to check the integrity of the backup archive below before starting the restore process in order to make double-sure that the restore will work. This can take a long time though depending on the size of the backup archive and is thus not required.
+
+ {% endif %}
+ Choose the backup that you want to restore and click on the button below to restore the selected backup. This will restore the whole AIO instance from backup. Please not that the current AIO password will be kept and the AIO password not restored from backup!
+ Somehow the restore failed which is unexpected! Please adjust the path and password, test it and try to restore again!
+ {% endif %}
+ {% endif %}
+ {% endif %}
{% endif %}
{% endif %}
+ {% if isBackupContainerRunning == true and domain == "" %}
+ Backup container is currently running.