diff --git a/package.json b/package.json index eacd615..704aaff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ipwhitelist", - "version": "0.1.22", + "version": "0.1.23", "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 1209504..14d526e 100644 --- a/v2/src/config/index.js +++ b/v2/src/config/index.js @@ -8,7 +8,7 @@ // ═══════════════════════════════════════════════════════════════════════════════ module.exports = { - version: '0.1.22', + version: '0.1.23', // ── Среда ───────────────────────────────────────────────────────────────── // APP_ENV=test → тестовый стенд (жёлтый баннер в UI, можно включать тестовый API) diff --git a/v2/src/db/schema.js b/v2/src/db/schema.js index 195d368..8358be8 100644 --- a/v2/src/db/schema.js +++ b/v2/src/db/schema.js @@ -1,11 +1,29 @@ // ═══════════════════════════════════════════════════════════════════════════════ -// V2 — инициализация схемы БД +// V2 — инициализация схемы БД + миграции // -// ЭТО: CREATE TABLE IF NOT EXISTS — безопасный повторный запуск. -// НИКАКИХ ALTER, миграций, DO$$ блоков. Только создание таблиц. +// ЭТО: CREATE TABLE IF NOT EXISTS (идемпотентно) + applyMigrations(). +// ЗАЧЕМ: приложение само создаёт таблицы и применяет новые миграции при старте. +// +// МИГРАЦИИ: встроенные SQL-строки, не из файлов. +// Каждая в транзакции (BEGIN/COMMIT), ошибка логируется и пропускается. +// Без fs, path, process.exit. // ═══════════════════════════════════════════════════════════════════════════════ +// ── Миграции (упорядочены, выполняются один раз) ────────────────────────────── +const MIGRATIONS = [ + { + name: '001_add_impersonated_by', + sql: `ALTER TABLE audit_log ADD COLUMN IF NOT EXISTS impersonated_by VARCHAR(255) DEFAULT NULL`, + }, + { + name: '002_add_audit_restore', + sql: `ALTER TABLE audit_log DROP CONSTRAINT IF EXISTS audit_log_action_check; +ALTER TABLE audit_log ADD CONSTRAINT audit_log_action_check CHECK (action IN ('CREATE','UPDATE','DELETE','RESTORE'))`, + }, +]; + async function ensureSchema(pool) { + // 1. Таблицы await pool.query(` CREATE TABLE IF NOT EXISTS companies ( id SERIAL PRIMARY KEY, @@ -47,9 +65,44 @@ async function ensureSchema(pool) { CREATE INDEX IF NOT EXISTS idx_audit_log_company_time ON audit_log(company_id, created_at DESC); + + -- Таблица учёта выполненных миграций + CREATE TABLE IF NOT EXISTS _migrations ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) UNIQUE NOT NULL, + applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + ); `); console.log('[db] Schema ensured'); + + // 2. Миграции + await applyMigrations(pool); +} + +async function applyMigrations(pool) { + try { + const done = new Set( + (await pool.query('SELECT name FROM _migrations')).rows.map(r => r.name) + ); + + for (const m of MIGRATIONS) { + if (done.has(m.name)) continue; + try { + await pool.query('BEGIN'); + await pool.query(m.sql); + await pool.query('INSERT INTO _migrations (name) VALUES ($1)', [m.name]); + await pool.query('COMMIT'); + console.log('[db] Migration applied:', m.name); + } catch (e) { + await pool.query('ROLLBACK').catch(() => {}); + console.error('[db] Migration failed:', m.name, e.message); + } + } + } catch (e) { + console.error('[db] applyMigrations error:', e.message); + } } module.exports = { ensureSchema }; +module.exports = { ensureSchema };