Files
Art-gallery/scripts/sync-prod-to-dev.js
T
Danila KhodjaefandCursor 2edf577faf Add dev/prod environments with TrueNAS Docker production deploy.
Split PostgreSQL into gallery_dev and gallery_prod, add Docker/Gitea deploy tooling,
SMB image sync, pgAdmin split script, dev:web on Keenetic :5173, and operator docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-04 15:15:19 +03:00

82 lines
2.4 KiB
JavaScript

/**
* Clone gallery_prod → gallery_dev (PostgreSQL TEMPLATE).
* Requires superuser or CREATEDB on the Postgres host.
*/
const path = require('path');
const { execFileSync } = require('child_process');
const pg = require('pg');
const {
DEV_DB_NAME,
PROD_DB_NAME,
loadProdPgConfig,
rootDir,
} = require('./db-env');
const { Client } = pg;
function quoteIdent(ident) {
return `"${ident.replace(/"/g, '""')}"`;
}
async function main() {
const prodConfig = loadProdPgConfig();
const { host, port, user, password } = prodConfig;
const admin = new Client({ host, port, user, password, database: 'postgres' });
await admin.connect();
console.log(`Terminating connections to "${DEV_DB_NAME}"...`);
await admin.query(
`SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = $1 AND pid <> pg_backend_pid()`,
[DEV_DB_NAME],
);
const exists = await admin.query(
'SELECT 1 FROM pg_database WHERE datname = $1',
[DEV_DB_NAME],
);
if (exists.rowCount > 0) {
console.log(`Dropping "${DEV_DB_NAME}"...`);
await admin.query(`DROP DATABASE ${quoteIdent(DEV_DB_NAME)}`);
}
console.log(`Terminating connections to "${PROD_DB_NAME}"...`);
await admin.query(
`SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = $1 AND pid <> pg_backend_pid()`,
[PROD_DB_NAME],
);
console.log(`Cloning "${PROD_DB_NAME}" → "${DEV_DB_NAME}" (TEMPLATE)...`);
await admin.query(
`CREATE DATABASE ${quoteIdent(DEV_DB_NAME)} WITH TEMPLATE ${quoteIdent(PROD_DB_NAME)} OWNER ${quoteIdent(user)}`,
);
await admin.end();
const verify = new Client({ host, port, user, password, database: DEV_DB_NAME });
await verify.connect();
const tables = await verify.query(
`SELECT count(*)::int AS n FROM information_schema.tables
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'`,
);
const paintings = await verify.query('SELECT count(*)::int AS n FROM paintings');
await verify.end();
console.log(`Done. "${DEV_DB_NAME}" is a copy of "${PROD_DB_NAME}" (${tables.rows[0].n} tables, ${paintings.rows[0].n} paintings).`);
console.log('Syncing image files from production...');
execFileSync('powershell', ['-NoProfile', '-File', path.join(rootDir, 'infra', 'scripts', 'sync-images-from-prod.ps1'), '-SkipConfirm'], {
cwd: rootDir,
stdio: 'inherit',
});
}
main().catch((error) => {
console.error(error);
process.exit(1);
});