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
- 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.
95 lines
6.5 KiB
Markdown
95 lines
6.5 KiB
Markdown
---
|
||
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 для вложенных сервисов (тот же %, но база = страна).
|