fix(containers): учитывать фактические состояния контейнеров
This commit is contained in:
1 parent
0cc8fe5572
commit
b3d11276dd
14 files changed
+357
-14
No files matched your search
@@ -0,0 +1,94 @@
|
||||
import fs from 'node:fs';
|
||||
import assert from 'node:assert/strict';
|
||||
import { randomUUID, createHash } from 'node:crypto';
|
||||
const { pool } = await import('/app/backend/dist/db/index.js');
|
||||
const { MikrotikClient } = await import('/app/backend/dist/services/mikrotik.js');
|
||||
const rows = (await pool.query("SELECT * FROM servers WHERE host='104.165.244.7' AND enabled=true")).rows; assert.equal(rows.length, 1);
|
||||
const server = rows[0], client = new MikrotikClient({ host: server.host, port: 80, useSsl: false, verifySsl: false, username: server.username, password: server.password });
|
||||
const controlRows = (await pool.query("SELECT * FROM servers WHERE host='194.226.121.150' AND enabled=true")).rows; assert.equal(controlRows.length, 1);
|
||||
const control = new MikrotikClient({ host: controlRows[0].host, port: 80, useSsl: false, verifySsl: false, username: controlRows[0].username, password: controlRows[0].password });
|
||||
const statePath = '/checks/state247-private.json', phase = process.argv[2];
|
||||
const save = state => fs.writeFileSync(statePath, JSON.stringify(state), { mode: 0o600 });
|
||||
const sha = value => createHash('sha256').update(value).digest('hex');
|
||||
const normalizedExport = value => value.split(/\r?\n/).filter(line => !line.startsWith('#') && line.trim()).join('\n');
|
||||
// RouterOS may list available, uninstalled extra packages after reboot; they have no installed version.
|
||||
const packages = rows => rows.filter(p => typeof p.version === 'string' && p.version.trim()).map(p => ({ name: p.name, version: p.version, disabled: p.disabled ?? 'false' })).sort((a,b) => a.name.localeCompare(b.name));
|
||||
const ownContainers = async state => (await client.get('/container', 5000)).filter(c => c.interface === state.veth && c.comment === state.comment);
|
||||
const nativeStatus = row => row.status ?? (row.running === 'true' && row.stopped !== 'true' ? 'running' : row.stopped === 'true' && row.running !== 'true' ? 'stopped' : 'unknown');
|
||||
async function poll(read, accept, maxMs = 150000) {
|
||||
const until = Date.now() + maxMs; let last;
|
||||
while (Date.now() < until) { try { last = await read(); if (accept(last)) return last; } catch {} await new Promise(r => setTimeout(r, 1500)); }
|
||||
throw Error('Native state not confirmed within polling budget');
|
||||
}
|
||||
async function reboot(state, key) {
|
||||
assert(!state[key], 'Never repeat a possibly applied reboot');
|
||||
state[key] = { requested: true, acknowledgement: 'unknown' }; save(state);
|
||||
try { await client.post('/system/reboot', {}, 5000); state[key].acknowledgement = 'confirmed'; }
|
||||
catch (e) { state[key].errorName = e.name; state[key].status = e.statusCode ?? null; }
|
||||
save(state); console.log(JSON.stringify({ phase, rebootRequestedOnce: true, acknowledgement: state[key].acknowledgement }));
|
||||
}
|
||||
try {
|
||||
if (phase === 'init') {
|
||||
assert(!fs.existsSync(statePath));
|
||||
const uuid = randomUUID().slice(0,8), state = { serverId: Number(server.id), targetHost: server.host, veth: 'mmc247-' + uuid, root: 'mmc247-' + uuid + '-root', comment: 'mmc247-' + uuid, imageFile: 'mmc247-busybox.tar', packageFile: 'container-7.24.5.npk', packageSize: 1163409, imageSize: 2226176, beforePackages: packages(await client.get('/system/package')), beforeFiles: await client.get('/file'), beforeExport: normalizedExport(await client.exportConfigScript()), stages: [] };
|
||||
assert(!state.beforePackages.some(p => p.name === 'container')); assert(state.beforePackages.every(p => p.version === '7.24.5'));
|
||||
assert(!state.beforeFiles.some(f => f.name === state.imageFile || f.name === state.packageFile || f.name.startsWith(state.root)));
|
||||
assert(!(await client.get('/interface')).some(i => i.name === state.veth)); await control.get('/system/resource', 5000);
|
||||
state.beforeExportSha256 = sha(state.beforeExport); save(state); console.log('Private native baseline captured; control CHR reachable');
|
||||
} else {
|
||||
const state = JSON.parse(fs.readFileSync(statePath)); assert(/^mmc247-[a-f0-9]{8}$/.test(state.veth)); assert.equal(state.root, state.veth + '-root');
|
||||
if (phase === 'reboot-install') {
|
||||
const files = await client.get('/file');
|
||||
assert(files.some(f => f.name === state.packageFile && Number(f.size) === state.packageSize));
|
||||
assert(files.some(f => f.name === state.imageFile && Number(f.size) === state.imageSize));
|
||||
await control.get('/system/resource', 5000); await reboot(state, 'installReboot');
|
||||
} else if (phase === 'installed') {
|
||||
const p = await poll(() => client.get('/system/package', 4000), p => p.some(p => p.name === 'container' && p.version === '7.24.5' && p.disabled === 'false'));
|
||||
assert.equal((await client.get('/container')).length, 0); await control.get('/system/resource', 5000);
|
||||
state.afterInstallPackages = packages(p); state.stages.push({ phase, packageInstalled: true, version: '7.24.5', controlReachable: true }); save(state); console.log('Container package installed on7.24.5; native endpoint200/control reachable');
|
||||
} else if (phase === 'create') {
|
||||
assert(state.afterInstallPackages); assert.equal((await ownContainers(state)).length, 0);
|
||||
await client.put('/interface/veth', { name: state.veth, address: '198.18.247.2/30', gateway: '198.18.247.1', comment: state.comment });
|
||||
await client.put('/container', { file: state.imageFile, interface: state.veth, 'root-dir': state.root, cmd: '/bin/sleep 600', 'start-on-boot': 'false', comment: state.comment });
|
||||
const containers = await poll(() => ownContainers(state), rows => rows.length === 1 && nativeStatus(rows[0]) === 'stopped'); assert.equal(containers.length, 1);
|
||||
state.containerId = containers[0]['.id']; state.stages.push({ phase, initialStatus: 'stopped' }); save(state);
|
||||
fs.writeFileSync('/checks/state247-public.json', JSON.stringify({ serverId: state.serverId, rosId: state.containerId, interface: state.veth, comment: state.comment, root: state.root })); console.log('Own container extracted/stopped; public identity saved');
|
||||
} else if (phase === 'adopt-created') {
|
||||
const containers = await ownContainers(state); assert.equal(containers.length, 1); assert.equal(nativeStatus(containers[0]), 'stopped');
|
||||
state.containerId = containers[0]['.id']; state.stages.push({ phase: 'create', initialStatus: 'stopped', observedNativeStoppedFlag: containers[0].stopped }); save(state);
|
||||
fs.writeFileSync('/checks/state247-public.json', JSON.stringify({ serverId: state.serverId, rosId: state.containerId, name: containers[0].name, interface: state.veth, comment: state.comment, root: state.root })); console.log('Existing own extracted container adopted read-only');
|
||||
} else if (phase === 'running' || phase === 'stopped') {
|
||||
const containers = await poll(() => ownContainers(state), rows => rows.length === 1 && nativeStatus(rows[0]) === phase, 30000);
|
||||
assert.equal(containers[0]['.id'], state.containerId); state.stages.push({ phase, nativeConfirmed: true }); save(state); console.log('Native status confirmed: ' + phase);
|
||||
} else if (phase === 'cleanup-network') {
|
||||
const containers = await ownContainers(state); assert(containers.length <= 1);
|
||||
if (containers.length) { if (nativeStatus(containers[0]) !== 'stopped') { await client.post('/container/stop', { '.id': containers[0]['.id'] }); await poll(() => ownContainers(state), rows => rows.length === 1 && nativeStatus(rows[0]) === 'stopped', 30000); } await client.delete('/container/' + encodeURIComponent(containers[0]['.id'])); }
|
||||
const veth = (await client.get('/interface/veth')).filter(i => i.name === state.veth); assert(veth.length <= 1);
|
||||
for (const i of veth) { assert.equal(i.comment, state.comment); await client.delete('/interface/veth/' + encodeURIComponent(i['.id'])); }
|
||||
const files = await client.get('/file');
|
||||
const ownFile = f => { const name = String(f.name).replace(/^\/+/, ''); return name === state.imageFile || name === state.packageFile || name === state.root || name.startsWith(state.root + '/'); };
|
||||
assert(!state.beforeFiles.some(ownFile), 'Never remove an original file');
|
||||
const own = files.filter(ownFile).sort((a,b) => b.name.length - a.name.length);
|
||||
assert(own.every(f => !f.name.split('/').includes('..')));
|
||||
for (const f of own) { const current = (await client.get('/file')).find(x => x.name === f.name); if (current) await client.removeFile(current['.id']); }
|
||||
assert.equal((await ownContainers(state)).length, 0); assert(!(await client.get('/interface/veth')).some(i => i.name === state.veth));
|
||||
assert(!(await client.get('/file')).some(ownFile));
|
||||
state.stages.push({ phase, ownObjectsAbsent: true }); save(state); console.log('Own container/veth/image/root removed');
|
||||
} else if (phase === 'uninstall') {
|
||||
assert(state.stages.some(s => s.phase === 'cleanup-network'));
|
||||
const p = (await client.get('/system/package')).filter(p => p.name === 'container'); assert.equal(p.length, 1); assert.equal(p[0].version, '7.24.5');
|
||||
await client.post('/system/package/uninstall', { '.id': p[0]['.id'] });
|
||||
const scheduled = (await client.get('/system/package')).find(p => p.name === 'container'); assert(['uninstall','scheduled for uninstall'].includes(scheduled.scheduled));
|
||||
await reboot(state, 'uninstallReboot');
|
||||
} else if (phase === 'reboot-uninstall') {
|
||||
const scheduled = (await client.get('/system/package')).find(p => p.name === 'container'); assert(scheduled && ['uninstall','scheduled for uninstall'].includes(scheduled.scheduled));
|
||||
await reboot(state, 'uninstallReboot');
|
||||
} else if (phase === 'final') {
|
||||
const p = await poll(() => client.get('/system/package', 4000), p => !packages(p).some(p => p.name === 'container'));
|
||||
assert.deepEqual(packages(p), state.beforePackages); assert.equal(normalizedExport(await client.exportConfigScript()), state.beforeExport);
|
||||
await control.get('/system/resource', 5000);
|
||||
const report = { date: '2026-10-14', block: '12.5.247', targetHost: server.host, controlHost: '194.226.121.150', stages: state.stages, installReboot: state.installReboot, uninstallReboot: state.uninstallReboot, originalPackagesMatched: true, originalNormalizedExportSha256: state.beforeExportSha256, originalNormalizedExportMatched: true, controlReachable: true, passwordChanges: 0, deviceModeChanges: 0, firmwareVersionChanged: false, passed: true };
|
||||
fs.writeFileSync('/checks/containers247-native.json', JSON.stringify(report, null, 2) + '\n'); fs.unlinkSync(statePath); console.log(JSON.stringify(report));
|
||||
} else throw Error('Unsupported phase');
|
||||
}
|
||||
} finally { await pool.end(); }
|
||||
@@ -0,0 +1,26 @@
|
||||
type Status = "running" | "stopped" | "error" | "unknown"
|
||||
const flags = ["running", "stopped", "error", "failed", "extracting", "downloading", "starting", "stopping"] as const
|
||||
/** RouterOS REST can expose active flags instead of the legacy status field. */
|
||||
export function containerRuntimeStatus(row: Record<string, unknown>): Status {
|
||||
const active = new Set<string>()
|
||||
for (const flag of flags) {
|
||||
const value = row[flag]
|
||||
if (value === undefined) continue
|
||||
if (value === true || value === "true" || value === "yes") active.add(flag)
|
||||
else if (value !== false && value !== "false" && value !== "no") return "unknown"
|
||||
}
|
||||
if (["extracting", "downloading", "starting", "stopping"].some(flag => active.has(flag))) return "unknown"
|
||||
const states: Status[] = []
|
||||
if (active.has("running")) states.push("running")
|
||||
if (active.has("stopped")) states.push("stopped")
|
||||
if (active.has("error") || active.has("failed")) states.push("error")
|
||||
if (states.length > 1) return "unknown"
|
||||
if (row.status !== undefined) {
|
||||
if (typeof row.status !== "string") return "unknown"
|
||||
const value = row.status.toLowerCase()
|
||||
const legacy = value === "running" || value === "stopped" ? value : value === "error" || value === "failed" ? "error" : "unknown"
|
||||
if (legacy === "unknown" || (states.length && states[0] !== legacy)) return "unknown"
|
||||
return legacy
|
||||
}
|
||||
return states[0] ?? "unknown"
|
||||
}
|
||||
@@ -10,8 +10,19 @@ const good = await readContainerServerState(server, read)
|
||||
assert.equal(good.availability, "available"); assert.equal(good.containers[0].status,"running")
|
||||
assert.equal(good.containers[0].cpu,0);assert.equal(good.containers[0].memMb,0);assert.equal(good.containers[0].startOnBoot,true)
|
||||
assert.deepEqual(good.containers[0].envs,[{key:"A",value:"B"}]);assert.deepEqual(good.containers[0].mounts,[{src:"/disk",dst:"/data"}])
|
||||
const nativeEnvLists = await readContainerServerState(server, async path => path === "/container" ? [{ ...row, envlist: undefined, envlists: "e" }] : read(path))
|
||||
assert.deepEqual(nativeEnvLists.containers[0].envs, [{ key: "A", value: "B" }])
|
||||
const unknown = await readContainerServerState(server, async path => path === "/container" ? [{".id":"*B",name:"pending",status:"extracting"}] : [])
|
||||
assert.equal(unknown.containers[0].status,"unknown");assert.equal(unknown.containers[0].startOnBoot,null);assert.equal(unknown.containers[0].image,"")
|
||||
for (const [flags, expected] of [
|
||||
[{ stopped: "true" }, "stopped"], [{ running: "true" }, "running"], [{ error: true }, "error"],
|
||||
[{ stopped: true, running: false }, "stopped"], [{ stopped: "true", running: "true" }, "unknown"],
|
||||
[{ stopped: "true", extracting: "true" }, "unknown"], [{ stopped: "maybe" }, "unknown"],
|
||||
[{ status: "running", stopped: "true" }, "unknown"], [{ running: "false", stopped: "false" }, "unknown"],
|
||||
] as const) {
|
||||
const result = await readContainerServerState(server, async path => path === "/container" ? [{ ".id": "*N", name: "native", ...flags }] : [])
|
||||
assert.equal(result.availability, "available"); assert.equal(result.containers[0].status, expected)
|
||||
}
|
||||
const empty = await readContainerServerState(server, async()=>[])
|
||||
assert.equal(empty.availability,"available");assert.equal(empty.containers.length,0)
|
||||
for (const raw of [{containers:[]},[{}],[row,row]]) {
|
||||
|
||||
@@ -2,14 +2,19 @@ import { z } from "zod"
|
||||
import { containersStateSchema, containerServerStateSchema, type ContainersState, type ContainerServerState } from "@mmapp/contracts/containers"
|
||||
import { mapContainerRow } from "./containers-live.js"
|
||||
import { MikrotikError } from "./mikrotik.js"
|
||||
import { containerRuntimeStatus } from "./container-runtime-status.js"
|
||||
|
||||
type ServerRow = Parameters<typeof mapContainerRow>[0]
|
||||
type Reader = (path: string) => Promise<unknown>
|
||||
const rawSchema = z.array(z.object({
|
||||
".id": z.string().min(1), name: z.string().optional(), tag: z.string().optional(), "remote-image": z.string().optional(),
|
||||
interface: z.string().optional(), envlist: z.string().optional(), mounts: z.string().optional(), mountlists: z.string().optional(),
|
||||
interface: z.string().optional(), envlist: z.string().optional(), envlists: z.string().optional(), mounts: z.string().optional(), mountlists: z.string().optional(),
|
||||
cmd: z.string().optional(), "start-on-boot": z.string().optional(), comment: z.string().optional(),
|
||||
status: z.string().optional(), "memory-high": z.string().optional(), "memory-current": z.string().optional(), cpu: z.string().optional(),
|
||||
running: z.union([z.string(), z.boolean()]).optional(), stopped: z.union([z.string(), z.boolean()]).optional(),
|
||||
error: z.union([z.string(), z.boolean()]).optional(), failed: z.union([z.string(), z.boolean()]).optional(),
|
||||
extracting: z.union([z.string(), z.boolean()]).optional(), downloading: z.union([z.string(), z.boolean()]).optional(),
|
||||
starting: z.union([z.string(), z.boolean()]).optional(), stopping: z.union([z.string(), z.boolean()]).optional(),
|
||||
})).superRefine((rows, ctx) => {
|
||||
if (new Set(rows.map(r => r[".id"])).size !== rows.length) ctx.addIssue({ code: "custom", message: "Дубли RouterOS ID" })
|
||||
})
|
||||
@@ -39,13 +44,12 @@ export async function readContainerServerState(server: ServerRow, read: Reader):
|
||||
const envRows = envs.status === "fulfilled" ? envs.value : []
|
||||
const mountRows = mounts.status === "fulfilled" ? mounts.value : []
|
||||
const mapped = mapContainerRow(server, { ...row, mounts: row.mountlists ?? row.mounts }, envRows.map(item => ({ ...item, name: item.list ?? item.name })), mountRows.map(item => ({ ...item, name: item.list ?? item.name })), index)
|
||||
const envLists = new Set((row.envlist ?? "").split(",").map(name => name.trim()).filter(Boolean))
|
||||
const envLists = new Set((row.envlists ?? row.envlist ?? "").split(",").map(name => name.trim()).filter(Boolean))
|
||||
mapped.envs = envRows.filter(item => envLists.has(item.list ?? item.name ?? "")).map(item => ({ key: item.key, value: item.value }))
|
||||
const status = (row.status ?? "").toLowerCase()
|
||||
const boot = row["start-on-boot"]
|
||||
const mem = Number(row["memory-current"]?.trim() || NaN)
|
||||
return { ...mapped, image: row["remote-image"] ? mapped.image : "", tag: row["remote-image"] ? mapped.tag : "",
|
||||
status: status === "running" || status === "stopped" ? status : status === "error" || status === "failed" ? "error" : "unknown",
|
||||
status: containerRuntimeStatus(row),
|
||||
startOnBoot: boot === "true" || boot === "yes" ? true : boot === "false" || boot === "no" ? false : null,
|
||||
cpu: mapped.cpu !== undefined && mapped.cpu >= 0 && mapped.cpu <= 100 ? mapped.cpu : undefined,
|
||||
memMb: Number.isFinite(mem) && mem >= 0 ? Math.round(mem / (1024 * 1024)) : undefined }
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
5. Через реальный Portal/API/UI проверить start → running → stop → stopped → restart → running → remove, отрицательные права и повторное действие. Сверять native state, не только toast/HTTP ack. Отдельно фиксировать неизвестные промежуточные статусы.
|
||||
6. Удалить только собственный контейнер, veth/bridge/IP/каталоги/image/import файлы и сравнить исходные конфигурации. Если согласовано полное возвращение пакетов — удалить только новый container package и выполнить второй контролируемый reboot; подтвердить исходный набор/версию. Сохранение пакета требует явного решения пользователя.
|
||||
|
||||
Текущий статус: **подготовка завершена, установка и перезапуски не выполнялись**. Отсутствие positive lifecycle нельзя заменять успешным `unsupported` UI. Полная история и границы — [журнал](../docs/ux-ui-progress.md).
|
||||
Текущий статус (247, 2026-10-14): **после явного согласия выполнены установка и удаление выбранного пакета с двумя перезагрузками**. Через настоящий Portal/UI подтверждены start/stop/start/restart; удаление работающего контейнера вернуло 502 и остаётся открытым. Собственные объекты удалены native API; исходные установленные пакеты и нормализованный экспорт совпали. Backend учитывает native running/stopped flags и envlists. E2E не стартовали из-за таймаута webServer. [Native-протокол](../docs/ux-ui/acceptance/containers247-native.json), [UI-протокол](../docs/ux-ui/acceptance/containers247-live-ui.json). Ранние формулировки подготовки выше отражают состояние до полученного согласия; пакет уже удалён. Следующий блок — исправление и приёмка удаления через UI. Полная история и границы — [журнал](../docs/ux-ui-progress.md).
|
||||
@@ -1,6 +1,6 @@
|
||||
# UX/UI: протокол внешней приёмки 12.5
|
||||
|
||||
Актуальное продолжение 2026-10-14:246 — по user correction подготовлен container7.24.5 сmikrotik.ltd→MikroTikPatch release:archive29,675,363bytes/digest matched, selected NPK1,163,409bytes/SHA70f1c528…, official244 rejected/no apply. Main/option/python/keygen scripts не выбраны. Procedure обновлена; установка/reboot ждут согласия на новый комплект. CI245 passed; runtime ux-geoip242/ux-backups237 unchanged. Следующий247 — positive container lifecycle после согласия; до него независимый аудит границ текущего deployment. Этап12.5 открыт.
|
||||
Актуальное продолжение 2026-10-14: 247 — разрешённый container-пакет из источника mikrotik.ltd установлен и удалён на 104.165.244.7 с двумя перезагрузками. Исправлена обработка native running/stopped flags. Через реальный Portal/UI прошли start, stop, повторный start и restart; remove работающего контейнера вернул 502 и остаётся открытым. Собственные объекты удалены через native API, исходный экспорт и пакеты совпали. Backend ux-containers247, frontend ux-backups237. Следующий 248 — исправление и приёмка удаления контейнера; отдельно выяснить расхождение авторизации REST/SSH. Этап 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,20 @@ 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.247 — фактические состояния и жизненный цикл контейнера (2026-10-14)
|
||||
|
||||
Пользователь разрешил конкретный пакет 246 и две перезагрузки. На 104.165.244.7 установлен только container-7.24.5.npk из указанного источника mikrotik.ltd → MikroTikPatch, с ранее проверенным SHA256. Core/option/python/keygen/patch scripts не применялись; версия RouterOS осталась 7.24.5. Образ BusyBox 1.37.0 linux/amd64 закреплён digest busybox@sha256:bdf57e528e45e4433820e045b29b45978255a1c9e38353532d90a01445013f82e. Созданы только собственные контейнер mmc247-busybox, veth mmc247-39da5c21 и каталог; bridge, IP, NAT, маршруты и глобальные registry credentials не изменялись.
|
||||
|
||||
Реальный REST отдаёт stopped/running флаги вместо status и envlists вместо envlist. Общая обработка теперь поддерживает эти поля, сохраняет unknown при переходе, неверном значении или противоречии; отсутствие статуса не считается stopped. Для envlists проверена backend-фикстура, отдельное live создание envlist не выполнялось. test:containers (service + HTTP), сборка contracts/backend и профильный ESLint прошли. Добавлены девять проверок native флагов и проверка alias. E2E containers-data/containers-operations не стартовали: тестовый webServer :3100 превысил 120 секунд; успешной E2E приёмки этот блок не заявляет.
|
||||
|
||||
Через настоящий Portal без моков в UI 1440×1000 получены start 200 → stop 200 → start 200 → restart 200 с ok=true и проверкой rosId. Native состояния независимо подтвердили running → stopped → running. Удаление работающего контейнера через UI вернуло 502/ok=false; причина не доказана, сценарий НЕ принят. Ошибок JavaScript нет. [UI-протокол](ux-ui/acceptance/containers247-live-ui.json).
|
||||
|
||||
Очистка выполнена native API: собственный контейнер остановлен и удалён, veth/image/root/NPK отсутствуют. Container package удалён второй однократной перезагрузкой; оба reboot получили подтверждение. Доступные, но не установленные пакеты RouterOS имеют пустую version — при сравнении учитываются только установленные. После reboot автоматическое определение часового пояса временно отличалось от исходного, затем вернулось к Europe/Warsaw. Попытка PATCH singleton clock получила 400 и ничего не изменила; manual clock restore не применён. Итоговый нормализованный export и исходные installed routeros/option 7.24.5 совпали. Приватный baseline удалён. [Native-проверка и очистка](ux-ui/acceptance/containers247-native.json).
|
||||
|
||||
Backend ux-containers247 развёрнут и healthy; frontend ux-backups237 сохранён. При сборке совпали 521 исходный файл и 1076 compiled файлов; последующие исправления только CLI helper для cleanup не входят в замороженный source audit. [Deployment](ux-ui/acceptance/containers247-deployment.json). Агент не менял passwords/device-mode/keys. С Debian REST работает после обеих перезагрузок; прямой REST с рабочего компьютера возвращал 401, SSH с прежним паролем сначала прошёл, повторный вход не прошёл. Причина расхождения неизвестна; глобальную исправность SSH/пароля не заявляем. Контрольный CHR 194.226.121.150 доступен.
|
||||
|
||||
Следующий блок 248: понятная и безопасная обработка удаления работающего контейнера, приёмка удаления остановленного через UI и повторного действия, затем диагностика запуска E2E и корреляция авторизации без изменения паролей. RBAC/повторное действие в live 247 не проверены. ACME, внешняя публикация релизов и другие ранее записанные ограничения остаются открытыми. Полная регрессия не повторялась.
|
||||
|
||||
## 12.5.246 — пакет контейнеров из указанного пользователем источника (2026-10-14)
|
||||
|
||||
User отклонил official244 с причиной«нужно использовать пакеты с сайтаmikrotik.ltd». Перезагрузки/installation не выполнялись. Прочитан [сайт](https://mikrotik.ltd/) и его ссылка [MikroTikPatch](https://github.com/elseif/MikroTikPatch); inline download template указывает GitHub releases. Зафиксирован [точный7.24.5/x86 archive](https://github.com/elseif/MikroTikPatch/releases/download/7.24.5/all_packages-x86-7.24.5.zip), size29,675,363 иSHA256b293b380e680aece7fd0895889fd77b01133ea5ed6d89b32fb6d53af1936acc9 совпали сGitHub API asset digest/size. Извлечён толькоcontainer-7.24.5.npk1,163,409bytes/SHA25670f1c5281a3d37b347e9a83c81d9daaac511357fe26cfbac11321e2dc1407259. Он отличаетсяотофициального244; подменаартефактаоднимfilenameнепринимается.
|
||||
|
||||
+15
-1
@@ -1,9 +1,23 @@
|
||||
# План реализации UX/UI по пятичасовым окнам
|
||||
|
||||
Актуальное продолжение 2026-10-14:246 — по user correction подготовлен container7.24.5 сmikrotik.ltd→MikroTikPatch release:archive29,675,363bytes/digest matched, selected NPK1,163,409bytes/SHA70f1c528…, official244 rejected/no apply. Main/option/python/keygen scripts не выбраны. Procedure обновлена; установка/reboot ждут согласия на новый комплект. CI245 passed; runtime ux-geoip242/ux-backups237 unchanged. Следующий247 — positive container lifecycle после согласия; до него независимый аудит границ текущего deployment. Этап12.5 открыт.
|
||||
Актуальное продолжение 2026-10-14: 247 — разрешённый container-пакет из источника mikrotik.ltd установлен и удалён на 104.165.244.7 с двумя перезагрузками. Исправлена обработка native running/stopped flags. Через реальный Portal/UI прошли start, stop, повторный start и restart; remove работающего контейнера вернул 502 и остаётся открытым. Собственные объекты удалены через native API, исходный экспорт и пакеты совпали. Backend ux-containers247, frontend ux-backups237. Следующий 248 — исправление и приёмка удаления контейнера; отдельно выяснить расхождение авторизации REST/SSH. Этап 12.5 открыт.
|
||||
|
||||
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-14. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
||||
|
||||
## 12.5.247 — фактические состояния и жизненный цикл контейнера (2026-10-14)
|
||||
|
||||
Пользователь разрешил конкретный пакет 246 и две перезагрузки. На 104.165.244.7 установлен только container-7.24.5.npk из указанного источника mikrotik.ltd → MikroTikPatch, с ранее проверенным SHA256. Core/option/python/keygen/patch scripts не применялись; версия RouterOS осталась 7.24.5. Образ BusyBox 1.37.0 linux/amd64 закреплён digest busybox@sha256:bdf57e528e45e4433820e045b29b45978255a1c9e38353532d90a01445013f82e. Созданы только собственные контейнер mmc247-busybox, veth mmc247-39da5c21 и каталог; bridge, IP, NAT, маршруты и глобальные registry credentials не изменялись.
|
||||
|
||||
Реальный REST отдаёт stopped/running флаги вместо status и envlists вместо envlist. Общая обработка теперь поддерживает эти поля, сохраняет unknown при переходе, неверном значении или противоречии; отсутствие статуса не считается stopped. Для envlists проверена backend-фикстура, отдельное live создание envlist не выполнялось. test:containers (service + HTTP), сборка contracts/backend и профильный ESLint прошли. Добавлены девять проверок native флагов и проверка alias. E2E containers-data/containers-operations не стартовали: тестовый webServer :3100 превысил 120 секунд; успешной E2E приёмки этот блок не заявляет.
|
||||
|
||||
Через настоящий Portal без моков в UI 1440×1000 получены start 200 → stop 200 → start 200 → restart 200 с ok=true и проверкой rosId. Native состояния независимо подтвердили running → stopped → running. Удаление работающего контейнера через UI вернуло 502/ok=false; причина не доказана, сценарий НЕ принят. Ошибок JavaScript нет. [UI-протокол](ux-ui/acceptance/containers247-live-ui.json).
|
||||
|
||||
Очистка выполнена native API: собственный контейнер остановлен и удалён, veth/image/root/NPK отсутствуют. Container package удалён второй однократной перезагрузкой; оба reboot получили подтверждение. Доступные, но не установленные пакеты RouterOS имеют пустую version — при сравнении учитываются только установленные. После reboot автоматическое определение часового пояса временно отличалось от исходного, затем вернулось к Europe/Warsaw. Попытка PATCH singleton clock получила 400 и ничего не изменила; manual clock restore не применён. Итоговый нормализованный export и исходные installed routeros/option 7.24.5 совпали. Приватный baseline удалён. [Native-проверка и очистка](ux-ui/acceptance/containers247-native.json).
|
||||
|
||||
Backend ux-containers247 развёрнут и healthy; frontend ux-backups237 сохранён. При сборке совпали 521 исходный файл и 1076 compiled файлов; последующие исправления только CLI helper для cleanup не входят в замороженный source audit. [Deployment](ux-ui/acceptance/containers247-deployment.json). Агент не менял passwords/device-mode/keys. С Debian REST работает после обеих перезагрузок; прямой REST с рабочего компьютера возвращал 401, SSH с прежним паролем сначала прошёл, повторный вход не прошёл. Причина расхождения неизвестна; глобальную исправность SSH/пароля не заявляем. Контрольный CHR 194.226.121.150 доступен.
|
||||
|
||||
Следующий блок 248: понятная и безопасная обработка удаления работающего контейнера, приёмка удаления остановленного через UI и повторного действия, затем диагностика запуска E2E и корреляция авторизации без изменения паролей. RBAC/повторное действие в live 247 не проверены. ACME, внешняя публикация релизов и другие ранее записанные ограничения остаются открытыми. Полная регрессия не повторялась.
|
||||
|
||||
## 12.5.246 — пакет контейнеров из указанного пользователем источника (2026-10-14)
|
||||
|
||||
User отклонил official244 с причиной«нужно использовать пакеты с сайтаmikrotik.ltd». Перезагрузки/installation не выполнялись. Прочитан [сайт](https://mikrotik.ltd/) и его ссылка [MikroTikPatch](https://github.com/elseif/MikroTikPatch); inline download template указывает GitHub releases. Зафиксирован [точный7.24.5/x86 archive](https://github.com/elseif/MikroTikPatch/releases/download/7.24.5/all_packages-x86-7.24.5.zip), size29,675,363 иSHA256b293b380e680aece7fd0895889fd77b01133ea5ed6d89b32fb6d53af1936acc9 совпали сGitHub API asset digest/size. Извлечён толькоcontainer-7.24.5.npk1,163,409bytes/SHA25670f1c5281a3d37b347e9a83c81d9daaac511357fe26cfbac11321e2dc1407259. Он отличаетсяотофициального244; подменаартефактаоднимfilenameнепринимается.
|
||||
|
||||
+15
-1
@@ -1,6 +1,6 @@
|
||||
# UX/UI: журнал реализации
|
||||
|
||||
Актуальное продолжение 2026-10-14:246 — по user correction подготовлен container7.24.5 сmikrotik.ltd→MikroTikPatch release:archive29,675,363bytes/digest matched, selected NPK1,163,409bytes/SHA70f1c528…, official244 rejected/no apply. Main/option/python/keygen scripts не выбраны. Procedure обновлена; установка/reboot ждут согласия на новый комплект. CI245 passed; runtime ux-geoip242/ux-backups237 unchanged. Следующий247 — positive container lifecycle после согласия; до него независимый аудит границ текущего deployment. Этап12.5 открыт.
|
||||
Актуальное продолжение 2026-10-14: 247 — разрешённый container-пакет из источника mikrotik.ltd установлен и удалён на 104.165.244.7 с двумя перезагрузками. Исправлена обработка native running/stopped flags. Через реальный Portal/UI прошли start, stop, повторный start и restart; remove работающего контейнера вернул 502 и остаётся открытым. Собственные объекты удалены через native API, исходный экспорт и пакеты совпали. Backend ux-containers247, frontend ux-backups237. Следующий 248 — исправление и приёмка удаления контейнера; отдельно выяснить расхождение авторизации REST/SSH. Этап 12.5 открыт.
|
||||
|
||||
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
|
||||
|
||||
@@ -38,6 +38,20 @@
|
||||
| 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.247 — фактические состояния и жизненный цикл контейнера (2026-10-14)
|
||||
|
||||
Пользователь разрешил конкретный пакет 246 и две перезагрузки. На 104.165.244.7 установлен только container-7.24.5.npk из указанного источника mikrotik.ltd → MikroTikPatch, с ранее проверенным SHA256. Core/option/python/keygen/patch scripts не применялись; версия RouterOS осталась 7.24.5. Образ BusyBox 1.37.0 linux/amd64 закреплён digest busybox@sha256:bdf57e528e45e4433820e045b29b45978255a1c9e38353532d90a01445013f82e. Созданы только собственные контейнер mmc247-busybox, veth mmc247-39da5c21 и каталог; bridge, IP, NAT, маршруты и глобальные registry credentials не изменялись.
|
||||
|
||||
Реальный REST отдаёт stopped/running флаги вместо status и envlists вместо envlist. Общая обработка теперь поддерживает эти поля, сохраняет unknown при переходе, неверном значении или противоречии; отсутствие статуса не считается stopped. Для envlists проверена backend-фикстура, отдельное live создание envlist не выполнялось. test:containers (service + HTTP), сборка contracts/backend и профильный ESLint прошли. Добавлены девять проверок native флагов и проверка alias. E2E containers-data/containers-operations не стартовали: тестовый webServer :3100 превысил 120 секунд; успешной E2E приёмки этот блок не заявляет.
|
||||
|
||||
Через настоящий Portal без моков в UI 1440×1000 получены start 200 → stop 200 → start 200 → restart 200 с ok=true и проверкой rosId. Native состояния независимо подтвердили running → stopped → running. Удаление работающего контейнера через UI вернуло 502/ok=false; причина не доказана, сценарий НЕ принят. Ошибок JavaScript нет. [UI-протокол](ux-ui/acceptance/containers247-live-ui.json).
|
||||
|
||||
Очистка выполнена native API: собственный контейнер остановлен и удалён, veth/image/root/NPK отсутствуют. Container package удалён второй однократной перезагрузкой; оба reboot получили подтверждение. Доступные, но не установленные пакеты RouterOS имеют пустую version — при сравнении учитываются только установленные. После reboot автоматическое определение часового пояса временно отличалось от исходного, затем вернулось к Europe/Warsaw. Попытка PATCH singleton clock получила 400 и ничего не изменила; manual clock restore не применён. Итоговый нормализованный export и исходные installed routeros/option 7.24.5 совпали. Приватный baseline удалён. [Native-проверка и очистка](ux-ui/acceptance/containers247-native.json).
|
||||
|
||||
Backend ux-containers247 развёрнут и healthy; frontend ux-backups237 сохранён. При сборке совпали 521 исходный файл и 1076 compiled файлов; последующие исправления только CLI helper для cleanup не входят в замороженный source audit. [Deployment](ux-ui/acceptance/containers247-deployment.json). Агент не менял passwords/device-mode/keys. С Debian REST работает после обеих перезагрузок; прямой REST с рабочего компьютера возвращал 401, SSH с прежним паролем сначала прошёл, повторный вход не прошёл. Причина расхождения неизвестна; глобальную исправность SSH/пароля не заявляем. Контрольный CHR 194.226.121.150 доступен.
|
||||
|
||||
Следующий блок 248: понятная и безопасная обработка удаления работающего контейнера, приёмка удаления остановленного через UI и повторного действия, затем диагностика запуска E2E и корреляция авторизации без изменения паролей. RBAC/повторное действие в live 247 не проверены. ACME, внешняя публикация релизов и другие ранее записанные ограничения остаются открытыми. Полная регрессия не повторялась.
|
||||
|
||||
## 12.5.246 — пакет контейнеров из указанного пользователем источника (2026-10-14)
|
||||
|
||||
User отклонил official244 с причиной«нужно использовать пакеты с сайтаmikrotik.ltd». Перезагрузки/installation не выполнялись. Прочитан [сайт](https://mikrotik.ltd/) и его ссылка [MikroTikPatch](https://github.com/elseif/MikroTikPatch); inline download template указывает GitHub releases. Зафиксирован [точный7.24.5/x86 archive](https://github.com/elseif/MikroTikPatch/releases/download/7.24.5/all_packages-x86-7.24.5.zip), size29,675,363 иSHA256b293b380e680aece7fd0895889fd77b01133ea5ed6d89b32fb6d53af1936acc9 совпали сGitHub API asset digest/size. Извлечён толькоcontainer-7.24.5.npk1,163,409bytes/SHA25670f1c5281a3d37b347e9a83c81d9daaac511357fe26cfbac11321e2dc1407259. Он отличаетсяотофициального244; подменаартефактаоднимfilenameнепринимается.
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
# Оставшаяся внешняя приёмка — 2026-10-14
|
||||
|
||||
Актуальное продолжение 2026-10-14:246 — по user correction подготовлен container7.24.5 сmikrotik.ltd→MikroTikPatch release:archive29,675,363bytes/digest matched, selected NPK1,163,409bytes/SHA70f1c528…, official244 rejected/no apply. Main/option/python/keygen scripts не выбраны. Procedure обновлена; установка/reboot ждут согласия на новый комплект. CI245 passed; runtime ux-geoip242/ux-backups237 unchanged. Следующий247 — positive container lifecycle после согласия; до него независимый аудит границ текущего deployment. Этап12.5 открыт.
|
||||
Актуальное продолжение 2026-10-14: 247 — разрешённый container-пакет из источника mikrotik.ltd установлен и удалён на 104.165.244.7 с двумя перезагрузками. Исправлена обработка native running/stopped flags. Через реальный Portal/UI прошли start, stop, повторный start и restart; remove работающего контейнера вернул 502 и остаётся открытым. Собственные объекты удалены через native API, исходный экспорт и пакеты совпали. Backend ux-containers247, frontend ux-backups237. Следующий 248 — исправление и приёмка удаления контейнера; отдельно выяснить расхождение авторизации REST/SSH. Этап 12.5 открыт.
|
||||
|
||||
Это актуальная матрица после 12.5.246. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
|
||||
Это актуальная матрица после 12.5.247. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
|
||||
|
||||
Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-geoip242`, frontend `ux-backups237`; 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 `ux-containers247`, frontend `ux-backups237`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
|
||||
|
||||
## 12.5.247 — фактические состояния и жизненный цикл контейнера (2026-10-14)
|
||||
|
||||
Пользователь разрешил конкретный пакет 246 и две перезагрузки. На 104.165.244.7 установлен только container-7.24.5.npk из указанного источника mikrotik.ltd → MikroTikPatch, с ранее проверенным SHA256. Core/option/python/keygen/patch scripts не применялись; версия RouterOS осталась 7.24.5. Образ BusyBox 1.37.0 linux/amd64 закреплён digest busybox@sha256:bdf57e528e45e4433820e045b29b45978255a1c9e38353532d90a01445013f82e. Созданы только собственные контейнер mmc247-busybox, veth mmc247-39da5c21 и каталог; bridge, IP, NAT, маршруты и глобальные registry credentials не изменялись.
|
||||
|
||||
Реальный REST отдаёт stopped/running флаги вместо status и envlists вместо envlist. Общая обработка теперь поддерживает эти поля, сохраняет unknown при переходе, неверном значении или противоречии; отсутствие статуса не считается stopped. Для envlists проверена backend-фикстура, отдельное live создание envlist не выполнялось. test:containers (service + HTTP), сборка contracts/backend и профильный ESLint прошли. Добавлены девять проверок native флагов и проверка alias. E2E containers-data/containers-operations не стартовали: тестовый webServer :3100 превысил 120 секунд; успешной E2E приёмки этот блок не заявляет.
|
||||
|
||||
Через настоящий Portal без моков в UI 1440×1000 получены start 200 → stop 200 → start 200 → restart 200 с ok=true и проверкой rosId. Native состояния независимо подтвердили running → stopped → running. Удаление работающего контейнера через UI вернуло 502/ok=false; причина не доказана, сценарий НЕ принят. Ошибок JavaScript нет. [UI-протокол](ux-ui/acceptance/containers247-live-ui.json).
|
||||
|
||||
Очистка выполнена native API: собственный контейнер остановлен и удалён, veth/image/root/NPK отсутствуют. Container package удалён второй однократной перезагрузкой; оба reboot получили подтверждение. Доступные, но не установленные пакеты RouterOS имеют пустую version — при сравнении учитываются только установленные. После reboot автоматическое определение часового пояса временно отличалось от исходного, затем вернулось к Europe/Warsaw. Попытка PATCH singleton clock получила 400 и ничего не изменила; manual clock restore не применён. Итоговый нормализованный export и исходные installed routeros/option 7.24.5 совпали. Приватный baseline удалён. [Native-проверка и очистка](ux-ui/acceptance/containers247-native.json).
|
||||
|
||||
Backend ux-containers247 развёрнут и healthy; frontend ux-backups237 сохранён. При сборке совпали 521 исходный файл и 1076 compiled файлов; последующие исправления только CLI helper для cleanup не входят в замороженный source audit. [Deployment](ux-ui/acceptance/containers247-deployment.json). Агент не менял passwords/device-mode/keys. С Debian REST работает после обеих перезагрузок; прямой REST с рабочего компьютера возвращал 401, SSH с прежним паролем сначала прошёл, повторный вход не прошёл. Причина расхождения неизвестна; глобальную исправность SSH/пароля не заявляем. Контрольный CHR 194.226.121.150 доступен.
|
||||
|
||||
Следующий блок 248: понятная и безопасная обработка удаления работающего контейнера, приёмка удаления остановленного через UI и повторного действия, затем диагностика запуска E2E и корреляция авторизации без изменения паролей. RBAC/повторное действие в live 247 не проверены. ACME, внешняя публикация релизов и другие ранее записанные ограничения остаются открытыми. Полная регрессия не повторялась.
|
||||
|
||||
## 12.5.246 — пакет контейнеров из указанного пользователем источника (2026-10-14)
|
||||
|
||||
@@ -168,7 +182,7 @@ Portal offline JWT действует доexp после logout/role change; э
|
||||
|
||||
## Следующая последовательность — 2026-10-14
|
||||
|
||||
1. **Контейнеры247:** requested-source246artifact/digest и процедураготовы; применение/перезапуски нового комплекта ещё ждутсогласия. CI245 принят; ACME и remote Gitea publishing требуютсвоихдоступов/разрешённогоscope.
|
||||
1. **Контейнеры248:** start/stop/restart подтверждены в 247; remove работающего контейнера вернул 502 и требует исправления/приёмки UI. Очистка и исходная конфигурация подтверждены. Отдельно выяснить REST/SSH авторизацию и запуск E2E. CI245 принят; ACME и remote Gitea publishing требуют своих доступов/разрешённого scope.
|
||||
2. **Профильный контроль:** для новых изменений выполнять затронутые SQL/API/UI проверки. Общая регрессия192 выполнена; не повторять её без изменений общей основы или нового основания.
|
||||
3. **Внешние зависимости:** Portal issuer/login/revoke, Telegram bot/chat/разрешённое сообщение, клиентские ОС, независимые WAN/public IPv6 и scale/multi-instance — отдельные группы по таблице выше. Ограничения текущего стенда не превращать в обещание приёмки всех конфигураций.
|
||||
|
||||
|
||||
+15
-1
@@ -1,6 +1,6 @@
|
||||
# Этап 12: итоговая приёмка UX/UI
|
||||
|
||||
Актуальное продолжение 2026-10-14:246 — по user correction подготовлен container7.24.5 сmikrotik.ltd→MikroTikPatch release:archive29,675,363bytes/digest matched, selected NPK1,163,409bytes/SHA70f1c528…, official244 rejected/no apply. Main/option/python/keygen scripts не выбраны. Procedure обновлена; установка/reboot ждут согласия на новый комплект. CI245 passed; runtime ux-geoip242/ux-backups237 unchanged. Следующий247 — positive container lifecycle после согласия; до него независимый аудит границ текущего deployment. Этап12.5 открыт.
|
||||
Актуальное продолжение 2026-10-14: 247 — разрешённый container-пакет из источника mikrotik.ltd установлен и удалён на 104.165.244.7 с двумя перезагрузками. Исправлена обработка native running/stopped flags. Через реальный Portal/UI прошли start, stop, повторный start и restart; remove работающего контейнера вернул 502 и остаётся открытым. Собственные объекты удалены через native API, исходный экспорт и пакеты совпали. Backend ux-containers247, frontend ux-backups237. Следующий 248 — исправление и приёмка удаления контейнера; отдельно выяснить расхождение авторизации REST/SSH. Этап 12.5 открыт.
|
||||
|
||||
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
|
||||
|
||||
@@ -4152,6 +4152,20 @@ 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.247 — фактические состояния и жизненный цикл контейнера (2026-10-14)
|
||||
|
||||
Пользователь разрешил конкретный пакет 246 и две перезагрузки. На 104.165.244.7 установлен только container-7.24.5.npk из указанного источника mikrotik.ltd → MikroTikPatch, с ранее проверенным SHA256. Core/option/python/keygen/patch scripts не применялись; версия RouterOS осталась 7.24.5. Образ BusyBox 1.37.0 linux/amd64 закреплён digest busybox@sha256:bdf57e528e45e4433820e045b29b45978255a1c9e38353532d90a01445013f82e. Созданы только собственные контейнер mmc247-busybox, veth mmc247-39da5c21 и каталог; bridge, IP, NAT, маршруты и глобальные registry credentials не изменялись.
|
||||
|
||||
Реальный REST отдаёт stopped/running флаги вместо status и envlists вместо envlist. Общая обработка теперь поддерживает эти поля, сохраняет unknown при переходе, неверном значении или противоречии; отсутствие статуса не считается stopped. Для envlists проверена backend-фикстура, отдельное live создание envlist не выполнялось. test:containers (service + HTTP), сборка contracts/backend и профильный ESLint прошли. Добавлены девять проверок native флагов и проверка alias. E2E containers-data/containers-operations не стартовали: тестовый webServer :3100 превысил 120 секунд; успешной E2E приёмки этот блок не заявляет.
|
||||
|
||||
Через настоящий Portal без моков в UI 1440×1000 получены start 200 → stop 200 → start 200 → restart 200 с ok=true и проверкой rosId. Native состояния независимо подтвердили running → stopped → running. Удаление работающего контейнера через UI вернуло 502/ok=false; причина не доказана, сценарий НЕ принят. Ошибок JavaScript нет. [UI-протокол](ux-ui/acceptance/containers247-live-ui.json).
|
||||
|
||||
Очистка выполнена native API: собственный контейнер остановлен и удалён, veth/image/root/NPK отсутствуют. Container package удалён второй однократной перезагрузкой; оба reboot получили подтверждение. Доступные, но не установленные пакеты RouterOS имеют пустую version — при сравнении учитываются только установленные. После reboot автоматическое определение часового пояса временно отличалось от исходного, затем вернулось к Europe/Warsaw. Попытка PATCH singleton clock получила 400 и ничего не изменила; manual clock restore не применён. Итоговый нормализованный export и исходные installed routeros/option 7.24.5 совпали. Приватный baseline удалён. [Native-проверка и очистка](ux-ui/acceptance/containers247-native.json).
|
||||
|
||||
Backend ux-containers247 развёрнут и healthy; frontend ux-backups237 сохранён. При сборке совпали 521 исходный файл и 1076 compiled файлов; последующие исправления только CLI helper для cleanup не входят в замороженный source audit. [Deployment](ux-ui/acceptance/containers247-deployment.json). Агент не менял passwords/device-mode/keys. С Debian REST работает после обеих перезагрузок; прямой REST с рабочего компьютера возвращал 401, SSH с прежним паролем сначала прошёл, повторный вход не прошёл. Причина расхождения неизвестна; глобальную исправность SSH/пароля не заявляем. Контрольный CHR 194.226.121.150 доступен.
|
||||
|
||||
Следующий блок 248: понятная и безопасная обработка удаления работающего контейнера, приёмка удаления остановленного через UI и повторного действия, затем диагностика запуска E2E и корреляция авторизации без изменения паролей. RBAC/повторное действие в live 247 не проверены. ACME, внешняя публикация релизов и другие ранее записанные ограничения остаются открытыми. Полная регрессия не повторялась.
|
||||
|
||||
## 12.5.246 — пакет контейнеров из указанного пользователем источника (2026-10-14)
|
||||
|
||||
User отклонил official244 с причиной«нужно использовать пакеты с сайтаmikrotik.ltd». Перезагрузки/installation не выполнялись. Прочитан [сайт](https://mikrotik.ltd/) и его ссылка [MikroTikPatch](https://github.com/elseif/MikroTikPatch); inline download template указывает GitHub releases. Зафиксирован [точный7.24.5/x86 archive](https://github.com/elseif/MikroTikPatch/releases/download/7.24.5/all_packages-x86-7.24.5.zip), size29,675,363 иSHA256b293b380e680aece7fd0895889fd77b01133ea5ed6d89b32fb6d53af1936acc9 совпали сGitHub API asset digest/size. Извлечён толькоcontainer-7.24.5.npk1,163,409bytes/SHA25670f1c5281a3d37b347e9a83c81d9daaac511357fe26cfbac11321e2dc1407259. Он отличаетсяотофициального244; подменаартефактаоднимfilenameнепринимается.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Тестовый сервер UX/UI — 2026-10-10
|
||||
|
||||
Актуальное продолжение 2026-10-14:246 — по user correction подготовлен container7.24.5 сmikrotik.ltd→MikroTikPatch release:archive29,675,363bytes/digest matched, selected NPK1,163,409bytes/SHA70f1c528…, official244 rejected/no apply. Main/option/python/keygen scripts не выбраны. Procedure обновлена; установка/reboot ждут согласия на новый комплект. CI245 passed; runtime ux-geoip242/ux-backups237 unchanged. Следующий247 — positive container lifecycle после согласия; до него независимый аудит границ текущего deployment. Этап12.5 открыт.
|
||||
Актуальное продолжение 2026-10-14: 247 — разрешённый container-пакет из источника mikrotik.ltd установлен и удалён на 104.165.244.7 с двумя перезагрузками. Исправлена обработка native running/stopped flags. Через реальный Portal/UI прошли start, stop, повторный start и restart; remove работающего контейнера вернул 502 и остаётся открытым. Собственные объекты удалены через native API, исходный экспорт и пакеты совпали. Backend ux-containers247, frontend ux-backups237. Следующий 248 — исправление и приёмка удаления контейнера; отдельно выяснить расхождение авторизации REST/SSH. Этап 12.5 открыт.
|
||||
|
||||
| Параметр | Проверенное состояние |
|
||||
| --- | --- |
|
||||
@@ -34,6 +34,20 @@ 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.247 — фактические состояния и жизненный цикл контейнера (2026-10-14)
|
||||
|
||||
Пользователь разрешил конкретный пакет 246 и две перезагрузки. На 104.165.244.7 установлен только container-7.24.5.npk из указанного источника mikrotik.ltd → MikroTikPatch, с ранее проверенным SHA256. Core/option/python/keygen/patch scripts не применялись; версия RouterOS осталась 7.24.5. Образ BusyBox 1.37.0 linux/amd64 закреплён digest busybox@sha256:bdf57e528e45e4433820e045b29b45978255a1c9e38353532d90a01445013f82e. Созданы только собственные контейнер mmc247-busybox, veth mmc247-39da5c21 и каталог; bridge, IP, NAT, маршруты и глобальные registry credentials не изменялись.
|
||||
|
||||
Реальный REST отдаёт stopped/running флаги вместо status и envlists вместо envlist. Общая обработка теперь поддерживает эти поля, сохраняет unknown при переходе, неверном значении или противоречии; отсутствие статуса не считается stopped. Для envlists проверена backend-фикстура, отдельное live создание envlist не выполнялось. test:containers (service + HTTP), сборка contracts/backend и профильный ESLint прошли. Добавлены девять проверок native флагов и проверка alias. E2E containers-data/containers-operations не стартовали: тестовый webServer :3100 превысил 120 секунд; успешной E2E приёмки этот блок не заявляет.
|
||||
|
||||
Через настоящий Portal без моков в UI 1440×1000 получены start 200 → stop 200 → start 200 → restart 200 с ok=true и проверкой rosId. Native состояния независимо подтвердили running → stopped → running. Удаление работающего контейнера через UI вернуло 502/ok=false; причина не доказана, сценарий НЕ принят. Ошибок JavaScript нет. [UI-протокол](ux-ui/acceptance/containers247-live-ui.json).
|
||||
|
||||
Очистка выполнена native API: собственный контейнер остановлен и удалён, veth/image/root/NPK отсутствуют. Container package удалён второй однократной перезагрузкой; оба reboot получили подтверждение. Доступные, но не установленные пакеты RouterOS имеют пустую version — при сравнении учитываются только установленные. После reboot автоматическое определение часового пояса временно отличалось от исходного, затем вернулось к Europe/Warsaw. Попытка PATCH singleton clock получила 400 и ничего не изменила; manual clock restore не применён. Итоговый нормализованный export и исходные installed routeros/option 7.24.5 совпали. Приватный baseline удалён. [Native-проверка и очистка](ux-ui/acceptance/containers247-native.json).
|
||||
|
||||
Backend ux-containers247 развёрнут и healthy; frontend ux-backups237 сохранён. При сборке совпали 521 исходный файл и 1076 compiled файлов; последующие исправления только CLI helper для cleanup не входят в замороженный source audit. [Deployment](ux-ui/acceptance/containers247-deployment.json). Агент не менял passwords/device-mode/keys. С Debian REST работает после обеих перезагрузок; прямой REST с рабочего компьютера возвращал 401, SSH с прежним паролем сначала прошёл, повторный вход не прошёл. Причина расхождения неизвестна; глобальную исправность SSH/пароля не заявляем. Контрольный CHR 194.226.121.150 доступен.
|
||||
|
||||
Следующий блок 248: понятная и безопасная обработка удаления работающего контейнера, приёмка удаления остановленного через UI и повторного действия, затем диагностика запуска E2E и корреляция авторизации без изменения паролей. RBAC/повторное действие в live 247 не проверены. ACME, внешняя публикация релизов и другие ранее записанные ограничения остаются открытыми. Полная регрессия не повторялась.
|
||||
|
||||
## 12.5.246 — пакет контейнеров из указанного пользователем источника (2026-10-14)
|
||||
|
||||
User отклонил official244 с причиной«нужно использовать пакеты с сайтаmikrotik.ltd». Перезагрузки/installation не выполнялись. Прочитан [сайт](https://mikrotik.ltd/) и его ссылка [MikroTikPatch](https://github.com/elseif/MikroTikPatch); inline download template указывает GitHub releases. Зафиксирован [точный7.24.5/x86 archive](https://github.com/elseif/MikroTikPatch/releases/download/7.24.5/all_packages-x86-7.24.5.zip), size29,675,363 иSHA256b293b380e680aece7fd0895889fd77b01133ea5ed6d89b32fb6d53af1936acc9 совпали сGitHub API asset digest/size. Извлечён толькоcontainer-7.24.5.npk1,163,409bytes/SHA25670f1c5281a3d37b347e9a83c81d9daaac511357fe26cfbac11321e2dc1407259. Он отличаетсяотофициального244; подменаартефактаоднимfilenameнепринимается.
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"date": "2026-10-14",
|
||||
"block": "12.5.247",
|
||||
"containers": [
|
||||
{
|
||||
"name": "mmapp-test-backend",
|
||||
"image": "mmapp-test/backend:ux-containers247",
|
||||
"running": true,
|
||||
"health": "healthy"
|
||||
},
|
||||
{
|
||||
"name": "mmapp-test-frontend",
|
||||
"image": "mmapp-test/frontend:ux-backups237",
|
||||
"running": true,
|
||||
"health": "not-configured"
|
||||
}
|
||||
],
|
||||
"sourceAudit": {
|
||||
"matched": 521,
|
||||
"missing": 0,
|
||||
"different": 0,
|
||||
"scope": "frozen backend build inputs; later CLI-only cleanup corrections excluded"
|
||||
},
|
||||
"compiledAudit": {
|
||||
"matched": 1076,
|
||||
"missing": 0,
|
||||
"different": 0
|
||||
},
|
||||
"frontendChanged": false
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"date": "2026-10-14",
|
||||
"block": "12.5.247",
|
||||
"realPortalLogin": true,
|
||||
"noMocks": true,
|
||||
"steps": [
|
||||
{
|
||||
"phase": "start",
|
||||
"status": 200,
|
||||
"receiptOk": true,
|
||||
"targetMatched": true
|
||||
},
|
||||
{
|
||||
"phase": "stop",
|
||||
"status": 200,
|
||||
"receiptOk": true,
|
||||
"targetMatched": true
|
||||
},
|
||||
{
|
||||
"phase": "start",
|
||||
"status": 200,
|
||||
"receiptOk": true,
|
||||
"targetMatched": true
|
||||
},
|
||||
{
|
||||
"phase": "restart",
|
||||
"status": 200,
|
||||
"receiptOk": true,
|
||||
"targetMatched": true
|
||||
},
|
||||
{
|
||||
"phase": "remove",
|
||||
"status": 502,
|
||||
"receiptOk": false,
|
||||
"targetMatched": true
|
||||
}
|
||||
],
|
||||
"pageErrors": [],
|
||||
"failure": {
|
||||
"phase": "remove",
|
||||
"name": "AssertionError"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"date": "2026-10-14",
|
||||
"block": "12.5.247",
|
||||
"targetHost": "104.165.244.7",
|
||||
"controlHost": "194.226.121.150",
|
||||
"stages": [
|
||||
{
|
||||
"phase": "installed",
|
||||
"packageInstalled": true,
|
||||
"version": "7.24.5",
|
||||
"controlReachable": true
|
||||
},
|
||||
{
|
||||
"phase": "create",
|
||||
"initialStatus": "stopped",
|
||||
"observedNativeStoppedFlag": "true"
|
||||
},
|
||||
{
|
||||
"phase": "running",
|
||||
"nativeConfirmed": true
|
||||
},
|
||||
{
|
||||
"phase": "stopped",
|
||||
"nativeConfirmed": true
|
||||
},
|
||||
{
|
||||
"phase": "running",
|
||||
"nativeConfirmed": true
|
||||
},
|
||||
{
|
||||
"phase": "cleanup-network",
|
||||
"ownObjectsAbsent": true
|
||||
}
|
||||
],
|
||||
"installReboot": {
|
||||
"requested": true,
|
||||
"acknowledgement": "confirmed"
|
||||
},
|
||||
"uninstallReboot": {
|
||||
"requested": true,
|
||||
"acknowledgement": "confirmed"
|
||||
},
|
||||
"originalPackagesMatched": true,
|
||||
"originalNormalizedExportSha256": "3d7d864c2e8d9e7c2bba3ef59aa64b7d05e2a660d4d307172c36c555184b6066",
|
||||
"originalNormalizedExportMatched": true,
|
||||
"controlReachable": true,
|
||||
"passwordChanges": 0,
|
||||
"deviceModeChanges": 0,
|
||||
"firmwareVersionChanged": false,
|
||||
"passed": true
|
||||
}
|
||||
Reference in new issue
Block a user