import pg from 'pg'; const { Pool, types } = pg; function parseNumericDatabaseValue(value) { if (value == null) return value; const parsed = Number(value); return Number.isFinite(parsed) ? parsed : value; } // Mobile/frontend routes expect numeric JSON values for database aggregates. types.setTypeParser(types.builtins.INT8, parseNumericDatabaseValue); types.setTypeParser(types.builtins.NUMERIC, parseNumericDatabaseValue); let pool; function parseIntOrDefault(value, fallback) { const parsed = Number.parseInt(`${value || fallback}`, 10); return Number.isFinite(parsed) ? parsed : fallback; } function resolveDatabasePoolConfig() { if (process.env.DATABASE_URL) { return { connectionString: process.env.DATABASE_URL, max: parseIntOrDefault(process.env.DB_POOL_MAX, 10), idleTimeoutMillis: parseIntOrDefault(process.env.DB_IDLE_TIMEOUT_MS, 30000), }; } const user = process.env.DB_USER; const password = process.env.DB_PASSWORD; const database = process.env.DB_NAME; const host = process.env.DB_HOST || ( process.env.INSTANCE_CONNECTION_NAME ? `/cloudsql/${process.env.INSTANCE_CONNECTION_NAME}` : '' ); if (!user || password == null || !database || !host) { return null; } return { host, port: parseIntOrDefault(process.env.DB_PORT, 5432), user, password, database, max: parseIntOrDefault(process.env.DB_POOL_MAX, 10), idleTimeoutMillis: parseIntOrDefault(process.env.DB_IDLE_TIMEOUT_MS, 30000), }; } export function isDatabaseConfigured() { return Boolean(resolveDatabasePoolConfig()); } function getPool() { if (!pool) { const resolved = resolveDatabasePoolConfig(); if (!resolved) { throw new Error('Database connection settings are required'); } pool = new Pool(resolved); } return pool; } export async function query(text, params = []) { return getPool().query(text, params); } export async function checkDatabaseHealth() { const result = await query('SELECT 1 AS ok'); return result.rows[0]?.ok === 1; } export async function closePool() { if (pool) { await pool.end(); pool = null; } }