Docker images / prepare-release (push) Successful in 8s
Docker images / backend-image (push) Failing after 1m31s
Docker images / frontend-image (push) Successful in 2m36s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 42s
Docker images / publish-release (push) Skipped
Считать payload отдельно от overlay GRE/ESP и mesh; вкладка Пути и KPI Wire из счётчиков iface. Co-authored-by: Cursor <cursoragent@cursor.com>
144 lines
4.7 KiB
TypeScript
144 lines
4.7 KiB
TypeScript
import { eq } from "drizzle-orm"
|
|
import { db } from "../db/index.js"
|
|
import { trafficFlowSettings } from "../db/schema.js"
|
|
import type { FlowHostPeer, TrafficFlowSettingsDto, TrafficFlowSettingsPatch } from "@mmapp/contracts/traffic-flow"
|
|
import { generateWireGuardKeyPair } from "./wg-keys.js"
|
|
|
|
function nowIso() {
|
|
return new Date().toISOString()
|
|
}
|
|
|
|
function parsePeers(raw: string): FlowHostPeer[] {
|
|
try {
|
|
const parsed = JSON.parse(raw) as unknown
|
|
if (!Array.isArray(parsed)) return []
|
|
return parsed.filter((p): p is FlowHostPeer =>
|
|
p != null && typeof p === "object" && typeof (p as FlowHostPeer).publicKey === "string",
|
|
)
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|
|
|
|
export function getTrafficFlowSettingsRow() {
|
|
const row = db.select().from(trafficFlowSettings).where(eq(trafficFlowSettings.id, 1)).limit(1).all()[0]
|
|
if (row) return row
|
|
const now = nowIso()
|
|
db.insert(trafficFlowSettings).values({
|
|
id: 1,
|
|
enabled: false,
|
|
collectorIp: "10.255.254.1",
|
|
flowListenPort: 4739,
|
|
wgListenPort: 51821,
|
|
prefix: "10.255.254.0/24",
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
}).run()
|
|
return db.select().from(trafficFlowSettings).where(eq(trafficFlowSettings.id, 1)).limit(1).all()[0]
|
|
}
|
|
|
|
export function toTrafficFlowSettingsDto(
|
|
listener: { bound: boolean; address: string | null },
|
|
): TrafficFlowSettingsDto {
|
|
const row = getTrafficFlowSettingsRow()
|
|
return {
|
|
enabled: row.enabled,
|
|
collectorIp: row.collectorIp,
|
|
flowListenPort: row.flowListenPort,
|
|
wgListenPort: row.wgListenPort,
|
|
prefix: row.prefix,
|
|
publicEndpoint: row.publicEndpoint,
|
|
hostPublicKey: row.hostPublicKey,
|
|
hasHostPrivateKey: Boolean(row.hostPrivateKey),
|
|
hubServerId: row.hubServerId ?? null,
|
|
retentionHours: row.retentionHours,
|
|
topN: row.topN,
|
|
lastDatagramAt: row.lastDatagramAt ?? null,
|
|
lastExporterIp: row.lastExporterIp ?? null,
|
|
lastError: row.lastError || null,
|
|
packetsReceived: row.packetsReceived,
|
|
listenerBound: listener.bound,
|
|
listenerAddress: listener.address,
|
|
peers: parsePeers(row.peersJson),
|
|
}
|
|
}
|
|
|
|
export function updateTrafficFlowSettings(patch: TrafficFlowSettingsPatch) {
|
|
const row = getTrafficFlowSettingsRow()
|
|
db.update(trafficFlowSettings).set({
|
|
enabled: patch.enabled ?? row.enabled,
|
|
collectorIp: patch.collectorIp ?? row.collectorIp,
|
|
flowListenPort: patch.flowListenPort ?? row.flowListenPort,
|
|
wgListenPort: patch.wgListenPort ?? row.wgListenPort,
|
|
prefix: patch.prefix ?? row.prefix,
|
|
publicEndpoint: patch.publicEndpoint ?? row.publicEndpoint,
|
|
hubServerId: patch.hubServerId === undefined ? row.hubServerId : patch.hubServerId,
|
|
retentionHours: patch.retentionHours ?? row.retentionHours,
|
|
topN: patch.topN ?? row.topN,
|
|
updatedAt: nowIso(),
|
|
}).where(eq(trafficFlowSettings.id, 1)).run()
|
|
return getTrafficFlowSettingsRow()
|
|
}
|
|
|
|
export function ensureHostKeys(): { publicKey: string; created: boolean } {
|
|
const row = getTrafficFlowSettingsRow()
|
|
if (row.hostPublicKey && row.hostPrivateKey) {
|
|
return { publicKey: row.hostPublicKey, created: false }
|
|
}
|
|
const keys = generateWireGuardKeyPair()
|
|
db.update(trafficFlowSettings).set({
|
|
hostPublicKey: keys.publicKey,
|
|
hostPrivateKey: keys.privateKey,
|
|
updatedAt: nowIso(),
|
|
}).where(eq(trafficFlowSettings.id, 1)).run()
|
|
return { publicKey: keys.publicKey, created: true }
|
|
}
|
|
|
|
export function upsertHostPeer(peer: FlowHostPeer) {
|
|
const row = getTrafficFlowSettingsRow()
|
|
const peers = parsePeers(row.peersJson).filter((p) => p.serverId !== peer.serverId)
|
|
peers.push(peer)
|
|
db.update(trafficFlowSettings).set({
|
|
peersJson: JSON.stringify(peers),
|
|
updatedAt: nowIso(),
|
|
}).where(eq(trafficFlowSettings.id, 1)).run()
|
|
}
|
|
|
|
export function recordFlowPacket(exporterIp: string) {
|
|
const row = getTrafficFlowSettingsRow()
|
|
db.update(trafficFlowSettings).set({
|
|
lastDatagramAt: nowIso(),
|
|
lastExporterIp: exporterIp,
|
|
packetsReceived: row.packetsReceived + 1,
|
|
updatedAt: nowIso(),
|
|
}).where(eq(trafficFlowSettings.id, 1)).run()
|
|
}
|
|
|
|
export function recordFlowListenerError(message: string) {
|
|
db.update(trafficFlowSettings).set({
|
|
lastError: message,
|
|
updatedAt: nowIso(),
|
|
}).where(eq(trafficFlowSettings.id, 1)).run()
|
|
}
|
|
|
|
export function enableTrafficFlowIngest() {
|
|
db.update(trafficFlowSettings).set({
|
|
enabled: true,
|
|
updatedAt: nowIso(),
|
|
}).where(eq(trafficFlowSettings.id, 1)).run()
|
|
}
|
|
|
|
export function listHostPeers(): FlowHostPeer[] {
|
|
return parsePeers(getTrafficFlowSettingsRow().peersJson)
|
|
}
|
|
|
|
export function resetFlowIngestCounters(): void {
|
|
db.update(trafficFlowSettings).set({
|
|
packetsReceived: 0,
|
|
lastDatagramAt: null,
|
|
lastExporterIp: null,
|
|
lastError: "",
|
|
updatedAt: nowIso(),
|
|
}).where(eq(trafficFlowSettings.id, 1)).run()
|
|
}
|