fix(ospf): подтвердить права и результат оптимизации на RouterOS

This commit is contained in:
Denozordec committed 2026-10-09 20:40:24 +07:00
1 parent 3bfd4ae6cf
commit 36c6a21ca5
19 files changed
+1069 -15

No files matched your search

+29
View File
@@ -720,3 +720,32 @@ flowchart LR
**Результат.** [Очищенный отчёт](ux-ui/acceptance/ospf-bfd-timing-2026-10-09.json): HTTP samples, scoped native logs, initial-attempt summary, recovery и baseline. Все свои firewall/logging/OSPF/BFD/IP/bridge/GRE объекты удалены после exact-ID/атрибутов preflight. Исходные ID10 коллекций×2 совпали; оба CHR доступны. Продукт/зависимости/runtime не менялись, сборки/широкие регрессии не повторялись. Локальный коммит без push. Квота5% использовано после завершения.
Настройки протокола подтверждены [MikroTik BFD](https://help.mikrotik.com/docs/spaces/ROS/pages/191299691/BFD), механика memory logging — [MikroTik Log](https://help.mikrotik.com/docs/spaces/ROS/pages/328094/Log). Продолжение87: собственные OSPF templates через production optimizer preview/apply, неизвестные/модельные метрики и частичный отказ без заявления полного успеха; далее recursive diagnostics. Этап12.5 остаётся открытым.
## 12.5.87 — OSPF optimizer: права, реальные costs и частичный результат (2026-10-09)
**Изменения.** Три проверки разрешения в OSPF UI приведены к backend mm:network:write. Роль только mm:servers:write не запускает оптимизацию, network writer может её выполнить. Существующие Frame/компоненты/композиция ReUI сохранены; skill ReUI применён для минимальной правки поведения.
POST optimize при отказе записи возвращает502 и request-local receipt: state partial для подтверждённых шагов перед RouterOS4xx; unknown при потерянном ACK/transport/5xx; rejected при первом явном4xx. applied содержит только подтверждённые interfaces/from/to, confirmedWrites, failedInterface и needsReview. После первого отказа следующие записи не выполняются. Preflight/plan ошибки остаются500 без заявления о записи. Автоматического rollback/retry, durable receipt или межпроцессной блокировки не добавлено. UI сохраняет прежнее сообщение о неподтверждённой операции и повторный опрос.
**RouterOS7.24.5.** GET может опустить disabled при unset и у interface-template, и у instance/area. GET disabled=false/proplist не раскрывает effective default: отдельный native POST /get с .id/value-name=disabled возвращает ret=false; при disable ret=true; при явно false GET уже содержит false. Добавлено чтение эффективного значения только для пропущенного disabled, до4 одновременно. Значение не угадывается по отсутствию или inactive. Неправильный ответ/отказ/повтор ID блокирует чтение. Явные значения сохраняются, исходный payload не мутируется. Большой набор unset потребует дополнительных RPC; maximum scale/performance этого пути не принят.
| Проверка | До | После / доказательство |
| --- | --- | --- |
| Права кнопки OSPF | servers:write расходится с API | network:write; обе роли проверены на320px |
| Неявный disabled | strict preview500 | native /get подтверждает состояние templates/instances/areas |
| Частичный отказ | обычный500 без шагов | partial/unknown/rejected и подтверждённые изменения |
| Production preview/apply | Не принято на устройстве | preview без записи, два costs применены на каждом CHR, повтор0 |
Схема: устройство → достоверные flags/costs → preview → последовательная запись → receipt → повторный опрос. Референс сохранён: [Data Grid Filtering2](https://reui.io/preview/base/data-grid-filtering-2?ref=mcp). [Экран ПК](ux-ui/screens/ospf-real-optimizer-server1-1280-2026-10-09.png), [320px](ux-ui/screens/ospf-real-optimizer-server1-320-2026-10-09.png); mobile просмотрен, кнопки/cur-opt помещаются в документе.
**Реальная приёмка.** Свои GRE/link198.18.87.0/30/loopbacks198.18.187.1/32↔2/32, OSPF instances/area, templates с начальными costs50/60. Public interfaces не включались в OSPF, originate-default=never. Production preview сохранил native costs, apply изменил два собственных шаблона на10/20 согласно плану, repeat optimizedCount0. DTO observed остаётся отдельным от модельных ping/speeds; fallback не объявляется измерением. OSPF Full, private route active, Ping3/3 по первоначальной проверке пары.
Изолированный экземпляр тех же compiled routes с настоящими PATCH собственных шаблонов: reject второго шага до передачи → partial/confirmed1, второй cost неизменён; исключение после подтверждённого native PATCH, вместо ACK вызывающему коду → unknown/confirmed1, отдельный GET подтверждает оба фактических изменения. Это инъекция ответа, не физический TCP disconnect. После тестов production API привёл costs к текущему плану. [Native/API/flags протокол](ux-ui/acceptance/ospf-optimizer-2026-10-09.json).
**Браузер и локальные проверки.** Настоящий deployed frontend/backend без HTTP interception: два устройства×1280/320 =4/4, apply повтор0, receipt/server context, кнопка после опроса, reload URL, document fits/pageerror0. [Browser отчёт](ux-ui/acceptance/browser-ospf-optimizer-2026-10-09.json). Профильные E2E OSPF operations/optimizer apply16/16; optimizer identity/metrics/plan/effective disabled/HTTP, catalog tests и signed JWT RBAC passed. Backend build и scoped frontend tsc passed, Docker frontend production build прошёл. Обычный root tsc захватывает старые ignored tmp build copies и имеет исходные ошибки; scoped config исключает tmp. ESLint новых helper/test/UI прошёл; OSPF прежние2 unused warnings, mikrotik.ts прежние2 prefer-const errors подтверждены на HEAD, новых ошибок нет. Полная регрессия не повторялась.
**Оснастка/очистка.** Первые два native runs отказались на отсутствующем disabled template, затем instance; оба exact cleanup7 коллекций×2 passed. Read-only discovery проверила proplist/filter/get; numbers оказался неподдерживаемым аргументом, рабочий .id проверен. Отдельная flags fixture cleaned baseline5 коллекций. Финальный run native/browser прошёл, own templates/areas/instances/IP/bridge/GRE удалены по точным ID/атрибутам, исходные IDs7 коллекций×2 совпали. 195.195.195.195/32/default/management routes сохранены. Оба CHR доступны, credentials не менялись, fixture контейнеров/DB записей не осталось.
**Runtime.** Backend ux-optimizer87c/version1.0.0-test.optimizer87c, image sha256:22cc9a09a89038e58c15dfd6224191d1fab7ad63f7af6f80ecc9e7577483d390; frontend ux-optimizer87/version1.0.0-test.optimizer87, image sha256:22f05c341a7dc0e40e04f23b0da61ba5cfbf31d741d4e22b0c9bcd71c85e0c49. Backend/PG healthy, frontend served/browser passed. Backend entry/node --check/pg_restore18.6, frontend entry/node --check и compose --wait passed. Три backend builds87/87b/87c по двум реальным flag incompatibilities; frontend build один, apt cache сохранён. Context backend routing83 +2 routes, затем4 файлов final-c; override SHA c04289ab57294d57cc9aed292e247e144c171ade3d1101f6ebb84129e41b065be; frontend ipsec77 +OSPF page, override dab57aad2cb248553b0fee9da73069f7a31cb627b26c414f03320315a299756db. Labels не Git SHA; это рабочие файлы до локального коммита. .env backup приватный, миграций нет. Один локальный commit без push.
Продолжение88 — recursive routes, затем диагностика. BFD sustained/alternate-path failover, IPv6/scale/maximum throughput, Portal SSO/browser auth и другие внешние ограничения сохраняются; lab AUTH_REQUIRED=false, RBAC checked отдельно. API /get и .proplist/query подтверждены [RouterOS REST API](https://help.mikrotik.com/docs/spaces/ROS/pages/47579162/REST%2BAPI) и реальным устройством. Квота23% использовано, этап12.5 открыт.
+1 -1
View File
@@ -1,6 +1,6 @@
# План реализации UX/UI по пятичасовым окнам
Актуальное продолжение 2026-10-09: 12.5.86 разделил наблюдения BFD/OSPF и удаления маршрута: BFD-only fault 629/630/1845ms; GRE loss 622/622/2842ms при dead20s. Это первые HTTP-наблюдения, не точные event timestamps/SLA. BFD-only допускает повторный Full при доступных Hello; устойчивый failover не принят. Baseline10 коллекций×2 восстановлен. Runtime backend ux-routing83/frontend ux-ipsec77. Следующий12.5.87 — реальная приёмка OSPF optimizer preview/apply и достоверность частичных операций; затем recursive diagnostics. Квота после блока5% использовано; этап12.5 открыт.
Актуальное продолжение 2026-10-09: 12.5.87 принял production OSPF optimizer preview/apply/repeat на двух своих GRE парах, partial/unknown с проверкой native costs и4 live UI проверки1280/320. Исправлены права кнопки и чтение эффективного disabled OSPF templates/instances/areas. Baseline7 коллекций×2 восстановлен. Backend ux-optimizer87c/frontend ux-optimizer87, healthy/served. Следующий12.5.88 — рекурсивные маршруты: запрет опроса выключенных устройств, реальные чтение/применение/история и достоверность поздних ошибок; далее диагностика. Квота23% использовано после блока, этап12.5 открыт.
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-09. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
+31 -2
View File
@@ -1,6 +1,6 @@
# UX/UI: журнал реализации
Актуальное продолжение 2026-10-09: 12.5.86 разделил наблюдения BFD/OSPF и удаления маршрута: BFD-only fault 629/630/1845ms; GRE loss 622/622/2842ms при dead20s. Это первые HTTP-наблюдения, не точные event timestamps/SLA. BFD-only допускает повторный Full при доступных Hello; устойчивый failover не принят. Baseline10 коллекций×2 восстановлен. Runtime backend ux-routing83/frontend ux-ipsec77. Следующий12.5.87 — реальная приёмка OSPF optimizer preview/apply и достоверность частичных операций; затем recursive diagnostics. Квота после блока5% использовано; этап12.5 открыт.
Актуальное продолжение 2026-10-09: 12.5.87 принял production OSPF optimizer preview/apply/repeat на двух своих GRE парах, partial/unknown с проверкой native costs и4 live UI проверки1280/320. Исправлены права кнопки и чтение эффективного disabled OSPF templates/instances/areas. Baseline7 коллекций×2 восстановлен. Backend ux-optimizer87c/frontend ux-optimizer87, healthy/served. Следующий12.5.88 — рекурсивные маршруты: запрет опроса выключенных устройств, реальные чтение/применение/история и достоверность поздних ошибок; далее диагностика. Квота23% использовано после блока, этап12.5 открыт.
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
@@ -36,7 +36,36 @@
| 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–86 и ограничения ниже. Runtime backend ux-routing83/frontend ux-ipsec77. |
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–87 и ограничения ниже. Runtime backend ux-optimizer87c/frontend ux-optimizer87. |
## 12.5.87 — OSPF optimizer: права, реальные costs и частичный результат (2026-10-09)
**Изменения.** Три проверки разрешения в OSPF UI приведены к backend mm:network:write. Роль только mm:servers:write не запускает оптимизацию, network writer может её выполнить. Существующие Frame/компоненты/композиция ReUI сохранены; skill ReUI применён для минимальной правки поведения.
POST optimize при отказе записи возвращает502 и request-local receipt: state partial для подтверждённых шагов перед RouterOS4xx; unknown при потерянном ACK/transport/5xx; rejected при первом явном4xx. applied содержит только подтверждённые interfaces/from/to, confirmedWrites, failedInterface и needsReview. После первого отказа следующие записи не выполняются. Preflight/plan ошибки остаются500 без заявления о записи. Автоматического rollback/retry, durable receipt или межпроцессной блокировки не добавлено. UI сохраняет прежнее сообщение о неподтверждённой операции и повторный опрос.
**RouterOS7.24.5.** GET может опустить disabled при unset и у interface-template, и у instance/area. GET disabled=false/proplist не раскрывает effective default: отдельный native POST /get с .id/value-name=disabled возвращает ret=false; при disable ret=true; при явно false GET уже содержит false. Добавлено чтение эффективного значения только для пропущенного disabled, до4 одновременно. Значение не угадывается по отсутствию или inactive. Неправильный ответ/отказ/повтор ID блокирует чтение. Явные значения сохраняются, исходный payload не мутируется. Большой набор unset потребует дополнительных RPC; maximum scale/performance этого пути не принят.
| Проверка | До | После / доказательство |
| --- | --- | --- |
| Права кнопки OSPF | servers:write расходится с API | network:write; обе роли проверены на320px |
| Неявный disabled | strict preview500 | native /get подтверждает состояние templates/instances/areas |
| Частичный отказ | обычный500 без шагов | partial/unknown/rejected и подтверждённые изменения |
| Production preview/apply | Не принято на устройстве | preview без записи, два costs применены на каждом CHR, повтор0 |
Схема: устройство → достоверные flags/costs → preview → последовательная запись → receipt → повторный опрос. Референс сохранён: [Data Grid Filtering2](https://reui.io/preview/base/data-grid-filtering-2?ref=mcp). [Экран ПК](ux-ui/screens/ospf-real-optimizer-server1-1280-2026-10-09.png), [320px](ux-ui/screens/ospf-real-optimizer-server1-320-2026-10-09.png); mobile просмотрен, кнопки/cur-opt помещаются в документе.
**Реальная приёмка.** Свои GRE/link198.18.87.0/30/loopbacks198.18.187.1/32↔2/32, OSPF instances/area, templates с начальными costs50/60. Public interfaces не включались в OSPF, originate-default=never. Production preview сохранил native costs, apply изменил два собственных шаблона на10/20 согласно плану, repeat optimizedCount0. DTO observed остаётся отдельным от модельных ping/speeds; fallback не объявляется измерением. OSPF Full, private route active, Ping3/3 по первоначальной проверке пары.
Изолированный экземпляр тех же compiled routes с настоящими PATCH собственных шаблонов: reject второго шага до передачи → partial/confirmed1, второй cost неизменён; исключение после подтверждённого native PATCH, вместо ACK вызывающему коду → unknown/confirmed1, отдельный GET подтверждает оба фактических изменения. Это инъекция ответа, не физический TCP disconnect. После тестов production API привёл costs к текущему плану. [Native/API/flags протокол](ux-ui/acceptance/ospf-optimizer-2026-10-09.json).
**Браузер и локальные проверки.** Настоящий deployed frontend/backend без HTTP interception: два устройства×1280/320 =4/4, apply повтор0, receipt/server context, кнопка после опроса, reload URL, document fits/pageerror0. [Browser отчёт](ux-ui/acceptance/browser-ospf-optimizer-2026-10-09.json). Профильные E2E OSPF operations/optimizer apply16/16; optimizer identity/metrics/plan/effective disabled/HTTP, catalog tests и signed JWT RBAC passed. Backend build и scoped frontend tsc passed, Docker frontend production build прошёл. Обычный root tsc захватывает старые ignored tmp build copies и имеет исходные ошибки; scoped config исключает tmp. ESLint новых helper/test/UI прошёл; OSPF прежние2 unused warnings, mikrotik.ts прежние2 prefer-const errors подтверждены на HEAD, новых ошибок нет. Полная регрессия не повторялась.
**Оснастка/очистка.** Первые два native runs отказались на отсутствующем disabled template, затем instance; оба exact cleanup7 коллекций×2 passed. Read-only discovery проверила proplist/filter/get; numbers оказался неподдерживаемым аргументом, рабочий .id проверен. Отдельная flags fixture cleaned baseline5 коллекций. Финальный run native/browser прошёл, own templates/areas/instances/IP/bridge/GRE удалены по точным ID/атрибутам, исходные IDs7 коллекций×2 совпали. 195.195.195.195/32/default/management routes сохранены. Оба CHR доступны, credentials не менялись, fixture контейнеров/DB записей не осталось.
**Runtime.** Backend ux-optimizer87c/version1.0.0-test.optimizer87c, image sha256:22cc9a09a89038e58c15dfd6224191d1fab7ad63f7af6f80ecc9e7577483d390; frontend ux-optimizer87/version1.0.0-test.optimizer87, image sha256:22f05c341a7dc0e40e04f23b0da61ba5cfbf31d741d4e22b0c9bcd71c85e0c49. Backend/PG healthy, frontend served/browser passed. Backend entry/node --check/pg_restore18.6, frontend entry/node --check и compose --wait passed. Три backend builds87/87b/87c по двум реальным flag incompatibilities; frontend build один, apt cache сохранён. Context backend routing83 +2 routes, затем4 файлов final-c; override SHA c04289ab57294d57cc9aed292e247e144c171ade3d1101f6ebb84129e41b065be; frontend ipsec77 +OSPF page, override dab57aad2cb248553b0fee9da73069f7a31cb627b26c414f03320315a299756db. Labels не Git SHA; это рабочие файлы до локального коммита. .env backup приватный, миграций нет. Один локальный commit без push.
Продолжение88 — recursive routes, затем диагностика. BFD sustained/alternate-path failover, IPv6/scale/maximum throughput, Portal SSO/browser auth и другие внешние ограничения сохраняются; lab AUTH_REQUIRED=false, RBAC checked отдельно. API /get и .proplist/query подтверждены [RouterOS REST API](https://help.mikrotik.com/docs/spaces/ROS/pages/47579162/REST%2BAPI) и реальным устройством. Квота23% использовано, этап12.5 открыт.
## 12.5.86 — BFD/OSPF: раздельные наблюдения реакции и маршрута (2026-10-09)
+29
View File
@@ -2029,3 +2029,32 @@ HTTP sampling показал withdrawal за5245ms при OSPF dead interval4000
**Результат.** [Очищенный отчёт](ux-ui/acceptance/ospf-bfd-timing-2026-10-09.json): HTTP samples, scoped native logs, initial-attempt summary, recovery и baseline. Все свои firewall/logging/OSPF/BFD/IP/bridge/GRE объекты удалены после exact-ID/атрибутов preflight. Исходные ID10 коллекций×2 совпали; оба CHR доступны. Продукт/зависимости/runtime не менялись, сборки/широкие регрессии не повторялись. Локальный коммит без push. Квота5% использовано после завершения.
Настройки протокола подтверждены [MikroTik BFD](https://help.mikrotik.com/docs/spaces/ROS/pages/191299691/BFD), механика memory logging — [MikroTik Log](https://help.mikrotik.com/docs/spaces/ROS/pages/328094/Log). Продолжение87: собственные OSPF templates через production optimizer preview/apply, неизвестные/модельные метрики и частичный отказ без заявления полного успеха; далее recursive diagnostics. Этап12.5 остаётся открытым.
## 12.5.87 — OSPF optimizer: права, реальные costs и частичный результат (2026-10-09)
**Изменения.** Три проверки разрешения в OSPF UI приведены к backend mm:network:write. Роль только mm:servers:write не запускает оптимизацию, network writer может её выполнить. Существующие Frame/компоненты/композиция ReUI сохранены; skill ReUI применён для минимальной правки поведения.
POST optimize при отказе записи возвращает502 и request-local receipt: state partial для подтверждённых шагов перед RouterOS4xx; unknown при потерянном ACK/transport/5xx; rejected при первом явном4xx. applied содержит только подтверждённые interfaces/from/to, confirmedWrites, failedInterface и needsReview. После первого отказа следующие записи не выполняются. Preflight/plan ошибки остаются500 без заявления о записи. Автоматического rollback/retry, durable receipt или межпроцессной блокировки не добавлено. UI сохраняет прежнее сообщение о неподтверждённой операции и повторный опрос.
**RouterOS7.24.5.** GET может опустить disabled при unset и у interface-template, и у instance/area. GET disabled=false/proplist не раскрывает effective default: отдельный native POST /get с .id/value-name=disabled возвращает ret=false; при disable ret=true; при явно false GET уже содержит false. Добавлено чтение эффективного значения только для пропущенного disabled, до4 одновременно. Значение не угадывается по отсутствию или inactive. Неправильный ответ/отказ/повтор ID блокирует чтение. Явные значения сохраняются, исходный payload не мутируется. Большой набор unset потребует дополнительных RPC; maximum scale/performance этого пути не принят.
| Проверка | До | После / доказательство |
| --- | --- | --- |
| Права кнопки OSPF | servers:write расходится с API | network:write; обе роли проверены на320px |
| Неявный disabled | strict preview500 | native /get подтверждает состояние templates/instances/areas |
| Частичный отказ | обычный500 без шагов | partial/unknown/rejected и подтверждённые изменения |
| Production preview/apply | Не принято на устройстве | preview без записи, два costs применены на каждом CHR, повтор0 |
Схема: устройство → достоверные flags/costs → preview → последовательная запись → receipt → повторный опрос. Референс сохранён: [Data Grid Filtering2](https://reui.io/preview/base/data-grid-filtering-2?ref=mcp). [Экран ПК](ux-ui/screens/ospf-real-optimizer-server1-1280-2026-10-09.png), [320px](ux-ui/screens/ospf-real-optimizer-server1-320-2026-10-09.png); mobile просмотрен, кнопки/cur-opt помещаются в документе.
**Реальная приёмка.** Свои GRE/link198.18.87.0/30/loopbacks198.18.187.1/32↔2/32, OSPF instances/area, templates с начальными costs50/60. Public interfaces не включались в OSPF, originate-default=never. Production preview сохранил native costs, apply изменил два собственных шаблона на10/20 согласно плану, repeat optimizedCount0. DTO observed остаётся отдельным от модельных ping/speeds; fallback не объявляется измерением. OSPF Full, private route active, Ping3/3 по первоначальной проверке пары.
Изолированный экземпляр тех же compiled routes с настоящими PATCH собственных шаблонов: reject второго шага до передачи → partial/confirmed1, второй cost неизменён; исключение после подтверждённого native PATCH, вместо ACK вызывающему коду → unknown/confirmed1, отдельный GET подтверждает оба фактических изменения. Это инъекция ответа, не физический TCP disconnect. После тестов production API привёл costs к текущему плану. [Native/API/flags протокол](ux-ui/acceptance/ospf-optimizer-2026-10-09.json).
**Браузер и локальные проверки.** Настоящий deployed frontend/backend без HTTP interception: два устройства×1280/320 =4/4, apply повтор0, receipt/server context, кнопка после опроса, reload URL, document fits/pageerror0. [Browser отчёт](ux-ui/acceptance/browser-ospf-optimizer-2026-10-09.json). Профильные E2E OSPF operations/optimizer apply16/16; optimizer identity/metrics/plan/effective disabled/HTTP, catalog tests и signed JWT RBAC passed. Backend build и scoped frontend tsc passed, Docker frontend production build прошёл. Обычный root tsc захватывает старые ignored tmp build copies и имеет исходные ошибки; scoped config исключает tmp. ESLint новых helper/test/UI прошёл; OSPF прежние2 unused warnings, mikrotik.ts прежние2 prefer-const errors подтверждены на HEAD, новых ошибок нет. Полная регрессия не повторялась.
**Оснастка/очистка.** Первые два native runs отказались на отсутствующем disabled template, затем instance; оба exact cleanup7 коллекций×2 passed. Read-only discovery проверила proplist/filter/get; numbers оказался неподдерживаемым аргументом, рабочий .id проверен. Отдельная flags fixture cleaned baseline5 коллекций. Финальный run native/browser прошёл, own templates/areas/instances/IP/bridge/GRE удалены по точным ID/атрибутам, исходные IDs7 коллекций×2 совпали. 195.195.195.195/32/default/management routes сохранены. Оба CHR доступны, credentials не менялись, fixture контейнеров/DB записей не осталось.
**Runtime.** Backend ux-optimizer87c/version1.0.0-test.optimizer87c, image sha256:22cc9a09a89038e58c15dfd6224191d1fab7ad63f7af6f80ecc9e7577483d390; frontend ux-optimizer87/version1.0.0-test.optimizer87, image sha256:22f05c341a7dc0e40e04f23b0da61ba5cfbf31d741d4e22b0c9bcd71c85e0c49. Backend/PG healthy, frontend served/browser passed. Backend entry/node --check/pg_restore18.6, frontend entry/node --check и compose --wait passed. Три backend builds87/87b/87c по двум реальным flag incompatibilities; frontend build один, apt cache сохранён. Context backend routing83 +2 routes, затем4 файлов final-c; override SHA c04289ab57294d57cc9aed292e247e144c171ade3d1101f6ebb84129e41b065be; frontend ipsec77 +OSPF page, override dab57aad2cb248553b0fee9da73069f7a31cb627b26c414f03320315a299756db. Labels не Git SHA; это рабочие файлы до локального коммита. .env backup приватный, миграций нет. Один локальный commit без push.
Продолжение88 — recursive routes, затем диагностика. BFD sustained/alternate-path failover, IPv6/scale/maximum throughput, Portal SSO/browser auth и другие внешние ограничения сохраняются; lab AUTH_REQUIRED=false, RBAC checked отдельно. API /get и .proplist/query подтверждены [RouterOS REST API](https://help.mikrotik.com/docs/spaces/ROS/pages/47579162/REST%2BAPI) и реальным устройством. Квота23% использовано, этап12.5 открыт.
+3 -1
View File
@@ -1,6 +1,6 @@
# Тестовый сервер UX/UI — 2026-10-09
Сервер161.104.46.222 предоставлен для временного тестирования. Backend ux-routing83/version1.0.0-test.routing83, frontend ux-ipsec77/version1.0.0-test.ipsec77; backend/PG healthy, frontend HTTP/browser passed. Основной блок IPsec77 принят в границах журнала, own fixtures/fake DB rows удалены; каталог flash CHR2 сохранён из-за неподтверждённого ownership. SSH/оба CHR доступны, пароли прежние.
Сервер161.104.46.222 предоставлен для временного тестирования. Backend ux-optimizer87c/version1.0.0-test.optimizer87c, frontend ux-optimizer87/version1.0.0-test.optimizer87; backend/PG healthy, frontend HTTP/browser passed. Основной блок IPsec77 принят в границах журнала, own fixtures/fake DB rows удалены; каталог flash CHR2 сохранён из-за неподтверждённого ownership. SSH/оба CHR доступны, пароли прежние.
| Параметр | Проверенное состояние |
| --- | --- |
@@ -711,3 +711,5 @@ flowchart LR
Актуальное продолжение 2026-10-09: 12.5.77 завершил укрупнённый основной блок IPsec: достоверное чтение, запрет опроса выключенных устройств, совместимый RouterOS7 мастер, раздельные ID клиентов, безопаснее удаление, URL устройства/вкладки, Docker обеих частей и реальная приёмка. Local30/30 + context6/6 + backend67, live browser4/4, PSK и сертификатный защищённый GRE/Ping3/3 на двух CHR; cleanup/DB controls passed. Runtime backend/frontend ux-ipsec77, version1.0.0-test.ipsec77. Следующий12.5.78 — остаточная профильная приёмка IPsec: сопоставление active-peer/online, выдача mode-config, поздние ошибки операций и история восстановления; затем маршрутизация. Этап12.5 открыт.
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
12.5.87 (2026-10-09): OSPF optimizer native/API и4 browser checks passed, собственные объекты очищены, baseline7 коллекций×2 сохранён. Подробности/runtime происхождение — в журнале.
@@ -0,0 +1,39 @@
{
"date": "2026-10-09",
"phase": "2 320",
"cases": [
{
"serverId": 1,
"width": 1280,
"realApplyReceipt": true,
"repeatedCount": 0,
"documentFits": true,
"urlRestore": true
},
{
"serverId": 1,
"width": 320,
"realApplyReceipt": true,
"repeatedCount": 0,
"documentFits": true,
"urlRestore": true
},
{
"serverId": 2,
"width": 1280,
"realApplyReceipt": true,
"repeatedCount": 0,
"documentFits": true,
"urlRestore": true
},
{
"serverId": 2,
"width": 320,
"realApplyReceipt": true,
"repeatedCount": 0,
"documentFits": true,
"urlRestore": true
}
],
"errors": []
}
@@ -0,0 +1,830 @@
{
"date": "2026-10-09",
"name": "mm-ospf87-1791553001174",
"observations": [
{
"serverId": 1,
"nativeNeighbor": {
".id": "*F3FDDC00",
"router-id": "198.18.187.2",
"address": "198.18.87.2",
"interface": "mm-ospf87-1791553001174",
"state": "Full",
"state-changes": "6"
},
"nativeRoute": {
".id": "*2014C000",
"dst-address": "198.18.187.2/32",
"gateway": "198.18.87.2%mm-ospf87-1791553001174",
"ospf": "true",
"active": "true",
"distance": "110"
},
"catalogNeighbors": [
{
"id": "*F3FDDC00",
"serverId": 1,
"serverName": "CHR",
"serverSite": "",
"serverCountry": "",
"address": "198.18.87.2",
"routerId": "198.18.187.2",
"instance": "mm-ospf87-1791553001174",
"area": "mm-ospf87-1791553001174",
"areaId": "0.0.0.0",
"interface": "mm-ospf87-1791553001174",
"state": "Full",
"uptime": "4s",
"stateChanges": 6,
"priority": null
}
],
"catalogRoutes": [
{
"id": "*2014C000",
"serverId": 1,
"serverName": "CHR",
"serverSite": "",
"destination": "198.18.187.2/32",
"type": "Unknown",
"cost": null,
"nextHop": "198.18.87.2",
"via": "mm-ospf87-1791553001174",
"area": ""
}
],
"pingReceived": 3
},
{
"serverId": 2,
"nativeNeighbor": {
".id": "*F3FDDC00",
"router-id": "198.18.187.1",
"address": "198.18.87.1",
"interface": "mm-ospf87-1791553001174",
"state": "Full",
"state-changes": "5"
},
"nativeRoute": {
".id": "*2014C000",
"dst-address": "198.18.187.1/32",
"gateway": "198.18.87.1%mm-ospf87-1791553001174",
"ospf": "true",
"active": "true",
"distance": "110"
},
"catalogNeighbors": [
{
"id": "*F3FDDC00",
"serverId": 2,
"serverName": "CHR",
"serverSite": "",
"serverCountry": "",
"address": "198.18.87.1",
"routerId": "198.18.187.1",
"instance": "mm-ospf87-1791553001174",
"area": "mm-ospf87-1791553001174",
"areaId": "0.0.0.0",
"interface": "mm-ospf87-1791553001174",
"state": "Full",
"uptime": "4s",
"stateChanges": 5,
"priority": null
}
],
"catalogRoutes": [
{
"id": "*2014C000",
"serverId": 2,
"serverName": "CHR",
"serverSite": "",
"destination": "198.18.187.1/32",
"type": "Unknown",
"cost": null,
"nextHop": "198.18.87.1",
"via": "mm-ospf87-1791553001174",
"area": ""
}
],
"pingReceived": 3
}
],
"cleanup": [
{
"serverId": 1,
"/routing/ospf/interface-template": true,
"/routing/ospf/area": true,
"/routing/ospf/instance": true,
"/interface/gre": true,
"/interface/bridge": true,
"/ip/address": true,
"/ip/route": true
},
{
"serverId": 2,
"/routing/ospf/interface-template": true,
"/routing/ospf/area": true,
"/routing/ospf/instance": true,
"/interface/gre": true,
"/interface/bridge": true,
"/ip/address": true,
"/ip/route": true
}
],
"currentRoutes": [
{
".id": "*20145050",
"dst-address": "198.18.87.0/30",
"gateway": "mm-ospf87-1791553001174",
"active": "true",
"distance": "0",
"routing-table": "main"
},
{
".id": "*2014C000",
"dst-address": "198.18.187.1/32",
"gateway": "198.18.87.1%mm-ospf87-1791553001174",
"active": "true",
"ospf": "true",
"distance": "110",
"routing-table": "main"
},
{
".id": "*20145060",
"dst-address": "198.18.187.2/32",
"gateway": "mm-ospf87-1791553001174-loop",
"active": "true",
"distance": "0",
"routing-table": "main"
}
],
"optimizer": [
{
"serverId": 1,
"preview": {
"serverId": 1,
"serverName": "CHR",
"pingWeight": 60,
"interfacesTotal": 2,
"changedCount": 2,
"unchangedCount": 0,
"interfaces": [
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*1A",
"interface": "mm-ospf87-1791553001174-loop",
"currentCost": 60,
"optimalCost": 10,
"score": 58,
"pingMs": 7,
"dlMbps": 10,
"ulMbps": 10
},
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*19",
"interface": "mm-ospf87-1791553001174",
"currentCost": 50,
"optimalCost": 20,
"score": 33,
"pingMs": 76,
"dlMbps": 10,
"ulMbps": 10
}
],
"changes": [
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*1A",
"interface": "mm-ospf87-1791553001174-loop",
"currentCost": 60,
"optimalCost": 10,
"score": 58,
"pingMs": 7,
"dlMbps": 10,
"ulMbps": 10
},
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*19",
"interface": "mm-ospf87-1791553001174",
"currentCost": 50,
"optimalCost": 20,
"score": 33,
"pingMs": 76,
"dlMbps": 10,
"ulMbps": 10
}
]
},
"applied": {
"serverId": 1,
"serverName": "CHR",
"pingWeight": 60,
"optimizedCount": 2,
"applied": [
{
"interface": "mm-ospf87-1791553001174-loop",
"from": 60,
"to": 10
},
{
"interface": "mm-ospf87-1791553001174",
"from": 50,
"to": 20
}
],
"interfaces": [
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*1A",
"interface": "mm-ospf87-1791553001174-loop",
"currentCost": 60,
"optimalCost": 10,
"score": 58,
"pingMs": 7,
"dlMbps": 10,
"ulMbps": 10
},
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*19",
"interface": "mm-ospf87-1791553001174",
"currentCost": 50,
"optimalCost": 20,
"score": 33,
"pingMs": 76,
"dlMbps": 10,
"ulMbps": 10
}
]
},
"repeatedCount": 0,
"previewReadOnly": true,
"nativeCostsConfirmed": true
},
{
"serverId": 2,
"preview": {
"serverId": 2,
"serverName": "CHR",
"pingWeight": 60,
"interfacesTotal": 2,
"changedCount": 2,
"unchangedCount": 0,
"interfaces": [
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*18",
"interface": "mm-ospf87-1791553001174-loop",
"currentCost": 60,
"optimalCost": 10,
"score": 36,
"pingMs": 69,
"dlMbps": 10,
"ulMbps": 10
},
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*17",
"interface": "mm-ospf87-1791553001174",
"currentCost": 50,
"optimalCost": 20,
"score": 33,
"pingMs": 76,
"dlMbps": 10,
"ulMbps": 10
}
],
"changes": [
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*18",
"interface": "mm-ospf87-1791553001174-loop",
"currentCost": 60,
"optimalCost": 10,
"score": 36,
"pingMs": 69,
"dlMbps": 10,
"ulMbps": 10
},
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*17",
"interface": "mm-ospf87-1791553001174",
"currentCost": 50,
"optimalCost": 20,
"score": 33,
"pingMs": 76,
"dlMbps": 10,
"ulMbps": 10
}
]
},
"applied": {
"serverId": 2,
"serverName": "CHR",
"pingWeight": 60,
"optimizedCount": 2,
"applied": [
{
"interface": "mm-ospf87-1791553001174-loop",
"from": 60,
"to": 10
},
{
"interface": "mm-ospf87-1791553001174",
"from": 50,
"to": 20
}
],
"interfaces": [
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*18",
"interface": "mm-ospf87-1791553001174-loop",
"currentCost": 60,
"optimalCost": 10,
"score": 36,
"pingMs": 69,
"dlMbps": 10,
"ulMbps": 10
},
{
"observed": {
"ping": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"dl": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
},
"ul": {
"value": null,
"source": "unknown",
"sourceId": null,
"measuredAt": null
}
},
"id": "*17",
"interface": "mm-ospf87-1791553001174",
"currentCost": 50,
"optimalCost": 20,
"score": 33,
"pingMs": 76,
"dlMbps": 10,
"ulMbps": 10
}
]
},
"repeatedCount": 0,
"previewReadOnly": true,
"nativeCostsConfirmed": true
}
],
"injected": [
{
"mode": "partial",
"response": {
"error": "API \u043d\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u043b \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 OSPF. \u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u0434 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0439 \u043f\u043e\u043f\u044b\u0442\u043a\u043e\u0439. MikrotikError: RouterOS API error 403 on /routing/ospf/interface-template/*19: injected rejection before transmission",
"serverId": 1,
"state": "partial",
"confirmedWrites": 1,
"applied": [
{
"interface": "mm-ospf87-1791553001174-loop",
"from": 50,
"to": 10
}
],
"failedInterface": "mm-ospf87-1791553001174",
"needsReview": true
},
"nativeCosts": [
{
"id": "*1A",
"interface": "mm-ospf87-1791553001174-loop",
"cost": 10
},
{
"id": "*19",
"interface": "mm-ospf87-1791553001174",
"cost": 60
}
],
"faultInjection": true,
"physicalDisconnect": false
},
{
"mode": "unknown",
"response": {
"error": "API \u043d\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u043b \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 OSPF. \u041e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u0434 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0439 \u043f\u043e\u043f\u044b\u0442\u043a\u043e\u0439. Error: injected lost acknowledgement after native PATCH",
"serverId": 1,
"state": "unknown",
"confirmedWrites": 1,
"applied": [
{
"interface": "mm-ospf87-1791553001174-loop",
"from": 50,
"to": 10
}
],
"failedInterface": "mm-ospf87-1791553001174",
"needsReview": true
},
"nativeCosts": [
{
"id": "*1A",
"interface": "mm-ospf87-1791553001174-loop",
"cost": 10
},
{
"id": "*19",
"interface": "mm-ospf87-1791553001174",
"cost": 20
}
],
"faultInjection": true,
"physicalDisconnect": false
}
],
"effectiveFlags": {
"name": "mm-flags87-1791552793732",
"reads": [
{
"mode": "unset",
"row": {
".id": "*18",
"area": "mm-flags87-1791552793732",
"comment": "mm-flags87-1791552793732",
"cost": "50",
"dead-interval": "40s",
"hello-interval": "10s",
"inactive": "false",
"instance-id": "0",
"interfaces": "mm-flags87-1791552793732",
"passive": "",
"priority": "128",
"retransmit-interval": "5s",
"transmit-delay": "1s",
"type": "broadcast"
},
".id": {
"ret": "false"
},
"numbers": "RouterOS API error 400 on /routing/ospf/interface-template/get: {\"detail\":\"unknown parameter numbers\",\"error\":400,\"message\":\"Bad Request\"}",
"queryFalse": [],
"queryTrue": []
},
{
"mode": "true",
"row": {
".id": "*18",
"area": "mm-flags87-1791552793732",
"comment": "mm-flags87-1791552793732",
"cost": "50",
"dead-interval": "40s",
"disabled": "true",
"hello-interval": "10s",
"inactive": "true",
"instance-id": "0",
"interfaces": "mm-flags87-1791552793732",
"passive": "",
"priority": "128",
"retransmit-interval": "5s",
"transmit-delay": "1s",
"type": "broadcast"
},
".id": {
"ret": "true"
},
"numbers": "RouterOS API error 400 on /routing/ospf/interface-template/get: {\"detail\":\"unknown parameter numbers\",\"error\":400,\"message\":\"Bad Request\"}",
"queryFalse": [],
"queryTrue": [
{
".id": "*18",
"area": "mm-flags87-1791552793732",
"comment": "mm-flags87-1791552793732",
"cost": "50",
"dead-interval": "40s",
"disabled": "true",
"hello-interval": "10s",
"inactive": "true",
"instance-id": "0",
"interfaces": "mm-flags87-1791552793732",
"passive": "",
"priority": "128",
"retransmit-interval": "5s",
"transmit-delay": "1s",
"type": "broadcast"
}
]
},
{
"mode": "false",
"row": {
".id": "*18",
"area": "mm-flags87-1791552793732",
"comment": "mm-flags87-1791552793732",
"cost": "50",
"dead-interval": "40s",
"disabled": "false",
"hello-interval": "10s",
"inactive": "false",
"instance-id": "0",
"interfaces": "mm-flags87-1791552793732",
"passive": "",
"priority": "128",
"retransmit-interval": "5s",
"transmit-delay": "1s",
"type": "broadcast"
},
".id": {
"ret": "false"
},
"numbers": "RouterOS API error 400 on /routing/ospf/interface-template/get: {\"detail\":\"unknown parameter numbers\",\"error\":400,\"message\":\"Bad Request\"}",
"queryFalse": [
{
".id": "*18",
"area": "mm-flags87-1791552793732",
"comment": "mm-flags87-1791552793732",
"cost": "50",
"dead-interval": "40s",
"disabled": "false",
"hello-interval": "10s",
"inactive": "false",
"instance-id": "0",
"interfaces": "mm-flags87-1791552793732",
"passive": "",
"priority": "128",
"retransmit-interval": "5s",
"transmit-delay": "1s",
"type": "broadcast"
}
],
"queryTrue": []
}
],
"cleanup": true,
"otherCollections": [
{
"path": "/routing/ospf/instance",
"row": {
".id": "*C",
"inactive": "false",
"name": "mm-flags87-1791552793732",
"originate-default": "never",
"router-id": "198.18.187.254",
"version": "2",
"vrf": "main"
},
"effectiveDisabled": {
"ret": "false"
}
},
{
"path": "/routing/ospf/area",
"row": {
".id": "*D",
"area-id": "0.0.0.0",
"inactive": "false",
"instance": "mm-flags87-1791552793732",
"name": "mm-flags87-1791552793732",
"type": "default"
},
"effectiveDisabled": {
"ret": "false"
}
}
]
},
"initialFailures": [
{
"error": "Missing disabled on OSPF template",
"cleanupPassed": true
},
{
"error": "Missing disabled on OSPF instance during Router ID owner resolution",
"cleanupPassed": true
}
],
"runtime": {
"backend": "ux-optimizer87c",
"frontend": "ux-optimizer87",
"revisionLabelsAreGitSha": false
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB