diff --git a/backend/scripts/acceptance/acme-live-issue.mjs b/backend/scripts/acceptance/acme-live-issue.mjs new file mode 100644 index 00000000..a3cfc903 --- /dev/null +++ b/backend/scripts/acceptance/acme-live-issue.mjs @@ -0,0 +1,16 @@ +import fs from 'node:fs';import assert from 'node:assert/strict';import {chromium,expect} from '@playwright/test'; +const privatePath=process.env.MMAPP_ACME_FIXTURE||'tmp/acme250-private.json',p=JSON.parse(fs.readFileSync(privatePath)),login=JSON.parse(fs.readFileSync('tmp/portal195-private.json')); +assert(/^mmt250-[a-f0-9]{8}\.pngs\.top$/.test(p.fqdn));assert(!p.jobId,'Never submit an unknown or existing issue job again'); +const report={date:'2026-10-14',block:'12.5.250',fqdn:p.fqdn,certName:p.certName,serverId:p.serverId,realPortalLogin:true,noMocks:true,issuePosts:0,pageErrors:[]}; +const browser=await chromium.launch({channel:'msedge',headless:true}); +try{ + const page=await browser.newPage({viewport:{width:1440,height:1000}});page.on('pageerror',e=>report.pageErrors.push(e.name));page.on('request',r=>{if(new URL(r.url()).pathname==='/api/certificates/issue'&&r.method()==='POST'){const b=r.postDataJSON();assert.equal(String(b.serverId),p.serverId);assert.equal(b.certName,p.certName);assert.deepEqual(b.domainNames,[p.fqdn]);p.jobId=b.requestId;assert(p.jobId);report.issuePosts++;report.jobId=p.jobId;fs.writeFileSync(privatePath,JSON.stringify(p))}}); + await page.goto('http://localhost:3000/certificates?server=2');await page.getByLabel('Email',{exact:true}).fill(login.email);await page.getByLabel('Пароль',{exact:true}).fill(login.password);await page.getByRole('button',{name:'Войти',exact:true}).click();await expect(page.getByRole('navigation',{name:'Путь страницы'})).toContainText('Сертификаты',{timeout:30000}); + const settings=await page.evaluate(async({token,zoneId})=>{const headers={Authorization:'Bearer '+localStorage.getItem('mmapp_token'),'Content-Type':'application/json'};const before=await fetch('/api/certificates/acme-settings',{headers});if(!before.ok)throw Error('Settings read failed');const old=await before.json();if(old.tokenConfigured)throw Error('Existing CF token must not be overwritten');const test=await fetch('/api/certificates/acme-settings/test',{method:'POST',headers,body:JSON.stringify({cloudflareApiToken:token})});const tested=await test.json();if(!test.ok||tested.ok!==true)return{testStatus:test.status,testOk:false,message:tested.message};const put=await fetch('/api/certificates/acme-settings',{method:'PUT',headers,body:JSON.stringify({directoryUrl:'https://acme-v02.api.letsencrypt.org/directory',defaultZoneId:zoneId,cloudflareApiToken:token})});const configured=await put.json();if(!put.ok||!configured.tokenConfigured)throw Error('Settings save failed');return{testStatus:test.status,testOk:true,saveStatus:put.status,tokenConfigured:configured.tokenConfigured,directoryUrl:configured.directoryUrl}}, {token:p.token,zoneId:p.zoneId}); + report.settings=settings;assert.equal(settings.testOk,true); + await page.getByRole('button',{name:'Выпустить сертификат',exact:true}).click();const dialog=page.getByRole('dialog',{name:'Выпуск сертификата'});await dialog.getByRole('combobox',{name:'Сервер выпуска'}).selectOption(p.serverId);await dialog.getByRole('textbox',{name:'Имя сертификата',exact:true}).fill(p.certName);await dialog.getByRole('button',{name:'Далее',exact:true}).click();await dialog.getByRole('textbox',{name:'Основной домен',exact:true}).fill(p.fqdn);await dialog.getByRole('button',{name:'Далее',exact:true}).click();await dialog.getByRole('button',{name:'Далее',exact:true}).click();await expect(dialog).toContainText(p.fqdn); + const response=page.waitForResponse(r=>new URL(r.url()).pathname==='/api/certificates/issue'&&r.request().method()==='POST');await dialog.getByRole('button',{name:'Выпустить',exact:true}).click();const accepted=await response;assert.equal(accepted.status(),200);assert.equal((await accepted.json()).jobId,p.jobId);report.accepted=true;fs.writeFileSync('docs/ux-ui/acceptance/acme250-live-ui.json',JSON.stringify(report,null,2)+'\n');console.log('One ACME issue accepted for '+p.fqdn); + await expect.poll(()=>page.evaluate(async(jobId)=>{const r=await fetch('/api/certificates/issue/'+jobId,{headers:{Authorization:'Bearer '+localStorage.getItem('mmapp_token')}});if(!r.ok)return'unknown';return(await r.json()).status},p.jobId),{timeout:180000,intervals:[2000]}).toMatch(/^(done|failed)$/); + report.job=await page.evaluate(async(jobId)=>{const r=await fetch('/api/certificates/issue/'+jobId,{headers:{Authorization:'Bearer '+localStorage.getItem('mmapp_token')}});const b=await r.json();return{id:b.id,status:b.status,step:b.step,error:b.error}},p.jobId);assert.equal(report.issuePosts,1);assert.equal(report.job.status,'done');await expect(dialog.getByRole('status')).toContainText('Завершена',{timeout:15000});assert.deepEqual(report.pageErrors,[]);report.uiTerminalConfirmed=true; + fs.writeFileSync('docs/ux-ui/acceptance/acme250-live-ui.json',JSON.stringify(report,null,2)+'\n');console.log('ACME UI job done; one issue POST, JS errors0'); +}catch(e){report.failure={name:e.name};fs.writeFileSync('docs/ux-ui/acceptance/acme250-live-ui.json',JSON.stringify(report,null,2)+'\n');console.error('ACME test incomplete: '+e.name);process.exitCode=1}finally{await browser.close()} diff --git a/deploy/TEST-ACME.md b/deploy/TEST-ACME.md new file mode 100644 index 00000000..37a6c75a --- /dev/null +++ b/deploy/TEST-ACME.md @@ -0,0 +1,15 @@ +# Реальная приёмка ACME — 2026-10-14 + +Принят блок 12.5.250: `mmt250-c22a9715.pngs.top`, CHR `104.165.244.7`, production Let's Encrypt DNS-01 / Cloudflare. Пользователь разрешил только собственные поддомены `mmt*` и подтвердил назначение сертификата сервисам с последующей очисткой. Звёздочка обозначает разрешённый префикс; в заявке использовалось точное обычное имя. + +Временные credentials только в ignored private fixture; ключи/почта/account ID в Git не публикуются. Перед запуском проверить token/zone/account и отсутствие записей на точном FQDN и `_acme-challenge.FQDN`. Снять приватный native export, сертификаты/файлы и исходные `www-ssl`/`api-ssl`; сохранить исходную строку `acme_settings`, включая ключ ACME-аккаунта и дату. После окончания восстановить её полностью. + +Текущий issuer создаёт TXT, выполняет ACME, синхронизирует A-запись с IPv4 сервера, импортирует leaf/key/chain и назначает сертификат **обоим** `www-ssl`/`api-ssl`, включая их. Переключатели trust-store не отключают назначение. Поэтому исходные параметры сервисов обязательны для отката. HTTP REST80 используется для управления во время проверки. + +Ручной helper: `backend/scripts/acceptance/acme-live-issue.mjs`, private fixture через `MMAPP_ACME_FIXTURE` (по умолчанию ignored `tmp/acme250-private.json`). Требуется отдельно разрешённый тест, baseline, Portal fixture и SSH-туннель. Helper не заменяет native/DNS очистку; при уже сохранённом jobId новый выпуск блокируется. Неподтверждённый запрос сначала проверять по тому же ID, не отправлять новую задачу. Не запускать этот helper повторно без свежей подготовки: временные credentials после блока удалены. + +Проверено: реальный Portal/UI, один POST issue, terminal done; trusted TLS/SNI/SAN, DNS A → `104.165.244.7`, REST HTTPS200 с обычной проверкой сертификата; RouterOS private key и назначение обоим сервисам. После теста сервисы восстановлены, собственные leaf/chain/files/job удалены, первоначальные коллекции/экспорт/settings совпали. Приложение удалило TXT; собственная A-запись удалена по проверенным имени/типу/content/ID. Даты PostgreSQL сравниваются после JSON-нормализации; неверное сравнение Date со строкой не должно повторять очистку. + +Выданный публичный сертификат CA не объявляется отозванным: экземпляры и ключевые файлы на CHR удалены, DNS очищен; запись о выпуске сохраняется у CA. Это не тест автоматического renewal, CA revocation или нескольких backend. + +Протоколы: [UI](../docs/ux-ui/acceptance/acme250-live-ui.json), [TLS](../docs/ux-ui/acceptance/acme250-tls.json), [DNS](../docs/ux-ui/acceptance/acme250-cloudflare.json), [native/cleanup](../docs/ux-ui/acceptance/acme250-native.json). diff --git a/docs/ux-ui-external-acceptance.md b/docs/ux-ui-external-acceptance.md index 992748ee..2ca6e9b3 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-14: 249 — legacy GET контейнеров не опрашивают выключенный сервер и не превращают ошибки в пустой список. Единый mapper сохраняет unknown, native flags/envlists/mountlists и фактическую память. Backend ux-containers249/frontend ux-containers248; 25 backend-профилей passed за378124ms, три специализированные SQL-ветки явно skipped. Owned БД/роль/container удалены. Real Portal/read UI1440/390 passed, writes0. Следующий250 — ACME на собственном mmt* поддомене pngs.top; предоставленный токен проверен read-only, выпуск ещё не выполнен. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 250 — production ACME принят на точном mmt250-c22a9715.pngs.top/CHR104: Portal/UI один issue POST → done; DNS-01, native import/private key, trusted TLS/SAN и REST HTTPS200. Native services/certificates/files/export и ACME settings восстановлены, собственные job/A/TXT отсутствуют; оба CHR доступны. Backend249/frontend248 unchanged. Следующий251 — раскрыть в форме выпуска автоматическое назначение SSL-сервисам и DNS A-sync, затем проверить UI; renewal/CA revoke/multi-instance отдельно. Этап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). @@ -18,6 +18,22 @@ OSPF84: real GRE Full adjacency/private routes/Ping3/3 и4 live PC/320px URL/bro BFD85: Up/Full/own route/Ping,4 live UI и loss/withdrawal/recovery приняты; exact own cleanup8 коллекций×2 passed. Withdrawal5,245ms не подтверждает sub-second BFD timing; этот вопрос открыт. Runtime routing83 без новой сборки. [Отчёт](ux-ui/acceptance/ospf-bfd-loss-2026-10-09.json). +## 12.5.250 — production ACME / Cloudflare / TLS через реальный UI (2026-10-14) + +Пользователь предоставил pngs.top/Cloudflare и разрешил поддомены с префиксом mmt*. После уточнения звёздочки получено согласие на точное имя mmt250-c22a9715.pngs.top и временное назначение www-ssl/api-ssl с откатом. Cloudflare token verify и exact zone query200/success, token/zone active/account matched; оба собственных DNS имени до начала имели0 records. Ключ/почта/account ID только в ignored private fixture; на Debian ключ поступил через разрешённый временный API settings, отдельный secret файл туда не передавался. Native/private settings baseline снят до начала. + +Настоящий Portal/UI без mocks: app Cloudflare test200, settings save200; выбран server2/CHR104, один POST issue с точным CN →200/jobId; backend queued/running →done, UI terminal «Завершена». Production directory Let's Encrypt, не staging/fake CA. CSR/DNS-01/импорт leaf+key+chain прошли; native leaf CN/private-key=true и оба SSL сервиса enabled с этим сертификатом подтверждены. [UI](ux-ui/acceptance/acme250-live-ui.json). + +Независимая проверка: DNS A104.165.244.7; TLSv1.2, обычный rejectUnauthorized=true, peer authorized/SAN exact domain, issuerYR2, срок2026-10-14→2027-01-12; REST по HTTPS200 с прежними учётными данными. TLS проверка не отключалась, CA в локальное доверие не добавлялась. Это проверка конкретного CA/RouterOS клиента, не всех TLS версий/клиентских ОС. [TLS](ux-ui/acceptance/acme250-tls.json). + +Очистка: сначала исходные disabled/certificate/port SSL сервисов, затем только собственные cert/root-chain и PEM files. Исходные certificate/file коллекции и полный нормализованный export совпали. ACME settings восстановлены полностью, включая accountPrivateKey/updatedAt; own terminal job удалён, чтобы не оставлять managed renewal target. Историческое событие теста в журнале приложения сохраняется. После фактического COMMIT начальная проверка Date vs JSON string дала assertion; исправлена только JSON-нормализация и повторное read-only сравнение, native/SQL операции не повторялись. Приватный baseline удалён. [Native](ux-ui/acceptance/acme250-native.json). + +Application TXT cleanup подтверждён GET0; A-sync создал единственную собственную A с content104/proxied=false, она удалена по exact name/type/content/ID. Final FQDN/challenge records0. Остальные DNS записи/zone settings не менялись. [Cloudflare](ux-ui/acceptance/acme250-cloudflare.json). Post-cleanup app settings tokenConfigured=false, own job404/certificate absent, оба CHR certificate read доступны. Выданный сертификат не объявляется отозванным у CA; запись о выпуске остаётся, native экземпляры/ключевые файлы удалены. Все временные credential файлы блока удалены после проверки. + +Runtime backend ux-containers249/frontend ux-containers248 unchanged; source changes только manual acceptance helper/документация, не production код. [Процедура](../deploy/TEST-ACME.md), helper acme-live-issue.mjs syntax/ESLint passed. Backend-профили и E2E не повторялись для документального блока:25 профилей249 и29 E2E248 уже проверены в своих границах. Дополнительный local locator/copy helper ремонт не считается новым live сценарием. + +Следующий251 — явное пояснение последствий в форме: текущий issuer всегда включает www-ssl/api-ssl и синхронизирует DNS A, а экран говорит лишь об импорте/trust-store. Изменить copy существующих шагов, сохранив сетевую логику; профильные UI проверки/build/deploy. Automatic renewal, CA revocation, remote release publishing/multi-instance/WAN и остальные обязательные варианты матрицы ещё не приняты этим выпуском. + ## 12.5.249 — достоверное legacy чтение контейнеров и общая backend-регрессия (2026-10-14) Аудит выявил: GET выбранного сервера обходил enabled gate; legacy mapper считал отсутствие status остановкой, memory-high — фактической памятью, скрывал ошибки загрузки пустым массивом. Прямой GET теперь даёт409 для выключенного и404 для отсутствующего сервера до dispatch; прямой service/read тоже не создаёт RouterOS клиент для disabled, aggregate исключает disabled до reader factory. Ошибки/частичные ответы legacy GET дают502; unsupported сохраняет прежний legacy [] для совместимости, подробное availability предоставляется /api/containers/state. ID разбирается общим строгим parseDbServerId, без принятия префикса1abc. diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md index d4beab96..dfb2f1de 100644 --- a/docs/ux-ui-plan.md +++ b/docs/ux-ui-plan.md @@ -1,9 +1,25 @@ # План реализации UX/UI по пятичасовым окнам -Актуальное продолжение 2026-10-14: 249 — legacy GET контейнеров не опрашивают выключенный сервер и не превращают ошибки в пустой список. Единый mapper сохраняет unknown, native flags/envlists/mountlists и фактическую память. Backend ux-containers249/frontend ux-containers248; 25 backend-профилей passed за378124ms, три специализированные SQL-ветки явно skipped. Owned БД/роль/container удалены. Real Portal/read UI1440/390 passed, writes0. Следующий250 — ACME на собственном mmt* поддомене pngs.top; предоставленный токен проверен read-only, выпуск ещё не выполнен. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 250 — production ACME принят на точном mmt250-c22a9715.pngs.top/CHR104: Portal/UI один issue POST → done; DNS-01, native import/private key, trusted TLS/SAN и REST HTTPS200. Native services/certificates/files/export и ACME settings восстановлены, собственные job/A/TXT отсутствуют; оба CHR доступны. Backend249/frontend248 unchanged. Следующий251 — раскрыть в форме выпуска автоматическое назначение SSL-сервисам и DNS A-sync, затем проверить UI; renewal/CA revoke/multi-instance отдельно. Этап12.5 открыт. Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-14. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат. +## 12.5.250 — production ACME / Cloudflare / TLS через реальный UI (2026-10-14) + +Пользователь предоставил pngs.top/Cloudflare и разрешил поддомены с префиксом mmt*. После уточнения звёздочки получено согласие на точное имя mmt250-c22a9715.pngs.top и временное назначение www-ssl/api-ssl с откатом. Cloudflare token verify и exact zone query200/success, token/zone active/account matched; оба собственных DNS имени до начала имели0 records. Ключ/почта/account ID только в ignored private fixture; на Debian ключ поступил через разрешённый временный API settings, отдельный secret файл туда не передавался. Native/private settings baseline снят до начала. + +Настоящий Portal/UI без mocks: app Cloudflare test200, settings save200; выбран server2/CHR104, один POST issue с точным CN →200/jobId; backend queued/running →done, UI terminal «Завершена». Production directory Let's Encrypt, не staging/fake CA. CSR/DNS-01/импорт leaf+key+chain прошли; native leaf CN/private-key=true и оба SSL сервиса enabled с этим сертификатом подтверждены. [UI](ux-ui/acceptance/acme250-live-ui.json). + +Независимая проверка: DNS A104.165.244.7; TLSv1.2, обычный rejectUnauthorized=true, peer authorized/SAN exact domain, issuerYR2, срок2026-10-14→2027-01-12; REST по HTTPS200 с прежними учётными данными. TLS проверка не отключалась, CA в локальное доверие не добавлялась. Это проверка конкретного CA/RouterOS клиента, не всех TLS версий/клиентских ОС. [TLS](ux-ui/acceptance/acme250-tls.json). + +Очистка: сначала исходные disabled/certificate/port SSL сервисов, затем только собственные cert/root-chain и PEM files. Исходные certificate/file коллекции и полный нормализованный export совпали. ACME settings восстановлены полностью, включая accountPrivateKey/updatedAt; own terminal job удалён, чтобы не оставлять managed renewal target. Историческое событие теста в журнале приложения сохраняется. После фактического COMMIT начальная проверка Date vs JSON string дала assertion; исправлена только JSON-нормализация и повторное read-only сравнение, native/SQL операции не повторялись. Приватный baseline удалён. [Native](ux-ui/acceptance/acme250-native.json). + +Application TXT cleanup подтверждён GET0; A-sync создал единственную собственную A с content104/proxied=false, она удалена по exact name/type/content/ID. Final FQDN/challenge records0. Остальные DNS записи/zone settings не менялись. [Cloudflare](ux-ui/acceptance/acme250-cloudflare.json). Post-cleanup app settings tokenConfigured=false, own job404/certificate absent, оба CHR certificate read доступны. Выданный сертификат не объявляется отозванным у CA; запись о выпуске остаётся, native экземпляры/ключевые файлы удалены. Все временные credential файлы блока удалены после проверки. + +Runtime backend ux-containers249/frontend ux-containers248 unchanged; source changes только manual acceptance helper/документация, не production код. [Процедура](../deploy/TEST-ACME.md), helper acme-live-issue.mjs syntax/ESLint passed. Backend-профили и E2E не повторялись для документального блока:25 профилей249 и29 E2E248 уже проверены в своих границах. Дополнительный local locator/copy helper ремонт не считается новым live сценарием. + +Следующий251 — явное пояснение последствий в форме: текущий issuer всегда включает www-ssl/api-ssl и синхронизирует DNS A, а экран говорит лишь об импорте/trust-store. Изменить copy существующих шагов, сохранив сетевую логику; профильные UI проверки/build/deploy. Automatic renewal, CA revocation, remote release publishing/multi-instance/WAN и остальные обязательные варианты матрицы ещё не приняты этим выпуском. + ## 12.5.249 — достоверное legacy чтение контейнеров и общая backend-регрессия (2026-10-14) Аудит выявил: GET выбранного сервера обходил enabled gate; legacy mapper считал отсутствие status остановкой, memory-high — фактической памятью, скрывал ошибки загрузки пустым массивом. Прямой GET теперь даёт409 для выключенного и404 для отсутствующего сервера до dispatch; прямой service/read тоже не создаёт RouterOS клиент для disabled, aggregate исключает disabled до reader factory. Ошибки/частичные ответы legacy GET дают502; unsupported сохраняет прежний legacy [] для совместимости, подробное availability предоставляется /api/containers/state. ID разбирается общим строгим parseDbServerId, без принятия префикса1abc. diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md index b31ac57d..b89087de 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -1,6 +1,6 @@ # UX/UI: журнал реализации -Актуальное продолжение 2026-10-14: 249 — legacy GET контейнеров не опрашивают выключенный сервер и не превращают ошибки в пустой список. Единый mapper сохраняет unknown, native flags/envlists/mountlists и фактическую память. Backend ux-containers249/frontend ux-containers248; 25 backend-профилей passed за378124ms, три специализированные SQL-ветки явно skipped. Owned БД/роль/container удалены. Real Portal/read UI1440/390 passed, writes0. Следующий250 — ACME на собственном mmt* поддомене pngs.top; предоставленный токен проверен read-only, выпуск ещё не выполнен. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 250 — production ACME принят на точном mmt250-c22a9715.pngs.top/CHR104: Portal/UI один issue POST → done; DNS-01, native import/private key, trusted TLS/SAN и REST HTTPS200. Native services/certificates/files/export и ACME settings восстановлены, собственные job/A/TXT отсутствуют; оба CHR доступны. Backend249/frontend248 unchanged. Следующий251 — раскрыть в форме выпуска автоматическое назначение SSL-сервисам и DNS A-sync, затем проверить UI; renewal/CA revoke/multi-instance отдельно. Этап12.5 открыт. Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md). @@ -38,6 +38,22 @@ | 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 | | 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка12.3.83 в границах журнала. Внешняя12.5: блоки1–236 завершены в записанных scopes, этап открыт. Runtime backend ux-backup234/frontend ux-routing218; backend25/UI52 и дополнительные SQL/UDP222 прошли. | +## 12.5.250 — production ACME / Cloudflare / TLS через реальный UI (2026-10-14) + +Пользователь предоставил pngs.top/Cloudflare и разрешил поддомены с префиксом mmt*. После уточнения звёздочки получено согласие на точное имя mmt250-c22a9715.pngs.top и временное назначение www-ssl/api-ssl с откатом. Cloudflare token verify и exact zone query200/success, token/zone active/account matched; оба собственных DNS имени до начала имели0 records. Ключ/почта/account ID только в ignored private fixture; на Debian ключ поступил через разрешённый временный API settings, отдельный secret файл туда не передавался. Native/private settings baseline снят до начала. + +Настоящий Portal/UI без mocks: app Cloudflare test200, settings save200; выбран server2/CHR104, один POST issue с точным CN →200/jobId; backend queued/running →done, UI terminal «Завершена». Production directory Let's Encrypt, не staging/fake CA. CSR/DNS-01/импорт leaf+key+chain прошли; native leaf CN/private-key=true и оба SSL сервиса enabled с этим сертификатом подтверждены. [UI](ux-ui/acceptance/acme250-live-ui.json). + +Независимая проверка: DNS A104.165.244.7; TLSv1.2, обычный rejectUnauthorized=true, peer authorized/SAN exact domain, issuerYR2, срок2026-10-14→2027-01-12; REST по HTTPS200 с прежними учётными данными. TLS проверка не отключалась, CA в локальное доверие не добавлялась. Это проверка конкретного CA/RouterOS клиента, не всех TLS версий/клиентских ОС. [TLS](ux-ui/acceptance/acme250-tls.json). + +Очистка: сначала исходные disabled/certificate/port SSL сервисов, затем только собственные cert/root-chain и PEM files. Исходные certificate/file коллекции и полный нормализованный export совпали. ACME settings восстановлены полностью, включая accountPrivateKey/updatedAt; own terminal job удалён, чтобы не оставлять managed renewal target. Историческое событие теста в журнале приложения сохраняется. После фактического COMMIT начальная проверка Date vs JSON string дала assertion; исправлена только JSON-нормализация и повторное read-only сравнение, native/SQL операции не повторялись. Приватный baseline удалён. [Native](ux-ui/acceptance/acme250-native.json). + +Application TXT cleanup подтверждён GET0; A-sync создал единственную собственную A с content104/proxied=false, она удалена по exact name/type/content/ID. Final FQDN/challenge records0. Остальные DNS записи/zone settings не менялись. [Cloudflare](ux-ui/acceptance/acme250-cloudflare.json). Post-cleanup app settings tokenConfigured=false, own job404/certificate absent, оба CHR certificate read доступны. Выданный сертификат не объявляется отозванным у CA; запись о выпуске остаётся, native экземпляры/ключевые файлы удалены. Все временные credential файлы блока удалены после проверки. + +Runtime backend ux-containers249/frontend ux-containers248 unchanged; source changes только manual acceptance helper/документация, не production код. [Процедура](../deploy/TEST-ACME.md), helper acme-live-issue.mjs syntax/ESLint passed. Backend-профили и E2E не повторялись для документального блока:25 профилей249 и29 E2E248 уже проверены в своих границах. Дополнительный local locator/copy helper ремонт не считается новым live сценарием. + +Следующий251 — явное пояснение последствий в форме: текущий issuer всегда включает www-ssl/api-ssl и синхронизирует DNS A, а экран говорит лишь об импорте/trust-store. Изменить copy существующих шагов, сохранив сетевую логику; профильные UI проверки/build/deploy. Automatic renewal, CA revocation, remote release publishing/multi-instance/WAN и остальные обязательные варианты матрицы ещё не приняты этим выпуском. + ## 12.5.249 — достоверное legacy чтение контейнеров и общая backend-регрессия (2026-10-14) Аудит выявил: GET выбранного сервера обходил enabled gate; legacy mapper считал отсутствие status остановкой, memory-high — фактической памятью, скрывал ошибки загрузки пустым массивом. Прямой GET теперь даёт409 для выключенного и404 для отсутствующего сервера до dispatch; прямой service/read тоже не создаёт RouterOS клиент для disabled, aggregate исключает disabled до reader factory. Ошибки/частичные ответы legacy GET дают502; unsupported сохраняет прежний legacy [] для совместимости, подробное availability предоставляется /api/containers/state. ID разбирается общим строгим parseDbServerId, без принятия префикса1abc. diff --git a/docs/ux-ui-remaining-acceptance.md b/docs/ux-ui-remaining-acceptance.md index d0d2a276..15448e2f 100644 --- a/docs/ux-ui-remaining-acceptance.md +++ b/docs/ux-ui-remaining-acceptance.md @@ -1,11 +1,27 @@ # Оставшаяся внешняя приёмка — 2026-10-14 -Актуальное продолжение 2026-10-14: 249 — legacy GET контейнеров не опрашивают выключенный сервер и не превращают ошибки в пустой список. Единый mapper сохраняет unknown, native flags/envlists/mountlists и фактическую память. Backend ux-containers249/frontend ux-containers248; 25 backend-профилей passed за378124ms, три специализированные SQL-ветки явно skipped. Owned БД/роль/container удалены. Real Portal/read UI1440/390 passed, writes0. Следующий250 — ACME на собственном mmt* поддомене pngs.top; предоставленный токен проверен read-only, выпуск ещё не выполнен. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 250 — production ACME принят на точном mmt250-c22a9715.pngs.top/CHR104: Portal/UI один issue POST → done; DNS-01, native import/private key, trusted TLS/SAN и REST HTTPS200. Native services/certificates/files/export и ACME settings восстановлены, собственные job/A/TXT отсутствуют; оба CHR доступны. Backend249/frontend248 unchanged. Следующий251 — раскрыть в форме выпуска автоматическое назначение SSL-сервисам и DNS A-sync, затем проверить UI; renewal/CA revoke/multi-instance отдельно. Этап12.5 открыт. -Это актуальная матрица после 12.5.249. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. +Это актуальная матрица после 12.5.250. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-containers249`, frontend `ux-containers248`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись. +## 12.5.250 — production ACME / Cloudflare / TLS через реальный UI (2026-10-14) + +Пользователь предоставил pngs.top/Cloudflare и разрешил поддомены с префиксом mmt*. После уточнения звёздочки получено согласие на точное имя mmt250-c22a9715.pngs.top и временное назначение www-ssl/api-ssl с откатом. Cloudflare token verify и exact zone query200/success, token/zone active/account matched; оба собственных DNS имени до начала имели0 records. Ключ/почта/account ID только в ignored private fixture; на Debian ключ поступил через разрешённый временный API settings, отдельный secret файл туда не передавался. Native/private settings baseline снят до начала. + +Настоящий Portal/UI без mocks: app Cloudflare test200, settings save200; выбран server2/CHR104, один POST issue с точным CN →200/jobId; backend queued/running →done, UI terminal «Завершена». Production directory Let's Encrypt, не staging/fake CA. CSR/DNS-01/импорт leaf+key+chain прошли; native leaf CN/private-key=true и оба SSL сервиса enabled с этим сертификатом подтверждены. [UI](ux-ui/acceptance/acme250-live-ui.json). + +Независимая проверка: DNS A104.165.244.7; TLSv1.2, обычный rejectUnauthorized=true, peer authorized/SAN exact domain, issuerYR2, срок2026-10-14→2027-01-12; REST по HTTPS200 с прежними учётными данными. TLS проверка не отключалась, CA в локальное доверие не добавлялась. Это проверка конкретного CA/RouterOS клиента, не всех TLS версий/клиентских ОС. [TLS](ux-ui/acceptance/acme250-tls.json). + +Очистка: сначала исходные disabled/certificate/port SSL сервисов, затем только собственные cert/root-chain и PEM files. Исходные certificate/file коллекции и полный нормализованный export совпали. ACME settings восстановлены полностью, включая accountPrivateKey/updatedAt; own terminal job удалён, чтобы не оставлять managed renewal target. Историческое событие теста в журнале приложения сохраняется. После фактического COMMIT начальная проверка Date vs JSON string дала assertion; исправлена только JSON-нормализация и повторное read-only сравнение, native/SQL операции не повторялись. Приватный baseline удалён. [Native](ux-ui/acceptance/acme250-native.json). + +Application TXT cleanup подтверждён GET0; A-sync создал единственную собственную A с content104/proxied=false, она удалена по exact name/type/content/ID. Final FQDN/challenge records0. Остальные DNS записи/zone settings не менялись. [Cloudflare](ux-ui/acceptance/acme250-cloudflare.json). Post-cleanup app settings tokenConfigured=false, own job404/certificate absent, оба CHR certificate read доступны. Выданный сертификат не объявляется отозванным у CA; запись о выпуске остаётся, native экземпляры/ключевые файлы удалены. Все временные credential файлы блока удалены после проверки. + +Runtime backend ux-containers249/frontend ux-containers248 unchanged; source changes только manual acceptance helper/документация, не production код. [Процедура](../deploy/TEST-ACME.md), helper acme-live-issue.mjs syntax/ESLint passed. Backend-профили и E2E не повторялись для документального блока:25 профилей249 и29 E2E248 уже проверены в своих границах. Дополнительный local locator/copy helper ремонт не считается новым live сценарием. + +Следующий251 — явное пояснение последствий в форме: текущий issuer всегда включает www-ssl/api-ssl и синхронизирует DNS A, а экран говорит лишь об импорте/trust-store. Изменить copy существующих шагов, сохранив сетевую логику; профильные UI проверки/build/deploy. Automatic renewal, CA revocation, remote release publishing/multi-instance/WAN и остальные обязательные варианты матрицы ещё не приняты этим выпуском. + ## 12.5.249 — достоверное legacy чтение контейнеров и общая backend-регрессия (2026-10-14) Аудит выявил: GET выбранного сервера обходил enabled gate; legacy mapper считал отсутствие status остановкой, memory-high — фактической памятью, скрывал ошибки загрузки пустым массивом. Прямой GET теперь даёт409 для выключенного и404 для отсутствующего сервера до dispatch; прямой service/read тоже не создаёт RouterOS клиент для disabled, aggregate исключает disabled до reader factory. Ошибки/частичные ответы legacy GET дают502; unsupported сохраняет прежний legacy [] для совместимости, подробное availability предоставляется /api/containers/state. ID разбирается общим строгим parseDbServerId, без принятия префикса1abc. @@ -203,14 +219,14 @@ Portal offline JWT действует доexp после logout/role change; э | BGP | Established83, exact /32 exchange/API/UI/administrative withdrawal92, BFD primary GRE loss/alternate/recovery93; basic native IPv6 eBGP/AFI/NLRI/Ping/withdrawal/API/UI155; native IPv6 BGP/BFD scoped withdrawal/recovery160; private4-byte ASN/as4/API/UI162 | Hold timer/TCP loss безBFD принят104; packet loss в переходе, разные внешние peers/WAN policy, basic BFDv6 принят160; IPv6 outer/public WAN/scale. Два логических пути используют тот же public WAN; physical redundancy не принята | | Recursive routes | Production PUT/change/own history restore88, disabled guards, partial/unknown/applied outcomes, live формы/контекст | Автоматический rollback/retry, durable receipts и несколько экземпляров отсутствуют; PUT не является idempotent noop. Полная произвольная legacy catalog миграция не принята | | Diagnostics/terminal | Allowlist/native reads, Ping/trace/route/MTU/DNS, actual avg-rtt, disabled guards, JWT/live PC/mobile89; real default memory tail/log limits/API/UI133 | DNS использует backend resolver; MTU — ограниченная оценка, не гарантированный максимум. Stop ожидания не доказывает остановку RouterOS команды; arbitrary CLI и physical disconnect не приняты | -| Portal/интеграции | Portal1.3.1/issuer/login/RBAC/expiry/context195–199, actual Telegram201; Selectel S3 native/API/live limited restore/retention228–240; real GeoIP mirror242 | Offline JWT сохраняетдоступ доexp; S3 large-native/migration/multi-instance отдельно; containers lifecycle248 принят в записанных границах; Cloudflare DNS ACME test-domain/token и remote Gitea publishing не приняты; direct licensed MaxMind не реализован. ClientOS/scale/WAN отдельно | +| Portal/интеграции | Portal1.3.1/issuer/login/RBAC/expiry/context195–199, actual Telegram201; Selectel S3 native/API/live limited restore/retention228–240; real GeoIP mirror242 | Offline JWT сохраняетдоступ доexp; S3 large-native/migration/multi-instance отдельно; containers lifecycle248 принят в записанных границах; ACME issuance250 принят; automatic renewal/CA revoke и remote Gitea publishing не приняты; direct licensed MaxMind не реализован. ClientOS/scale/WAN отдельно | | Общая приёмка | Локальная12.3.83; UI1244/context221/compiled984 parity139; профильные build/E2E/live проверки последующих блоков | Traffic initial live mock-id gate passed141; IPv6 special ranges/cache passed142; unknown RIPE coordinates passed143; IPv6 synthetic UDP/worker/SQL passed144; native CHR ULA/IPFIX/API/UI passed145; ICMPv6/IPv6 endpoint labels passed146; HTTP DTO validation passed147; scoped flow catalog/serverId guards passed148; strict monthly calendar passed149; Sessions labels passed150; application GRE static IPv6 lifecycle151 passed; WireGuard static IPv6 lifecycle152 и disabled SQL/API gates passed; OSPF/BFD boundary/warning153 passed; OSPFv3 routes154 accepted in recorded bounds; BGP IPv6 basic155 passed; bigint ID/SQL guards156 passed; общая UI1260/context221/backend24 regression157 passed; IPv6 BFD correlation158 profile/deployed passed; native OSPFv3/BFDv6 boundary/recovery159 passed; BGP IPv6/BFD withdrawal/recovery160 passed; selected BGP/alert unknown guards161 replay/profiles/deployed passed (SQL stability skipped); private4-byte ASN162 native/API/UI passed; duplicate BGP alert keys163 replay/profiles/deployed passed; nullable selected BGP164 replay/profiles/API/UI passed; collector165 parallel/current guards/read-only PG passed; positive partial/error SQL166/full24 with PG passed (optional network SQL not repeated); GRE/BGP freshness167/SQL8 passed; other source audit168 выявил identity/disabled/freshness gaps; локальный server/traffic identity guard169 passed без SQL/deploy, source/per-probe/freshness170/ownSQL16/compiled1008/deployed passed; source diagnostics171/42E2E/ownSQL metadata/live2 passed; monitoring history172/ownSQL26/compiled1020/deployed passed; Ping known/error173/ownHTTP-SQL8/85uniqueE2E/native4/4/deployed passed; traffic confidence174 baseline reproduced/local guard passed; owned SQL9/9358ms/deploy175/1024compiled/live smoke passed; legacy history/provenance open; compound RTT176 local/native replay2 passed, SQL10/deploy177/1024compiled/live smoke passed; history178/legacy provenance/current TTL/manual truth/SQL9+14/35unique E2E/native GET/deployed passed; live179 complete/disable-config/bounded fallback/ownSQL10/nativeSSE2/liveUI2 passed; volume180 exact/unit marker/ownSQL6/18E2E/API/liveUI/deployed passed; binding status181 native/ownSQL9/8E2E/deployed passed; peer atomicity182 ownSQL3/native/API/UI passed; peer failure warning183 ownSQL2/4E2E/deployed passed; inactive history184 ownSQL2/deployed passed; gap185/identity186/restore187/wave188/sourceguard189/confirmed peak190 passed; aggregate peaks191, request dedupe193, cross-process lock194/200, full regression192 (1286 unique E2E и25-й профиль privacy196 scoped), Portal195–199/Telegram201 passed; последующие изменения проверять отдельно; scoped axe не означает полную WCAG сертификацию | Доказательства и точные ограничения: [полная история этапа12](ux-ui-stage-12.md), [внешний протокол](ux-ui-external-acceptance.md), [BGP93](ux-ui/acceptance/bgp-failover-2026-10-10.json), [паспорт стенда](ux-ui-test-server.md). Невыполненные пункты не являются все блокерами данного лабораторного сценария: для выпуска необходимо явно определить требуемые конфигурации и принять их либо согласовать ограничение. ## Следующая последовательность — 2026-10-14 -1. **ACME250:** стенд pngs.top доступен; разрешён mmt* scope, token/zone verified read-only. Проверить реальную выдачу/импорт/результат и очистку. Containers248–249 приняты; общая backend-регрессия249 passed с тремя записанными SQL skips. Remote Gitea publishing требует своего разрешённого scope. +1. **Сертификаты251:** issuance250 принят; раскрыть в UI включение обоих SSL-сервисов и DNS A-sync. Затем определить scope для automatic renewal/CA revoke/remote publishing/multi-instance; эти сценарии текущим выпуском не доказаны. 2. **Профильный контроль:** для новых изменений выполнять затронутые SQL/API/UI проверки. Общая регрессия192 выполнена; не повторять её без изменений общей основы или нового основания. 3. **Внешние зависимости:** Portal issuer/login/revoke, Telegram bot/chat/разрешённое сообщение, клиентские ОС, независимые WAN/public IPv6 и scale/multi-instance — отдельные группы по таблице выше. Ограничения текущего стенда не превращать в обещание приёмки всех конфигураций. diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md index ac850b95..61d2130e 100644 --- a/docs/ux-ui-stage-12.md +++ b/docs/ux-ui-stage-12.md @@ -1,6 +1,6 @@ # Этап 12: итоговая приёмка UX/UI -Актуальное продолжение 2026-10-14: 249 — legacy GET контейнеров не опрашивают выключенный сервер и не превращают ошибки в пустой список. Единый mapper сохраняет unknown, native flags/envlists/mountlists и фактическую память. Backend ux-containers249/frontend ux-containers248; 25 backend-профилей passed за378124ms, три специализированные SQL-ветки явно skipped. Owned БД/роль/container удалены. Real Portal/read UI1440/390 passed, writes0. Следующий250 — ACME на собственном mmt* поддомене pngs.top; предоставленный токен проверен read-only, выпуск ещё не выполнен. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 250 — production ACME принят на точном mmt250-c22a9715.pngs.top/CHR104: Portal/UI один issue POST → done; DNS-01, native import/private key, trusted TLS/SAN и REST HTTPS200. Native services/certificates/files/export и ACME settings восстановлены, собственные job/A/TXT отсутствуют; оба CHR доступны. Backend249/frontend248 unchanged. Следующий251 — раскрыть в форме выпуска автоматическое назначение SSL-сервисам и DNS A-sync, затем проверить UI; renewal/CA revoke/multi-instance отдельно. Этап12.5 открыт. План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push. @@ -4152,6 +4152,22 @@ Real Portal/browser API, provider local verified: создан один job ро Runtime backend ux-backup234/frontend ux-routing218 healthy; новое application image для volume-only fix не требовалось. Пароли CHR не менялись, S3 mutations0 в235, configs body/JWT не опубликованы, private local manifest удалён. Исходные10 missing local files не восстановлены и records не удалены. Force-recreate — не host reboot/power loss/fsync/DR приёмка. Далее236 — UX недоступных local files, затем version retention границы и оставшиеся7 группы матрицы; multi-instance guard не принимается. Этап12.5 открыт. +## 12.5.250 — production ACME / Cloudflare / TLS через реальный UI (2026-10-14) + +Пользователь предоставил pngs.top/Cloudflare и разрешил поддомены с префиксом mmt*. После уточнения звёздочки получено согласие на точное имя mmt250-c22a9715.pngs.top и временное назначение www-ssl/api-ssl с откатом. Cloudflare token verify и exact zone query200/success, token/zone active/account matched; оба собственных DNS имени до начала имели0 records. Ключ/почта/account ID только в ignored private fixture; на Debian ключ поступил через разрешённый временный API settings, отдельный secret файл туда не передавался. Native/private settings baseline снят до начала. + +Настоящий Portal/UI без mocks: app Cloudflare test200, settings save200; выбран server2/CHR104, один POST issue с точным CN →200/jobId; backend queued/running →done, UI terminal «Завершена». Production directory Let's Encrypt, не staging/fake CA. CSR/DNS-01/импорт leaf+key+chain прошли; native leaf CN/private-key=true и оба SSL сервиса enabled с этим сертификатом подтверждены. [UI](ux-ui/acceptance/acme250-live-ui.json). + +Независимая проверка: DNS A104.165.244.7; TLSv1.2, обычный rejectUnauthorized=true, peer authorized/SAN exact domain, issuerYR2, срок2026-10-14→2027-01-12; REST по HTTPS200 с прежними учётными данными. TLS проверка не отключалась, CA в локальное доверие не добавлялась. Это проверка конкретного CA/RouterOS клиента, не всех TLS версий/клиентских ОС. [TLS](ux-ui/acceptance/acme250-tls.json). + +Очистка: сначала исходные disabled/certificate/port SSL сервисов, затем только собственные cert/root-chain и PEM files. Исходные certificate/file коллекции и полный нормализованный export совпали. ACME settings восстановлены полностью, включая accountPrivateKey/updatedAt; own terminal job удалён, чтобы не оставлять managed renewal target. Историческое событие теста в журнале приложения сохраняется. После фактического COMMIT начальная проверка Date vs JSON string дала assertion; исправлена только JSON-нормализация и повторное read-only сравнение, native/SQL операции не повторялись. Приватный baseline удалён. [Native](ux-ui/acceptance/acme250-native.json). + +Application TXT cleanup подтверждён GET0; A-sync создал единственную собственную A с content104/proxied=false, она удалена по exact name/type/content/ID. Final FQDN/challenge records0. Остальные DNS записи/zone settings не менялись. [Cloudflare](ux-ui/acceptance/acme250-cloudflare.json). Post-cleanup app settings tokenConfigured=false, own job404/certificate absent, оба CHR certificate read доступны. Выданный сертификат не объявляется отозванным у CA; запись о выпуске остаётся, native экземпляры/ключевые файлы удалены. Все временные credential файлы блока удалены после проверки. + +Runtime backend ux-containers249/frontend ux-containers248 unchanged; source changes только manual acceptance helper/документация, не production код. [Процедура](../deploy/TEST-ACME.md), helper acme-live-issue.mjs syntax/ESLint passed. Backend-профили и E2E не повторялись для документального блока:25 профилей249 и29 E2E248 уже проверены в своих границах. Дополнительный local locator/copy helper ремонт не считается новым live сценарием. + +Следующий251 — явное пояснение последствий в форме: текущий issuer всегда включает www-ssl/api-ssl и синхронизирует DNS A, а экран говорит лишь об импорте/trust-store. Изменить copy существующих шагов, сохранив сетевую логику; профильные UI проверки/build/deploy. Automatic renewal, CA revocation, remote release publishing/multi-instance/WAN и остальные обязательные варианты матрицы ещё не приняты этим выпуском. + ## 12.5.249 — достоверное legacy чтение контейнеров и общая backend-регрессия (2026-10-14) Аудит выявил: GET выбранного сервера обходил enabled gate; legacy mapper считал отсутствие status остановкой, memory-high — фактической памятью, скрывал ошибки загрузки пустым массивом. Прямой GET теперь даёт409 для выключенного и404 для отсутствующего сервера до dispatch; прямой service/read тоже не создаёт RouterOS клиент для disabled, aggregate исключает disabled до reader factory. Ошибки/частичные ответы legacy GET дают502; unsupported сохраняет прежний legacy [] для совместимости, подробное availability предоставляется /api/containers/state. ID разбирается общим строгим parseDbServerId, без принятия префикса1abc. diff --git a/docs/ux-ui-test-server.md b/docs/ux-ui-test-server.md index 0f355747..b7fb6c7f 100644 --- a/docs/ux-ui-test-server.md +++ b/docs/ux-ui-test-server.md @@ -1,6 +1,6 @@ # Тестовый сервер UX/UI — 2026-10-10 -Актуальное продолжение 2026-10-14: 249 — legacy GET контейнеров не опрашивают выключенный сервер и не превращают ошибки в пустой список. Единый mapper сохраняет unknown, native flags/envlists/mountlists и фактическую память. Backend ux-containers249/frontend ux-containers248; 25 backend-профилей passed за378124ms, три специализированные SQL-ветки явно skipped. Owned БД/роль/container удалены. Real Portal/read UI1440/390 passed, writes0. Следующий250 — ACME на собственном mmt* поддомене pngs.top; предоставленный токен проверен read-only, выпуск ещё не выполнен. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 250 — production ACME принят на точном mmt250-c22a9715.pngs.top/CHR104: Portal/UI один issue POST → done; DNS-01, native import/private key, trusted TLS/SAN и REST HTTPS200. Native services/certificates/files/export и ACME settings восстановлены, собственные job/A/TXT отсутствуют; оба CHR доступны. Backend249/frontend248 unchanged. Следующий251 — раскрыть в форме выпуска автоматическое назначение SSL-сервисам и DNS A-sync, затем проверить UI; renewal/CA revoke/multi-instance отдельно. Этап12.5 открыт. | Параметр | Проверенное состояние | | --- | --- | @@ -34,6 +34,22 @@ OSPF84: real GRE Full adjacency/private routes/Ping3/3 и4 live PC/320px URL/bro BFD85: Up/Full/own route/Ping,4 live UI и loss/withdrawal/recovery приняты; exact own cleanup8 коллекций×2 passed. Withdrawal5,245ms не подтверждает sub-second BFD timing; этот вопрос открыт. Runtime routing83 без новой сборки. [Отчёт](ux-ui/acceptance/ospf-bfd-loss-2026-10-09.json). +## 12.5.250 — production ACME / Cloudflare / TLS через реальный UI (2026-10-14) + +Пользователь предоставил pngs.top/Cloudflare и разрешил поддомены с префиксом mmt*. После уточнения звёздочки получено согласие на точное имя mmt250-c22a9715.pngs.top и временное назначение www-ssl/api-ssl с откатом. Cloudflare token verify и exact zone query200/success, token/zone active/account matched; оба собственных DNS имени до начала имели0 records. Ключ/почта/account ID только в ignored private fixture; на Debian ключ поступил через разрешённый временный API settings, отдельный secret файл туда не передавался. Native/private settings baseline снят до начала. + +Настоящий Portal/UI без mocks: app Cloudflare test200, settings save200; выбран server2/CHR104, один POST issue с точным CN →200/jobId; backend queued/running →done, UI terminal «Завершена». Production directory Let's Encrypt, не staging/fake CA. CSR/DNS-01/импорт leaf+key+chain прошли; native leaf CN/private-key=true и оба SSL сервиса enabled с этим сертификатом подтверждены. [UI](ux-ui/acceptance/acme250-live-ui.json). + +Независимая проверка: DNS A104.165.244.7; TLSv1.2, обычный rejectUnauthorized=true, peer authorized/SAN exact domain, issuerYR2, срок2026-10-14→2027-01-12; REST по HTTPS200 с прежними учётными данными. TLS проверка не отключалась, CA в локальное доверие не добавлялась. Это проверка конкретного CA/RouterOS клиента, не всех TLS версий/клиентских ОС. [TLS](ux-ui/acceptance/acme250-tls.json). + +Очистка: сначала исходные disabled/certificate/port SSL сервисов, затем только собственные cert/root-chain и PEM files. Исходные certificate/file коллекции и полный нормализованный export совпали. ACME settings восстановлены полностью, включая accountPrivateKey/updatedAt; own terminal job удалён, чтобы не оставлять managed renewal target. Историческое событие теста в журнале приложения сохраняется. После фактического COMMIT начальная проверка Date vs JSON string дала assertion; исправлена только JSON-нормализация и повторное read-only сравнение, native/SQL операции не повторялись. Приватный baseline удалён. [Native](ux-ui/acceptance/acme250-native.json). + +Application TXT cleanup подтверждён GET0; A-sync создал единственную собственную A с content104/proxied=false, она удалена по exact name/type/content/ID. Final FQDN/challenge records0. Остальные DNS записи/zone settings не менялись. [Cloudflare](ux-ui/acceptance/acme250-cloudflare.json). Post-cleanup app settings tokenConfigured=false, own job404/certificate absent, оба CHR certificate read доступны. Выданный сертификат не объявляется отозванным у CA; запись о выпуске остаётся, native экземпляры/ключевые файлы удалены. Все временные credential файлы блока удалены после проверки. + +Runtime backend ux-containers249/frontend ux-containers248 unchanged; source changes только manual acceptance helper/документация, не production код. [Процедура](../deploy/TEST-ACME.md), helper acme-live-issue.mjs syntax/ESLint passed. Backend-профили и E2E не повторялись для документального блока:25 профилей249 и29 E2E248 уже проверены в своих границах. Дополнительный local locator/copy helper ремонт не считается новым live сценарием. + +Следующий251 — явное пояснение последствий в форме: текущий issuer всегда включает www-ssl/api-ssl и синхронизирует DNS A, а экран говорит лишь об импорте/trust-store. Изменить copy существующих шагов, сохранив сетевую логику; профильные UI проверки/build/deploy. Automatic renewal, CA revocation, remote release publishing/multi-instance/WAN и остальные обязательные варианты матрицы ещё не приняты этим выпуском. + ## 12.5.249 — достоверное legacy чтение контейнеров и общая backend-регрессия (2026-10-14) Аудит выявил: GET выбранного сервера обходил enabled gate; legacy mapper считал отсутствие status остановкой, memory-high — фактической памятью, скрывал ошибки загрузки пустым массивом. Прямой GET теперь даёт409 для выключенного и404 для отсутствующего сервера до dispatch; прямой service/read тоже не создаёт RouterOS клиент для disabled, aggregate исключает disabled до reader factory. Ошибки/частичные ответы legacy GET дают502; unsupported сохраняет прежний legacy [] для совместимости, подробное availability предоставляется /api/containers/state. ID разбирается общим строгим parseDbServerId, без принятия префикса1abc. diff --git a/docs/ux-ui/acceptance/acme250-cloudflare.json b/docs/ux-ui/acceptance/acme250-cloudflare.json new file mode 100644 index 00000000..50726313 --- /dev/null +++ b/docs/ux-ui/acceptance/acme250-cloudflare.json @@ -0,0 +1,33 @@ +{ + "date": "2026-10-14", + "block": "12.5.250", + "domain": "pngs.top", + "checks": [ + { + "endpoint": "/user/tokens/verify", + "status": 200, + "success": true, + "errors": [] + }, + { + "endpoint": "/zones?name=pngs.top", + "status": 200, + "success": true, + "errors": [] + } + ], + "tokenActive": true, + "zoneActive": true, + "accountMatched": true, + "nameservers": [ + "cheryl.ns.cloudflare.com", + "doug.ns.cloudflare.com" + ], + "originalFqdnRecords": 0, + "originalChallengeRecords": 0, + "fqdn": "mmt250-c22a9715.pngs.top", + "applicationTxtCleanupConfirmed": true, + "ownARecordDeleted": true, + "finalFqdnRecords": 0, + "finalChallengeRecords": 0 +} diff --git a/docs/ux-ui/acceptance/acme250-live-ui.json b/docs/ux-ui/acceptance/acme250-live-ui.json new file mode 100644 index 00000000..ea5b0c42 --- /dev/null +++ b/docs/ux-ui/acceptance/acme250-live-ui.json @@ -0,0 +1,35 @@ +{ + "date": "2026-10-14", + "block": "12.5.250", + "fqdn": "mmt250-c22a9715.pngs.top", + "certName": "mmt250-c22a9715", + "serverId": "2", + "realPortalLogin": true, + "noMocks": true, + "issuePosts": 1, + "pageErrors": [], + "settings": { + "testStatus": 200, + "testOk": true, + "saveStatus": 200, + "tokenConfigured": true, + "directoryUrl": "https://acme-v02.api.letsencrypt.org/directory" + }, + "jobId": "8046a992-c5cd-4c1b-b343-6cb4ffd9a597", + "accepted": true, + "job": { + "id": "8046a992-c5cd-4c1b-b343-6cb4ffd9a597", + "status": "done", + "step": "done" + }, + "uiTerminalConfirmed": true, + "postCleanup": { + "tokenConfigured": false, + "ownJobStatus": 404, + "reachableServerIds": [ + "1", + "2" + ], + "ownCertificateAbsent": true + } +} diff --git a/docs/ux-ui/acceptance/acme250-native.json b/docs/ux-ui/acceptance/acme250-native.json new file mode 100644 index 00000000..6e3fd31f --- /dev/null +++ b/docs/ux-ui/acceptance/acme250-native.json @@ -0,0 +1,19 @@ +{ + "date": "2026-10-14", + "block": "12.5.250", + "fqdn": "mmt250-c22a9715.pngs.top", + "targetHost": "104.165.244.7", + "nativePrivateKeyConfirmedBeforeCleanup": true, + "servicesApplied": [ + "www-ssl", + "api-ssl" + ], + "servicesRestored": true, + "originalCertificateCollectionMatched": true, + "originalFileCollectionMatched": true, + "originalNormalizedExportMatched": true, + "acmeSettingsRestored": true, + "ownIssueJobRemoved": true, + "cleanupValidationRepair": "PG Date objects compared after JSON normalization; no device operation replayed", + "passwordChanges": 0 +} diff --git a/docs/ux-ui/acceptance/acme250-tls.json b/docs/ux-ui/acceptance/acme250-tls.json new file mode 100644 index 00000000..3590cf08 --- /dev/null +++ b/docs/ux-ui/acceptance/acme250-tls.json @@ -0,0 +1,20 @@ +{ + "date": "2026-10-14", + "block": "12.5.250", + "fqdn": "mmt250-c22a9715.pngs.top", + "addresses": [ + "104.165.244.7" + ], + "tls": { + "authorized": true, + "protocol": "TLSv1.2", + "remoteAddress": "104.165.244.7", + "subjectAltName": "DNS:mmt250-c22a9715.pngs.top", + "issuer": "YR2", + "fingerprint256": "6A:21:31:D7:C7:02:F8:4D:C9:95:23:7F:1A:3C:84:D4:68:C7:7D:A2:D2:7B:C2:E7:09:D3:5D:F8:ED:2D:C9:BE", + "validFrom": "Oct 14 11:32:10 2026 GMT", + "validUntil": "Jan 12 11:32:09 2027 GMT" + }, + "restHttpsStatus": 200, + "passwordChanged": false +}