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>
82 lines
2.4 KiB
JavaScript
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);
|
|
});
|