Files
picpeak/backend
Luca 7fdf01ad21 fix(backup): inline DB dump + fail-loud guard so "Run Backup Now" can't ship files-only
The previous file-backup workflow only LOOKED UP an existing
database dump via getDatabaseBackupInfo() and silently shipped a
files-only manifest when none was found. Admins clicking "Run
Backup Now" (or relying on the schedule) got an apparent success
that omitted every customer / quote / invoice / contract / payment-
log row. The data-loss footgun was discovered 2026-05-29 when an
admin who'd been "backing up" for weeks via the UI lost the entire
CRM after a routine docker compose down -v — every produced
manifest had database: { backup_file: null, size: 0, tables: {} }.
New helper `ensureDatabaseDumpForBackup(config)` encapsulates:
  1. Inline pg_dump (or SQLite copy) before the file scan, via
     databaseBackupService.backup(). Result lands in
     database_backup_runs and is picked up by the existing
     getDatabaseBackupInfo lookup that writes the manifest.
  2. Fail-loud guard: if no usable dump file is reachable (path
     missing, 0 bytes, or never existed), throw — the existing
     catch in runBackupInternal marks the backup_runs row failed
     with the error_message and emails the admin if configured.
     No more silent files-only manifests.
  3. Opt-out: `backup_database_inline_dump = false` skips the
     inline dump for admins who already run their own scheduled
     `backup_database_schedule`. The fail-loud guard still
     applies, so an opted-out install with no recent dump still
     aborts loudly instead of producing a partial backup. Default
     ON is encoded as "skip only when explicitly false" — undefined
     (existing installs upgrading) falls through to the safe-
     default ON branch.
The helper returns the verified `databaseInfo` so the manifest-build
step at runBackupInternal:917 reuses it instead of calling
getDatabaseBackupInfo a second time. S3/future destinations that
override `result.databaseInfo` are still respected (the existing
`result.databaseInfo ||` fallback shape stays put).
Test suite covers: default-on happy path, dump-throws-aborts-run,
opt-out + recent dump + proceeds, opt-out + no-dump + fail-loud,
opt-out + 0-byte dump + fail-loud. Mocks
databaseBackupService.backup so the tests don't depend on pg_dump
or sqlite3 CLI binaries being installed.
Stage A of three-stage backup hardening plan. Stage B (config-
driven walker) and Stage C (audit + diagnostic UI) follow in
separate commits.
2026-05-29 22:00:15 +02:00
..