v2: подробные комментарии во всех модулях — что, зачем, контракты

This commit is contained in:
2026-06-13 07:37:07 +04:00
parent 1af304b2a8
commit 616b4ead09
14 changed files with 592 additions and 518 deletions
+183
View File
@@ -369,3 +369,186 @@ remove(entryId, clientId, email, impBy) → void
1. **Тестирование**: crud тестируется без Express, user тестируется с моком crud
2. **Переиспользование**: admin, test, export — все через crud
3. **Независимость**: фронтенд не знает про deviceId, БД-схему, транзакции
---
## Текущее состояние (0.5.93) — полная архитектура
### Слои приложения
```
┌─────────────────────────────────────────────────────┐
│ Express HTTP │
├────────────┬────────────────┬───────────────────────┤
│ user/ │ admin/ │ test/ │
│ (юзер) │ (админ) │ (тестовый слой) │
│ HTML+POST │ HTML+POST │ JSON, no session │
├────────────┴────────────────┴───────────────────────┤
│ router/ │
│ resolveContext middleware │
│ сессия → req.clientId, email, isAdmin │
├─────────────────────────────────────────────────────┤
│ crud/ │
│ API БД (чистые функции) │
│ list(clientId) / add / edit / remove │
│ validate() вызывается здесь │
├─────────────────────────────────────────────────────┤
│ db/ │
│ queries.js (SQL) + schema.js (DDL) │
│ чистый SQL: createEntry, updateEntry, ... │
├─────────────────────────────────────────────────────┤
│ validators/ │
│ validate(cidr), overlaps(), BLOCKED_RANGES │
├─────────────────────────────────────────────────────┤
│ auth/ + config/ │
│ OIDC: login, exchangeCode, fetchIamUser │
└─────────────────────────────────────────────────────┘
```
### Слои — внутренние API (не HTTP)
Каждый слой — модуль Node.js с контрактом:
| Слой | Экспорт | Вход | Выход |
|------|---------|------|-------|
| **validators/** | `validate(raw)` | строка CIDR | `{ cidr, wasNormalized }` или throw |
| **crud/** | `add(clientId, cidr, ...)` | W-номер + параметры | `{ entry, wasNormalized }` или throw |
| **crud/** | `list(clientId)` | W-номер | `{ entries, used, limit }` |
| **crud/** | `edit(entryId, clientId, cidr, ...)` | ID + W-номер | `{ entry, wasNormalized }` |
| **crud/** | `remove(entryId, clientId, ...)` | ID + W-номер | void |
| **db/** | `createEntry(companyId, cidr, ...)` | внутренний ID + чистый CIDR | `{ entry }` |
| **db/** | `getAllCompanies()` | — | массив компаний |
| **db/** | `getAudit(companyId)` | companyId или null | массив записей аудита |
| **db/** | `setLimit(companyId, limit)` | ID + число | void |
### Структура файлов
```
v2/
├── server.js # createV2Router() — монтаж всех роутеров
├── history/
│ └── 2026-06-12.md # этот файл
└── src/
├── auth/index.js # OIDC: login, exchangeCode, fetchIamUser, buildAuthUrl
├── config/index.js # V2_* env, version, умолчания
├── router/index.js # resolveContext — сессия → req.*
├── validators/
│ └── index.js # validate, overlaps, BLOCKED_RANGES (14 диапазонов ТЗ)
├── crud/
│ └── index.js # list, add, edit, remove — API БД, вызывает validate()
├── db/
│ ├── index.js # pg pool
│ ├── queries.js # SQL: CRUD + admin (getAllCompanies, setLimit, getAudit)
│ └── schema.js # ensureSchema — автосоздание v2_companies, v2_entries, v2_audit
├── user/
│ └── index.js # createUserRouter — фронт юзера: HTML + POST /add /edit /delete
├── admin/
│ └── index.js # createAdminRouter — дашборд, аудит, лимиты, записи
└── test/
├── index.js # createTestRouter — тестовый API + chaos + userFlow
└── test.sh # 54 curl-теста
```
### Маршруты /v2
| Маршрут | Слой | Авторизация | Что |
|---------|------|------------|-----|
| `/v2/login` | server.js | Нет | Редирект на `/login?returnTo=/v2/app` |
| `/v2/iam` | server.js | Сессия | IAM-данные (отладка) |
| `/v2/app` | user/ | resolveContext | CRUD юзера: список, добавить, изменить, удалить |
| `/v2/admin` | admin/ | isAdmin | Дашборд компаний |
| `/v2/admin/audit` | admin/ | isAdmin | Аудит по компании |
| `/v2/admin/limit` | admin/ | isAdmin | POST — установить лимит |
| `/v2/admin/entries` | admin/ | isAdmin | Записи любой компании |
| `/v2/test` | test/ | Нет (флаг) | Тестовый API: `?action=add/list/edit/delete/audit/limit/switch/cleanup/userFlow` |
| `/v2/test/chaos` | test/ | Нет (флаг) | Параллельный хаос-тест (40 операций) |
| `/v2/logout` | server.js | Нет | Редирект на `/logout` |
### Цепочка вызовов (юзер добавляет CIDR)
```
Браузер: форма <form method="POST" action="/v2/app/add">
→ POST /v2/app/add
→ user/index.js router.post('/add')
→ crud.add(clientId, rawCidr, comment, email, impBy)
→ validators.validate(rawCidr)
→ db.getOrCreateCompany(clientId)
→ db.createEntry(companyId, validatedCidr, ...)
→ overlaps() — проверка дубликатов в БД
→ SQL INSERT v2_entries
→ SQL INSERT v2_audit
→ 302 /v2/app?msg=Добавлено
```
### Админка — возможности
| Функция | Маршрут | Через |
|---------|---------|-------|
| Список всех компаний | GET /v2/admin | q.getAllCompanies() |
| Аудит компании | GET /v2/admin/audit?companyId=X | q.getAudit() |
| Установка лимита | POST /v2/admin/limit | q.setLimit() |
| Записи компании | GET /v2/admin/entries?companyId=X | crud.list(clientId) |
Колонки аудита: Дата, Действие, Кто, От имени (impersonated_by), Компания, Значения (old→new).
### Тестовый слой
`/v2/test` — полный доступ ко всем слоям через curl, без KC-сессии:
| Action | Что тестирует | Слои |
|--------|--------------|------|
| `?action=add&cidr=X` | Добавление | test→crud→validate→db |
| `?action=edit&id=X&cidr=Y` | Изменение | test→crud→validate→db |
| `?action=delete&id=X` | Удаление | test→crud→db |
| `?action=list` | Список | test→crud→db |
| `?action=audit` | Аудит | test→db |
| `?action=userFlow&sub=add` | Полная эмуляция юзера | test→мок сессии→crud→db |
| `/chaos` | Параллельный (40 ops) | test→crud→db |
| `?action=cleanup` | Очистка | test→db (прямые DELETE) |
Флаг отключения: `ENABLE_TEST_API=false` — код остаётся, роутер не монтируется.
### Тесты — сводка
| Группа | Кол-во | Статус |
|--------|--------|--------|
| test.sh (curl) | 54 | ✅ |
| Chaos (параллельные) | 1 | ✅ |
| Router (resolveContext) | 9 | ✅ |
| **Всего** | **64** | **✅** |
### Деплой
git push → Gitea → Nubes UI redeploy → `whitelist.nodejsk8s.services.ngcloud.ru`
VM (italo.kube5s.ru) — НЕ используется для деплоя v2.
### Ключевые решения
1. **Слои — внутренние API**: не HTTP, не микросервисы, чистые функции в одном процессе
2. **validate() вызывается в crud/**: db/queries получает готовый CIDR, не вызывает validate
3. **crud/ резолвит clientId→companyId**: фронтенды не знают про внутренние ID БД
4. **test/ — отдельный вход**: мок-сессия, без KC, доступен только в dev
5. **ENABLE_TEST_API=false** — отключение без удаления кода
6. **v2_ префиксы**: на таблицах БД — v2_companies, v2_entries, v2_audit. При интеграции убрать.
7. **Сессия без v2_ префикса**: `req.session.user` — совместимо с основным приложением
### Что дальше
| # | Задача | Статус |
|---|--------|--------|
| 1 | export/ — выгрузка CIDR | ❌ |
| 2 | EJS-шаблоны вместо inline HTML | ❌ |
| 3 | Auth end-to-end (KC callback) | ❌ |
| 4 | Интеграция в основной код (убрать v2_) | ❌ |
### Известные ошибки (исправлены)
| Баг | Причина | Фикс | Версия |
|-----|---------|------|--------|
| 10.x CIDR → 45 ошибок chaos | 10.0.0.0/8 в BLOCKED | Префиксы 1114 | 0.5.89 |
| auditCount=0 | Проверяли только WZ10001 | Все 4 компании | 0.5.89 |
| 105/150 ошибок | 3 задачи × 15 > лимит 15 | 1 задача × 10 | 0.5.89 |
| /chaos → 302 /login | Роутер на /test/api | Сменили на /test | 0.5.88 |
| validate() в db/queries | Смешаны слои | Вынесен в crud/ | 0.5.91 |