fix(api): проверить каталог speed-проб и изолировать источник
This commit is contained in:
1 parent
f30319ac55
commit
d4f2961f76
13 files changed
+123
-51
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { mapSpeedProbeRead } from "../services/speed-probe-dto.js"
|
||||
import { mapSpeedHistoryRun } from "../services/speed-history-dto.js"
|
||||
import type { FastifyPluginAsyncZod } from "@fastify/type-provider-zod"
|
||||
import { asc, desc, eq } from "drizzle-orm"
|
||||
@@ -181,28 +182,11 @@ const uptimeRoutes: FastifyPluginAsyncZod = async (app) => {
|
||||
|
||||
app.get("/uptime/speed-probes", async (_req, reply) => {
|
||||
const rows = await db.select().from(uptimeSpeedProbes).orderBy(asc(uptimeSpeedProbes.sortOrder))
|
||||
return reply.send({
|
||||
probes: rows.map((r) => ({
|
||||
id: r.id,
|
||||
srcServerId: String(r.srcServerId),
|
||||
dstServerId: String(r.dstServerId),
|
||||
srcInterface: r.srcInterface || "",
|
||||
dstInterface: r.dstInterface || "",
|
||||
protocol: r.protocol === "udp" ? "udp" : "tcp",
|
||||
direction: r.direction === "transmit" || r.direction === "receive" ? r.direction : "both",
|
||||
durationSec: String(Math.max(3, r.durationSec || 10)),
|
||||
enabled: r.enabled !== false,
|
||||
lastRunAt: r.lastRunAt ?? null,
|
||||
lastTxAvgMbps: r.lastTxAvgMbps ?? null,
|
||||
lastRxAvgMbps: r.lastRxAvgMbps ?? null,
|
||||
lastStatus: r.lastStatus ?? null,
|
||||
lastError: r.lastError ?? null,
|
||||
lastPingRttMs: r.lastPingRttMs ?? null,
|
||||
lastPingLossPct: r.lastPingLossPct ?? null,
|
||||
lastPingAt: r.lastPingAt ?? null,
|
||||
lastPingError: r.lastPingError ?? null,
|
||||
})),
|
||||
})
|
||||
try {
|
||||
return reply.send({ probes: rows.map(mapSpeedProbeRead) })
|
||||
} catch {
|
||||
return reply.code(502).send({ error: "Каталог speed-проб содержит некорректную запись. Данные не подтверждены." })
|
||||
}
|
||||
})
|
||||
|
||||
app.get("/uptime/speed-test/runs", async (_req, reply) => {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { test } from "node:test"
|
||||
import assert from "node:assert/strict"
|
||||
import { mapSpeedProbeRead } from "./speed-probe-dto.js"
|
||||
import type { UptimeSpeedProbeRow } from "../db/schema.js"
|
||||
const row:UptimeSpeedProbeRow={id:"probe-1",srcServerId:1,dstServerId:2,srcInterface:"",dstInterface:"",protocol:"tcp",direction:"both",durationSec:10,enabled:false,lastRunAt:null,lastTxAvgMbps:null,lastRxAvgMbps:0,lastStatus:null,lastError:null,lastPingRttMs:null,lastPingLossPct:0,lastPingAt:null,lastPingError:null,sortOrder:0,createdAt:"2026-10-04T00:00:00Z",updatedAt:"2026-10-04T00:00:00Z"}
|
||||
test("Speed probe: null/zero/disabled и неизвестный status не подменяются",()=>{const dto=mapSpeedProbeRead(row);assert.equal(dto.enabled,false);assert.equal(dto.lastTxAvgMbps,null);assert.equal(dto.lastRxAvgMbps,0);assert.equal(dto.lastStatus,null);assert.equal(dto.lastPingRttMs,null);assert.equal(dto.lastPingLossPct,0)})
|
||||
test("Speed probe: timestamp PostgreSQL сохраняет instant",()=>{assert.equal(mapSpeedProbeRead({...row,lastRunAt:"2026-10-04 07:00:00+07"}).lastRunAt,"2026-10-04T00:00:00.000Z")})
|
||||
test("Speed probe: некорректная DB запись отклоняется без fallback",()=>{for(const raw of [{...row,protocol:"bad"},{...row,direction:"bad"},{...row,durationSec:0},{...row,enabled:null},{...row,lastStatus:"bad"},{...row,lastTxAvgMbps:-1},{...row,lastRunAt:"bad"},{...row,srcServerId:0}])assert.throws(()=>mapSpeedProbeRead(raw as UptimeSpeedProbeRow))})
|
||||
@@ -0,0 +1,13 @@
|
||||
import { speedProbeReadSchema } from "@mmapp/contracts"
|
||||
import type { UptimeSpeedProbeRow } from "../db/schema.js"
|
||||
const timestamp = (value: string | null) => value === null ? null : new Date(value).toISOString()
|
||||
export function mapSpeedProbeRead(row: UptimeSpeedProbeRow) {
|
||||
return speedProbeReadSchema.parse({
|
||||
id: row.id, srcServerId: String(row.srcServerId), dstServerId: String(row.dstServerId),
|
||||
srcInterface: row.srcInterface, dstInterface: row.dstInterface,
|
||||
protocol: row.protocol, direction: row.direction, durationSec: String(row.durationSec), enabled: row.enabled,
|
||||
lastRunAt: timestamp(row.lastRunAt), lastTxAvgMbps: row.lastTxAvgMbps, lastRxAvgMbps: row.lastRxAvgMbps,
|
||||
lastStatus: row.lastStatus, lastError: row.lastError,
|
||||
lastPingRttMs: row.lastPingRttMs, lastPingLossPct: row.lastPingLossPct, lastPingAt: timestamp(row.lastPingAt), lastPingError: row.lastPingError,
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user