/** * getpgdata — Node.js приложение для доступа к PostgreSQL (managed k8s, internal-only). * * Точка входа для Nubes NodeJS managed service (аналогично ipwhitelist-app). * Подключение к БД — через переменные окружения DB_HOST/DB_PORT/DB_NAME/DB_USER/DB_PASS/DB_SSLMODE. * * Функции: * GET / — состояние подключения + список таблиц * GET /table?schema=&name=&page= — содержимое таблицы с пагинацией * POST /query — SQL-консоль (read-only, только SELECT) * GET /healthz — liveness-проба для k8s */ const path = require('path'); const express = require('express'); const { Pool } = require('pg'); const app = express(); app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); const APP_ENV = process.env.APP_ENV || 'production'; const QUERY_MAX_ROWS = parseInt(process.env.QUERY_MAX_ROWS || '200', 10); // --------------------------------------------------------------------------- // Конфигурация подключения к PostgreSQL // --------------------------------------------------------------------------- function dbConfig() { return { host: process.env.DB_HOST || 'postgresqlk8s-master.60bdf3e3-5087-41ff-b760-fe6ea544a80e.svc.cluster.local', port: parseInt(process.env.DB_PORT || '5432', 10), database: process.env.DB_NAME || 'ipwhitelist', user: process.env.DB_USER || 'super', password: process.env.DB_PASS || '', ssl: process.env.DB_SSLMODE === 'require' ? { rejectUnauthorized: false } : false, connectionTimeoutMillis: parseInt(process.env.DB_CONNECT_TIMEOUT || '5000', 10), }; } // Пул создаётся при старте; если БД недоступна — приложение не падает, // а отдаёт ошибку на страницах (важно для health-проб k8s). const pool = new Pool(dbConfig()); // --------------------------------------------------------------------------- // Middleware // --------------------------------------------------------------------------- app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use('/public', express.static(path.join(__dirname, 'public'))); // --------------------------------------------------------------------------- // Маршруты // --------------------------------------------------------------------------- // Liveness-проба app.get('/healthz', async (req, res) => { try { await pool.query('SELECT 1'); res.status(200).json({ status: 'ok', db: 'connected' }); } catch (e) { res.status(503).json({ status: 'degraded', db: 'unreachable', error: e.message }); } }); // Главная: минимальный вывод информации на экран. // Не зависит от EJS-шаблонов — всегда отдаёт HTML, даже если БД недоступна. app.get('/', async (req, res) => { const cfg = dbConfig(); let dbStatus = 'нет данных'; let tablesHtml = ''; try { const r = await pool.query('SELECT version() AS v'); dbStatus = 'подключено: ' + (r.rows[0] && r.rows[0].v ? r.rows[0].v : 'ok'); } catch (e) { dbStatus = 'недоступна: ' + (e && e.message ? e.message : String(e)); } try { const t = await pool.query( `SELECT schemaname, tablename FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema') ORDER BY schemaname, tablename` ); if (t.rows && t.rows.length) { tablesHtml = t.rows .map((r) => `
Node.js ${esc(process.version)} · env=${esc(APP_ENV)} · PID=${esc(String(process.pid))}
| Host | ${esc(cfg.host)} |
|---|---|
| Port | ${esc(String(cfg.port))} |
| Database | ${esc(cfg.database)} |
| User | ${esc(cfg.user)} |
| Статус | ${esc(dbStatus)} |
| Схема | Таблица |
|---|