From 2358ecef60cfd1b57fe2cd550c2186feb791de99 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Mon, 12 Oct 2026 21:35:55 +0700 Subject: [PATCH] =?UTF-8?q?fix(traffic):=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=8F=D1=82=D1=8C=20live-=D1=81=D1=80=D0=B5=D0=B7=20?= =?UTF-8?q?=D0=B8=20=D0=BF=D1=80=D0=B5=D0=BA=D1=80=D0=B0=D1=89=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BE=D0=BF=D1=80=D0=BE=D1=81=20=D0=B2=D1=8B=D0=BA?= =?UTF-8?q?=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D1=83=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=B9=D1=81=D1=82=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(main)/traffic/page.tsx | 16 +- backend/package.json | 2 +- backend/src/routes/traffic.ts | 77 +- backend/src/services/traffic-collector.ts | 12 +- backend/src/services/traffic-live-sql.test.ts | 56 + backend/src/services/traffic-live.test.ts | 37 + backend/src/services/traffic-live.ts | 73 ++ backend/src/services/traffic-rate.ts | 36 +- components/traffic/traffic-data-time.tsx | 5 +- docs/ux-ui-external-acceptance.md | 27 +- docs/ux-ui-plan.md | 6 +- docs/ux-ui-progress.md | 25 +- docs/ux-ui-remaining-acceptance.md | 29 +- docs/ux-ui-stage-12.md | 25 +- docs/ux-ui-test-server.md | 27 +- .../traffic-live-compiled-2026-10-12.json | 1046 +++++++++++++++++ .../traffic-live-deployed-2026-10-12.json | 46 + .../traffic-live-interop-2026-10-12.json | 10 + .../traffic-live-native-2026-10-12.json | 32 + .../traffic-live-runtime-2026-10-12.json | 25 + .../traffic-live-source-2026-10-12.json | 10 + .../traffic-live-sql-2026-10-12.json | 29 + .../traffic-live-error-1440-2026-10-12.png | Bin 0 -> 141198 bytes .../traffic-live-error-320-2026-10-12.png | Bin 0 -> 52235 bytes hooks/use-traffic-live.ts | 34 +- lib/fmt-rate.test.ts | 6 + lib/fmt-rate.ts | 1 + tests/e2e/traffic-live-confidence.spec.ts | 40 + tests/e2e/traffic-live.spec.ts | 2 +- 29 files changed, 1620 insertions(+), 114 deletions(-) create mode 100644 backend/src/services/traffic-live-sql.test.ts create mode 100644 backend/src/services/traffic-live.test.ts create mode 100644 backend/src/services/traffic-live.ts create mode 100644 docs/ux-ui/acceptance/traffic-live-compiled-2026-10-12.json create mode 100644 docs/ux-ui/acceptance/traffic-live-deployed-2026-10-12.json create mode 100644 docs/ux-ui/acceptance/traffic-live-interop-2026-10-12.json create mode 100644 docs/ux-ui/acceptance/traffic-live-native-2026-10-12.json create mode 100644 docs/ux-ui/acceptance/traffic-live-runtime-2026-10-12.json create mode 100644 docs/ux-ui/acceptance/traffic-live-source-2026-10-12.json create mode 100644 docs/ux-ui/acceptance/traffic-live-sql-2026-10-12.json create mode 100644 docs/ux-ui/screens/traffic-live-error-1440-2026-10-12.png create mode 100644 docs/ux-ui/screens/traffic-live-error-320-2026-10-12.png create mode 100644 lib/fmt-rate.test.ts create mode 100644 tests/e2e/traffic-live-confidence.spec.ts diff --git a/app/(main)/traffic/page.tsx b/app/(main)/traffic/page.tsx index d5c6cebb..83393ee1 100644 --- a/app/(main)/traffic/page.tsx +++ b/app/(main)/traffic/page.tsx @@ -793,6 +793,7 @@ export default function TrafficPage() { const [range, setRange] = useState("1h") const [search, setSearch] = useState("") const [trafficRetry, setTrafficRetry] = useState(0) + const [liveRetry, setLiveRetry] = useState(0) const trafficKey = JSON.stringify([isLive, backendUrl, range, groupMode, trafficRetry]) const [trafficState, setTrafficState] = useState<{ key: string; status: "ready" | "error"; receivedAt?: number; error?: string } | null>(null) const trafficReady = trafficState?.key === trafficKey && trafficState.status === "ready" @@ -843,6 +844,7 @@ export default function TrafficPage() { backendUrl, serverId: selectedId, iface: selectedIface, + reloadKey: liveRetry, }) const confirmedFlowSelection = isLive && Boolean(currentFlows) && !liveError && ( range === "30d" @@ -1472,7 +1474,14 @@ export default function TrafficPage() { } {isLive && effectiveMode === "servers" && detailServer && <> - + + {liveStreamError && + {liveStreamError === "Ожидание нового live-измерения" ? "Подключение live-потока" : "Live-поток: нет свежих данных"} + {liveStreamError} + + + } } {effectiveMode === "servers" && detailServer && ( )} diff --git a/backend/package.json b/backend/package.json index 8f50dff9..064fa5a0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -19,7 +19,7 @@ "test:exec": "tsx src/services/mikrotik-logs.test.ts && tsx src/routes/exec.test.ts && tsx src/plugins/auth.exec.test.ts", "test:auth": "tsx src/lib/permissions.test.ts && tsx src/plugins/auth.smoke.test.ts && tsx src/plugins/auth.ospf-optimizer.test.ts", "test:wireguard": "npx tsx src/services/wireguard-config.test.ts && npx tsx src/services/wireguard-ipv6.test.ts && npx tsx --test src/services/wireguard-import.test.ts src/services/wireguard-import-apply.test.ts src/services/wireguard-import-revision.test.ts src/services/wireguard-delete.test.ts && npx tsx src/routes/wireguard-import.test.ts", - "test:traffic-rate": "tsx src/services/traffic-rate.test.ts && tsx src/services/traffic-observation.test.ts && tsx src/services/traffic-freshness.test.ts && tsx src/services/traffic-observation-sql.test.ts && tsx src/services/traffic-history-sql.test.ts", + "test:traffic-rate": "tsx src/services/traffic-rate.test.ts && tsx src/services/traffic-observation.test.ts && tsx src/services/traffic-freshness.test.ts && tsx src/services/traffic-live.test.ts && tsx src/services/traffic-observation-sql.test.ts && tsx src/services/traffic-history-sql.test.ts && tsx src/services/traffic-live-sql.test.ts", "test:traffic-flow": "tsx src/routes/traffic-flow-query.test.ts && tsx src/services/traffic-flow-month.test.ts && tsx src/services/traffic-flow-parse.test.ts && tsx src/services/traffic-flow-ipv6-pipeline.test.ts && tsx src/services/traffic-flow-map-exporter.test.ts && tsx src/services/traffic-flow-ifaces.test.ts && tsx src/services/traffic-flow-ifindex.test.ts && tsx src/services/traffic-flow-dedup.test.ts && tsx src/services/traffic-flow-planes.test.ts && tsx src/services/traffic-flow-ip.test.ts && tsx src/services/traffic-flow-dest.test.ts && tsx src/services/traffic-flow-classify.test.ts && tsx src/services/traffic-flow-ripe.test.ts && tsx src/services/traffic-flow-brands.test.ts && tsx src/services/traffic-flow-ingest.test.ts && tsx src/services/traffic-flow-analytics.test.ts && tsx src/services/traffic-flow-map-hops.test.ts && tsx src/services/traffic-flow-purge.test.ts && tsx src/services/traffic-flow-geoip.test.ts && tsx src/services/traffic-flow-facts.test.ts && tsx src/services/traffic-flow-facts-filter.test.ts && tsx src/services/traffic-flow-facts-rebuild.test.ts && tsx src/services/statistics-aggregate.test.ts", "test:users": "tsx src/modules/users/iface-type.test.ts && tsx src/modules/users/bindings.test.ts && tsx src/modules/users/interface-inventory.test.ts && tsx src/routes/users-catalog.test.ts", "test:pg": "tsx src/db/pg-pool.test.ts && tsx src/db/sql-bind.test.ts && tsx src/db/sqlite-json.test.ts && tsx src/db/traffic-flags.test.ts && tsx src/db/pg-schema.test.ts && tsx src/services/config-revisions.test.ts", diff --git a/backend/src/routes/traffic.ts b/backend/src/routes/traffic.ts index 72ed2a74..f58d8787 100644 --- a/backend/src/routes/traffic.ts +++ b/backend/src/routes/traffic.ts @@ -12,7 +12,8 @@ import { } from "../services/traffic-collector.js" import { refreshScheduler, runSchedulerJobNow } from "../services/scheduler.js" import { appendEvent } from "../modules/events/service/events-service.js" -import { MikrotikClient } from "../services/mikrotik.js" +import { readTrafficLiveSample, TrafficLiveSourceChanged } from "../services/traffic-live.js" +import { MikrotikClient, MikrotikError } from "../services/mikrotik.js" import { getEnabledServerById } from "../services/wireguard-live.js" import { readTrafficCollectionContext } from "../services/traffic-collection-state.js" import { applyTrafficFreshness } from "../services/traffic-freshness.js" @@ -20,7 +21,6 @@ import { buildTrafficFromSamples, type TrafficObservations, isLoopbackName, - parseMonitorTraffic, } from "../services/traffic-rate.js" import { buildBoundInterfaceTraffic, @@ -54,7 +54,6 @@ interface TrafficInterfaceDto { } const LIVE_TICK_MS = 1500 -const LIVE_ROS_TIMEOUT_MS = 4000 async function latestSnapshot(serverId: number) { return (await db @@ -141,68 +140,6 @@ function sleep(ms: number, signal: AbortSignal): Promise { }) } -function flattenMonitor(raw: unknown): unknown[] { - if (Array.isArray(raw)) return raw - if (raw != null) return [raw] - return [] -} - -async function listRunningIfaceNames(client: MikrotikClient): Promise { - const ifaces = await client.get>( - "/interface", - LIVE_ROS_TIMEOUT_MS, - ) - return ifaces - .filter((i) => (i.running ?? "false") === "true" - && (i.disabled ?? "false") !== "true" - && !isLoopbackName(i.name ?? "")) - .map((i) => i.name ?? "") - .filter(Boolean) -} - -async function monitorTrafficOnce( - client: MikrotikClient, - onlyInterface: string | undefined, - cache: { names: string[]; joinedFailed: boolean }, - signal: AbortSignal, -): Promise { - if (onlyInterface) { - return client.post( - "/interface/monitor-traffic", - { interface: onlyInterface, once: "" }, - LIVE_ROS_TIMEOUT_MS, - signal, - ) - } - if (cache.names.length === 0) { - cache.names = await listRunningIfaceNames(client) - } - if (cache.names.length === 0) return [] - if (!cache.joinedFailed) { - try { - return await client.post( - "/interface/monitor-traffic", - { interface: cache.names.join(","), once: "" }, - LIVE_ROS_TIMEOUT_MS, - signal, - ) - } catch { - cache.joinedFailed = true - } - } - const chunks = await Promise.all( - cache.names.map((name) => - client.post( - "/interface/monitor-traffic", - { interface: name, once: "" }, - LIVE_ROS_TIMEOUT_MS, - signal, - ).then(flattenMonitor).catch(() => [] as unknown[]), - ), - ) - return chunks.flat() -} - const trafficRoutes: FastifyPluginAsyncZod = async (app) => { app.get("/traffic/settings", async (_req, reply) => { const settings = await getTrafficSettings() @@ -344,13 +281,17 @@ const trafficRoutes: FastifyPluginAsyncZod = async (app) => { try { while (!abort.signal.aborted) { try { - const raw = await monitorTrafficOnce(client, onlyInterface, cache, abort.signal) - const sample = parseMonitorTraffic(raw, { onlyInterface }) + const sample = await readTrafficLiveSample(client, onlyInterface, cache, abort.signal, async () => { + const current = await getEnabledServerById(server.id) + if (!current) throw new TrafficLiveSourceChanged("Устройство выключено или удалено. Live-поток остановлен") + if (Date.parse(current.updatedAt) !== Date.parse(server.updatedAt)) throw new TrafficLiveSourceChanged("Конфигурация устройства изменилась. Переподключите live-поток") + }) writeSse(reply.raw, "sample", sample) } catch (error) { if (abort.signal.aborted) break const msg = error instanceof Error ? error.message : String(error) - writeSse(reply.raw, "error", { error: msg }) + writeSse(reply.raw, "error", { error: msg.slice(0,1024) }) + if (error instanceof TrafficLiveSourceChanged || error instanceof MikrotikError && [401,403].includes(error.statusCode)) break } await sleep(LIVE_TICK_MS, abort.signal) } diff --git a/backend/src/services/traffic-collector.ts b/backend/src/services/traffic-collector.ts index 19af9f12..063685ae 100644 --- a/backend/src/services/traffic-collector.ts +++ b/backend/src/services/traffic-collector.ts @@ -157,8 +157,8 @@ export async function collectTrafficOnce(): Promise { sampledAt: now, rxBytes, txBytes, - rxBps, - txBps, + rxBps: Math.round(rxBps), + txBps: Math.round(txBps), flags: encodeTrafficFlags(running, disabled, true), } }) @@ -186,8 +186,8 @@ export async function collectTrafficOnce(): Promise { sampledAt: now, rxBytes, txBytes, - rxBps, - txBps, + rxBps: Math.round(rxBps), + txBps: Math.round(txBps), flags: encodeTrafficFlags(running, disabled, true), }) } @@ -205,8 +205,8 @@ export async function collectTrafficOnce(): Promise { ok: true, interfaces: ifaces.length, ...(includedInterfaces > 0 && totalsMeasured ? { - sumRxMbps: Math.round(sumRxMbps * 1000) / 1000, - sumTxMbps: Math.round(sumTxMbps * 1000) / 1000, + sumRxMbps, + sumTxMbps, } : {}), }) } catch (err) { diff --git a/backend/src/services/traffic-live-sql.test.ts b/backend/src/services/traffic-live-sql.test.ts new file mode 100644 index 00000000..722ea3d0 --- /dev/null +++ b/backend/src/services/traffic-live-sql.test.ts @@ -0,0 +1,56 @@ +import assert from "node:assert/strict" +import { createServer } from "node:http" +import Fastify from "fastify" +import { pool, closePool } from "../db/index.js" +import { initDatabase } from "../db/bootstrap.js" +import trafficRoutes from "../routes/traffic.js" + +const database=process.env.MMAPP_OWN_SQL_DB +if(!database) console.log("traffic live SQL: skipped; explicit owned database required") +else { + assert(/^mm_accept_166_\d+$/.test(database));assert.equal(new URL(process.env.DATABASE_URL!).pathname,"/"+database) + assert.equal((await pool.query("SELECT current_database() AS name")).rows[0].name,database) + const prefix="own-live179-"+Date.now()+"-" + let serverId:number|undefined,mode="zero",nativeRequests=0,cases=0,two=false + const point=(name:string)=>({name,"rx-bits-per-second":"0","tx-bits-per-second":"0"}) + const http=createServer(async(req,res)=>{ + nativeRequests++;res.setHeader("Content-Type","application/json") + if(req.method==="GET")return res.end(JSON.stringify((two?["ether1","ether2"]:["ether1"]).map(name=>({name,running:"true",disabled:"false"})))) + let data="";for await(const chunk of req)data+=chunk;const name=JSON.parse(data).interface + if(mode==="auth"||mode==="fallback-deny"&&name==="ether2"){res.statusCode=401;return res.end('{"message":"Unauthorized"}')} + if(mode.startsWith("fallback")&&name.includes(",")){res.statusCode=400;return res.end('{"message":"Unsupported joined interface"}')} + if(mode==="empty")return res.end("[]") + if(mode==="malformed")return res.end(JSON.stringify([{name,"rx-bits-per-second":"12bad","tx-bits-per-second":"0"}])) + if(mode==="partial")return res.end(JSON.stringify([point("ether1")])) + res.end(JSON.stringify(name.split(",").map(point))) + }) + await new Promise(resolve=>http.listen(0,"127.0.0.1",resolve));const address=http.address();assert(address&&typeof address==="object") + const app=Fastify();await app.register(trafficRoutes) + const streams=new Set() + async function open() { + const ac=new AbortController();streams.add(ac);const r=await fetch(app.listeningOrigin+`/traffic/servers/${serverId}/live`,{signal:ac.signal});assert.equal(r.status,200);assert(r.body) + const reader=r.body.getReader(),decoder=new TextDecoder();let buffer="" + return {ac, async next(){const timeout=setTimeout(()=>ac.abort(),8000);try{ + for(;;){const end=buffer.indexOf("\n\n");if(end>=0){const block=buffer.slice(0,end);buffer=buffer.slice(end+2);const event=block.match(/^event: (.*)$/m)?.[1],data=block.match(/^data: (.*)$/m)?.[1];if(event&&data)return{event,data:JSON.parse(data)};continue} + const chunk=await reader.read();if(chunk.done)return null;buffer+=decoder.decode(chunk.value,{stream:true})} + }finally{clearTimeout(timeout)}},close(){ac.abort();streams.delete(ac)}} + } + try { + await initDatabase();await app.listen({host:"127.0.0.1",port:0}) + serverId=Number((await pool.query("INSERT INTO servers(name,host,port,use_ssl,enabled,username,password,updated_at) VALUES($1,'127.0.0.1',$2,false,true,'fixture','fixture',$3) RETURNING id",[prefix+"server",address.port,new Date(Date.now()-3_600_000).toISOString()])).rows[0].id) + let stream=await open(),event=await stream.next();assert.equal(event?.event,"sample");assert.equal(event?.data.rxMbps,0);assert.equal(event?.data.timestampKind,"backend-receipt");stream.close();cases++ + for(const kind of ["empty","malformed","partial"]){mode=kind;two=kind==="partial";stream=await open();event=await stream.next();assert.equal(event?.event,"error");stream.close();cases++} + mode="fallback";two=true;stream=await open();event=await stream.next();assert.equal(event?.event,"sample");assert.equal(event?.data.rxMbps,0);stream.close();cases++ + mode="fallback-deny";stream=await open();event=await stream.next();assert.equal(event?.event,"error");assert.match(event?.data.error,/401/);assert.equal(await stream.next(),null);stream.close();cases++ + mode="auth";two=false;stream=await open();event=await stream.next();assert.equal(event?.event,"error");assert.equal(await stream.next(),null);stream.close();cases++ + mode="zero";stream=await open();await stream.next();const beforeDisable=nativeRequests + await pool.query("UPDATE servers SET enabled=false WHERE id=$1",[serverId]);event=await stream.next();assert.equal(event?.event,"error");assert.match(event?.data.error,/выключено/);assert.equal(await stream.next(),null);assert.equal(nativeRequests,beforeDisable);stream.close();cases++ + const beforeInitial=nativeRequests;const disabled=await app.inject({method:"GET",url:`/traffic/servers/${serverId}/live`});assert.equal(disabled.statusCode,404);assert.equal(nativeRequests,beforeInitial);cases++ + await pool.query("UPDATE servers SET enabled=true WHERE id=$1",[serverId]);stream=await open();await stream.next();const beforeConfig=nativeRequests + await pool.query("UPDATE servers SET updated_at=$1 WHERE id=$2",[new Date(Date.now()+1000).toISOString(),serverId]);event=await stream.next();assert.match(event?.data.error,/Конфигурация/);assert.equal(await stream.next(),null);assert.equal(nativeRequests,beforeConfig);stream.close();cases++ + console.log(`traffic live owned SQL: ${cases} cases; real TCP SSE/HTTP/zero/empty/malformed/partial/fallback/auth/disabled/config passed`) + }finally{ + for(const ac of streams)ac.abort();await app.close();await new Promise(resolve=>http.close(()=>resolve())) + if(serverId!==undefined)await pool.query("DELETE FROM servers WHERE id=$1",[serverId]);await closePool() + } +} diff --git a/backend/src/services/traffic-live.test.ts b/backend/src/services/traffic-live.test.ts new file mode 100644 index 00000000..2ff1e42e --- /dev/null +++ b/backend/src/services/traffic-live.test.ts @@ -0,0 +1,37 @@ +import assert from "node:assert/strict" +import { parseMonitorTraffic, rateBpsFromDelta, bpsToMbps } from "./traffic-rate.js" +import { readTrafficLiveSample, TrafficLiveSourceChanged } from "./traffic-live.js" +import { MikrotikError, type MikrotikClient } from "./mikrotik.js" +const point=(name="ether1",rx:unknown="0",tx:unknown="0")=>({name,"rx-bits-per-second":rx,"tx-bits-per-second":tx}) +for(const raw of [[],{},null,[null],[{...point(),"rx-bits-per-second":undefined}], [point("ether1","12bad")],[point("ether1",-1)], [point("ether1"),point("ether1")]]) { + assert.throws(()=>parseMonitorTraffic(raw)) +} +assert.throws(()=>parseMonitorTraffic([point()],{expectedInterfaces:["ether1","ether2"]})) +assert.throws(()=>parseMonitorTraffic([point("other")],{onlyInterface:"ether1"})) +assert.equal(parseMonitorTraffic([point()]).rxMbps,0) +assert.equal(parseMonitorTraffic([point("ether1","1")]).rxMbps,0.000001) +assert(rateBpsFromDelta(1000,1001,0,30_000)!>0);assert(bpsToMbps(rateBpsFromDelta(1000,1001,0,30_000)!)>0) +assert.equal(parseMonitorTraffic([point()]).timestampKind,"backend-receipt") +let mode="fallback",postNames:string[]=[],guards=0 +const client={get:async()=>[{name:"ether1",running:"true",disabled:"false"},{name:"ether2",running:true,disabled:false}], + post:async(_path:string,body:{interface:string})=>{postNames.push(body.interface);if(body.interface.includes(",")){ + if(mode==="auth")throw new MikrotikError(401,"/interface/monitor-traffic","Unauthorized") + if(mode==="partial")return[point()] + throw new MikrotikError(400,"/interface/monitor-traffic","Unsupported joined interface") + }if(mode==="deny"&&body.interface==="ether2")throw new MikrotikError(401,"/interface/monitor-traffic","Unauthorized");return[point(body.interface,"1")]}} +const cast=client as unknown as Pick +const run=()=>readTrafficLiveSample(cast,undefined,{names:[],joinedFailed:false},new AbortController().signal,async()=>{guards++}) +const complete=await run();assert.equal(complete.rxMbps,0.000002);assert(guards>=4) +mode="deny";await assert.rejects(run(),/401/) +mode="auth";postNames=[];await assert.rejects(run(),/401/);assert.equal(postNames.length,1) +mode="partial";postNames=[];await assert.rejects(run(),/все live/);assert.equal(postNames.length,1) +await assert.rejects(readTrafficLiveSample(cast,undefined,{names:[],joinedFailed:false},new AbortController().signal,async()=>{throw new TrafficLiveSourceChanged("disabled")}),/disabled/) +const abort=new AbortController();abort.abort();postNames=[];await assert.rejects(readTrafficLiveSample(cast,"ether1",{names:[],joinedFailed:false},abort.signal,async()=>{}));assert.equal(postNames.length,0) +let active=0,maximum=0 +const many={get:async()=>Array.from({length:10},(_,i)=>({name:`ether${i}`,running:true,disabled:false})),post:async(_path:string,body:{interface:string})=>{ + if(body.interface.includes(","))throw new MikrotikError(400,"/interface/monitor-traffic","Joined unsupported") + active++;maximum=Math.max(maximum,active);await new Promise(resolve=>setTimeout(resolve,5));active--;return[point(body.interface)] +}} as unknown as Pick +const bounded=await readTrafficLiveSample(many,undefined,{names:[],joinedFailed:false},new AbortController().signal,async()=>{}) +assert.equal(bounded.rxMbps,0);assert.equal(maximum,4) +console.log("traffic live completeness/fallback/auth/source/abort/positive-small-rate tests ok") diff --git a/backend/src/services/traffic-live.ts b/backend/src/services/traffic-live.ts new file mode 100644 index 00000000..1a7de242 --- /dev/null +++ b/backend/src/services/traffic-live.ts @@ -0,0 +1,73 @@ +import { MikrotikError, type MikrotikClient } from "./mikrotik.js" +import { isLoopbackName, parseMonitorTraffic, type MonitorLiveSample } from "./traffic-rate.js" + +const TIMEOUT_MS = 4000 +export class TrafficLiveSourceChanged extends Error {} +export interface TrafficLiveCache { names: string[]; joinedFailed: boolean } +type Client = Pick + +/** Never emit a partial sum, and check the source before every new native request. */ +export async function readTrafficLiveSample(client: Client, onlyInterface: string | undefined, + cache: TrafficLiveCache, signal: AbortSignal, assertSource: () => Promise): Promise { + const parentSignal = signal, wave = new AbortController() + const onAbort = () => wave.abort(parentSignal.reason) + parentSignal.addEventListener("abort", onAbort, { once: true }) + if (parentSignal.aborted) onAbort() + const timeout = setTimeout(() => wave.abort(new Error("Не успели получить полный live-срез за 8 секунд. Выберите отдельный интерфейс")), 8000) + signal = wave.signal + try { + const ensure = async () => { signal.throwIfAborted(); await assertSource(); signal.throwIfAborted() } + const monitor = async (name: string) => { + await ensure() + return client.post("/interface/monitor-traffic", { interface: name, once: "" }, TIMEOUT_MS, signal) + } + if (onlyInterface) { + const raw = await monitor(onlyInterface) + await ensure() + return parseMonitorTraffic(raw, { onlyInterface }) + } + await ensure() + const raw = await client.get("/interface", TIMEOUT_MS) + if (!Array.isArray(raw)) throw new Error("Не получен каталог интерфейсов live") + const names: string[] = [] + const flag = (value: unknown) => { + if (value === true || value === "true" || value === "yes") return true + if (value === false || value === "false" || value === "no") return false + throw new Error("Неизвестное состояние интерфейса live") + } + for (const row of raw) { + if (!row || typeof row !== "object" || Array.isArray(row) || typeof row.name !== "string" || !row.name.trim()) throw new Error("Некорректный каталог интерфейсов live") + if (flag(row.running) && !flag(row.disabled) && !isLoopbackName(row.name)) names.push(row.name) + } + if (!names.length || new Set(names).size !== names.length) throw new Error("Нет достоверного набора активных интерфейсов live") + if ([...names].sort().join("\0") !== [...cache.names].sort().join("\0")) cache.joinedFailed = false + cache.names = names + if (!cache.joinedFailed) { + try { + const result = await monitor(names.join(",")) + await ensure() + return parseMonitorTraffic(result, { expectedInterfaces: names }) + } catch (error) { + // Unsupported joined command may fall back; auth/transport/invalid payload cannot become partial zero. + if (!(error instanceof MikrotikError) || error.statusCode !== 400 || names.length < 2) throw error + cache.joinedFailed = true + } + } + const results: MonitorLiveSample[] = [] + for (let i = 0; i < names.length; i += 4) { + results.push(...await Promise.all(names.slice(i, i + 4).map(async name => + parseMonitorTraffic(await monitor(name), { onlyInterface: name })))) + } + await ensure() + const rxMbps = results.reduce((sum, r) => sum + r.rxMbps, 0), txMbps = results.reduce((sum, r) => sum + r.txMbps, 0) + if (![rxMbps, txMbps].every(n => Number.isFinite(n) && n <= Number.MAX_SAFE_INTEGER / 1_000_000)) throw new Error("Суммарная live-скорость вне допустимого диапазона") + return { rxMbps, txMbps, at: new Date().toISOString(), timestampKind: "backend-receipt" } + } catch (error) { + if (signal.aborted && !parentSignal.aborted && signal.reason instanceof Error) throw signal.reason + throw error + } finally { + clearTimeout(timeout) + parentSignal.removeEventListener("abort", onAbort) + wave.abort() + } +} diff --git a/backend/src/services/traffic-rate.ts b/backend/src/services/traffic-rate.ts index 1f9c2c58..6d9ecb14 100644 --- a/backend/src/services/traffic-rate.ts +++ b/backend/src/services/traffic-rate.ts @@ -42,6 +42,7 @@ export interface MonitorLiveSample { rxMbps: number txMbps: number at: string + timestampKind: "backend-receipt" } export function isLoopbackName(name: string): boolean { @@ -61,7 +62,7 @@ export function shouldIncludeIface( export function bpsToMbps(bps: number): number { if (!Number.isFinite(bps) || bps <= 0) return 0 - return Math.round((bps / 1_000_000) * 1000) / 1000 + return bps / 1_000_000 } /** bits/s из соседних счётчиков. null = нельзя (Δt≤0 или сброс). */ @@ -75,7 +76,7 @@ export function rateBpsFromDelta( const dtSec = (nextAtMs - prevAtMs) / 1000 if (!(dtSec > 0) || !Number.isFinite(dtSec)) return null if (nextBytes < prevBytes) return null - const rate=Math.round(((nextBytes - prevBytes) * 8) / dtSec) + const rate=((nextBytes - prevBytes) * 8) / dtSec return Number.isFinite(rate)?rate:null } @@ -274,26 +275,41 @@ export function mergeBuiltTraffic(parts: BuiltTrafficSeries[]): BuiltTrafficSeri export function parseMonitorTraffic( raw: unknown, - opts?: { onlyInterface?: string }, + opts?: { onlyInterface?: string; expectedInterfaces?: readonly string[] }, ): MonitorLiveSample { const items = Array.isArray(raw) ? raw : raw != null ? [raw] : [] let rxBps = 0 let txBps = 0 + const seen = new Set() + const expected = opts?.expectedInterfaces ? new Set(opts.expectedInterfaces) : null + const rate = (raw: unknown) => { + if (typeof raw !== "number" && (typeof raw !== "string" || !/^(?:\d+(?:\.\d+)?|\.\d+)$/.test(raw.trim()))) throw new Error("Некорректная скорость monitor-traffic") + const n = Number(raw) + if (!Number.isFinite(n) || n < 0 || n > Number.MAX_SAFE_INTEGER) throw new Error("Некорректная скорость monitor-traffic") + return n + } for (const item of items) { - if (!item || typeof item !== "object") continue + if (!item || typeof item !== "object" || Array.isArray(item)) throw new Error("Некорректный ответ monitor-traffic") const rec = item as Record - const name = String(rec.name ?? rec.interface ?? "") + const name = rec.name ?? rec.interface + if (typeof name !== "string" || !name.trim()) throw new Error("Не указан интерфейс monitor-traffic") if (opts?.onlyInterface) { - if (name && name !== opts.onlyInterface) continue + if (name !== opts.onlyInterface) throw new Error("Ответ monitor-traffic относится к другому интерфейсу") } else if (isLoopbackName(name)) { continue } - rxBps += Number.parseFloat(String(rec["rx-bits-per-second"] ?? 0)) || 0 - txBps += Number.parseFloat(String(rec["tx-bits-per-second"] ?? 0)) || 0 + if (seen.has(name) || expected && !expected.has(name)) throw new Error("Некорректный набор интерфейсов monitor-traffic") + seen.add(name) + rxBps += rate(rec["rx-bits-per-second"]) + txBps += rate(rec["tx-bits-per-second"]) } + if (!seen.size || expected && (expected.size !== opts!.expectedInterfaces!.length || + [...expected].some(name => !seen.has(name)))) throw new Error("Не получены все live-измерения интерфейсов") + if (![rxBps, txBps].every(n => Number.isFinite(n) && n <= Number.MAX_SAFE_INTEGER)) throw new Error("Суммарная live-скорость вне допустимого диапазона") return { - rxMbps: bpsToMbps(rxBps), - txMbps: bpsToMbps(txBps), + rxMbps: rxBps / 1_000_000, + txMbps: txBps / 1_000_000, at: new Date().toISOString(), + timestampKind: "backend-receipt", } } diff --git a/components/traffic/traffic-data-time.tsx b/components/traffic/traffic-data-time.tsx index cac4f39c..dacc6854 100644 --- a/components/traffic/traffic-data-time.tsx +++ b/components/traffic/traffic-data-time.tsx @@ -1,9 +1,10 @@ /** Receipt time is not the timestamp of a network measurement. */ -export function TrafficDataTime({ label, receivedAt, source, serverAt }: { +export function TrafficDataTime({ label, receivedAt, source, serverAt, serverTimeLabel = "метка сервера" }: { label: string receivedAt?: number | null source: "API" | "live-поток" serverAt?: string + serverTimeLabel?: string }) { if (receivedAt == null || !Number.isFinite(receivedAt)) return null const date = new Date(receivedAt) @@ -11,7 +12,7 @@ export function TrafficDataTime({ label, receivedAt, source, serverAt }: {

Получено ({source}): {serverAt && Number.isFinite(Date.parse(serverAt)) - ? <> · метка сервера: + ? <> · {serverTimeLabel}: : " · время измерения не предоставлено"}

) diff --git a/docs/ux-ui-external-acceptance.md b/docs/ux-ui-external-acceptance.md index bcfba366..6d6bdfd0 100644 --- a/docs/ux-ui-external-acceptance.md +++ b/docs/ux-ui-external-acceptance.md @@ -1,6 +1,6 @@ # UX/UI: протокол внешней приёмки 12.5 -Актуальное продолжение 2026-10-12: завершён12.5.178 — traffic history/current freshness и legacy counter provenance, manual truth/journal, masks/TTL UI серверов/клиентов/привязок. OwnSQL9+14/15709ms иsource26+GRE8/20075ms,35 unique E2E eventually passed,build/types/ESLint/1032compiled parity. Both ux-history178 healthy/running, migration0011 applied/2native CHR GET/live UI2 passed; ownDBs/roles/containers removed. Следующий179 — live monitor-traffic complete payload и disable-after-stream-start guard;12.5 открыта. +Актуальное продолжение 2026-10-12: завершён12.5.179 — live complete payload/expected interfaces/auth/disable-config guard; scoped retry/retain cause/latest sample, abort cleanup/receipt timestamp, positive tiny rates. OwnSQL9+14+10/24570ms,7E2E passed13.3s,types/build/ESLint/1036compiled parity. Both ux-live179 healthy/running,2 actual CHR SSE samples+live UI2 passed; ownDB/role/container removed. Следующий180 — точность и binary units учтённых объёмов: baseline positive1KiB/1MiB rounded0 и1GiB mislabeledGB подтверждён, ещё не исправлен.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). @@ -696,7 +696,7 @@ flowchart LR ## 12.5.77 — основной блок IPsec (2026-10-09) -Актуальное продолжение 2026-10-12: завершён12.5.178 — traffic history/current freshness и legacy counter provenance, manual truth/journal, masks/TTL UI серверов/клиентов/привязок. OwnSQL9+14/15709ms иsource26+GRE8/20075ms,35 unique E2E eventually passed,build/types/ESLint/1032compiled parity. Both ux-history178 healthy/running, migration0011 applied/2native CHR GET/live UI2 passed; ownDBs/roles/containers removed. Следующий179 — live monitor-traffic complete payload и disable-after-stream-start guard;12.5 открыта. +Актуальное продолжение 2026-10-12: завершён12.5.179 — live complete payload/expected interfaces/auth/disable-config guard; scoped retry/retain cause/latest sample, abort cleanup/receipt timestamp, positive tiny rates. OwnSQL9+14+10/24570ms,7E2E passed13.3s,types/build/ESLint/1036compiled parity. Both ux-live179 healthy/running,2 actual CHR SSE samples+live UI2 passed; ownDB/role/container removed. Следующий180 — точность и binary units учтённых объёмов: baseline positive1KiB/1MiB rounded0 и1GiB mislabeledGB подтверждён, ещё не исправлен.12.5 открыта. Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md). @@ -2047,3 +2047,26 @@ Owned PG18.6/Node22: test:traffic-rate15709ms,9 counter/source +14 history contr [Native read-only](ux-ui/acceptance/traffic-history-native-2026-10-12.json): оба CHR194.226.121.150/104.165.244.7, GET identity/interface200, по2 interfaces, counters valid;4 native GET, no routerConfigWrites/password changes/messages. Это не loss/speed/WAN proof. [Live smoke1440/320+3 APIs](ux-ui/acceptance/traffic-history-deployed-2026-10-12.json) подтверждает actual provenance/UI/overflow/pageerrors0; error/legacy branches — отдельно fixtures/ownSQL. Main DB read-only только для native checker; schema migration и штатный scheduler writes были частью deploy. Следующий179 — inspect/fix live monitor-traffic empty/partial/malformed rates (текущий parseMonitorTraffic заменяет их нулями), per-interface fallback и disabled-after-stream-start polling. Не объявлять этот путь принятым по history guards. Peer collection completeness, long sparse intervals, interface recreation/identity, snapshot after restore, scale/multiple instances и external integrations остаются условиями12.5. Локальный коммит без push. + +## 12.5.179 — достоверный live-трафик, остановка и переподключение (2026-10-12) + +parseMonitorTraffic больше не превращает empty/missing/malformed/negative/prefix values в0. Требуются именованные уникальные интерфейсы и обе конечные неотрицательные скорости; для aggregate — полный expected set актуальных активных интерфейсов, loopback исключён. Duplicate/wrong/missing interface и неполный ответ дают error. Настоящий measured0 сохраняется. Дельты и Mbps больше не округляются до хранения/передачи: positive1byte/30s не исчезает; legacy bigint rxBps/txBps округляются только при insert, read consumers используют counters. sum snapshot сохраняет полную точность. UI positive меньше разрешения отображается «<1 Кбит/с», а не fake idle0. Это не подтверждение максимальной производительности интерфейса. + +Новый readTrafficLiveSample revalidates enabled/current config перед каждым новым native request и перед публикацией. Device disabled/deleted/config change закрывает SSE с причиной; HTTP401/403 закрывают поток без повторного опроса. Inventory обновляется каждый aggregate tick. Только joined HTTP400 при нескольких интерфейсах допускает per-interface fallback; failures не заглушаются пустыми массивами, partial sum не публикуется. Fallback≤4 concurrent commands, complete-wave budget8s; parent abort/deadline отменяет POST/ожидаемые продолжения. Существующий GET транспорт не принимает AbortSignal, его already-started read ограничен4s; после abort не запускаются следующие commands. Нельзя отозвать уже отправленную native команду. Multiple instances/large inventory/long WAN/revocation of already-established JWT streams отдельно не приняты. + +Hook сохраняет конкретную причину error при end-of-stream, закрывает transport после decoding/HTTP failure и cleanup, валидирует timestampKind. «Переподключить live» создаёт новый request key в том же object scope, сохраняет последнее подтверждённое значение как historical до нового sample. При смене scope значения не переносятся. Connecting/error состояния явные; disabled retry during connecting предотвращает повторный запуск. Короткая подпись «Последнее live» помещается на320px, причина видна отдельной Alert. TimestampKind backend-receipt подписан «ответ RouterOS получен приложением»: at не выдаётся за часы устройства. Existing ReUI primitives/layout/theme сохранены. + +| Состояние | Схема | Артефакт | +| --- | --- | --- | +| Source выключен / SSE закрыт | Причина → последнее значение → переподключить → новый sample | [ПК1440](ux-ui/screens/traffic-live-error-1440-2026-10-12.png), [телефон320](ux-ui/screens/traffic-live-error-320-2026-10-12.png), API-fixture | +| Partial native response | Inventory → expected set → unknown/error вместо partial0 | Real TCP/HTTP owned SQL | + +Unit tests parser/completeness/fallback/auth/source/abort/10-interface concurrency/positive-small-rate прошли; frontend formatter/projection, application-source TypeScript/backend build/ESLint прошли. Новая unit initially использовала undefined default argument вместо missing field; fixture исправлена.7 unique E2E (2new retry/cause/320/1440 +5 existing SSE), final pass13.3s. Screenshot scoped repeats2 не новые scenarios; visual review выявила clipped long hint, caption сокращена и final frontend пересобран. Hidden tooltip duplicates в production smoke selector исключены filter visible, functional UI не менялся ради теста. + +Owned Node22/PG18.6 profile test:traffic-rate24570ms без skip:9 counters/source+14history+10 live transport cases. Последние — actual Fastify TCP SSE и MikrotikClient HTTP к loopback simulator:0,empty,malformed,partial,complete fallback,failed fallback,401 terminal,disable after first sample/no further native requests,initial disabled404/no native requests,config change/closed stream. Fake RouterOS response не объявлен физической неисправностью CHR. Budget/concurrency refinement вызвала оправданный scoped rerun; финальный DB mm_accept_166_1791814796307/ограниченная role/container удалены, отсутствие проверено. Предыдущий временный mm_accept_166_1791813403915 также удалён trap. Production env/credentials/JWT не копировались. [SQL](ux-ui/acceptance/traffic-live-sql-2026-10-12.json). + +[Source451 backend+522 frontend](ux-ui/acceptance/traffic-live-source-2026-10-12.json)/[1036 compiled](ux-ui/acceptance/traffic-live-compiled-2026-10-12.json) совпали. Production builds прошли; первый frontend build с длинной caption superseded до deploy. Staged frontend ux-live179 с backend ux-history178 [interop](ux-ui/acceptance/traffic-live-interop-2026-10-12.json), затем both ux-live179/version1.0.0-test.live179 [runtime](ux-ui/acceptance/traffic-live-runtime-2026-10-12.json) healthy/running/imageIds. Только.env tags, rollback both ux-history178 без secret env copy; storage migration не добавлялась. + +[Настоящие CHR SSE](ux-ui/acceptance/traffic-live-native-2026-10-12.json) получили первый полный sample на обоих sourceId1/2, затем streams отменены: native interface GET + monitor-traffic read-only POST. Пример краткого наблюдения194 RX0.031584/TX0.002016Mbps,104 RX/TX0.002112Mbps; это не SLA/capacity/WAN proof. Пароли/конфигурация не менялись, messages0. [Production browser1440/320](ux-ui/acceptance/traffic-live-deployed-2026-10-12.json): actual live labels/receipt provenance,3APIs200,no overflow/pageerrors. Lab AUTH_REQUIRED=false; JWT/RBAC scopes не объявлены полностью принятыми по этой smoke. + +Следующий180 — volume precision/binary units. Read-only baseline уже показал: measured1KiB/1MiB API round to0;100×1MiB также aggregate0;1GiB подписан1.0GB. Baseline сохранён в ignored tmp/volume180-baseline.json и пока не выдан за исправление. Sparse intervals/peer completeness/identity recreation/restore/multi-instance/scale и внешние integrations остаются12.5. Локальный коммит без push. diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md index 67066bf9..a471e63b 100644 --- a/docs/ux-ui-plan.md +++ b/docs/ux-ui-plan.md @@ -1,12 +1,12 @@ # План реализации UX/UI по пятичасовым окнам -Актуальное продолжение 2026-10-12: завершён12.5.178 — traffic history/current freshness и legacy counter provenance, manual truth/journal, masks/TTL UI серверов/клиентов/привязок. OwnSQL9+14/15709ms иsource26+GRE8/20075ms,35 unique E2E eventually passed,build/types/ESLint/1032compiled parity. Both ux-history178 healthy/running, migration0011 applied/2native CHR GET/live UI2 passed; ownDBs/roles/containers removed. Следующий179 — live monitor-traffic complete payload и disable-after-stream-start guard;12.5 открыта. +Актуальное продолжение 2026-10-12: завершён12.5.179 — live complete payload/expected interfaces/auth/disable-config guard; scoped retry/retain cause/latest sample, abort cleanup/receipt timestamp, positive tiny rates. OwnSQL9+14+10/24570ms,7E2E passed13.3s,types/build/ESLint/1036compiled parity. Both ux-live179 healthy/running,2 actual CHR SSE samples+live UI2 passed; ownDB/role/container removed. Следующий180 — точность и binary units учтённых объёмов: baseline positive1KiB/1MiB rounded0 и1GiB mislabeledGB подтверждён, ещё не исправлен.12.5 открыта. Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-12. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат. ## Актуальная точка продолжения — 2026-10-12 -Актуальное продолжение 2026-10-12: завершён12.5.178 — traffic history/current freshness и legacy counter provenance, manual truth/journal, masks/TTL UI серверов/клиентов/привязок. OwnSQL9+14/15709ms иsource26+GRE8/20075ms,35 unique E2E eventually passed,build/types/ESLint/1032compiled parity. Both ux-history178 healthy/running, migration0011 applied/2native CHR GET/live UI2 passed; ownDBs/roles/containers removed. Следующий179 — live monitor-traffic complete payload и disable-after-stream-start guard;12.5 открыта. +Актуальное продолжение 2026-10-12: завершён12.5.179 — live complete payload/expected interfaces/auth/disable-config guard; scoped retry/retain cause/latest sample, abort cleanup/receipt timestamp, positive tiny rates. OwnSQL9+14+10/24570ms,7E2E passed13.3s,types/build/ESLint/1036compiled parity. Both ux-live179 healthy/running,2 actual CHR SSE samples+live UI2 passed; ownDB/role/container removed. Следующий180 — точность и binary units учтённых объёмов: baseline positive1KiB/1MiB rounded0 и1GiB mislabeledGB подтверждён, ещё не исправлен.12.5 открыта. Этапы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) -Актуальное продолжение 2026-10-12: завершён12.5.178 — traffic history/current freshness и legacy counter provenance, manual truth/journal, masks/TTL UI серверов/клиентов/привязок. OwnSQL9+14/15709ms иsource26+GRE8/20075ms,35 unique E2E eventually passed,build/types/ESLint/1032compiled parity. Both ux-history178 healthy/running, migration0011 applied/2native CHR GET/live UI2 passed; ownDBs/roles/containers removed. Следующий179 — live monitor-traffic complete payload и disable-after-stream-start guard;12.5 открыта. +Актуальное продолжение 2026-10-12: завершён12.5.179 — live complete payload/expected interfaces/auth/disable-config guard; scoped retry/retain cause/latest sample, abort cleanup/receipt timestamp, positive tiny rates. OwnSQL9+14+10/24570ms,7E2E passed13.3s,types/build/ESLint/1036compiled parity. Both ux-live179 healthy/running,2 actual CHR SSE samples+live UI2 passed; ownDB/role/container removed. Следующий180 — точность и binary units учтённых объёмов: baseline positive1KiB/1MiB rounded0 и1GiB mislabeledGB подтверждён, ещё не исправлен.12.5 открыта. Подробный объём, протоколы, схема и ограничения: [12.5.77 в журнале](ux-ui-progress.md). diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md index db41fc4a..b1474b56 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -1,6 +1,6 @@ # UX/UI: журнал реализации -Актуальное продолжение 2026-10-12: завершён12.5.178 — traffic history/current freshness и legacy counter provenance, manual truth/journal, masks/TTL UI серверов/клиентов/привязок. OwnSQL9+14/15709ms иsource26+GRE8/20075ms,35 unique E2E eventually passed,build/types/ESLint/1032compiled parity. Both ux-history178 healthy/running, migration0011 applied/2native CHR GET/live UI2 passed; ownDBs/roles/containers removed. Следующий179 — live monitor-traffic complete payload и disable-after-stream-start guard;12.5 открыта. +Актуальное продолжение 2026-10-12: завершён12.5.179 — live complete payload/expected interfaces/auth/disable-config guard; scoped retry/retain cause/latest sample, abort cleanup/receipt timestamp, positive tiny rates. OwnSQL9+14+10/24570ms,7E2E passed13.3s,types/build/ESLint/1036compiled parity. Both ux-live179 healthy/running,2 actual CHR SSE samples+live UI2 passed; ownDB/role/container removed. Следующий180 — точность и binary units учтённых объёмов: baseline positive1KiB/1MiB rounded0 и1GiB mislabeledGB подтверждён, ещё не исправлен.12.5 открыта. Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md). @@ -38,6 +38,29 @@ | 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 | | 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–168 и ограничения ниже. Runtime backend ux-month149/frontend ux-sessions150. | +## 12.5.179 — достоверный live-трафик, остановка и переподключение (2026-10-12) + +parseMonitorTraffic больше не превращает empty/missing/malformed/negative/prefix values в0. Требуются именованные уникальные интерфейсы и обе конечные неотрицательные скорости; для aggregate — полный expected set актуальных активных интерфейсов, loopback исключён. Duplicate/wrong/missing interface и неполный ответ дают error. Настоящий measured0 сохраняется. Дельты и Mbps больше не округляются до хранения/передачи: positive1byte/30s не исчезает; legacy bigint rxBps/txBps округляются только при insert, read consumers используют counters. sum snapshot сохраняет полную точность. UI positive меньше разрешения отображается «<1 Кбит/с», а не fake idle0. Это не подтверждение максимальной производительности интерфейса. + +Новый readTrafficLiveSample revalidates enabled/current config перед каждым новым native request и перед публикацией. Device disabled/deleted/config change закрывает SSE с причиной; HTTP401/403 закрывают поток без повторного опроса. Inventory обновляется каждый aggregate tick. Только joined HTTP400 при нескольких интерфейсах допускает per-interface fallback; failures не заглушаются пустыми массивами, partial sum не публикуется. Fallback≤4 concurrent commands, complete-wave budget8s; parent abort/deadline отменяет POST/ожидаемые продолжения. Существующий GET транспорт не принимает AbortSignal, его already-started read ограничен4s; после abort не запускаются следующие commands. Нельзя отозвать уже отправленную native команду. Multiple instances/large inventory/long WAN/revocation of already-established JWT streams отдельно не приняты. + +Hook сохраняет конкретную причину error при end-of-stream, закрывает transport после decoding/HTTP failure и cleanup, валидирует timestampKind. «Переподключить live» создаёт новый request key в том же object scope, сохраняет последнее подтверждённое значение как historical до нового sample. При смене scope значения не переносятся. Connecting/error состояния явные; disabled retry during connecting предотвращает повторный запуск. Короткая подпись «Последнее live» помещается на320px, причина видна отдельной Alert. TimestampKind backend-receipt подписан «ответ RouterOS получен приложением»: at не выдаётся за часы устройства. Existing ReUI primitives/layout/theme сохранены. + +| Состояние | Схема | Артефакт | +| --- | --- | --- | +| Source выключен / SSE закрыт | Причина → последнее значение → переподключить → новый sample | [ПК1440](ux-ui/screens/traffic-live-error-1440-2026-10-12.png), [телефон320](ux-ui/screens/traffic-live-error-320-2026-10-12.png), API-fixture | +| Partial native response | Inventory → expected set → unknown/error вместо partial0 | Real TCP/HTTP owned SQL | + +Unit tests parser/completeness/fallback/auth/source/abort/10-interface concurrency/positive-small-rate прошли; frontend formatter/projection, application-source TypeScript/backend build/ESLint прошли. Новая unit initially использовала undefined default argument вместо missing field; fixture исправлена.7 unique E2E (2new retry/cause/320/1440 +5 existing SSE), final pass13.3s. Screenshot scoped repeats2 не новые scenarios; visual review выявила clipped long hint, caption сокращена и final frontend пересобран. Hidden tooltip duplicates в production smoke selector исключены filter visible, functional UI не менялся ради теста. + +Owned Node22/PG18.6 profile test:traffic-rate24570ms без skip:9 counters/source+14history+10 live transport cases. Последние — actual Fastify TCP SSE и MikrotikClient HTTP к loopback simulator:0,empty,malformed,partial,complete fallback,failed fallback,401 terminal,disable after first sample/no further native requests,initial disabled404/no native requests,config change/closed stream. Fake RouterOS response не объявлен физической неисправностью CHR. Budget/concurrency refinement вызвала оправданный scoped rerun; финальный DB mm_accept_166_1791814796307/ограниченная role/container удалены, отсутствие проверено. Предыдущий временный mm_accept_166_1791813403915 также удалён trap. Production env/credentials/JWT не копировались. [SQL](ux-ui/acceptance/traffic-live-sql-2026-10-12.json). + +[Source451 backend+522 frontend](ux-ui/acceptance/traffic-live-source-2026-10-12.json)/[1036 compiled](ux-ui/acceptance/traffic-live-compiled-2026-10-12.json) совпали. Production builds прошли; первый frontend build с длинной caption superseded до deploy. Staged frontend ux-live179 с backend ux-history178 [interop](ux-ui/acceptance/traffic-live-interop-2026-10-12.json), затем both ux-live179/version1.0.0-test.live179 [runtime](ux-ui/acceptance/traffic-live-runtime-2026-10-12.json) healthy/running/imageIds. Только.env tags, rollback both ux-history178 без secret env copy; storage migration не добавлялась. + +[Настоящие CHR SSE](ux-ui/acceptance/traffic-live-native-2026-10-12.json) получили первый полный sample на обоих sourceId1/2, затем streams отменены: native interface GET + monitor-traffic read-only POST. Пример краткого наблюдения194 RX0.031584/TX0.002016Mbps,104 RX/TX0.002112Mbps; это не SLA/capacity/WAN proof. Пароли/конфигурация не менялись, messages0. [Production browser1440/320](ux-ui/acceptance/traffic-live-deployed-2026-10-12.json): actual live labels/receipt provenance,3APIs200,no overflow/pageerrors. Lab AUTH_REQUIRED=false; JWT/RBAC scopes не объявлены полностью принятыми по этой smoke. + +Следующий180 — volume precision/binary units. Read-only baseline уже показал: measured1KiB/1MiB API round to0;100×1MiB также aggregate0;1GiB подписан1.0GB. Baseline сохранён в ignored tmp/volume180-baseline.json и пока не выдан за исправление. Sparse intervals/peer completeness/identity recreation/restore/multi-instance/scale и внешние integrations остаются12.5. Локальный коммит без push. + ## 12.5.178 — достоверная история трафика, ручной сбор и свежесть UI (2026-10-12) Подтверждены source gaps: исторические deltas могли выдавать старую скорость как текущую после failed collection; manual collect-now игнорировал snapshot failures и публиковал success. Добавлена migration0011/traffic_collection_state (одна актуальная запись на server): pending до HTTP, ok/error после записи, lastAttemptAt/lastSucceededAt и конфигурационный updatedAt fingerprint. Pending/error не стирают successful history. Completion обновляет только свою lastAttemptAt; pending не переписывает более новую попытку. Это ограничивает гонки записей, но не является распределённым lock/полной приёмкой нескольких backend. diff --git a/docs/ux-ui-remaining-acceptance.md b/docs/ux-ui-remaining-acceptance.md index c8a38b11..8165c073 100644 --- a/docs/ux-ui-remaining-acceptance.md +++ b/docs/ux-ui-remaining-acceptance.md @@ -1,8 +1,8 @@ # Оставшаяся внешняя приёмка — 2026-10-12 -Это актуальная матрица после 12.5.178. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. +Это актуальная матрица после 12.5.179. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. -Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-history178`, frontend `ux-history178`; production lab использует AUTH_REQUIRED=false. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись. +Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-live179`, frontend `ux-live179`; production lab использует AUTH_REQUIRED=false. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись. | Раздел | Подтверждено | Осталось / условие завершения | | --- | --- | --- | @@ -18,7 +18,7 @@ | Recursive routes | Production PUT/change/own history restore88, disabled guards, partial/unknown/applied outcomes, live формы/контекст | Автоматический rollback/retry, durable receipts и несколько экземпляров отсутствуют; PUT не является idempotent noop. Полная произвольная legacy catalog миграция не принята | | Diagnostics/terminal | Allowlist/native reads, Ping/trace/route/MTU/DNS, actual avg-rtt, disabled guards, JWT/live PC/mobile89; real default memory tail/log limits/API/UI133 | DNS использует backend resolver; MTU — ограниченная оценка, не гарантированный максимум. Stop ожидания не доказывает остановку RouterOS команды; arbitrary CLI и physical disconnect не приняты | | Portal/интеграции | Локальные и isolated JWT guards; UI/API error states в границах журналов | Настоящий Portal issuer/login/отзыв прав; RIPE SDK two-IP/no-fetch passed140, native IPv6 ULA flow pipeline passed145 (IPv4 transport), public IPv6 WAN/local licensed MaxMind/S3/container/certificate jobs и Gitea delivery требуют отдельных тестовых endpoints/аккаунтов. Не отправлять сообщения и не публиковать релиз без соответствующего поручения | -| Общая приёмка | Локальная12.3.83; UI1244/context221/compiled984 parity139; профильные build/E2E/live проверки последующих блоков | Traffic initial live mock-id gate passed141; IPv6 special ranges/cache passed142; unknown RIPE coordinates passed143; IPv6 synthetic UDP/worker/SQL passed144; native CHR ULA/IPFIX/API/UI passed145; ICMPv6/IPv6 endpoint labels passed146; HTTP DTO validation passed147; scoped flow catalog/serverId guards passed148; strict monthly calendar passed149; Sessions labels passed150; application GRE static IPv6 lifecycle151 passed; WireGuard static IPv6 lifecycle152 и disabled SQL/API gates passed; OSPF/BFD boundary/warning153 passed; OSPFv3 routes154 accepted in recorded bounds; BGP IPv6 basic155 passed; bigint ID/SQL guards156 passed; общая UI1260/context221/backend24 regression157 passed; IPv6 BFD correlation158 profile/deployed passed; native OSPFv3/BFDv6 boundary/recovery159 passed; BGP IPv6/BFD withdrawal/recovery160 passed; selected BGP/alert unknown guards161 replay/profiles/deployed passed (SQL stability skipped); private4-byte ASN162 native/API/UI passed; duplicate BGP alert keys163 replay/profiles/deployed passed; nullable selected BGP164 replay/profiles/API/UI passed; collector165 parallel/current guards/read-only PG passed; positive partial/error SQL166/full24 with PG passed (optional network SQL not repeated); GRE/BGP freshness167/SQL8 passed; other source audit168 выявил identity/disabled/freshness gaps; локальный server/traffic identity guard169 passed без SQL/deploy, source/per-probe/freshness170/ownSQL16/compiled1008/deployed passed; source diagnostics171/42E2E/ownSQL metadata/live2 passed; monitoring history172/ownSQL26/compiled1020/deployed passed; Ping known/error173/ownHTTP-SQL8/85uniqueE2E/native4/4/deployed passed; traffic confidence174 baseline reproduced/local guard passed; owned SQL9/9358ms/deploy175/1024compiled/live smoke passed; legacy history/provenance open; compound RTT176 local/native replay2 passed, SQL10/deploy177/1024compiled/live smoke passed; history178/legacy provenance/current TTL/manual truth/SQL9+14/35unique E2E/native GET/deployed passed; live incomplete/disable-stream179 next; последующие изменения проверять отдельно; scoped axe не означает полную WCAG сертификацию | +| Общая приёмка | Локальная12.3.83; UI1244/context221/compiled984 parity139; профильные build/E2E/live проверки последующих блоков | Traffic initial live mock-id gate passed141; IPv6 special ranges/cache passed142; unknown RIPE coordinates passed143; IPv6 synthetic UDP/worker/SQL passed144; native CHR ULA/IPFIX/API/UI passed145; ICMPv6/IPv6 endpoint labels passed146; HTTP DTO validation passed147; scoped flow catalog/serverId guards passed148; strict monthly calendar passed149; Sessions labels passed150; application GRE static IPv6 lifecycle151 passed; WireGuard static IPv6 lifecycle152 и disabled SQL/API gates passed; OSPF/BFD boundary/warning153 passed; OSPFv3 routes154 accepted in recorded bounds; BGP IPv6 basic155 passed; bigint ID/SQL guards156 passed; общая UI1260/context221/backend24 regression157 passed; IPv6 BFD correlation158 profile/deployed passed; native OSPFv3/BFDv6 boundary/recovery159 passed; BGP IPv6/BFD withdrawal/recovery160 passed; selected BGP/alert unknown guards161 replay/profiles/deployed passed (SQL stability skipped); private4-byte ASN162 native/API/UI passed; duplicate BGP alert keys163 replay/profiles/deployed passed; nullable selected BGP164 replay/profiles/API/UI passed; collector165 parallel/current guards/read-only PG passed; positive partial/error SQL166/full24 with PG passed (optional network SQL not repeated); GRE/BGP freshness167/SQL8 passed; other source audit168 выявил identity/disabled/freshness gaps; локальный server/traffic identity guard169 passed без SQL/deploy, source/per-probe/freshness170/ownSQL16/compiled1008/deployed passed; source diagnostics171/42E2E/ownSQL metadata/live2 passed; monitoring history172/ownSQL26/compiled1020/deployed passed; Ping known/error173/ownHTTP-SQL8/85uniqueE2E/native4/4/deployed passed; traffic confidence174 baseline reproduced/local guard passed; owned SQL9/9358ms/deploy175/1024compiled/live smoke passed; legacy history/provenance open; compound RTT176 local/native replay2 passed, SQL10/deploy177/1024compiled/live smoke passed; history178/legacy provenance/current TTL/manual truth/SQL9+14/35unique E2E/native GET/deployed passed; live179 complete/disable-config/bounded fallback/ownSQL10/nativeSSE2/liveUI2 passed; volume precision/units180 next; последующие изменения проверять отдельно; scoped axe не означает полную WCAG сертификацию | Доказательства и точные ограничения: [полная история этапа12](ux-ui-stage-12.md), [внешний протокол](ux-ui-external-acceptance.md), [BGP93](ux-ui/acceptance/bgp-failover-2026-10-10.json), [паспорт стенда](ux-ui-test-server.md). Невыполненные пункты не являются все блокерами данного лабораторного сценария: для выпуска необходимо явно определить требуемые конфигурации и принять их либо согласовать ограничение. @@ -962,3 +962,26 @@ Owned PG18.6/Node22: test:traffic-rate15709ms,9 counter/source +14 history contr [Native read-only](ux-ui/acceptance/traffic-history-native-2026-10-12.json): оба CHR194.226.121.150/104.165.244.7, GET identity/interface200, по2 interfaces, counters valid;4 native GET, no routerConfigWrites/password changes/messages. Это не loss/speed/WAN proof. [Live smoke1440/320+3 APIs](ux-ui/acceptance/traffic-history-deployed-2026-10-12.json) подтверждает actual provenance/UI/overflow/pageerrors0; error/legacy branches — отдельно fixtures/ownSQL. Main DB read-only только для native checker; schema migration и штатный scheduler writes были частью deploy. Следующий179 — inspect/fix live monitor-traffic empty/partial/malformed rates (текущий parseMonitorTraffic заменяет их нулями), per-interface fallback и disabled-after-stream-start polling. Не объявлять этот путь принятым по history guards. Peer collection completeness, long sparse intervals, interface recreation/identity, snapshot after restore, scale/multiple instances и external integrations остаются условиями12.5. Локальный коммит без push. + +## 12.5.179 — достоверный live-трафик, остановка и переподключение (2026-10-12) + +parseMonitorTraffic больше не превращает empty/missing/malformed/negative/prefix values в0. Требуются именованные уникальные интерфейсы и обе конечные неотрицательные скорости; для aggregate — полный expected set актуальных активных интерфейсов, loopback исключён. Duplicate/wrong/missing interface и неполный ответ дают error. Настоящий measured0 сохраняется. Дельты и Mbps больше не округляются до хранения/передачи: positive1byte/30s не исчезает; legacy bigint rxBps/txBps округляются только при insert, read consumers используют counters. sum snapshot сохраняет полную точность. UI positive меньше разрешения отображается «<1 Кбит/с», а не fake idle0. Это не подтверждение максимальной производительности интерфейса. + +Новый readTrafficLiveSample revalidates enabled/current config перед каждым новым native request и перед публикацией. Device disabled/deleted/config change закрывает SSE с причиной; HTTP401/403 закрывают поток без повторного опроса. Inventory обновляется каждый aggregate tick. Только joined HTTP400 при нескольких интерфейсах допускает per-interface fallback; failures не заглушаются пустыми массивами, partial sum не публикуется. Fallback≤4 concurrent commands, complete-wave budget8s; parent abort/deadline отменяет POST/ожидаемые продолжения. Существующий GET транспорт не принимает AbortSignal, его already-started read ограничен4s; после abort не запускаются следующие commands. Нельзя отозвать уже отправленную native команду. Multiple instances/large inventory/long WAN/revocation of already-established JWT streams отдельно не приняты. + +Hook сохраняет конкретную причину error при end-of-stream, закрывает transport после decoding/HTTP failure и cleanup, валидирует timestampKind. «Переподключить live» создаёт новый request key в том же object scope, сохраняет последнее подтверждённое значение как historical до нового sample. При смене scope значения не переносятся. Connecting/error состояния явные; disabled retry during connecting предотвращает повторный запуск. Короткая подпись «Последнее live» помещается на320px, причина видна отдельной Alert. TimestampKind backend-receipt подписан «ответ RouterOS получен приложением»: at не выдаётся за часы устройства. Existing ReUI primitives/layout/theme сохранены. + +| Состояние | Схема | Артефакт | +| --- | --- | --- | +| Source выключен / SSE закрыт | Причина → последнее значение → переподключить → новый sample | [ПК1440](ux-ui/screens/traffic-live-error-1440-2026-10-12.png), [телефон320](ux-ui/screens/traffic-live-error-320-2026-10-12.png), API-fixture | +| Partial native response | Inventory → expected set → unknown/error вместо partial0 | Real TCP/HTTP owned SQL | + +Unit tests parser/completeness/fallback/auth/source/abort/10-interface concurrency/positive-small-rate прошли; frontend formatter/projection, application-source TypeScript/backend build/ESLint прошли. Новая unit initially использовала undefined default argument вместо missing field; fixture исправлена.7 unique E2E (2new retry/cause/320/1440 +5 existing SSE), final pass13.3s. Screenshot scoped repeats2 не новые scenarios; visual review выявила clipped long hint, caption сокращена и final frontend пересобран. Hidden tooltip duplicates в production smoke selector исключены filter visible, functional UI не менялся ради теста. + +Owned Node22/PG18.6 profile test:traffic-rate24570ms без skip:9 counters/source+14history+10 live transport cases. Последние — actual Fastify TCP SSE и MikrotikClient HTTP к loopback simulator:0,empty,malformed,partial,complete fallback,failed fallback,401 terminal,disable after first sample/no further native requests,initial disabled404/no native requests,config change/closed stream. Fake RouterOS response не объявлен физической неисправностью CHR. Budget/concurrency refinement вызвала оправданный scoped rerun; финальный DB mm_accept_166_1791814796307/ограниченная role/container удалены, отсутствие проверено. Предыдущий временный mm_accept_166_1791813403915 также удалён trap. Production env/credentials/JWT не копировались. [SQL](ux-ui/acceptance/traffic-live-sql-2026-10-12.json). + +[Source451 backend+522 frontend](ux-ui/acceptance/traffic-live-source-2026-10-12.json)/[1036 compiled](ux-ui/acceptance/traffic-live-compiled-2026-10-12.json) совпали. Production builds прошли; первый frontend build с длинной caption superseded до deploy. Staged frontend ux-live179 с backend ux-history178 [interop](ux-ui/acceptance/traffic-live-interop-2026-10-12.json), затем both ux-live179/version1.0.0-test.live179 [runtime](ux-ui/acceptance/traffic-live-runtime-2026-10-12.json) healthy/running/imageIds. Только.env tags, rollback both ux-history178 без secret env copy; storage migration не добавлялась. + +[Настоящие CHR SSE](ux-ui/acceptance/traffic-live-native-2026-10-12.json) получили первый полный sample на обоих sourceId1/2, затем streams отменены: native interface GET + monitor-traffic read-only POST. Пример краткого наблюдения194 RX0.031584/TX0.002016Mbps,104 RX/TX0.002112Mbps; это не SLA/capacity/WAN proof. Пароли/конфигурация не менялись, messages0. [Production browser1440/320](ux-ui/acceptance/traffic-live-deployed-2026-10-12.json): actual live labels/receipt provenance,3APIs200,no overflow/pageerrors. Lab AUTH_REQUIRED=false; JWT/RBAC scopes не объявлены полностью принятыми по этой smoke. + +Следующий180 — volume precision/binary units. Read-only baseline уже показал: measured1KiB/1MiB API round to0;100×1MiB также aggregate0;1GiB подписан1.0GB. Baseline сохранён в ignored tmp/volume180-baseline.json и пока не выдан за исправление. Sparse intervals/peer completeness/identity recreation/restore/multi-instance/scale и внешние integrations остаются12.5. Локальный коммит без push. diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md index 1c1007d1..0e2f7257 100644 --- a/docs/ux-ui-stage-12.md +++ b/docs/ux-ui-stage-12.md @@ -1,6 +1,6 @@ # Этап 12: итоговая приёмка UX/UI -Актуальное продолжение 2026-10-12: завершён12.5.178 — traffic history/current freshness и legacy counter provenance, manual truth/journal, masks/TTL UI серверов/клиентов/привязок. OwnSQL9+14/15709ms иsource26+GRE8/20075ms,35 unique E2E eventually passed,build/types/ESLint/1032compiled parity. Both ux-history178 healthy/running, migration0011 applied/2native CHR GET/live UI2 passed; ownDBs/roles/containers removed. Следующий179 — live monitor-traffic complete payload и disable-after-stream-start guard;12.5 открыта. +Актуальное продолжение 2026-10-12: завершён12.5.179 — live complete payload/expected interfaces/auth/disable-config guard; scoped retry/retain cause/latest sample, abort cleanup/receipt timestamp, positive tiny rates. OwnSQL9+14+10/24570ms,7E2E passed13.3s,types/build/ESLint/1036compiled parity. Both ux-live179 healthy/running,2 actual CHR SSE samples+live UI2 passed; ownDB/role/container removed. Следующий180 — точность и binary units учтённых объёмов: baseline positive1KiB/1MiB rounded0 и1GiB mislabeledGB подтверждён, ещё не исправлен.12.5 открыта. План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push. @@ -3352,3 +3352,26 @@ Owned PG18.6/Node22: test:traffic-rate15709ms,9 counter/source +14 history contr [Native read-only](ux-ui/acceptance/traffic-history-native-2026-10-12.json): оба CHR194.226.121.150/104.165.244.7, GET identity/interface200, по2 interfaces, counters valid;4 native GET, no routerConfigWrites/password changes/messages. Это не loss/speed/WAN proof. [Live smoke1440/320+3 APIs](ux-ui/acceptance/traffic-history-deployed-2026-10-12.json) подтверждает actual provenance/UI/overflow/pageerrors0; error/legacy branches — отдельно fixtures/ownSQL. Main DB read-only только для native checker; schema migration и штатный scheduler writes были частью deploy. Следующий179 — inspect/fix live monitor-traffic empty/partial/malformed rates (текущий parseMonitorTraffic заменяет их нулями), per-interface fallback и disabled-after-stream-start polling. Не объявлять этот путь принятым по history guards. Peer collection completeness, long sparse intervals, interface recreation/identity, snapshot after restore, scale/multiple instances и external integrations остаются условиями12.5. Локальный коммит без push. + +## 12.5.179 — достоверный live-трафик, остановка и переподключение (2026-10-12) + +parseMonitorTraffic больше не превращает empty/missing/malformed/negative/prefix values в0. Требуются именованные уникальные интерфейсы и обе конечные неотрицательные скорости; для aggregate — полный expected set актуальных активных интерфейсов, loopback исключён. Duplicate/wrong/missing interface и неполный ответ дают error. Настоящий measured0 сохраняется. Дельты и Mbps больше не округляются до хранения/передачи: positive1byte/30s не исчезает; legacy bigint rxBps/txBps округляются только при insert, read consumers используют counters. sum snapshot сохраняет полную точность. UI positive меньше разрешения отображается «<1 Кбит/с», а не fake idle0. Это не подтверждение максимальной производительности интерфейса. + +Новый readTrafficLiveSample revalidates enabled/current config перед каждым новым native request и перед публикацией. Device disabled/deleted/config change закрывает SSE с причиной; HTTP401/403 закрывают поток без повторного опроса. Inventory обновляется каждый aggregate tick. Только joined HTTP400 при нескольких интерфейсах допускает per-interface fallback; failures не заглушаются пустыми массивами, partial sum не публикуется. Fallback≤4 concurrent commands, complete-wave budget8s; parent abort/deadline отменяет POST/ожидаемые продолжения. Существующий GET транспорт не принимает AbortSignal, его already-started read ограничен4s; после abort не запускаются следующие commands. Нельзя отозвать уже отправленную native команду. Multiple instances/large inventory/long WAN/revocation of already-established JWT streams отдельно не приняты. + +Hook сохраняет конкретную причину error при end-of-stream, закрывает transport после decoding/HTTP failure и cleanup, валидирует timestampKind. «Переподключить live» создаёт новый request key в том же object scope, сохраняет последнее подтверждённое значение как historical до нового sample. При смене scope значения не переносятся. Connecting/error состояния явные; disabled retry during connecting предотвращает повторный запуск. Короткая подпись «Последнее live» помещается на320px, причина видна отдельной Alert. TimestampKind backend-receipt подписан «ответ RouterOS получен приложением»: at не выдаётся за часы устройства. Existing ReUI primitives/layout/theme сохранены. + +| Состояние | Схема | Артефакт | +| --- | --- | --- | +| Source выключен / SSE закрыт | Причина → последнее значение → переподключить → новый sample | [ПК1440](ux-ui/screens/traffic-live-error-1440-2026-10-12.png), [телефон320](ux-ui/screens/traffic-live-error-320-2026-10-12.png), API-fixture | +| Partial native response | Inventory → expected set → unknown/error вместо partial0 | Real TCP/HTTP owned SQL | + +Unit tests parser/completeness/fallback/auth/source/abort/10-interface concurrency/positive-small-rate прошли; frontend formatter/projection, application-source TypeScript/backend build/ESLint прошли. Новая unit initially использовала undefined default argument вместо missing field; fixture исправлена.7 unique E2E (2new retry/cause/320/1440 +5 existing SSE), final pass13.3s. Screenshot scoped repeats2 не новые scenarios; visual review выявила clipped long hint, caption сокращена и final frontend пересобран. Hidden tooltip duplicates в production smoke selector исключены filter visible, functional UI не менялся ради теста. + +Owned Node22/PG18.6 profile test:traffic-rate24570ms без skip:9 counters/source+14history+10 live transport cases. Последние — actual Fastify TCP SSE и MikrotikClient HTTP к loopback simulator:0,empty,malformed,partial,complete fallback,failed fallback,401 terminal,disable after first sample/no further native requests,initial disabled404/no native requests,config change/closed stream. Fake RouterOS response не объявлен физической неисправностью CHR. Budget/concurrency refinement вызвала оправданный scoped rerun; финальный DB mm_accept_166_1791814796307/ограниченная role/container удалены, отсутствие проверено. Предыдущий временный mm_accept_166_1791813403915 также удалён trap. Production env/credentials/JWT не копировались. [SQL](ux-ui/acceptance/traffic-live-sql-2026-10-12.json). + +[Source451 backend+522 frontend](ux-ui/acceptance/traffic-live-source-2026-10-12.json)/[1036 compiled](ux-ui/acceptance/traffic-live-compiled-2026-10-12.json) совпали. Production builds прошли; первый frontend build с длинной caption superseded до deploy. Staged frontend ux-live179 с backend ux-history178 [interop](ux-ui/acceptance/traffic-live-interop-2026-10-12.json), затем both ux-live179/version1.0.0-test.live179 [runtime](ux-ui/acceptance/traffic-live-runtime-2026-10-12.json) healthy/running/imageIds. Только.env tags, rollback both ux-history178 без secret env copy; storage migration не добавлялась. + +[Настоящие CHR SSE](ux-ui/acceptance/traffic-live-native-2026-10-12.json) получили первый полный sample на обоих sourceId1/2, затем streams отменены: native interface GET + monitor-traffic read-only POST. Пример краткого наблюдения194 RX0.031584/TX0.002016Mbps,104 RX/TX0.002112Mbps; это не SLA/capacity/WAN proof. Пароли/конфигурация не менялись, messages0. [Production browser1440/320](ux-ui/acceptance/traffic-live-deployed-2026-10-12.json): actual live labels/receipt provenance,3APIs200,no overflow/pageerrors. Lab AUTH_REQUIRED=false; JWT/RBAC scopes не объявлены полностью принятыми по этой smoke. + +Следующий180 — volume precision/binary units. Read-only baseline уже показал: measured1KiB/1MiB API round to0;100×1MiB также aggregate0;1GiB подписан1.0GB. Baseline сохранён в ignored tmp/volume180-baseline.json и пока не выдан за исправление. Sparse intervals/peer completeness/identity recreation/restore/multi-instance/scale и внешние integrations остаются12.5. Локальный коммит без push. diff --git a/docs/ux-ui-test-server.md b/docs/ux-ui-test-server.md index 1a9e2d36..baa53247 100644 --- a/docs/ux-ui-test-server.md +++ b/docs/ux-ui-test-server.md @@ -1,6 +1,6 @@ # Тестовый сервер UX/UI — 2026-10-10 -Сервер161.104.46.222: завершён12.5.178 — traffic history/current freshness и legacy counter provenance, manual truth/journal, masks/TTL UI серверов/клиентов/привязок. OwnSQL9+14/15709ms иsource26+GRE8/20075ms,35 unique E2E eventually passed,build/types/ESLint/1032compiled parity. Both ux-history178 healthy/running, migration0011 applied/2native CHR GET/live UI2 passed; ownDBs/roles/containers removed. Следующий179 — live monitor-traffic complete payload и disable-after-stream-start guard;12.5 открыта. +Сервер161.104.46.222: завершён12.5.179 — live complete payload/expected interfaces/auth/disable-config guard; scoped retry/retain cause/latest sample, abort cleanup/receipt timestamp, positive tiny rates. OwnSQL9+14+10/24570ms,7E2E passed13.3s,types/build/ESLint/1036compiled parity. Both ux-live179 healthy/running,2 actual CHR SSE samples+live UI2 passed; ownDB/role/container removed. Следующий180 — точность и binary units учтённых объёмов: baseline positive1KiB/1MiB rounded0 и1GiB mislabeledGB подтверждён, ещё не исправлен.12.5 открыта. | Параметр | Проверенное состояние | | --- | --- | @@ -14,7 +14,7 @@ | PostgreSQL | postgres:18-alpine,18.6;mmapp-test-postgres healthy;SELECT version() passed | | Данные | Отдельный Compose project mmapp-test,том mmapp-test_mmapp-pgdata и каталог ./data | | Доступ | PostgreSQL не публикуется;frontend3000/backend8000 привязаны к127.0.0.1 после запуска | -| Образы приложения | mmapp-test/backend:ux-history178 и mmapp-test/frontend:ux-history178; запущены | +| Образы приложения | mmapp-test/backend:ux-live179 и mmapp-test/frontend:ux-live179; запущены | Compose SHA256:3052bd6ab5d9865f97605cebfb5ff27a4410cd3357b81153a65a87fb7c92582f. OptionalBACKEND_IMAGE_TAG позволяетобновлятьbackendотдельно. Локальный и удалённый файлы совпали. Шаблоны: [Compose](../deploy/docker-compose.test.yml), [env без секретов](../deploy/env.test.example). @@ -1778,3 +1778,26 @@ Owned PG18.6/Node22: test:traffic-rate15709ms,9 counter/source +14 history contr [Native read-only](ux-ui/acceptance/traffic-history-native-2026-10-12.json): оба CHR194.226.121.150/104.165.244.7, GET identity/interface200, по2 interfaces, counters valid;4 native GET, no routerConfigWrites/password changes/messages. Это не loss/speed/WAN proof. [Live smoke1440/320+3 APIs](ux-ui/acceptance/traffic-history-deployed-2026-10-12.json) подтверждает actual provenance/UI/overflow/pageerrors0; error/legacy branches — отдельно fixtures/ownSQL. Main DB read-only только для native checker; schema migration и штатный scheduler writes были частью deploy. Следующий179 — inspect/fix live monitor-traffic empty/partial/malformed rates (текущий parseMonitorTraffic заменяет их нулями), per-interface fallback и disabled-after-stream-start polling. Не объявлять этот путь принятым по history guards. Peer collection completeness, long sparse intervals, interface recreation/identity, snapshot after restore, scale/multiple instances и external integrations остаются условиями12.5. Локальный коммит без push. + +## 12.5.179 — достоверный live-трафик, остановка и переподключение (2026-10-12) + +parseMonitorTraffic больше не превращает empty/missing/malformed/negative/prefix values в0. Требуются именованные уникальные интерфейсы и обе конечные неотрицательные скорости; для aggregate — полный expected set актуальных активных интерфейсов, loopback исключён. Duplicate/wrong/missing interface и неполный ответ дают error. Настоящий measured0 сохраняется. Дельты и Mbps больше не округляются до хранения/передачи: positive1byte/30s не исчезает; legacy bigint rxBps/txBps округляются только при insert, read consumers используют counters. sum snapshot сохраняет полную точность. UI positive меньше разрешения отображается «<1 Кбит/с», а не fake idle0. Это не подтверждение максимальной производительности интерфейса. + +Новый readTrafficLiveSample revalidates enabled/current config перед каждым новым native request и перед публикацией. Device disabled/deleted/config change закрывает SSE с причиной; HTTP401/403 закрывают поток без повторного опроса. Inventory обновляется каждый aggregate tick. Только joined HTTP400 при нескольких интерфейсах допускает per-interface fallback; failures не заглушаются пустыми массивами, partial sum не публикуется. Fallback≤4 concurrent commands, complete-wave budget8s; parent abort/deadline отменяет POST/ожидаемые продолжения. Существующий GET транспорт не принимает AbortSignal, его already-started read ограничен4s; после abort не запускаются следующие commands. Нельзя отозвать уже отправленную native команду. Multiple instances/large inventory/long WAN/revocation of already-established JWT streams отдельно не приняты. + +Hook сохраняет конкретную причину error при end-of-stream, закрывает transport после decoding/HTTP failure и cleanup, валидирует timestampKind. «Переподключить live» создаёт новый request key в том же object scope, сохраняет последнее подтверждённое значение как historical до нового sample. При смене scope значения не переносятся. Connecting/error состояния явные; disabled retry during connecting предотвращает повторный запуск. Короткая подпись «Последнее live» помещается на320px, причина видна отдельной Alert. TimestampKind backend-receipt подписан «ответ RouterOS получен приложением»: at не выдаётся за часы устройства. Existing ReUI primitives/layout/theme сохранены. + +| Состояние | Схема | Артефакт | +| --- | --- | --- | +| Source выключен / SSE закрыт | Причина → последнее значение → переподключить → новый sample | [ПК1440](ux-ui/screens/traffic-live-error-1440-2026-10-12.png), [телефон320](ux-ui/screens/traffic-live-error-320-2026-10-12.png), API-fixture | +| Partial native response | Inventory → expected set → unknown/error вместо partial0 | Real TCP/HTTP owned SQL | + +Unit tests parser/completeness/fallback/auth/source/abort/10-interface concurrency/positive-small-rate прошли; frontend formatter/projection, application-source TypeScript/backend build/ESLint прошли. Новая unit initially использовала undefined default argument вместо missing field; fixture исправлена.7 unique E2E (2new retry/cause/320/1440 +5 existing SSE), final pass13.3s. Screenshot scoped repeats2 не новые scenarios; visual review выявила clipped long hint, caption сокращена и final frontend пересобран. Hidden tooltip duplicates в production smoke selector исключены filter visible, functional UI не менялся ради теста. + +Owned Node22/PG18.6 profile test:traffic-rate24570ms без skip:9 counters/source+14history+10 live transport cases. Последние — actual Fastify TCP SSE и MikrotikClient HTTP к loopback simulator:0,empty,malformed,partial,complete fallback,failed fallback,401 terminal,disable after first sample/no further native requests,initial disabled404/no native requests,config change/closed stream. Fake RouterOS response не объявлен физической неисправностью CHR. Budget/concurrency refinement вызвала оправданный scoped rerun; финальный DB mm_accept_166_1791814796307/ограниченная role/container удалены, отсутствие проверено. Предыдущий временный mm_accept_166_1791813403915 также удалён trap. Production env/credentials/JWT не копировались. [SQL](ux-ui/acceptance/traffic-live-sql-2026-10-12.json). + +[Source451 backend+522 frontend](ux-ui/acceptance/traffic-live-source-2026-10-12.json)/[1036 compiled](ux-ui/acceptance/traffic-live-compiled-2026-10-12.json) совпали. Production builds прошли; первый frontend build с длинной caption superseded до deploy. Staged frontend ux-live179 с backend ux-history178 [interop](ux-ui/acceptance/traffic-live-interop-2026-10-12.json), затем both ux-live179/version1.0.0-test.live179 [runtime](ux-ui/acceptance/traffic-live-runtime-2026-10-12.json) healthy/running/imageIds. Только.env tags, rollback both ux-history178 без secret env copy; storage migration не добавлялась. + +[Настоящие CHR SSE](ux-ui/acceptance/traffic-live-native-2026-10-12.json) получили первый полный sample на обоих sourceId1/2, затем streams отменены: native interface GET + monitor-traffic read-only POST. Пример краткого наблюдения194 RX0.031584/TX0.002016Mbps,104 RX/TX0.002112Mbps; это не SLA/capacity/WAN proof. Пароли/конфигурация не менялись, messages0. [Production browser1440/320](ux-ui/acceptance/traffic-live-deployed-2026-10-12.json): actual live labels/receipt provenance,3APIs200,no overflow/pageerrors. Lab AUTH_REQUIRED=false; JWT/RBAC scopes не объявлены полностью принятыми по этой smoke. + +Следующий180 — volume precision/binary units. Read-only baseline уже показал: measured1KiB/1MiB API round to0;100×1MiB также aggregate0;1GiB подписан1.0GB. Baseline сохранён в ignored tmp/volume180-baseline.json и пока не выдан за исправление. Sparse intervals/peer completeness/identity recreation/restore/multi-instance/scale и внешние integrations остаются12.5. Локальный коммит без push. diff --git a/docs/ux-ui/acceptance/traffic-live-compiled-2026-10-12.json b/docs/ux-ui/acceptance/traffic-live-compiled-2026-10-12.json new file mode 100644 index 00000000..babed173 --- /dev/null +++ b/docs/ux-ui/acceptance/traffic-live-compiled-2026-10-12.json @@ -0,0 +1,1046 @@ +{ + "date": "2026-10-12", + "block": "12.5.179", + "backendVersion": "1.0.0-test.live179", + "normalization": "CRLF to LF", + "matched": [ + "backend/dist/services/traffic-live.js", + "backend/dist/services/traffic-live.js.map", + "backend/dist/services/traffic-live.d.ts", + "backend/dist/services/traffic-live.d.ts.map", + "backend/dist/config.js", + "backend/dist/config.js.map", + "backend/dist/config.d.ts", + "backend/dist/config.d.ts.map", + "backend/dist/constants/ping-probe.js", + "backend/dist/constants/ping-probe.js.map", + "backend/dist/constants/ping-probe.d.ts", + "backend/dist/constants/ping-probe.d.ts.map", + "backend/dist/db/bootstrap.js", + "backend/dist/db/bootstrap.js.map", + "backend/dist/db/bootstrap.d.ts", + "backend/dist/db/bootstrap.d.ts.map", + "backend/dist/db/index.js", + "backend/dist/db/index.js.map", + "backend/dist/db/index.d.ts", + "backend/dist/db/index.d.ts.map", + "backend/dist/db/json.js", + "backend/dist/db/json.js.map", + "backend/dist/db/json.d.ts", + "backend/dist/db/json.d.ts.map", + "backend/dist/db/migrate.js", + "backend/dist/db/migrate.js.map", + "backend/dist/db/migrate.d.ts", + "backend/dist/db/migrate.d.ts.map", + "backend/dist/db/operation-lock.js", + "backend/dist/db/operation-lock.js.map", + "backend/dist/db/operation-lock.d.ts", + "backend/dist/db/operation-lock.d.ts.map", + "backend/dist/db/partitions.js", + "backend/dist/db/partitions.js.map", + "backend/dist/db/partitions.d.ts", + "backend/dist/db/partitions.d.ts.map", + "backend/dist/db/schema.js", + "backend/dist/db/schema.js.map", + "backend/dist/db/schema.d.ts", + "backend/dist/db/schema.d.ts.map", + "backend/dist/db/sql-bind.js", + "backend/dist/db/sql-bind.js.map", + "backend/dist/db/sql-bind.d.ts", + "backend/dist/db/sql-bind.d.ts.map", + "backend/dist/db/sqlite-import.js", + "backend/dist/db/sqlite-import.js.map", + "backend/dist/db/sqlite-import.d.ts", + "backend/dist/db/sqlite-import.d.ts.map", + "backend/dist/db/traffic-flags.js", + "backend/dist/db/traffic-flags.js.map", + "backend/dist/db/traffic-flags.d.ts", + "backend/dist/db/traffic-flags.d.ts.map", + "backend/dist/index.js", + "backend/dist/index.js.map", + "backend/dist/index.d.ts", + "backend/dist/index.d.ts.map", + "backend/dist/lib/permissions.js", + "backend/dist/lib/permissions.js.map", + "backend/dist/lib/permissions.d.ts", + "backend/dist/lib/permissions.d.ts.map", + "backend/dist/managed-markers.js", + "backend/dist/managed-markers.js.map", + "backend/dist/managed-markers.d.ts", + "backend/dist/managed-markers.d.ts.map", + "backend/dist/modules/events/repository/events-repository.js", + "backend/dist/modules/events/repository/events-repository.js.map", + "backend/dist/modules/events/repository/events-repository.d.ts", + "backend/dist/modules/events/repository/events-repository.d.ts.map", + "backend/dist/modules/events/service/events-service.js", + "backend/dist/modules/events/service/events-service.js.map", + "backend/dist/modules/events/service/events-service.d.ts", + "backend/dist/modules/events/service/events-service.d.ts.map", + "backend/dist/modules/servers/mapper/servers-mapper.js", + "backend/dist/modules/servers/mapper/servers-mapper.js.map", + "backend/dist/modules/servers/mapper/servers-mapper.d.ts", + "backend/dist/modules/servers/mapper/servers-mapper.d.ts.map", + "backend/dist/modules/servers/repository/servers-repository.js", + "backend/dist/modules/servers/repository/servers-repository.js.map", + "backend/dist/modules/servers/repository/servers-repository.d.ts", + "backend/dist/modules/servers/repository/servers-repository.d.ts.map", + "backend/dist/modules/servers/service/servers-service.js", + "backend/dist/modules/servers/service/servers-service.js.map", + "backend/dist/modules/servers/service/servers-service.d.ts", + "backend/dist/modules/servers/service/servers-service.d.ts.map", + "backend/dist/modules/users/iface-type.js", + "backend/dist/modules/users/iface-type.js.map", + "backend/dist/modules/users/iface-type.d.ts", + "backend/dist/modules/users/iface-type.d.ts.map", + "backend/dist/modules/users/interface-inventory.js", + "backend/dist/modules/users/interface-inventory.js.map", + "backend/dist/modules/users/interface-inventory.d.ts", + "backend/dist/modules/users/interface-inventory.d.ts.map", + "backend/dist/modules/users/peer-bind.js", + "backend/dist/modules/users/peer-bind.js.map", + "backend/dist/modules/users/peer-bind.d.ts", + "backend/dist/modules/users/peer-bind.d.ts.map", + "backend/dist/modules/users/repository/users-repository.js", + "backend/dist/modules/users/repository/users-repository.js.map", + "backend/dist/modules/users/repository/users-repository.d.ts", + "backend/dist/modules/users/repository/users-repository.d.ts.map", + "backend/dist/modules/users/service/users-service.js", + "backend/dist/modules/users/service/users-service.js.map", + "backend/dist/modules/users/service/users-service.d.ts", + "backend/dist/modules/users/service/users-service.d.ts.map", + "backend/dist/plugins/auth.js", + "backend/dist/plugins/auth.js.map", + "backend/dist/plugins/auth.d.ts", + "backend/dist/plugins/auth.d.ts.map", + "backend/dist/plugins/database-request-gate.js", + "backend/dist/plugins/database-request-gate.js.map", + "backend/dist/plugins/database-request-gate.d.ts", + "backend/dist/plugins/database-request-gate.d.ts.map", + "backend/dist/routes/alerts.js", + "backend/dist/routes/alerts.js.map", + "backend/dist/routes/alerts.d.ts", + "backend/dist/routes/alerts.d.ts.map", + "backend/dist/routes/backups.js", + "backend/dist/routes/backups.js.map", + "backend/dist/routes/backups.d.ts", + "backend/dist/routes/backups.d.ts.map", + "backend/dist/routes/bgp.js", + "backend/dist/routes/bgp.js.map", + "backend/dist/routes/bgp.d.ts", + "backend/dist/routes/bgp.d.ts.map", + "backend/dist/routes/certificates.js", + "backend/dist/routes/certificates.js.map", + "backend/dist/routes/certificates.d.ts", + "backend/dist/routes/certificates.d.ts.map", + "backend/dist/routes/containers.js", + "backend/dist/routes/containers.js.map", + "backend/dist/routes/containers.d.ts", + "backend/dist/routes/containers.d.ts.map", + "backend/dist/routes/events.js", + "backend/dist/routes/events.js.map", + "backend/dist/routes/events.d.ts", + "backend/dist/routes/events.d.ts.map", + "backend/dist/routes/evobgp-catalog.js", + "backend/dist/routes/evobgp-catalog.js.map", + "backend/dist/routes/evobgp-catalog.d.ts", + "backend/dist/routes/evobgp-catalog.d.ts.map", + "backend/dist/routes/evobgp-settings.js", + "backend/dist/routes/evobgp-settings.js.map", + "backend/dist/routes/evobgp-settings.d.ts", + "backend/dist/routes/evobgp-settings.d.ts.map", + "backend/dist/routes/evobgp.js", + "backend/dist/routes/evobgp.js.map", + "backend/dist/routes/evobgp.d.ts", + "backend/dist/routes/evobgp.d.ts.map", + "backend/dist/routes/exec.js", + "backend/dist/routes/exec.js.map", + "backend/dist/routes/exec.d.ts", + "backend/dist/routes/exec.d.ts.map", + "backend/dist/routes/filters.js", + "backend/dist/routes/filters.js.map", + "backend/dist/routes/filters.d.ts", + "backend/dist/routes/filters.d.ts.map", + "backend/dist/routes/firewall.js", + "backend/dist/routes/firewall.js.map", + "backend/dist/routes/firewall.d.ts", + "backend/dist/routes/firewall.d.ts.map", + "backend/dist/routes/geoip.js", + "backend/dist/routes/geoip.js.map", + "backend/dist/routes/geoip.d.ts", + "backend/dist/routes/geoip.d.ts.map", + "backend/dist/routes/gre.js", + "backend/dist/routes/gre.js.map", + "backend/dist/routes/gre.d.ts", + "backend/dist/routes/gre.d.ts.map", + "backend/dist/routes/internet-path.js", + "backend/dist/routes/internet-path.js.map", + "backend/dist/routes/internet-path.d.ts", + "backend/dist/routes/internet-path.d.ts.map", + "backend/dist/routes/ipsec.js", + "backend/dist/routes/ipsec.js.map", + "backend/dist/routes/ipsec.d.ts", + "backend/dist/routes/ipsec.d.ts.map", + "backend/dist/routes/network.js", + "backend/dist/routes/network.js.map", + "backend/dist/routes/network.d.ts", + "backend/dist/routes/network.d.ts.map", + "backend/dist/routes/ospf.js", + "backend/dist/routes/ospf.js.map", + "backend/dist/routes/ospf.d.ts", + "backend/dist/routes/ospf.d.ts.map", + "backend/dist/routes/ping-probes-write.js", + "backend/dist/routes/ping-probes-write.js.map", + "backend/dist/routes/ping-probes-write.d.ts", + "backend/dist/routes/ping-probes-write.d.ts.map", + "backend/dist/routes/probes.js", + "backend/dist/routes/probes.js.map", + "backend/dist/routes/probes.d.ts", + "backend/dist/routes/probes.d.ts.map", + "backend/dist/routes/recursive-routes.js", + "backend/dist/routes/recursive-routes.js.map", + "backend/dist/routes/recursive-routes.d.ts", + "backend/dist/routes/recursive-routes.d.ts.map", + "backend/dist/routes/scheduler.js", + "backend/dist/routes/scheduler.js.map", + "backend/dist/routes/scheduler.d.ts", + "backend/dist/routes/scheduler.d.ts.map", + "backend/dist/routes/servers-api-ping.js", + "backend/dist/routes/servers-api-ping.js.map", + "backend/dist/routes/servers-api-ping.d.ts", + "backend/dist/routes/servers-api-ping.d.ts.map", + "backend/dist/routes/servers.js", + "backend/dist/routes/servers.js.map", + "backend/dist/routes/servers.d.ts", + "backend/dist/routes/servers.d.ts.map", + "backend/dist/routes/sidebar-counts.js", + "backend/dist/routes/sidebar-counts.js.map", + "backend/dist/routes/sidebar-counts.d.ts", + "backend/dist/routes/sidebar-counts.d.ts.map", + "backend/dist/routes/speed-probes-write.js", + "backend/dist/routes/speed-probes-write.js.map", + "backend/dist/routes/speed-probes-write.d.ts", + "backend/dist/routes/speed-probes-write.d.ts.map", + "backend/dist/routes/speed-test.js", + "backend/dist/routes/speed-test.js.map", + "backend/dist/routes/speed-test.d.ts", + "backend/dist/routes/speed-test.d.ts.map", + "backend/dist/routes/statistics.js", + "backend/dist/routes/statistics.js.map", + "backend/dist/routes/statistics.d.ts", + "backend/dist/routes/statistics.d.ts.map", + "backend/dist/routes/system-database.js", + "backend/dist/routes/system-database.js.map", + "backend/dist/routes/system-database.d.ts", + "backend/dist/routes/system-database.d.ts.map", + "backend/dist/routes/traffic-flow-purge.js", + "backend/dist/routes/traffic-flow-purge.js.map", + "backend/dist/routes/traffic-flow-purge.d.ts", + "backend/dist/routes/traffic-flow-purge.d.ts.map", + "backend/dist/routes/traffic-flow-settings.js", + "backend/dist/routes/traffic-flow-settings.js.map", + "backend/dist/routes/traffic-flow-settings.d.ts", + "backend/dist/routes/traffic-flow-settings.d.ts.map", + "backend/dist/routes/traffic-flow.js", + "backend/dist/routes/traffic-flow.js.map", + "backend/dist/routes/traffic-flow.d.ts", + "backend/dist/routes/traffic-flow.d.ts.map", + "backend/dist/routes/traffic.js", + "backend/dist/routes/traffic.js.map", + "backend/dist/routes/traffic.d.ts", + "backend/dist/routes/traffic.d.ts.map", + "backend/dist/routes/uptime.js", + "backend/dist/routes/uptime.js.map", + "backend/dist/routes/uptime.d.ts", + "backend/dist/routes/uptime.d.ts.map", + "backend/dist/routes/users.js", + "backend/dist/routes/users.js.map", + "backend/dist/routes/users.d.ts", + "backend/dist/routes/users.d.ts.map", + "backend/dist/routes/vxlan.js", + "backend/dist/routes/vxlan.js.map", + "backend/dist/routes/vxlan.d.ts", + "backend/dist/routes/vxlan.d.ts.map", + "backend/dist/routes/wireguard.js", + "backend/dist/routes/wireguard.js.map", + "backend/dist/routes/wireguard.d.ts", + "backend/dist/routes/wireguard.d.ts.map", + "backend/dist/scripts/migrate-sqlite-to-pg.js", + "backend/dist/scripts/migrate-sqlite-to-pg.js.map", + "backend/dist/scripts/migrate-sqlite-to-pg.d.ts", + "backend/dist/scripts/migrate-sqlite-to-pg.d.ts.map", + "backend/dist/scripts/rebuild-flow-facts.js", + "backend/dist/scripts/rebuild-flow-facts.js.map", + "backend/dist/scripts/rebuild-flow-facts.d.ts", + "backend/dist/scripts/rebuild-flow-facts.d.ts.map", + "backend/dist/services/acme-cloudflare.js", + "backend/dist/services/acme-cloudflare.js.map", + "backend/dist/services/acme-cloudflare.d.ts", + "backend/dist/services/acme-cloudflare.d.ts.map", + "backend/dist/services/alert-collector-hooks.js", + "backend/dist/services/alert-collector-hooks.js.map", + "backend/dist/services/alert-collector-hooks.d.ts", + "backend/dist/services/alert-collector-hooks.d.ts.map", + "backend/dist/services/alert-engine/bgp-signals.js", + "backend/dist/services/alert-engine/bgp-signals.js.map", + "backend/dist/services/alert-engine/bgp-signals.d.ts", + "backend/dist/services/alert-engine/bgp-signals.d.ts.map", + "backend/dist/services/alert-engine/collector-context-reader.js", + "backend/dist/services/alert-engine/collector-context-reader.js.map", + "backend/dist/services/alert-engine/collector-context-reader.d.ts", + "backend/dist/services/alert-engine/collector-context-reader.d.ts.map", + "backend/dist/services/alert-engine/collector-source-signals.js", + "backend/dist/services/alert-engine/collector-source-signals.js.map", + "backend/dist/services/alert-engine/collector-source-signals.d.ts", + "backend/dist/services/alert-engine/collector-source-signals.d.ts.map", + "backend/dist/services/alert-engine/confirm-stability.js", + "backend/dist/services/alert-engine/confirm-stability.js.map", + "backend/dist/services/alert-engine/confirm-stability.d.ts", + "backend/dist/services/alert-engine/confirm-stability.d.ts.map", + "backend/dist/services/alert-engine/cooldown.js", + "backend/dist/services/alert-engine/cooldown.js.map", + "backend/dist/services/alert-engine/cooldown.d.ts", + "backend/dist/services/alert-engine/cooldown.d.ts.map", + "backend/dist/services/alert-engine/decision-engine.js", + "backend/dist/services/alert-engine/decision-engine.js.map", + "backend/dist/services/alert-engine/decision-engine.d.ts", + "backend/dist/services/alert-engine/decision-engine.d.ts.map", + "backend/dist/services/alert-engine/evaluate-rule.js", + "backend/dist/services/alert-engine/evaluate-rule.js.map", + "backend/dist/services/alert-engine/evaluate-rule.d.ts", + "backend/dist/services/alert-engine/evaluate-rule.d.ts.map", + "backend/dist/services/alert-engine/gre-signals.js", + "backend/dist/services/alert-engine/gre-signals.js.map", + "backend/dist/services/alert-engine/gre-signals.d.ts", + "backend/dist/services/alert-engine/gre-signals.d.ts.map", + "backend/dist/services/alert-engine/groups.js", + "backend/dist/services/alert-engine/groups.js.map", + "backend/dist/services/alert-engine/groups.d.ts", + "backend/dist/services/alert-engine/groups.d.ts.map", + "backend/dist/services/alert-engine/monitoring-history.js", + "backend/dist/services/alert-engine/monitoring-history.js.map", + "backend/dist/services/alert-engine/monitoring-history.d.ts", + "backend/dist/services/alert-engine/monitoring-history.d.ts.map", + "backend/dist/services/alert-engine/outbox-execution.js", + "backend/dist/services/alert-engine/outbox-execution.js.map", + "backend/dist/services/alert-engine/outbox-execution.d.ts", + "backend/dist/services/alert-engine/outbox-execution.d.ts.map", + "backend/dist/services/alert-engine/outbox.js", + "backend/dist/services/alert-engine/outbox.js.map", + "backend/dist/services/alert-engine/outbox.d.ts", + "backend/dist/services/alert-engine/outbox.d.ts.map", + "backend/dist/services/alert-engine/prev-live-store.js", + "backend/dist/services/alert-engine/prev-live-store.js.map", + "backend/dist/services/alert-engine/prev-live-store.d.ts", + "backend/dist/services/alert-engine/prev-live-store.d.ts.map", + "backend/dist/services/alert-engine/rule-matcher.js", + "backend/dist/services/alert-engine/rule-matcher.js.map", + "backend/dist/services/alert-engine/rule-matcher.d.ts", + "backend/dist/services/alert-engine/rule-matcher.d.ts.map", + "backend/dist/services/alert-engine/run-once.js", + "backend/dist/services/alert-engine/run-once.js.map", + "backend/dist/services/alert-engine/run-once.d.ts", + "backend/dist/services/alert-engine/run-once.d.ts.map", + "backend/dist/services/alert-engine/server-signal-identity.js", + "backend/dist/services/alert-engine/server-signal-identity.js.map", + "backend/dist/services/alert-engine/server-signal-identity.d.ts", + "backend/dist/services/alert-engine/server-signal-identity.d.ts.map", + "backend/dist/services/alert-engine/signal-ingestor.js", + "backend/dist/services/alert-engine/signal-ingestor.js.map", + "backend/dist/services/alert-engine/signal-ingestor.d.ts", + "backend/dist/services/alert-engine/signal-ingestor.d.ts.map", + "backend/dist/services/alert-engine/signals-from-collectors.js", + "backend/dist/services/alert-engine/signals-from-collectors.js.map", + "backend/dist/services/alert-engine/signals-from-collectors.d.ts", + "backend/dist/services/alert-engine/signals-from-collectors.d.ts.map", + "backend/dist/services/alert-engine/snapshot.js", + "backend/dist/services/alert-engine/snapshot.js.map", + "backend/dist/services/alert-engine/snapshot.d.ts", + "backend/dist/services/alert-engine/snapshot.d.ts.map", + "backend/dist/services/alert-engine/source-watermark.js", + "backend/dist/services/alert-engine/source-watermark.js.map", + "backend/dist/services/alert-engine/source-watermark.d.ts", + "backend/dist/services/alert-engine/source-watermark.d.ts.map", + "backend/dist/services/alert-engine/state-machine.js", + "backend/dist/services/alert-engine/state-machine.js.map", + "backend/dist/services/alert-engine/state-machine.d.ts", + "backend/dist/services/alert-engine/state-machine.d.ts.map", + "backend/dist/services/alert-engine/target-issues.js", + "backend/dist/services/alert-engine/target-issues.js.map", + "backend/dist/services/alert-engine/target-issues.d.ts", + "backend/dist/services/alert-engine/target-issues.d.ts.map", + "backend/dist/services/alert-engine/telegram-emoji.js", + "backend/dist/services/alert-engine/telegram-emoji.js.map", + "backend/dist/services/alert-engine/telegram-emoji.d.ts", + "backend/dist/services/alert-engine/telegram-emoji.d.ts.map", + "backend/dist/services/alert-engine/types.js", + "backend/dist/services/alert-engine/types.js.map", + "backend/dist/services/alert-engine/types.d.ts", + "backend/dist/services/alert-engine/types.d.ts.map", + "backend/dist/services/alerts-config-execution.js", + "backend/dist/services/alerts-config-execution.js.map", + "backend/dist/services/alerts-config-execution.d.ts", + "backend/dist/services/alerts-config-execution.d.ts.map", + "backend/dist/services/alerts-outbox-overview.js", + "backend/dist/services/alerts-outbox-overview.js.map", + "backend/dist/services/alerts-outbox-overview.d.ts", + "backend/dist/services/alerts-outbox-overview.d.ts.map", + "backend/dist/services/alerts-outbox-retry.js", + "backend/dist/services/alerts-outbox-retry.js.map", + "backend/dist/services/alerts-outbox-retry.d.ts", + "backend/dist/services/alerts-outbox-retry.d.ts.map", + "backend/dist/services/alerts-overview.js", + "backend/dist/services/alerts-overview.js.map", + "backend/dist/services/alerts-overview.d.ts", + "backend/dist/services/alerts-overview.d.ts.map", + "backend/dist/services/alerts-revision.js", + "backend/dist/services/alerts-revision.js.map", + "backend/dist/services/alerts-revision.d.ts", + "backend/dist/services/alerts-revision.d.ts.map", + "backend/dist/services/alerts-service.js", + "backend/dist/services/alerts-service.js.map", + "backend/dist/services/alerts-service.d.ts", + "backend/dist/services/alerts-service.d.ts.map", + "backend/dist/services/alerts-telegram-delivery.js", + "backend/dist/services/alerts-telegram-delivery.js.map", + "backend/dist/services/alerts-telegram-delivery.d.ts", + "backend/dist/services/alerts-telegram-delivery.d.ts.map", + "backend/dist/services/alerts-telegram-send.js", + "backend/dist/services/alerts-telegram-send.js.map", + "backend/dist/services/alerts-telegram-send.d.ts", + "backend/dist/services/alerts-telegram-send.d.ts.map", + "backend/dist/services/alerts-telegram-settings.js", + "backend/dist/services/alerts-telegram-settings.js.map", + "backend/dist/services/alerts-telegram-settings.d.ts", + "backend/dist/services/alerts-telegram-settings.d.ts.map", + "backend/dist/services/backup-deletion.js", + "backend/dist/services/backup-deletion.js.map", + "backend/dist/services/backup-deletion.d.ts", + "backend/dist/services/backup-deletion.d.ts.map", + "backend/dist/services/backup-dto.js", + "backend/dist/services/backup-dto.js.map", + "backend/dist/services/backup-dto.d.ts", + "backend/dist/services/backup-dto.d.ts.map", + "backend/dist/services/backup-job-execution.js", + "backend/dist/services/backup-job-execution.js.map", + "backend/dist/services/backup-job-execution.d.ts", + "backend/dist/services/backup-job-execution.d.ts.map", + "backend/dist/services/backup-prune.js", + "backend/dist/services/backup-prune.js.map", + "backend/dist/services/backup-prune.d.ts", + "backend/dist/services/backup-prune.d.ts.map", + "backend/dist/services/backup-restore.js", + "backend/dist/services/backup-restore.js.map", + "backend/dist/services/backup-restore.d.ts", + "backend/dist/services/backup-restore.d.ts.map", + "backend/dist/services/backup-scheduler-collector.js", + "backend/dist/services/backup-scheduler-collector.js.map", + "backend/dist/services/backup-scheduler-collector.d.ts", + "backend/dist/services/backup-scheduler-collector.d.ts.map", + "backend/dist/services/backup-scheduler-run.js", + "backend/dist/services/backup-scheduler-run.js.map", + "backend/dist/services/backup-scheduler-run.d.ts", + "backend/dist/services/backup-scheduler-run.d.ts.map", + "backend/dist/services/backup-service.js", + "backend/dist/services/backup-service.js.map", + "backend/dist/services/backup-service.d.ts", + "backend/dist/services/backup-service.d.ts.map", + "backend/dist/services/backup-storage-update.js", + "backend/dist/services/backup-storage-update.js.map", + "backend/dist/services/backup-storage-update.d.ts", + "backend/dist/services/backup-storage-update.d.ts.map", + "backend/dist/services/bandwidth-test-rates.js", + "backend/dist/services/bandwidth-test-rates.js.map", + "backend/dist/services/bandwidth-test-rates.d.ts", + "backend/dist/services/bandwidth-test-rates.d.ts.map", + "backend/dist/services/bandwidth-test-status.js", + "backend/dist/services/bandwidth-test-status.js.map", + "backend/dist/services/bandwidth-test-status.d.ts", + "backend/dist/services/bandwidth-test-status.d.ts.map", + "backend/dist/services/bgp-catalog-poll.js", + "backend/dist/services/bgp-catalog-poll.js.map", + "backend/dist/services/bgp-catalog-poll.d.ts", + "backend/dist/services/bgp-catalog-poll.d.ts.map", + "backend/dist/services/bgp-parse-sessions.js", + "backend/dist/services/bgp-parse-sessions.js.map", + "backend/dist/services/bgp-parse-sessions.d.ts", + "backend/dist/services/bgp-parse-sessions.d.ts.map", + "backend/dist/services/bgp-peers-live.js", + "backend/dist/services/bgp-peers-live.js.map", + "backend/dist/services/bgp-peers-live.d.ts", + "backend/dist/services/bgp-peers-live.d.ts.map", + "backend/dist/services/btest-node-locks.js", + "backend/dist/services/btest-node-locks.js.map", + "backend/dist/services/btest-node-locks.d.ts", + "backend/dist/services/btest-node-locks.d.ts.map", + "backend/dist/services/certificate-issue-dto.js", + "backend/dist/services/certificate-issue-dto.js.map", + "backend/dist/services/certificate-issue-dto.d.ts", + "backend/dist/services/certificate-issue-dto.d.ts.map", + "backend/dist/services/certificate-issue-execution.js", + "backend/dist/services/certificate-issue-execution.js.map", + "backend/dist/services/certificate-issue-execution.d.ts", + "backend/dist/services/certificate-issue-execution.d.ts.map", + "backend/dist/services/certificate-issue-request.js", + "backend/dist/services/certificate-issue-request.js.map", + "backend/dist/services/certificate-issue-request.d.ts", + "backend/dist/services/certificate-issue-request.d.ts.map", + "backend/dist/services/certificate-issue-runner.js", + "backend/dist/services/certificate-issue-runner.js.map", + "backend/dist/services/certificate-issue-runner.d.ts", + "backend/dist/services/certificate-issue-runner.d.ts.map", + "backend/dist/services/certificate-parse.js", + "backend/dist/services/certificate-parse.js.map", + "backend/dist/services/certificate-parse.d.ts", + "backend/dist/services/certificate-parse.d.ts.map", + "backend/dist/services/certificate-renew-collector.js", + "backend/dist/services/certificate-renew-collector.js.map", + "backend/dist/services/certificate-renew-collector.d.ts", + "backend/dist/services/certificate-renew-collector.d.ts.map", + "backend/dist/services/certificates-service.js", + "backend/dist/services/certificates-service.js.map", + "backend/dist/services/certificates-service.d.ts", + "backend/dist/services/certificates-service.d.ts.map", + "backend/dist/services/certificates-state.js", + "backend/dist/services/certificates-state.js.map", + "backend/dist/services/certificates-state.d.ts", + "backend/dist/services/certificates-state.d.ts.map", + "backend/dist/services/complete-count.js", + "backend/dist/services/complete-count.js.map", + "backend/dist/services/complete-count.d.ts", + "backend/dist/services/complete-count.d.ts.map", + "backend/dist/services/config-apply-plan.js", + "backend/dist/services/config-apply-plan.js.map", + "backend/dist/services/config-apply-plan.d.ts", + "backend/dist/services/config-apply-plan.d.ts.map", + "backend/dist/services/config-revisions.js", + "backend/dist/services/config-revisions.js.map", + "backend/dist/services/config-revisions.d.ts", + "backend/dist/services/config-revisions.d.ts.map", + "backend/dist/services/containers-live.js", + "backend/dist/services/containers-live.js.map", + "backend/dist/services/containers-live.d.ts", + "backend/dist/services/containers-live.d.ts.map", + "backend/dist/services/containers-state.js", + "backend/dist/services/containers-state.js.map", + "backend/dist/services/containers-state.d.ts", + "backend/dist/services/containers-state.d.ts.map", + "backend/dist/services/database-background-work.js", + "backend/dist/services/database-background-work.js.map", + "backend/dist/services/database-background-work.d.ts", + "backend/dist/services/database-background-work.d.ts.map", + "backend/dist/services/database-request-gate.js", + "backend/dist/services/database-request-gate.js.map", + "backend/dist/services/database-request-gate.d.ts", + "backend/dist/services/database-request-gate.d.ts.map", + "backend/dist/services/entity-snapshots.js", + "backend/dist/services/entity-snapshots.js.map", + "backend/dist/services/entity-snapshots.d.ts", + "backend/dist/services/entity-snapshots.d.ts.map", + "backend/dist/services/evobgp-catalog.js", + "backend/dist/services/evobgp-catalog.js.map", + "backend/dist/services/evobgp-catalog.d.ts", + "backend/dist/services/evobgp-catalog.d.ts.map", + "backend/dist/services/firewall-live.js", + "backend/dist/services/firewall-live.js.map", + "backend/dist/services/firewall-live.d.ts", + "backend/dist/services/firewall-live.d.ts.map", + "backend/dist/services/firewall-poll.js", + "backend/dist/services/firewall-poll.js.map", + "backend/dist/services/firewall-poll.d.ts", + "backend/dist/services/firewall-poll.d.ts.map", + "backend/dist/services/geoip-settings.js", + "backend/dist/services/geoip-settings.js.map", + "backend/dist/services/geoip-settings.d.ts", + "backend/dist/services/geoip-settings.d.ts.map", + "backend/dist/services/geoip-update-collector.js", + "backend/dist/services/geoip-update-collector.js.map", + "backend/dist/services/geoip-update-collector.d.ts", + "backend/dist/services/geoip-update-collector.d.ts.map", + "backend/dist/services/gre-bgp-snapshot-collector.js", + "backend/dist/services/gre-bgp-snapshot-collector.js.map", + "backend/dist/services/gre-bgp-snapshot-collector.d.ts", + "backend/dist/services/gre-bgp-snapshot-collector.d.ts.map", + "backend/dist/services/gre-bgp-snapshot-history.js", + "backend/dist/services/gre-bgp-snapshot-history.js.map", + "backend/dist/services/gre-bgp-snapshot-history.d.ts", + "backend/dist/services/gre-bgp-snapshot-history.d.ts.map", + "backend/dist/services/gre-bgp-timing.js", + "backend/dist/services/gre-bgp-timing.js.map", + "backend/dist/services/gre-bgp-timing.d.ts", + "backend/dist/services/gre-bgp-timing.d.ts.map", + "backend/dist/services/gre-catalog-poll.js", + "backend/dist/services/gre-catalog-poll.js.map", + "backend/dist/services/gre-catalog-poll.d.ts", + "backend/dist/services/gre-catalog-poll.d.ts.map", + "backend/dist/services/gre-comment.js", + "backend/dist/services/gre-comment.js.map", + "backend/dist/services/gre-comment.d.ts", + "backend/dist/services/gre-comment.d.ts.map", + "backend/dist/services/gre-live.js", + "backend/dist/services/gre-live.js.map", + "backend/dist/services/gre-live.d.ts", + "backend/dist/services/gre-live.d.ts.map", + "backend/dist/services/gre-tunnels-live.js", + "backend/dist/services/gre-tunnels-live.js.map", + "backend/dist/services/gre-tunnels-live.d.ts", + "backend/dist/services/gre-tunnels-live.d.ts.map", + "backend/dist/services/internet-path-collector.js", + "backend/dist/services/internet-path-collector.js.map", + "backend/dist/services/internet-path-collector.d.ts", + "backend/dist/services/internet-path-collector.d.ts.map", + "backend/dist/services/internet-path-snapshot.js", + "backend/dist/services/internet-path-snapshot.js.map", + "backend/dist/services/internet-path-snapshot.d.ts", + "backend/dist/services/internet-path-snapshot.d.ts.map", + "backend/dist/services/ipsec-ca.js", + "backend/dist/services/ipsec-ca.js.map", + "backend/dist/services/ipsec-ca.d.ts", + "backend/dist/services/ipsec-ca.d.ts.map", + "backend/dist/services/ipsec-certificate-dn.js", + "backend/dist/services/ipsec-certificate-dn.js.map", + "backend/dist/services/ipsec-certificate-dn.d.ts", + "backend/dist/services/ipsec-certificate-dn.d.ts.map", + "backend/dist/services/ipsec-cleanup.js", + "backend/dist/services/ipsec-cleanup.js.map", + "backend/dist/services/ipsec-cleanup.d.ts", + "backend/dist/services/ipsec-cleanup.d.ts.map", + "backend/dist/services/ipsec-config.js", + "backend/dist/services/ipsec-config.js.map", + "backend/dist/services/ipsec-config.d.ts", + "backend/dist/services/ipsec-config.d.ts.map", + "backend/dist/services/ipsec-live.js", + "backend/dist/services/ipsec-live.js.map", + "backend/dist/services/ipsec-live.d.ts", + "backend/dist/services/ipsec-live.d.ts.map", + "backend/dist/services/ipsec-operation.js", + "backend/dist/services/ipsec-operation.js.map", + "backend/dist/services/ipsec-operation.d.ts", + "backend/dist/services/ipsec-operation.d.ts.map", + "backend/dist/services/ipsec-ros.js", + "backend/dist/services/ipsec-ros.js.map", + "backend/dist/services/ipsec-ros.d.ts", + "backend/dist/services/ipsec-ros.d.ts.map", + "backend/dist/services/mikrotik.js", + "backend/dist/services/mikrotik.js.map", + "backend/dist/services/mikrotik.d.ts", + "backend/dist/services/mikrotik.d.ts.map", + "backend/dist/services/monitoring-server-catalog.js", + "backend/dist/services/monitoring-server-catalog.js.map", + "backend/dist/services/monitoring-server-catalog.d.ts", + "backend/dist/services/monitoring-server-catalog.d.ts.map", + "backend/dist/services/ospf-catalog-poll.js", + "backend/dist/services/ospf-catalog-poll.js.map", + "backend/dist/services/ospf-catalog-poll.d.ts", + "backend/dist/services/ospf-catalog-poll.d.ts.map", + "backend/dist/services/ospf-effective-disabled.js", + "backend/dist/services/ospf-effective-disabled.js.map", + "backend/dist/services/ospf-effective-disabled.d.ts", + "backend/dist/services/ospf-effective-disabled.d.ts.map", + "backend/dist/services/ospf-observed-values.js", + "backend/dist/services/ospf-observed-values.js.map", + "backend/dist/services/ospf-observed-values.d.ts", + "backend/dist/services/ospf-observed-values.d.ts.map", + "backend/dist/services/ospf-optimizer-identity.js", + "backend/dist/services/ospf-optimizer-identity.js.map", + "backend/dist/services/ospf-optimizer-identity.d.ts", + "backend/dist/services/ospf-optimizer-identity.d.ts.map", + "backend/dist/services/ospf-optimizer-metrics.js", + "backend/dist/services/ospf-optimizer-metrics.js.map", + "backend/dist/services/ospf-optimizer-metrics.d.ts", + "backend/dist/services/ospf-optimizer-metrics.d.ts.map", + "backend/dist/services/ospf-optimizer-plan.js", + "backend/dist/services/ospf-optimizer-plan.js.map", + "backend/dist/services/ospf-optimizer-plan.d.ts", + "backend/dist/services/ospf-optimizer-plan.d.ts.map", + "backend/dist/services/ospf-route-parse.js", + "backend/dist/services/ospf-route-parse.js.map", + "backend/dist/services/ospf-route-parse.d.ts", + "backend/dist/services/ospf-route-parse.d.ts.map", + "backend/dist/services/ping-probe-replacement.js", + "backend/dist/services/ping-probe-replacement.js.map", + "backend/dist/services/ping-probe-replacement.d.ts", + "backend/dist/services/ping-probe-replacement.d.ts.map", + "backend/dist/services/poller.js", + "backend/dist/services/poller.js.map", + "backend/dist/services/poller.d.ts", + "backend/dist/services/poller.d.ts.map", + "backend/dist/services/recursive-apply.js", + "backend/dist/services/recursive-apply.js.map", + "backend/dist/services/recursive-apply.d.ts", + "backend/dist/services/recursive-apply.d.ts.map", + "backend/dist/services/recursive-route-catalog.js", + "backend/dist/services/recursive-route-catalog.js.map", + "backend/dist/services/recursive-route-catalog.d.ts", + "backend/dist/services/recursive-route-catalog.d.ts.map", + "backend/dist/services/recursive-route-validation.js", + "backend/dist/services/recursive-route-validation.js.map", + "backend/dist/services/recursive-route-validation.d.ts", + "backend/dist/services/recursive-route-validation.d.ts.map", + "backend/dist/services/ros-metric-parse.js", + "backend/dist/services/ros-metric-parse.js.map", + "backend/dist/services/ros-metric-parse.d.ts", + "backend/dist/services/ros-metric-parse.d.ts.map", + "backend/dist/services/ros-ops.js", + "backend/dist/services/ros-ops.js.map", + "backend/dist/services/ros-ops.d.ts", + "backend/dist/services/ros-ops.d.ts.map", + "backend/dist/services/s3-backup-client.js", + "backend/dist/services/s3-backup-client.js.map", + "backend/dist/services/s3-backup-client.d.ts", + "backend/dist/services/s3-backup-client.d.ts.map", + "backend/dist/services/scheduler-recorded-run.js", + "backend/dist/services/scheduler-recorded-run.js.map", + "backend/dist/services/scheduler-recorded-run.d.ts", + "backend/dist/services/scheduler-recorded-run.d.ts.map", + "backend/dist/services/scheduler-running.js", + "backend/dist/services/scheduler-running.js.map", + "backend/dist/services/scheduler-running.d.ts", + "backend/dist/services/scheduler-running.d.ts.map", + "backend/dist/services/scheduler.js", + "backend/dist/services/scheduler.js.map", + "backend/dist/services/scheduler.d.ts", + "backend/dist/services/scheduler.d.ts.map", + "backend/dist/services/serial-operation.js", + "backend/dist/services/serial-operation.js.map", + "backend/dist/services/serial-operation.d.ts", + "backend/dist/services/serial-operation.d.ts.map", + "backend/dist/services/servers-rest-ping-collector.js", + "backend/dist/services/servers-rest-ping-collector.js.map", + "backend/dist/services/servers-rest-ping-collector.d.ts", + "backend/dist/services/servers-rest-ping-collector.d.ts.map", + "backend/dist/services/speed-history-dto.js", + "backend/dist/services/speed-history-dto.js.map", + "backend/dist/services/speed-history-dto.d.ts", + "backend/dist/services/speed-history-dto.d.ts.map", + "backend/dist/services/speed-probe-dto.js", + "backend/dist/services/speed-probe-dto.js.map", + "backend/dist/services/speed-probe-dto.d.ts", + "backend/dist/services/speed-probe-dto.d.ts.map", + "backend/dist/services/speed-probe-replacement.js", + "backend/dist/services/speed-probe-replacement.js.map", + "backend/dist/services/speed-probe-replacement.d.ts", + "backend/dist/services/speed-probe-replacement.d.ts.map", + "backend/dist/services/speed-run-error-message.js", + "backend/dist/services/speed-run-error-message.js.map", + "backend/dist/services/speed-run-error-message.d.ts", + "backend/dist/services/speed-run-error-message.d.ts.map", + "backend/dist/services/speed-run-persistence.js", + "backend/dist/services/speed-run-persistence.js.map", + "backend/dist/services/speed-run-persistence.d.ts", + "backend/dist/services/speed-run-persistence.d.ts.map", + "backend/dist/services/statistics-aggregate.js", + "backend/dist/services/statistics-aggregate.js.map", + "backend/dist/services/statistics-aggregate.d.ts", + "backend/dist/services/statistics-aggregate.d.ts.map", + "backend/dist/services/system-database-backup.js", + "backend/dist/services/system-database-backup.js.map", + "backend/dist/services/system-database-backup.d.ts", + "backend/dist/services/system-database-backup.d.ts.map", + "backend/dist/services/system-database-cache.js", + "backend/dist/services/system-database-cache.js.map", + "backend/dist/services/system-database-cache.d.ts", + "backend/dist/services/system-database-cache.d.ts.map", + "backend/dist/services/system-database-operation.js", + "backend/dist/services/system-database-operation.js.map", + "backend/dist/services/system-database-operation.d.ts", + "backend/dist/services/system-database-operation.d.ts.map", + "backend/dist/services/system-database-partition-restore.js", + "backend/dist/services/system-database-partition-restore.js.map", + "backend/dist/services/system-database-partition-restore.d.ts", + "backend/dist/services/system-database-partition-restore.d.ts.map", + "backend/dist/services/system-database-restore-execution.js", + "backend/dist/services/system-database-restore-execution.js.map", + "backend/dist/services/system-database-restore-execution.d.ts", + "backend/dist/services/system-database-restore-execution.d.ts.map", + "backend/dist/services/traffic-collection-state.js", + "backend/dist/services/traffic-collection-state.js.map", + "backend/dist/services/traffic-collection-state.d.ts", + "backend/dist/services/traffic-collection-state.d.ts.map", + "backend/dist/services/traffic-collector.js", + "backend/dist/services/traffic-collector.js.map", + "backend/dist/services/traffic-collector.d.ts", + "backend/dist/services/traffic-collector.d.ts.map", + "backend/dist/services/traffic-flow-analytics.js", + "backend/dist/services/traffic-flow-analytics.js.map", + "backend/dist/services/traffic-flow-analytics.d.ts", + "backend/dist/services/traffic-flow-analytics.d.ts.map", + "backend/dist/services/traffic-flow-apps.js", + "backend/dist/services/traffic-flow-apps.js.map", + "backend/dist/services/traffic-flow-apps.d.ts", + "backend/dist/services/traffic-flow-apps.d.ts.map", + "backend/dist/services/traffic-flow-brands.js", + "backend/dist/services/traffic-flow-brands.js.map", + "backend/dist/services/traffic-flow-brands.d.ts", + "backend/dist/services/traffic-flow-brands.d.ts.map", + "backend/dist/services/traffic-flow-classify.js", + "backend/dist/services/traffic-flow-classify.js.map", + "backend/dist/services/traffic-flow-classify.d.ts", + "backend/dist/services/traffic-flow-classify.d.ts.map", + "backend/dist/services/traffic-flow-collector-ipc.js", + "backend/dist/services/traffic-flow-collector-ipc.js.map", + "backend/dist/services/traffic-flow-collector-ipc.d.ts", + "backend/dist/services/traffic-flow-collector-ipc.d.ts.map", + "backend/dist/services/traffic-flow-collector-worker.js", + "backend/dist/services/traffic-flow-collector-worker.js.map", + "backend/dist/services/traffic-flow-collector-worker.d.ts", + "backend/dist/services/traffic-flow-collector-worker.d.ts.map", + "backend/dist/services/traffic-flow-dedup.js", + "backend/dist/services/traffic-flow-dedup.js.map", + "backend/dist/services/traffic-flow-dedup.d.ts", + "backend/dist/services/traffic-flow-dedup.d.ts.map", + "backend/dist/services/traffic-flow-dest.js", + "backend/dist/services/traffic-flow-dest.js.map", + "backend/dist/services/traffic-flow-dest.d.ts", + "backend/dist/services/traffic-flow-dest.d.ts.map", + "backend/dist/services/traffic-flow-engine.js", + "backend/dist/services/traffic-flow-engine.js.map", + "backend/dist/services/traffic-flow-engine.d.ts", + "backend/dist/services/traffic-flow-engine.d.ts.map", + "backend/dist/services/traffic-flow-facts-filter.js", + "backend/dist/services/traffic-flow-facts-filter.js.map", + "backend/dist/services/traffic-flow-facts-filter.d.ts", + "backend/dist/services/traffic-flow-facts-filter.d.ts.map", + "backend/dist/services/traffic-flow-facts-rebuild.js", + "backend/dist/services/traffic-flow-facts-rebuild.js.map", + "backend/dist/services/traffic-flow-facts-rebuild.d.ts", + "backend/dist/services/traffic-flow-facts-rebuild.d.ts.map", + "backend/dist/services/traffic-flow-facts.js", + "backend/dist/services/traffic-flow-facts.js.map", + "backend/dist/services/traffic-flow-facts.d.ts", + "backend/dist/services/traffic-flow-facts.d.ts.map", + "backend/dist/services/traffic-flow-geoip.js", + "backend/dist/services/traffic-flow-geoip.js.map", + "backend/dist/services/traffic-flow-geoip.d.ts", + "backend/dist/services/traffic-flow-geoip.d.ts.map", + "backend/dist/services/traffic-flow-host-file-builders.js", + "backend/dist/services/traffic-flow-host-file-builders.js.map", + "backend/dist/services/traffic-flow-host-file-builders.d.ts", + "backend/dist/services/traffic-flow-host-file-builders.d.ts.map", + "backend/dist/services/traffic-flow-host-files.js", + "backend/dist/services/traffic-flow-host-files.js.map", + "backend/dist/services/traffic-flow-host-files.d.ts", + "backend/dist/services/traffic-flow-host-files.d.ts.map", + "backend/dist/services/traffic-flow-ifaces.js", + "backend/dist/services/traffic-flow-ifaces.js.map", + "backend/dist/services/traffic-flow-ifaces.d.ts", + "backend/dist/services/traffic-flow-ifaces.d.ts.map", + "backend/dist/services/traffic-flow-ifindex.js", + "backend/dist/services/traffic-flow-ifindex.js.map", + "backend/dist/services/traffic-flow-ifindex.d.ts", + "backend/dist/services/traffic-flow-ifindex.d.ts.map", + "backend/dist/services/traffic-flow-ingest.js", + "backend/dist/services/traffic-flow-ingest.js.map", + "backend/dist/services/traffic-flow-ingest.d.ts", + "backend/dist/services/traffic-flow-ingest.d.ts.map", + "backend/dist/services/traffic-flow-ip.js", + "backend/dist/services/traffic-flow-ip.js.map", + "backend/dist/services/traffic-flow-ip.d.ts", + "backend/dist/services/traffic-flow-ip.d.ts.map", + "backend/dist/services/traffic-flow-key-execution.js", + "backend/dist/services/traffic-flow-key-execution.js.map", + "backend/dist/services/traffic-flow-key-execution.d.ts", + "backend/dist/services/traffic-flow-key-execution.d.ts.map", + "backend/dist/services/traffic-flow-map-exporter.js", + "backend/dist/services/traffic-flow-map-exporter.js.map", + "backend/dist/services/traffic-flow-map-exporter.d.ts", + "backend/dist/services/traffic-flow-map-exporter.d.ts.map", + "backend/dist/services/traffic-flow-map-hops.js", + "backend/dist/services/traffic-flow-map-hops.js.map", + "backend/dist/services/traffic-flow-map-hops.d.ts", + "backend/dist/services/traffic-flow-map-hops.d.ts.map", + "backend/dist/services/traffic-flow-overlay.js", + "backend/dist/services/traffic-flow-overlay.js.map", + "backend/dist/services/traffic-flow-overlay.d.ts", + "backend/dist/services/traffic-flow-overlay.d.ts.map", + "backend/dist/services/traffic-flow-parse.js", + "backend/dist/services/traffic-flow-parse.js.map", + "backend/dist/services/traffic-flow-parse.d.ts", + "backend/dist/services/traffic-flow-parse.d.ts.map", + "backend/dist/services/traffic-flow-planes.js", + "backend/dist/services/traffic-flow-planes.js.map", + "backend/dist/services/traffic-flow-planes.d.ts", + "backend/dist/services/traffic-flow-planes.d.ts.map", + "backend/dist/services/traffic-flow-purge-execution.js", + "backend/dist/services/traffic-flow-purge-execution.js.map", + "backend/dist/services/traffic-flow-purge-execution.d.ts", + "backend/dist/services/traffic-flow-purge-execution.d.ts.map", + "backend/dist/services/traffic-flow-ripe.js", + "backend/dist/services/traffic-flow-ripe.js.map", + "backend/dist/services/traffic-flow-ripe.d.ts", + "backend/dist/services/traffic-flow-ripe.d.ts.map", + "backend/dist/services/traffic-flow-settings.js", + "backend/dist/services/traffic-flow-settings.js.map", + "backend/dist/services/traffic-flow-settings.d.ts", + "backend/dist/services/traffic-flow-settings.d.ts.map", + "backend/dist/services/traffic-flow-topology.js", + "backend/dist/services/traffic-flow-topology.js.map", + "backend/dist/services/traffic-flow-topology.d.ts", + "backend/dist/services/traffic-flow-topology.d.ts.map", + "backend/dist/services/traffic-flow-worker-shutdown.js", + "backend/dist/services/traffic-flow-worker-shutdown.js.map", + "backend/dist/services/traffic-flow-worker-shutdown.d.ts", + "backend/dist/services/traffic-flow-worker-shutdown.d.ts.map", + "backend/dist/services/traffic-freshness.js", + "backend/dist/services/traffic-freshness.js.map", + "backend/dist/services/traffic-freshness.d.ts", + "backend/dist/services/traffic-freshness.d.ts.map", + "backend/dist/services/traffic-observation.js", + "backend/dist/services/traffic-observation.js.map", + "backend/dist/services/traffic-observation.d.ts", + "backend/dist/services/traffic-observation.d.ts.map", + "backend/dist/services/traffic-rate.js", + "backend/dist/services/traffic-rate.js.map", + "backend/dist/services/traffic-rate.d.ts", + "backend/dist/services/traffic-rate.d.ts.map", + "backend/dist/services/traffic-users.js", + "backend/dist/services/traffic-users.js.map", + "backend/dist/services/traffic-users.d.ts", + "backend/dist/services/traffic-users.d.ts.map", + "backend/dist/services/uptime-collector.js", + "backend/dist/services/uptime-collector.js.map", + "backend/dist/services/uptime-collector.d.ts", + "backend/dist/services/uptime-collector.d.ts.map", + "backend/dist/services/uptime-probe-observations.js", + "backend/dist/services/uptime-probe-observations.js.map", + "backend/dist/services/uptime-probe-observations.d.ts", + "backend/dist/services/uptime-probe-observations.d.ts.map", + "backend/dist/services/uptime-speed-service.js", + "backend/dist/services/uptime-speed-service.js.map", + "backend/dist/services/uptime-speed-service.d.ts", + "backend/dist/services/uptime-speed-service.d.ts.map", + "backend/dist/services/vxlan-live.js", + "backend/dist/services/vxlan-live.js.map", + "backend/dist/services/vxlan-live.d.ts", + "backend/dist/services/vxlan-live.d.ts.map", + "backend/dist/services/wg-keys.js", + "backend/dist/services/wg-keys.js.map", + "backend/dist/services/wg-keys.d.ts", + "backend/dist/services/wg-keys.d.ts.map", + "backend/dist/services/wireguard-config.js", + "backend/dist/services/wireguard-config.js.map", + "backend/dist/services/wireguard-config.d.ts", + "backend/dist/services/wireguard-config.d.ts.map", + "backend/dist/services/wireguard-delete.js", + "backend/dist/services/wireguard-delete.js.map", + "backend/dist/services/wireguard-delete.d.ts", + "backend/dist/services/wireguard-delete.d.ts.map", + "backend/dist/services/wireguard-import-apply.js", + "backend/dist/services/wireguard-import-apply.js.map", + "backend/dist/services/wireguard-import-apply.d.ts", + "backend/dist/services/wireguard-import-apply.d.ts.map", + "backend/dist/services/wireguard-import-revision.js", + "backend/dist/services/wireguard-import-revision.js.map", + "backend/dist/services/wireguard-import-revision.d.ts", + "backend/dist/services/wireguard-import-revision.d.ts.map", + "backend/dist/services/wireguard-import.js", + "backend/dist/services/wireguard-import.js.map", + "backend/dist/services/wireguard-import.d.ts", + "backend/dist/services/wireguard-import.d.ts.map", + "backend/dist/services/wireguard-live.js", + "backend/dist/services/wireguard-live.js.map", + "backend/dist/services/wireguard-live.d.ts", + "backend/dist/services/wireguard-live.d.ts.map", + "backend/dist/services/wireguard-observed-values.js", + "backend/dist/services/wireguard-observed-values.js.map", + "backend/dist/services/wireguard-observed-values.d.ts", + "backend/dist/services/wireguard-observed-values.d.ts.map", + "backend/dist/services/wireguard-ros.js", + "backend/dist/services/wireguard-ros.js.map", + "backend/dist/services/wireguard-ros.d.ts", + "backend/dist/services/wireguard-ros.d.ts.map", + "backend/dist/test/pg.js", + "backend/dist/test/pg.js.map", + "backend/dist/test/pg.d.ts", + "backend/dist/test/pg.d.ts.map", + "backend/dist/types/scheduler-run-snapshot.js", + "backend/dist/types/scheduler-run-snapshot.js.map", + "backend/dist/types/scheduler-run-snapshot.d.ts", + "backend/dist/types/scheduler-run-snapshot.d.ts.map", + "backend/dist/types/server.js", + "backend/dist/types/server.js.map", + "backend/dist/types/server.d.ts", + "backend/dist/types/server.d.ts.map", + "backend/dist/utils/abort-signals.js", + "backend/dist/utils/abort-signals.js.map", + "backend/dist/utils/abort-signals.d.ts", + "backend/dist/utils/abort-signals.d.ts.map", + "backend/dist/utils/ros-src-address.js", + "backend/dist/utils/ros-src-address.js.map", + "backend/dist/utils/ros-src-address.d.ts", + "backend/dist/utils/ros-src-address.d.ts.map", + "backend/dist/utils/server-id.js", + "backend/dist/utils/server-id.js.map", + "backend/dist/utils/server-id.d.ts", + "backend/dist/utils/server-id.d.ts.map", + "packages/contracts/dist/alerts.js", + "packages/contracts/dist/alerts.js.map", + "packages/contracts/dist/alerts.d.ts", + "packages/contracts/dist/alerts.d.ts.map", + "packages/contracts/dist/backups.js", + "packages/contracts/dist/backups.js.map", + "packages/contracts/dist/backups.d.ts", + "packages/contracts/dist/backups.d.ts.map", + "packages/contracts/dist/certificates.js", + "packages/contracts/dist/certificates.js.map", + "packages/contracts/dist/certificates.d.ts", + "packages/contracts/dist/certificates.d.ts.map", + "packages/contracts/dist/containers.js", + "packages/contracts/dist/containers.js.map", + "packages/contracts/dist/containers.d.ts", + "packages/contracts/dist/containers.d.ts.map", + "packages/contracts/dist/events.js", + "packages/contracts/dist/events.js.map", + "packages/contracts/dist/events.d.ts", + "packages/contracts/dist/events.d.ts.map", + "packages/contracts/dist/evobgp.js", + "packages/contracts/dist/evobgp.js.map", + "packages/contracts/dist/evobgp.d.ts", + "packages/contracts/dist/evobgp.d.ts.map", + "packages/contracts/dist/geoip.js", + "packages/contracts/dist/geoip.js.map", + "packages/contracts/dist/geoip.d.ts", + "packages/contracts/dist/geoip.d.ts.map", + "packages/contracts/dist/index.js", + "packages/contracts/dist/index.js.map", + "packages/contracts/dist/index.d.ts", + "packages/contracts/dist/index.d.ts.map", + "packages/contracts/dist/ipsec.js", + "packages/contracts/dist/ipsec.js.map", + "packages/contracts/dist/ipsec.d.ts", + "packages/contracts/dist/ipsec.d.ts.map", + "packages/contracts/dist/ospf-optimizer.js", + "packages/contracts/dist/ospf-optimizer.js.map", + "packages/contracts/dist/ospf-optimizer.d.ts", + "packages/contracts/dist/ospf-optimizer.d.ts.map", + "packages/contracts/dist/servers.js", + "packages/contracts/dist/servers.js.map", + "packages/contracts/dist/servers.d.ts", + "packages/contracts/dist/servers.d.ts.map", + "packages/contracts/dist/statistics.js", + "packages/contracts/dist/statistics.js.map", + "packages/contracts/dist/statistics.d.ts", + "packages/contracts/dist/statistics.d.ts.map", + "packages/contracts/dist/traffic-flow.js", + "packages/contracts/dist/traffic-flow.js.map", + "packages/contracts/dist/traffic-flow.d.ts", + "packages/contracts/dist/traffic-flow.d.ts.map", + "packages/contracts/dist/uptime-probes.js", + "packages/contracts/dist/uptime-probes.js.map", + "packages/contracts/dist/uptime-probes.d.ts", + "packages/contracts/dist/uptime-probes.d.ts.map", + "packages/contracts/dist/uptime-speed.js", + "packages/contracts/dist/uptime-speed.js.map", + "packages/contracts/dist/uptime-speed.d.ts", + "packages/contracts/dist/uptime-speed.d.ts.map", + "packages/contracts/dist/users.js", + "packages/contracts/dist/users.js.map", + "packages/contracts/dist/users.d.ts", + "packages/contracts/dist/users.d.ts.map", + "packages/contracts/dist/wireguard.js", + "packages/contracts/dist/wireguard.js.map", + "packages/contracts/dist/wireguard.d.ts", + "packages/contracts/dist/wireguard.d.ts.map" + ], + "missing": [], + "different": [] +} \ No newline at end of file diff --git a/docs/ux-ui/acceptance/traffic-live-deployed-2026-10-12.json b/docs/ux-ui/acceptance/traffic-live-deployed-2026-10-12.json new file mode 100644 index 00000000..4f20e6e1 --- /dev/null +++ b/docs/ux-ui/acceptance/traffic-live-deployed-2026-10-12.json @@ -0,0 +1,46 @@ +{ + "date": "2026-10-12", + "block": "12.5.179", + "runtime": { + "backend": "ux-live179", + "frontend": "ux-live179" + }, + "apis": [ + { + "path": "/api/traffic/servers", + "status": 200 + }, + { + "path": "/api/traffic/users", + "status": 200 + }, + { + "path": "/api/traffic/bound-interfaces", + "status": 200 + } + ], + "cases": [ + { + "width": 1440, + "trafficServers200": true, + "attemptProvenanceVisible": true, + "actualLiveSample": true, + "backendReceiptLabel": true, + "pageErrors": 0, + "noOverflow": true + }, + { + "width": 320, + "trafficServers200": true, + "attemptProvenanceVisible": true, + "actualLiveSample": true, + "backendReceiptLabel": true, + "pageErrors": 0, + "noOverflow": true + } + ], + "bounds": [ + "Read-only smoke; error/stale/legacy branches proven by owned SQL and UI fixtures", + "No manual production scheduler/config/password/message writes" + ] +} diff --git a/docs/ux-ui/acceptance/traffic-live-interop-2026-10-12.json b/docs/ux-ui/acceptance/traffic-live-interop-2026-10-12.json new file mode 100644 index 00000000..4f300dba --- /dev/null +++ b/docs/ux-ui/acceptance/traffic-live-interop-2026-10-12.json @@ -0,0 +1,10 @@ +{ + "date": "2026-10-12", + "block": "12.5.179", + "newFrontend": "ux-live179", + "oldBackend": "ux-history178", + "readerAccepted": true, + "serverCount": 2, + "pageErrors": 0, + "bound": "Additive observed DTO reader compatibility; legacy numeric-only paths verified separately as unknown" +} \ No newline at end of file diff --git a/docs/ux-ui/acceptance/traffic-live-native-2026-10-12.json b/docs/ux-ui/acceptance/traffic-live-native-2026-10-12.json new file mode 100644 index 00000000..79f4d520 --- /dev/null +++ b/docs/ux-ui/acceptance/traffic-live-native-2026-10-12.json @@ -0,0 +1,32 @@ +{ + "date": "2026-10-12", + "block": "12.5.179", + "version": "1.0.0-test.live179", + "method": "Two actual application TCP SSE GET streams; native interface GET and monitor-traffic read-only POST; stop after first confirmed point", + "cases": [ + { + "sourceId": 1, + "sample": { + "rxMbps": 0.031584, + "txMbps": 0.002016, + "at": "2026-10-12T14:31:58.084Z", + "timestampKind": "backend-receipt" + }, + "streamStatus": 200 + }, + { + "sourceId": 2, + "sample": { + "rxMbps": 0.002112, + "txMbps": 0.002112, + "at": "2026-10-12T14:31:58.237Z", + "timestampKind": "backend-receipt" + }, + "streamStatus": 200 + } + ], + "routerConfigWrites": 0, + "passwordChanges": 0, + "messages": 0, + "passed": true +} \ No newline at end of file diff --git a/docs/ux-ui/acceptance/traffic-live-runtime-2026-10-12.json b/docs/ux-ui/acceptance/traffic-live-runtime-2026-10-12.json new file mode 100644 index 00000000..348585c8 --- /dev/null +++ b/docs/ux-ui/acceptance/traffic-live-runtime-2026-10-12.json @@ -0,0 +1,25 @@ +{ + "date": "2026-10-12", + "block": "12.5.179", + "secretEnvCopied": false, + "rollbackTags": { + "MMAPP_IMAGE_TAG": "ux-history178", + "BACKEND_IMAGE_TAG": "ux-history178" + }, + "services": { + "backend": { + "image": "mmapp-test/backend:ux-live179", + "imageId": "sha256:34a5638656bfa98ef38440553735a7067e1040fd3422df37d157ed3124981d4c", + "state": "running", + "health": "healthy", + "matchedVerifiedImage": true + }, + "frontend": { + "image": "mmapp-test/frontend:ux-live179", + "imageId": "sha256:af94a199a4c140c70769cb7b375940309a4fa169e734741648bd8cf1e27d14a3", + "state": "running", + "health": null, + "matchedVerifiedImage": true + } + } +} \ No newline at end of file diff --git a/docs/ux-ui/acceptance/traffic-live-source-2026-10-12.json b/docs/ux-ui/acceptance/traffic-live-source-2026-10-12.json new file mode 100644 index 00000000..e20ec5e3 --- /dev/null +++ b/docs/ux-ui/acceptance/traffic-live-source-2026-10-12.json @@ -0,0 +1,10 @@ +{ + "date": "2026-10-12", + "block": "12.5.179", + "matched": { + "backend": 451, + "frontend": 522 + }, + "different": [], + "missing": [] +} \ No newline at end of file diff --git a/docs/ux-ui/acceptance/traffic-live-sql-2026-10-12.json b/docs/ux-ui/acceptance/traffic-live-sql-2026-10-12.json new file mode 100644 index 00000000..36f2b612 --- /dev/null +++ b/docs/ux-ui/acceptance/traffic-live-sql-2026-10-12.json @@ -0,0 +1,29 @@ +{ + "date": "2026-10-12", + "block": "12.5.179", + "passed": true, + "profiles": [ + { + "date": "2026-10-12", + "block": "12.5.179", + "database": "mm_accept_166_1791814796307", + "scope": "ephemeral role and precreated owned database; no copied production credentials", + "tests": [ + { + "command": "npm --prefix backend run test:traffic-rate", + "passed": true, + "durationMs": 24570, + "ownedTrafficSqlCases": 9, + "ownedHistorySqlCases": 14, + "ownedLiveSqlCases": 10, + "postgresUnavailableSkip": false + } + ], + "scopedRoleVerified": true, + "passed": true, + "databaseRemoved": true, + "roleRemoved": true, + "testContainerRemoved": true + } + ] +} \ No newline at end of file diff --git a/docs/ux-ui/screens/traffic-live-error-1440-2026-10-12.png b/docs/ux-ui/screens/traffic-live-error-1440-2026-10-12.png new file mode 100644 index 0000000000000000000000000000000000000000..36b37076671765c1a01b034808f3bdc5420e872f GIT binary patch literal 141198 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 literal 0 HcmV?d00001 diff --git a/docs/ux-ui/screens/traffic-live-error-320-2026-10-12.png b/docs/ux-ui/screens/traffic-live-error-320-2026-10-12.png new file mode 100644 index 0000000000000000000000000000000000000000..937fe91ab9b719d90461f56cd61835f8515e3360 GIT binary patch literal 52235 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 + const scope = JSON.stringify([opts.backendUrl, opts.serverId, opts.iface]) + const key = JSON.stringify([opts.enabled, scope, opts.reloadKey ?? 0]) + const [result, setResult] = useState<{ key: string; scope: string; sample: TrafficLiveSample | null; receivedAt: number | null; error: string | null } | null>(null) useEffect(() => { if (!opts.enabled || !opts.serverId) return const ac = new AbortController() + let lastStreamError: string | null = null + // A new connection owns the status; the same device's previous sample remains historical. + // eslint-disable-next-line react-hooks/set-state-in-effect + setResult(previous => ({ key, scope, sample: previous?.scope === scope ? previous.sample : null, + receivedAt: previous?.scope === scope ? previous.receivedAt : null, error: "Ожидание нового live-измерения" })) let sampleTimer: ReturnType | undefined const stopTimer = () => clearTimeout(sampleTimer) const setError = (error: string) => { + lastStreamError = error stopTimer() - if (!ac.signal.aborted) setResult(previous => ({ key, sample: previous?.key === key ? previous.sample : null, receivedAt: previous?.key === key ? previous.receivedAt : null, error })) + if (!ac.signal.aborted) setResult(previous => ({ key, scope, sample: previous?.scope === scope ? previous.sample : null, receivedAt: previous?.scope === scope ? previous.receivedAt : null, error })) } const ifaceQ = opts.iface && opts.iface !== "__all__" @@ -61,7 +70,8 @@ export function useTrafficLive(opts: { }) if (ac.signal.aborted) return if (!res.ok || !res.body) { - setError(`live HTTP ${res.status}`) + const body = await res.json().catch(() => null) as { error?: unknown } | null + setError(`${typeof body?.error === "string" ? body.error : "Live-поток недоступен"} (HTTP ${res.status})`) return } const reader = res.body.getReader() @@ -70,7 +80,7 @@ export function useTrafficLive(opts: { const { done, value } = await reader.read() if (ac.signal.aborted) return if (done) { - setError("Поток live завершён") + setError(lastStreamError ?? "Поток live завершён") break } buf += decoder.decode(value, { stream: true }) @@ -81,14 +91,15 @@ export function useTrafficLive(opts: { const ev = parseSseBlock(raw) if (ev.event === "sample" && ev.data) { const parsed = JSON.parse(ev.data) as TrafficLiveSample - if (!parsed || !Number.isFinite(parsed.rxMbps) || !Number.isFinite(parsed.txMbps) || parsed.rxMbps < 0 || parsed.txMbps < 0 || typeof parsed.at !== "string" || !Number.isFinite(Date.parse(parsed.at))) { + if (!parsed || !Number.isFinite(parsed.rxMbps) || !Number.isFinite(parsed.txMbps) || parsed.rxMbps < 0 || parsed.txMbps < 0 || typeof parsed.at !== "string" || !Number.isFinite(Date.parse(parsed.at)) || parsed.timestampKind !== undefined && parsed.timestampKind !== "backend-receipt") { throw new Error("Некорректные данные live-потока") } awaitSample() - setResult({ key, sample: parsed, receivedAt: Date.now(), error: null }) + lastStreamError = null + setResult({ key, scope, sample: parsed, receivedAt: Date.now(), error: null }) } else if (ev.event === "error" && ev.data) { - const parsed = JSON.parse(ev.data) as { error?: string } - setError(parsed.error ?? "live error") + const parsed = JSON.parse(ev.data) as { error?: unknown } | null + setError(typeof parsed?.error === "string" ? parsed.error : "Некорректный ответ об ошибке live-потока") } } } @@ -97,11 +108,12 @@ export function useTrafficLive(opts: { setError(e instanceof Error ? e.message : "live error") } finally { stopTimer() + ac.abort() } })() return () => { stopTimer(); ac.abort() } - }, [opts.enabled, opts.backendUrl, opts.serverId, opts.iface, key]) + }, [opts.enabled, opts.backendUrl, opts.serverId, opts.iface, key, scope]) - return result?.key === key ? { sample: result.sample, error: result.error, receivedAt: result.receivedAt } : { sample: null, error: null, receivedAt: null } + return opts.enabled && result?.scope === scope ? { sample: result.sample, error: result.key === key ? result.error : "Ожидание нового live-измерения", receivedAt: result.receivedAt } : { sample: null, error: null, receivedAt: null } } diff --git a/lib/fmt-rate.test.ts b/lib/fmt-rate.test.ts new file mode 100644 index 00000000..5d291f48 --- /dev/null +++ b/lib/fmt-rate.test.ts @@ -0,0 +1,6 @@ +import assert from "node:assert/strict" +import { fmtRate } from "./fmt-rate.js" +assert.equal(fmtRate(null),"—");assert.equal(fmtRate(undefined),"—");assert.equal(fmtRate(0),"0 Кбит/с") +assert.equal(fmtRate(0.000001),"<1 Кбит/с");assert.equal(fmtRate(0.001),"1 Кбит/с") +assert.equal(fmtRate(1),"1.00 Мбит/с");assert.equal(fmtRate(1000),"1.00 Гбит/с") +console.log("rate formatting unknown/zero/positive-small/units tests ok") diff --git a/lib/fmt-rate.ts b/lib/fmt-rate.ts index db121dd6..9a31599a 100644 --- a/lib/fmt-rate.ts +++ b/lib/fmt-rate.ts @@ -2,6 +2,7 @@ export function fmtRate(v: number | null | undefined): string { if (v == null) return "—" if (!Number.isFinite(v) || v <= 0) return "0 Кбит/с" + if (v < 0.0005) return "<1 Кбит/с" if (v >= 1000) return `${(v / 1000).toFixed(2)} Гбит/с` if (v < 1) return `${Math.round(v * 1000)} Кбит/с` if (v < 10) return `${v.toFixed(2)} Мбит/с` diff --git a/tests/e2e/traffic-live-confidence.spec.ts b/tests/e2e/traffic-live-confidence.spec.ts new file mode 100644 index 00000000..ea9056cc --- /dev/null +++ b/tests/e2e/traffic-live-confidence.spec.ts @@ -0,0 +1,40 @@ +import { test, expect, type Page } from "@playwright/test" +import { observedTrafficFixturePayload } from "./traffic-observation-fixture" +async function setup(page: Page, terminal = true) { + const control = { calls: 0, release: () => {} } + const gate = new Promise(resolve => { control.release = resolve }) + const server = { id:"1",name:"CHR live",site:"TEST",country:"RU",status:"online",rxNow:2,txNow:3,rxPeak:4,txPeak:5,rxTotal:1,txTotal:2,sessions:1,rxSeries:[1,2],txSeries:[2,3] } + await page.route("**/health",r=>r.fulfill({json:{ok:true}})) + await page.route("**/api/**",async r=>{ + const path=new URL(r.request().url()).pathname + if(path.endsWith("/live")) { + control.calls++ + if(control.calls>1) await gate + const sample={rxMbps:control.calls===1?17:0,txMbps:0,at:"2026-10-12T12:00:00Z",timestampKind:"backend-receipt"} + const end=terminal&&control.calls===1?'event: error\ndata: '+JSON.stringify({error:"Устройство выключено. Live-поток остановлен"})+'\n\n':'' + return r.fulfill({contentType:"text/event-stream",body:'event: sample\ndata: '+JSON.stringify(sample)+'\n\n'+end}) + } + return r.fulfill({json:observedTrafficFixturePayload(path==="/api/auth/config"?{required:false}:path==="/api/traffic/servers"?{servers:[server]}:path==="/api/traffic/servers/1"?{server}:path.endsWith("/interfaces")||path==="/api/traffic/bound-interfaces"?{interfaces:[]}:path==="/api/traffic/users"?{users:[]}:path==="/api/servers"?[]:{})}) + }) + await page.goto("/traffic") + await expect(page.getByRole("heading",{name:"CHR live",exact:true})).toBeVisible() + return control +} +for(const width of [1440,320])test(`Причина закрытия live сохранена, переподключение сохраняет последнее значение ${width}`,async({page})=>{ + await page.setViewportSize({width,height:1000});const c=await setup(page) + const status=page.getByRole("status").filter({hasText:"Live-поток: нет свежих данных"}) + await expect(status).toContainText("Устройство выключено") + await expect(status).not.toContainText("Поток live завершён") + await expect(page.getByLabel("Скорость выбранного сервера")).toContainText("17.0") + await expect(page.getByLabel("Последнее измерение сервера")).toContainText("ответ RouterOS получен приложением") + await status.scrollIntoViewIfNeeded() + await status.evaluate(el => { let parent=el.parentElement;while(parent&&(!/auto|scroll/.test(getComputedStyle(parent).overflowY)||parent.scrollHeight<=parent.clientHeight))parent=parent.parentElement;if(parent)parent.scrollTop+=el.getBoundingClientRect().top-parent.getBoundingClientRect().top-24 }) + await page.screenshot({path:`docs/ux-ui/screens/traffic-live-error-${width}-2026-10-12.png`,fullPage:true}) + await page.getByRole("button",{name:"Переподключить live",exact:true}).click() + await expect.poll(()=>c.calls).toBe(2) + await expect(page.getByRole("status").filter({hasText:"Подключение live-потока"})).toBeVisible() + await expect(page.getByLabel("Скорость выбранного сервера")).toContainText("17.0") + c.release();await expect(page.getByLabel("Скорость выбранного сервера")).not.toContainText("17.0") + await expect(page.getByLabel("Скорость выбранного сервера").getByText("0 Кбит/с",{exact:true})).toHaveCount(2) + expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth+1)).toBe(true) +}) diff --git a/tests/e2e/traffic-live.spec.ts b/tests/e2e/traffic-live.spec.ts index aca0f755..7dcd5ff1 100644 --- a/tests/e2e/traffic-live.spec.ts +++ b/tests/e2e/traffic-live.spec.ts @@ -62,7 +62,7 @@ for (const variant of ['closed', 'invalid', 'http-error', 'silent', 'awaiting'] await expect(page.getByLabel('Последнее измерение сервера')).toHaveCount(0) return } - const hint = variant === 'http-error' ? 'История · live недоступен' : 'Последнее live-измерение · поток недоступен' + const hint = variant === 'http-error' ? 'История · live недоступен' : 'Последнее live' await expect(summary.getByText(hint, { exact: true }).filter({ visible: true })).toHaveCount(2) await expect(summary.getByText('live', { exact: true })).toHaveCount(0) await expect(page.getByLabel('Получение истории сервера').locator('time')).toHaveAttribute('datetime', received.toISOString())