Adds a server-wide backup capability beyond the existing ops_dashboard pg_dump flow: - Daily systemd timer (03:30) runs pg_dumpall + Forgejo dump, then restic to a local NAS repo and an offsite Backblaze B2 repo with Object Lock. Phase-based script with single-instance flock, structured statusfile, systemd hardening, and live-datadir excludes (Postgres / Forgejo) so the dumps stay authoritative. - Ops-agent gets nine new read-only/trigger commands (snapshots, stats, status, logs, plus two triggers) backed by sudoers-whitelisted wrapper scripts that source /etc/restic-backup.env so the agent never sees the restic password or B2 keys. - Two new flows (server_backup_full, server_backup_restore_test) drive the dashboard's "Backup now" and "Restore test" buttons. - /settings/backups gains a Server backup section with overall + per-phase status, NAS / B2 snapshot tables, restore-size / raw-data / dedup-ratio stats, and the last restore-test result. The existing pg_dump section is preserved unchanged. - Runbook docs/runbooks/server-backup.md follows the tailscale-setup pattern (plan + addendum) and covers B2 Object Lock + scoped keys, Forgejo subplan with isolated restore-test stack, the off-server maintenance flow for B2 prune, and the integrity-check schedule. Code-only change — installation on scrum4me-srv follows the runbook. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
25 lines
820 B
Bash
25 lines
820 B
Bash
#!/usr/bin/env bash
|
|
# Read /srv/backups/status/last-run.json. Returns "{}" if missing, so the
|
|
# dashboard can render an "unknown" state instead of erroring.
|
|
|
|
set -uo pipefail
|
|
|
|
STATUS_FILE="${STATUS_FILE:-/srv/backups/status/last-run.json}"
|
|
RESTORE_STATUS_FILE="${RESTORE_STATUS_FILE:-/srv/backups/status/last-restore-test.json}"
|
|
|
|
# We emit a small wrapper object with both files so the UI can render the
|
|
# server-backup status AND the most recent restore-test status from one call.
|
|
last_run='{}'
|
|
if [ -r "$STATUS_FILE" ]; then
|
|
last_run=$(cat "$STATUS_FILE")
|
|
fi
|
|
|
|
last_restore='null'
|
|
if [ -r "$RESTORE_STATUS_FILE" ]; then
|
|
last_restore=$(cat "$RESTORE_STATUS_FILE")
|
|
fi
|
|
|
|
jq -n \
|
|
--argjson last_run "$last_run" \
|
|
--argjson last_restore "$last_restore" \
|
|
'{ last_run: $last_run, last_restore_test: $last_restore }'
|