feat(backups): scheduled database backups via cron tick

- database_services gains backup_schedule + next_backup_at (goose migration,
  sqlc schema, new queries).
- PUT /databases/:id accepts backup_schedule — validated cron expression,
  empty string disables; invalid returns 400.
- Cron scheduler tick now also folds due databases into the existing
  snapshot pipeline (createBackupProcess).
- Databases page backups section gets a schedule row showing current expr
  and next run.
- resolveBackupConfig reports real next_backup/enabled instead of a fake
  last+24h estimate.
- Verified e2e: '* * * * *' produced three consecutive completed archives
  containing the seeded payload; invalid schedule rejected.

Authored By: TDvorak <[email protected]>
This commit is contained in:
Tomas Dvorak committed 2026-09-17 17:46:02 +02:00
1 parent aeb91b03e9
commit 08d92fb10f
13 files changed
+387 -99

No files matched your search

+6 -3
View File
@@ -332,9 +332,12 @@ All of these have working APIs and no UI. Cheapest feature wins in the repo.
through logs was not viable). Download action in the `/databases` backups
list. Verified e2e: byte-identical archive, 404 missing/cross-user,
401 unauthenticated.
- [ ] **Scheduled database backups**: backups are manual-only despite a
`retention` field. Add a `schedule` column + fold due database backups
into the cron scheduler tick.
- [x] **Scheduled database backups**: `database_services.backup_schedule`
(cron expr) + `next_backup_at`; `PUT /databases/:id` accepts
`backup_schedule` (validated, empty string disables). The cron tick folds
due databases into `createBackupProcess`. Schedule row in the
`/databases` backups section. Verified e2e: `* * * * *` produced three
consecutive completed archives with real volume data.
**Gate**: every registered route is reachable from the UI or has an explicit
"API-only" note in the spec. A request to `/g/{service}` with a valid key