266 lines
7.3 KiB
TypeScript
266 lines
7.3 KiB
TypeScript
/**
|
|
* Снимок прогона планировщика (JSON из `scheduler_runs.result_json`).
|
|
* Дублирует контракт `backend/src/types/scheduler-run-snapshot.ts` для UI.
|
|
*/
|
|
|
|
export interface ServerRestPingSnapshot {
|
|
serverId: number
|
|
name: string
|
|
host: string
|
|
ok: boolean
|
|
latencyMs?: number | null
|
|
error?: string
|
|
}
|
|
|
|
export interface ServersRestPingRunSnapshot {
|
|
v: number
|
|
job: "servers_rest_ping"
|
|
sampledAt: string
|
|
skipped?: boolean
|
|
fatalError?: string
|
|
servers: ServerRestPingSnapshot[]
|
|
}
|
|
|
|
export type AlertEngineRuleDiagSnapshot = {
|
|
ruleId: string
|
|
inGroup: boolean
|
|
evalHit: boolean
|
|
hitTransition?: "problem" | "recovery" | "neutral"
|
|
hitMessage?: string
|
|
stabilityOk: boolean
|
|
cooldownOk: boolean
|
|
telegramOk: boolean
|
|
blocked?:
|
|
| "no_hit"
|
|
| "stability"
|
|
| "cooldown"
|
|
| "no_telegram"
|
|
| "dedupe_positive"
|
|
| "in_group"
|
|
}
|
|
|
|
/** Сводка прогона движка оповещений (JSON из `scheduler_runs` для job `alert_engine`). */
|
|
export interface AlertEngineRunSnapshot {
|
|
v: number
|
|
job: "alert_engine"
|
|
sampledAt: string
|
|
rulesChecked: number
|
|
standaloneFires: number
|
|
groupFires: number
|
|
skippedNoTelegram: boolean
|
|
errors?: string[]
|
|
ruleDiag?: AlertEngineRuleDiagSnapshot[]
|
|
}
|
|
|
|
export interface GreBgpSnapshotRunSnapshot {
|
|
v: number
|
|
job: "gre_bgp"
|
|
sampledAt: string
|
|
skipped?: boolean
|
|
fatalError?: string
|
|
greWritten: number
|
|
bgpWritten: number
|
|
errors?: string[]
|
|
}
|
|
|
|
export interface InternetPathRunSnapshot {
|
|
v: number
|
|
job: "internet_path"
|
|
sampledAt: string
|
|
homes: number
|
|
snapshotSaved: boolean
|
|
fatalError?: string
|
|
}
|
|
|
|
export interface CertificatesRenewRunSnapshot {
|
|
v: number
|
|
job: "certificates_renew"
|
|
sampledAt: string
|
|
skipped?: boolean
|
|
fatalError?: string
|
|
checked: number
|
|
renewed: number
|
|
skippedTargets: number
|
|
errors: string[]
|
|
}
|
|
|
|
export interface BackupsRunSnapshot {
|
|
v: number
|
|
job: "backups"
|
|
sampledAt: string
|
|
skipped?: boolean
|
|
due: boolean
|
|
created: number
|
|
failures: number
|
|
pruned: number
|
|
uploadFailures?: number
|
|
pruneFailures?: number
|
|
metadataError?: string
|
|
eventErrors?: string[]
|
|
errors?: string[]
|
|
fatalError?: string
|
|
}
|
|
|
|
export interface GeoipUpdateRunSnapshot {
|
|
v: number
|
|
job: "geoip_update"
|
|
sampledAt: string
|
|
skipped?: boolean
|
|
fatalError?: string
|
|
checked: number
|
|
downloaded: number
|
|
skippedUnchanged: number
|
|
bytes: number
|
|
errors: string[]
|
|
}
|
|
|
|
export interface RouterLogsRunSnapshot {
|
|
v: 1
|
|
job: "router_logs"
|
|
sampledAt: string
|
|
skipped?: boolean
|
|
errors: string[]
|
|
servers: { serverId: number; name: string; added: number; gap: boolean; error?: string }[]
|
|
}
|
|
export type SchedulerRunSnapshot =
|
|
| RouterLogsRunSnapshot
|
|
| TrafficRunSnapshot
|
|
| ResourcesRunSnapshot
|
|
| PingRunSnapshot
|
|
| SpeedScheduledRunSnapshot
|
|
| ServersRestPingRunSnapshot
|
|
| GreBgpSnapshotRunSnapshot
|
|
| InternetPathRunSnapshot
|
|
| CertificatesRenewRunSnapshot
|
|
| BackupsRunSnapshot
|
|
| GeoipUpdateRunSnapshot
|
|
| AlertEngineRunSnapshot
|
|
|
|
export interface TrafficServerSnapshot {
|
|
serverId: number
|
|
name: string
|
|
host: string
|
|
ok: boolean
|
|
error?: string
|
|
interfaces?: number
|
|
sumRxMbps?: number
|
|
sumTxMbps?: number
|
|
}
|
|
|
|
export interface TrafficRunSnapshot {
|
|
v: number
|
|
job: "traffic"
|
|
sampledAt: string
|
|
skipped?: boolean
|
|
fatalError?: string
|
|
servers: TrafficServerSnapshot[]
|
|
}
|
|
|
|
export interface ResourceServerSnapshot {
|
|
serverId: number
|
|
name: string
|
|
host: string
|
|
status: "online" | "offline"
|
|
error?: string
|
|
cpuLoadPct?: number
|
|
memUsedMb?: number
|
|
memTotalMb?: number
|
|
memUsedPct?: number
|
|
diskFreeMb?: number
|
|
diskTotalMb?: number
|
|
uptimeSeconds?: number
|
|
boardName?: string
|
|
rosVersion?: string
|
|
}
|
|
|
|
export interface ResourcesRunSnapshot {
|
|
v: number
|
|
job: "uptime_resources"
|
|
sampledAt: string
|
|
skipped?: boolean
|
|
fatalError?: string
|
|
servers: ResourceServerSnapshot[]
|
|
}
|
|
|
|
export interface PingProbeSnapshot {
|
|
probeId: string
|
|
name: string
|
|
target: string
|
|
srcServerId: number
|
|
srcServerName: string
|
|
srcInterface: string
|
|
ok: boolean
|
|
rttMs: number | null
|
|
lossPct: number | null
|
|
status: "up" | "warn" | "down" | "unknown"
|
|
error?: string
|
|
}
|
|
|
|
export interface PingRunSnapshot {
|
|
v: number
|
|
job: "uptime_ping"
|
|
sampledAt: string
|
|
skipped?: boolean
|
|
fatalError?: string
|
|
probes: PingProbeSnapshot[]
|
|
skippedByInterval?: number
|
|
}
|
|
|
|
export interface SpeedRunSnapshot {
|
|
probeId: string
|
|
srcServerId: number
|
|
srcServerName: string
|
|
dstServerId: number
|
|
dstServerName: string
|
|
srcInterface: string
|
|
dstInterface: string
|
|
protocol: string
|
|
direction: string
|
|
durationSec: number
|
|
ok: boolean
|
|
txAvgMbps?: number | null
|
|
rxAvgMbps?: number | null
|
|
pingRttMs?: number | null
|
|
pingLossPct?: number | null
|
|
pingError?: string | null
|
|
error?: string
|
|
}
|
|
|
|
export interface SpeedScheduledRunSnapshot {
|
|
v: number
|
|
job: "uptime_speed"
|
|
sampledAt: string
|
|
runs: SpeedRunSnapshot[]
|
|
}
|
|
|
|
/** PostgreSQL JSONB returns an object; older records may still contain JSON text. */
|
|
export function parseSchedulerRunSnapshot(raw: unknown): SchedulerRunSnapshot | null {
|
|
try {
|
|
const value: unknown = typeof raw === "string" ? (raw.trim() ? JSON.parse(raw) : null) : raw
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null
|
|
const o = value as Record<string, unknown>
|
|
if (o.v !== 1 || typeof o.job !== "string" || typeof o.sampledAt !== "string" || !Number.isFinite(Date.parse(o.sampledAt))) return null
|
|
if (o.skipped !== undefined && typeof o.skipped !== "boolean") return null
|
|
if (o.fatalError !== undefined && typeof o.fatalError !== "string") return null
|
|
for (const field of ["errors", "eventErrors"]) if (o[field] !== undefined && (!Array.isArray(o[field]) || !(o[field] as unknown[]).every(error => typeof error === "string"))) return null
|
|
const arrays: Record<string, string> = { router_logs: "servers", traffic: "servers", uptime_resources: "servers", servers_rest_ping: "servers", uptime_ping: "probes", uptime_speed: "runs" }
|
|
if (Object.hasOwn(arrays, o.job)) {
|
|
if (!Array.isArray(o[arrays[o.job]])) return null
|
|
} else {
|
|
const fields: Record<string, string[]> = { gre_bgp: ["greWritten", "bgpWritten"], internet_path: ["homes"], certificates_renew: ["checked", "renewed", "skippedTargets"], backups: ["created", "failures", "pruned"], geoip_update: ["checked", "downloaded", "skippedUnchanged", "bytes"], alert_engine: ["rulesChecked", "standaloneFires", "groupFires"] }
|
|
if (!Object.hasOwn(fields, o.job) || !fields[o.job].every(field => typeof o[field] === "number" && Number.isSafeInteger(o[field]) && (o[field] as number) >= 0)) return null
|
|
if (["certificates_renew", "geoip_update"].includes(o.job) && !Array.isArray(o.errors)) return null
|
|
if (o.job === "backups") {
|
|
if (typeof o.due !== "boolean") return null
|
|
for (const field of ["uploadFailures", "pruneFailures"]) if (o[field] !== undefined && (typeof o[field] !== "number" || !Number.isSafeInteger(o[field]) || (o[field] as number) < 0)) return null
|
|
if (o.metadataError !== undefined && typeof o.metadataError !== "string") return null
|
|
}
|
|
if (o.job === "internet_path" && typeof o.snapshotSaved !== "boolean") return null
|
|
if (o.job === "alert_engine" && typeof o.skippedNoTelegram !== "boolean") return null
|
|
}
|
|
return o as unknown as SchedulerRunSnapshot
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|