94 lines
2.6 KiB
JavaScript
94 lines
2.6 KiB
JavaScript
const { Pool } = require('pg');
|
|
|
|
const parseBoolean = (value, fallback = false) => {
|
|
if (typeof value !== 'string') return fallback;
|
|
const normalized = value.trim().toLowerCase();
|
|
if (['1', 'true', 'yes', 'on'].includes(normalized)) return true;
|
|
if (['0', 'false', 'no', 'off'].includes(normalized)) return false;
|
|
return fallback;
|
|
};
|
|
|
|
const buildDatabaseUrlFromParts = () => {
|
|
const host = (process.env.POSTGRES_HOST || 'postgres').trim();
|
|
const port = Number(process.env.POSTGRES_PORT || 5432);
|
|
const database = (process.env.POSTGRES_DB || 'greenlns').trim();
|
|
const user = (process.env.POSTGRES_USER || 'greenlns').trim();
|
|
const password = process.env.POSTGRES_PASSWORD;
|
|
if (!password) {
|
|
return '';
|
|
}
|
|
|
|
return `postgresql://${encodeURIComponent(user)}:${encodeURIComponent(password)}@${host}:${port}/${encodeURIComponent(database)}`;
|
|
};
|
|
|
|
const getDefaultDbPath = () => {
|
|
return (process.env.DATABASE_URL || buildDatabaseUrlFromParts()).trim();
|
|
};
|
|
|
|
const getPoolConfig = () => {
|
|
const connectionString = getDefaultDbPath();
|
|
if (!connectionString) {
|
|
throw new Error('DATABASE_URL or POSTGRES_* environment variables are required.');
|
|
}
|
|
|
|
const sslEnabled = parseBoolean(process.env.DATABASE_SSL, false);
|
|
return {
|
|
connectionString,
|
|
max: Number(process.env.PGPOOL_MAX || 10),
|
|
ssl: sslEnabled ? { rejectUnauthorized: false } : false,
|
|
};
|
|
};
|
|
|
|
const translateSql = (sql) => {
|
|
if (typeof sql !== 'string') return sql;
|
|
|
|
let placeholderIndex = 0;
|
|
return sql
|
|
.replace(/\?/g, () => {
|
|
placeholderIndex += 1;
|
|
return `$${placeholderIndex}`;
|
|
})
|
|
.replace(/BEGIN\s+IMMEDIATE\s+TRANSACTION/gi, 'BEGIN')
|
|
.replace(/datetime\('now'\)/gi, 'CURRENT_TIMESTAMP')
|
|
.replace(/\s+COLLATE\s+NOCASE/gi, '');
|
|
};
|
|
|
|
const openDatabase = async () => {
|
|
const pool = new Pool(getPoolConfig());
|
|
await pool.query('SELECT 1');
|
|
return pool;
|
|
};
|
|
|
|
const closeDatabase = async (db) => {
|
|
if (!db || typeof db.end !== 'function') return;
|
|
await db.end();
|
|
};
|
|
|
|
const run = async (db, sql, params = []) => {
|
|
const result = await db.query(translateSql(sql), params);
|
|
return {
|
|
lastId: result.rows?.[0]?.id ?? null,
|
|
changes: result.rowCount || 0,
|
|
rows: result.rows || [],
|
|
};
|
|
};
|
|
|
|
const get = async (db, sql, params = []) => {
|
|
const result = await db.query(translateSql(sql), params);
|
|
return result.rows[0] || null;
|
|
};
|
|
|
|
const all = async (db, sql, params = []) => {
|
|
const result = await db.query(translateSql(sql), params);
|
|
return result.rows || [];
|
|
};
|
|
|
|
module.exports = {
|
|
all,
|
|
closeDatabase,
|
|
get,
|
|
getDefaultDbPath,
|
|
openDatabase,
|
|
run,
|
|
};
|