fix(wireguard): сохранять меню действий при перерисовке
This commit is contained in:
1 parent
4c4ba02b44
commit
6a46fb1e40
13 files changed
+466
-85
No files matched your search
@@ -2,7 +2,7 @@
|
||||
|
||||
import { DataGridContainer, DataGridScrollArea, DataGridTable, dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid"
|
||||
|
||||
import { useEffect, useMemo, type ReactNode } from "react"
|
||||
import { createContext, useContext, useEffect, useMemo, type ReactNode } from "react"
|
||||
import {
|
||||
type ColumnDef,
|
||||
useTable,
|
||||
@@ -58,6 +58,76 @@ interface WireguardDataGridProps {
|
||||
onExportPeer?: (iface: WgIfaceWithServer, peerId: string) => void
|
||||
}
|
||||
|
||||
type WireguardActions = Pick<WireguardDataGridProps, "onExport" | "onAddPeer" | "onToggle" | "toggleBusy" | "onDelete">
|
||||
const WireguardActionsContext = createContext<WireguardActions | null>(null)
|
||||
|
||||
// Preserve menu state while callbacks and the current row data update.
|
||||
function WireguardActionsCell({ row }: { row: { original: WgIfaceWithServer } }) {
|
||||
const actions = useContext(WireguardActionsContext)
|
||||
if (!actions) throw new Error("WireGuard actions context is missing")
|
||||
const { onExport, onAddPeer, onToggle, toggleBusy, onDelete } = actions
|
||||
const iface = row.original
|
||||
return (
|
||||
<div
|
||||
className="flex justify-end"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className={cn(
|
||||
"size-11 shrink-0 border-border/60 bg-background/80 text-muted-foreground shadow-none",
|
||||
"transition-[background-color,color,border-color]",
|
||||
"group-hover/row:opacity-100 focus-visible:opacity-100",
|
||||
"data-popup-open:opacity-100 data-popup-open:bg-muted",
|
||||
)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
aria-label={`Действия: ${iface.name}`}
|
||||
>
|
||||
<MoreHorizontalIcon className="size-4" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent side="bottom" align="end">
|
||||
<DropdownMenuItem onClick={() => onExport(iface)}>
|
||||
<CodeXmlIcon className="size-4" />
|
||||
Экспорт
|
||||
</DropdownMenuItem>
|
||||
{onAddPeer && (
|
||||
<DropdownMenuItem onClick={() => onAddPeer(iface)}>
|
||||
<PlusIcon className="size-4" />
|
||||
Добавить пира
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{onToggle && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem disabled={toggleBusy} onClick={() => onToggle(iface)}>
|
||||
<PowerIcon className="size-4" />
|
||||
{iface.enabled ? "Отключить" : "Включить"}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
{onDelete && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem variant="destructive" onClick={() => onDelete(iface)}>
|
||||
<Trash2Icon className="size-4" />
|
||||
Удалить
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function WireguardDataGrid({
|
||||
interfaces,
|
||||
compactServer = false,
|
||||
@@ -198,68 +268,7 @@ function WireguardDataGrid({
|
||||
{
|
||||
id: "actions",
|
||||
header: () => <span className="sr-only">Действия</span>,
|
||||
cell: ({ row }) => {
|
||||
const iface = row.original
|
||||
return (
|
||||
<div
|
||||
className="flex justify-end"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className={cn(
|
||||
"size-11 shrink-0 border-border/60 bg-background/80 text-muted-foreground shadow-none",
|
||||
"transition-[background-color,color,border-color]",
|
||||
"group-hover/row:opacity-100 focus-visible:opacity-100",
|
||||
"data-popup-open:opacity-100 data-popup-open:bg-muted",
|
||||
)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
aria-label={`Действия: ${iface.name}`}
|
||||
>
|
||||
<MoreHorizontalIcon className="size-4" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<DropdownMenuContent side="bottom" align="end">
|
||||
<DropdownMenuItem onClick={() => onExport(iface)}>
|
||||
<CodeXmlIcon className="size-4" />
|
||||
Экспорт
|
||||
</DropdownMenuItem>
|
||||
{onAddPeer && (
|
||||
<DropdownMenuItem onClick={() => onAddPeer(iface)}>
|
||||
<PlusIcon className="size-4" />
|
||||
Добавить пира
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{onToggle && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem disabled={toggleBusy} onClick={() => onToggle(iface)}>
|
||||
<PowerIcon className="size-4" />
|
||||
{iface.enabled ? "Отключить" : "Включить"}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
{onDelete && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem variant="destructive" onClick={() => onDelete(iface)}>
|
||||
<Trash2Icon className="size-4" />
|
||||
Удалить
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
cell: WireguardActionsCell,
|
||||
enableSorting: false,
|
||||
size: 72,
|
||||
meta: {
|
||||
@@ -268,7 +277,7 @@ function WireguardDataGrid({
|
||||
},
|
||||
},
|
||||
],
|
||||
[compactServer, onExport, onAddPeer, onToggle, toggleBusy, onDelete, onDeletePeer, onExportPeer],
|
||||
[compactServer, onDeletePeer, onExportPeer],
|
||||
)
|
||||
|
||||
const table = useTable({
|
||||
@@ -302,21 +311,23 @@ function WireguardDataGrid({
|
||||
}
|
||||
|
||||
return (
|
||||
<DataGridShell
|
||||
table={table}
|
||||
recordCount={interfaces.length}
|
||||
pagination={interfaces.length > 50 || pageSize !== 50}
|
||||
tableLayout={{ dense: true, columnsPinnable: true }}
|
||||
onRowClick={(row) => table.getRow(JSON.stringify([row.serverId, row.id])).toggleExpanded()}
|
||||
tableClassNames={{
|
||||
headerRow: "border-b border-border",
|
||||
bodyRow: cn("group/row", "[&[data-disabled=true]]:opacity-50"),
|
||||
}}
|
||||
>
|
||||
<DataGridContainer border={false} className="w-full rounded-none border-0">
|
||||
<DataGridScrollArea orientation="horizontal"><DataGridTable /></DataGridScrollArea>
|
||||
</DataGridContainer>
|
||||
</DataGridShell>
|
||||
<WireguardActionsContext.Provider value={{ onExport, onAddPeer, onToggle, toggleBusy, onDelete }}>
|
||||
<DataGridShell
|
||||
table={table}
|
||||
recordCount={interfaces.length}
|
||||
pagination={interfaces.length > 50 || pageSize !== 50}
|
||||
tableLayout={{ dense: true, columnsPinnable: true }}
|
||||
onRowClick={(row) => table.getRow(JSON.stringify([row.serverId, row.id])).toggleExpanded()}
|
||||
tableClassNames={{
|
||||
headerRow: "border-b border-border",
|
||||
bodyRow: cn("group/row", "[&[data-disabled=true]]:opacity-50"),
|
||||
}}
|
||||
>
|
||||
<DataGridContainer border={false} className="w-full rounded-none border-0">
|
||||
<DataGridScrollArea orientation="horizontal"><DataGridTable /></DataGridScrollArea>
|
||||
</DataGridContainer>
|
||||
</DataGridShell>
|
||||
</WireguardActionsContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# UX/UI: протокол внешней приёмки 12.5
|
||||
|
||||
Статус на2026-10-08: backend/frontend3722fad, backend healthy. Мобильная GRE форма и native сохранение на обоих CHR приняты в12.5.57. WireGuard handshake/короткий Ping/native API/read-only UI приняты в границах12.5.58; 12.5.59 воспроизвёл оставшиеся peers/IP; 12.5.60 развернул исправление и принял обычное удаление own WG интерфейса с peer/IPv4 адресом на обоих CHR. Устойчивость меню GRE при live refresh на обоих CHR принята в12.5.55. GRE peer metadata и реальный lifecycle/API/UI/двусторонний Ping подтверждены в12.5.53. Browser retry/checkpoint на реальном HTTP/PG принят в границах12.5.43–45; Telegram sender responses подменены, реальная доставка не проверена. PG-1/2/3/4/5 приняты в описанных границах, PG-6 integrated для одного isolated instance, PG-7 real JWT/processcounts и browser restore passed. PG-8 real PG/native и browser unknown→history done приняты с fault injection после настоящего COMMIT. ROS-8/9 real API/SQL/native приняты в границах12.5.23; browser pair/duration change и delete приняты в12.5.24. ROS-2 authentication failure принят в12.5.25. PortalSSO, физический TCP disconnect, мультиэкземпляры и остальные ROS/Q/интеграции остаются открытыми. [Паспорт](ux-ui-test-server.md), [журнал](ux-ui-progress.md).
|
||||
Статус на2026-10-08: backend/frontend3722fad, backend healthy. Мобильная GRE форма и native сохранение на обоих CHR приняты в12.5.57. WireGuard handshake/короткий Ping/native API/read-only UI приняты в границах12.5.58; 12.5.59 воспроизвёл оставшиеся peers/IP; 12.5.60 развернул исправление и принял обычное удаление own WG интерфейса с peer/IPv4 адресом на обоих CHR. UI создание WG принято в границах12.5.61, WireGuard toggle не завершён; локальная устойчивость его меню ещё не развёрнута. Устойчивость меню GRE при live refresh на обоих CHR принята в12.5.55. GRE peer metadata и реальный lifecycle/API/UI/двусторонний Ping подтверждены в12.5.53. Browser retry/checkpoint на реальном HTTP/PG принят в границах12.5.43–45; Telegram sender responses подменены, реальная доставка не проверена. PG-1/2/3/4/5 приняты в описанных границах, PG-6 integrated для одного isolated instance, PG-7 real JWT/processcounts и browser restore passed. PG-8 real PG/native и browser unknown→history done приняты с fault injection после настоящего COMMIT. ROS-8/9 real API/SQL/native приняты в границах12.5.23; browser pair/duration change и delete приняты в12.5.24. ROS-2 authentication failure принят в12.5.25. PortalSSO, физический TCP disconnect, мультиэкземпляры и остальные ROS/Q/интеграции остаются открытыми. [Паспорт](ux-ui-test-server.md), [журнал](ux-ui-progress.md).
|
||||
|
||||
## Паспорт стенда
|
||||
|
||||
@@ -453,3 +453,15 @@ DELETE200/200, собственные GRE/IP/connected routes удалены; в
|
||||
Итоговый harness exit0: afterBrowserDelete пуст на обеих сторонах, выбранные WG/peers/IP/routes/IPv4 Firewall поля совпали с baseline. Fallback не понадобился для peers/IP/interfaces; ephemeral контейнер удалён, остались три основные Compose-службы. WireGuard revisions намеренно сохранены как история операций. [Smoke](ux-ui/acceptance/deploy-browser-3722fad-2026-10-08.json): dashboard/servers/uptime/settings/filters/alerts200,320px fit, JS/API errors0.
|
||||
|
||||
Приняты успешное обычное удаление own интерфейса с IPv4 адресом и пиром и соответствующий UI результат. Partial failures/read errors/сохранение чужих WG объектов проверены локальными тестами59; на этом стенде такие отказы не инжектировались. IPv6 lifecycle, concurrency/multi-instance, throughput, Telegram/Portal и остальные внешние ограничения остаются открытыми. Следующий12.5.61 — реальные WireGuard действия через UI: создание интерфейса и пира, переключение и очистка собственной пары; ключи не публиковать. Пользовательское правило настоящих VPN объектов сохраняется. Локальный коммит без push.
|
||||
|
||||
## 12.5.61 — UI создание WireGuard и локальное исправление нестабильного меню (2026-10-08)
|
||||
|
||||
[Первый native observer](ux-ui/acceptance/wg-ui-first-attempt-native-2026-10-08.json): собственная пара mm-acceptance-wg-ui-1791472667527, свободные UDP51879/198.18.255.240/30, интерфейсы и пиры созданы только через production формы приложения. Observer не создавал конфигурацию. Оба интерфейса/IP/native key generation и поля peers/endpoint/allowed/keepalive/comment подтвердились, native peer key association и первый двусторонний Ping3/3 прошли. Публичные ключи переданы в форму в памяти, ключи не включены в отчёты. Создание интерфейса наCHR1 выполнено наПК, наCHR2 при320px. [Мобильная форма](ux-ui/screens/wg-ui-create-server2-320-2026-10-08.png) сохранена без ключей и визуально просмотрена; этот снимок второй попытки, не доказательство переключения.
|
||||
|
||||
Полный сценарий НЕ завершён: первая попытка не получила PATCH переключения; вторая, mm-acceptance-wg-ui-1791472879650, после создания интерфейсов остановилась на Добавить пира. Playwright зафиксировал unstable→detached DOM menuitem при перерисовке. Callback props пересоздавали columns/inline actions cell, React сбрасывал Base UI menu. Временные browser harness имеют эти ограничения; успешный финальный browser report не создавался и не заявляется.
|
||||
|
||||
Локально components/data-grids/wireguard-data-grid.tsx адаптирован по уже принятому GRE образцу: стабильный WireguardActionsCell и типизированный context передают свежие onExport/onAddPeer/onToggle/onDelete/toggleBusy. Данные строки остаются текущими; composition, styles, row key и installed ReUI APIs сохранены. Tests/e2e/wireguard-menu-refresh.spec.ts воспроизвёл исчезнувшее меню на прежнем коде; после исправления меню переживает pending refresh, показывает новое enabled state и вызывает PATCH с актуальным disabled=false. Вместе с wireguard-tables.spec.ts —6/6 passed. ESLint и scoped TypeScript passed. Первый TypeScript запуск совпал с генерацией .next/dev/types и получил временные syntax errors; повтор после остановки Next прошёл. ReUI checklist проверен, общие primitives не менялись.
|
||||
|
||||
Первая пара очищена observer, выбранный baseline полностью совпал. Во второй попытке [observer получил401](ux-ui/acceptance/wg-ui-second-observer-error-2026-10-08.json) на завершающем native чтении до cleanup; причина не установлена, пароли не менялись. Повторное чтение через приложение успешно. [Штатный API cleanup](ux-ui/acceptance/wg-ui-second-api-cleanup-2026-10-08.json) проверил точные name/ownership comment и удалил оба own интерфейса DELETE200/200. [Независимая native проверка](ux-ui/acceptance/wg-ui-final-native-cleanup-2026-10-08.json) подтвердила отсутствие own WG/peers/IP/routes на обоих CHR, total IPv4 addresses1/2 и routes4/5; WG interfaces/peers0. Второй baseline был только в памяти и потерян при выходе observer: точное равенство его baseline не заявляется. [DB controls](ux-ui/acceptance/wg-ui-db-controls-2026-10-08.json) servers2/speedHistory9/retention14/migrations9 совпали. Эфемерные контейнеры завершены, остались три основные службы, backend healthy. Audit revisions намеренно сохранены.
|
||||
|
||||
Runtime backend/frontend3722fad, локальное меню ещё не развёрнуто. Следующий12.5.62 — frontend Docker deploy и полный UI create interface/peer→disable/enable→повторный handshake/Ping→delete на новой реальной паре, без повторных открытий меню. Перед повтором observer должен сохранить приватный baseline на сервере и продолжать независимую cleanup каждого устройства при ошибке финального state read; запуск browser только после подтверждения готовности нового observer. Переключение, экспорт/import, throughput/RBAC и прочие внешние ограничения остаются открытыми. Пользовательское правило настоящих VPN объектов сохраняется. Локальный коммит без push.
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
# План реализации UX/UI по пятичасовым окнам
|
||||
|
||||
Актуальное продолжение 2026-10-08: 12.5.60 развернул backend/frontend3722fad и подтвердил исправление удаления WG на новой реальной паре CHR: обычный UI DELETE200/200 удалил интерфейсы, peers и IPv4 addresses без предварительной очистки. Native состояние проверено до fallback, выбранный baseline и DB controls совпали, backend healthy; production builds/TypeScript и smoke6 passed. Следующий12.5.61 — WireGuard через UI: создание интерфейса/пира, переключение и очистка собственной реальной пары; секреты не публиковать. Этап12.5 открыт.
|
||||
Актуальное продолжение 2026-10-08: 12.5.61 подтвердил UI создание WireGuard interfaces/peers, native поля, handshake и первый Ping; переключение не завершено из-за remount меню (DOM detached). Локально WireGuard actions вынесен в стабильный renderer/context, red→green regression и profile6/6/TypeScript/ESLint passed. Обе попытки own объектов очищены, native отсутствие остатков и DB controls подтверждены; transient observer401/recovery отражены отдельно. Runtime backend/frontend3722fad остаётся прежним. Следующий12.5.62 — frontend deploy и полный UI create/peer/toggle/Ping/delete без повторного открытия меню; observer cleanup защитить от ошибок завершающего чтения. Этап12.5 открыт.
|
||||
|
||||
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-08. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
||||
|
||||
## Актуальная точка продолжения — 2026-10-08
|
||||
|
||||
Этапы0–11 завершены в описанных локальных границах. Этап12 открыт. На тестовом сервере161.104.46.222 работают backend/frontend3722fad и два CHR. Внешние проверки подтвердили PG backup/restore/rollback, остановку фоновой работы для одного экземпляра, API JWT/RBAC и браузерные состояния восстановления. Native TCP/UDP speed, история и UI проверены в границах журнала; доступ ко второму CHR восстановлен пользователем. Очередь Telegram проверена на реальном PG: dedupe, конкурентный claim, retry/version, ошибки сохранения и неопределённый исход; ответы отправителя подменялись, реальной доставки это не доказывает. Блоки12.5.43–45 подтвердили browser retry/checkpoint и развернули понятное пояснение HTTP500. 12.5.46 подтвердил browser JWT/RBAC очереди на реальном HTTP/PG; 12.5.47 подтвердил изоляцию checkpoint при смене двух реальных HTTP/PG источников в конфигурации local; 12.5.48 подтвердил scoped axe/клавиатуру в обеих темах и320px; 12.5.49 собрал Q-матрицу и подтвердил отсутствие настройки Telegram; 12.5.50 подтвердил GET-only инвентаризацию: VPN/BGP/OSPF пусты, роли home-router/home-router; По уточнению пользователя12.5.51 создал реальные GRE туннели и подтвердил lifecycle/связность; обнаружена потеря peer IP, 12.5.52 локально исправил сохранение peer metadata, профильные проверки passed; 12.5.53 развернул исправление и подтвердил его на реальной GRE паре/API/UI; 12.5.54 локально исправил устойчивость меню GRE, профильные проверки passed; 12.5.55 развернул frontend731803b и подтвердил устойчивость меню на реальной GRE паре; 12.5.56 локально адаптировал внутренние IP поля GRE для320px, обе темы и профильные проверки passed; 12.5.57 развернул frontendfd521f6 и подтвердил мобильную GRE форму на реальной паре CHR; 12.5.58 подтвердил настоящую WireGuard пару, handshake/Ping/native/UI и финальную очистку; 12.5.59 подтвердил оставшиеся peers/IP после удаления WG и локально исправил backend; 12.5.60 развернул backend/frontend3722fad и подтвердил полное обычное удаление WG; следующий12.5.61 — WireGuard UI создание/переключение собственной реальной пары. Реальная Telegram доставка, Portal SSO, физический TCP disconnect, multi-instance и остальные профильные сетевые сценарии остаются открытыми. [Протокол](ux-ui-external-acceptance.md) и [журнал](ux-ui-stage-12.md) содержат доказательства и ограничения.
|
||||
Этапы0–11 завершены в описанных локальных границах. Этап12 открыт. На тестовом сервере161.104.46.222 работают backend/frontend3722fad и два CHR. Внешние проверки подтвердили PG backup/restore/rollback, остановку фоновой работы для одного экземпляра, API JWT/RBAC и браузерные состояния восстановления. Native TCP/UDP speed, история и UI проверены в границах журнала; доступ ко второму CHR восстановлен пользователем. Очередь Telegram проверена на реальном PG: dedupe, конкурентный claim, retry/version, ошибки сохранения и неопределённый исход; ответы отправителя подменялись, реальной доставки это не доказывает. Блоки12.5.43–45 подтвердили browser retry/checkpoint и развернули понятное пояснение HTTP500. 12.5.46 подтвердил browser JWT/RBAC очереди на реальном HTTP/PG; 12.5.47 подтвердил изоляцию checkpoint при смене двух реальных HTTP/PG источников в конфигурации local; 12.5.48 подтвердил scoped axe/клавиатуру в обеих темах и320px; 12.5.49 собрал Q-матрицу и подтвердил отсутствие настройки Telegram; 12.5.50 подтвердил GET-only инвентаризацию: VPN/BGP/OSPF пусты, роли home-router/home-router; По уточнению пользователя12.5.51 создал реальные GRE туннели и подтвердил lifecycle/связность; обнаружена потеря peer IP, 12.5.52 локально исправил сохранение peer metadata, профильные проверки passed; 12.5.53 развернул исправление и подтвердил его на реальной GRE паре/API/UI; 12.5.54 локально исправил устойчивость меню GRE, профильные проверки passed; 12.5.55 развернул frontend731803b и подтвердил устойчивость меню на реальной GRE паре; 12.5.56 локально адаптировал внутренние IP поля GRE для320px, обе темы и профильные проверки passed; 12.5.57 развернул frontendfd521f6 и подтвердил мобильную GRE форму на реальной паре CHR; 12.5.58 подтвердил настоящую WireGuard пару, handshake/Ping/native/UI и финальную очистку; 12.5.59 подтвердил оставшиеся peers/IP после удаления WG и локально исправил backend; 12.5.60 развернул backend/frontend3722fad и подтвердил полное обычное удаление WG; 12.5.61 подтвердил UI создание WG и локально исправил remount меню; переключение осталось открытым. Следующий12.5.62 — frontend deploy и полный UI lifecycle с независимой защищённой очисткой. Реальная Telegram доставка, Portal SSO, физический TCP disconnect, multi-instance и остальные профильные сетевые сценарии остаются открытыми. [Протокол](ux-ui-external-acceptance.md) и [журнал](ux-ui-stage-12.md) содержат доказательства и ограничения.
|
||||
|
||||
Выполненные внешние сценарии не заменяют приёмку оставшихся интеграций и условий эксплуатации. Датированные продолжения ниже — история; точный следующий шаг указан в начале [журнала](ux-ui-progress.md).
|
||||
|
||||
|
||||
+14
-2
@@ -1,6 +1,6 @@
|
||||
# UX/UI: журнал реализации
|
||||
|
||||
Актуальное продолжение 2026-10-08: 12.5.60 развернул backend/frontend3722fad и подтвердил исправление удаления WG на новой реальной паре CHR: обычный UI DELETE200/200 удалил интерфейсы, peers и IPv4 addresses без предварительной очистки. Native состояние проверено до fallback, выбранный baseline и DB controls совпали, backend healthy; production builds/TypeScript и smoke6 passed. Следующий12.5.61 — WireGuard через UI: создание интерфейса/пира, переключение и очистка собственной реальной пары; секреты не публиковать. Этап12.5 открыт.
|
||||
Актуальное продолжение 2026-10-08: 12.5.61 подтвердил UI создание WireGuard interfaces/peers, native поля, handshake и первый Ping; переключение не завершено из-за remount меню (DOM detached). Локально WireGuard actions вынесен в стабильный renderer/context, red→green regression и profile6/6/TypeScript/ESLint passed. Обе попытки own объектов очищены, native отсутствие остатков и DB controls подтверждены; transient observer401/recovery отражены отдельно. Runtime backend/frontend3722fad остаётся прежним. Следующий12.5.62 — frontend deploy и полный UI create/peer/toggle/Ping/delete без повторного открытия меню; observer cleanup защитить от ошибок завершающего чтения. Этап12.5 открыт.
|
||||
|
||||
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
|
||||
|
||||
@@ -34,7 +34,19 @@
|
||||
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
||||
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
|
||||
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
|
||||
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–60 и ограничения ниже. Runtime backend/frontend3722fad. |
|
||||
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–61 и ограничения ниже. Runtime backend/frontend3722fad. |
|
||||
|
||||
## 12.5.61 — UI создание WireGuard и локальное исправление нестабильного меню (2026-10-08)
|
||||
|
||||
[Первый native observer](ux-ui/acceptance/wg-ui-first-attempt-native-2026-10-08.json): собственная пара mm-acceptance-wg-ui-1791472667527, свободные UDP51879/198.18.255.240/30, интерфейсы и пиры созданы только через production формы приложения. Observer не создавал конфигурацию. Оба интерфейса/IP/native key generation и поля peers/endpoint/allowed/keepalive/comment подтвердились, native peer key association и первый двусторонний Ping3/3 прошли. Публичные ключи переданы в форму в памяти, ключи не включены в отчёты. Создание интерфейса наCHR1 выполнено наПК, наCHR2 при320px. [Мобильная форма](ux-ui/screens/wg-ui-create-server2-320-2026-10-08.png) сохранена без ключей и визуально просмотрена; этот снимок второй попытки, не доказательство переключения.
|
||||
|
||||
Полный сценарий НЕ завершён: первая попытка не получила PATCH переключения; вторая, mm-acceptance-wg-ui-1791472879650, после создания интерфейсов остановилась на Добавить пира. Playwright зафиксировал unstable→detached DOM menuitem при перерисовке. Callback props пересоздавали columns/inline actions cell, React сбрасывал Base UI menu. Временные browser harness имеют эти ограничения; успешный финальный browser report не создавался и не заявляется.
|
||||
|
||||
Локально components/data-grids/wireguard-data-grid.tsx адаптирован по уже принятому GRE образцу: стабильный WireguardActionsCell и типизированный context передают свежие onExport/onAddPeer/onToggle/onDelete/toggleBusy. Данные строки остаются текущими; composition, styles, row key и installed ReUI APIs сохранены. Tests/e2e/wireguard-menu-refresh.spec.ts воспроизвёл исчезнувшее меню на прежнем коде; после исправления меню переживает pending refresh, показывает новое enabled state и вызывает PATCH с актуальным disabled=false. Вместе с wireguard-tables.spec.ts —6/6 passed. ESLint и scoped TypeScript passed. Первый TypeScript запуск совпал с генерацией .next/dev/types и получил временные syntax errors; повтор после остановки Next прошёл. ReUI checklist проверен, общие primitives не менялись.
|
||||
|
||||
Первая пара очищена observer, выбранный baseline полностью совпал. Во второй попытке [observer получил401](ux-ui/acceptance/wg-ui-second-observer-error-2026-10-08.json) на завершающем native чтении до cleanup; причина не установлена, пароли не менялись. Повторное чтение через приложение успешно. [Штатный API cleanup](ux-ui/acceptance/wg-ui-second-api-cleanup-2026-10-08.json) проверил точные name/ownership comment и удалил оба own интерфейса DELETE200/200. [Независимая native проверка](ux-ui/acceptance/wg-ui-final-native-cleanup-2026-10-08.json) подтвердила отсутствие own WG/peers/IP/routes на обоих CHR, total IPv4 addresses1/2 и routes4/5; WG interfaces/peers0. Второй baseline был только в памяти и потерян при выходе observer: точное равенство его baseline не заявляется. [DB controls](ux-ui/acceptance/wg-ui-db-controls-2026-10-08.json) servers2/speedHistory9/retention14/migrations9 совпали. Эфемерные контейнеры завершены, остались три основные службы, backend healthy. Audit revisions намеренно сохранены.
|
||||
|
||||
Runtime backend/frontend3722fad, локальное меню ещё не развёрнуто. Следующий12.5.62 — frontend Docker deploy и полный UI create interface/peer→disable/enable→повторный handshake/Ping→delete на новой реальной паре, без повторных открытий меню. Перед повтором observer должен сохранить приватный baseline на сервере и продолжать независимую cleanup каждого устройства при ошибке финального state read; запуск browser только после подтверждения готовности нового observer. Переключение, экспорт/import, throughput/RBAC и прочие внешние ограничения остаются открытыми. Пользовательское правило настоящих VPN объектов сохраняется. Локальный коммит без push.
|
||||
|
||||
## 12.5.60 — развёртывание и реальное полное удаление WireGuard (2026-10-08)
|
||||
|
||||
|
||||
+13
-1
@@ -1,6 +1,6 @@
|
||||
# Этап 12: итоговая приёмка UX/UI
|
||||
|
||||
Актуальное продолжение 2026-10-08: 12.5.60 развернул backend/frontend3722fad и подтвердил исправление удаления WG на новой реальной паре CHR: обычный UI DELETE200/200 удалил интерфейсы, peers и IPv4 addresses без предварительной очистки. Native состояние проверено до fallback, выбранный baseline и DB controls совпали, backend healthy; production builds/TypeScript и smoke6 passed. Следующий12.5.61 — WireGuard через UI: создание интерфейса/пира, переключение и очистка собственной реальной пары; секреты не публиковать. Этап12.5 открыт.
|
||||
Актуальное продолжение 2026-10-08: 12.5.61 подтвердил UI создание WireGuard interfaces/peers, native поля, handshake и первый Ping; переключение не завершено из-за remount меню (DOM detached). Локально WireGuard actions вынесен в стабильный renderer/context, red→green regression и profile6/6/TypeScript/ESLint passed. Обе попытки own объектов очищены, native отсутствие остатков и DB controls подтверждены; transient observer401/recovery отражены отдельно. Runtime backend/frontend3722fad остаётся прежним. Следующий12.5.62 — frontend deploy и полный UI create/peer/toggle/Ping/delete без повторного открытия меню; observer cleanup защитить от ошибок завершающего чтения. Этап12.5 открыт.
|
||||
|
||||
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
|
||||
|
||||
@@ -1618,3 +1618,15 @@ DELETE200/200, собственные GRE/IP/connected routes удалены; в
|
||||
Итоговый harness exit0: afterBrowserDelete пуст на обеих сторонах, выбранные WG/peers/IP/routes/IPv4 Firewall поля совпали с baseline. Fallback не понадобился для peers/IP/interfaces; ephemeral контейнер удалён, остались три основные Compose-службы. WireGuard revisions намеренно сохранены как история операций. [Smoke](ux-ui/acceptance/deploy-browser-3722fad-2026-10-08.json): dashboard/servers/uptime/settings/filters/alerts200,320px fit, JS/API errors0.
|
||||
|
||||
Приняты успешное обычное удаление own интерфейса с IPv4 адресом и пиром и соответствующий UI результат. Partial failures/read errors/сохранение чужих WG объектов проверены локальными тестами59; на этом стенде такие отказы не инжектировались. IPv6 lifecycle, concurrency/multi-instance, throughput, Telegram/Portal и остальные внешние ограничения остаются открытыми. Следующий12.5.61 — реальные WireGuard действия через UI: создание интерфейса и пира, переключение и очистка собственной пары; ключи не публиковать. Пользовательское правило настоящих VPN объектов сохраняется. Локальный коммит без push.
|
||||
|
||||
## 12.5.61 — UI создание WireGuard и локальное исправление нестабильного меню (2026-10-08)
|
||||
|
||||
[Первый native observer](ux-ui/acceptance/wg-ui-first-attempt-native-2026-10-08.json): собственная пара mm-acceptance-wg-ui-1791472667527, свободные UDP51879/198.18.255.240/30, интерфейсы и пиры созданы только через production формы приложения. Observer не создавал конфигурацию. Оба интерфейса/IP/native key generation и поля peers/endpoint/allowed/keepalive/comment подтвердились, native peer key association и первый двусторонний Ping3/3 прошли. Публичные ключи переданы в форму в памяти, ключи не включены в отчёты. Создание интерфейса наCHR1 выполнено наПК, наCHR2 при320px. [Мобильная форма](ux-ui/screens/wg-ui-create-server2-320-2026-10-08.png) сохранена без ключей и визуально просмотрена; этот снимок второй попытки, не доказательство переключения.
|
||||
|
||||
Полный сценарий НЕ завершён: первая попытка не получила PATCH переключения; вторая, mm-acceptance-wg-ui-1791472879650, после создания интерфейсов остановилась на Добавить пира. Playwright зафиксировал unstable→detached DOM menuitem при перерисовке. Callback props пересоздавали columns/inline actions cell, React сбрасывал Base UI menu. Временные browser harness имеют эти ограничения; успешный финальный browser report не создавался и не заявляется.
|
||||
|
||||
Локально components/data-grids/wireguard-data-grid.tsx адаптирован по уже принятому GRE образцу: стабильный WireguardActionsCell и типизированный context передают свежие onExport/onAddPeer/onToggle/onDelete/toggleBusy. Данные строки остаются текущими; composition, styles, row key и installed ReUI APIs сохранены. Tests/e2e/wireguard-menu-refresh.spec.ts воспроизвёл исчезнувшее меню на прежнем коде; после исправления меню переживает pending refresh, показывает новое enabled state и вызывает PATCH с актуальным disabled=false. Вместе с wireguard-tables.spec.ts —6/6 passed. ESLint и scoped TypeScript passed. Первый TypeScript запуск совпал с генерацией .next/dev/types и получил временные syntax errors; повтор после остановки Next прошёл. ReUI checklist проверен, общие primitives не менялись.
|
||||
|
||||
Первая пара очищена observer, выбранный baseline полностью совпал. Во второй попытке [observer получил401](ux-ui/acceptance/wg-ui-second-observer-error-2026-10-08.json) на завершающем native чтении до cleanup; причина не установлена, пароли не менялись. Повторное чтение через приложение успешно. [Штатный API cleanup](ux-ui/acceptance/wg-ui-second-api-cleanup-2026-10-08.json) проверил точные name/ownership comment и удалил оба own интерфейса DELETE200/200. [Независимая native проверка](ux-ui/acceptance/wg-ui-final-native-cleanup-2026-10-08.json) подтвердила отсутствие own WG/peers/IP/routes на обоих CHR, total IPv4 addresses1/2 и routes4/5; WG interfaces/peers0. Второй baseline был только в памяти и потерян при выходе observer: точное равенство его baseline не заявляется. [DB controls](ux-ui/acceptance/wg-ui-db-controls-2026-10-08.json) servers2/speedHistory9/retention14/migrations9 совпали. Эфемерные контейнеры завершены, остались три основные службы, backend healthy. Audit revisions намеренно сохранены.
|
||||
|
||||
Runtime backend/frontend3722fad, локальное меню ещё не развёрнуто. Следующий12.5.62 — frontend Docker deploy и полный UI create interface/peer→disable/enable→повторный handshake/Ping→delete на новой реальной паре, без повторных открытий меню. Перед повтором observer должен сохранить приватный baseline на сервере и продолжать независимую cleanup каждого устройства при ошибке финального state read; запуск browser только после подтверждения готовности нового observer. Переключение, экспорт/import, throughput/RBAC и прочие внешние ограничения остаются открытыми. Пользовательское правило настоящих VPN объектов сохраняется. Локальный коммит без push.
|
||||
@@ -461,3 +461,15 @@ DELETE200/200, собственные GRE/IP/connected routes удалены; в
|
||||
Итоговый harness exit0: afterBrowserDelete пуст на обеих сторонах, выбранные WG/peers/IP/routes/IPv4 Firewall поля совпали с baseline. Fallback не понадобился для peers/IP/interfaces; ephemeral контейнер удалён, остались три основные Compose-службы. WireGuard revisions намеренно сохранены как история операций. [Smoke](ux-ui/acceptance/deploy-browser-3722fad-2026-10-08.json): dashboard/servers/uptime/settings/filters/alerts200,320px fit, JS/API errors0.
|
||||
|
||||
Приняты успешное обычное удаление own интерфейса с IPv4 адресом и пиром и соответствующий UI результат. Partial failures/read errors/сохранение чужих WG объектов проверены локальными тестами59; на этом стенде такие отказы не инжектировались. IPv6 lifecycle, concurrency/multi-instance, throughput, Telegram/Portal и остальные внешние ограничения остаются открытыми. Следующий12.5.61 — реальные WireGuard действия через UI: создание интерфейса и пира, переключение и очистка собственной пары; ключи не публиковать. Пользовательское правило настоящих VPN объектов сохраняется. Локальный коммит без push.
|
||||
|
||||
## 12.5.61 — UI создание WireGuard и локальное исправление нестабильного меню (2026-10-08)
|
||||
|
||||
[Первый native observer](ux-ui/acceptance/wg-ui-first-attempt-native-2026-10-08.json): собственная пара mm-acceptance-wg-ui-1791472667527, свободные UDP51879/198.18.255.240/30, интерфейсы и пиры созданы только через production формы приложения. Observer не создавал конфигурацию. Оба интерфейса/IP/native key generation и поля peers/endpoint/allowed/keepalive/comment подтвердились, native peer key association и первый двусторонний Ping3/3 прошли. Публичные ключи переданы в форму в памяти, ключи не включены в отчёты. Создание интерфейса наCHR1 выполнено наПК, наCHR2 при320px. [Мобильная форма](ux-ui/screens/wg-ui-create-server2-320-2026-10-08.png) сохранена без ключей и визуально просмотрена; этот снимок второй попытки, не доказательство переключения.
|
||||
|
||||
Полный сценарий НЕ завершён: первая попытка не получила PATCH переключения; вторая, mm-acceptance-wg-ui-1791472879650, после создания интерфейсов остановилась на Добавить пира. Playwright зафиксировал unstable→detached DOM menuitem при перерисовке. Callback props пересоздавали columns/inline actions cell, React сбрасывал Base UI menu. Временные browser harness имеют эти ограничения; успешный финальный browser report не создавался и не заявляется.
|
||||
|
||||
Локально components/data-grids/wireguard-data-grid.tsx адаптирован по уже принятому GRE образцу: стабильный WireguardActionsCell и типизированный context передают свежие onExport/onAddPeer/onToggle/onDelete/toggleBusy. Данные строки остаются текущими; composition, styles, row key и installed ReUI APIs сохранены. Tests/e2e/wireguard-menu-refresh.spec.ts воспроизвёл исчезнувшее меню на прежнем коде; после исправления меню переживает pending refresh, показывает новое enabled state и вызывает PATCH с актуальным disabled=false. Вместе с wireguard-tables.spec.ts —6/6 passed. ESLint и scoped TypeScript passed. Первый TypeScript запуск совпал с генерацией .next/dev/types и получил временные syntax errors; повтор после остановки Next прошёл. ReUI checklist проверен, общие primitives не менялись.
|
||||
|
||||
Первая пара очищена observer, выбранный baseline полностью совпал. Во второй попытке [observer получил401](ux-ui/acceptance/wg-ui-second-observer-error-2026-10-08.json) на завершающем native чтении до cleanup; причина не установлена, пароли не менялись. Повторное чтение через приложение успешно. [Штатный API cleanup](ux-ui/acceptance/wg-ui-second-api-cleanup-2026-10-08.json) проверил точные name/ownership comment и удалил оба own интерфейса DELETE200/200. [Независимая native проверка](ux-ui/acceptance/wg-ui-final-native-cleanup-2026-10-08.json) подтвердила отсутствие own WG/peers/IP/routes на обоих CHR, total IPv4 addresses1/2 и routes4/5; WG interfaces/peers0. Второй baseline был только в памяти и потерян при выходе observer: точное равенство его baseline не заявляется. [DB controls](ux-ui/acceptance/wg-ui-db-controls-2026-10-08.json) servers2/speedHistory9/retention14/migrations9 совпали. Эфемерные контейнеры завершены, остались три основные службы, backend healthy. Audit revisions намеренно сохранены.
|
||||
|
||||
Runtime backend/frontend3722fad, локальное меню ещё не развёрнуто. Следующий12.5.62 — frontend Docker deploy и полный UI create interface/peer→disable/enable→повторный handshake/Ping→delete на новой реальной паре, без повторных открытий меню. Перед повтором observer должен сохранить приватный baseline на сервере и продолжать независимую cleanup каждого устройства при ошибке финального state read; запуск browser только после подтверждения готовности нового observer. Переключение, экспорт/import, throughput/RBAC и прочие внешние ограничения остаются открытыми. Пользовательское правило настоящих VPN объектов сохраняется. Локальный коммит без push.
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"date": "2026-10-08",
|
||||
"before": {
|
||||
"servers": 2,
|
||||
"speedHistory": 9,
|
||||
"retentionDays": 14,
|
||||
"migrations": 9
|
||||
},
|
||||
"after": {
|
||||
"servers": 2,
|
||||
"speedHistory": 9,
|
||||
"retentionDays": 14,
|
||||
"migrations": 9
|
||||
},
|
||||
"unchanged": true
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"date": "2026-10-08",
|
||||
"names": [
|
||||
"mm-acceptance-wg-ui-1791472667527",
|
||||
"mm-acceptance-wg-ui-1791472879650"
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"serverId": 1,
|
||||
"ownInterfaces": 0,
|
||||
"ownPeers": 0,
|
||||
"ownAddresses": 0,
|
||||
"ownRoutes": 0,
|
||||
"totalInterfaces": 0,
|
||||
"totalPeers": 0,
|
||||
"totalAddresses": 1,
|
||||
"totalRoutes": 4
|
||||
},
|
||||
{
|
||||
"serverId": 2,
|
||||
"ownInterfaces": 0,
|
||||
"ownPeers": 0,
|
||||
"ownAddresses": 0,
|
||||
"ownRoutes": 0,
|
||||
"totalInterfaces": 0,
|
||||
"totalPeers": 0,
|
||||
"totalAddresses": 2,
|
||||
"totalRoutes": 5
|
||||
}
|
||||
],
|
||||
"keysOmitted": true,
|
||||
"limits": [
|
||||
"Independent final absence check; original baseline of second attempt lost after observer exit",
|
||||
"First attempt full selected baseline was verified"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
{
|
||||
"date": "2026-10-08",
|
||||
"backendSource": "3722fad",
|
||||
"frontendSource": "3722fad",
|
||||
"name": "mm-acceptance-wg-ui-1791472667527",
|
||||
"listenPort": 51879,
|
||||
"subnet": "198.18.255.240/30",
|
||||
"realRouterOs": true,
|
||||
"pings": [
|
||||
{
|
||||
"round": 1,
|
||||
"results": [
|
||||
{
|
||||
"serverId": 1,
|
||||
"received": 3,
|
||||
"summary": {
|
||||
"sent": "3",
|
||||
"received": "3",
|
||||
"packet-loss": "0",
|
||||
"avg-rtt": "55ms80us"
|
||||
}
|
||||
},
|
||||
{
|
||||
"serverId": 2,
|
||||
"received": 3,
|
||||
"summary": {
|
||||
"sent": "3",
|
||||
"received": "3",
|
||||
"packet-loss": "0",
|
||||
"avg-rtt": "55ms40us"
|
||||
}
|
||||
}
|
||||
],
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"cleanup": [
|
||||
{
|
||||
"serverId": 1,
|
||||
"/interface/wireguard": true,
|
||||
"/interface/wireguard/peers": true,
|
||||
"/ip/address": true,
|
||||
"/ip/route": true,
|
||||
"/ip/firewall/filter": true,
|
||||
"/ip/firewall/nat": true,
|
||||
"/ip/firewall/mangle": true
|
||||
},
|
||||
{
|
||||
"serverId": 2,
|
||||
"/interface/wireguard": true,
|
||||
"/interface/wireguard/peers": true,
|
||||
"/ip/address": true,
|
||||
"/ip/route": true,
|
||||
"/ip/firewall/filter": true,
|
||||
"/ip/firewall/nat": true,
|
||||
"/ip/firewall/mangle": true
|
||||
}
|
||||
],
|
||||
"uiCreatesOnly": true,
|
||||
"nativePeerAssociationConfirmed": true,
|
||||
"connectedState": [
|
||||
{
|
||||
"serverId": 1,
|
||||
"host": "194.226.121.150",
|
||||
"expectedAddress": "198.18.255.241/30",
|
||||
"interface": {
|
||||
"rosId": "*9",
|
||||
"appId": "1:*9",
|
||||
"name": "mm-acceptance-wg-ui-1791472667527",
|
||||
"listenPort": "51879",
|
||||
"mtu": "1420",
|
||||
"disabled": "false",
|
||||
"running": "true",
|
||||
"comment": "Temporary UX acceptance mm-acceptance-wg-ui-1791472667527",
|
||||
"keyGenerated": true
|
||||
},
|
||||
"addresses": [
|
||||
"198.18.255.241/30"
|
||||
],
|
||||
"peers": [
|
||||
{
|
||||
"rosId": "*5",
|
||||
"allowedAddresses": "198.18.255.242/32",
|
||||
"endpoint": "104.165.244.7:51879",
|
||||
"keepalive": "5s",
|
||||
"comment": "Temporary UX acceptance mm-acceptance-wg-ui-1791472667527",
|
||||
"disabled": "false",
|
||||
"handshake": "2s",
|
||||
"rx": "756",
|
||||
"tx": "848"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"serverId": 2,
|
||||
"host": "104.165.244.7",
|
||||
"expectedAddress": "198.18.255.242/30",
|
||||
"interface": {
|
||||
"rosId": "*5",
|
||||
"appId": "2:*5",
|
||||
"name": "mm-acceptance-wg-ui-1791472667527",
|
||||
"listenPort": "51879",
|
||||
"mtu": "1420",
|
||||
"disabled": "false",
|
||||
"running": "true",
|
||||
"comment": "Temporary UX acceptance mm-acceptance-wg-ui-1791472667527",
|
||||
"keyGenerated": true
|
||||
},
|
||||
"addresses": [
|
||||
"198.18.255.242/30"
|
||||
],
|
||||
"peers": [
|
||||
{
|
||||
"rosId": "*3",
|
||||
"allowedAddresses": "198.18.255.241/32",
|
||||
"endpoint": "194.226.121.150:51879",
|
||||
"keepalive": "5s",
|
||||
"comment": "Temporary UX acceptance mm-acceptance-wg-ui-1791472667527",
|
||||
"disabled": "false",
|
||||
"handshake": "2s",
|
||||
"rx": "700",
|
||||
"tx": "756"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"afterBrowser": [
|
||||
{
|
||||
"serverId": 1,
|
||||
"host": "194.226.121.150",
|
||||
"expectedAddress": "198.18.255.241/30",
|
||||
"interface": {
|
||||
"rosId": "*9",
|
||||
"appId": "1:*9",
|
||||
"name": "mm-acceptance-wg-ui-1791472667527",
|
||||
"listenPort": "51879",
|
||||
"mtu": "1420",
|
||||
"disabled": "false",
|
||||
"running": "true",
|
||||
"comment": "Temporary UX acceptance mm-acceptance-wg-ui-1791472667527",
|
||||
"keyGenerated": true
|
||||
},
|
||||
"addresses": [
|
||||
"198.18.255.241/30"
|
||||
],
|
||||
"peers": [
|
||||
{
|
||||
"rosId": "*5",
|
||||
"allowedAddresses": "198.18.255.242/32",
|
||||
"endpoint": "104.165.244.7:51879",
|
||||
"keepalive": "5s",
|
||||
"comment": "Temporary UX acceptance mm-acceptance-wg-ui-1791472667527",
|
||||
"disabled": "false",
|
||||
"handshake": "1m54s",
|
||||
"rx": "1552",
|
||||
"tx": "1028"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"serverId": 2,
|
||||
"host": "104.165.244.7",
|
||||
"expectedAddress": "198.18.255.242/30",
|
||||
"interface": {
|
||||
"rosId": "*5",
|
||||
"appId": "2:*5",
|
||||
"name": "mm-acceptance-wg-ui-1791472667527",
|
||||
"listenPort": "51879",
|
||||
"mtu": "1420",
|
||||
"disabled": "false",
|
||||
"running": "true",
|
||||
"comment": "Temporary UX acceptance mm-acceptance-wg-ui-1791472667527",
|
||||
"keyGenerated": true
|
||||
},
|
||||
"addresses": [
|
||||
"198.18.255.242/30"
|
||||
],
|
||||
"peers": [
|
||||
{
|
||||
"rosId": "*3",
|
||||
"allowedAddresses": "198.18.255.241/32",
|
||||
"endpoint": "194.226.121.150:51879",
|
||||
"keepalive": "5s",
|
||||
"comment": "Temporary UX acceptance mm-acceptance-wg-ui-1791472667527",
|
||||
"disabled": "false",
|
||||
"handshake": "1m54s",
|
||||
"rx": "880",
|
||||
"tx": "1552"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"limits": [
|
||||
"Only own WireGuard resources; no default route or firewall changes",
|
||||
"Public keys transferred to browser only in memory; no keys persisted in report",
|
||||
"Short handshake/ICMP, not throughput or failure/RBAC acceptance",
|
||||
"WireGuard revision audit retained"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"date": "2026-10-08",
|
||||
"name": "mm-acceptance-wg-ui-1791472879650",
|
||||
"results": [
|
||||
{
|
||||
"serverId": "1",
|
||||
"status": 200
|
||||
},
|
||||
{
|
||||
"serverId": "2",
|
||||
"status": 200
|
||||
}
|
||||
],
|
||||
"applicationCatalogAbsent": true,
|
||||
"limits": [
|
||||
"Cleanup recovered after transient observer 401; separate native reads verify no own remnants",
|
||||
"Only exact name/ownership comment fixtures deleted"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"date": "2026-10-08",
|
||||
"name": "mm-acceptance-wg-ui-1791472879650",
|
||||
"observerError": {
|
||||
"status": 401,
|
||||
"path": "/interface/wireguard",
|
||||
"stage": "final native read before cleanup"
|
||||
},
|
||||
"cleanupSkippedByObserver": true,
|
||||
"recoveredViaApplicationDelete": true,
|
||||
"finalNativeOwnObjectsAbsent": true,
|
||||
"limits": [
|
||||
"Cause of transient 401 not established; later app/native reads succeeded",
|
||||
"Passwords were not changed",
|
||||
"Second in-memory baseline was lost; no claim of exact second baseline equality"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
@@ -0,0 +1,35 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
test('WireGuard: меню переживает refresh и использует новое состояние интерфейса', async ({ page }) => {
|
||||
let refreshing = false, release: (() => void) | undefined, patches = 0
|
||||
const gate = new Promise<void>(resolve => { release = resolve })
|
||||
const iface = { id: '1:*A', rosId: '*A', name: 'wg-menu', serverId: '1', serverName: 'CHR', listenPort: 51879, mtu: 1420, comment: '', enabled: true, status: 'up', peers: [] }
|
||||
await page.route('**/health', route => route.fulfill({ json: { ok: true } }))
|
||||
await page.route('**/api/**', async route => {
|
||||
const path = new URL(route.request().url()).pathname
|
||||
if (route.request().method() === 'PATCH') {
|
||||
expect(path).toBe('/api/wireguard/interfaces/1/*A')
|
||||
expect(route.request().postDataJSON()).toEqual({ disabled: false })
|
||||
patches++
|
||||
return route.fulfill({ json: { ok: true } })
|
||||
}
|
||||
if (path === '/api/wireguard') {
|
||||
if (refreshing) await gate
|
||||
return route.fulfill({ json: { interfaces: [{ ...iface, enabled: !refreshing, status: refreshing ? 'down' : 'up' }] } })
|
||||
}
|
||||
return route.fulfill({ json: path === '/api/auth/config' ? { required: false } : path === '/api/servers' ? [{ id: 1, name: 'CHR', host: '192.0.2.1', enabled: true, type: 'home-router', country: 'RU', status: 'online' }] : {} })
|
||||
})
|
||||
await page.goto('/wireguard')
|
||||
const refresh = page.getByRole('button', { name: 'Обновить', exact: true })
|
||||
await expect(refresh).toBeEnabled()
|
||||
await page.getByRole('button', { name: 'Действия: wg-menu' }).click()
|
||||
await expect(page.getByRole('menuitem', { name: 'Отключить', exact: true })).toBeVisible()
|
||||
refreshing = true
|
||||
await refresh.evaluate(e => (e as HTMLButtonElement).click())
|
||||
await expect(refresh).toBeDisabled()
|
||||
await expect(page.getByRole('menuitem', { name: 'Добавить пира', exact: true })).toBeVisible()
|
||||
release!()
|
||||
await expect(refresh).toBeEnabled()
|
||||
await expect(page.getByRole('menuitem', { name: 'Включить', exact: true })).toBeVisible()
|
||||
await page.getByRole('menuitem', { name: 'Включить', exact: true }).click()
|
||||
await expect.poll(() => patches).toBe(1)
|
||||
})
|
||||
Reference in new issue
Block a user