80 lines
3.1 KiB
JavaScript
80 lines
3.1 KiB
JavaScript
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 };
|