Files
shd.build/scripts/db-restore.mjs
jb dd84dff055
Validate and deploy production / validate (push) Successful in 23s
Validate and deploy staging / validate (push) Successful in 50s
Validate and deploy production / deploy (push) Failing after 40s
Validate and deploy staging / deploy (push) Failing after 7s
feat: add community publishing and news syndication
2026-08-01 21:42:09 -04:00

70 lines
2.1 KiB
JavaScript

import { connect } from '@tursodatabase/database';
import { readFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import process from 'node:process';
const backupArgument = process.argv[2];
if (!backupArgument) {
console.error('Usage: npm run db:restore -- <backup-file>');
process.exit(2);
}
const backupPath = resolve(backupArgument);
const databasePath = resolve(
process.env.TDR_DATABASE_PATH || './data/shd-build-development.db',
);
const backup = JSON.parse(await readFile(backupPath, 'utf8'));
if (
backup?.format !== 'shd-build-database-backup' ||
backup?.version !== 1 ||
!Array.isArray(backup.tables)
) {
throw new Error(
'The selected file is not a supported SHD.build database backup.',
);
}
const quoteIdentifier = (identifier) =>
`"${String(identifier).replaceAll('"', '""')}"`;
const database = await connect(databasePath);
// Restores are deliberately limited to empty databases. This prevents an
// accidental merge from overwriting contributor-entered records.
for (const table of backup.tables) {
const existing = await (
await database.prepare(
`SELECT COUNT(*) AS count FROM ${quoteIdentifier(table.name)}`,
)
).get();
if (Number(existing?.count) > 0) {
throw new Error(
`Restore refused: table "${table.name}" is not empty. Use a new migrated database.`,
);
}
}
let restoredRows = 0;
await database.exec('BEGIN');
try {
for (const table of backup.tables) {
for (const row of table.rows) {
const columns = Object.keys(row);
if (!columns.length) continue;
const columnSql = columns.map(quoteIdentifier).join(', ');
const placeholders = columns.map(() => '?').join(', ');
const insert = await database.prepare(
`INSERT INTO ${quoteIdentifier(table.name)} (${columnSql}) VALUES (${placeholders})`,
);
await insert.run(...columns.map((column) => row[column]));
restoredRows++;
}
}
await database.exec('COMMIT');
} catch (error) {
await database.exec('ROLLBACK');
throw error;
}
console.log(`Restored ${restoredRows} rows into ${databasePath}.`);
console.log(`Source backup: ${backupPath}`);