Netpulse_SasS/ROADMAP.md
byrsapty 65b37a28ff
Some checks are pending
CI / web (push) Waiting to run
CI / server (push) Waiting to run
CI / agent (push) Waiting to run
Git-двигун NCM: версіювання конфігів, переливання історії, довільне порівняння
go-git, чистий Go без cgo. Голий репозиторій на тенанта, гілка на
пристрій за ідентифікатором (ім'я змінюють, історія не має від цього
розсипатись), файл за іменем. Однаковий вміст нового коміту не створює.

netpulse-gitsync переливає накопичену історію й відтворює втрачений
репозиторій із бази — тіла конфігів там і так лежать зашифрованими.

У вебі з'явився вибір версії, з якою порівнювати: сервер це вмів
(?from=), інтерфейс — ні.

Дорогою виправлено чотири тести, які CI запустив уперше з базою.
Серед них справжня помилка: машинний токен не міг читати мапи —
ACL мап отримував порожній рядок замість uuid і давав 500.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 13:15:14 +03:00

331 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# NetPulse — план робіт
Документ доповнює [HISTORY.md](HISTORY.md): той описує зроблене, цей — що лишилось
і чому саме в такому порядку.
## Де ми зараз
Працює наскрізний ланцюг **кабель → браузер**: зонд знаходить сусідів по
LLDP/CDP/ARP, сервер зводить їх у `topo.links`, сам заводить чеки на трафік,
API віддає готове полотно з живими статусами, редактор пише назад із контролем
конфліктів і відкатом.
Перевірено на живому стенді проти справжнього `snmpd`. Деталі — в README кожного
компонента.
## Чого немає: чесний зріз
Схема БД з Етапу 1 покриває майже все з технічного завдання, але **схема ≠
реалізація**. Нижче — розрив між ними.
| Підсистема | Схема | Реалізація |
|------------|-------|------------|
| Мапа, топологія, телеметрія | ✅ | ✅ |
| Автовиявлення LLDP/CDP/ARP/FDB | ✅ | ✅ |
| **Користувачі, ролі, вхід** | ✅ | ✅ |
| **Шаблони опитування** | ❌ | ❌ |
| **NCM (збір конфігів)** | ✅ | ⚠️ збір, розклад і Git є; лишився тригер за Syslog |
| **Керування зондом із UI** | ✅ | ⚠️ транспорт є |
| **Алерти й сповіщення** | ✅ | ✅ |
| **Мобільна адаптивність, PWA** | — | ⚠️ адаптив є, PWA немає |
| Веб-інтерфейс (навігація, сторінки) | — | ✅ |
| Дашборди, NOC TV | ✅ | ❌ |
| Білінг, ліцензії | ✅ | ❌ |
---
## Етап 5. Веб із користувачами і правами — ✅ зроблено 2026-08-15
> Реалізовано: вхід, JWT + ротація refresh-сесій, RBAC на всіх ендпоїнтах,
> `netpulse-user` для першого власника, сторінка входу, приховування дій без
> права, мобільний адаптив. Подробиці — [HISTORY.md](HISTORY.md),
> контракт — [server/API.md](server/API.md).
>
> **Доповнено 2026-08-15:** повноцінний веб — навігація й вісім сторінок
> (мапа, пристрої, алерти, правила, канали, зонди, команда, профіль),
> примітиви UI з мобільними картками замість таблиць, захист маршрутів
> правами.
>
> **Відкладено з цього етапу:** TOTP (`totp_secret_enc` є в схемі, коду немає),
> запрошення поштою (`core.invitations` порожня — користувача заводять із
> паролем одразу), кастомні ролі (`POST /api/v1/roles`), фільтр за
> `memberships.scope_group_ids`, редагування умови правила (лише створення
> й вимкнення), маршрути сповіщень і вікна обслуговування в UI.
**Навіщо перше.** Зараз API автентифікує лише машинні токени
(`core.api_tokens`). Людина увійти не може, а `core.roles` / `permissions` /
`memberships` лежать порожні. Без цього не можна ані впустити клієнта, ані
розмежувати доступ між інженером і глядачем — тобто продукт неможливо продати
навіть одній команді.
### Сервер
```
POST /api/v1/auth/login email + пароль → access (JWT, 15 хв) + refresh
POST /api/v1/auth/refresh обмін refresh-токена
POST /api/v1/auth/logout відкликання сесії
GET /api/v1/me профіль + ефективні права
POST /api/v1/auth/totp/enroll двофакторка (поле totp_secret_enc уже є)
GET /api/v1/users керування командою
POST /api/v1/users/invite запрошення (core.invitations уже є)
PATCH /api/v1/users/{id} зміна ролі, скоупу груп
GET /api/v1/roles системні + кастомні (Enterprise)
POST /api/v1/roles кастомна роль із набором прав
```
**Паролі — argon2id**, не bcrypt: він стійкіший до GPU-перебору, а `password_hash`
у схемі вже text і формат не диктує.
**Refresh-токен зберігається як `sha256` у `core.sessions`** — так само, як
агентські. Витік дампа БД не дає жодної живої сесії.
**RBAC замінює перевірку scopes.** Зараз обробники питають `tok.Can("maps:write")`.
Стане: посередник резолвить `memberships → roles → role_permissions` у набір прав
і кладе в контекст; машинні токени лишаються, але їхні scopes перетинаються з
правами власника. Скоуп груп (`memberships.scope_group_ids`) фільтрує вибірки
пристроїв — це вже не middleware, а предикат у запитах `store`.
### Фронтенд
Сторінка входу, зберігання refresh у httpOnly-cookie, сторінки «Команда» й «Ролі»,
приховування дій без права (кнопка, яка завжди дає 403, гірша за її відсутність).
**Обсяг:** ~23 дні. Ризиків мало: схема готова, візерунок автентифікації в
проєкті вже відпрацьований на агентських токенах.
---
## Етап 6. Шаблони опитування (Zabbix-подібні) — ⚠️ наполовину, 2026-08-24
> **Зроблено:** схема `tpl.*`, реконсиляція шаблонів у `core.checks`,
> звірка планів зонда, REST і редактор у вебі, чотири вбудовані шаблони.
> Перевірено наскрізно на живому net-snmp.
>
> **Додано 2026-08-24:** шаблон описує перевірки будь-якого типу (не лише
> OID), імпорт/експорт глобальний і поштучний, ручні інтервали.
>
> **Додано 2026-08-24:** графіки описуються в шаблоні (шість видів,
> жорсткі межі осей), картка хоста на вкладках без інтервалів.
>
> **Лишилось:** автопризначення за `sysObjectID`, прототипи шаблонів,
> `snmp.walk` як тип елемента (таблиці з динамічним індексом), тригери
> всередині шаблону.
## Етап 6 (початковий план)
**Найбільша нова підсистема.** Зараз `snmp.if`-чеки народжує захардкоджений
`autochecks.go`. Це працює рівно для одного випадку — інтерфейсів. Щойно
знадобиться CPU Cisco, температура MikroTik чи ємність UPS, доведеться дописувати
Go-код під кожен вендор. Шаблони роблять це даними.
### Нова схема: `tpl`
```sql
tpl.templates -- key, name, vendor, is_builtin, tenant_id NULL = вбудований
tpl.template_links -- успадкування шаблон → шаблон
tpl.macros -- {$SNMP_COMMUNITY}, три рівні: глобальний → шаблон → пристрій
tpl.items -- один OID → одна метрика: key, check_type, params, interval,
-- units, value_type, preprocessing, metric_key
tpl.discovery_rules -- LLD: walk по таблиці (ifTable, entPhysicalTable, dot1dBase)
tpl.item_prototypes -- прототипи з {#IFNAME}, {#SNMPINDEX}
tpl.trigger_prototypes -- пороги, що народжують alr.rules
inv.device_templates -- прив'язка шаблон → пристрій
inv.group_templates -- прив'язка шаблон → динамічна група
```
### Рішення, які треба зафіксувати одразу
**Шаблон — декларація, `core.checks` — матеріалізація.** Агент не має знати про
шаблони взагалі: він і далі отримує плаский план задач. Сервер реконсилює
`шаблони × пристрої × LLD → core.checks` при кожній зміні шаблону, складу групи
або результату виявлення. Це зберігає контракт агента незмінним і дозволяє
міняти шаблони без оновлення зондів у полі.
**Low-level discovery — це той самий `snmp.walk`,** результат якого йде не в
метрики, а в реконсиляцію. Тобто `autochecks.go` узагальнюється: замість
«знайшов інтерфейси → створив snmp.if» стане «правило виявлення повернуло
рядки → застосував прототипи → створив items».
**Препроцесинг ділиться між агентом і сервером.** Дельта лічильника (`change per
second`) лишається на агенті: **лише він знає фактичний інтервал** між двома
опитуваннями — це вже реалізовано й перевірено. Решта (множник, регулярка,
JSONPath, `discard unchanged`) — на сервері при записі: інакше кожна зміна
правила вимагала б оновлення агентів.
**Макроси розшифровуються на сервері** й доїжджають до агента вже підставленими,
разом із креденшелами. Секретні макроси (`{$SNMP_COMMUNITY}`) лягають у
`core.secrets` тим самим механізмом, що й паролі.
### Готова база шаблонів
Формат `tpl.*` варто спроєктувати так, щоб він приймав **Zabbix-експорт**
(YAML/JSON з items, discovery rules, prototypes, macros): його структура майже
один-до-одного лягає на запропоновану схему, а публічних шаблонів там сотні.
Це знімає потребу набивати базу вручну.
Команди збору конфігу вже є власним каталогом — `db/profiles/catalog.json`,
147 платформ. Шаблони опитування SNMP — окрема задача, і базу для них варто
починати саме з імпортера, а не з ручного наповнення.
**Обсяг:** ~57 днів на схему + реконсиляцію + імпортер + UI редактора
шаблонів.
---
## Етап 7. NCM — збір конфігів до кінця — ⚠️ майже, 2026-08-25
> **Зроблено:** агентський модуль SSH/Telnet, черга завдань, диспетчер,
> вивантаження зі стисненням і дедуплікацією, візуальний diff у вебі,
> планувальник за cron, Git-двигун на go-git, порівняння з довільною
> версією в UI, переливання історії командою `netpulse-gitsync`.
> Перевірено наскрізно.
>
> **Лишилось:** тригер за Syslog. Він упирається не в тригер: приймача
> syslog немає взагалі — `ts.syslog` і `WriteLogs` у сховищі є, але на
> агенті немає модуля, який слухав би 514/udp, і транспорт до сервера
> не під'єднано. Це окрема робота розміром із власний етап, а не
> дописування умови до наявного коду.
## Етап 7 (початковий план)
Половина шляху вже є: `ncm.*` у схемі, `ConfigJob`/`ConfigUpload` у контракті,
сервер приймає чанки, звіряє sha256, дедуплікує за `content_hash` і шифрує тіло.
Бракує трьох частин.
**Модуль `ncm` на агенті.** SSH через `golang.org/x/crypto/ssh`, Telnet своїм
кодом (протокол тривіальний). Виконує команди з профілю, ловить prompt за
регуляркою, віддає сирий текст чанками. Для `bdcom-olt` і `mikrotik-routeros`
профілі вже в сіді — вони й стануть першими підопічними.
**Планувальник на сервері.** ✅ Cron із `ncm.device_policies.cron` — власний
парсер `internal/cronx`, тік раз на хвилину під advisory-блокуванням. Лишився
тригер за Syslog-подією (`on_syslog`, `%SYS-5-CONFIG_I`) — поле в схемі є,
читати його нікому.
**Git-двигун.**`go-git`, чистий Go без cgo. Голий репозиторій на тенанта,
гілка `refs/heads/device/<device_id>` на пристрій, файл `<пристрій>/<тип>.cfg`.
Однаковий вміст нового коміту не створює. `netpulse-gitsync` переливає вже
накопичену історію й відтворює втрачений репозиторій із бази.
**UI:** ✅ список версій і порівняння з довільною попередньою. Кнопка відкату
лишилась (сутність `ncm.rollbacks` із двоетапним погодженням уже є).
**Обсяг:** ~45 днів.
---
## Етап 8. Керування зондом із UI — ✅ зроблено 2026-08-25
> **Зроблено:** `EnrollmentService` із одноразовими запрошеннями, файл
> посвідчення на агенті, сторінка зондів із командою встановлення,
> керування модулями й лімітами, видалення зонда. Перевірено наскрізно.
>
> **Лишилось:** справжній CA з підписом CSR — контракт це передбачає,
> але власний центр сертифікації з ротацією є окремою системою.
## Етап 8 (початковий план)
Транспорт готовий повністю: сервер уже вміє штовхати `ModuleControl` (які модулі
вмикати), `TaskDelta` (що опитувати), ліміти в `Welcome` (паралельність, розмір
батчу, темп ICMP) і `Directive` (пауза, перезавантаження конфігу, оновлення).
Бракує того, що це все вмикає.
```
POST /api/v1/agents створити зонд + одноразовий enrollment-токен
PATCH /api/v1/agents/{id} ліміти, дозволені модулі, сайт
POST /api/v1/agents/{id}/discover запустити автовиявлення зараз (TriggerNow уже є)
DELETE /api/v1/agents/{id}
```
**`EnrollmentService`** — реалізувати серверну сторону: зонд приходить із
одноразовим токеном і CSR, іде з підписаним сертифікатом. Контракт написано ще на
Етапі 2, реалізації немає. Без цього зонди заводяться `INSERT`ом, що прийнятно на
стенді й неприйнятно у клієнта.
**UI:** сторінка зонда з самометриками (`ts.agent_health` уже наповнюється),
повзунки лімітів, перемикачі модулів, кнопка «запустити виявлення», інструкція
встановлення з готовою командою й токеном.
**Обсяг:** ~23 дні.
---
## Етап 9. Алерти й сповіщення — ✅ зроблено 2026-08-15
> Реалізовано: движок правил (icmp/interface/metric/no_data), антифлап
> вікном, кореляція за топологією, вікна обслуговування й ручне
> заглушення, маршрути з тихими годинами, доставка в Telegram/webhook/
> SMTP, панель алертів у UI. Подробиці — [HISTORY.md](HISTORY.md),
> контракт — [server/API.md](server/API.md).
>
> **Відкладено з цього етапу:** ескалації й повторні сповіщення,
> приймач кнопок Telegram (`callback_data`), Web Push (іде з Етапом 10),
> правила з джерел `syslog`/`trap`/`ncm`/`compliance`.
**Те, без чого це не моніторинг.** Система малює мапу, але мовчить, коли щось
падає. Схема готова з Етапу 1 (`alr.rules`, `alerts`, `channels`, `routes`,
`escalation_policies`, `maintenance_windows`, `mutes`) і повністю порожня.
Дані вже течуть: зміни статусу йдуть через `core.event_outbox`, метрики лежать у
TSDB, пороги описані в `map_edges.thresholds`.
**Движок правил** читає `ts.icmp_samples`, `ts.if_counters` і `ts.samples_5m`,
застосовує `for_seconds` (антифлап) і пише в `alr.alerts`. Дедуплікацію вже
гарантує унікальний індекс `alerts_active_dedup_uniq`.
**Кореляція за топологією** — та, заради якої будувалась `topo.links`: коли падає
маршрутизатор, 40 пристроїв за ним не мають дати 40 сповіщень. Поля
`root_alert_id` і `depends_on_topology` для цього вже є.
**Канали:** Telegram (бот + Mini App із кнопками *Ack*/*Mute*), Web Push, email,
webhook. `alr.push_subscriptions` у схемі готова.
**Обсяг:** ~45 днів.
---
## Етап 10. Мобільна адаптивність і PWA
Зараз UI розрахований лише на десктоп: фіксований сайдбар 240 px, шапка з десятком
елементів в один рядок, **жодного брейкпойнта**, полотно без тач-жестів.
**Адаптив:** сайдбар у висувну панель під `md`, шапка в дві смуги, цілі дотику не
менше 44 px, інспектор вузла — нижнім аркушем замість бічної колонки.
**Полотно на дотик:** React Flow вміє pinch-zoom і pan, але перетягування вузла
пальцем конфліктує з панорамуванням — потрібен явний режим «редагування», інакше
кожна спроба посунути карту рухатиме вузол.
**PWA:** manifest, service worker (кеш оболонки, не даних — застаріла мапа гірша
за її відсутність), Web Push через `alr.push_subscriptions`.
**Telegram Mini App:** перегляд мапи й алертів, кнопки *Ack* / *Mute* / *View
Diff* — усе з ТЗ.
**Обсяг:** ~34 дні на адаптив + PWA, Mini App окремо.
---
## Порядок і чому саме такий
1. ~~**Етап 5 (користувачі)** — без входу продукт не можна віддати нікому.~~
2. ~~**Етап 9 (алерти)** — без сповіщень це не моніторинг.~~
3. **Етап 6 (шаблони)** — знімає потребу дописувати Go під кожен вендор; що
раніше, то менше захардкодженого коду доведеться викидати.
4. ~~**Етап 8 (керування зондом)** — дешевий і робить онбординг можливим.~~
5. **Етап 7 (NCM)** — головний аргумент Enterprise-тарифу.
6. **Етап 10 (мобільний)** — після того, як є що показувати.
Далі за ТЗ лишаються дашборди з NOC TV-режимом, білінг зі Stripe і ліцензійні
ключі — усі три мають готову схему й жодного коду.
## Дрібний борг
- Повторне малювання зв'язку між тією ж парою вузлів дає загальне «такий запис
уже існує» замість зрозумілого пояснення.
- Перетягування вузла через UI не покрите автотестом: синтетичні події не
запускають d3-drag у React Flow. Шлях перевірений тестами сервера.
- Підкладки-плани приміщень віддаються API, але полотно їх не малює — потрібен
прийом і роздача файлів (S3/MinIO або локальний диск).
- Модулі `http`/`ssl` і `modbus` оголошені в сіді плагінів, але не написані.