Includes current frontend and backend work in progress and removes generated artifacts from tracking to keep the repository clean for дальнейшая разработка. Co-authored-by: Cursor <[email protected]>
105 lines
3.4 KiB
TypeScript
105 lines
3.4 KiB
TypeScript
import { eq } from "drizzle-orm"
|
|
import { db } from "../db/index.js"
|
|
import { servers, serverSnapshots } from "../db/schema.js"
|
|
import type { SnapshotInsert } from "../db/schema.js"
|
|
import type { SnapshotRead } from "../types/server.js"
|
|
import { MikrotikClient } from "./mikrotik.js"
|
|
import { parseRosCpuLoadPercent, parseRosDataSizeBytes } from "./ros-metric-parse.js"
|
|
|
|
// ── pollServer ─────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Connect to a RouterOS device, collect system info, persist a snapshot,
|
|
* and sync the server's display name from system/identity.
|
|
*/
|
|
export async function pollServer(serverId: number): Promise<SnapshotRead> {
|
|
const server = db
|
|
.select()
|
|
.from(servers)
|
|
.where(eq(servers.id, serverId))
|
|
.limit(1)
|
|
.all()[0]
|
|
|
|
if (!server) {
|
|
throw new Error(`Server with id=${serverId} not found`)
|
|
}
|
|
|
|
const now = new Date().toISOString()
|
|
const client = MikrotikClient.fromServer(server)
|
|
const t0 = performance.now()
|
|
|
|
const partialSnap: Partial<SnapshotInsert> = {
|
|
serverId,
|
|
polledAt: now,
|
|
status: "offline",
|
|
latencyMs: null,
|
|
}
|
|
|
|
try {
|
|
// Fire all requests in parallel for speed
|
|
const [identity, resource, ifaces, addresses] = await Promise.all([
|
|
client.getIdentity(),
|
|
client.getResource(),
|
|
client.getInterfaces(),
|
|
client.getIpAddresses(),
|
|
])
|
|
|
|
const latencyMs = performance.now() - t0
|
|
|
|
const cpuLoad = parseRosCpuLoadPercent(resource["cpu-load"])
|
|
const freeMem = parseRosDataSizeBytes(resource["free-memory"])
|
|
const totalMem = parseRosDataSizeBytes(resource["total-memory"])
|
|
|
|
Object.assign(partialSnap, {
|
|
status: "online",
|
|
latencyMs,
|
|
identityName: identity.name,
|
|
rosVersion: resource["version"],
|
|
boardName: resource["board-name"],
|
|
uptime: resource["uptime"],
|
|
cpuLoad,
|
|
freeMemory: freeMem,
|
|
totalMemory: totalMem,
|
|
rawInterfaces: JSON.stringify(ifaces),
|
|
rawIpAddresses: JSON.stringify(addresses),
|
|
} satisfies Partial<SnapshotInsert>)
|
|
|
|
// Keep server.name in sync with RouterOS identity
|
|
db.update(servers)
|
|
.set({ name: identity.name, updatedAt: now })
|
|
.where(eq(servers.id, serverId))
|
|
.run()
|
|
|
|
} catch (err) {
|
|
// Log but don't throw — we still persist the offline snapshot
|
|
console.warn(`[poller] server id=${serverId} unreachable:`, (err as Error).message)
|
|
}
|
|
|
|
const [inserted] = db
|
|
.insert(serverSnapshots)
|
|
.values(partialSnap as SnapshotInsert)
|
|
.returning()
|
|
.all()
|
|
|
|
return toSnapshotRead(inserted)
|
|
}
|
|
|
|
// ── helper ─────────────────────────────────────────────────────────────────────
|
|
|
|
export function toSnapshotRead(s: typeof serverSnapshots.$inferSelect): SnapshotRead {
|
|
return {
|
|
id: s.id,
|
|
serverId: s.serverId,
|
|
polledAt: s.polledAt,
|
|
status: s.status,
|
|
latencyMs: s.latencyMs ?? null,
|
|
rosVersion: s.rosVersion ?? null,
|
|
boardName: s.boardName ?? null,
|
|
uptime: s.uptime ?? null,
|
|
cpuLoad: s.cpuLoad ?? null,
|
|
freeMemory: s.freeMemory ?? null,
|
|
totalMemory: s.totalMemory ?? null,
|
|
identityName: s.identityName ?? null,
|
|
}
|
|
}
|