fix(netflow): изолировать коллектор IPFIX и срезать раздувание базы
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 2m23s
Docker images / frontend-image (push) Successful in 3m16s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 57s
Docker images / publish-release (push) Successful in 12s
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 2m23s
Docker images / frontend-image (push) Successful in 3m16s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 57s
Docker images / publish-release (push) Successful in 12s
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
e0ddb17539
commit
cb799da13a
24 files changed
+1884
-530
No files matched your search
@@ -18,13 +18,31 @@ import {
|
||||
} from "../services/traffic-flow-ingest.js"
|
||||
import {
|
||||
buildFlowAnalytics,
|
||||
getFlowMonthly,
|
||||
listFlowClients,
|
||||
listFlowExporters,
|
||||
safeBuildLiveFlowSample,
|
||||
} from "../services/traffic-flow-analytics.js"
|
||||
import { applyFlowOverlay } from "../services/traffic-flow-overlay.js"
|
||||
import { listTrafficFlowHostFiles } from "../services/traffic-flow-host-files.js"
|
||||
|
||||
const LIVE_TICK_MS = 2000
|
||||
export const MAX_FLOW_LIVE_SUBSCRIBERS = 4
|
||||
let liveSubscribers = 0
|
||||
|
||||
export function tryAcquireFlowLiveSlot(): boolean {
|
||||
if (liveSubscribers >= MAX_FLOW_LIVE_SUBSCRIBERS) return false
|
||||
liveSubscribers += 1
|
||||
return true
|
||||
}
|
||||
|
||||
export function releaseFlowLiveSlot(): void {
|
||||
liveSubscribers = Math.max(0, liveSubscribers - 1)
|
||||
}
|
||||
|
||||
export function resetFlowLiveSlotsForTests(): void {
|
||||
liveSubscribers = 0
|
||||
}
|
||||
|
||||
function rangeToMinutes(range: string | undefined): number {
|
||||
switch ((range ?? "5m").toLowerCase()) {
|
||||
@@ -33,6 +51,7 @@ function rangeToMinutes(range: string | undefined): number {
|
||||
case "1h": return 60
|
||||
case "4h": return 240
|
||||
case "24h": return 1440
|
||||
case "30d": return 1440
|
||||
default: return 5
|
||||
}
|
||||
}
|
||||
@@ -162,8 +181,26 @@ const trafficFlowRoutes: FastifyPluginAsyncZod = async (app) => {
|
||||
return reply.send(buildFlowAnalytics(analyticsQuery(req)))
|
||||
})
|
||||
|
||||
app.get("/traffic/flow/monthly", async (req, reply) => {
|
||||
const q = req.query as { month?: string; serverId?: string }
|
||||
const now = new Date()
|
||||
const month = /^\d{4}-\d{2}$/.test(q.month ?? "")
|
||||
? (q.month as string)
|
||||
: `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, "0")}`
|
||||
return reply.send(getFlowMonthly(month, parseId(q.serverId)))
|
||||
})
|
||||
|
||||
app.get("/traffic/flow/live", async (req, reply) => {
|
||||
if (!tryAcquireFlowLiveSlot()) {
|
||||
return reply.status(429).send({ error: "Слишком много live-подписок" })
|
||||
}
|
||||
const query = analyticsQuery(req)
|
||||
const liveQuery = {
|
||||
serverId: query.serverId,
|
||||
userId: query.userId,
|
||||
iface: query.iface,
|
||||
dedup: query.dedup,
|
||||
}
|
||||
const abort = new AbortController()
|
||||
const onClose = () => abort.abort()
|
||||
req.raw.on("close", onClose)
|
||||
@@ -190,12 +227,14 @@ const trafficFlowRoutes: FastifyPluginAsyncZod = async (app) => {
|
||||
|
||||
try {
|
||||
while (!abort.signal.aborted) {
|
||||
writeSse(reply.raw, "sample", buildFlowAnalytics(query))
|
||||
const payload = safeBuildLiveFlowSample(liveQuery)
|
||||
writeSse(reply.raw, payload.event, payload.data)
|
||||
await sleep(LIVE_TICK_MS, abort.signal)
|
||||
}
|
||||
} catch {
|
||||
/* abort / disconnect */
|
||||
} finally {
|
||||
releaseFlowLiveSlot()
|
||||
req.raw.off("close", onClose)
|
||||
try {
|
||||
reply.raw.end()
|
||||
|
||||
Reference in new issue
Block a user