Files
ipwhitelist-app/v2/src/db/schema.js
T

90 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ═══════════════════════════════════════════════════════════════════════════════
// V2 — инициализация схемы БД
//
// ЭТО: CREATE TABLE IF NOT EXISTS — безопасный повторный запуск.
// ЗАЧЕМ:
// 1. Приложение само создаёт таблицы при старте.
// 2. Безопасно для production: IF NOT EXISTS.
//
// ТАБЛИЦЫ:
// companies — client_id (W-номер), name, custom_limit
// whitelist_entries — CIDR, комментарий, кто создал/изменил/удалил
// audit_log — все действия: CREATE/UPDATE/DELETE/RESTORE, с impersonated_by
// ═══════════════════════════════════════════════════════════════════════════════
async function ensureSchema(pool) {
await pool.query(`
-- Компании: W-номер уникален, upsert через ON CONFLICT
CREATE TABLE IF NOT EXISTS companies (
id SERIAL PRIMARY KEY,
client_id VARCHAR(64) NOT NULL UNIQUE,
name VARCHAR(255),
custom_limit INTEGER DEFAULT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ
);
-- Записи: CIDR + метаданные, soft delete
CREATE TABLE IF NOT EXISTS whitelist_entries (
id SERIAL PRIMARY KEY,
company_id INTEGER NOT NULL,
value_cidr VARCHAR(64) NOT NULL,
comment TEXT,
created_by VARCHAR(255),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_by VARCHAR(255),
updated_at TIMESTAMPTZ,
deleted_by VARCHAR(255),
deleted_at TIMESTAMPTZ
);
-- Защита от дублей: уникальный CIDR в компании среди активных записей
CREATE UNIQUE INDEX IF NOT EXISTS idx_whitelist_entries_active_cidr
ON whitelist_entries(company_id, value_cidr)
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','RESTORE')),
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_log_company_time
ON audit_log(company_id, created_at DESC);
`);
// Миграция: impersonated_by (если колонки ещё нет)
await pool.query(`
ALTER TABLE audit_log ADD COLUMN IF NOT EXISTS impersonated_by VARCHAR(255) DEFAULT NULL
`).catch(() => {});
// Миграция: добавить RESTORE в CHECK (если ещё нет)
await pool.query(`
DO $$
DECLARE
cn text;
BEGIN
SELECT con.conname INTO cn
FROM pg_constraint con
JOIN pg_class rel ON rel.oid = con.conrelid
WHERE rel.relname = 'audit_log' AND con.contype = 'c';
IF cn IS NOT NULL THEN
EXECUTE 'ALTER TABLE audit_log DROP CONSTRAINT ' || cn;
END IF;
EXECUTE 'ALTER TABLE audit_log ADD CONSTRAINT audit_log_action_check CHECK (action IN (''CREATE'',''UPDATE'',''DELETE'',''RESTORE''))';
END;
$$;
`).catch(() => {});
console.log('[db] Schema ensured');
}
module.exports = { ensureSchema };