feat(traffic-flow): раскрытие сервисов страны на карте сети
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-test (push) Successful in 2m11s
Docker images / frontend-image (push) Successful in 3m14s
Docker images / updater-image (push) Successful in 43s
Docker images / backend-image (push) Successful in 2m44s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 10s

- Контракт: countryServiceGroups (группа сервисов страны) в flowMapHopsDto, тип FlowMapCountryServiceGroup.
- Бэкенд: вложенная агрегация brand×ISO в том же проходе hops; рёбра страна→сервис, пути с реальным EN; cap 8 / min 4, доля от байтов страны.
- Хелпер mapCountryServiceNodeId: id вида cc:us|svc:google, сервисы разных стран не смешиваются.
- Карта сети: клик по стране сдвигает колонку стран и раскрывает столбец её сервисов; остальные страны приглушены; панели страны и вложенного сервиса.
- Тесты: группы US/US+NL, рёбра от страны, cap/min вложенного слоя; mock countryServiceGroups для демо-режима.
This commit is contained in:
Denozordec
2026-09-12 12:47:30 +07:00
parent f15a7348db
commit 0fdd2fc1e1
7 changed files with 550 additions and 21 deletions
+21
View File
@@ -39,6 +39,7 @@ const W = 1240
const H = 580
const MARGIN = 72
const SERVICE_COL_W = 150
export { SERVICE_COL_W }
/** Карточка конечного сервиса на карте (центр = позиция узла). */
export const MAP_SERVICE_NODE_W = 86
@@ -454,6 +455,26 @@ export function placeServiceNodes(
return out
}
/** Столбец сервисов раскрытой страны: тот же правый x, по вертикали вокруг Y страны. */
export function placeCountryServiceNodes(
serviceIds: string[],
parentPos: { x: number; y: number } | undefined,
): Record<string, { x: number; y: number }> {
const out: Record<string, { x: number; y: number }> = {}
if (serviceIds.length === 0 || !parentPos) return out
const minY = MARGIN + 70
const maxY = H - 72
const x = W - MARGIN - SERVICE_COL_W / 2
const n = serviceIds.length
const gap = Math.min(80, (maxY - minY) / Math.max(1, n))
const span = gap * (n - 1)
const start = clamp(parentPos.y - span / 2, minY, maxY - span)
serviceIds.forEach((id, i) => {
out[id] = { x, y: n === 1 ? clamp(parentPos.y, minY, maxY) : start + i * gap }
})
return out
}
/**
* Суммарная задержка «дом → JH» в миллисекундах: те же поля `Server.latency`, что показываются в разделе Серверы.
* Отдельного ICMP по ребру нет — это не замер линии, а сумма каталожных latency концов.