feat(backup): full DR bundle export + admin-configurable offsite destinations
Backend-agnostic disaster-recovery backup engine that runs on the current storage backend (no storage cutover required): - Full-bundle export: db.dump (pg_dump custom) + every storage blob + manifest.json with per-object SHA-256, streamed as a tar. Entry points: admin UI download, GET /api/v1/admin/backup/export, scripts/create-full-backup.ts. - Admin-configurable push destinations (backup_destinations table, migration 0091): SFTP/SSH, S3-compatible (reuses the minio client), and mounted path/NAS behind one transport interface (test/push/prune). Secrets AES-GCM at rest; API returns only *IsSet markers. - Opt-in per-destination AES-256 bundle encryption (scrypt KDF, streamed) + scripts/decrypt-backup.ts for restore. - Wired the previously-dead database-backup cron to runScheduledBackupPush (push to enabled destinations, prune to retention, alert super-admins on failure). Tests: 1608 unit/integration pass; tsc + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -136,7 +136,7 @@ async function markRowMigrated(
|
||||
`);
|
||||
}
|
||||
|
||||
interface RowRef {
|
||||
export interface RowRef {
|
||||
tableName: string;
|
||||
pk: string;
|
||||
key: string;
|
||||
@@ -164,6 +164,22 @@ async function listKeysFor(tbl: StorageKeyTable): Promise<RowRef[]> {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inventory every blob reference across all blob-bearing tables. Used by the
|
||||
* full-backup exporter (Phase 4a) to enumerate what to bundle. `excludeTables`
|
||||
* lets the exporter drop `backup_jobs` so a full export doesn't recursively
|
||||
* include prior backup artefacts.
|
||||
*/
|
||||
export async function collectStorageRefs(opts?: { excludeTables?: string[] }): Promise<RowRef[]> {
|
||||
const exclude = new Set(opts?.excludeTables ?? []);
|
||||
const all: RowRef[] = [];
|
||||
for (const tbl of TABLES_WITH_STORAGE_KEYS) {
|
||||
if (exclude.has(tbl.table)) continue;
|
||||
all.push(...(await listKeysFor(tbl)));
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
// ─── streaming + sha256 verify ──────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user