const express = require('express'); const { Pool } = require('pg'); const dotenv = require('dotenv'); const cors = require('cors'); // Load environment variables dotenv.config(); const app = express(); const PORT = process.env.PORT || 3000; // Middleware app.use(cors()); app.use(express.json({ limit: '10mb' })); // PostgreSQL connection pool function handleHeaders(headers) { const alias = (headers['db-alias'] || 'default').toUpperCase().replace(/-/g, '_'); let pool = { host: process.env[`DB_${alias}_HOST`], port: process.env[`DB_${alias}_PORT`], database: process.env[`DB_${alias}_DATABASE`], user: process.env[`DB_${alias}_USER`], password: process.env[`DB_${alias}_PASSWORD`], max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }; const allUndefined = pool.host === undefined && pool.port === undefined && pool.database === undefined && pool.user === undefined && pool.password === undefined; const someUndefined = pool.host === '' || pool.port === '' || pool.database === '' || pool.user === '' || pool.password === ''; const someDefined = pool.host != undefined || pool.port != undefined || pool.database != undefined || pool.user != undefined || pool.password != undefined; if (allUndefined) { throw new Error('Invalid db alias'); } else if (someUndefined && someDefined) { console.log(pool); if (pool.host === '') { throw new Error('Host is not configured for the specified db alias'); } if (pool.port === '') { throw new Error('Port is not configured for the specified db alias'); } if (pool.database === '') { throw new Error('Database is not configured for the specified db alias'); } if (pool.user === '') { throw new Error('User is not configured for the specified db alias'); } if (pool.password === '') { throw new Error('Password is not configured for the specified db alias'); } } else { return new Pool({ host: process.env[`DB_${alias}_HOST`], port: process.env[`DB_${alias}_PORT`], database: process.env[`DB_${alias}_DATABASE`], user: process.env[`DB_${alias}_USER`], password: process.env[`DB_${alias}_PASSWORD`], max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, ssl: { rejectUnauthorized: false }, }); } } // Health check endpoint app.get('/api/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString(), node_version: process.version, }); }); // SELECT query endpoint app.post('/api/postgres/select', async (req, res) => { const { query, bindings = [] } = req.body; if (!query) { return res.status(422).json({ success: false, error: 'Query is required', }); } let pool; try { pool = handleHeaders(req.headers); const result = await pool.query(query, bindings); res.json({ success: true, data: result.rows, count: result.rowCount, }); } catch (error) { console.error('SELECT Error:', error); res.status(400).json({ success: false, error: error.message, }); } finally { if (pool) await pool.end(); } }); // EXECUTE query endpoint (INSERT, UPDATE, DELETE) app.post('/api/postgres/execute', async (req, res) => { const { query, bindings = [] } = req.body; if (!query) { return res.status(422).json({ success: false, error: 'Query is required', }); } let pool; try { pool = handleHeaders(req.headers); const result = await pool.query(query, bindings); res.json({ success: true, affected_rows: result.rowCount, }); } catch (error) { console.error('EXECUTE Error:', error); res.status(400).json({ success: false, error: error.message, }); } finally { if (pool) await pool.end(); } }); // TRANSACTION endpoint app.post('/api/postgres/transaction', async (req, res) => { const { queries } = req.body; if (!queries || !Array.isArray(queries)) { return res.status(422).json({ success: false, error: 'Queries array is required', }); } let pool; let client; try { pool = handleHeaders(req.headers); client = await pool.connect(); await client.query('BEGIN'); const results = []; for (const item of queries) { const result = await client.query(item.query, item.bindings || []); results.push({ rowCount: result.rowCount, success: true, }); } await client.query('COMMIT'); res.json({ success: true, results: results, }); } catch (error) { if (client) await client.query('ROLLBACK'); console.error('TRANSACTION Error:', error); res.status(400).json({ success: false, error: error.message, }); } finally { if (client) client.release(); if (pool) await pool.end(); } }); // INSERT simplified endpoint app.post('/api/postgres/insert', async (req, res) => { const { table, data } = req.body; if (!table || !data) { return res.status(422).json({ success: false, error: 'Table and data are required', }); } let pool; try { pool = handleHeaders(req.headers); const columns = Object.keys(data); const values = Object.values(data); const placeholders = values.map((_, i) => `$${i + 1}`).join(', '); const query = ` INSERT INTO "${table}" (${columns.map((c) => `"${c}"`).join(', ')}) VALUES (${placeholders}) RETURNING * `; const result = await pool.query(query, values); res.json({ success: true, data: result.rows[0], }); } catch (error) { console.error('INSERT Error:', error); res.status(400).json({ success: false, error: error.message, }); } finally { if (pool) await pool.end(); } }); // UPDATE simplified endpoint app.post('/api/postgres/update', async (req, res) => { const { table, data, where } = req.body; if (!table || !data || !where) { return res.status(422).json({ success: false, error: 'Table, data and where are required', }); } let pool; try { pool = handleHeaders(req.headers); const setColumns = Object.keys(data); const setValues = Object.values(data); const whereColumns = Object.keys(where); const whereValues = Object.values(where); const setClause = setColumns.map((col, i) => `"${col}" = $${i + 1}`).join(', '); const whereClause = whereColumns.map((col, i) => `"${col}" = $${setValues.length + i + 1}`).join(' AND '); const query = ` UPDATE "${table}" SET ${setClause} WHERE ${whereClause} `; const result = await pool.query(query, [...setValues, ...whereValues]); res.json({ success: true, affected_rows: result.rowCount, }); } catch (error) { console.error('UPDATE Error:', error); res.status(400).json({ success: false, error: error.message, }); } finally { if (pool) await pool.end(); } }); // DELETE simplified endpoint app.post('/api/postgres/delete', async (req, res) => { const { table, where } = req.body; if (!table || !where) { return res.status(422).json({ success: false, error: 'Table and where are required', }); } let pool; try { pool = handleHeaders(req.headers); const whereColumns = Object.keys(where); const whereValues = Object.values(where); const whereClause = whereColumns.map((col, i) => `"${col}" = $${i + 1}`).join(' AND '); const query = `DELETE FROM "${table}" WHERE ${whereClause}`; const result = await pool.query(query, whereValues); res.json({ success: true, affected_rows: result.rowCount, }); } catch (error) { console.error('DELETE Error:', error); res.status(400).json({ success: false, error: error.message, }); } finally { if (pool) await pool.end(); } }); // Start server app.listen(PORT, () => { console.log(`✓ Postgres API running on http://localhost:${PORT}`); console.log(`✓ Health check: http://localhost:${PORT}/api/health`); }); // Handle shutdown gracefully process.on('SIGINT', async () => { console.log('\nShutting down gracefully...'); process.exit(0); });