mAi
99c9d89daa
feat(backups): t-paliad-246 — Backup Mode Slice A (on-demand admin org export)
m/paliad#77 Slice A. Folds the unbuilt t-paliad-214 Slice 3 (org async
export) into a new "Backup Mode" surface gated by adminGate.
m's calls (all 4 material picks per design §2):
- Storage: local disk PALIAD_EXPORT_DIR (LocalDiskStore only)
- Format: .zip bundle (xlsx + JSON + CSV + README) — no-lock-in preserved
- paliadin_turns + paliadin_aichat_conversation: EXCLUDE structurally
- Scheduler (Slice B): nightly 03:00 UTC, env-tunable
Wiring:
- mig 123 adds paliad.backups catalog table (kind/status/storage_uri/
size/row_counts/warnings/error/deleted_at + admin-only RLS).
- ExportService.WriteOrg + orgSheetQueries enumerate 37 entity sheets
+ 12 ref sheets; REPEATABLE READ READ ONLY tx wraps the dump for
snapshot consistency (design §3.3).
- writeBundle + runSheetQuery refactored to take a sqlx.QueryerContext
so both *sqlx.DB (personal/project paths, unchanged) and *sqlx.Tx
(org snapshot path) work.
- BackupRunner orchestrates: catalog INSERT → audit INSERT
(event_type='backup_created') → WriteOrg → ArtifactStore.Put → patch
catalog + audit on success/failure.
- ArtifactStore interface + LocalDiskStore impl (defense-in-depth key
validation + URI-outside-dir guard).
- Sentinel actor for scheduled runs: actor_email='system@paliad',
actor_id=NULL — no phantom user in paliad.users.
- Admin handlers POST /api/admin/backups/run + GET list/get/download
behind adminGate(users, …); /admin/backups page + sidebar entry +
bilingual i18n keys.
- BackupRunner only wired when PALIAD_EXPORT_DIR is set; routes return
503 otherwise (same shape as requireDB).
Tests: 8 pure-function tests cover registry shape (no dups, paliadin
absent both as sheet name and SQL substring, ref__* sheets unscoped,
every sheet has ORDER BY) and LocalDiskStore (round-trip, bad-key
rejection, URI-traversal rejection, mkdir on construction).
go build ./... + go test ./internal/... clean. bun run build clean.
Slice B (BackupScheduler + retention cleanup) and Slice C (UI polish)
are separate follow-ups per head's instruction.
2026-05-25 15:28:37 +02:00
..
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-30 03:42:25 +02:00
2026-04-16 13:54:19 +02:00
2026-04-30 03:42:25 +02:00
2026-04-16 13:54:19 +02:00
2026-04-30 03:42:25 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 13:54:19 +02:00
2026-04-16 17:11:02 +02:00
2026-04-16 17:11:02 +02:00
2026-04-17 11:59:49 +02:00
2026-04-17 11:59:49 +02:00
2026-04-17 13:54:32 +02:00
2026-04-17 13:54:32 +02:00
2026-04-18 20:26:11 +02:00
2026-04-18 20:26:11 +02:00
2026-04-20 12:34:38 +02:00
2026-04-20 12:34:38 +02:00
2026-04-20 13:17:24 +02:00
2026-04-20 13:17:24 +02:00
2026-04-20 17:35:38 +02:00
2026-04-20 17:35:38 +02:00
2026-04-20 17:35:38 +02:00
2026-04-20 17:35:38 +02:00
2026-04-23 01:00:31 +02:00
2026-04-23 01:00:31 +02:00
2026-04-25 23:37:51 +02:00
2026-04-25 23:37:51 +02:00
2026-04-27 11:47:10 +02:00
2026-04-27 11:47:10 +02:00
2026-04-27 14:59:03 +02:00
2026-04-27 14:59:03 +02:00
2026-04-27 19:34:56 +02:00
2026-04-27 19:34:56 +02:00
2026-04-28 13:05:22 +02:00
2026-04-28 13:05:22 +02:00
2026-04-29 22:09:39 +02:00
2026-04-29 22:09:39 +02:00
2026-04-30 03:42:25 +02:00
2026-04-29 22:17:32 +02:00
2026-04-30 10:54:46 +02:00
2026-04-30 10:54:46 +02:00
2026-04-30 11:11:47 +02:00
2026-04-30 11:11:47 +02:00
2026-04-30 12:49:04 +02:00
2026-04-30 12:49:04 +02:00
2026-04-30 16:41:38 +02:00
2026-04-30 16:41:38 +02:00
2026-05-04 14:36:50 +02:00
2026-05-04 14:36:50 +02:00
2026-05-04 14:36:50 +02:00
2026-05-04 14:36:50 +02:00
2026-05-04 14:57:54 +02:00
2026-05-04 14:57:54 +02:00
2026-05-04 17:03:58 +02:00
2026-05-04 17:03:58 +02:00
2026-05-04 17:03:58 +02:00
2026-05-04 17:03:58 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 00:05:12 +02:00
2026-05-05 01:25:03 +02:00
2026-05-05 01:25:03 +02:00
2026-05-05 01:49:01 +02:00
2026-05-05 01:49:01 +02:00
2026-05-05 02:19:37 +02:00
2026-05-05 02:19:37 +02:00
2026-05-05 02:48:31 +02:00
2026-05-05 02:48:31 +02:00
2026-05-05 03:17:46 +02:00
2026-05-05 03:17:46 +02:00
2026-05-05 03:46:45 +02:00
2026-05-05 03:46:45 +02:00
2026-05-05 04:32:50 +02:00
2026-05-05 04:32:50 +02:00
2026-05-05 11:18:38 +02:00
2026-05-05 11:18:38 +02:00
2026-05-05 11:18:38 +02:00
2026-05-05 11:22:14 +02:00
2026-05-05 11:18:38 +02:00
2026-05-05 11:18:38 +02:00
2026-05-05 11:53:13 +02:00
2026-05-05 11:53:13 +02:00
2026-05-05 13:29:47 +02:00
2026-05-05 13:29:47 +02:00
2026-05-06 12:37:08 +02:00
2026-05-06 12:37:08 +02:00
2026-05-06 15:13:26 +02:00
2026-05-06 15:13:26 +02:00
2026-05-06 16:45:19 +02:00
2026-05-06 16:45:19 +02:00
2026-05-07 12:51:37 +02:00
2026-05-07 12:51:37 +02:00
2026-05-07 20:58:57 +02:00
2026-05-07 20:58:57 +02:00
2026-05-07 21:49:33 +02:00
2026-05-07 21:49:33 +02:00
2026-05-07 22:00:26 +02:00
2026-05-07 22:00:26 +02:00
2026-05-07 22:21:45 +02:00
2026-05-07 22:21:45 +02:00
2026-05-07 22:41:18 +02:00
2026-05-07 22:41:18 +02:00
2026-05-08 02:13:58 +02:00
2026-05-08 02:13:58 +02:00
2026-05-08 16:00:21 +02:00
2026-05-08 16:00:21 +02:00
2026-05-08 16:23:12 +02:00
2026-05-08 16:23:12 +02:00
2026-05-08 16:54:34 +02:00
2026-05-08 16:54:34 +02:00
2026-05-08 17:16:57 +02:00
2026-05-08 17:16:57 +02:00
2026-05-08 17:16:57 +02:00
2026-05-08 17:16:57 +02:00
2026-05-08 19:15:44 +02:00
2026-05-08 19:15:44 +02:00
2026-05-08 19:20:52 +02:00
2026-05-08 19:20:52 +02:00
2026-05-08 19:42:05 +02:00
2026-05-08 19:42:05 +02:00
2026-05-08 20:21:13 +02:00
2026-05-08 20:21:13 +02:00
2026-05-08 21:52:50 +02:00
2026-05-08 21:52:50 +02:00
2026-05-08 22:01:44 +02:00
2026-05-08 22:01:44 +02:00
2026-05-08 22:16:55 +02:00
2026-05-08 22:16:55 +02:00
2026-05-08 23:33:53 +02:00
2026-05-08 23:33:53 +02:00
2026-05-09 15:33:20 +02:00
2026-05-09 15:33:20 +02:00
2026-05-09 16:07:17 +02:00
2026-05-09 16:07:17 +02:00
2026-05-15 00:19:19 +02:00
2026-05-15 00:19:19 +02:00
2026-05-15 00:19:31 +02:00
2026-05-15 00:19:31 +02:00
2026-05-15 00:19:37 +02:00
2026-05-15 00:19:37 +02:00
2026-05-15 00:28:38 +02:00
2026-05-15 00:28:38 +02:00
2026-05-15 00:28:49 +02:00
2026-05-15 00:28:49 +02:00
2026-05-15 00:29:00 +02:00
2026-05-15 00:29:00 +02:00
2026-05-15 00:40:50 +02:00
2026-05-15 00:40:50 +02:00
2026-05-15 00:40:59 +02:00
2026-05-15 00:40:59 +02:00
2026-05-15 01:01:08 +02:00
2026-05-15 01:01:08 +02:00
2026-05-15 01:01:17 +02:00
2026-05-15 01:01:17 +02:00
2026-05-15 01:37:34 +02:00
2026-05-15 01:37:34 +02:00
2026-05-15 01:37:57 +02:00
2026-05-15 01:37:57 +02:00
2026-05-15 17:53:08 +02:00
2026-05-15 17:53:08 +02:00
2026-05-16 01:17:23 +02:00
2026-05-16 01:17:23 +02:00
2026-05-16 01:29:31 +02:00
2026-05-16 01:29:31 +02:00
2026-05-17 12:30:53 +02:00
2026-05-17 12:30:53 +02:00
2026-05-18 11:46:12 +02:00
2026-05-18 11:46:12 +02:00
2026-05-18 12:13:13 +02:00
2026-05-18 12:13:13 +02:00
2026-05-18 15:39:03 +02:00
2026-05-18 15:39:03 +02:00
2026-05-18 15:05:46 +02:00
2026-05-18 17:28:19 +02:00
2026-05-18 17:29:21 +02:00
2026-05-18 17:33:01 +02:00
2026-05-18 17:46:08 +02:00
2026-05-18 17:46:08 +02:00
2026-05-19 12:44:27 +02:00
2026-05-19 12:44:27 +02:00
2026-05-19 12:51:52 +02:00
2026-05-19 12:51:52 +02:00
2026-05-20 09:50:07 +02:00
2026-05-20 09:50:07 +02:00
2026-05-20 09:47:14 +02:00
2026-05-20 09:47:14 +02:00
2026-05-20 09:47:14 +02:00
2026-05-20 09:47:14 +02:00
2026-05-20 09:52:28 +02:00
2026-05-20 09:52:28 +02:00
2026-05-20 13:05:27 +02:00
2026-05-20 13:05:27 +02:00
2026-05-20 13:26:23 +02:00
2026-05-20 13:26:23 +02:00
2026-05-20 13:55:56 +02:00
2026-05-20 13:55:56 +02:00
2026-05-20 14:43:42 +02:00
2026-05-20 14:43:42 +02:00
2026-05-20 14:46:36 +02:00
2026-05-20 14:46:36 +02:00
2026-05-20 14:55:55 +02:00
2026-05-20 14:55:55 +02:00
2026-05-20 14:55:55 +02:00
2026-05-20 14:55:55 +02:00
2026-05-20 15:24:06 +02:00
2026-05-20 15:24:06 +02:00
2026-05-20 15:38:30 +02:00
2026-05-20 15:38:30 +02:00
2026-05-20 15:50:21 +02:00
2026-05-20 15:50:21 +02:00
2026-05-20 19:15:32 +02:00
2026-05-20 19:15:32 +02:00
2026-05-22 15:17:24 +02:00
2026-05-22 15:17:24 +02:00
2026-05-23 00:06:08 +02:00
2026-05-23 00:06:08 +02:00
2026-05-23 02:19:55 +02:00
2026-05-23 02:19:55 +02:00
2026-05-25 13:57:38 +02:00
2026-05-25 13:57:38 +02:00
2026-05-25 14:54:51 +02:00
2026-05-25 14:54:51 +02:00
2026-05-25 15:28:37 +02:00
2026-05-25 15:28:37 +02:00