fix(auth): скрыть пароли от читателя и согласовать Portal
This commit is contained in:
1 parent
a15917d08b
commit
608f743003
20 files changed
+1412
-19
No files matched your search
@@ -39,7 +39,8 @@
|
|||||||
"test:containers": "tsx src/services/containers-state.test.ts && tsx src/routes/containers-state.test.ts",
|
"test:containers": "tsx src/services/containers-state.test.ts && tsx src/routes/containers-state.test.ts",
|
||||||
"test:certificates": "tsx src/services/certificate-issue-execution.test.ts && tsx src/services/certificate-issue-request.test.ts && tsx src/routes/certificates-issue.test.ts && tsx src/services/certificate-issue-dto.test.ts && tsx src/services/certificate-parse.test.ts && tsx src/services/certificates-state.test.ts && tsx src/routes/certificates-state.test.ts",
|
"test:certificates": "tsx src/services/certificate-issue-execution.test.ts && tsx src/services/certificate-issue-request.test.ts && tsx src/routes/certificates-issue.test.ts && tsx src/services/certificate-issue-dto.test.ts && tsx src/services/certificate-parse.test.ts && tsx src/services/certificates-state.test.ts && tsx src/routes/certificates-state.test.ts",
|
||||||
"test:system-database": "tsx src/services/system-database-partition-restore.test.ts && tsx src/services/system-database-cache.test.ts && tsx src/services/database-background-work.test.ts && tsx src/services/database-request-gate.test.ts && tsx src/services/traffic-flow-worker-shutdown.test.ts && tsx src/services/system-database-restore-execution.test.ts && tsx src/services/system-database-operation.test.ts && tsx src/routes/system-database.test.ts && tsx src/plugins/auth.system-database.test.ts",
|
"test:system-database": "tsx src/services/system-database-partition-restore.test.ts && tsx src/services/system-database-cache.test.ts && tsx src/services/database-background-work.test.ts && tsx src/services/database-request-gate.test.ts && tsx src/services/traffic-flow-worker-shutdown.test.ts && tsx src/services/system-database-restore-execution.test.ts && tsx src/services/system-database-operation.test.ts && tsx src/routes/system-database.test.ts && tsx src/plugins/auth.system-database.test.ts",
|
||||||
"test:all": "npm run test:probes && npm run test:exec && npm run test:alert-engine && npm run test:alerts-telegram && npm run test:auth && npm run test:wireguard && npm run test:ipsec && npm run test:traffic-rate && npm run test:traffic-flow && npm run test:users && npm run test:pg && npm run test:backups && npm run test:scheduler && npm run test:live-maps && npm run test:ospf-optimizer && npm run test:recursive-routes && npm run test:config-sync && npm run test:ros-file && npm run test:containers && npm run test:certificates && npm run test:netflow-settings && npm run test:netflow-purge && npm run test:evolution && npm run test:system-database"
|
"test:all": "npm run test:probes && npm run test:exec && npm run test:alert-engine && npm run test:alerts-telegram && npm run test:auth && npm run test:wireguard && npm run test:ipsec && npm run test:traffic-rate && npm run test:traffic-flow && npm run test:users && npm run test:pg && npm run test:backups && npm run test:scheduler && npm run test:live-maps && npm run test:ospf-optimizer && npm run test:recursive-routes && npm run test:config-sync && npm run test:ros-file && npm run test:containers && npm run test:certificates && npm run test:netflow-settings && npm run test:netflow-purge && npm run test:evolution && npm run test:system-database && npm run test:server-privacy",
|
||||||
|
"test:server-privacy": "tsx src/routes/server-secrets-sql.test.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.888.0",
|
"@aws-sdk/client-s3": "^3.888.0",
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import { parseJsonArray } from "../../../db/json.js"
|
import { parseJsonArray } from "../../../db/json.js"
|
||||||
import type { ServerRead, WanUplinkRead } from "../../../types/server.js"
|
import type { ServerRead, WanUplinkRead } from "../../../types/server.js"
|
||||||
import type { ServerRow, SnapshotRow } from "../repository/servers-repository.js"
|
import type { ServerRow, SnapshotRow } from "../repository/servers-repository.js"
|
||||||
|
import {hasPermission,type AuthUser} from "../../../lib/permissions.js"
|
||||||
|
|
||||||
|
/** Reading inventory does not grant access to credentials used by device operations. */
|
||||||
|
export function serverReadForUser(server:ServerRead,user?:AuthUser):ServerRead {
|
||||||
|
return !user || hasPermission(user.permissions,"mm:servers:write") ? server : {...server,password:""}
|
||||||
|
}
|
||||||
|
|
||||||
function parseWanUplinksJson(raw: unknown): WanUplinkRead[] {
|
function parseWanUplinksJson(raw: unknown): WanUplinkRead[] {
|
||||||
const data = parseJsonArray(raw)
|
const data = parseJsonArray(raw)
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import {SignJWT} from 'jose'
|
||||||
|
const database=process.env.MMAPP_OWN_SQL_DB
|
||||||
|
if(!database)console.log('server secrets SQL: skipped; explicit owned database required')
|
||||||
|
else{
|
||||||
|
assert(/^mm_accept_166_\d+$/.test(database));assert.equal(new URL(process.env.DATABASE_URL!).pathname,'/'+database)
|
||||||
|
process.env.AUTH_REQUIRED='true';process.env.AUTH_JWT_SECRET='owned196-server-read-secret';process.env.AUTH_ISSUER='https://owned196.test';process.env.NODE_ENV='test'
|
||||||
|
const {pool,closePool}=await import('../db/index.js');const {initDatabase}=await import('../db/bootstrap.js');const {buildApp}=await import('../index.js')
|
||||||
|
assert.equal((await pool.query('SELECT current_database() AS name')).rows[0].name,database)
|
||||||
|
let serverId:number|undefined;const app=await buildApp({logger:false,startScheduler:false})
|
||||||
|
try{
|
||||||
|
await initDatabase()
|
||||||
|
serverId=Number((await pool.query("INSERT INTO servers(name,host,enabled,username,password) VALUES('own196-secret','192.0.2.196',false,'fixture','own196-password') RETURNING id")).rows[0].id)
|
||||||
|
for(const permission of ['mm:servers:read','mm:servers:write','mm:servers:admin']){
|
||||||
|
const token=await new SignJWT({sub:'own196',apps:['mm'],permissions:[permission]}).setProtectedHeader({alg:'HS256'}).setIssuer('https://owned196.test').setExpirationTime('1h').sign(new TextEncoder().encode('owned196-server-read-secret'))
|
||||||
|
for(const url of ['/api/servers','/api/servers/'+serverId]){
|
||||||
|
const r=await app.inject({method:'GET',url,headers:{authorization:'Bearer '+token}});assert.equal(r.statusCode,200)
|
||||||
|
const body=r.json(),row=Array.isArray(body)?body.find(s=>s.id===serverId):body
|
||||||
|
assert.equal(row.password,permission.endsWith(':read')?'':'own196-password');assert.equal(row.username,'fixture')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.equal((await pool.query('SELECT password FROM servers WHERE id=$1',[serverId])).rows[0].password,'own196-password','projection never changes stored credentials')
|
||||||
|
const noToken=await app.inject({method:'GET',url:'/api/servers'});assert.equal(noToken.statusCode,401)
|
||||||
|
console.log('server secrets owned SQL: reader/writer/admin list/detail and unchanged stored password passed')
|
||||||
|
}finally{
|
||||||
|
await app.close();if(serverId!==undefined)await pool.query('DELETE FROM servers WHERE id=$1',[serverId]);await closePool()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
updateServer,
|
updateServer,
|
||||||
} from "../modules/servers/service/servers-service.js"
|
} from "../modules/servers/service/servers-service.js"
|
||||||
import { getServerRowById } from "../modules/servers/repository/servers-repository.js"
|
import { getServerRowById } from "../modules/servers/repository/servers-repository.js"
|
||||||
|
import {serverReadForUser} from "../modules/servers/mapper/servers-mapper.js"
|
||||||
|
|
||||||
// ── plugin ─────────────────────────────────────────────────────────────────────
|
// ── plugin ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -76,8 +77,8 @@ const serversRoutes: FastifyPluginAsyncZod = async (app) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// GET /api/servers
|
// GET /api/servers
|
||||||
app.get("/", async (_req, reply) => {
|
app.get("/", async (req, reply) => {
|
||||||
return reply.send(await listServersRead())
|
return reply.send((await listServersRead()).map(server=>serverReadForUser(server,req.authUser)))
|
||||||
})
|
})
|
||||||
|
|
||||||
// GET /api/servers/:id/ros-src-address — IPv4 для src-address в RouterOS (не FQDN)
|
// GET /api/servers/:id/ros-src-address — IPv4 для src-address в RouterOS (не FQDN)
|
||||||
@@ -238,7 +239,7 @@ const serversRoutes: FastifyPluginAsyncZod = async (app) => {
|
|||||||
const params = req.params as ServerIdParams
|
const params = req.params as ServerIdParams
|
||||||
const server = await getServerReadById(params.id)
|
const server = await getServerReadById(params.id)
|
||||||
if (!server) return reply.status(404).send({ error: "Server not found" })
|
if (!server) return reply.status(404).send({ error: "Server not found" })
|
||||||
return reply.send(server)
|
return reply.send(serverReadForUser(server,req.authUser))
|
||||||
})
|
})
|
||||||
|
|
||||||
// PUT /api/servers/:id
|
// PUT /api/servers/:id
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# UX/UI: протокол внешней приёмки 12.5
|
# UX/UI: протокол внешней приёмки 12.5
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-13: завершены191–195. Backend ux-peak194/frontend ux-peak191; Portal1.3.1 установлен наlocalhost:5175, AUTH_REQUIRED=true, actual login/JWT/RBAC/browser SSO passed. Telegram test принят API и подтверждён пользователем. Найдены196: пароль CHR выдаётся reader JWT и ссылка AppsMenu использует build-time Portal URL — исправить до приёмки приватности/ссылок. Public IPv6 недоступен у пользователя; crash/lock-loss перенесены после интеграций.12.5 открыта.
|
Актуальное продолжение 2026-10-13: завершены191–196. Runtime backend/frontend ux-peak196, Portal1.3.1 healthy, AUTH_REQUIRED=true; reader password redacted вlist/detail, writer/admin сохранены, runtime Portal links исправлены. SQL roles3×2 endpoints/10996ms, E2E41/1.4m, live reader/admin UI4 passed. Telegram базовая доставка подтверждена пользователем. Следующий197 — real Telegram outbox/dedupe/multiple senders/unknown hold/manual retry в собственной временной БД.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).
|
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).
|
||||||
|
|
||||||
@@ -696,7 +696,7 @@ flowchart LR
|
|||||||
|
|
||||||
## 12.5.77 — основной блок IPsec (2026-10-09)
|
## 12.5.77 — основной блок IPsec (2026-10-09)
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-13: завершены191–195. Backend ux-peak194/frontend ux-peak191; Portal1.3.1 установлен наlocalhost:5175, AUTH_REQUIRED=true, actual login/JWT/RBAC/browser SSO passed. Telegram test принят API и подтверждён пользователем. Найдены196: пароль CHR выдаётся reader JWT и ссылка AppsMenu использует build-time Portal URL — исправить до приёмки приватности/ссылок. Public IPv6 недоступен у пользователя; crash/lock-loss перенесены после интеграций.12.5 открыта.
|
Актуальное продолжение 2026-10-13: завершены191–196. Runtime backend/frontend ux-peak196, Portal1.3.1 healthy, AUTH_REQUIRED=true; reader password redacted вlist/detail, writer/admin сохранены, runtime Portal links исправлены. SQL roles3×2 endpoints/10996ms, E2E41/1.4m, live reader/admin UI4 passed. Telegram базовая доставка подтверждена пользователем. Следующий197 — real Telegram outbox/dedupe/multiple senders/unknown hold/manual retry в собственной временной БД.12.5 открыта.
|
||||||
|
|
||||||
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
|
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
|
||||||
|
|
||||||
@@ -2370,3 +2370,21 @@ Frontend:59 lib test files прошли с одной исправленной
|
|||||||
Найден следующий196: настоящее reader JWT получает plaintext password CHR через GETservers. Подтверждён только boolean совпадения, секрет не выведен. Также authPortalBaseUrl для ссылки AppsMenu использует NEXT_PUBLIC_AUTH_PORTAL_URL сборки вместо текущего runtime Portal URL; hook switcher fetch уже использует runtime. Исправить оба API/UI недостатка, проверить reader/writer/admin и реальный Portal на ПК/телефоне, собрать/развернуть и создать локальный commit. Это ограничивает принятие приватности/навигации, но не отменяет доказанный базовый SSO/Telegram transport.
|
Найден следующий196: настоящее reader JWT получает plaintext password CHR через GETservers. Подтверждён только boolean совпадения, секрет не выведен. Также authPortalBaseUrl для ссылки AppsMenu использует NEXT_PUBLIC_AUTH_PORTAL_URL сборки вместо текущего runtime Portal URL; hook switcher fetch уже использует runtime. Исправить оба API/UI недостатка, проверить reader/writer/admin и реальный Portal на ПК/телефоне, собрать/развернуть и создать локальный commit. Это ограничивает принятие приватности/навигации, но не отменяет доказанный базовый SSO/Telegram transport.
|
||||||
|
|
||||||
Пользователь подтвердил отсутствие public IPv6 на всех его стендах — внешняя IPv6 outer приёмка сейчас недоступна. Проверку crash/lock-connection loss из прежнего следующего195 перенести после Portal/Telegram.12.5 открыта.
|
Пользователь подтвердил отсутствие public IPv6 на всех его стендах — внешняя IPv6 outer приёмка сейчас недоступна. Проверку crash/lock-connection loss из прежнего следующего195 перенести после Portal/Telegram.12.5 открыта.
|
||||||
|
|
||||||
|
## 12.5.196 — приватность серверов и согласованный runtime Portal (2026-10-13)
|
||||||
|
|
||||||
|
Реальное reader JWT в195 подтверждённо получало plaintext password CHR. Теперь serverReadForUser на HTTP list/detail возвращает password="" для пользователя без mm:servers:write/admin. Writer/admin и прежний AUTH_REQUIRED=false сохраняют existing DTO для редактирования. is_admin сам по себе не заменяет permission. Проекция создаёт копию и не меняет repository rows, хранимые/native credentials или работу collectors. Изменены servers-mapper.ts и два GET handlers routes/servers.ts; внутренние get/listServersRead остаются пригодными для native операций. Это защита этих endpoints, не аудит всех экспортов/secret-bearing DTO проекта.
|
||||||
|
|
||||||
|
authPortalBaseUrl теперь предпочитает runtime config, как существующий hook switcher fetch. Ссылка «Настроить на портале» больше не ведёт к build-time auth.shnt.top при отдельном тестовом issuer. ReUI PRO/base-nova/Frame/AppsMenu переиспользованы, vendor/стили/разметка не менялись. Unit подтвердил build fallback/runtime override/trailing slash.
|
||||||
|
|
||||||
|
Owned SQL test выполняет actual buildApp/auth middleware/handlers с подписанными test JWT на отдельной ограниченной роли: reader/writer/admin ×list/detail,401 безtoken, password вБД unchanged. Passed10996ms, own DB/role/container удалены. Новый test:server-privacy включён вtest:all, обычный прогон без явной own DB честно skip. Source TypeScript/backend build/scoped ESLint passed.41 unique E2E (runtime links2,header4,servers35) passed1.4m. Изолированные JWT не выдаются за реальный Portal: он проверен отдельно.
|
||||||
|
|
||||||
|
Production backend/frontend ux-peak196 развёрнуты.466 backend+525 frontend source files и1044 compiled backend artifacts совпали. Portal1.3.1/SQLite/shared lab secret и AUTH_REQUIRED=true сохранены новой закрытой Compose override; оригинальные credentials/.env не копировались. Реальный Portal admin API сохраняет необходимые поля редактирования, реальный reader list/detail не содержитпароль, валидная попытка создания403.4 live UI (reader/admin1440/320) безmocks/page errors/overflow: readonly control disabled, admin enabled, runtime Portal link http://localhost:5175/admin/apps. Собственные Portal users удалены. CHR config/passwords не менялись, новых Telegram send в196 нет.
|
||||||
|
|
||||||
|
Протоколы: [SQL](ux-ui/acceptance/portal196-sql196-result.json), [source](ux-ui/acceptance/portal196-source196-result.json), [compiled](ux-ui/acceptance/portal196-compiled196-result.json), [runtime](ux-ui/acceptance/portal196-runtime196-result.json), [fixture E2E](ux-ui/acceptance/portal196-e2e-2026-10-13.json), [actual Portal API](ux-ui/acceptance/portal-live196-api-2026-10-13.json), [live UI4](ux-ui/acceptance/portal196-live-ui-2026-10-13.json).
|
||||||
|
|
||||||
|
| Референс | Схема |
|
||||||
|
| --- | --- |
|
||||||
|
| [ReUI App Shell12](https://reui.io/preview/base/app-shell-12?ref=mcp) | Runtime issuer/Portal → меню приложений → тот же Portal; reader → masked DTO + blocked edit, writer → existing editor |
|
||||||
|
|
||||||
|
Следующий197: реальная доставка из outbox через собственную временную БД, dedupe/two senders, controlled lost receipt после реального Telegram acceptance, no automatic repeat, explicit acknowledged retry/stale version/RBAC. Планируется3 помеченных test messages, включая намеренный повтор№2; не превращать controlled receipt suppression в физическую WAN/lost-ACK приёмку. Portal JWT revocation доexp, настоящее expiry/refresh/client OS и crash/lock-loss остаются отдельными границами.12.5 открыта.
|
||||||
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
# План реализации UX/UI по пятичасовым окнам
|
# План реализации UX/UI по пятичасовым окнам
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-13: завершены191–195. Backend ux-peak194/frontend ux-peak191; Portal1.3.1 установлен наlocalhost:5175, AUTH_REQUIRED=true, actual login/JWT/RBAC/browser SSO passed. Telegram test принят API и подтверждён пользователем. Найдены196: пароль CHR выдаётся reader JWT и ссылка AppsMenu использует build-time Portal URL — исправить до приёмки приватности/ссылок. Public IPv6 недоступен у пользователя; crash/lock-loss перенесены после интеграций.12.5 открыта.
|
Актуальное продолжение 2026-10-13: завершены191–196. Runtime backend/frontend ux-peak196, Portal1.3.1 healthy, AUTH_REQUIRED=true; reader password redacted вlist/detail, writer/admin сохранены, runtime Portal links исправлены. SQL roles3×2 endpoints/10996ms, E2E41/1.4m, live reader/admin UI4 passed. Telegram базовая доставка подтверждена пользователем. Следующий197 — real Telegram outbox/dedupe/multiple senders/unknown hold/manual retry в собственной временной БД.12.5 открыта.
|
||||||
|
|
||||||
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-13. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-13. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
||||||
|
|
||||||
## Актуальная точка продолжения — 2026-10-13
|
## Актуальная точка продолжения — 2026-10-13
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-13: завершены191–195. Backend ux-peak194/frontend ux-peak191; Portal1.3.1 установлен наlocalhost:5175, AUTH_REQUIRED=true, actual login/JWT/RBAC/browser SSO passed. Telegram test принят API и подтверждён пользователем. Найдены196: пароль CHR выдаётся reader JWT и ссылка AppsMenu использует build-time Portal URL — исправить до приёмки приватности/ссылок. Public IPv6 недоступен у пользователя; crash/lock-loss перенесены после интеграций.12.5 открыта.
|
Актуальное продолжение 2026-10-13: завершены191–196. Runtime backend/frontend ux-peak196, Portal1.3.1 healthy, AUTH_REQUIRED=true; reader password redacted вlist/detail, writer/admin сохранены, runtime Portal links исправлены. SQL roles3×2 endpoints/10996ms, E2E41/1.4m, live reader/admin UI4 passed. Telegram базовая доставка подтверждена пользователем. Следующий197 — real Telegram outbox/dedupe/multiple senders/unknown hold/manual retry в собственной временной БД.12.5 открыта.
|
||||||
|
|
||||||
Этапы0–11 завершены в локальных границах журнала. История выполненных внешних блоков и доказательства сохранены в [журнале](ux-ui-stage-12.md) и [протоколе](ux-ui-external-acceptance.md). Реальная Telegram доставка, Portal SSO, multi-instance и оставшиеся профильные сетевые ограничения открыты.
|
Этапы0–11 завершены в локальных границах журнала. История выполненных внешних блоков и доказательства сохранены в [журнале](ux-ui-stage-12.md) и [протоколе](ux-ui-external-acceptance.md). Реальная Telegram доставка, Portal SSO, multi-instance и оставшиеся профильные сетевые ограничения открыты.
|
||||||
|
|
||||||
@@ -371,6 +371,6 @@ flowchart LR
|
|||||||
|
|
||||||
## 12.5.77 — основной блок IPsec (2026-10-09)
|
## 12.5.77 — основной блок IPsec (2026-10-09)
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-13: завершены191–195. Backend ux-peak194/frontend ux-peak191; Portal1.3.1 установлен наlocalhost:5175, AUTH_REQUIRED=true, actual login/JWT/RBAC/browser SSO passed. Telegram test принят API и подтверждён пользователем. Найдены196: пароль CHR выдаётся reader JWT и ссылка AppsMenu использует build-time Portal URL — исправить до приёмки приватности/ссылок. Public IPv6 недоступен у пользователя; crash/lock-loss перенесены после интеграций.12.5 открыта.
|
Актуальное продолжение 2026-10-13: завершены191–196. Runtime backend/frontend ux-peak196, Portal1.3.1 healthy, AUTH_REQUIRED=true; reader password redacted вlist/detail, writer/admin сохранены, runtime Portal links исправлены. SQL roles3×2 endpoints/10996ms, E2E41/1.4m, live reader/admin UI4 passed. Telegram базовая доставка подтверждена пользователем. Следующий197 — real Telegram outbox/dedupe/multiple senders/unknown hold/manual retry в собственной временной БД.12.5 открыта.
|
||||||
|
|
||||||
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
|
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
|
||||||
+20
-2
@@ -1,6 +1,6 @@
|
|||||||
# UX/UI: журнал реализации
|
# UX/UI: журнал реализации
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-13: завершены191–195. Backend ux-peak194/frontend ux-peak191; Portal1.3.1 установлен наlocalhost:5175, AUTH_REQUIRED=true, actual login/JWT/RBAC/browser SSO passed. Telegram test принят API и подтверждён пользователем. Найдены196: пароль CHR выдаётся reader JWT и ссылка AppsMenu использует build-time Portal URL — исправить до приёмки приватности/ссылок. Public IPv6 недоступен у пользователя; crash/lock-loss перенесены после интеграций.12.5 открыта.
|
Актуальное продолжение 2026-10-13: завершены191–196. Runtime backend/frontend ux-peak196, Portal1.3.1 healthy, AUTH_REQUIRED=true; reader password redacted вlist/detail, writer/admin сохранены, runtime Portal links исправлены. SQL roles3×2 endpoints/10996ms, E2E41/1.4m, live reader/admin UI4 passed. Telegram базовая доставка подтверждена пользователем. Следующий197 — real Telegram outbox/dedupe/multiple senders/unknown hold/manual retry в собственной временной БД.12.5 открыта.
|
||||||
|
|
||||||
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
|
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
|
||||||
|
|
||||||
@@ -36,7 +36,25 @@
|
|||||||
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
||||||
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
|
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
|
||||||
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
|
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
|
||||||
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–195 и ограничения ниже. Runtime backend ux-peak194/frontend ux-peak191. |
|
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–196 и ограничения ниже. Runtime backend/frontend ux-peak196. |
|
||||||
|
|
||||||
|
## 12.5.196 — приватность серверов и согласованный runtime Portal (2026-10-13)
|
||||||
|
|
||||||
|
Реальное reader JWT в195 подтверждённо получало plaintext password CHR. Теперь serverReadForUser на HTTP list/detail возвращает password="" для пользователя без mm:servers:write/admin. Writer/admin и прежний AUTH_REQUIRED=false сохраняют existing DTO для редактирования. is_admin сам по себе не заменяет permission. Проекция создаёт копию и не меняет repository rows, хранимые/native credentials или работу collectors. Изменены servers-mapper.ts и два GET handlers routes/servers.ts; внутренние get/listServersRead остаются пригодными для native операций. Это защита этих endpoints, не аудит всех экспортов/secret-bearing DTO проекта.
|
||||||
|
|
||||||
|
authPortalBaseUrl теперь предпочитает runtime config, как существующий hook switcher fetch. Ссылка «Настроить на портале» больше не ведёт к build-time auth.shnt.top при отдельном тестовом issuer. ReUI PRO/base-nova/Frame/AppsMenu переиспользованы, vendor/стили/разметка не менялись. Unit подтвердил build fallback/runtime override/trailing slash.
|
||||||
|
|
||||||
|
Owned SQL test выполняет actual buildApp/auth middleware/handlers с подписанными test JWT на отдельной ограниченной роли: reader/writer/admin ×list/detail,401 безtoken, password вБД unchanged. Passed10996ms, own DB/role/container удалены. Новый test:server-privacy включён вtest:all, обычный прогон без явной own DB честно skip. Source TypeScript/backend build/scoped ESLint passed.41 unique E2E (runtime links2,header4,servers35) passed1.4m. Изолированные JWT не выдаются за реальный Portal: он проверен отдельно.
|
||||||
|
|
||||||
|
Production backend/frontend ux-peak196 развёрнуты.466 backend+525 frontend source files и1044 compiled backend artifacts совпали. Portal1.3.1/SQLite/shared lab secret и AUTH_REQUIRED=true сохранены новой закрытой Compose override; оригинальные credentials/.env не копировались. Реальный Portal admin API сохраняет необходимые поля редактирования, реальный reader list/detail не содержитпароль, валидная попытка создания403.4 live UI (reader/admin1440/320) безmocks/page errors/overflow: readonly control disabled, admin enabled, runtime Portal link http://localhost:5175/admin/apps. Собственные Portal users удалены. CHR config/passwords не менялись, новых Telegram send в196 нет.
|
||||||
|
|
||||||
|
Протоколы: [SQL](ux-ui/acceptance/portal196-sql196-result.json), [source](ux-ui/acceptance/portal196-source196-result.json), [compiled](ux-ui/acceptance/portal196-compiled196-result.json), [runtime](ux-ui/acceptance/portal196-runtime196-result.json), [fixture E2E](ux-ui/acceptance/portal196-e2e-2026-10-13.json), [actual Portal API](ux-ui/acceptance/portal-live196-api-2026-10-13.json), [live UI4](ux-ui/acceptance/portal196-live-ui-2026-10-13.json).
|
||||||
|
|
||||||
|
| Референс | Схема |
|
||||||
|
| --- | --- |
|
||||||
|
| [ReUI App Shell12](https://reui.io/preview/base/app-shell-12?ref=mcp) | Runtime issuer/Portal → меню приложений → тот же Portal; reader → masked DTO + blocked edit, writer → existing editor |
|
||||||
|
|
||||||
|
Следующий197: реальная доставка из outbox через собственную временную БД, dedupe/two senders, controlled lost receipt после реального Telegram acceptance, no automatic repeat, explicit acknowledged retry/stale version/RBAC. Планируется3 помеченных test messages, включая намеренный повтор№2; не превращать controlled receipt suppression в физическую WAN/lost-ACK приёмку. Portal JWT revocation доexp, настоящее expiry/refresh/client OS и crash/lock-loss остаются отдельными границами.12.5 открыта.
|
||||||
|
|
||||||
## 12.5.195 — реальный Portal1.3.1 и доставка Telegram (2026-10-13)
|
## 12.5.195 — реальный Portal1.3.1 и доставка Telegram (2026-10-13)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Оставшаяся внешняя приёмка — 2026-10-13
|
# Оставшаяся внешняя приёмка — 2026-10-13
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-13: завершены191–195. Backend ux-peak194/frontend ux-peak191; Portal1.3.1 установлен наlocalhost:5175, AUTH_REQUIRED=true, actual login/JWT/RBAC/browser SSO passed. Telegram test принят API и подтверждён пользователем. Найдены196: пароль CHR выдаётся reader JWT и ссылка AppsMenu использует build-time Portal URL — исправить до приёмки приватности/ссылок. Public IPv6 недоступен у пользователя; crash/lock-loss перенесены после интеграций.12.5 открыта.
|
Актуальное продолжение 2026-10-13: завершены191–196. Runtime backend/frontend ux-peak196, Portal1.3.1 healthy, AUTH_REQUIRED=true; reader password redacted вlist/detail, writer/admin сохранены, runtime Portal links исправлены. SQL roles3×2 endpoints/10996ms, E2E41/1.4m, live reader/admin UI4 passed. Telegram базовая доставка подтверждена пользователем. Следующий197 — real Telegram outbox/dedupe/multiple senders/unknown hold/manual retry в собственной временной БД.12.5 открыта.
|
||||||
|
|
||||||
Это актуальная матрица после 12.5.195. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
|
Это актуальная матрица после 12.5.196. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
|
||||||
|
|
||||||
Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-peak194`, frontend `ux-peak191`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
|
Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend/frontend `ux-peak196`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
|
||||||
|
|
||||||
| Раздел | Подтверждено | Осталось / условие завершения |
|
| Раздел | Подтверждено | Осталось / условие завершения |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
## Следующая последовательность — 2026-10-13
|
## Следующая последовательность — 2026-10-13
|
||||||
|
|
||||||
1. **196 — приватность серверов и runtime Portal links:** скрыть password от reader JWT, сохранить writer/admin редактирование; исправить ссылку AppsMenu на runtime Portal.195 базовый SSO/Telegram транспорт принят, автоматические доставки/expiry/revoke boundary ещё открыты. Затем вернуться к crash/lock-loss.
|
1. **197 — реальный Telegram outbox:** dedupe/two senders/unknown hold/explicit retry на собственной БД и тестовом чате.195–196 базовый SSO/Telegram transport, masked server DTO и runtime links приняты. Затем Portal expiry/revoke boundary и crash/lock-loss.
|
||||||
2. **Профильный контроль:** для новых изменений выполнять затронутые SQL/API/UI проверки. Общая регрессия192 выполнена; не повторять её без изменений общей основы или нового основания.
|
2. **Профильный контроль:** для новых изменений выполнять затронутые SQL/API/UI проверки. Общая регрессия192 выполнена; не повторять её без изменений общей основы или нового основания.
|
||||||
3. **Внешние зависимости:** Portal issuer/login/revoke, Telegram bot/chat/разрешённое сообщение, клиентские ОС, независимые WAN/public IPv6 и scale/multi-instance — отдельные группы по таблице выше. Ограничения текущего стенда не превращать в обещание приёмки всех конфигураций.
|
3. **Внешние зависимости:** Portal issuer/login/revoke, Telegram bot/chat/разрешённое сообщение, клиентские ОС, независимые WAN/public IPv6 и scale/multi-instance — отдельные группы по таблице выше. Ограничения текущего стенда не превращать в обещание приёмки всех конфигураций.
|
||||||
|
|
||||||
@@ -1286,3 +1286,21 @@ Frontend:59 lib test files прошли с одной исправленной
|
|||||||
Найден следующий196: настоящее reader JWT получает plaintext password CHR через GETservers. Подтверждён только boolean совпадения, секрет не выведен. Также authPortalBaseUrl для ссылки AppsMenu использует NEXT_PUBLIC_AUTH_PORTAL_URL сборки вместо текущего runtime Portal URL; hook switcher fetch уже использует runtime. Исправить оба API/UI недостатка, проверить reader/writer/admin и реальный Portal на ПК/телефоне, собрать/развернуть и создать локальный commit. Это ограничивает принятие приватности/навигации, но не отменяет доказанный базовый SSO/Telegram transport.
|
Найден следующий196: настоящее reader JWT получает plaintext password CHR через GETservers. Подтверждён только boolean совпадения, секрет не выведен. Также authPortalBaseUrl для ссылки AppsMenu использует NEXT_PUBLIC_AUTH_PORTAL_URL сборки вместо текущего runtime Portal URL; hook switcher fetch уже использует runtime. Исправить оба API/UI недостатка, проверить reader/writer/admin и реальный Portal на ПК/телефоне, собрать/развернуть и создать локальный commit. Это ограничивает принятие приватности/навигации, но не отменяет доказанный базовый SSO/Telegram transport.
|
||||||
|
|
||||||
Пользователь подтвердил отсутствие public IPv6 на всех его стендах — внешняя IPv6 outer приёмка сейчас недоступна. Проверку crash/lock-connection loss из прежнего следующего195 перенести после Portal/Telegram.12.5 открыта.
|
Пользователь подтвердил отсутствие public IPv6 на всех его стендах — внешняя IPv6 outer приёмка сейчас недоступна. Проверку crash/lock-connection loss из прежнего следующего195 перенести после Portal/Telegram.12.5 открыта.
|
||||||
|
|
||||||
|
## 12.5.196 — приватность серверов и согласованный runtime Portal (2026-10-13)
|
||||||
|
|
||||||
|
Реальное reader JWT в195 подтверждённо получало plaintext password CHR. Теперь serverReadForUser на HTTP list/detail возвращает password="" для пользователя без mm:servers:write/admin. Writer/admin и прежний AUTH_REQUIRED=false сохраняют existing DTO для редактирования. is_admin сам по себе не заменяет permission. Проекция создаёт копию и не меняет repository rows, хранимые/native credentials или работу collectors. Изменены servers-mapper.ts и два GET handlers routes/servers.ts; внутренние get/listServersRead остаются пригодными для native операций. Это защита этих endpoints, не аудит всех экспортов/secret-bearing DTO проекта.
|
||||||
|
|
||||||
|
authPortalBaseUrl теперь предпочитает runtime config, как существующий hook switcher fetch. Ссылка «Настроить на портале» больше не ведёт к build-time auth.shnt.top при отдельном тестовом issuer. ReUI PRO/base-nova/Frame/AppsMenu переиспользованы, vendor/стили/разметка не менялись. Unit подтвердил build fallback/runtime override/trailing slash.
|
||||||
|
|
||||||
|
Owned SQL test выполняет actual buildApp/auth middleware/handlers с подписанными test JWT на отдельной ограниченной роли: reader/writer/admin ×list/detail,401 безtoken, password вБД unchanged. Passed10996ms, own DB/role/container удалены. Новый test:server-privacy включён вtest:all, обычный прогон без явной own DB честно skip. Source TypeScript/backend build/scoped ESLint passed.41 unique E2E (runtime links2,header4,servers35) passed1.4m. Изолированные JWT не выдаются за реальный Portal: он проверен отдельно.
|
||||||
|
|
||||||
|
Production backend/frontend ux-peak196 развёрнуты.466 backend+525 frontend source files и1044 compiled backend artifacts совпали. Portal1.3.1/SQLite/shared lab secret и AUTH_REQUIRED=true сохранены новой закрытой Compose override; оригинальные credentials/.env не копировались. Реальный Portal admin API сохраняет необходимые поля редактирования, реальный reader list/detail не содержитпароль, валидная попытка создания403.4 live UI (reader/admin1440/320) безmocks/page errors/overflow: readonly control disabled, admin enabled, runtime Portal link http://localhost:5175/admin/apps. Собственные Portal users удалены. CHR config/passwords не менялись, новых Telegram send в196 нет.
|
||||||
|
|
||||||
|
Протоколы: [SQL](ux-ui/acceptance/portal196-sql196-result.json), [source](ux-ui/acceptance/portal196-source196-result.json), [compiled](ux-ui/acceptance/portal196-compiled196-result.json), [runtime](ux-ui/acceptance/portal196-runtime196-result.json), [fixture E2E](ux-ui/acceptance/portal196-e2e-2026-10-13.json), [actual Portal API](ux-ui/acceptance/portal-live196-api-2026-10-13.json), [live UI4](ux-ui/acceptance/portal196-live-ui-2026-10-13.json).
|
||||||
|
|
||||||
|
| Референс | Схема |
|
||||||
|
| --- | --- |
|
||||||
|
| [ReUI App Shell12](https://reui.io/preview/base/app-shell-12?ref=mcp) | Runtime issuer/Portal → меню приложений → тот же Portal; reader → masked DTO + blocked edit, writer → existing editor |
|
||||||
|
|
||||||
|
Следующий197: реальная доставка из outbox через собственную временную БД, dedupe/two senders, controlled lost receipt после реального Telegram acceptance, no automatic repeat, explicit acknowledged retry/stale version/RBAC. Планируется3 помеченных test messages, включая намеренный повтор№2; не превращать controlled receipt suppression в физическую WAN/lost-ACK приёмку. Portal JWT revocation доexp, настоящее expiry/refresh/client OS и crash/lock-loss остаются отдельными границами.12.5 открыта.
|
||||||
+19
-1
@@ -1,6 +1,6 @@
|
|||||||
# Этап 12: итоговая приёмка UX/UI
|
# Этап 12: итоговая приёмка UX/UI
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-13: завершены191–195. Backend ux-peak194/frontend ux-peak191; Portal1.3.1 установлен наlocalhost:5175, AUTH_REQUIRED=true, actual login/JWT/RBAC/browser SSO passed. Telegram test принят API и подтверждён пользователем. Найдены196: пароль CHR выдаётся reader JWT и ссылка AppsMenu использует build-time Portal URL — исправить до приёмки приватности/ссылок. Public IPv6 недоступен у пользователя; crash/lock-loss перенесены после интеграций.12.5 открыта.
|
Актуальное продолжение 2026-10-13: завершены191–196. Runtime backend/frontend ux-peak196, Portal1.3.1 healthy, AUTH_REQUIRED=true; reader password redacted вlist/detail, writer/admin сохранены, runtime Portal links исправлены. SQL roles3×2 endpoints/10996ms, E2E41/1.4m, live reader/admin UI4 passed. Telegram базовая доставка подтверждена пользователем. Следующий197 — real Telegram outbox/dedupe/multiple senders/unknown hold/manual retry в собственной временной БД.12.5 открыта.
|
||||||
|
|
||||||
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
|
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
|
||||||
|
|
||||||
@@ -3675,3 +3675,21 @@ Frontend:59 lib test files прошли с одной исправленной
|
|||||||
Найден следующий196: настоящее reader JWT получает plaintext password CHR через GETservers. Подтверждён только boolean совпадения, секрет не выведен. Также authPortalBaseUrl для ссылки AppsMenu использует NEXT_PUBLIC_AUTH_PORTAL_URL сборки вместо текущего runtime Portal URL; hook switcher fetch уже использует runtime. Исправить оба API/UI недостатка, проверить reader/writer/admin и реальный Portal на ПК/телефоне, собрать/развернуть и создать локальный commit. Это ограничивает принятие приватности/навигации, но не отменяет доказанный базовый SSO/Telegram transport.
|
Найден следующий196: настоящее reader JWT получает plaintext password CHR через GETservers. Подтверждён только boolean совпадения, секрет не выведен. Также authPortalBaseUrl для ссылки AppsMenu использует NEXT_PUBLIC_AUTH_PORTAL_URL сборки вместо текущего runtime Portal URL; hook switcher fetch уже использует runtime. Исправить оба API/UI недостатка, проверить reader/writer/admin и реальный Portal на ПК/телефоне, собрать/развернуть и создать локальный commit. Это ограничивает принятие приватности/навигации, но не отменяет доказанный базовый SSO/Telegram transport.
|
||||||
|
|
||||||
Пользователь подтвердил отсутствие public IPv6 на всех его стендах — внешняя IPv6 outer приёмка сейчас недоступна. Проверку crash/lock-connection loss из прежнего следующего195 перенести после Portal/Telegram.12.5 открыта.
|
Пользователь подтвердил отсутствие public IPv6 на всех его стендах — внешняя IPv6 outer приёмка сейчас недоступна. Проверку crash/lock-connection loss из прежнего следующего195 перенести после Portal/Telegram.12.5 открыта.
|
||||||
|
|
||||||
|
## 12.5.196 — приватность серверов и согласованный runtime Portal (2026-10-13)
|
||||||
|
|
||||||
|
Реальное reader JWT в195 подтверждённо получало plaintext password CHR. Теперь serverReadForUser на HTTP list/detail возвращает password="" для пользователя без mm:servers:write/admin. Writer/admin и прежний AUTH_REQUIRED=false сохраняют existing DTO для редактирования. is_admin сам по себе не заменяет permission. Проекция создаёт копию и не меняет repository rows, хранимые/native credentials или работу collectors. Изменены servers-mapper.ts и два GET handlers routes/servers.ts; внутренние get/listServersRead остаются пригодными для native операций. Это защита этих endpoints, не аудит всех экспортов/secret-bearing DTO проекта.
|
||||||
|
|
||||||
|
authPortalBaseUrl теперь предпочитает runtime config, как существующий hook switcher fetch. Ссылка «Настроить на портале» больше не ведёт к build-time auth.shnt.top при отдельном тестовом issuer. ReUI PRO/base-nova/Frame/AppsMenu переиспользованы, vendor/стили/разметка не менялись. Unit подтвердил build fallback/runtime override/trailing slash.
|
||||||
|
|
||||||
|
Owned SQL test выполняет actual buildApp/auth middleware/handlers с подписанными test JWT на отдельной ограниченной роли: reader/writer/admin ×list/detail,401 безtoken, password вБД unchanged. Passed10996ms, own DB/role/container удалены. Новый test:server-privacy включён вtest:all, обычный прогон без явной own DB честно skip. Source TypeScript/backend build/scoped ESLint passed.41 unique E2E (runtime links2,header4,servers35) passed1.4m. Изолированные JWT не выдаются за реальный Portal: он проверен отдельно.
|
||||||
|
|
||||||
|
Production backend/frontend ux-peak196 развёрнуты.466 backend+525 frontend source files и1044 compiled backend artifacts совпали. Portal1.3.1/SQLite/shared lab secret и AUTH_REQUIRED=true сохранены новой закрытой Compose override; оригинальные credentials/.env не копировались. Реальный Portal admin API сохраняет необходимые поля редактирования, реальный reader list/detail не содержитпароль, валидная попытка создания403.4 live UI (reader/admin1440/320) безmocks/page errors/overflow: readonly control disabled, admin enabled, runtime Portal link http://localhost:5175/admin/apps. Собственные Portal users удалены. CHR config/passwords не менялись, новых Telegram send в196 нет.
|
||||||
|
|
||||||
|
Протоколы: [SQL](ux-ui/acceptance/portal196-sql196-result.json), [source](ux-ui/acceptance/portal196-source196-result.json), [compiled](ux-ui/acceptance/portal196-compiled196-result.json), [runtime](ux-ui/acceptance/portal196-runtime196-result.json), [fixture E2E](ux-ui/acceptance/portal196-e2e-2026-10-13.json), [actual Portal API](ux-ui/acceptance/portal-live196-api-2026-10-13.json), [live UI4](ux-ui/acceptance/portal196-live-ui-2026-10-13.json).
|
||||||
|
|
||||||
|
| Референс | Схема |
|
||||||
|
| --- | --- |
|
||||||
|
| [ReUI App Shell12](https://reui.io/preview/base/app-shell-12?ref=mcp) | Runtime issuer/Portal → меню приложений → тот же Portal; reader → masked DTO + blocked edit, writer → existing editor |
|
||||||
|
|
||||||
|
Следующий197: реальная доставка из outbox через собственную временную БД, dedupe/two senders, controlled lost receipt после реального Telegram acceptance, no automatic repeat, explicit acknowledged retry/stale version/RBAC. Планируется3 помеченных test messages, включая намеренный повтор№2; не превращать controlled receipt suppression в физическую WAN/lost-ACK приёмку. Portal JWT revocation доexp, настоящее expiry/refresh/client OS и crash/lock-loss остаются отдельными границами.12.5 открыта.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Тестовый сервер UX/UI — 2026-10-10
|
# Тестовый сервер UX/UI — 2026-10-10
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-13: завершены191–195. Backend ux-peak194/frontend ux-peak191; Portal1.3.1 установлен наlocalhost:5175, AUTH_REQUIRED=true, actual login/JWT/RBAC/browser SSO passed. Telegram test принят API и подтверждён пользователем. Найдены196: пароль CHR выдаётся reader JWT и ссылка AppsMenu использует build-time Portal URL — исправить до приёмки приватности/ссылок. Public IPv6 недоступен у пользователя; crash/lock-loss перенесены после интеграций.12.5 открыта.
|
Актуальное продолжение 2026-10-13: завершены191–196. Runtime backend/frontend ux-peak196, Portal1.3.1 healthy, AUTH_REQUIRED=true; reader password redacted вlist/detail, writer/admin сохранены, runtime Portal links исправлены. SQL roles3×2 endpoints/10996ms, E2E41/1.4m, live reader/admin UI4 passed. Telegram базовая доставка подтверждена пользователем. Следующий197 — real Telegram outbox/dedupe/multiple senders/unknown hold/manual retry в собственной временной БД.12.5 открыта.
|
||||||
|
|
||||||
| Параметр | Проверенное состояние |
|
| Параметр | Проверенное состояние |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
@@ -708,7 +708,7 @@ flowchart LR
|
|||||||
|
|
||||||
## 12.5.77 — основной блок IPsec (2026-10-09)
|
## 12.5.77 — основной блок IPsec (2026-10-09)
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-13: завершены191–195. Backend ux-peak194/frontend ux-peak191; Portal1.3.1 установлен наlocalhost:5175, AUTH_REQUIRED=true, actual login/JWT/RBAC/browser SSO passed. Telegram test принят API и подтверждён пользователем. Найдены196: пароль CHR выдаётся reader JWT и ссылка AppsMenu использует build-time Portal URL — исправить до приёмки приватности/ссылок. Public IPv6 недоступен у пользователя; crash/lock-loss перенесены после интеграций.12.5 открыта.
|
Актуальное продолжение 2026-10-13: завершены191–196. Runtime backend/frontend ux-peak196, Portal1.3.1 healthy, AUTH_REQUIRED=true; reader password redacted вlist/detail, writer/admin сохранены, runtime Portal links исправлены. SQL roles3×2 endpoints/10996ms, E2E41/1.4m, live reader/admin UI4 passed. Telegram базовая доставка подтверждена пользователем. Следующий197 — real Telegram outbox/dedupe/multiple senders/unknown hold/manual retry в собственной временной БД.12.5 открыта.
|
||||||
|
|
||||||
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
|
Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md).
|
||||||
|
|
||||||
@@ -2101,3 +2101,21 @@ Frontend:59 lib test files прошли с одной исправленной
|
|||||||
Найден следующий196: настоящее reader JWT получает plaintext password CHR через GETservers. Подтверждён только boolean совпадения, секрет не выведен. Также authPortalBaseUrl для ссылки AppsMenu использует NEXT_PUBLIC_AUTH_PORTAL_URL сборки вместо текущего runtime Portal URL; hook switcher fetch уже использует runtime. Исправить оба API/UI недостатка, проверить reader/writer/admin и реальный Portal на ПК/телефоне, собрать/развернуть и создать локальный commit. Это ограничивает принятие приватности/навигации, но не отменяет доказанный базовый SSO/Telegram transport.
|
Найден следующий196: настоящее reader JWT получает plaintext password CHR через GETservers. Подтверждён только boolean совпадения, секрет не выведен. Также authPortalBaseUrl для ссылки AppsMenu использует NEXT_PUBLIC_AUTH_PORTAL_URL сборки вместо текущего runtime Portal URL; hook switcher fetch уже использует runtime. Исправить оба API/UI недостатка, проверить reader/writer/admin и реальный Portal на ПК/телефоне, собрать/развернуть и создать локальный commit. Это ограничивает принятие приватности/навигации, но не отменяет доказанный базовый SSO/Telegram transport.
|
||||||
|
|
||||||
Пользователь подтвердил отсутствие public IPv6 на всех его стендах — внешняя IPv6 outer приёмка сейчас недоступна. Проверку crash/lock-connection loss из прежнего следующего195 перенести после Portal/Telegram.12.5 открыта.
|
Пользователь подтвердил отсутствие public IPv6 на всех его стендах — внешняя IPv6 outer приёмка сейчас недоступна. Проверку crash/lock-connection loss из прежнего следующего195 перенести после Portal/Telegram.12.5 открыта.
|
||||||
|
|
||||||
|
## 12.5.196 — приватность серверов и согласованный runtime Portal (2026-10-13)
|
||||||
|
|
||||||
|
Реальное reader JWT в195 подтверждённо получало plaintext password CHR. Теперь serverReadForUser на HTTP list/detail возвращает password="" для пользователя без mm:servers:write/admin. Writer/admin и прежний AUTH_REQUIRED=false сохраняют existing DTO для редактирования. is_admin сам по себе не заменяет permission. Проекция создаёт копию и не меняет repository rows, хранимые/native credentials или работу collectors. Изменены servers-mapper.ts и два GET handlers routes/servers.ts; внутренние get/listServersRead остаются пригодными для native операций. Это защита этих endpoints, не аудит всех экспортов/secret-bearing DTO проекта.
|
||||||
|
|
||||||
|
authPortalBaseUrl теперь предпочитает runtime config, как существующий hook switcher fetch. Ссылка «Настроить на портале» больше не ведёт к build-time auth.shnt.top при отдельном тестовом issuer. ReUI PRO/base-nova/Frame/AppsMenu переиспользованы, vendor/стили/разметка не менялись. Unit подтвердил build fallback/runtime override/trailing slash.
|
||||||
|
|
||||||
|
Owned SQL test выполняет actual buildApp/auth middleware/handlers с подписанными test JWT на отдельной ограниченной роли: reader/writer/admin ×list/detail,401 безtoken, password вБД unchanged. Passed10996ms, own DB/role/container удалены. Новый test:server-privacy включён вtest:all, обычный прогон без явной own DB честно skip. Source TypeScript/backend build/scoped ESLint passed.41 unique E2E (runtime links2,header4,servers35) passed1.4m. Изолированные JWT не выдаются за реальный Portal: он проверен отдельно.
|
||||||
|
|
||||||
|
Production backend/frontend ux-peak196 развёрнуты.466 backend+525 frontend source files и1044 compiled backend artifacts совпали. Portal1.3.1/SQLite/shared lab secret и AUTH_REQUIRED=true сохранены новой закрытой Compose override; оригинальные credentials/.env не копировались. Реальный Portal admin API сохраняет необходимые поля редактирования, реальный reader list/detail не содержитпароль, валидная попытка создания403.4 live UI (reader/admin1440/320) безmocks/page errors/overflow: readonly control disabled, admin enabled, runtime Portal link http://localhost:5175/admin/apps. Собственные Portal users удалены. CHR config/passwords не менялись, новых Telegram send в196 нет.
|
||||||
|
|
||||||
|
Протоколы: [SQL](ux-ui/acceptance/portal196-sql196-result.json), [source](ux-ui/acceptance/portal196-source196-result.json), [compiled](ux-ui/acceptance/portal196-compiled196-result.json), [runtime](ux-ui/acceptance/portal196-runtime196-result.json), [fixture E2E](ux-ui/acceptance/portal196-e2e-2026-10-13.json), [actual Portal API](ux-ui/acceptance/portal-live196-api-2026-10-13.json), [live UI4](ux-ui/acceptance/portal196-live-ui-2026-10-13.json).
|
||||||
|
|
||||||
|
| Референс | Схема |
|
||||||
|
| --- | --- |
|
||||||
|
| [ReUI App Shell12](https://reui.io/preview/base/app-shell-12?ref=mcp) | Runtime issuer/Portal → меню приложений → тот же Portal; reader → masked DTO + blocked edit, writer → existing editor |
|
||||||
|
|
||||||
|
Следующий197: реальная доставка из outbox через собственную временную БД, dedupe/two senders, controlled lost receipt после реального Telegram acceptance, no automatic repeat, explicit acknowledged retry/stale version/RBAC. Планируется3 помеченных test messages, включая намеренный повтор№2; не превращать controlled receipt suppression в физическую WAN/lost-ACK приёмку. Portal JWT revocation доexp, настоящее expiry/refresh/client OS и crash/lock-loss остаются отдельными границами.12.5 открыта.
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-13",
|
||||||
|
"block": "12.5.196",
|
||||||
|
"portalImage": "1.3.1",
|
||||||
|
"actualPortalTokens": true,
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"name": "writer/admin credentials",
|
||||||
|
"editableCredentialsRetained": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "unauthenticated",
|
||||||
|
"status": 401
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "real portal admin",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "reader credentials",
|
||||||
|
"listRedacted": true,
|
||||||
|
"detailRedacted": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "reader",
|
||||||
|
"read": 200,
|
||||||
|
"write": 403,
|
||||||
|
"serverWrites": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "revocation boundary",
|
||||||
|
"revokedRefreshSessions": 1,
|
||||||
|
"disabledLogin": 401,
|
||||||
|
"portalMe": 401,
|
||||||
|
"oldMmAccessJwt": 200,
|
||||||
|
"bound": "MM validates self-contained JWT offline; Portal session/account revocation does not revoke issued MM JWT before exp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "no-app",
|
||||||
|
"read": 403,
|
||||||
|
"write": 403,
|
||||||
|
"serverWrites": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tampered signature",
|
||||||
|
"status": 401
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"userFixtureIds": [
|
||||||
|
"2b463ef5-cd53-4aa6-a68d-5e0485ed44c1",
|
||||||
|
"de4d888c-1890-4bde-90f8-dc35755f09d3"
|
||||||
|
],
|
||||||
|
"cleaned": true,
|
||||||
|
"noPasswordsOrTokensPublished": true
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-13",
|
||||||
|
"block": "12.5.196",
|
||||||
|
"command": "playwright test portal-runtime-links page-header servers",
|
||||||
|
"passed": 41,
|
||||||
|
"duration": "1.4m",
|
||||||
|
"sourceFrontendTypeScript": true,
|
||||||
|
"scopedEslint": true,
|
||||||
|
"appSwitcherRuntimeUnitPassed": true,
|
||||||
|
"productionBuilds": [
|
||||||
|
"backend",
|
||||||
|
"frontend"
|
||||||
|
],
|
||||||
|
"scope": "Ordinary API fixtures; live Portal/CHR read checks separately"
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-13",
|
||||||
|
"block": "12.5.196",
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"width": 1440,
|
||||||
|
"role": "reader",
|
||||||
|
"realPortalLogin": true,
|
||||||
|
"runtimePortalLink": true,
|
||||||
|
"writeControlAllowed": false,
|
||||||
|
"noOverflow": true,
|
||||||
|
"pageErrors": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 1440,
|
||||||
|
"role": "admin",
|
||||||
|
"realPortalLogin": true,
|
||||||
|
"runtimePortalLink": true,
|
||||||
|
"writeControlAllowed": true,
|
||||||
|
"noOverflow": true,
|
||||||
|
"pageErrors": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 320,
|
||||||
|
"role": "reader",
|
||||||
|
"realPortalLogin": true,
|
||||||
|
"runtimePortalLink": true,
|
||||||
|
"writeControlAllowed": false,
|
||||||
|
"noOverflow": true,
|
||||||
|
"pageErrors": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 320,
|
||||||
|
"role": "admin",
|
||||||
|
"realPortalLogin": true,
|
||||||
|
"runtimePortalLink": true,
|
||||||
|
"writeControlAllowed": true,
|
||||||
|
"noOverflow": true,
|
||||||
|
"pageErrors": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"apiMocks": false,
|
||||||
|
"serverWrites": 0,
|
||||||
|
"ownPortalUserRemoved": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-13",
|
||||||
|
"block": "12.5.196",
|
||||||
|
"secretEnvCopied": false,
|
||||||
|
"rollbackTags": {
|
||||||
|
"MMAPP_IMAGE_TAG": "ux-peak191",
|
||||||
|
"BACKEND_IMAGE_TAG": "ux-peak194"
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"backend": {
|
||||||
|
"image": "mmapp-test/backend:ux-peak196",
|
||||||
|
"imageId": "sha256:ed826f796c3682af02920cb5bab345505984d20183e1219d0431f9db93d3ce49",
|
||||||
|
"state": "running",
|
||||||
|
"health": "healthy",
|
||||||
|
"matchedVerifiedImage": true
|
||||||
|
},
|
||||||
|
"frontend": {
|
||||||
|
"image": "mmapp-test/frontend:ux-peak196",
|
||||||
|
"imageId": "sha256:1d16efa79e7e9c7bddb1771bf1fcc56862f5461a1ecd6d8c9f87490a0ead0b8f",
|
||||||
|
"state": "running",
|
||||||
|
"health": null,
|
||||||
|
"matchedVerifiedImage": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-13",
|
||||||
|
"block": "12.5.196",
|
||||||
|
"matched": {
|
||||||
|
"backend": 466,
|
||||||
|
"frontend": 525
|
||||||
|
},
|
||||||
|
"different": [],
|
||||||
|
"missing": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-13",
|
||||||
|
"block": "12.5.196",
|
||||||
|
"passed": true,
|
||||||
|
"profiles": [
|
||||||
|
{
|
||||||
|
"date": "2026-10-13",
|
||||||
|
"block": "12.5.196",
|
||||||
|
"database": "mm_accept_166_1791879658920",
|
||||||
|
"scope": "ephemeral role and precreated owned database; no copied production credentials",
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"command": "npm --prefix backend run test:server-privacy",
|
||||||
|
"passed": true,
|
||||||
|
"durationMs": 10996,
|
||||||
|
"roles": 3,
|
||||||
|
"endpointsPerRole": 2,
|
||||||
|
"storedPasswordPreserved": true,
|
||||||
|
"postgresUnavailableSkip": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"scopedRoleVerified": true,
|
||||||
|
"passed": true,
|
||||||
|
"databaseRemoved": true,
|
||||||
|
"roleRemoved": true,
|
||||||
|
"testContainerRemoved": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import {authPortalBaseUrl} from './app-switcher-config'
|
||||||
|
import {ensureAuthConfig} from './auth'
|
||||||
|
import {test} from 'node:test'
|
||||||
|
test('runtime Portal links override the build address',async()=>{
|
||||||
|
const previous=process.env.NEXT_PUBLIC_AUTH_PORTAL_URL,originalFetch=globalThis.fetch
|
||||||
|
try{
|
||||||
|
process.env.NEXT_PUBLIC_AUTH_PORTAL_URL='https://build.example/'
|
||||||
|
assert.equal(authPortalBaseUrl(),'https://build.example')
|
||||||
|
globalThis.fetch=async()=>new Response(JSON.stringify({required:true,portal_url:'http://localhost:5175/'}),{headers:{'content-type':'application/json'}})
|
||||||
|
await ensureAuthConfig()
|
||||||
|
assert.equal(authPortalBaseUrl(),'http://localhost:5175','header links and switcher fetch share runtime Portal')
|
||||||
|
}finally{
|
||||||
|
globalThis.fetch=originalFetch
|
||||||
|
if(previous===undefined)delete process.env.NEXT_PUBLIC_AUTH_PORTAL_URL;else process.env.NEXT_PUBLIC_AUTH_PORTAL_URL=previous
|
||||||
|
}
|
||||||
|
console.log('app switcher: runtime Portal overrides build URL; fallback and trailing slash passed')
|
||||||
|
})
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
type LucideIcon,
|
type LucideIcon,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { formatAppVersionLabel, getAppVersion } from "@/lib/app-version"
|
import { formatAppVersionLabel, getAppVersion } from "@/lib/app-version"
|
||||||
|
import {getAuthConfigSync} from "@/lib/auth"
|
||||||
|
|
||||||
export const CURRENT_APP_ID = "mm"
|
export const CURRENT_APP_ID = "mm"
|
||||||
|
|
||||||
@@ -150,7 +151,7 @@ export function mergeWithLocalApp(config: AppSwitcherConfig): AppSwitcherConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function authPortalBaseUrl(): string | null {
|
export function authPortalBaseUrl(): string | null {
|
||||||
const raw = process.env.NEXT_PUBLIC_AUTH_PORTAL_URL?.trim()
|
const raw = (getAuthConfigSync()?.portalUrl || process.env.NEXT_PUBLIC_AUTH_PORTAL_URL)?.trim()
|
||||||
if (!raw) return null
|
if (!raw) return null
|
||||||
return raw.replace(/\/$/, "")
|
return raw.replace(/\/$/, "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import {test,expect} from '@playwright/test'
|
||||||
|
for(const width of [1440,320])test(`Меню приложений использует runtime Portal ${width}`,async({page})=>{
|
||||||
|
await page.setViewportSize({width,height:1000})
|
||||||
|
await page.route('**/health',r=>r.fulfill({json:{ok:true}}))
|
||||||
|
await page.route('**/api/**',r=>{
|
||||||
|
const path=new URL(r.request().url()).pathname
|
||||||
|
return r.fulfill({json:path==='/api/auth/config'?{required:false,portal_url:'https://runtime.example.test/'}:
|
||||||
|
path==='/api/v1/app-switcher'?{menuLabel:'Приложения',apps:[]}:
|
||||||
|
path==='/api/servers'?[]:path==='/api/evobgp/settings'?{enabled:false,baseUrl:'',secretConfigured:false}:{}})
|
||||||
|
})
|
||||||
|
await page.goto('/servers')
|
||||||
|
await page.getByRole('button',{name:'Приложения',exact:true}).click()
|
||||||
|
await expect(page.getByRole('menuitem',{name:'Настроить на портале',exact:true})).toHaveAttribute('href','https://runtime.example.test/admin/apps')
|
||||||
|
expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth+1)).toBe(true)
|
||||||
|
})
|
||||||
Reference in new issue
Block a user