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>
53 lines
1.2 KiB
TypeScript
53 lines
1.2 KiB
TypeScript
'use client'
|
|
|
|
import type { BackupFile } from '../page'
|
|
import type {
|
|
BackupStatusEnvelope,
|
|
ResticSnapshot,
|
|
ResticStats,
|
|
} from '../_lib/types'
|
|
import DatabaseBackupsSection from './database-backups-section'
|
|
import ServerBackupSection from './server-backup-section'
|
|
|
|
type Props = {
|
|
backups: BackupFile[]
|
|
listError: string | null
|
|
envelope: BackupStatusEnvelope
|
|
nasSnapshots: ResticSnapshot[]
|
|
b2Snapshots: ResticSnapshot[]
|
|
nasStats: ResticStats | null
|
|
b2Stats: ResticStats | null
|
|
serverBackupErrors: {
|
|
status?: string
|
|
nasSnapshots?: string
|
|
b2Snapshots?: string
|
|
nasStats?: string
|
|
b2Stats?: string
|
|
}
|
|
}
|
|
|
|
export default function BackupsPanel({
|
|
backups,
|
|
listError,
|
|
envelope,
|
|
nasSnapshots,
|
|
b2Snapshots,
|
|
nasStats,
|
|
b2Stats,
|
|
serverBackupErrors,
|
|
}: Props) {
|
|
return (
|
|
<div className="space-y-12">
|
|
<DatabaseBackupsSection backups={backups} listError={listError} />
|
|
<div className="h-px bg-border" />
|
|
<ServerBackupSection
|
|
envelope={envelope}
|
|
nasSnapshots={nasSnapshots}
|
|
b2Snapshots={b2Snapshots}
|
|
nasStats={nasStats}
|
|
b2Stats={b2Stats}
|
|
errors={serverBackupErrors}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|