fix(traffic): прекращать опрос изменённого источника
This commit is contained in:
1 parent
5d19b6a258
commit
504f62fc0b
15 files changed
+1296
-14
No files matched your search
@@ -131,10 +131,17 @@ export async function collectTrafficOnce(): Promise<TrafficRunSnapshot> {
|
||||
|
||||
for (const srv of enabledServers) {
|
||||
const name = srv.name || srv.host
|
||||
const ensureSource = async () => {
|
||||
const current = (await db.select({enabled:servers.enabled,updatedAt:servers.updatedAt}).from(servers).where(eq(servers.id,srv.id)).limit(1))[0]
|
||||
if (!current?.enabled || Date.parse(current.updatedAt) !== Date.parse(srv.updatedAt))
|
||||
throw new Error("Сервер выключен, удалён или его конфигурация изменилась во время опроса")
|
||||
}
|
||||
try {
|
||||
await recordTrafficCollectionAttempt(srv, "pending", now)
|
||||
const client = MikrotikClient.fromServer(srv)
|
||||
await ensureSource()
|
||||
const ifaces = await client.get<RosIfaceTraffic[]>("/interface")
|
||||
await ensureSource()
|
||||
rememberServerIfaces(srv.id, ifaces)
|
||||
const prevWave = await readPreviousWave(srv.id)
|
||||
const nowMs = Date.parse(now)
|
||||
@@ -174,6 +181,7 @@ export async function collectTrafficOnce(): Promise<TrafficRunSnapshot> {
|
||||
})
|
||||
let peerWarning: string | undefined
|
||||
try {
|
||||
await ensureSource()
|
||||
const peers = await client.get<RosWgPeerTraffic[]>("/interface/wireguard/peers")
|
||||
for (const p of parseTrafficPeers(peers)) {
|
||||
const { interfaceName, peerPublicKey, rxBytes, txBytes, disabled } = p
|
||||
@@ -205,9 +213,11 @@ export async function collectTrafficOnce(): Promise<TrafficRunSnapshot> {
|
||||
} catch (error) {
|
||||
peerWarning = `Измерения WireGuard peers недоступны: ${error instanceof Error ? error.message : String(error)}`
|
||||
}
|
||||
await ensureSource()
|
||||
if (rows.length > 0) {
|
||||
await db.insert(trafficSamples).values(rows)
|
||||
}
|
||||
await ensureSource()
|
||||
await recordTrafficCollectionAttempt(srv, "ok", now, peerWarning)
|
||||
snapshot.servers.push({
|
||||
serverId: srv.id,
|
||||
|
||||
@@ -50,7 +50,7 @@ const context = vm.createContext({
|
||||
MikrotikClient: { fromServer: () => ({ get: async (path: string) => path === "/interface" ?
|
||||
[{ ".id":"*1",name: "ether1", running: String(included), disabled: "false", "rx-byte": counters, "tx-byte": counters }] : [] }) },
|
||||
db: {
|
||||
select: () => ({ from: () => ({ where: async () => [{ updatedAt:iso,id: 1, name: "audit", host: "audit.example", enabled: true }] }) }),
|
||||
select: () => ({ from: () => ({ where: () => {const rows=[{updatedAt:iso,id:1,name:"audit",host:"audit.example",enabled:true}];return Object.assign(Promise.resolve(rows),{limit:async()=>rows})} }) }),
|
||||
insert: () => ({ values: async (rows: unknown[]) => { inserted.push(...rows) } }),
|
||||
update: () => ({ set: () => ({ where: async () => {} }) }),
|
||||
},
|
||||
|
||||
@@ -10,7 +10,13 @@ if(!database)console.log("traffic status SQL: skipped; explicit owned database r
|
||||
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-status181-"+Date.now()+"-",userId=prefix+"user";let serverId:number|undefined,cases=0,deny=false,flag:unknown=true,peerDisabled=false,peerVariant="valid",denyPeer=false,ifaceId:unknown="*1",counterValue="1000",peerId="*2",peerCounter="1000"
|
||||
const http=createServer((req,res)=>{res.setHeader("Content-Type","application/json");if(deny || denyPeer && req.url?.includes("/wireguard/")){res.statusCode=401;return res.end('{"message":"Unauthorized"}')}res.end(JSON.stringify(req.url?.includes("/wireguard/")?(peerVariant==="shape"?{}:peerVariant==="duplicate"?[{".id":peerId,interface:"wg1","public-key":"fixturekey",rx:"1000",tx:"1000",disabled:false},{".id":peerId,interface:"wg1","public-key":"fixturekey",rx:"1000",tx:"1000",disabled:false}]:[{".id":peerId,interface:"wg1","public-key":"fixturekey",rx:peerCounter,tx:peerCounter,disabled:peerDisabled},...(peerVariant==="partial"?[{".id":peerId,interface:"wg1","public-key":"broken",rx:"invalid",tx:"1000",disabled:false}]:[])]):[{".id":ifaceId,name:"ether1",running:flag,disabled:false,"rx-byte":counterValue,"tx-byte":counterValue}]))})
|
||||
let switchSource="",peerRequests=0;const http=createServer(async(req,res)=>{if(req.url?.includes("/wireguard/"))peerRequests++;
|
||||
if(switchSource && !req.url?.includes("/wireguard/")){
|
||||
const action=switchSource;switchSource="";
|
||||
if(action==="disable")await pool.query("UPDATE servers SET enabled=false WHERE id=$1",[serverId]);
|
||||
else await pool.query("UPDATE servers SET updated_at=$1 WHERE id=$2",[new Date().toISOString(),serverId]);
|
||||
}
|
||||
res.setHeader("Content-Type","application/json");if(deny || denyPeer && req.url?.includes("/wireguard/")){res.statusCode=401;return res.end('{"message":"Unauthorized"}')}res.end(JSON.stringify(req.url?.includes("/wireguard/")?(peerVariant==="shape"?{}:peerVariant==="duplicate"?[{".id":peerId,interface:"wg1","public-key":"fixturekey",rx:"1000",tx:"1000",disabled:false},{".id":peerId,interface:"wg1","public-key":"fixturekey",rx:"1000",tx:"1000",disabled:false}]:[{".id":peerId,interface:"wg1","public-key":"fixturekey",rx:peerCounter,tx:peerCounter,disabled:peerDisabled},...(peerVariant==="partial"?[{".id":peerId,interface:"wg1","public-key":"broken",rx:"invalid",tx:"1000",disabled:false}]:[])]):[{".id":ifaceId,name:"ether1",running:flag,disabled:false,"rx-byte":counterValue,"tx-byte":counterValue}]))})
|
||||
await new Promise<void>(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 bindings=async()=>{const r=await app.inject({method:"GET",url:"/traffic/bound-interfaces?range=1h"});assert.equal(r.statusCode,200);return r.json().interfaces.filter((i:{userId:string})=>i.userId===userId)}
|
||||
try{
|
||||
@@ -47,6 +53,9 @@ else{
|
||||
}
|
||||
ifaceId="*10";await collectTrafficOnce();await collectTrafficOnce();peerId="*20";peerCounter="2000";await collectTrafficOnce();rows=await bindings();let peer=rows.find((r:{interfaceName:string})=>r.interfaceName==="wg1");assert.equal(peer.observations.rxNow,false);assert.equal(peer.rxTotal,0);
|
||||
await collectTrafficOnce();rows=await bindings();peer=rows.find((r:{interfaceName:string})=>r.interfaceName==="wg1");assert.equal(peer.observations.rxNow,true);assert.equal(peer.rxNow,0);cases++;
|
||||
for(const action of["disable","config"]){
|
||||
const before=peerRequests,count=Number((await pool.query("SELECT count(*) AS n FROM traffic_samples WHERE server_id=$1",[serverId])).rows[0].n);switchSource=action;const switched=await collectTrafficOnce();assert.equal(switched.servers[0].ok,false);assert(switched.servers[0].error?.includes("Сервер"));assert.equal(peerRequests,before);assert.equal(Number((await pool.query("SELECT count(*) AS n FROM traffic_samples WHERE server_id=$1",[serverId])).rows[0].n),count);await pool.query("UPDATE servers SET enabled=true WHERE id=$1",[serverId]);cases++;
|
||||
}
|
||||
await pool.query("UPDATE servers SET enabled=false WHERE id=$1",[serverId]);rows=await bindings();assert(rows.every((r:{status:string})=>r.status==="unknown"));cases++
|
||||
console.log(`traffic status owned SQL: ${cases} cases; first-wave native flags/peer config/invalid/error/legacy/stale/disabled passed`)
|
||||
}finally{await app.close();await new Promise<void>(resolve=>http.close(()=>resolve()));await pool.query("DELETE FROM app_users WHERE id=$1",[userId]);if(serverId!==undefined){await pool.query("DELETE FROM traffic_samples WHERE server_id=$1",[serverId]);await pool.query("DELETE FROM servers WHERE id=$1",[serverId])}await closePool()}
|
||||
|
||||
Reference in new issue
Block a user