Files
Art-gallery/scripts/db-env.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

104 lines
2.9 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const readline = require('readline');
const rootDir = path.resolve(__dirname, '..');
const PROD_DB_SUFFIX = '_prod';
const DEV_DB_NAME = 'gallery_dev';
const PROD_DB_NAME = 'gallery_prod';
const LEGACY_DB_NAMES = ['Gallery', 'gallery'];
function loadEnvFile(filePath) {
if (!fs.existsSync(filePath)) return;
const content = fs.readFileSync(filePath, 'utf8');
for (const line of content.split(/\r?\n/)) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) continue;
const eq = trimmed.indexOf('=');
if (eq === -1) continue;
const key = trimmed.slice(0, eq).trim();
let value = trimmed.slice(eq + 1).trim();
if (
(value.startsWith('"') && value.endsWith('"'))
|| (value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
if (process.env[key] === undefined) process.env[key] = value;
}
}
function pgConfigFromEnv() {
const host = process.env.DB_HOST || '192.168.10.122';
const port = Number(process.env.DB_PORT || 5432);
const user = process.env.DB_USER;
const password = process.env.DB_PASSWORD;
const database = process.env.DB_NAME;
if (!user || !password || !database) {
throw new Error('DB_USER, DB_PASSWORD, and DB_NAME are required');
}
return { host, port, user, password, database };
}
function assertProdDatabase(dbName) {
if (!dbName.endsWith(PROD_DB_SUFFIX) && dbName !== PROD_DB_NAME) {
throw new Error(
`Refusing prod operation on database "${dbName}". `
+ `Target must be "${PROD_DB_NAME}" or end with "${PROD_DB_SUFFIX}".`,
);
}
return dbName;
}
function assertDevDatabase(dbName) {
if (dbName === PROD_DB_NAME || dbName.endsWith(PROD_DB_SUFFIX)) {
throw new Error(
`Refusing dev operation on production database "${dbName}". `
+ `Use "${DEV_DB_NAME}" for development.`,
);
}
return dbName;
}
function loadDevPgConfig() {
loadEnvFile(path.join(rootDir, '.env'));
const config = pgConfigFromEnv();
assertDevDatabase(config.database);
return config;
}
function loadProdPgConfig() {
loadEnvFile(path.join(rootDir, 'infra', 'docker', '.env.prod'));
const config = pgConfigFromEnv();
assertProdDatabase(config.database);
return config;
}
async function confirmProdAction(message) {
if (process.env.CONFIRM_PROD === '1') return;
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const answer = await new Promise((resolve) => {
rl.question(`${message}\nType "yes" to continue: `, resolve);
});
rl.close();
if (answer.trim().toLowerCase() !== 'yes') {
throw new Error('Aborted.');
}
}
module.exports = {
PROD_DB_SUFFIX,
DEV_DB_NAME,
PROD_DB_NAME,
LEGACY_DB_NAMES,
rootDir,
loadEnvFile,
pgConfigFromEnv,
assertProdDatabase,
assertDevDatabase,
loadDevPgConfig,
loadProdPgConfig,
confirmProdAction,
};