Files
MikrotikManager/страна_раскрывает_сервисы_424c424b.plan.md
Denozordec 7365d8d8fb
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-test (push) Successful in 2m39s
Docker images / frontend-image (push) Successful in 2m56s
Docker images / updater-image (push) Successful in 44s
Docker images / backend-image (push) Successful in 2m35s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 15s
feat(ipsec): enhance IPsec management with peer and certificate handling
- Added support for managing IPsec peers, including editing and deleting functionality.
- Introduced a new UI component for displaying peers and their details within the IPsec server grid.
- Updated the IPsec user creation form to allow binding identities to specific peers.
- Enhanced backend routes and services to handle peer patching and deletion requests.
- Improved data structures to accommodate multiple peers and certificates for each server.
- Added tests to ensure proper functionality of new peer management features.
2026-09-12 21:04:49 +07:00

95 lines
6.5 KiB
Markdown
Raw Permalink 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.
---
name: Страна раскрывает сервисы
overview: Клик по стране на карте оставляет остальные страны на холсте и раскрывает справа столбец сервисов этой страны (доля от трафика страны). Данные — вложенная агрегация brand×ISO в том же hops-проходе.
todos:
- id: contract-nested
content: countryServiceGroups + mapCountryServiceNodeId в контракте/брендах
status: done
- id: backend-nested
content: Агрегация brand×country в hops, рёбра страна→сервис
status: done
- id: backend-nested-tests
content: Тесты групп US/NL, id cc:us|svc:*, fromId=страна
status: done
- id: ui-expand
content: "Клик по стране: сдвиг колонки, столбец сервисов, панель, mock"
status: done
isProject: false
---
# Раскрытие сервисов страны на карте
Эталон холста — [`app/(main)/network-map/page.tsx`](c:/Users/shats/Dev/MikrotikManager-3/app/(main)/network-map/page.tsx) ([Frame](https://reui.io/docs/components/base/frame) не меняем: SVG, не ops-list). UX: **клик по стране → справа узлы сервисов, остальные страны остаются**.
```mermaid
flowchart LR
dest[internetPeer] --> brand[mapInternetBrand]
dest --> geo[ISO country]
brand --> nested["group cc:us"]
geo --> nested
nested --> col["столбец сервисов"]
click[клик по стране] --> col
```
Сейчас hops считает бренд и страну **независимо** в одном цикле по `dstAcc` ([`traffic-flow-map-hops.ts`](c:/Users/shats/Dev/MikrotikManager-3/backend/src/services/traffic-flow-map-hops.ts) ~567–583). Связки «Google внутри US» в API нет — без неё UI может только гадать.
## Поведение на холсте
- Режим **Страны**, слой «Назначения» включён: как сейчас (EN → страны).
- **Клик по стране**: она selected; справа появляется **второй столбец** сервисов этой страны; остальные страны **остаются**, но приглушаются (`opacity` как у `isVis`).
- Рёбра раскрытия: **страна → сервис** (пунктир, те же подписи скорости). EN → страна для выбранной страны подсвечивается.
- Повторный клик по той же стране / клик по пустому холсту / смена `destMode` → свернуть столбец.
- Клик по другой стране → свернуть предыдущий набор и раскрыть новый.
- Клик по сервису: selected = сервис; столбец остаётся; панель — «Сервис в {страна}».
Раскладка в [`lib/network-map-layout.ts`](c:/Users/shats/Dev/MikrotikManager-3/lib/network-map-layout.ts): сейчас одна колонка `x = W - MARGIN - SERVICE_COL_W/2`. При раскрытии:
- страны сдвигаются влево на `SERVICE_COL_W` (чтобы не налезать на Канаду/Вьетнам с скрина);
- сервисы занимают прежний правый `x`, **по вертикали вокруг Y выбранной страны** (новый хелпер `placeCountryServiceNodes(ids, parentPos)`), не общий центр EN.
Доля на узле сервиса = **bytes / bytes страны** (не доля окна): иначе у DE 2% все сервисы будут «0%». В панели дополнительно можно показать долю окна.
Cap вложенного слоя: тот же `pickMapServices`, но `shareBase` = байты страны, cap **8**, min узлов **4**. «Прочее» внутри страны — `svc:other`.
## Контракт
В [`packages/contracts/src/traffic-flow.ts`](c:/Users/shats/Dev/MikrotikManager-3/packages/contracts/src/traffic-flow.ts) optional:
```ts
countryServiceGroups: z.array(z.object({
countryId: z.string(), // cc:us
services: z.array(flowMapServiceDtoSchema),
edges: z.array(flowMapServiceEdgeDtoSchema), // fromId = countryId
paths: z.array(flowMapServicePathDtoSchema), // serviceId = nested id
})).optional()
```
id сервиса во вложении: `cc:us|svc:google` (`mapCountryServiceNodeId`), чтобы Google в US и NL не смешивались в одном payload. Группы только для стран, прошедших `pickMapServices` стран.
## Бэкенд
В том же цикле `dstAcc`: после `svcId` + `country.id` копить `nestedTotals/edges/paths` с ключом `${country.id}|${svcId}`. Рёбра вложенного слоя: `fromId = country.id` (не EN). Пути: как сервисные, `serviceId` = nested id (enId остаётся для подсветки HR→JH→EN).
`finalize` по каждой стране из `countriesOut.nodes`.
## Фронт
- Состояние `expandedCountryId` (не в sessionStorage).
- `visibleNested = groups.find(g => g.countryId === expandedCountryId)`.
- `ServiceNode` для вложенных: `destMode="services"` (бренд), подпись label, доля от страны.
- Панель страны: текущие KPI + короткий список топ-сервисов (клик = тот же expand/select).
- Панель сервиса: бренд + «в {countryName}», доля страны, пути `visibleNested.paths` с `serviceId`.
- Mock: `MOCK_COUNTRY_SERVICE_GROUPS` для `cc:us` (Google / Cloudflare / AWS).
## Тесты
[`traffic-flow-map-hops.test.ts`](c:/Users/shats/Dev/MikrotikManager-3/backend/src/services/traffic-flow-map-hops.test.ts): ripe US → группа `cc:us` с брендом 8.8.8.8; кейс US+NL — сервисы не пересекают `countryId`; nested `edges.fromId` = `cc:us`, не `svc:*`.
Хелпер id — рядом с `mapCountryNodeId` в brands-тесте.
## Вне скоупа
- Одновременное раскрытие двух стран.
- Географическая карта мира.
- Отдельный порог NetFlow для вложенных сервисов (тот же %, но база = страна).