2 * @file PostgreSQL Database Connection Pool
5 * Centralized PostgreSQL connection pool using node-postgres (pg). Provides database
6 * connectivity for all backend services with automatic connection management, pooling,
9 * **Configuration Priority:**
10 * 1. Root `.env` file - Shared configuration for DevOps scripts
11 * 2. Backend `.env` file - Overrides for backend-specific settings
13 * **Connection Modes:**
14 * - **Managed Database** (DigitalOcean, Heroku): Uses `DATABASE_URL` connection string
15 * - **Self-Hosted**: Uses individual environment variables (DB_HOST, DB_PORT, etc.)
18 * - Automatically enables SSL for DATABASE_URL or if DB_SSL=true
19 * - Uses `rejectUnauthorized: false` for managed databases with self-signed certs
20 * - Strips `sslmode=require` from URL to prevent pg conflicts
22 * **Environment Variables:**
23 * - `DATABASE_URL` - PostgreSQL connection string (overrides individual vars)
24 * - `DB_HOST` - Database host (default: DigitalOcean managed DB)
25 * - `DB_PORT` - Database port (default: 25060)
26 * - `DB_USER` - Database username (default: doadmin)
27 * - `DB_PASSWORD` - Database password (strips quotes)
28 * - `DB_NAME` - Database name (default: defaultdb)
29 * - `DB_SSL` - Enable SSL (default: true)
31 * **Pool Configuration:**
32 * - Uses pg.Pool defaults (10 max connections, 30s idle timeout)
33 * - Automatic connection reuse and recycling
34 * - Event handlers for connection lifecycle
37 * - Logs unexpected errors on idle clients
38 * - Exits process on connection pool errors (requires restart)
39 * - Prevents silent connection failures
40 * @exports {Pool} PostgreSQL connection pool instance
42 * // Query with parameterized values (SQL injection safe)
43 * const pool = require('./services/db');
44 * const result = await pool.query(
45 * 'SELECT * FROM customers WHERE tenant_id = $1',
48 * console.log(result.rows);
50 * // Transaction with client checkout
51 * const pool = require('./services/db');
52 * const client = await pool.connect();
54 * await client.query('BEGIN');
55 * await client.query('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [100, 1]);
56 * await client.query('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [100, 2]);
57 * await client.query('COMMIT');
59 * await client.query('ROLLBACK');
65 * // Bulk insert with transaction
66 * const pool = require('./services/db');
67 * const client = await pool.connect();
69 * await client.query('BEGIN');
70 * for (const row of data) {
71 * await client.query('INSERT INTO metrics (...) VALUES (...)', row);
73 * await client.query('COMMIT');
77 * @exports pool - PostgreSQL connection pool instance
78 * @see {@link https://node-postgres.com/} node-postgres documentation
79 * @see {@link https://www.postgresql.org/docs/} PostgreSQL documentation
80 * @see {@link module:middleware/tenant} for tenant-scoped queries
81 * @requires pg - PostgreSQL client library
82 * @requires dotenv - Environment variable loader
85const { Pool } = require('pg');
86const path = require('path');
88// Load env in this order:
89// 1) Project root .env (used by devops scripts and shared config)
90// 2) backend/.env overrides root values for backend-specific settings
91require('dotenv').config({ path: path.resolve(__dirname, '../../.env') });
92require('dotenv').config({ path: path.resolve(__dirname, '../.env'), override: true });
94// Support both individual vars and DATABASE_URL for managed DB
95const dbConfig = process.env.DATABASE_URL
97 // Strip sslmode=require from URL to ensure pg respects explicit ssl options
98 const cleanedUrl = process.env.DATABASE_URL.replace(/([?&])sslmode=require(&|$)/, (m, p1, p2) => (p2 === '&' ? p1 : ''));
100 connectionString: cleanedUrl,
101 ssl: process.env.DB_SSL === 'true' || process.env.DATABASE_URL.includes('sslmode=require')
102 ? { rejectUnauthorized: false }
107 host: process.env.DB_HOST || 'rmm-psa-db-do-user-28531160-0.i.db.ondigitalocean.com',
108 port: parseInt(process.env.DB_PORT || '25060', 10),
109 user: process.env.DB_USER || 'doadmin',
110 password: (process.env.DB_PASSWORD || 'AVNS_J8RJAmsEwsHFG52_-F2').replace(/^"|"$/g, ''),
111 database: process.env.DB_NAME || 'defaultdb',
112 ssl: (process.env.DB_SSL === 'false' ? false : true)
113 ? { rejectUnauthorized: false }
117const pool = new Pool(dbConfig);
119// Test connection and log status
121pool.on('connect', () => {
123 console.log('[DB] Connected to PostgreSQL');
128pool.on('error', (err) => {
129 console.error('[DB] Unexpected error on idle client', err);
133module.exports = pool;