Чистый ensureSchema: только CREATE TABLE IF NOT EXISTS, без миграций. Версия 0.1.21
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ipwhitelist",
|
"name": "ipwhitelist",
|
||||||
"version": "0.1.20",
|
"version": "0.1.21",
|
||||||
"description": "IP WhiteList microservice for cloud provider",
|
"description": "IP WhiteList microservice for cloud provider",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
version: '0.1.20',
|
version: '0.1.21',
|
||||||
|
|
||||||
// ── Среда ─────────────────────────────────────────────────────────────────
|
// ── Среда ─────────────────────────────────────────────────────────────────
|
||||||
// APP_ENV=test → тестовый стенд (жёлтый баннер в UI, можно включать тестовый API)
|
// APP_ENV=test → тестовый стенд (жёлтый баннер в UI, можно включать тестовый API)
|
||||||
|
|||||||
+2
-47
@@ -2,19 +2,11 @@
|
|||||||
// V2 — инициализация схемы БД
|
// V2 — инициализация схемы БД
|
||||||
//
|
//
|
||||||
// ЭТО: CREATE TABLE IF NOT EXISTS — безопасный повторный запуск.
|
// ЭТО: CREATE TABLE IF NOT EXISTS — безопасный повторный запуск.
|
||||||
// ЗАЧЕМ:
|
// НИКАКИХ ALTER, миграций, DO$$ блоков. Только создание таблиц.
|
||||||
// 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) {
|
async function ensureSchema(pool) {
|
||||||
await pool.query(`
|
await pool.query(`
|
||||||
-- Компании: W-номер уникален, upsert через ON CONFLICT
|
|
||||||
CREATE TABLE IF NOT EXISTS companies (
|
CREATE TABLE IF NOT EXISTS companies (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
client_id VARCHAR(64) NOT NULL UNIQUE,
|
client_id VARCHAR(64) NOT NULL UNIQUE,
|
||||||
@@ -24,7 +16,6 @@ async function ensureSchema(pool) {
|
|||||||
updated_at TIMESTAMPTZ
|
updated_at TIMESTAMPTZ
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Записи: CIDR + метаданные, soft delete
|
|
||||||
CREATE TABLE IF NOT EXISTS whitelist_entries (
|
CREATE TABLE IF NOT EXISTS whitelist_entries (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
company_id INTEGER NOT NULL,
|
company_id INTEGER NOT NULL,
|
||||||
@@ -38,17 +29,15 @@ async function ensureSchema(pool) {
|
|||||||
deleted_at TIMESTAMPTZ
|
deleted_at TIMESTAMPTZ
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Защита от дублей: уникальный CIDR в компании среди активных записей
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_whitelist_entries_active_cidr
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_whitelist_entries_active_cidr
|
||||||
ON whitelist_entries(company_id, value_cidr)
|
ON whitelist_entries(company_id, value_cidr)
|
||||||
WHERE deleted_at IS NULL;
|
WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
-- Аудит: каждое действие с возможностью отследить имперсонацию
|
|
||||||
CREATE TABLE IF NOT EXISTS audit_log (
|
CREATE TABLE IF NOT EXISTS audit_log (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
user_email VARCHAR(255) NOT NULL,
|
user_email VARCHAR(255) NOT NULL,
|
||||||
company_id INTEGER 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,
|
old_value TEXT,
|
||||||
new_value TEXT,
|
new_value TEXT,
|
||||||
entry_id INTEGER,
|
entry_id INTEGER,
|
||||||
@@ -60,40 +49,6 @@ async function ensureSchema(pool) {
|
|||||||
ON audit_log(company_id, created_at DESC);
|
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');
|
console.log('[db] Schema ensured');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user