fix(api): изолировать ошибки создания клиента устройства
This commit is contained in:
1 parent
c7b4981dff
commit
f99612f6aa
7 files changed
+55
-3
No files matched your search
@@ -22,3 +22,5 @@ assert.equal(partial.servers[0].certificates.length,1);assert.equal(partial.serv
|
||||
for(const value of [{...partial,servers:[partial.servers[0],partial.servers[0]]},{...partial,servers:[{...partial.servers[0],serverId:"2"}]},{...partial,servers:[{...partial.servers[0],error:"failure"}]}])assert.equal(certificatesStateSchema.safeParse(value).success,false)
|
||||
assert.equal(certificateObservedSchema.safeParse({...good,validUntil:null}).success,false)
|
||||
console.log("Certificate observed: unknown/date timezone/invalid calendar, flags, key size, known empty, malformed/duplicate/context, partial devices; no DB/RouterOS")
|
||||
|
||||
const setupFailure=await collectCertificatesState([server,{id:2,name:"bad client"}],srv=>{if(srv.id===2)throw new Error("client setup refused");return async()=>[raw]});assert.equal(setupFailure.servers[0].certificates.length,1);assert.equal(setupFailure.servers[1].error,"client setup refused")
|
||||
@@ -36,6 +36,6 @@ export async function readCertificateServerState(server:ServerContext,read:()=>P
|
||||
}
|
||||
}
|
||||
export async function collectCertificatesState<T extends ServerContext>(servers:T[],makeReader:(server:T)=>()=>Promise<unknown>):Promise<CertificatesState>{
|
||||
const states=await Promise.all(servers.map(server=>readCertificateServerState(server,makeReader(server))))
|
||||
const states=await Promise.all(servers.map(server=>readCertificateServerState(server,()=>makeReader(server)())))
|
||||
return certificatesStateSchema.parse({collectedAt:new Date().toISOString(),servers:states})
|
||||
}
|
||||
@@ -35,3 +35,6 @@ console.log("Containers state: independent devices, malformed/duplicate IDs, unk
|
||||
|
||||
const limitOnly=await readContainerServerState(server,async path=>path==="/container"?[{".id":"*C","memory-high":"52428800"}]:[]);assert.equal(limitOnly.containers[0].memMb,undefined)
|
||||
const modern=await readContainerServerState(server,async path=>path==="/container"?[{...row,envlist:"e,e2",mounts:undefined,mountlists:"m"}]:path==="/container/envs"?[{list:"e",key:"A",value:"1"},{list:"e2",key:"B",value:"2"}]:[{list:"m",src:"/disk",dst:"/data"}]);assert.equal(modern.containers[0].envs.length,2);assert.equal(modern.containers[0].mounts.length,1)
|
||||
|
||||
let factories=0
|
||||
const setupFailure=await collectContainersState([server,{...server,id:2}],srv=>{if(srv.id===2)throw new Error("client setup refused");factories++;return read});assert.equal(setupFailure.servers[0].containers.length,1);assert.equal(setupFailure.servers[1].availability,"error");assert.equal(setupFailure.servers[1].failures[0].error,"client setup refused");assert.equal(factories,1)
|
||||
@@ -53,6 +53,9 @@ export async function readContainerServerState(server: ServerRow, read: Reader):
|
||||
return containerServerStateSchema.parse({ serverId: server.id, collectedAt, availability: "available", containers, failures })
|
||||
}
|
||||
export async function collectContainersState(servers: ServerRow[], makeReader: (server: ServerRow) => Reader): Promise<ContainersState> {
|
||||
const states = await Promise.all(servers.map(server => readContainerServerState(server, makeReader(server))))
|
||||
const states = await Promise.all(servers.map(server => {
|
||||
let reader: Reader | undefined
|
||||
return readContainerServerState(server, path => { reader ??= makeReader(server); return reader(path) })
|
||||
}))
|
||||
return containersStateSchema.parse({ collectedAt: new Date().toISOString(), servers: states })
|
||||
}
|
||||
@@ -225,3 +225,5 @@ REST запрос → ожидание → подтверждённый output /
|
||||
| --- | --- | --- |
|
||||
| Пользователи | [Профиль](ux-ui/screens/users-profile-desktop.png), [ошибка](ux-ui/screens/users-error-desktop.png) | [390](ux-ui/screens/users-profile-mobile.png), [320](ux-ui/screens/users-profile-narrow.png) |
|
||||
| Контейнеры | [Обзор](ux-ui/screens/containers-overview-desktop.png), [снимок](ux-ui/screens/containers-export-desktop.png) | [390](ux-ui/screens/containers-export-mobile.png), [320](ux-ui/screens/containers-export-narrow.png) |
|
||||
|
||||
Актуальная точка продолжения: 11А.17 — подключение observed сертификатов в страницу/таблицу/детали. API и snapshot merge подготовлены в 11А.14–11А.16; таблица требований — [этап 11А](ux-ui-stage-11a.md). Сертификаты и весь 11А ещё не приняты.
|
||||
+10
-1
@@ -27,7 +27,7 @@
|
||||
| 8 | Трафик и статистика | Завершён 2026-09-28 в границах локальной приёмки; ограничения в блоке 8.13 |
|
||||
| 9 | VPN, Firewall и Filters | Завершён в границах локальной приёмки 2026-09-30; блоки 9.1–9.64, ограничения в 9.64 |
|
||||
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
||||
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | В работе: 11А.1–11А.15; пользователи/контейнеры приняты локально, reader/snapshots сертификатов готовы |
|
||||
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | В работе: 11А.1–11А.16; пользователи/контейнеры приняты локально, сертификаты: API/reader готовы; далее 11А.17 |
|
||||
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Не начат |
|
||||
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Не начат |
|
||||
|
||||
@@ -36,6 +36,15 @@
|
||||
|
||||
|
||||
|
||||
## Блок 11А.16 — изоляция setup ошибок устройств (2026-10-01)
|
||||
|
||||
- В обоих новых state каталогах создание клиента/reader перенесено внутрь защищённого чтения устройства. Setup исключение одного клиента становится его error/partial, остальные строки остаются доступны; общий каталог не срывается.
|
||||
- Container reader создаётся один раз и используется для /container/envs/mounts. Certificate reader остаётся отдельным для каждого устройства; сетевые команды, Legacy API и авторизация не изменены.
|
||||
- Fault тесты setup для обоих каталогов, test:containers, test:certificates, backend build, профильный lint и diff check прошли. Frontend production/TypeScript проверены в 11А.15; новых frontend изменений в этом блоке нет.
|
||||
- Последняя квота: 96% пятичасового и 77% недельного использовано. Новый большой блок не начинать при этом остатке; продолжить после проверки актуальной квоты.
|
||||
- **Точный следующий шаг — 11А.17:** подключить readCertificatesState + mergeCertificateSnapshots в app/(main)/certificates/page.tsx. Мигрировать CertificatesDataGrid и CertificateExpandedDetail на CertificateObserved/совместимый view type без nullable→0/false; обновить фильтры/KPI, independent server read, error/partial/freshness, prefsHydrated/key backend-mode и отмену/revision чтения. Проверить live HTTP failures/unknown/empty/partial/recovery/права/смену источника, TypeScript/lint/build, обновить журнал и сделать локальный коммит без push.
|
||||
- Далее отдельными блоками ACME settings и renew, issue job polling/частичное выполнение, реальный экспорт/импорт сертификатов, UX/ПК/390/320. Резервные копии, 11Б и общий 12 ещё не приняты; реальные RouterOS/Portal/PostgreSQL/ACME/S3 проверки остаются внешними.
|
||||
|
||||
## Блок 11А.15 — typed reader и последние снимки сертификатов (2026-10-01)
|
||||
|
||||
- readCertificatesState использует строгий новый контракт, GET, caller AbortSignal + 30s срок. Unknown поля сохраняются; malformed/duplicate/чужой контекст, 204 и ошибки HTTP не становятся успешным пустым каталогом.
|
||||
|
||||
@@ -171,3 +171,36 @@
|
||||
- 5 fault/HTTP unit, TypeScript, профильный lint и production frontend прошли. Reader/merge готовы к подключению; текущая страница ещё использует legacy DTO. Приёмка сертификатов не завершена.
|
||||
- Далее узкая проверка setup ошибки клиента устройства (11А.16), затем миграция страницы/таблицы/деталей на observed nullable поля и независимые состояния. Не заменять null числом 0/false, не терять partial rows и выбранный сервер; ACME settings/jobs/экспорт проверять отдельными блоками.
|
||||
|
||||
|
||||
## Блок 11А.16 — изоляция setup ошибок устройств (2026-10-01)
|
||||
|
||||
- В обоих новых state каталогах создание клиента/reader перенесено внутрь защищённого чтения устройства. Setup исключение одного клиента становится его error/partial, остальные строки остаются доступны; общий каталог не срывается.
|
||||
- Container reader создаётся один раз и используется для /container/envs/mounts. Certificate reader остаётся отдельным для каждого устройства; сетевые команды, Legacy API и авторизация не изменены.
|
||||
- Fault тесты setup для обоих каталогов, test:containers, test:certificates, backend build, профильный lint и diff check прошли. Frontend production/TypeScript проверены в 11А.15; новых frontend изменений в этом блоке нет.
|
||||
- Последняя квота: 96% пятичасового и 77% недельного использовано. Новый большой блок не начинать при этом остатке; продолжить после проверки актуальной квоты.
|
||||
- **Точный следующий шаг — 11А.17:** подключить readCertificatesState + mergeCertificateSnapshots в app/(main)/certificates/page.tsx. Мигрировать CertificatesDataGrid и CertificateExpandedDetail на CertificateObserved/совместимый view type без nullable→0/false; обновить фильтры/KPI, independent server read, error/partial/freshness, prefsHydrated/key backend-mode и отмену/revision чтения. Проверить live HTTP failures/unknown/empty/partial/recovery/права/смену источника, TypeScript/lint/build, обновить журнал и сделать локальный коммит без push.
|
||||
- Далее отдельными блоками ACME settings и renew, issue job polling/частичное выполнение, реальный экспорт/импорт сертификатов, UX/ПК/390/320. Резервные копии, 11Б и общий 12 ещё не приняты; реальные RouterOS/Portal/PostgreSQL/ACME/S3 проверки остаются внешними.
|
||||
|
||||
|
||||
## Подключение observed сертификатов: следующий блок 11А.17
|
||||
|
||||
Референсы: существующий [Data Grid Filtering 2](https://reui.io/preview/base/data-grid-filtering-2?ref=mcp) / [Sheet 8](https://reui.io/preview/base/sheet-8?ref=mcp). Сохранить композицию DataGrid v9/Frame/ServerRail и семантические токены.
|
||||
|
||||
| Данные | Сейчас в legacy UI | Требование observed UI |
|
||||
| --- | --- | --- |
|
||||
| Срок отсутствует / дата без timezone | daysLeft=0, возможный valid | —, raw дата с пояснением, unknown; не включать в подтверждённо действительные/истекающие |
|
||||
| keySize/trusted отсутствуют | 0 / false | Размер/доверие неизвестны, действия определяются API/правами |
|
||||
| Отказ одного устройства | Общий error или краткий toast | latest.error постоянно виден, confirmed строки/время сохранены |
|
||||
| Начальный отказ | Пустая таблица/нули | Список и KPI неизвестны, явный повтор |
|
||||
| Подтверждённое empty | Пустая таблица | Известный пустой каталог и нули; очищает предыдущий снимок |
|
||||
| Смена источника | Общий эффект старого контекста | Изолированный key + отмена/revision; поздний ответ не меняет новый экран |
|
||||
|
||||
[Источник и права] → [Независимые state + servers]
|
||||
↓
|
||||
[latest ошибка / confirmed снимок]
|
||||
↓
|
||||
[KPI известных данных → таблица → детали]
|
||||
↓
|
||||
[ACME/экспорт: последующие блоки]
|
||||
|
||||
Проверенные заготовки: shared/api/certificates.ts (readCertificatesState), lib/certificate-snapshots.ts, новый contracts CertificateObserved. Legacy API/renew/issue ещё используются, их миграцию не считать завершённой.
|
||||
Reference in new issue
Block a user