const fs = require('node:fs') const path = require('node:path') const { PrismaClient } = require('@prisma/client') const { loadEnv } = require('./lib/admin-account') async function verify(file) { const db = new PrismaClient({ datasources: { db: { url: 'file:' + file.split(path.sep).join('/') } } }) try { const result = await db.$queryRawUnsafe('PRAGMA integrity_check') if (result.length !== 1 || Object.values(result[0])[0] !== 'ok') throw new Error('SQLite integrity check failed') const counts = {} for (const model of ['user', 'privacyPolicy', 'policyDefaults', 'sitePages']) counts[model] = await db[model].count() return counts } finally { await db.$disconnect() } } async function main() { loadEnv() const [action, input, output] = process.argv.slice(2) if (!input || !['backup', 'verify', 'restore'].includes(action)) throw new Error('Usage: node scripts/database-backup.js backup FILE | verify FILE | restore BACKUP NEW_FILE') const file = path.resolve(input) if (action === 'verify') { if (!fs.existsSync(file)) throw new Error('Backup does not exist') console.log(JSON.stringify(await verify(file))) return } const target = action === 'restore' ? (output ? path.resolve(output) : null) : file if (!target) throw new Error('Restore requires a new destination file; existing files are never overwritten') if (fs.existsSync(target)) throw new Error('Destination already exists; refusing to overwrite') fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 }) if (action === 'backup') { const db = new PrismaClient() try { await db.$executeRawUnsafe('VACUUM INTO ?', target) } finally { await db.$disconnect() } } else { if (!fs.existsSync(file)) throw new Error('Backup does not exist') await verify(file) fs.copyFileSync(file, target, fs.constants.COPYFILE_EXCL) } fs.chmodSync(target, 0o600) console.log(JSON.stringify({ action, file: target, counts: await verify(target) })) } main().catch(error => { console.error(error.message); process.exitCode = 1 })