feat(backup): admin UI with admin-chosen target directory
The backup destination is now chosen by the on-site admin in the UI (Setup -> Backup), not a server env var. An env-pinned target defeats the purpose: the admin can't point backups at a freshly-plugged USB or a NAS mount without editing .env and restarting. The encryption key stays a server secret. Target storage: - New site_config.backup_target_dir (migration 0016, nullable; null = not configured). BackupService reads it fresh each run, so a UI change takes effect with no restart. Only BACKUP_KEY stays env -- a key must never live in the DB it backs up. Routes: - PUT /api/backup/config -- set/clear the target (backup:update; upserts id=1). - POST /api/backup/test -- probe a candidate path server-side (exists / is a directory / writable) so the admin gets feedback before relying on it. - status() now exposes targetDir + keyPresent, so the UI distinguishes 'no target set' from 'BACKUP_KEY missing'. UI (apps/web/src/BackupSettings.tsx): - A Setup -> Backup tab (gated backup:read): an editable target-path field with a Test-target probe (localized ok/missing/not-a-dir/not-writable), Save, the status panel (config state, last-run size/pruned/error, a distinct amber missing-key warning), a Back up now button, and the restore-is-out-of-band note. Full i18n (sq + en); nav.backup. - API client: fetchBackupStatus / setBackupTarget / testBackupTarget / runBackup. Also includes a small in-progress copy trim to the setup-intro i18n strings. Verified live with Playwright: typed a path -> Test reported writable -> Save persisted it -> status reflected it and showed the key-missing warning. Whole monorepo build/lint/test green. Wiki backup-recovery + open-question #5 updated. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
+14
@@ -1934,3 +1934,17 @@ route — out-of-band by design). New `backup` permission resource in @parking/s
|
||||
daily timer, no-op until configured, NOT run at startup. `openRawDb()` added to @parking/db/testing.
|
||||
SMB/NFS work as mount paths; SFTP + admin UI + restore runbook deferred. build/lint/test green (212 server
|
||||
tests, 25 files). Updated [[open-questions]] #5 (first slice BUILT). NOT yet committed beyond this branch.
|
||||
|
||||
## [2026-06-29] feat | Backup admin UI + admin-chosen target (site_config, not env)
|
||||
The backup TARGET DIRECTORY is now chosen by the on-site admin in the UI, not a server env var — env
|
||||
target defeats the purpose (admin can't change where backups land without editing .env + restart). Moved
|
||||
to `site_config.backup_target_dir` (migration 0016, nullable); BackupService reads it fresh each run (no
|
||||
restart to change). Only BACKUP_KEY stays an env secret — a key must NEVER live in the DB it backs up.
|
||||
New routes: PUT /api/backup/config (set/clear target, backup:update, upserts the id=1 row), POST
|
||||
/api/backup/test (server-side path probe: exists/is-dir/writable, backup:update). status() now exposes
|
||||
targetDir + keyPresent so the UI tells "no target" from "no key". UI: Setup → Backup tab
|
||||
(apps/web/src/BackupSettings.tsx) — editable target field + Test-target probe (localized reasons) + Save +
|
||||
status panel (distinct amber "BACKUP_KEY missing" warning) + Back-up-now + restore-out-of-band note; full
|
||||
i18n sq+en; nav.backup. Verified live with Playwright: typed path → Test "writable" → Save persisted →
|
||||
status reflects it + key-missing warning shown. build/lint/test green (whole monorepo). Updated
|
||||
[[backup-recovery]] as-built + [[open-questions]] #5.
|
||||
|
||||
Reference in New Issue
Block a user