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, }); } } // 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); });