allow to back up additional directories

Signed-off-by: szaimen <szaimen@e.mail.de>
This commit is contained in:
szaimen 2022-08-22 17:35:03 +02:00
parent 6c63adc37f
commit 2ee63dbe42
9 changed files with 153 additions and 2 deletions

View file

@ -233,7 +233,9 @@
"BORG_PASSWORD=%BORGBACKUP_PASSWORD%",
"BORG_MODE=%BORGBACKUP_MODE%",
"SELECTED_RESTORE_TIME=%SELECTED_RESTORE_TIME%",
"BACKUP_RESTORE_PASSWORD=%BACKUP_RESTORE_PASSWORD%"
"BACKUP_RESTORE_PASSWORD=%BACKUP_RESTORE_PASSWORD%",
"ADDITIONAL_DIRECTORIES_BACKUP=%ADDITIONAL_DIRECTORIES_BACKUP%",
"BORGBACKUP_HOST_LOCATION=%BORGBACKUP_HOST_LOCATION%"
],
"volumes": [
{

View file

@ -104,6 +104,7 @@ $app->get('/containers', function ($request, $response, $args) use ($container)
'is_backup_section_enabled' => $configurationManager->isBackupSectionEnabled(),
'is_imaginary_enabled' => $configurationManager->isImaginaryEnabled(),
'is_fulltextsearch_enabled' => $configurationManager->isFulltextsearchEnabled(),
'additional_backup_directories' => $configurationManager->GetAdditionalBackupDirectoriesString(),
]);
})->setName('profile');
$app->get('/login', function ($request, $response, $args) use ($container) {

View file

@ -57,6 +57,11 @@ class ConfigurationController
$this->configurationManager->DeleteDailyBackupTime();
}
if (isset($request->getParsedBody()['additional_backup_directories'])) {
$additionalBackupDirectories = $request->getParsedBody()['additional_backup_directories'] ?? '';
$this->configurationManager->SetAdditionalBackupDirectories($additionalBackupDirectories);
}
if (isset($request->getParsedBody()['delete_timezone'])) {
$this->configurationManager->DeleteTimezone();
}

View file

@ -584,6 +584,45 @@ class ConfigurationManager
}
}
/**
* @throws InvalidSettingConfigurationException
*/
public function SetAdditionalBackupDirectories(string $additionalBackupDirectories) : void {
$additionalBackupDirectoriesArray = explode("\n", $additionalBackupDirectories);
$validDirectories = '';
foreach($additionalBackupDirectoriesArray as $entry) {
// Trim all unwanted chars on both sites
$entry = trim($entry);
if ($entry !== "") {
if (!preg_match("#^/[0-1a-zA-Z/-_]$#", $entry) && !preg_match("#^[0-1a-zA-Z_-]$#", $entry)) {
throw new InvalidSettingConfigurationException("You entered unallowed characters! Problematic is " . $entry);
}
$validDirectories .= rtrim($entry, '/') . PHP_EOL;
}
}
if ($validDirectories === '') {
unlink(DataConst::GetAdditionalBackupDirectoriesFile());
} else {
file_put_contents(DataConst::GetAdditionalBackupDirectoriesFile(), $validDirectories);
}
}
public function GetAdditionalBackupDirectoriesString() : string {
if (!file_exists(DataConst::GetAdditionalBackupDirectoriesFile())) {
return '';
}
$additionalBackupDirectories = file_get_contents(DataConst::GetAdditionalBackupDirectoriesFile());
return $additionalBackupDirectories;
}
public function GetAdditionalBackupDirectoriesArray() : array {
$additionalBackupDirectories = $this->GetAdditionalBackupDirectoriesString();
$additionalBackupDirectoriesArray = explode("\n", $additionalBackupDirectories);
$additionalBackupDirectoriesArray = array_unique($additionalBackupDirectoriesArray, SORT_REGULAR);
return $additionalBackupDirectoriesArray;
}
public function isDailyBackupRunning() : bool {
if (file_exists(DataConst::GetDailyBackupBlockFile())) {
return true;

View file

@ -31,6 +31,10 @@ class DataConst {
return self::GetDataDirectory() . '/daily_backup_time';
}
public static function GetAdditionalBackupDirectoriesFile() : string {
return self::GetDataDirectory() . '/additional_backup_directories';
}
public static function GetDailyBackupBlockFile() : string {
return self::GetDataDirectory() . '/daily_backup_running';
}

View file

@ -314,6 +314,14 @@ class DockerActionManager
$replacements[1] = $this->configurationManager->GetNextcloudUploadLimit();
} elseif ($out[1] === 'NEXTCLOUD_MAX_TIME') {
$replacements[1] = $this->configurationManager->GetNextcloudMaxTime();
} elseif ($out[1] === 'ADDITIONAL_DIRECTORIES_BACKUP') {
if ($this->configurationManager->GetAdditionalBackupDirectoriesString() !== '') {
$replacements[1] = 'yes';
} else {
$replacements[1] = '';
}
} elseif ($out[1] === 'BORGBACKUP_HOST_LOCATION') {
$replacements[1] = $this->configurationManager->GetBorgBackupHostLocation();
} else {
$replacements[1] = $this->configurationManager->GetSecret($out[1]);
}
@ -354,6 +362,22 @@ class DockerActionManager
$requestBody['HostConfig']['CapAdd'] = ["SYS_ADMIN"];
$requestBody['HostConfig']['Devices'] = [["PathOnHost" => "/dev/fuse", "PathInContainer" => "/dev/fuse", "CgroupPermissions" => "rwm"]];
$requestBody['HostConfig']['SecurityOpt'] = ["apparmor:unconfined"];
// Additional backup directories
$mounts = [];
foreach ($this->configurationManager->GetAdditionalBackupDirectoriesArray() as $additionalBackupDirectories) {
if ($additionalBackupDirectories !== '') {
if (!str_starts_with($additionalBackupDirectories, '/')) {
$target = '/docker_volumes/';
} else {
$target = '/host_mounts';
}
$mounts[] = ["Type" => "bind", "Source" => $additionalBackupDirectories, "Target" => $target . $additionalBackupDirectories, "ReadOnly" => true, "BindOptions" => ["NonRecursive" => true]];
}
}
if(count($mounts) > 0) {
$requestBody['HostConfig']['Mounts'] = $mounts;
}
}
$url = $this->BuildApiUrl('containers/create?name=' . $container->GetIdentifier());

View file

@ -371,7 +371,7 @@
</form>
<h3>Backup restore</h3>
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>
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. Please note that this will not restore additionally chosen backup directories!<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}}">
@ -406,6 +406,21 @@
<input class="button" type="submit" value="Disable daily backups" />
</form>
{% endif %}
<h3>Back up additional directories and volumes of your host</h3>
Below, you can enter directories and docker volumes of your host that will backed up additionally into the same borg backup archive.<br><br>
<form method="POST" action="/api/configuration" class="xhr">
<textarea id="additional_backup_directories" name="additional_backup_directories" rows="4" cols="50" placeholder="/directory/on/the/host&#10;my_custom_docker_volume">{{ additional_backup_directories }}</textarea>
<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="Submit" /><br>
</form>
Each line and entry needs to start with a slash or letter/digit. Allowed are only <b>a-z</b>, <b>A-Z</b>, <b>0-9</b>, <b>_</b>, <b>-</b>, and <b>/</b>. If the entry begins with a letter/digit are slashes not supported. Two valid entries are <b>/directory/on/the/host</b> and <b>my_custom_docker_volume</b><br><br/>
Make sure to specify all storages that you want to back up separately since storages will not be mounted recursively. E.g. providing <b>/</b> as additional backup directory will only back up files and folders that are stored on the root partition and not on the EFI partition or any other. Excluded by the backup will be caches and a few other directories. You should make sure to stop all services before the backup can run correctly if you want to back up the root partition. For automating this see <a href="https://github.com/nextcloud/all-in-one#how-to-stopstartupdate-containers-or-trigger-the-daily-backup-from-a-script-externally">this documentation</a><br><br/>
Please note that the chosen directories/volumes will not be restored when you restore your instance, so this would need to be done manually. <br><br>
{% if additional_backup_directories != "" %}
This option is currently set. You can disable it again by clearing the field and submitting your changes.<br><br>
{% endif %}
{% endif %}
{% endif %}
{% if has_backup_run_once == false %}