/** Backup → restore into a throwaway database → compare row counts and content hashes → drop the temp DB. Proves recoverability without touching production. * Requires DATABASE_URL_ADMIN (role that can CREATE DATABASE). */ import "./_env"; import postgres from "postgres"; import { runBackup, restoreInto } from "../src/lib/jobs/backup"; const admin = process.env.DATABASE_URL_ADMIN; if (!admin) { console.error("DATABASE_URL_ADMIN required"); process.exit(1); } const pass = process.env.BACKUP_PASSPHRASE!; const tmpName = `tools_restore_drill_${Date.now()}`; const a = postgres(admin, { max: 1, onnotice: () => {} }); const counts = async (url: string) => { const s = postgres(url, { max: 1, onnotice: () => {} }); const r = await s`SELECT (SELECT count(*)::int FROM tools) AS tools, (SELECT count(*)::int FROM tool_sources) AS sources, (SELECT count(*)::int FROM audit_log) AS audit, (SELECT count(*)::int FROM owner_notes) AS notes, (SELECT md5(string_agg(content_hash, ',' ORDER BY slug)) FROM tools) AS hash`; await s.end(); return r[0]; }; const t0 = Date.now(); const b = await runBackup(); console.log("backup:", b.file, b.bytes, "bytes"); await a.unsafe(`CREATE DATABASE "${tmpName}"`); const tmpUrl = admin.replace(/\/[^/?]+(\?|$)/, `/${tmpName}$1`); try { const t = postgres(tmpUrl, { max: 1, onnotice: () => {} }); await t`CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE EXTENSION IF NOT EXISTS pg_trgm; CREATE EXTENSION IF NOT EXISTS unaccent;`.simple(); try { await t`CREATE EXTENSION IF NOT EXISTS vector`; } catch {} await t.end(); await restoreInto(b.file, tmpUrl, pass); const [src, dst] = await Promise.all([counts(admin), counts(tmpUrl)]); const ok = JSON.stringify(src) === JSON.stringify(dst); console.log(JSON.stringify({ source: src, restored: dst, identical: ok, seconds: Math.round((Date.now() - t0) / 1000) }, null, 2)); if (!ok) process.exitCode = 1; } finally { await a.unsafe(`DROP DATABASE IF EXISTS "${tmpName}" WITH (FORCE)`); await a.end(); }