diff --git a/package.json b/package.json index a49dabf..26caab5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ipwhitelist", - "version": "0.1.20", + "version": "0.1.21", "description": "IP WhiteList microservice for cloud provider", "main": "server.js", "scripts": { diff --git a/v2/src/config/index.js b/v2/src/config/index.js index 698b12e..730c28f 100644 --- a/v2/src/config/index.js +++ b/v2/src/config/index.js @@ -8,7 +8,7 @@ // ═══════════════════════════════════════════════════════════════════════════════ module.exports = { - version: '0.1.20', + version: '0.1.21', // ── Среда ───────────────────────────────────────────────────────────────── // APP_ENV=test → тестовый стенд (жёлтый баннер в UI, можно включать тестовый API) diff --git a/v2/src/db/schema.js b/v2/src/db/schema.js index cf8e905..195d368 100644 --- a/v2/src/db/schema.js +++ b/v2/src/db/schema.js @@ -2,19 +2,11 @@ // 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 +// НИКАКИХ ALTER, миграций, DO$$ блоков. Только создание таблиц. // ═══════════════════════════════════════════════════════════════════════════════ 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, @@ -24,7 +16,6 @@ async function ensureSchema(pool) { updated_at TIMESTAMPTZ ); - -- Записи: CIDR + метаданные, soft delete CREATE TABLE IF NOT EXISTS whitelist_entries ( id SERIAL PRIMARY KEY, company_id INTEGER NOT NULL, @@ -38,17 +29,15 @@ async function ensureSchema(pool) { 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')), + action VARCHAR(32) NOT NULL, old_value TEXT, new_value TEXT, entry_id INTEGER, @@ -60,40 +49,6 @@ async function ensureSchema(pool) { 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 — только если ещё нет (без DROP+ADD каждый раз) - await pool.query(` - DO $$ - DECLARE - cn text; - has_restore boolean; - BEGIN - -- Проверяем, есть ли уже RESTORE в constraint - SELECT con.consrc LIKE '%RESTORE%' OR pg_get_constraintdef(con.oid) LIKE '%RESTORE%' INTO has_restore - FROM pg_constraint con - JOIN pg_class rel ON rel.oid = con.conrelid - WHERE rel.relname = 'audit_log' AND con.contype = 'c'; - -- Если уже есть — ничего не делаем - IF has_restore THEN - RAISE NOTICE 'audit_log CHECK already includes RESTORE, skipping'; - RETURN; - END IF; - 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'); }