From 12e797eb1ad8e970b954ce115bd8a265cd9b64c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=D1=81=20=D0=A1=D1=83=D0=BB=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=D0=B2?= Date: Tue, 8 Sep 2026 10:28:44 +0300 Subject: [PATCH] =?UTF-8?q?fix(rls):=20startup-=D0=BF=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D0=BA=D0=B0=20schema=20drift=20=E2=80=94=20array?= =?UTF-8?q?=5Fagg=20name[]=20=D0=BF=D1=80=D0=B8=D1=85=D0=BE=D0=B4=D0=B8?= =?UTF-8?q?=D1=82=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Шаг 0.1 (подготовка) плана production-готовности: pg-драйвер возвращает name[] как "{a,b,c}" (нет парсера OID 1003) — проверка находила 0 таблиц и запуск с ENABLE_RLS=true всегда падал. Поддержаны оба формата. Проверено на копии прод-БД: app стартует, 31 таблица ENABLE+FORCE, login/search/sync работают --- IMPLEMENTATION_LOG.md | 13 +++++++++++++ server/index.ts | 9 ++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/IMPLEMENTATION_LOG.md b/IMPLEMENTATION_LOG.md index 37962b7..acbce58 100644 --- a/IMPLEMENTATION_LOG.md +++ b/IMPLEMENTATION_LOG.md @@ -416,3 +416,16 @@ - Как проверялось: docker compose config валиден для всех 4 файлов и для бэкапа на сервере. - Влияние на поиск/UX: нет. - Подводные камни: сумма лимитов ~6.3 ГБ > 5.9 ГБ RAM — лимиты это потолки, не резерв; ollama в простое 36 МБ. На проде лимиты применяются к app/document-worker при ближайшем деплое; остальные сервисы — при их пересоздании. + +--- + +## [0.1] RLS обязательным в production — подготовка и локальная проверка + +- Статус: ⏸ частично (локальная проверка пройдена; прод-включение и fatal-проверка — после подтверждения пользователя) +- Зачем: изоляция tenant'ов на уровне БД (аудит, Фаза 0). +- Что сделано: + - Локальная проверка на КОПИИ прод-БД (scratch pgvector контейнер, свежий дамп): app стартует с ENABLE_RLS=true, 31 таблица ENABLE+FORCE, login/поиск (подстрока «варий»→«Аварийка»)/список задач (50, без description)/формы/sync работают, passwordHash не утекает. + - **Найден и исправлен блокирующий баг** в `server/index.ts` (startup RLS-проверка): pg-драйвер возвращает `array_agg(tablename)` (тип name[]) СТРОКОЙ "{tasks,users,...}", а не массивом — проверка schema drift находила 0 таблиц и падала при ЛЮБОМ запуске с ENABLE_RLS=true. Т.е. RLS нельзя было включить без этого фикса. Поддержаны оба формата. +- Что осталось (прод, с подтверждения): 1) добавить ENABLE_RLS=true в .env сервера и перезапустить app, проверить; 2) затем задеплоить fatal-проверку (NODE_ENV=production без ENABLE_RLS → процесс не стартует) + пометки в .env.example/DOCKER.md. +- Влияние на поиск/UX: нет. +- Подводные камни: порядок важен — fatal-проверку деплоить ТОЛЬКО после включения флага на проде, иначе app не поднимется. diff --git a/server/index.ts b/server/index.ts index fea0abe..472aef1 100644 --- a/server/index.ts +++ b/server/index.ts @@ -931,8 +931,15 @@ async function runStartupDataPatches() { FROM pg_tables WHERE schemaname = 'public' AND tablename IN (${tableList}) `)); + // pg-драйвер возвращает name[] строкой "{tasks,users,...}" (нет парсера для OID 1003) — + // поддерживаем оба формата, иначе ни одна таблица не будет «найдена». + const rawFound = (existsCheck.rows[0] as Record)?.found; const foundTables = new Set( - ((existsCheck.rows[0] as Record)?.found as string[] | null) ?? [] + Array.isArray(rawFound) + ? (rawFound as string[]) + : typeof rawFound === "string" + ? rawFound.replace(/^\{|\}$/g, "").split(",").filter(Boolean) + : [] ); const missingTables = ALL_RLS_TABLES.filter(t => !foundTables.has(t)); if (missingTables.length > 0) {