feat: add community publishing and news syndication
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

This commit is contained in:
jb
2026-08-01 21:42:09 -04:00
parent b47e2588e9
commit dd84dff055
288 changed files with 83254 additions and 38351 deletions
+41 -30
View File
@@ -5,53 +5,64 @@ import process from 'node:process';
const backupArgument = process.argv[2];
if (!backupArgument) {
console.error('Usage: npm run db:restore -- <backup-file>');
process.exit(2);
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 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.');
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 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.`
);
}
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');
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;
await database.exec('ROLLBACK');
throw error;
}
console.log(`Restored ${restoredRows} rows into ${databasePath}.`);