fix(traffic): проверять live-срез и прекращать опрос выключенных устройств

This commit is contained in:
Denozordec committed 2026-10-12 21:35:55 +07:00
1 parent 1275dd7b9b
commit 2358ecef60
29 files changed
+1620 -114

No files matched your search

+26 -10
View File
@@ -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<string>()
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<string, unknown>
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",
}
}