LibrePortal/scripts/backup/app/backup_app_all.sh
librelad fe770ae699 feat(backup): system-config snapshot + skip the reproducible WebUI; reserved-name docs
(a) Docs: reserve tools/ scripts/ resources/ as LibrePortal folder names (apps must
not bind-mount to them); document resources/ as the home for nest-able data AND for
.sh payloads that execute on load (vs scripts/ for sourced functions); document the
backup model (what's captured vs reproducible).

(b) System-config backup so a bare-metal restore is self-sufficient — this is why
the system root is its own tree. New scripts/backup/system/backup_system.sh:
- backupSystemConfig snapshots <system>/configs (global settings, WebUI creds, and
  the BACKUP-LOCATION creds — otherwise the keys to reach your own backups live only
  on the box) to every enabled location. Lightweight static-dir snapshot — it does
  NOT go through backupAppStart (no containers to quiesce / DBs to dump).
- restic adapter resticBackupSystemToLocation (tag system=config) + dispatcher
  engineBackupSystem; restore via resticRestoreSystemLatest / engineRestoreSystemLatest
  + backupRestoreSystemConfig (restores to a STAGING dir — never auto-overwrites
  live config).
- backupAllApps runs it after the app loop.

WebUI exclusion: backupAllApps skips the 'libreportal' app — its frontend + generated
JSON regenerate, and its only state (the login) is in the system config now captured
above. Nothing in its data dir warrants a snapshot.

Verified with stubs: app loop skips libreportal + invokes the system backup; the
system backup dispatches to both locations; backup/restore function names pair with
the dispatcher. NOTE: restic-only (the sole live engine adapter); end-to-end repo
round-trip still needs a live box before being relied on.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-26 00:20:31 +01:00

41 lines
1.4 KiB
Bash

#!/bin/bash
backupAllApps()
{
isHeader "Backing up all installed applications"
if [ ! -f "$docker_dir/$db_file" ]; then
isError "Database not found: $docker_dir/$db_file"
return 1
fi
local app_names=()
while IFS= read -r name; do
app_names+=("$name")
done < <(runInstallOp sqlite3 "$docker_dir/$db_file" "SELECT name FROM apps WHERE status = 1;")
local done_count=0
for name in "${app_names[@]}"; do
# The libreportal WebUI app is reproducible — frontend + generated JSON
# regenerate on deploy, and its only state (the login) lives in the system
# config, captured by backupSystemConfig below. Nothing in its data dir is
# worth a snapshot.
if [[ "$name" == "libreportal" ]]; then
isNotice "Skipping '$name' — WebUI is reproducible; its state is in the system config"
continue
fi
backupAppStart "$name"
((done_count++))
done
# System config snapshot (global settings, WebUI creds, backup-location creds)
# so a bare-metal restore is self-sufficient — this is why the system root is
# its own backup-able tree. The code/install tree is reproducible from the
# release, so it is deliberately NOT included.
if declare -f backupSystemConfig >/dev/null 2>&1; then
backupSystemConfig
fi
isSuccessful "Backup pass complete — ${done_count} app(s) + system config"
}