fix(map): сохранить узлы при датах опроса из базы данных
This commit is contained in:
1 parent
72bf648ca8
commit
fda7f353ad
5 files changed
+114
-1
No files matched your search
@@ -2,6 +2,18 @@ import {test} from "node:test"
|
||||
import assert from "node:assert/strict"
|
||||
import {validateNetworkMapSnapshot} from "./network-map-response"
|
||||
const server={id:1,name:"Router",host:"192.0.2.1",type:"home-router",country:"RU",enabled:true,status:null,latency:0}
|
||||
test("network map retains jump and exit nodes with PostgreSQL timestamps",()=>{
|
||||
const timestamps=["2026-10-15 13:46:40.855+00","2026-10-15 20:46:40.855+07","2026-10-15T13:46:40.855Z",null]
|
||||
const rows=timestamps.map((polledAt,index)=>({...server,id:index+1,type:index===0?"exit-node":"jump-host",polledAt}))
|
||||
const snapshot=validateNetworkMapSnapshot(rows,{tunnels:[],failures:[]},{probes:[]})
|
||||
assert.equal(snapshot.servers.length,4)
|
||||
assert.equal(snapshot.servers[0].type,"exit-node")
|
||||
assert.equal(Date.parse(snapshot.servers[0].polledAt!),Date.parse(snapshot.servers[1].polledAt!))
|
||||
assert.equal(snapshot.servers[3].polledAt,null)
|
||||
for(const polledAt of ["2026-10-15 13:46:40.855","2026-02-30 13:46:40+00","not-a-date",42]) {
|
||||
assert.throws(()=>validateNetworkMapSnapshot([{...server,polledAt}],{tunnels:[],failures:[]},{probes:[]}))
|
||||
}
|
||||
})
|
||||
test("network map retains configured GRE IPsec without key material",()=>{
|
||||
const tunnel={id:"*1",serverId:"1",name:"own210",localAddress:"192.0.2.1",remoteAddress:"192.0.2.2",localInnerIp:"10.0.0.1/30",remoteInnerIp:"10.0.0.2/30",poolId:"",ipsec:{secret:""},mtu:1476,keepaliveInterval:60,keepaliveRetries:3,dscp:"inherit",clampTcpMss:true,allowFastPath:false,comment:"",enabled:true,status:"up"}
|
||||
const result=validateNetworkMapSnapshot([server],{tunnels:[tunnel],failures:[]},{probes:[]})
|
||||
|
||||
@@ -2,12 +2,18 @@ import {z} from "zod"
|
||||
import {optimizerServersSchema,optimizerProbesSchema} from "./route-optimizer-response"
|
||||
import {filtersGreResponseSchema} from "./filters-response"
|
||||
import {greResponseSchema} from "./gre-response"
|
||||
// Drizzle's string-mode timestamptz is returned as PostgreSQL text (+00),
|
||||
// while test/other API adapters may already return ISO. Preserve the explicit
|
||||
// offset and validate the resulting calendar date; never infer a missing zone.
|
||||
const mapObservedTime=z.preprocess(value=>typeof value==="string"
|
||||
?value.replace(/^(\d{4}-\d{2}-\d{2}) /,"$1T").replace(/([+-]\d{2})$/,"$1:00")
|
||||
:value,z.string().datetime({offset:true}))
|
||||
export const networkMapServersSchema=z.array(optimizerServersSchema.element.extend({
|
||||
status:z.enum(["online","offline","degraded"]).nullable(),
|
||||
asn:z.string().optional(),comment:z.string().optional(),lanSubnet:z.string().optional(),os:z.string().nullable().optional(),
|
||||
sessions:z.number().int().nonnegative().nullable().optional(),uptime:z.string().nullable().optional(),
|
||||
cpuLoad:z.number().finite().min(0).max(100).nullable().optional(),freeMemory:z.number().int().nonnegative().nullable().optional(),totalMemory:z.number().int().nonnegative().nullable().optional(),
|
||||
polledAt:z.string().datetime({offset:true}).nullable().optional(),
|
||||
polledAt:mapObservedTime.nullable().optional(),
|
||||
})).refine(rows=>new Set(rows.map(row=>row.id)).size===rows.length && rows.every(row=>row.freeMemory==null||row.totalMemory==null||row.freeMemory<=row.totalMemory))
|
||||
export type NetworkMapServerRow=z.infer<typeof networkMapServersSchema>[number]
|
||||
const mapGreSchema=z.object({tunnels:z.array(filtersGreResponseSchema.shape.tunnels.element).refine(rows=>new Set(rows.map(row=>JSON.stringify([row.serverId,row.id]))).size===rows.length),failures:greResponseSchema.shape.failures}).refine(data=>new Set(data.failures.map(row=>row.serverId)).size===data.failures.length&&!data.tunnels.some(tunnel=>data.failures.some(failure=>failure.serverId===tunnel.serverId)))
|
||||
|
||||
Reference in new issue
Block a user