15 KiB
V2 — история 2026-06-12 (финал)
V2 — это тестовый стенд для модулей
Код в v2/ — НЕ production. Это полигон для отладки модулей.
Каждый модуль тестируется изолированно, затем будет интегрирован в основной код.
Текущее состояние (0.5.73)
v2/
├── server.js # createV2Router() → /v2/login, /v2/iam, /v2/logout
├── src/
│ ├── auth/index.js # fetchIamUser(), login(), exchangeCode(), buildAuthUrl()
│ ├── config/index.js # iamUrl, appUrl, version (без KC — пока не нужен)
│ ├── crud/index.js # createCrudRouter() — НЕ монтирован (будет позже)
│ ├── db/
│ │ ├── index.js # pg pool
│ │ ├── queries.js # CRUD SQL
│ │ └── schema.js # ensureSchema(pool) — автосоздание v2_* таблиц
│ └── validators/index.js # CIDR validate, overlaps, BLOCKED_RANGES
└── history/
└── 2026-06-12.md
Как работает сейчас
/v2/login→ редирект на/login?returnTo=/v2/iam- Основной OIDC: production-KC →
/callback→ сессия → редирект на/v2/iam /v2/iam→ показывает IAM-данные изreq.session.user
V2 НЕ делает свой OIDC. Использует готовую сессию основного приложения.
Никаких своих KC-настроек. Никакого /v2/callback.
Модуль auth/index.js — готов, но не используется
Функции готовы к production:
buildAuthUrl(config)— URL редиректа на KCexchangeCode(code, config)— обмен code → токенfetchIamUser(token, iamUrl)— IAM → профильgenerateState()— CSRFlogin(code, oidcConfig, iamUrl)— оркестратор
Не тестировались: нужен /v2/callback в redirect URIs production-KC.
Модуль crud/ — готов, не монтирован
Функции:
createCrudRouter()— роутер с выбором компании (всегда список, даже если одна)- CRUD через
db/queries.js
Таблицы: v2_companies, v2_entries, v2_audit (автосоздание через schema.js).
Модуль db/ — готов
db/index.js— pg pooldb/queries.js— getOrCreateCompany, listEntries, createEntry, updateEntry, deleteEntry, getAudit, ...db/schema.js— ensureSchema(pool) — CREATE TABLE IF NOT EXISTS
Модуль validators/ — готов
CIDR validate, overlaps, cidrToRange, aggregateCIDRs, BLOCKED_RANGES.
Что дальше
- fio в fetchIamUser — возвращать строку (сейчас объект)
- Добавить
/v2/callbackв redirect URIs production-KC - Протестировать auth/login() с production-KC
- Подключить crud модуль
- Админка, экспорт
Ключевые решения
- V2 НЕ трогает основной код (server.js — только app.use('/v2', ...))
- Каждый модуль — отдельная папка в src/
- env-переменные — через process.env, без .env/dotenv
- Умолчания — хардкод, переопределяются через V2_* env
Модуль router/ (0.5.75)
resolveContext middleware — определяет контекст из req.session.v2_user:
нет сессии / нет v2_user → 302 /v2/login
обычный юзер → req.v2_email, req.v2_clientId (activeClientId)
юзер с несколькими компаниями → activeClientId из сессии
админ + adminMode → req.v2_isAdmin = true
админ без adminMode → req.v2_isAdmin = false (видит как юзер)
имперсонация → email подменён на originalUserEmail
clientId подменён на impersonatedCompanyId
req.v2_impersonatedBy = реальный админ
Тесты (v2/src/router/test.js)
9 тестов, все пройдены ✅:
- нет сессии → /v2/login
- нет v2_user → /v2/login
- обычный юзер 1 компания
- юзер 2 компании → activeClientId
- админ adminMode=true
- админ adminMode=false
- имперсонация с originalUserEmail
- имперсонация без originalUserEmail
- без activeClientId → fallback на clientId
Ошибки при тестировании
- Причина: спешка. null вместо undefined, двойной префикс v2_v2_ в ключах.
- Урок: сначала думать, потом писать.
Текущая структура (0.5.75)
v2/src/
├── auth/index.js # fetchIamUser(), login(), exchangeCode()
├── config/index.js # iamUrl, appUrl, version
├── router/
│ ├── index.js # resolveContext middleware
│ └── test.js # 9 тестов
├── user/index.js # createUserRouter (пустышка)
├── admin/index.js # createAdminRouter (пустышка)
├── crud/index.js # createCrudRouter (не монтирован)
├── db/ # pool, queries, schema
└── validators/index.js # CIDR validate
⚠️ ПРИ ИНТЕГРАЦИИ — что переименовать
Таблицы (префикс v2_ → убрать)
v2_companies → companies
v2_entries → whitelist_entries
v2_audit → audit_log
Причина: PostgreSQL один на всё приложение.
Сессия — НЕ трогать (уже без префикса)
req.session.user ← одинаково в v2 и основном коде
req.session.token
req.session.adminMode
req.v2_* контекст — убрать префикс
req.v2_email → req.email
req.v2_clientId → req.clientId
req.v2_isAdmin → req.isAdmin
req.v2_isImpersonated → req.isImpersonated
req.v2_impersonatedBy → req.impersonatedBy
req.v2_allClientIds → req.allClientIds
req.v2_profiles → req.profiles
req.v2_companyName → req.companyName
Причина: в основном коде нет префиксов.
Текущее состояние (0.5.89)
Структура v2/
v2/
├── server.js # createV2Router() — монтируется в server.js как /v2
├── history/
│ └── 2026-06-12.md # этот файл
└── src/
├── auth/index.js # fetchIamUser(), login(), exchangeCode(), buildAuthUrl()
├── config/index.js # V2_* env, version, умолчания
├── router/index.js # resolveContext middleware (email, clientId, isAdmin, impersonation)
├── user/index.js # createUserRouter() — UI CRUD с выбором компании
├── admin/index.js # createAdminRouter() — ПУСТЫШКА (email, clientId)
├── crud/index.js # createCrudRouter() — старый, НЕ монтирован
├── db/
│ ├── index.js # pg pool
│ ├── queries.js # CRUD: getOrCreateCompany, listEntries, createEntry, updateEntry, deleteEntry, getAudit, getLimit, setLimit, getExportCIDRs, getAllCompanies
│ └── schema.js # ensureSchema(pool) — автосоздание v2_companies, v2_entries, v2_audit
├── test/
│ ├── index.js # createTestRouter() — /v2/test?action=... и GET /chaos
│ └── test.sh # 54 curl-теста
└── validators/index.js # validate(), overlaps(), cidrToRange(), aggregateCIDRs(), BLOCKED_RANGES
Маршруты /v2
| Маршрут | Что | Авторизация |
|---|---|---|
/v2/login |
Редирект на /login?returnTo=/v2/app |
Нет |
/v2/iam |
IAM-данные из сессии | Да (сессия) |
/v2/app |
UI CRUD (createUserRouter) | resolveContext |
/v2/admin |
Админка (пустышка) | resolveContext |
/v2/test |
Тестовый API (?action=...) |
Нет |
/v2/test/chaos |
Параллельный хаос-тест | Нет |
/v2/logout |
Редирект на /logout |
Нет |
Деплой
git push → Gitea → Nubes UI redeploy → whitelist.nodejsk8s.services.ngcloud.ru
VM (italo.kube5s.ru) — НЕ используется для деплоя v2.
Тесты — полный список
1. test.sh — 54 curl-теста (✅ все пройдены)
Запрещённые диапазоны (14 тестов): 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 192.0.0.0/24, 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 198.18.0.0/15, 224.0.0.0/4, 240.0.0.0/4, 255.255.255.255/32
Нормализация и маски (5 тестов):
- /22 работает, /21 отклонена, /33 отклонена, IPv6 отклонён, домен отклонён
- Нормализация хостовой части (13.0.0.5/24 → 13.0.0.0/24)
- wasNormalized=true
Лимиты (2 теста):
- 16-я запись отклонена (лимит 15)
- Сообщение о лимите
CRUD полный цикл (10 тестов):
- Добавление, добавление 2, добавление /32
- Список — 3 записи
- Изменение CIDR, проверка в БД
- Soft delete, список после удаления, с удалёнными
Изоляция компаний (3 теста):
- Добавление в компанию A/B, пересечение разрешено
Переключение компаний (5 тестов):
- Добавление в активную, переключение, список, добавление в другую, обратно
Имперсонация — двойной аудит (6 тестов):
- created_by = originalUserEmail
- audit: impersonated_by, user_email
- UPDATE: old→new записаны
- DELETE записан
Нагрузка (2 теста):
- 50 добавлений (первые 15 ок, остальные лимит)
- БД: ровно 15 записей
Экспорт (1 тест):
- Эндпоинт доступен
2. Chaos-тест — параллельные пользователи (✅)
GET /v2/test/chaos — 4 компании × 10 записей = 40 параллельных операций.
Проверки:
- ok: 40, errors: 0
- totalEntries: 40, totalAudit: 40
- 4 компании по 10 записей, без дубликатов, в лимите
Префиксы CIDR: 11, 12, 13, 14 (10.x заблокирован 10.0.0.0/8).
3. Router — resolveContext (✅ 9 тестов)
Файл: v2/src/router/test.js (или внутренние).
- нет сессии → /v2/login
- нет user → /v2/login
- обычный юзер 1 компания
- юзер 2 компании → activeClientId
- админ adminMode=true
- админ adminMode=false
- имперсонация с originalUserEmail
- имперсонация без originalUserEmail
- без activeClientId → fallback на clientId
Сводка
| Группа | Кол-во | Статус |
|---|---|---|
| test.sh | 54 | ✅ |
| Chaos | 1 | ✅ |
| Router | 9 | ✅ |
| Всего | 64 | ✅ |
НЕ протестировано
| Модуль | Причина |
|---|---|
| auth/ (login, exchangeCode, fetchIamUser) | Нужен /v2/callback в redirect URIs production KC |
| admin/ | Пустышка — только email/clientId |
| user/ UI (EJS) | Не тестировался UI-интерфейс |
| Интеграция через основную сессию | Не проверялся полный путь KC → IAM → CRUD |
Что дальше (план)
- Реализовать admin/ — аудит, лимиты, все компании
- Реализовать export/ — выгрузка CIDR
- Добавить
/v2/callbackв production KC → протестировать auth/ - EJS-шаблоны вместо inline HTML
- Интеграция v2 модулей в основной код (убрать префиксы v2_)
Известные ошибки (исправлены)
| Баг | Причина | Фикс |
|---|---|---|
10.x CIDR → 45 ошибок в chaos |
10.0.0.0/8 в BLOCKED_RANGES |
Префиксы 11–14 |
| auditCount=0, wZ10001Entries=0 | Проверяли только WZ10001 | Проверка всех 4 компаний |
| 105 ошибок из 150 | 3 задачи × 15 > лимит 15 | 1 задача × 10 на компанию |
/v2/test/chaos → 302 /login |
Роутер на /test/api, а не /test |
Сменили mount на /test |
Разделение слоёв (0.5.90+) — ПЛАН
Проблема
Сейчас user/index.js знает про db/queries.js и getOrCreateCompany/companyId:
user/index.js → db/queries.js → SQL
Фронтенд смешан с логикой БД.
Решение: три слоя
user/index.js (фронтенд, Express) → crud/index.js (API БД) → db/queries.js (SQL)
crud/index.js — чистые функции-агностики:
- Не знает про Express, req, res, сессии
- Принимает
clientId(W-номер), сам резолвитcompanyIdчерезgetOrCreateCompany - Вход: plain values, Выход: plain object или throw
list(clientId) → { entries, used, limit }
add(clientId, cidr, comment, email, impBy) → { entry, wasNormalized }
edit(entryId, clientId, cidr, comment, email, impBy) → { entry, wasNormalized }
remove(entryId, clientId, email, impBy) → void
user/index.js — только Express:
- Дёргает crud.*, рендерит HTML
- Не знает про companyId, getOrCreateCompany, SQL
db/queries.js — без изменений (чистый SQL)
Что изменится
| Файл | Было | Стало |
|---|---|---|
| crud/index.js | старый, не рабочий | новый API-слой |
| user/index.js | require('../db/queries') | require('../crud') |
| test/index.js | require('../db/queries') | require('../crud') |
Преимущества
- Тестирование: crud тестируется без Express, user тестируется с моком crud
- Переиспользование: admin, test, export — все через crud
- Независимость: фронтенд не знает про deviceId, БД-схему, транзакции