fix(ui): показывать сведения о рукопожатии без ложного статуса

This commit is contained in:
Denozordec committed 2026-10-04 12:21:53 +07:00
1 parent 837298a171
commit c6702efcc4
9 files changed
+53 -16

No files matched your search

+6 -5
View File
@@ -384,8 +384,8 @@ function WireGuardPageContent({ selectedServerId, selectedInterfaceId, allInterf
}, [scopedIfaces, search])
const totalPeers = scopedIfaces.reduce((s, i) => s + i.peers.length, 0)
const onlinePeers = scopedIfaces.reduce(
(s, i) => s + i.peers.filter((p) => !!p.latestHandshake).length,
const peersWithHandshakeData = scopedIfaces.reduce(
(s, i) => s + i.peers.filter((p) => !!p.latestHandshake?.trim()).length,
0,
)
const upIfaces = scopedIfaces.filter((i) => i.status === "up").length
@@ -754,9 +754,10 @@ function WireGuardPageContent({ selectedServerId, selectedInterfaceId, allInterf
iconClassName: "text-info",
},
{
id: "online",
label: "Пиров онлайн",
value: `${onlinePeers}/${totalPeers}`,
id: "handshake-data",
label: "Данные рукопожатия",
value: `${peersWithHandshakeData}/${totalPeers}`,
hint: <span className="block text-xs text-muted-foreground">Доступность пиров не проверена</span>,
icon: <KeyRoundIcon className="size-4" />,
iconClassName: "text-primary",
},
@@ -86,7 +86,6 @@ function WireguardDataGrid({
cell: ({ row }) => {
const iface = row.original
const expanded = row.getIsExpanded()
const onlinePeers = iface.peers.filter((p) => !!p.latestHandshake).length
return (
<div className="flex items-start gap-2 min-w-0">
<Button type="button" variant="ghost" size="icon" className="size-11 shrink-0" aria-label={"Пиры: " + iface.name} aria-expanded={expanded} onClick={event => { event.stopPropagation(); row.toggleExpanded() }}>
@@ -109,7 +108,7 @@ function WireguardDataGrid({
</div>
) : null}
<p className="sr-only">
{onlinePeers}/{iface.peers.length} пиров
{iface.peers.length} пиров
</p>
</div>
</div>
@@ -164,11 +163,9 @@ function WireguardDataGrid({
enableSorting: false,
cell: ({ row }) => {
const iface = row.original
const onlinePeers = iface.peers.filter((p) => !!p.latestHandshake).length
return (
<span className="font-mono text-sm text-center block">
<span className="text-success">{onlinePeers}</span>
<span className="text-muted-foreground">/{iface.peers.length}</span>
{iface.peers.length}
</span>
)
},
@@ -82,10 +82,10 @@ function WireGuardPeersDetail({
<span
className={cn(
"font-mono text-[11px] whitespace-nowrap",
peer.latestHandshake ? "text-success" : "text-muted-foreground",
"text-muted-foreground",
)}
>
<span className="sr-only">Последнее рукопожатие: </span>{peer.latestHandshake ?? "нет рукопожатия"}
<span className="sr-only">Последнее рукопожатие: </span><span>{peer.latestHandshake?.trim() || "Нет данных"}</span>
</span>
<div className="flex items-center gap-2 text-muted-foreground whitespace-nowrap">
<span aria-label="RX" className="flex items-center gap-0.5">
@@ -97,7 +97,7 @@ function WireGuardPeersDetail({
{fmtBytes(peer.transferTx)}
</span>
</div>
<span className="min-w-0 break-all font-mono text-muted-foreground/60 text-[11px]"><span className="sr-only">Endpoint: </span>{peer.endpoint ?? "—"}</span>
<span className="min-w-0 break-all font-mono text-muted-foreground/60 text-[11px]"><span>Endpoint: </span>{peer.endpoint ?? "—"}</span>
<div className="flex items-center gap-1 justify-end">
{onExportPeer && (
<Button
@@ -141,10 +141,10 @@ function WireguardPeersGrid({
<span
className={cn(
"whitespace-nowrap font-mono text-[11px]",
row.original.latestHandshake ? "text-success" : "text-muted-foreground",
"text-muted-foreground",
)}
>
{row.original.latestHandshake ?? "нет рукопожатия"}
{row.original.latestHandshake?.trim() || "Нет данных"}
</span>
),
meta: {
+1 -1
View File
@@ -4,7 +4,7 @@
## Актуальная точка продолжения — 2026-10-04
Этап 11Б завершён в границах локальной приёмки (11Б.7); внешний выпуск не проверен. 12.1 принят локально: доступность оболочки/клавиатура/health в описанных сценариях. 12.2.1 принят локально: strict BGP catalog/partial/nullable counters. 12.2.2 принят локально: strict traffic DTO и агрегация без выдуманных точек. 12.2.3 принят локально: observations для server traffic от backend до dashboard. 12.2.4 принят локально: strict servers/probes и unknown server status. 12.2.5 принят локально: реальные ping samples/unknown/ось RTT/Recharts. 12.2.6 принят локально: strict GRE/WireGuard/VXLAN и device failures. 12.2.7 принят локально: strict Internet path snapshot/fallback. 12.2.8 принят локально: подтверждение полного backend сбора. 12.2.9 принят локально: метрики участков и наблюдаемый WAN без эвристической подмены. 12.3.1 принят локально:1000 серверов/штатная пагинация. 12.3.2 принят локально: пагинация CompactDataGrid/1000 строк статистики. 12.3.3 принят локально: порционный DOM очереди проблем. 12.3.4 принят локально: большой BGP каталог/страница при обновлении. Production build и123 профильных browser приняты локально на3978976. 12.2.10 принят локально: обязательные RouterOS поля. 12.3.5 принят локально: страница CompactDataGrid сохраняется при опросе,89 browser и повторный production build прошли. 12.3.6 сохранён: baseline1000 speed probes (ограничение DOM пока не выполнено). 12.3.7 принят локально:50 строк speed-проб/сохранение страницы/глобальная сортировка/1000→1; каталог1001 устройства остаётся в DOM. 12.3.8 принят локально:50 строк ресурсов, URL/поиск/сохранение страницы/1000→1; исправлена pagination оболочки с children, production build и8 больших browser прошли. 12.3.9 принят локально:порционное раскрытие1000 предупреждений ресурсов/клавиатура/axe; исходная ошибка ESLint speedDraft подтверждена. 12.3.10 принят локально:недоступные интерфейсы очищаются в обработчиках,54 browser/полный ESLint/production build прошли. 12.3.11 принят локально:ServerTileRail50 устройств/URL/поиск/клавиатура/две площадки/mobile Sheet/axe обе темы;47 browser и production build прошли. 12.3.12 принят локально:BGP ручная страница при выбранной URL сессии/refresh/reload,16 browser/build прошли. 12.4.1 записан:main1091 сценарий/1088 passed/3 устаревших fixtures; оба исправленных файла6 passed. 12.4.2 принят локально:context205/24 файла и unit28 прошли. 12.3.13 принят локально:wide pivot51×16/ScrollArea/axe/24+2 browser/builds. 12.3.14 принят локально:history200/50 DOM/54+1 browser/build. 12.3.15 принят локально:live расписание1000/page20 error cache/15 browser. 12.3.16 принят локально:certificates1000/50 DOM/refresh деталей/19+3 browser/build. 12.3.17 принят локально:pinning идентификатора/видимость значений/29 browser/build. 12.3.18 принят локально:users1000/поиск всего списка/детали и ошибка refresh/32 browser/build. 12.3.19 принят локально:backups1000/все мобильные действия/возврат focus/39+4 browser/build. 12.3.20 принят локально:независимые speed GET/cache/freshness/58+4 browser/build. 12.3.21 принят локально:strict history DTO/null rates от DB до UI/60+5 browser/unit4/builds. 12.4.3 принят локально:users/backups delta context36 на a558412. 12.3.22 принят локально:shared speed-probes DTO/context8/60+6+47 browser/unit5/builds. 12.3.23 принят локально:strict write DTO/atomic replacement/confirmed scheduler warning/65 browser/HTTP DI JWT/builds. 12.3.24 принят локально:OSPF routes1000/50 DOM/page20/cache/320px/22+1 browser/build. 12.3.25 принLine truncated
Этап 11Б завершён в границах локальной приёмки (11Б.7); внешний выпуск не проверен. 12.1 принят локально: доступность оболочки/клавиатура/health в описанных сценариях. 12.2.1 принят локально: strict BGP catalog/partial/nullable counters. 12.2.2 принят локально: strict traffic DTO и агрегация без выдуманных точек. 12.2.3 принят локально: observations для server traffic от backend до dashboard. 12.2.4 принят локально: strict servers/probes и unknown server status. 12.2.5 принят локально: реальные ping samples/unknown/ось RTT/Recharts. 12.2.6 принят локально: strict GRE/WireGuard/VXLAN и device failures. 12.2.7 принят локально: strict Internet path snapshot/fallback. 12.2.8 принят локально: подтверждение полного backend сбора. 12.2.9 принят локально: метрики участков и наблюдаемый WAN без эвристической подмены. 12.3.1 принят локально:1000 серверов/штатная пагинация. 12.3.2 принят локально: пагинация CompactDataGrid/1000 строк статистики. 12.3.3 принят локально: порционный DOM очереди проблем. 12.3.4 принят локально: большой BGP каталог/страница при обновлении. Production build и123 профильных browser приняты локально на3978976. 12.2.10 принят локально: обязательные RouterOS поля. 12.3.5 принят локально: страница CompactDataGrid сохраняется при опросе,89 browser и повторный production build прошли. 12.3.6 сохранён: baseline1000 speed probes (ограничение DOM пока не выполнено). 12.3.7 принят локально:50 строк speed-проб/сохранение страницы/глобальная сортировка/1000→1; каталог1001 устройства остаётся в DOM. 12.3.8 принят локально:50 строк ресурсов, URL/поиск/сохранение страницы/1000→1; исправлена pagination оболочки с children, production build и8 больших browser прошли. 12.3.9 принят локально:порционное раскрытие1000 предупреждений ресурсов/клавиатура/axe; исходная ошибка ESLint speedDraft подтверждена. 12.3.10 принят локально:недоступные интерфейсы очищаются в обработчиках,54 browser/полный ESLint/production build прошли. 12.3.11 принят локально:ServerTileRail50 устройств/URL/поиск/клавиатура/две площадки/mobile Sheet/axe обе темы;47 browser и production build прошли. 12.3.12 принят локально:BGP ручная страница при выбранной URL сессии/refresh/reload,16 browser/build прошли. 12.4.1 записан:main1091 сценарий/1088 passed/3 устаревших fixtures; оба исправленных файла6 passed. 12.4.2 принят локально:context205/24 файла и unit28 прошли. 12.3.13 принят локально:wide pivot51×16/ScrollArea/axe/24+2 browser/builds. 12.3.14 принят локально:history200/50 DOM/54+1 browser/build. 12.3.15 принят локально:live расписание1000/page20 error cache/15 browser. 12.3.16 принят локально:certificates1000/50 DOM/refresh деталей/19+3 browser/build. 12.3.17 принят локально:pinning идентификатора/видимость значений/29 browser/build. 12.3.18 принят локально:users1000/поиск всего списка/детали и ошибка refresh/32 browser/build. 12.3.19 принят локально:backups1000/все мобильные действия/возврат focus/39+4 browser/build. 12.3.20 принят локально:независимые speed GET/cache/freshness/58+4 browser/build. 12.3.21 принят локально:strict history DTO/null rates от DB до UI/60+5 browser/unit4/builds. 12.4.3 принят локально:users/backups delta context36 на a558412. 12.3.22 принят локально:shared speed-probes DTO/context8/60+6+47 browser/unit5/builds. 12.3.23 принят локально:strict write DTO/atomic replacement/confirmed scheduler warning/65 browser/HTTP DI JWT/builds. 12.3.24 принят локально:OSPF routes1000/50 DOM/page20/cache/320px/22+1 browser/build. 12.3.25 принLine truncated
Реальные pg_dump/pg_restore, PG rollback и остановка UDP worker требуют внешней приёмки. Датированные продолжения ниже — история; точный следующий шаг также указан в начале [журнала](ux-ui-progress.md).
+8
View File
@@ -36,6 +36,14 @@
## 12.3.31 — рукопожатие не подменяет доступность WireGuard (2026-10-04)
Убрана оценка «пиров онлайн» по presence старого last-handshake. KPI «Данные рукопожатия» считает непустые сведения в ответе и явно сообщает, что доступность пиров не проверена. Колонка «Пиры» показывает число записей. Старое время рукопожатия отображается без зелёного вывода о доступности; отсутствующее/пустое значение — «Нет данных». В мобильных деталях видна подпись Endpoint.
Основание: официальный [RouterOS WireGuard](https://help.mikrotik.com/docs/spaces/ROS/pages/69664792/WireGuard), last-handshake означает время после последнего успешного рукопожатия. Порогов актуальности/новых правил сетевых операций не введено. Проверены missing/empty/1h/0s в обеих композициях и KPI2/4. Семь прежних/table/large browser прошли, новый handshake сценарий после уточнения adaptive KPI селектора1/1. TypeScript без incremental/ESLint и production frontend build прошли. [Снимок320px](ux-ui/screens/wireguard-handshake-320.png) просмотрен.
Следующий блок12.3.32: WireGuard URL search/tab/status — reload/история/сохранение server/interface/неверные параметры без расширения среза. Первоначально неизвестные общие KPI WireGuard и freshness/source switching требуют отдельного аудита; этот блок исправляет именно смысл handshake.17 остальных specialized grids/общая регрессия после delta/внешняя12.5 остаются. Коммит локальный без push.
## 12.3.30 — порционное раскрытие пиров WireGuard (2026-10-04)
Раскрытие интерфейса ограничено50 пирами на страницу, сохраняет страницу при refresh и ограничивает её при сокращении списка до отображения. Имена и точные IDs действий сохранены. На320px карточки имеют две колонки внутри sticky области шириной экрана; кнопки44px, пагинация доступна с клавиатуры и показывает диапазон/общий счётчик. На ПК сохраняется компактная строка полей.
+9
View File
@@ -425,3 +425,12 @@ Peer grid теперь использует client pagination50, clamp и сох
Следующий блок12.3.31: убрать вывод «онлайн» из наличия старого handshake, показать отсутствие данных отдельно. Официальный RouterOS last-handshake — время после последнего успешного рукопожатия: https://help.mikrotik.com/docs/spaces/ROS/pages/69664792/WireGuard. Затем WireGuard URL filters/context и оставшиеся network grids.17 manualPagination:true grids/общая регрессия после delta/внешняя12.5 остаются. Коммит локальный без push.
## 12.3.31 — рукопожатие не подменяет доступность WireGuard (2026-10-04)
Убрана оценка «пиров онлайн» по presence старого last-handshake. KPI «Данные рукопожатия» считает непустые сведения в ответе и явно сообщает, что доступность пиров не проверена. Колонка «Пиры» показывает число записей. Старое время рукопожатия отображается без зелёного вывода о доступности; отсутствующее/пустое значение — «Нет данных». В мобильных деталях видна подпись Endpoint.
Основание: официальный [RouterOS WireGuard](https://help.mikrotik.com/docs/spaces/ROS/pages/69664792/WireGuard), last-handshake означает время после последнего успешного рукопожатия. Порогов актуальности/новых правил сетевых операций не введено. Проверены missing/empty/1h/0s в обеих композициях и KPI2/4. Семь прежних/table/large browser прошли, новый handshake сценарий после уточнения adaptive KPI селектора1/1. TypeScript без incremental/ESLint и production frontend build прошли. [Снимок320px](ux-ui/screens/wireguard-handshake-320.png) просмотрен.
Следующий блок12.3.32: WireGuard URL search/tab/status — reload/история/сохранение server/interface/неверные параметры без расширения среза. Первоначально неизвестные общие KPI WireGuard и freshness/source switching требуют отдельного аудита; этот блок исправляет именно смысл handshake.17 остальных specialized grids/общая регрессия после delta/внешняя12.5 остаются. Коммит локальный без push.
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

+22
View File
@@ -65,3 +65,25 @@ for (const view of ["details", "peers"] as const) test("WireGuard traffic: obser
if (view === "details") await row.click()
}
})
test("WireGuard handshake: old observation never proves online, missing remains unknown", async ({ page }, info) => {
await setup(page)
const interfaces = fixture.interfaces.slice(0, 1).map(row => ({ ...row, peers: Array.from({length:4}, (_, i) => ({ ...row.peers[0], id: "h"+i, rosId: "*H"+i, name: "Handshake "+i, latestHandshake: [undefined, "", "1h", "0s"][i] })) }))
await page.route("**/api/wireguard", route => route.fulfill({json:wgListResponseSchema.parse({interfaces})}))
await page.reload()
await expect(page.getByText("Пиров онлайн",{exact:true})).toHaveCount(0)
const label=page.getByText("Данные рукопожатия",{exact:true})
await expect(label.locator("..").locator("..")).toContainText("2/4")
await expect(page.getByText("Доступность пиров не проверена",{exact:true}).filter({visible:true})).toBeVisible()
await page.getByRole("button",{name:"Пиры: wg-test-1",exact:true}).click()
const detail=page.getByRole("region",{name:"Пиры: wg-test-1",exact:true})
await expect(detail.getByText("Нет данных",{exact:true})).toHaveCount(2)
await expect(detail.getByText("1h",{exact:true})).toBeVisible()
await page.getByRole("tab",{name:/Пиры/}).click()
const rows=page.locator("tr[data-row-id]")
await expect(rows).toHaveCount(4)
await expect(rows.nth(0)).toContainText("Нет данных");await expect(rows.nth(1)).toContainText("Нет данных")
await expect(rows.nth(2)).toContainText("1h");await expect(rows.nth(3)).toContainText("0s")
await page.setViewportSize({width:320,height:1000});expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth)).toBe(true)
await page.addStyleTag({content:"nextjs-portal{display:none!important}"});await page.screenshot({path:info.outputPath("wireguard-handshake-320.png"),animations:"disabled"})
})