const { Pool } = require('pg'); require('dotenv').config(); const pool = new Pool({ host: process.env.DB_HOST || 'postgresqlk8s-master', port: process.env.DB_PORT || 5432, 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, max: 10, idleTimeoutMillis: 30000, }); pool.on('error', (err) => { console.error('DB pool error:', err.message); }); async function checkConnection() { const { rows } = await pool.query('SELECT 1 AS ok'); return rows[0].ok === 1; } async function ensureSchema() { await pool.query(` CREATE TABLE IF NOT EXISTS companies ( id SERIAL PRIMARY KEY, client_id VARCHAR(64) UNIQUE NOT NULL, name VARCHAR(255), custom_limit INTEGER DEFAULT NULL CHECK (custom_limit IS NULL OR custom_limit >= 0), owner_email TEXT DEFAULT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS whitelist_entries ( id SERIAL PRIMARY KEY, company_id INTEGER NOT NULL REFERENCES companies(id) ON DELETE RESTRICT, value_cidr VARCHAR(18) NOT NULL CHECK (value_cidr LIKE '%/%'), comment VARCHAR(255), created_by VARCHAR(255) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_by VARCHAR(255), updated_at TIMESTAMPTZ, deleted_by VARCHAR(255), deleted_at TIMESTAMPTZ ); CREATE UNIQUE INDEX IF NOT EXISTS uq_entries_active_cidr ON whitelist_entries(company_id, value_cidr) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_entries_active ON whitelist_entries(company_id, created_at DESC) WHERE deleted_at IS NULL; CREATE TABLE IF NOT EXISTS audit_log ( id SERIAL PRIMARY KEY, user_email VARCHAR(255) NOT NULL, company_id INTEGER NOT NULL, action VARCHAR(32) NOT NULL CHECK (action IN ('CREATE','UPDATE','DELETE')), old_value TEXT, new_value TEXT, entry_id INTEGER, impersonated_by VARCHAR(255) DEFAULT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_audit_company_time ON audit_log(company_id, created_at DESC); `); // Замена constraint на managed-совместимый (без regex, который ломается от standard_conforming_strings) await pool.query(` ALTER TABLE whitelist_entries DROP CONSTRAINT IF EXISTS whitelist_entries_value_cidr_check; ALTER TABLE whitelist_entries ADD CONSTRAINT whitelist_entries_value_cidr_check CHECK (value_cidr LIKE '%/%'); `).catch(() => {}); // игнор если нет прав на ALTER // Миграция: колонка impersonated_by для двойного аудита await pool.query(` ALTER TABLE audit_log ADD COLUMN IF NOT EXISTS impersonated_by VARCHAR(255) DEFAULT NULL `).catch(() => {}); console.log('[db] Schema ensured'); } module.exports = { pool, checkConnection, ensureSchema };