Fix debug upload persistence and UX; exclude prod audit log from DB restore
Uploads and fixes now bust browser cache via file-mtime keys in API responses. Debug upload shows a centered loading overlay and blocks search while uploading. Prod DB restore skips curator_audit_log. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
62096e8210
commit
f78c14f307
@@ -18,6 +18,23 @@ const { printCliResult } = require('./lib/cli-result');
|
||||
|
||||
const { Client } = pg;
|
||||
|
||||
// Prod restore keeps these tables untouched (no TRUNCATE, no INSERT from dev backup).
|
||||
const PROD_RESTORE_SKIP_TABLES = new Set(['curator_audit_log']);
|
||||
|
||||
function getInsertTableName(statement) {
|
||||
const match = statement.match(/^INSERT INTO "([^"]+)"/i)
|
||||
|| statement.match(/^INSERT INTO ([a-zA-Z_][a-zA-Z0-9_]*)/i);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
function filterInsertsForRestore(inserts, prod) {
|
||||
if (!prod) return inserts;
|
||||
return inserts.filter((statement) => {
|
||||
const table = getInsertTableName(statement);
|
||||
return !table || !PROD_RESTORE_SKIP_TABLES.has(table);
|
||||
});
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const fileIdx = argv.indexOf('--file');
|
||||
if (fileIdx === -1 || !argv[fileIdx + 1]) {
|
||||
@@ -71,14 +88,18 @@ function extractInsertStatements(content) {
|
||||
return statements;
|
||||
}
|
||||
|
||||
async function truncatePublicTables(client) {
|
||||
async function truncatePublicTables(client, { excludeTables = new Set() } = {}) {
|
||||
const tablesRes = await client.query(
|
||||
`SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
||||
ORDER BY table_name`,
|
||||
);
|
||||
const names = tablesRes.rows.map((r) => `"${r.table_name}"`).join(', ');
|
||||
const names = tablesRes.rows
|
||||
.map((r) => r.table_name)
|
||||
.filter((name) => !excludeTables.has(name))
|
||||
.map((name) => `"${name}"`)
|
||||
.join(', ');
|
||||
if (!names) return;
|
||||
await client.query(`TRUNCATE TABLE ${names} RESTART IDENTITY CASCADE`);
|
||||
}
|
||||
@@ -92,14 +113,19 @@ async function main() {
|
||||
|
||||
const config = prod ? loadProdPgConfig() : loadDevPgConfig();
|
||||
const dbName = config.database;
|
||||
const inserts = extractInsertStatements(fs.readFileSync(filePath, 'utf8'));
|
||||
const allInserts = extractInsertStatements(fs.readFileSync(filePath, 'utf8'));
|
||||
const inserts = filterInsertsForRestore(allInserts, prod);
|
||||
const skippedInserts = prod ? allInserts.length - inserts.length : 0;
|
||||
|
||||
if (inserts.length === 0) {
|
||||
throw new Error('No INSERT statements found in backup file');
|
||||
}
|
||||
|
||||
const skipNote = prod && PROD_RESTORE_SKIP_TABLES.size > 0
|
||||
? `\nExcluded from prod restore: ${[...PROD_RESTORE_SKIP_TABLES].join(', ')}`
|
||||
: '';
|
||||
const prompt = prod
|
||||
? `RESTORE ${inserts.length} rows into PRODUCTION "${dbName}" from:\n ${filePath}\nThis TRUNCATES all public tables first.`
|
||||
? `RESTORE ${inserts.length} rows into PRODUCTION "${dbName}" from:\n ${filePath}\nThis TRUNCATES public tables first (except excluded tables).${skipNote}`
|
||||
: `Restore ${inserts.length} rows into "${dbName}" from:\n ${filePath}\nThis TRUNCATES all public tables first.`;
|
||||
|
||||
if (prod) {
|
||||
@@ -113,7 +139,12 @@ async function main() {
|
||||
await client.connect();
|
||||
|
||||
console.log(`Truncating public tables in "${dbName}"...`);
|
||||
await truncatePublicTables(client);
|
||||
if (prod && PROD_RESTORE_SKIP_TABLES.size > 0) {
|
||||
console.log(` skipping: ${[...PROD_RESTORE_SKIP_TABLES].join(', ')}`);
|
||||
}
|
||||
await truncatePublicTables(client, {
|
||||
excludeTables: prod ? PROD_RESTORE_SKIP_TABLES : new Set(),
|
||||
});
|
||||
|
||||
// Fast path: disable FK/trigger checks for a single-pass load. Requires
|
||||
// permission to set session_replication_role (superuser, or on PG 15+ a
|
||||
@@ -186,12 +217,20 @@ async function main() {
|
||||
|
||||
await client.end();
|
||||
console.log(`Restore complete: ${restored} statements into "${dbName}".`);
|
||||
if (skippedInserts > 0) {
|
||||
console.log(`Skipped ${skippedInserts} INSERT statement(s) for excluded prod tables.`);
|
||||
}
|
||||
|
||||
printCliResult({
|
||||
script: 'restore-db-data',
|
||||
ok: true,
|
||||
summary: `Restore complete into "${dbName}".`,
|
||||
details: [`Statements restored: ${restored}`],
|
||||
details: [
|
||||
`Statements restored: ${restored}`,
|
||||
...(skippedInserts > 0
|
||||
? [`Skipped (excluded tables): ${skippedInserts}`]
|
||||
: []),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user