Docker images / prepare-release (push) Successful in 10s
Docker images / backend-test (push) Successful in 2m2s
Docker images / frontend-image (push) Successful in 3m23s
Docker images / updater-image (push) Successful in 44s
Docker images / backend-image (push) Successful in 2m47s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 11s
При первом рестарте wipe всех таблиц и импорт из SQLite в компактную схему. Retention через DROP PARTITION, lz4 и AIO worker. Co-authored-by: Cursor <cursoragent@cursor.com>
117 lines
4.0 KiB
TypeScript
117 lines
4.0 KiB
TypeScript
import { eq } from "drizzle-orm"
|
|
import { db, dbQuery, pool } from "../db/index.js"
|
|
import { dropExpiredPartitions, ensurePartitionsAround } from "../db/partitions.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"
|
|
import { invalidateFlowCatalogCache } from "./traffic-flow-topology.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 = (await db
|
|
.select()
|
|
.from(servers)
|
|
.where(eq(servers.id, serverId))
|
|
.limit(1))[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: ifaces,
|
|
rawIpAddresses: addresses,
|
|
} satisfies Partial<SnapshotInsert>)
|
|
|
|
if ((identity.name || "") !== (server.name || "")) {
|
|
await db.update(servers)
|
|
.set({ name: identity.name, updatedAt: now })
|
|
.where(eq(servers.id, serverId))
|
|
invalidateFlowCatalogCache()
|
|
}
|
|
|
|
} 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] = await db
|
|
.insert(serverSnapshots)
|
|
.values(partialSnap as SnapshotInsert)
|
|
.returning()
|
|
|
|
if (inserted) {
|
|
await dbQuery(
|
|
`UPDATE server_snapshots
|
|
SET raw_interfaces = NULL, raw_ip_addresses = NULL
|
|
WHERE server_id = $1 AND polled_at < $2
|
|
AND (raw_interfaces IS NOT NULL OR raw_ip_addresses IS NOT NULL)`,
|
|
[serverId, inserted.polledAt],
|
|
)
|
|
}
|
|
void dropExpiredPartitions(pool).then(() => ensurePartitionsAround(pool))
|
|
|
|
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,
|
|
}
|
|
}
|