From 7f0c2aad0fb69b37e271e5c0e3730149ae02c2a2 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Sat, 10 Oct 2026 02:06:56 +0700 Subject: [PATCH] =?UTF-8?q?fix(api):=20=D0=B7=D0=B0=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=88=D0=B0=D1=82=D1=8C=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=20=D0=BE=D0=B1=D1=80=D1=8B=D0=B2=D0=B5=20?= =?UTF-8?q?=D0=BE=D1=82=D0=B2=D0=B5=D1=82=D0=B0=20RouterOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/package.json | 2 +- .../src/services/mikrotik-response.test.ts | 61 ++ backend/src/services/mikrotik.ts | 12 + docs/ux-ui-external-acceptance.md | 24 +- docs/ux-ui-plan.md | 4 +- docs/ux-ui-progress.md | 30 +- docs/ux-ui-remaining-acceptance.md | 6 +- docs/ux-ui-stage-12.md | 26 +- docs/ux-ui-test-server.md | 14 +- ...rowser-speed-response-body-2026-10-10.json | 22 + .../live-shell-a11y-2026-10-10.json | 703 ++++++++++++++++++ ...outeros-response-transport-2026-10-10.json | 78 ++ .../speed-response-body-2026-10-10.json | 54 ++ 13 files changed, 1005 insertions(+), 31 deletions(-) create mode 100644 backend/src/services/mikrotik-response.test.ts create mode 100644 docs/ux-ui/acceptance/browser-speed-response-body-2026-10-10.json create mode 100644 docs/ux-ui/acceptance/live-shell-a11y-2026-10-10.json create mode 100644 docs/ux-ui/acceptance/routeros-response-transport-2026-10-10.json create mode 100644 docs/ux-ui/acceptance/speed-response-body-2026-10-10.json diff --git a/backend/package.json b/backend/package.json index 97b3d0a6..3a1d7c5a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -25,7 +25,7 @@ "test:pg": "tsx src/db/sql-bind.test.ts && tsx src/db/sqlite-json.test.ts && tsx src/db/traffic-flags.test.ts && tsx src/db/pg-schema.test.ts && tsx src/services/config-revisions.test.ts", "test:recursive-routes": "tsx src/routes/recursive-routes.test.ts && tsx src/services/recursive-route-catalog.test.ts && tsx src/services/recursive-apply.test.ts", "test:config-sync": "tsx src/services/config-apply-plan.test.ts && tsx src/services/entity-snapshots.test.ts", - "test:ros-file": "tsx src/services/ros-file-contents.test.ts", + "test:ros-file": "tsx src/services/ros-file-contents.test.ts && tsx src/services/mikrotik-response.test.ts", "test:scheduler": "tsx src/services/scheduler-running.test.ts && tsx src/services/scheduler-recorded-run.test.ts && tsx src/routes/scheduler.test.ts", "test:backups": "tsx src/services/backup-scheduler-run.test.ts && tsx src/services/backup-restore.test.ts && tsx src/services/backup-deletion.test.ts && tsx src/services/backup-dto.test.ts && tsx src/services/backup-storage-update.test.ts && tsx src/services/backup-job-execution.test.ts && tsx src/routes/backups-operations.test.ts && tsx src/services/s3-backup-client.test.ts", "test:ipsec": "tsx src/services/ipsec-config.test.ts && tsx src/services/ipsec-state.test.ts && tsx src/routes/ipsec-history.test.ts && tsx src/services/ipsec-restore.test.ts && tsx src/services/ipsec-operation.test.ts && tsx src/routes/ipsec-mutation.test.ts && tsx src/routes/ipsec-nat.test.ts && tsx src/routes/ipsec-group.test.ts", diff --git a/backend/src/services/mikrotik-response.test.ts b/backend/src/services/mikrotik-response.test.ts new file mode 100644 index 00000000..f4e8a634 --- /dev/null +++ b/backend/src/services/mikrotik-response.test.ts @@ -0,0 +1,61 @@ +import { test } from "node:test" +import assert from "node:assert/strict" +import { createServer } from "node:http" +import { MikrotikClient } from "./mikrotik.js" + +const methods = ["get", "post", "put", "patch", "delete", "file"] as const +for (const method of methods) { + test(`RouterOS ${method}: interrupted response rejects promptly without a successful receipt`, async () => { + let requests = 0 + const server = createServer((req, res) => { + req.resume() + req.on("end", () => { + requests++ + res.writeHead(200, { "content-type": "application/json", "content-length": "1000" }) + res.write('[{"status":') + setTimeout(() => res.destroy(), 20) + }) + }) + await new Promise(resolve => server.listen(0, "127.0.0.1", resolve)) + const address = server.address() + assert.ok(address && typeof address !== "string") + const client = new MikrotikClient({ host: "127.0.0.1", port: address.port, useSsl: false, verifySsl: false, username: "fixture", password: "fixture" }) + let deadline: ReturnType | undefined + try { + const operation = method === "file" ? client.downloadFile({ id: "*A", name: "", size: 8 }, 500) + : method === "get" || method === "delete" ? client[method]("/fixture", 500) + : client[method]("/fixture", {}, 500) + await assert.rejects(Promise.race([operation, new Promise((_, reject) => { + deadline = setTimeout(() => reject(Error("Request did not settle after interrupted response")), 1200) + })]), /ECONNRESET/) + assert.equal(requests, method === "file" ? 2 : 1, "Writes must not retry; file reads keep two existing compatibility strategies") + } finally { + clearTimeout(deadline) + await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve())) + } + }) +} + +test("RouterOS complete response: success, explicit HTTP rejection and body survive", async () => { + const server = createServer((req, res) => { + req.resume() + req.on("end", () => { + res.writeHead(req.url?.endsWith("/denied") ? 401 : 200, { "content-type": "application/json" }) + res.end(req.url?.endsWith("/denied") ? '{"message":"Unauthorized"}' : '{"ack":"confirmed"}') + }) + }) + await new Promise(resolve => server.listen(0, "127.0.0.1", resolve)) + const address = server.address() + assert.ok(address && typeof address !== "string") + const client = new MikrotikClient({ host: "127.0.0.1", port: address.port, useSsl: false, verifySsl: false, username: "fixture", password: "fixture" }) + try { + for (const method of ["get", "post", "put", "patch"] as const) { + assert.deepEqual(await (method === "get" ? client.get("/fixture", 500) : client[method]("/fixture", {}, 500)), { ack: "confirmed" }) + await assert.rejects(method === "get" ? client.get("/denied", 500) : client[method]("/denied", {}, 500), /401.*Unauthorized/) + } + await client.delete("/fixture", 500) + await assert.rejects(client.delete("/denied", 500), /401.*Unauthorized/) + } finally { + await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve())) + } +}) diff --git a/backend/src/services/mikrotik.ts b/backend/src/services/mikrotik.ts index 1d743bd4..9766a4c1 100644 --- a/backend/src/services/mikrotik.ts +++ b/backend/src/services/mikrotik.ts @@ -15,6 +15,12 @@ import type { const MAX_ROS_BODY_BYTES = 8 * 1024 * 1024 +function watchRosResponseFailure(res: http.IncomingMessage, reject: (error: Error) => void): void { + res.once("aborted", () => reject(new Error("RouterOS response interrupted (ECONNRESET)"))) + // IncomingMessage errors are separate from ClientRequest errors after headers arrive. + res.once("error", reject) +} + function appendRosBody(body: string, chunk: string, req?: http.ClientRequest): string { if (body.length + chunk.length > MAX_ROS_BODY_BYTES) { req?.destroy(new Error("RouterOS: ответ больше 8 МиБ")) @@ -62,6 +68,7 @@ function rosRequest( }, timeoutMs) const req = lib.request(options, (res) => { + watchRosResponseFailure(res, error => { clearTimeout(timer); reject(error) }) let body = "" res.setEncoding("utf8") res.on("data", (chunk: string) => { body = appendRosBody(body, chunk, req) }) @@ -145,6 +152,7 @@ function rosPost( externalSignal?.addEventListener("abort", onExternalAbort) req = lib.request(options, (res) => { + watchRosResponseFailure(res, error => settle(() => reject(error))) let buf = "" res.setEncoding("utf8") res.on("data", (chunk: string) => { buf = appendRosBody(buf, chunk, req) }) @@ -199,6 +207,7 @@ function rosPut( }, timeoutMs) const req = lib.request(options, (res) => { + watchRosResponseFailure(res, error => { clearTimeout(timer); reject(error) }) let buf = "" res.setEncoding("utf8") res.on("data", (chunk: string) => { buf = appendRosBody(buf, chunk, req) }) @@ -249,6 +258,7 @@ function rosDelete( }, timeoutMs) const req = lib.request(options, (res) => { + watchRosResponseFailure(res, error => { clearTimeout(timer); reject(error) }) let body = "" res.setEncoding("utf8") res.on("data", (chunk: string) => { body = appendRosBody(body, chunk, req) }) @@ -296,6 +306,7 @@ function rosRawRequest( const lib = params.useSsl ? https : http const req = lib.request(options, (res) => { + watchRosResponseFailure(res, error => { clearTimeout(timer); reject(error) }) const chunks: Buffer[] = [] res.on("data", (chunk: Buffer) => { chunks.push(chunk) }) res.on("end", () => { @@ -351,6 +362,7 @@ function rosPatch( }, timeoutMs) const req = lib.request(options, (res) => { + watchRosResponseFailure(res, error => { clearTimeout(timer); reject(error) }) let buf = "" res.setEncoding("utf8") res.on("data", (chunk: string) => { buf = appendRosBody(buf, chunk, req) }) diff --git a/docs/ux-ui-external-acceptance.md b/docs/ux-ui-external-acceptance.md index 1c898645..56a59566 100644 --- a/docs/ux-ui-external-acceptance.md +++ b/docs/ux-ui-external-acceptance.md @@ -1,6 +1,6 @@ # UX/UI: протокол внешней приёмки 12.5 -Актуальное продолжение 2026-10-10: завершены12.5.93–98. На стенде backend ux-speed98/frontend ux-ospf91; неизвестный native результат при transport loss теперь явно объяснён одинаково в API/history/UI, live2/2. Собственные fixtures очищены, credentials не менялись. Следующий12.5.99 — общая backend/main/context регрессия; backend/main уже запущены и пока не объявлены прошедшими. Актуальная внешняя матрица docs/ux-ui-remaining-acceptance.md; этап12.5 открыт. +Актуальное продолжение 2026-10-10: завершены12.5.93–98 и100;99 general main UI regression в работе. Backend ux-response100/frontend ux-ospf91: partial RouterOS response теперь prompt ECONNRESET/unknown receipt,7/7 transport и общая backend регрессия24 groups/171 node cases passed, live2/2/cleanup. 30-route live обзор нашёл нарушения контраста/keyboard;101 исправить после main99, затем context. Актуальная матрица docs/ux-ui-remaining-acceptance.md; этап12.5 открыт. IPsec78: реальный PSK responder established, mode-config198.18.78.10 и online=true подтверждены API; точные собственные объекты удалены. Сертификатный/DN online, late failure и history restore остаются открытыми. [Отчёт](ux-ui/acceptance/ipsec-mode-config-2026-10-09.json), [журнал](ux-ui-progress.md). @@ -138,10 +138,6 @@ BGP-prefix evaluator, отдельное live GRE расписание и recove 12.5.21: отдельная строка unknown/runId, сохранение snapshot и сверка по идентификатору при чтении истории. 13 браузерных тестов с HTTP-моками/typecheck/ESLint прошли; это не приёмка real browser/PG. Основной backend4c12c55/frontend673e5ba прежний. Следующий 12.5.22 — сборка и проверка обновлённых образов. Полный 12.5 открыт. -## Локальный unknown UX — 2026-10-08 - -12.5.21: отдельная строка unknown/runId, сохранение snapshot и сверка по идентификатору при чтении истории. 13 браузерных тестов с HTTP-моками/typecheck/ESLint прошли; это не приёмка real browser/PG. Основной backend4c12c55/frontend673e5ba прежний. Следующий 12.5.22 — сборка и проверка обновлённых образов. Полный 12.5 открыт. - ## Real browser unknown и deployment — 2026-10-08 12.5.22: [real browser speed unknown](ux-ui/acceptance/browser-speed-unknown-2026-10-08.json) подтвердил snapshot42, видимый unknown/runId, reconciliation к SQL history done, снятие предупреждения, native1/POST1/JS0/320fits. HTTP/RouterOS-моков нет; COMMIT acknowledgement fault введён на node-pg boundary, физический network disconnect не проверен. [Оба образа8782054 развёрнуты](ux-ui/acceptance/deployed-8782054-2026-10-08.json), [пять экранов](ux-ui/acceptance/deploy-browser-8782054-2026-10-08.json) прошли. Основная БД не восстанавливалась, контроль сохранён. Следующий12.5.23 — ROS-8/9: preflight/late-context changes на реальном PG/native. Полный12.5 открыт. @@ -935,3 +931,21 @@ API502, history error/TXRX/Pingnull, probe error с прежними42/17/15/0; [Native/API/PG](ux-ui/acceptance/speed-message-2026-10-10.json) и [browser](ux-ui/acceptance/browser-speed-message-2026-10-10.json): повторён только изменённый receipt path, одна capped10Mbit/s TCP3s команда, реальное закрытие socket после передачи, native independently done, API/history содержат новое сообщение; прежние синтетические42/17/15/0 сохранены, native1/noauto retry. Live1280/3202/2, сообщение видно в probe card, reload/document fits/pageerror0/UI command POST0. Свои disabled probe/history удалены, исходные IDs совпали, только3 compose containers/backend/PG healthy; RouterOS/credentials не менялись. При согласовании указателей94–97 обнаружено, что массовая замена также затронула исторический абзац77 в плане/протоколе. Восстановлен точный абзац из40f730b, история/дата2026-10-09 сохранены; следующие обновления ограничены текущими указателями. Следующий99 — одна общая backend/main/context регрессия на текущем source, с восстановлением исходных доказательств, затем итоговая сводка. General backend/main runs уже запущены, их результат этим блоком не заявлен. Квота23% использовано; этап12.5 открыт. Один локальный commit без push. + +## 12.5.99 — общая регрессия и live обзор (2026-10-10) + +**В работе.** Backend98 npm test24 groups/164 node:test cases/exit0 прошёл с недоступным fixture DB URL; это не real-PG приёмка. Main UI regression запущена на3100 с BACKEND_INTERNAL_URL=http://127.0.0.1:1, API responses контролируются test fixtures. JSON reporter выдаёт итог после завершения; main/context пока не объявлены прошедшими. UI production source не меняется во время main run.493 исходных evidence files защищены snapshot, включая чужие изменённые GRE/VXLAN screenshots; восстановление — после main/context. + +[Live обзор](ux-ui/acceptance/live-shell-a11y-2026-10-10.json):30 routes×1280/320=60, HTTP200/route errors0/pageerror0/document overflow0. Два автоматических read-only OSPF preview POST, apply/mutations0. Default dark theme/scoped axe выявил10 нарушений: alerts empty text, certificate badge/pre keyboard, containers hint, uptime hint; filters320 contrast не воспроизвёлся в устойчивом900ms повторе. Дополнительная светлая тема выявила sidebar subtitles и HR badge. Полная WCAG/все интеграции этим обзором не приняты; исправление101 после завершения main run. + +Согласованность журналов: у всех numbered records есть даты, найден и удалён один точный дубликат12.5.21 изprogress/stage12/external; первая запись и дата2026-10-08 сохранены. Другие исторические данные не удалялись. В процессе внешнего HTTP контроля выявлен общий partial-response hang; исправление и отдельная фиксация100 ниже. Следующий шаг99 — дождаться main result, затем context; не перезапускать уже успешные unchanged suites. + +## 12.5.100 — RouterOS: обрыв тела HTTP ответа больше не зависает (2026-10-10) + +У ClientRequest и IncomingMessage разные error events. После получения headers частичный ответ не вызывал request error/end; запрос оставался pending даже после500ms timeout. Изолированная проверка воспроизвела6 failures по deadline1200ms для GET/POST/PUT/PATCH/DELETE/raw file reading; полныйsuccess/401 проходил. Добавлен общий watchRosResponseFailure на aborted/error во всех6 paths, очистка timer и существующий settle/AbortSignal cleanup дляPOST. Partial response отклоняется как ECONNRESET, без выдуманного ACK и повторных writes. Две прежние read-only file compatibility стратегии сохранены. + +[Протокол](ux-ui/acceptance/routeros-response-transport-2026-10-10.json): после изменения7/7 включая complete success/401, probes.cancel/actual HTTP AbortSignal и byte file parser passed. Тест включён в test:ros-file. Локальный production build, новый test ESLint и Docker build прошли. Исходные2 prefer-const errors mikrotik.ts сохраняются, новые ошибки не добавлены; whole-file lint clean не заявлен. Общая backend регрессия повторена именно из-за изменения общей основы:24 npm groups/171 node:test cases/exit0. Console assertion scripts дополнительно входят в npm suite,171 не заявляется числом всех их assertions. + +**Live.** [Native/API/PG](ux-ui/acceptance/speed-response-body-2026-10-10.json), [browser](ux-ui/acceptance/browser-speed-response-body-2026-10-10.json). Relay сначала получил actual RouterOS done testing, затем передал headers/первые16 bytes настоящего JSON и оборвал body. Теперь API502/понятный неизвестный результат вместо pending, история/прежний synthetic snapshot42/17/15/0 согласованы. Один capped10Mbit/s TCP3s test, native1/noauto retry, PC/3202/2/new message/reload/document fits/pageerror0. Свои disabled probe/history удалены, original IDs совпали, credentials/configuration RouterOS не менялись. Это controlled HTTP socket loss после headers, не independent WAN failure/cancelled native command. + +**Runtime.** Backend ux-response100/version1.0.0-test.response100/image797979da82d6…; одна сборка изspeed98 +client/test/package-script files, source archive SHA в отчёте. Entry/node --check/pg_restore18.6/compose --wait passed, приватный .env backup; обновлён только BACKEND_IMAGE_TAG, frontend ux-ospf91/dependencies/migrations прежние. Label response100 — имя блока, не Git SHA. Backend/PG healthy,3 compose containers. Одна локальная фиксация без push; main UI99 всё ещё выполняется на неизменённом frontend source, его итог не заявлен. Квота37% использовано. Следующий101 — подтверждённые нарушения контраста/keyboard code после main99, затем context/финальная сводка. diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md index 7e98c6ed..d71fdd2c 100644 --- a/docs/ux-ui-plan.md +++ b/docs/ux-ui-plan.md @@ -1,12 +1,12 @@ # План реализации UX/UI по пятичасовым окнам -Актуальное продолжение 2026-10-10: завершены12.5.93–98. На стенде backend ux-speed98/frontend ux-ospf91; неизвестный native результат при transport loss теперь явно объяснён одинаково в API/history/UI, live2/2. Собственные fixtures очищены, credentials не менялись. Следующий12.5.99 — общая backend/main/context регрессия; backend/main уже запущены и пока не объявлены прошедшими. Актуальная внешняя матрица docs/ux-ui-remaining-acceptance.md; этап12.5 открыт. +Актуальное продолжение 2026-10-10: завершены12.5.93–98 и100;99 general main UI regression в работе. Backend ux-response100/frontend ux-ospf91: partial RouterOS response теперь prompt ECONNRESET/unknown receipt,7/7 transport и общая backend регрессия24 groups/171 node cases passed, live2/2/cleanup. 30-route live обзор нашёл нарушения контраста/keyboard;101 исправить после main99, затем context. Актуальная матрица docs/ux-ui-remaining-acceptance.md; этап12.5 открыт. Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-10. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат. ## Актуальная точка продолжения — 2026-10-10 -Актуальное продолжение 2026-10-10: завершены12.5.93–98. На стенде backend ux-speed98/frontend ux-ospf91; неизвестный native результат при transport loss теперь явно объяснён одинаково в API/history/UI, live2/2. Собственные fixtures очищены, credentials не менялись. Следующий12.5.99 — общая backend/main/context регрессия; backend/main уже запущены и пока не объявлены прошедшими. Актуальная внешняя матрица docs/ux-ui-remaining-acceptance.md; этап12.5 открыт. +Актуальное продолжение 2026-10-10: завершены12.5.93–98 и100;99 general main UI regression в работе. Backend ux-response100/frontend ux-ospf91: partial RouterOS response теперь prompt ECONNRESET/unknown receipt,7/7 transport и общая backend регрессия24 groups/171 node cases passed, live2/2/cleanup. 30-route live обзор нашёл нарушения контраста/keyboard;101 исправить после main99, затем context. Актуальная матрица docs/ux-ui-remaining-acceptance.md; этап12.5 открыт. Этапы0–11 завершены в локальных границах журнала. История выполненных внешних блоков и доказательства сохранены в [журнале](ux-ui-stage-12.md) и [протоколе](ux-ui-external-acceptance.md). Реальная Telegram доставка, Portal SSO, multi-instance и оставшиеся профильные сетевые ограничения открыты. diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md index b2627c8c..c832c938 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -1,6 +1,6 @@ # UX/UI: журнал реализации -Актуальное продолжение 2026-10-10: завершены12.5.93–98. На стенде backend ux-speed98/frontend ux-ospf91; неизвестный native результат при transport loss теперь явно объяснён одинаково в API/history/UI, live2/2. Собственные fixtures очищены, credentials не менялись. Следующий12.5.99 — общая backend/main/context регрессия; backend/main уже запущены и пока не объявлены прошедшими. Актуальная внешняя матрица docs/ux-ui-remaining-acceptance.md; этап12.5 открыт. +Актуальное продолжение 2026-10-10: завершены12.5.93–98 и100;99 general main UI regression в работе. Backend ux-response100/frontend ux-ospf91: partial RouterOS response теперь prompt ECONNRESET/unknown receipt,7/7 transport и общая backend регрессия24 groups/171 node cases passed, live2/2/cleanup. 30-route live обзор нашёл нарушения контраста/keyboard;101 исправить после main99, затем context. Актуальная матрица docs/ux-ui-remaining-acceptance.md; этап12.5 открыт. Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md). @@ -36,7 +36,25 @@ | 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–98 и ограничения ниже. Runtime backend ux-diagnostics89/frontend ux-ospf91. | +| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–98 и100;99 в работе и ограничения ниже. Runtime backend ux-diagnostics89/frontend ux-ospf91. | + +## 12.5.100 — RouterOS: обрыв тела HTTP ответа больше не зависает (2026-10-10) + +У ClientRequest и IncomingMessage разные error events. После получения headers частичный ответ не вызывал request error/end; запрос оставался pending даже после500ms timeout. Изолированная проверка воспроизвела6 failures по deadline1200ms для GET/POST/PUT/PATCH/DELETE/raw file reading; полныйsuccess/401 проходил. Добавлен общий watchRosResponseFailure на aborted/error во всех6 paths, очистка timer и существующий settle/AbortSignal cleanup дляPOST. Partial response отклоняется как ECONNRESET, без выдуманного ACK и повторных writes. Две прежние read-only file compatibility стратегии сохранены. + +[Протокол](ux-ui/acceptance/routeros-response-transport-2026-10-10.json): после изменения7/7 включая complete success/401, probes.cancel/actual HTTP AbortSignal и byte file parser passed. Тест включён в test:ros-file. Локальный production build, новый test ESLint и Docker build прошли. Исходные2 prefer-const errors mikrotik.ts сохраняются, новые ошибки не добавлены; whole-file lint clean не заявлен. Общая backend регрессия повторена именно из-за изменения общей основы:24 npm groups/171 node:test cases/exit0. Console assertion scripts дополнительно входят в npm suite,171 не заявляется числом всех их assertions. + +**Live.** [Native/API/PG](ux-ui/acceptance/speed-response-body-2026-10-10.json), [browser](ux-ui/acceptance/browser-speed-response-body-2026-10-10.json). Relay сначала получил actual RouterOS done testing, затем передал headers/первые16 bytes настоящего JSON и оборвал body. Теперь API502/понятный неизвестный результат вместо pending, история/прежний synthetic snapshot42/17/15/0 согласованы. Один capped10Mbit/s TCP3s test, native1/noauto retry, PC/3202/2/new message/reload/document fits/pageerror0. Свои disabled probe/history удалены, original IDs совпали, credentials/configuration RouterOS не менялись. Это controlled HTTP socket loss после headers, не independent WAN failure/cancelled native command. + +**Runtime.** Backend ux-response100/version1.0.0-test.response100/image797979da82d6…; одна сборка изspeed98 +client/test/package-script files, source archive SHA в отчёте. Entry/node --check/pg_restore18.6/compose --wait passed, приватный .env backup; обновлён только BACKEND_IMAGE_TAG, frontend ux-ospf91/dependencies/migrations прежние. Label response100 — имя блока, не Git SHA. Backend/PG healthy,3 compose containers. Одна локальная фиксация без push; main UI99 всё ещё выполняется на неизменённом frontend source, его итог не заявлен. Квота37% использовано. Следующий101 — подтверждённые нарушения контраста/keyboard code после main99, затем context/финальная сводка. + +## 12.5.99 — общая регрессия и live обзор (2026-10-10) + +**В работе.** Backend98 npm test24 groups/164 node:test cases/exit0 прошёл с недоступным fixture DB URL; это не real-PG приёмка. Main UI regression запущена на3100 с BACKEND_INTERNAL_URL=http://127.0.0.1:1, API responses контролируются test fixtures. JSON reporter выдаёт итог после завершения; main/context пока не объявлены прошедшими. UI production source не меняется во время main run.493 исходных evidence files защищены snapshot, включая чужие изменённые GRE/VXLAN screenshots; восстановление — после main/context. + +[Live обзор](ux-ui/acceptance/live-shell-a11y-2026-10-10.json):30 routes×1280/320=60, HTTP200/route errors0/pageerror0/document overflow0. Два автоматических read-only OSPF preview POST, apply/mutations0. Default dark theme/scoped axe выявил10 нарушений: alerts empty text, certificate badge/pre keyboard, containers hint, uptime hint; filters320 contrast не воспроизвёлся в устойчивом900ms повторе. Дополнительная светлая тема выявила sidebar subtitles и HR badge. Полная WCAG/все интеграции этим обзором не приняты; исправление101 после завершения main run. + +Согласованность журналов: у всех numbered records есть даты, найден и удалён один точный дубликат12.5.21 изprogress/stage12/external; первая запись и дата2026-10-08 сохранены. Другие исторические данные не удалялись. В процессе внешнего HTTP контроля выявлен общий partial-response hang; исправление и отдельная фиксация100 ниже. Следующий шаг99 — дождаться main result, затем context; не перезапускать уже успешные unchanged suites. ## 12.5.98 — понятный неизвестный результат теста скорости (2026-10-10) @@ -1130,14 +1148,6 @@ PG-8 browser unknown/reconciliation принят в этих границах. P Основной backend4c12c55/frontend673e5ba не изменён. Новые backend/frontend исправления ещё не развёрнуты. Полный 12.5 открыт. Следующий 12.5.22 — сборка обновлённых образов и приёмка на стенде перед deployment. Локальный коммит без push. -## 12.5.21 — неопределённый исход speed-запуска и сверка с историей (2026-10-08) - -В локальной строке запуска введён status unknown: «Сохранение не подтверждено», с видимым runId; это не новый статус серверного DTO (история API по-прежнему принимает только done/error). При обновлении истории незавершённая или неподтверждённая локальная строка сохраняется, если её идентификатора нет в ответе. Подтверждённая строка с тем же runId заменяет локальную, предупреждение снимается. Unknown не подменяет последний подтверждённый snapshot карточки пробы; предупреждение показано без destructive-оформления. Автоматического повторного сетевого теста нет. Ручной новый запуск остаётся доступен; предупреждение просит проверить историю перед повтором. Состояние локальной строки не сохраняется после перезагрузки страницы. - -Проверки: 13/13 Playwright (uptime-speed-data и uptime-speed-unknown) за 27.7s, включая unknown → пустая история → done/error, сохранение TX42, один POST и адаптацию 320px. Это live-ветка UI с HTTP-моками, не real PG/browser proof. Scoped frontend typecheck (tmp/tsconfig-speed-check.json, исключён старый ignored tmp build context) и ESLint изменённых файлов прошли. Существующие null/zero, malformed DTO, свежесть, независимые источники и Ping-состояния сохранены. - -Основной backend4c12c55/frontend673e5ba не изменён. Новые backend/frontend исправления ещё не развёрнуты. Полный 12.5 открыт. Следующий 12.5.22 — сборка обновлённых образов и приёмка на стенде перед deployment. Локальный коммит без push. - ## 12.5.20 — сохранение подтверждённого speed snapshot при unknown receipt (2026-10-08) Приостатке5%окнавыполненнебольшойлокальныйблокпередdeployment. Frontendcatch раньше ставилlastStatuserror/новыйlastRunAt любойошибкеPOSTspeed-test,включаяunknowncommit. Теперьподтверждённыйsnapshot пробысохраняется,еслиApiClientError502 содержитoutcomeunknown иrunId текущегозапроса. ЧужойrunId/неправильныйpayload/403/обычнаяошибка нераспознаютсякакэтотисход. Существующееservererrorсообщениепоказывается;новыекомпоненты/композицияReUIне менялись. Временнаястрокасамогозапросапо-прежнемуимеетstatuserror; полныйunknown-state/browserUX ещёнужнопроверить. diff --git a/docs/ux-ui-remaining-acceptance.md b/docs/ux-ui-remaining-acceptance.md index 7b2e8b2b..8ed83200 100644 --- a/docs/ux-ui-remaining-acceptance.md +++ b/docs/ux-ui-remaining-acceptance.md @@ -1,8 +1,8 @@ # Оставшаяся внешняя приёмка — 2026-10-10 -Это актуальная матрица после 12.5.98. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. +Это актуальная матрица после 12.5.100 (99 main/context в работе). Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. -Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-speed98`, frontend `ux-ospf91`; production lab использует AUTH_REQUIRED=false. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты последнего сценария удалены, исходные11 коллекций×2 восстановлены; пароли не менялись. +Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-response100`, frontend `ux-ospf91`; production lab использует AUTH_REQUIRED=false. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты последнего сценария удалены, исходные11 коллекций×2 восстановлены; пароли не менялись. | Раздел | Подтверждено | Осталось / условие завершения | | --- | --- | --- | @@ -25,7 +25,7 @@ ## Следующая последовательность 1. **12.5.95 завершён:** [source/context/compiled audit](ux-ui/acceptance/deployment-source-audit-2026-10-10.json), production source drift не выявлен; нормализация и исключения явно указаны. -2. **12.5.96 timezone завершён:** [6 live UI cases](ux-ui/acceptance/browser-speed-timezone-2026-10-10.json). 97 HTTP receipt disconnect принят: [протокол](ux-ui/acceptance/speed-disconnect-2026-10-10.json). 98 сообщение неизвестного native результата исправлено/развёрнуто. Следующий99 — общая регрессия. +2. **12.5.96 timezone завершён:** [6 live UI cases](ux-ui/acceptance/browser-speed-timezone-2026-10-10.json). 97 HTTP receipt disconnect принят: [протокол](ux-ui/acceptance/speed-disconnect-2026-10-10.json). 98 сообщение неизвестного native результата исправлено/развёрнуто. 99 main/context в работе;100 partial-response hang исправлен/развёрнут. 101 — реальные a11y нарушения после main. 3. **Заключительный профильный контроль:** после последнего исправления собрать только затронутую часть, проверить её live и обновить матрицу. Общую регрессию выполнить один раз на согласованном runtime. 4. **Внешние зависимости:** Telegram/Portal/client OS/multi-WAN/scale проходят отдельную приёмку при наличии нужных аккаунтов, устройств и разрешений. Лабораторный результат двух CHR не закрывает их автоматически. diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md index 9a7da907..14163b6f 100644 --- a/docs/ux-ui-stage-12.md +++ b/docs/ux-ui-stage-12.md @@ -1163,14 +1163,6 @@ SchedulerSnapshotError раньшеигнорировалruns[].okfalse уuptime Основной backend4c12c55/frontend673e5ba не изменён. Новые backend/frontend исправления ещё не развёрнуты. Полный 12.5 открыт. Следующий 12.5.22 — сборка обновлённых образов и приёмка на стенде перед deployment. Локальный коммит без push. -## 12.5.21 — неопределённый исход speed-запуска и сверка с историей (2026-10-08) - -В локальной строке запуска введён status unknown: «Сохранение не подтверждено», с видимым runId; это не новый статус серверного DTO (история API по-прежнему принимает только done/error). При обновлении истории незавершённая или неподтверждённая локальная строка сохраняется, если её идентификатора нет в ответе. Подтверждённая строка с тем же runId заменяет локальную, предупреждение снимается. Unknown не подменяет последний подтверждённый snapshot карточки пробы; предупреждение показано без destructive-оформления. Автоматического повторного сетевого теста нет. Ручной новый запуск остаётся доступен; предупреждение просит проверить историю перед повтором. Состояние локальной строки не сохраняется после перезагрузки страницы. - -Проверки: 13/13 Playwright (uptime-speed-data и uptime-speed-unknown) за 27.7s, включая unknown → пустая история → done/error, сохранение TX42, один POST и адаптацию 320px. Это live-ветка UI с HTTP-моками, не real PG/browser proof. Scoped frontend typecheck (tmp/tsconfig-speed-check.json, исключён старый ignored tmp build context) и ESLint изменённых файлов прошли. Существующие null/zero, malformed DTO, свежесть, независимые источники и Ping-состояния сохранены. - -Основной backend4c12c55/frontend673e5ba не изменён. Новые backend/frontend исправления ещё не развёрнуты. Полный 12.5 открыт. Следующий 12.5.22 — сборка обновлённых образов и приёмка на стенде перед deployment. Локальный коммит без push. - ## 12.5.22 — новые Docker-образы и real browser speed unknown (2026-10-08) Из зафиксированного source8782054078304f36cc611876af2d17f87b857f60 подготовлены отдельные frontend/backend contexts и последовательно собраны production-образы на Debian-стенде. Frontend production build и проверка TypeScript внутри Docker прошли; backend contracts/TypeScript build прошёл. Незакоммиченные изменения, .env, ключи и локальные node_modules в сборку не передавались. Образы не публиковались. @@ -2244,3 +2236,21 @@ API502, history error/TXRX/Pingnull, probe error с прежними42/17/15/0; [Native/API/PG](ux-ui/acceptance/speed-message-2026-10-10.json) и [browser](ux-ui/acceptance/browser-speed-message-2026-10-10.json): повторён только изменённый receipt path, одна capped10Mbit/s TCP3s команда, реальное закрытие socket после передачи, native independently done, API/history содержат новое сообщение; прежние синтетические42/17/15/0 сохранены, native1/noauto retry. Live1280/3202/2, сообщение видно в probe card, reload/document fits/pageerror0/UI command POST0. Свои disabled probe/history удалены, исходные IDs совпали, только3 compose containers/backend/PG healthy; RouterOS/credentials не менялись. При согласовании указателей94–97 обнаружено, что массовая замена также затронула исторический абзац77 в плане/протоколе. Восстановлен точный абзац из40f730b, история/дата2026-10-09 сохранены; следующие обновления ограничены текущими указателями. Следующий99 — одна общая backend/main/context регрессия на текущем source, с восстановлением исходных доказательств, затем итоговая сводка. General backend/main runs уже запущены, их результат этим блоком не заявлен. Квота23% использовано; этап12.5 открыт. Один локальный commit без push. + +## 12.5.99 — общая регрессия и live обзор (2026-10-10) + +**В работе.** Backend98 npm test24 groups/164 node:test cases/exit0 прошёл с недоступным fixture DB URL; это не real-PG приёмка. Main UI regression запущена на3100 с BACKEND_INTERNAL_URL=http://127.0.0.1:1, API responses контролируются test fixtures. JSON reporter выдаёт итог после завершения; main/context пока не объявлены прошедшими. UI production source не меняется во время main run.493 исходных evidence files защищены snapshot, включая чужие изменённые GRE/VXLAN screenshots; восстановление — после main/context. + +[Live обзор](ux-ui/acceptance/live-shell-a11y-2026-10-10.json):30 routes×1280/320=60, HTTP200/route errors0/pageerror0/document overflow0. Два автоматических read-only OSPF preview POST, apply/mutations0. Default dark theme/scoped axe выявил10 нарушений: alerts empty text, certificate badge/pre keyboard, containers hint, uptime hint; filters320 contrast не воспроизвёлся в устойчивом900ms повторе. Дополнительная светлая тема выявила sidebar subtitles и HR badge. Полная WCAG/все интеграции этим обзором не приняты; исправление101 после завершения main run. + +Согласованность журналов: у всех numbered records есть даты, найден и удалён один точный дубликат12.5.21 изprogress/stage12/external; первая запись и дата2026-10-08 сохранены. Другие исторические данные не удалялись. В процессе внешнего HTTP контроля выявлен общий partial-response hang; исправление и отдельная фиксация100 ниже. Следующий шаг99 — дождаться main result, затем context; не перезапускать уже успешные unchanged suites. + +## 12.5.100 — RouterOS: обрыв тела HTTP ответа больше не зависает (2026-10-10) + +У ClientRequest и IncomingMessage разные error events. После получения headers частичный ответ не вызывал request error/end; запрос оставался pending даже после500ms timeout. Изолированная проверка воспроизвела6 failures по deadline1200ms для GET/POST/PUT/PATCH/DELETE/raw file reading; полныйsuccess/401 проходил. Добавлен общий watchRosResponseFailure на aborted/error во всех6 paths, очистка timer и существующий settle/AbortSignal cleanup дляPOST. Partial response отклоняется как ECONNRESET, без выдуманного ACK и повторных writes. Две прежние read-only file compatibility стратегии сохранены. + +[Протокол](ux-ui/acceptance/routeros-response-transport-2026-10-10.json): после изменения7/7 включая complete success/401, probes.cancel/actual HTTP AbortSignal и byte file parser passed. Тест включён в test:ros-file. Локальный production build, новый test ESLint и Docker build прошли. Исходные2 prefer-const errors mikrotik.ts сохраняются, новые ошибки не добавлены; whole-file lint clean не заявлен. Общая backend регрессия повторена именно из-за изменения общей основы:24 npm groups/171 node:test cases/exit0. Console assertion scripts дополнительно входят в npm suite,171 не заявляется числом всех их assertions. + +**Live.** [Native/API/PG](ux-ui/acceptance/speed-response-body-2026-10-10.json), [browser](ux-ui/acceptance/browser-speed-response-body-2026-10-10.json). Relay сначала получил actual RouterOS done testing, затем передал headers/первые16 bytes настоящего JSON и оборвал body. Теперь API502/понятный неизвестный результат вместо pending, история/прежний synthetic snapshot42/17/15/0 согласованы. Один capped10Mbit/s TCP3s test, native1/noauto retry, PC/3202/2/new message/reload/document fits/pageerror0. Свои disabled probe/history удалены, original IDs совпали, credentials/configuration RouterOS не менялись. Это controlled HTTP socket loss после headers, не independent WAN failure/cancelled native command. + +**Runtime.** Backend ux-response100/version1.0.0-test.response100/image797979da82d6…; одна сборка изspeed98 +client/test/package-script files, source archive SHA в отчёте. Entry/node --check/pg_restore18.6/compose --wait passed, приватный .env backup; обновлён только BACKEND_IMAGE_TAG, frontend ux-ospf91/dependencies/migrations прежние. Label response100 — имя блока, не Git SHA. Backend/PG healthy,3 compose containers. Одна локальная фиксация без push; main UI99 всё ещё выполняется на неизменённом frontend source, его итог не заявлен. Квота37% использовано. Следующий101 — подтверждённые нарушения контраста/keyboard code после main99, затем context/финальная сводка. diff --git a/docs/ux-ui-test-server.md b/docs/ux-ui-test-server.md index 80d62f1b..0ee253c9 100644 --- a/docs/ux-ui-test-server.md +++ b/docs/ux-ui-test-server.md @@ -1,6 +1,6 @@ # Тестовый сервер UX/UI — 2026-10-10 -Сервер161.104.46.222 предоставлен для временного тестирования. Backend ux-speed98/version1.0.0-test.speed98, frontend ux-ospf91/version1.0.0-test.ospf91; backend/PG healthy, frontend HTTP/browser passed. Основной блок IPsec77 принят в границах журнала, own fixtures/fake DB rows удалены; каталог flash CHR2 сохранён из-за неподтверждённого ownership. SSH/оба CHR доступны, пароли прежние. +Сервер161.104.46.222 предоставлен для временного тестирования. Backend ux-response100/version1.0.0-test.response100, frontend ux-ospf91/version1.0.0-test.ospf91; backend/PG healthy, frontend HTTP/browser passed. Основной блок IPsec77 принят в границах журнала, own fixtures/fake DB rows удалены; каталог flash CHR2 сохранён из-за неподтверждённого ownership. SSH/оба CHR доступны, пароли прежние. | Параметр | Проверенное состояние | | --- | --- | @@ -14,7 +14,7 @@ | PostgreSQL | postgres:18-alpine,18.6;mmapp-test-postgres healthy;SELECT version() passed | | Данные | Отдельный Compose project mmapp-test,том mmapp-test_mmapp-pgdata и каталог ./data | | Доступ | PostgreSQL не публикуется;frontend3000/backend8000 привязаны к127.0.0.1 после запуска | -| Образы приложения | mmapp-test/backend:ux-speed98 и mmapp-test/frontend:ux-ospf91; запущены | +| Образы приложения | mmapp-test/backend:ux-response100 и mmapp-test/frontend:ux-ospf91; запущены | Compose SHA256:3052bd6ab5d9865f97605cebfb5ff27a4410cd3357b81153a65a87fb7c92582f. OptionalBACKEND_IMAGE_TAG позволяетобновлятьbackendотдельно. Локальный и удалённый файлы совпали. Шаблоны: [Compose](../deploy/docker-compose.test.yml), [env без секретов](../deploy/env.test.example). @@ -757,3 +757,13 @@ API502, history error/TXRX/Pingnull, probe error с прежними42/17/15/0; [Native/API/PG](ux-ui/acceptance/speed-message-2026-10-10.json) и [browser](ux-ui/acceptance/browser-speed-message-2026-10-10.json): повторён только изменённый receipt path, одна capped10Mbit/s TCP3s команда, реальное закрытие socket после передачи, native independently done, API/history содержат новое сообщение; прежние синтетические42/17/15/0 сохранены, native1/noauto retry. Live1280/3202/2, сообщение видно в probe card, reload/document fits/pageerror0/UI command POST0. Свои disabled probe/history удалены, исходные IDs совпали, только3 compose containers/backend/PG healthy; RouterOS/credentials не менялись. При согласовании указателей94–97 обнаружено, что массовая замена также затронула исторический абзац77 в плане/протоколе. Восстановлен точный абзац из40f730b, история/дата2026-10-09 сохранены; следующие обновления ограничены текущими указателями. Следующий99 — одна общая backend/main/context регрессия на текущем source, с восстановлением исходных доказательств, затем итоговая сводка. General backend/main runs уже запущены, их результат этим блоком не заявлен. Квота23% использовано; этап12.5 открыт. Один локальный commit без push. + +## 12.5.100 — RouterOS: обрыв тела HTTP ответа больше не зависает (2026-10-10) + +У ClientRequest и IncomingMessage разные error events. После получения headers частичный ответ не вызывал request error/end; запрос оставался pending даже после500ms timeout. Изолированная проверка воспроизвела6 failures по deadline1200ms для GET/POST/PUT/PATCH/DELETE/raw file reading; полныйsuccess/401 проходил. Добавлен общий watchRosResponseFailure на aborted/error во всех6 paths, очистка timer и существующий settle/AbortSignal cleanup дляPOST. Partial response отклоняется как ECONNRESET, без выдуманного ACK и повторных writes. Две прежние read-only file compatibility стратегии сохранены. + +[Протокол](ux-ui/acceptance/routeros-response-transport-2026-10-10.json): после изменения7/7 включая complete success/401, probes.cancel/actual HTTP AbortSignal и byte file parser passed. Тест включён в test:ros-file. Локальный production build, новый test ESLint и Docker build прошли. Исходные2 prefer-const errors mikrotik.ts сохраняются, новые ошибки не добавлены; whole-file lint clean не заявлен. Общая backend регрессия повторена именно из-за изменения общей основы:24 npm groups/171 node:test cases/exit0. Console assertion scripts дополнительно входят в npm suite,171 не заявляется числом всех их assertions. + +**Live.** [Native/API/PG](ux-ui/acceptance/speed-response-body-2026-10-10.json), [browser](ux-ui/acceptance/browser-speed-response-body-2026-10-10.json). Relay сначала получил actual RouterOS done testing, затем передал headers/первые16 bytes настоящего JSON и оборвал body. Теперь API502/понятный неизвестный результат вместо pending, история/прежний synthetic snapshot42/17/15/0 согласованы. Один capped10Mbit/s TCP3s test, native1/noauto retry, PC/3202/2/new message/reload/document fits/pageerror0. Свои disabled probe/history удалены, original IDs совпали, credentials/configuration RouterOS не менялись. Это controlled HTTP socket loss после headers, не independent WAN failure/cancelled native command. + +**Runtime.** Backend ux-response100/version1.0.0-test.response100/image797979da82d6…; одна сборка изspeed98 +client/test/package-script files, source archive SHA в отчёте. Entry/node --check/pg_restore18.6/compose --wait passed, приватный .env backup; обновлён только BACKEND_IMAGE_TAG, frontend ux-ospf91/dependencies/migrations прежние. Label response100 — имя блока, не Git SHA. Backend/PG healthy,3 compose containers. Одна локальная фиксация без push; main UI99 всё ещё выполняется на неизменённом frontend source, его итог не заявлен. Квота37% использовано. Следующий101 — подтверждённые нарушения контраста/keyboard code после main99, затем context/финальная сводка. diff --git a/docs/ux-ui/acceptance/browser-speed-response-body-2026-10-10.json b/docs/ux-ui/acceptance/browser-speed-response-body-2026-10-10.json new file mode 100644 index 00000000..2f4dd0f2 --- /dev/null +++ b/docs/ux-ui/acceptance/browser-speed-response-body-2026-10-10.json @@ -0,0 +1,22 @@ +{ + "date": "2026-10-10", + "cases": [ + { + "width": 1280, + "errorHistoryVisible": true, + "previousSnapshot42and17Preserved": true, + "reload": true, + "documentFits": true + }, + { + "width": 320, + "errorHistoryVisible": true, + "previousSnapshot42and17Preserved": true, + "reload": true, + "documentFits": true + } + ], + "errors": [], + "httpMocks": false, + "uiNativePosts": 0 +} \ No newline at end of file diff --git a/docs/ux-ui/acceptance/live-shell-a11y-2026-10-10.json b/docs/ux-ui/acceptance/live-shell-a11y-2026-10-10.json new file mode 100644 index 00000000..cd64bc51 --- /dev/null +++ b/docs/ux-ui/acceptance/live-shell-a11y-2026-10-10.json @@ -0,0 +1,703 @@ +{ + "date": "2026-10-10", + "runtime": { + "backend": "ux-speed98", + "frontend": "ux-ospf91" + }, + "httpMocks": false, + "readOnly": true, + "cases": [ + { + "route": "alerts", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds", + "targets": [ + [ + ".py-12 > p" + ] + ] + } + ], + "apiFailures": [] + }, + { + "route": "alerts", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds", + "targets": [ + [ + ".py-12 > .text-sm" + ] + ] + } + ], + "apiFailures": [] + }, + { + "route": "asns", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "asns", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "backups", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "backups", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "bgp", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "bgp", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "certificates", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds", + "targets": [ + [ + ".text-success-foreground" + ] + ] + }, + { + "id": "scrollable-region-focusable", + "impact": "serious", + "description": "Ensure elements that have scrollable content are accessible by keyboard in Safari", + "targets": [ + [ + "div:nth-child(1) > pre" + ], + [ + "div:nth-child(3) > pre" + ] + ] + } + ], + "apiFailures": [] + }, + { + "route": "certificates", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds", + "targets": [ + [ + ".text-success-foreground" + ] + ] + }, + { + "id": "scrollable-region-focusable", + "impact": "serious", + "description": "Ensure elements that have scrollable content are accessible by keyboard in Safari", + "targets": [ + [ + "div:nth-child(3) > pre" + ] + ] + } + ], + "apiFailures": [] + }, + { + "route": "communities", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "communities", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "containers", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds", + "targets": [ + [ + ".bg-info\\/5 > div > .text-info" + ] + ] + } + ], + "apiFailures": [] + }, + { + "route": "containers", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds", + "targets": [ + [ + ".bg-info\\/5 > div > .text-info" + ] + ] + } + ], + "apiFailures": [] + }, + { + "route": "dashboard", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "dashboard", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "data-collection", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "data-collection", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "domains", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "domains", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "filters", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "filters", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds", + "targets": [ + [ + ".bg-primary" + ] + ] + } + ], + "apiFailures": [] + }, + { + "route": "firewall", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "firewall", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "gre", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "gre", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "ip-ranges", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "ip-ranges", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "ipsec", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "ipsec", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "network-map", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "network-map", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "ospf", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "ospf", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "probes", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "probes", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "recursive-routes", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "recursive-routes", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "releases", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "releases", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "route-optimizer", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "route-optimizer", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "servers", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "servers", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "settings", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "settings", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "statistics", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "statistics", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "terminal", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "terminal", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "traffic", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [ + { + "path": "/api/traffic/servers/srv1/interfaces", + "status": 400 + }, + { + "path": "/api/traffic/servers/srv1", + "status": 400 + } + ] + }, + { + "route": "traffic", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [ + { + "path": "/api/traffic/servers/srv1/interfaces", + "status": 400 + }, + { + "path": "/api/traffic/servers/srv1", + "status": 400 + } + ] + }, + { + "route": "uptime", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [ + { + "id": "color-contrast", + "impact": "serious", + "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds", + "targets": [ + [ + ".text-muted-foreground\\/80" + ] + ] + } + ], + "apiFailures": [] + }, + { + "route": "uptime", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "users", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "users", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "vxlan", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "vxlan", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "wireguard", + "width": 1280, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + }, + { + "route": "wireguard", + "width": 320, + "httpStatus": 200, + "fits": true, + "title": "MikrotikManager", + "violations": [], + "apiFailures": [] + } + ], + "errors": [], + "writes": [ + { + "method": "POST", + "path": "/api/servers/1/ospf/optimize/preview" + }, + { + "method": "POST", + "path": "/api/servers/1/ospf/optimize/preview" + } + ], + "automaticReadOnlyRequests": [ + { + "method": "POST", + "path": "/api/servers/1/ospf/optimize/preview" + }, + { + "method": "POST", + "path": "/api/servers/1/ospf/optimize/preview" + } + ], + "mutations": [], + "note": "Two automatic OSPF optimizer preview POSTs only; no apply/mutation requests. Scoped axe at700ms after fonts ready; not full WCAG or functional acceptance of all integrations." +} diff --git a/docs/ux-ui/acceptance/routeros-response-transport-2026-10-10.json b/docs/ux-ui/acceptance/routeros-response-transport-2026-10-10.json new file mode 100644 index 00000000..d9ac0194 --- /dev/null +++ b/docs/ux-ui/acceptance/routeros-response-transport-2026-10-10.json @@ -0,0 +1,78 @@ +{ + "date": "2026-10-10", + "runtime": { + "backend": "ux-response100", + "version": "1.0.0-test.response100", + "imageId": "sha256:797979da82d6bd10147bd57557c65cc28a61b1e583abed13ed7326635d4e5adf", + "frontend": "ux-ospf91" + }, + "source": { + "base": "build-speed98", + "archiveSha256": "08e9b4bb3a70c5d9d20e0ffef387d9bbba7bcc51f80968be740c7c64982dc5da", + "workingTreeOverrides": [ + "backend/src/services/mikrotik.ts", + "backend/src/services/mikrotik-response.test.ts", + "backend/package.json" + ], + "revisionLabel": "response100 (stage label, not Git SHA)" + }, + "before": { + "boundedTests": 7, + "passed": 1, + "failed": 6, + "reason": "Incomplete IncomingMessage left promise pending past500ms timeout; test1200ms deadline rejected" + }, + "after": { + "boundedTests": 7, + "passed": 7, + "failed": 0, + "methods": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE", + "raw file POST" + ], + "completeSuccessAnd401": true, + "mutationRetries": 0, + "fileCompatibilityReadStrategies": 2 + }, + "checks": { + "abortSignalAndProbeCancellation": true, + "fileByteParser": true, + "localProductionBuild": true, + "dockerProductionBuild": true, + "entryNodeCheck": true, + "pgRestore": "18.6", + "composeHealthy": true, + "backendNpmTest": { + "groups": 24, + "nodeTestCases": 171, + "reportedFailures": 0, + "exitCode": 0, + "database": "Deliberately unavailable fixture URL; no external DB writes" + }, + "lintNewTest": true, + "existingMikrotikPreferConstErrors": 2 + }, + "live": { + "nativeReport": "speed-response-body-2026-10-10.json", + "browserReport": "browser-speed-response-body-2026-10-10.json", + "nativeCommands": 1, + "durationSec": 3, + "rateLimitBps": 10000000, + "body": "First16 bytes of actual completed native JSON delivered; relay closed socket before remaining body", + "api502": true, + "unknownMessage": true, + "priorSnapshotPreserved": true, + "automaticRetry": false, + "uiCases": 2, + "cleanup": true + }, + "limits": [ + "HTTP localhost partial-response tests plus real RouterOS command through controlled relay; not independent physical WAN loss", + "Complete malformed PATCH JSON semantics, general response size limits and durable cancellation not changed", + "Main/context UI matrix still pending; frontend unchanged" + ] +} diff --git a/docs/ux-ui/acceptance/speed-response-body-2026-10-10.json b/docs/ux-ui/acceptance/speed-response-body-2026-10-10.json new file mode 100644 index 00000000..2720f77e --- /dev/null +++ b/docs/ux-ui/acceptance/speed-response-body-2026-10-10.json @@ -0,0 +1,54 @@ +{ + "date": "2026-10-10", + "name": "mm-body100-1791572367426", + "rateLimitBps": 10000000, + "durationSec": 3, + "syntheticPreviousSnapshot": { + "txAvgMbps": 42, + "rxAvgMbps": 17, + "pingRttMs": 15, + "pingLossPct": 0 + }, + "httpMocks": false, + "routerOsMocks": false, + "nativeCompletion": { + "status": "done testing", + "responseRows": 5, + "httpStatus": 200 + }, + "receiptPhase": "partial-response-body", + "receiptSocketDestroyed": true, + "httpStatus": 502, + "receipt": { + "error": "Результат теста скорости неизвестен: ответ устройства не получен. Проверьте его состояние перед повторным запуском." + }, + "nativeCount": 1, + "probe": { + "id": "mm-body100-1791572367426", + "last_status": "error", + "last_tx_avg_mbps": 42, + "last_rx_avg_mbps": 17, + "last_ping_rtt_ms": 15, + "last_ping_loss_pct": 0, + "last_error": "Результат теста скорости неизвестен: ответ устройства не получен. Проверьте его состояние перед повторным запуском." + }, + "history": [ + { + "id": "mm-body100-1791572367426", + "status": "error", + "tx_avg_mbps": null, + "rx_avg_mbps": null, + "ping_rtt_ms": null, + "ping_loss_pct": null, + "error": "Результат теста скорости неизвестен: ответ устройства не получен. Проверьте его состояние перед повторным запуском." + } + ], + "automaticRetry": false, + "limits": [ + "Real Node HTTP client socket closed after request forwarded; RouterOS independently completed. Not physical WAN loss or cancelled native command.", + "Previous metrics deliberately seeded on own disabled probe, not new real measurements.", + "History error means application did not obtain confirmed test result; native completion was observed only by independent relay." + ], + "cleanupHistory": true, + "cleanupProbes": true +} \ No newline at end of file