feat(traffic): добавить приём Traffic Flow с jump-host
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 2m1s
Docker images / frontend-image (push) Successful in 3m56s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 46s
Docker images / publish-release (push) Successful in 12s

Чтобы видеть «кто с кем», а не только объём порта: IPFIX внутри WG на хосте Docker MM, REST-счётчики не трогаем.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-09-06 21:54:30 +07:00
co-authored by Cursor
parent 5884bd8873
commit 1e9312acbd
24 changed files with 2007 additions and 71 deletions
+4
View File
@@ -41,6 +41,10 @@
"./users": {
"types": "./dist/users.d.ts",
"default": "./dist/users.js"
},
"./traffic-flow": {
"types": "./dist/traffic-flow.d.ts",
"default": "./dist/traffic-flow.js"
}
},
"dependencies": {
+1
View File
@@ -5,3 +5,4 @@ export * from "./certificates.js"
export * from "./backups.js"
export * from "./wireguard.js"
export * from "./users.js"
export * from "./traffic-flow.js"
+88
View File
@@ -0,0 +1,88 @@
import { z } from "zod"
export const flowHostPeerSchema = z.object({
serverId: z.number().int().positive(),
name: z.string(),
publicKey: z.string().min(1),
allowedIps: z.array(z.string().min(1)).min(1),
address: z.string().min(1),
})
export const trafficFlowSettingsDtoSchema = z.object({
enabled: z.boolean(),
collectorIp: z.string().min(1),
flowListenPort: z.number().int().positive(),
wgListenPort: z.number().int().positive(),
prefix: z.string().min(1),
publicEndpoint: z.string(),
hostPublicKey: z.string(),
hasHostPrivateKey: z.boolean(),
hubServerId: z.number().int().positive().nullable(),
retentionHours: z.number().int().positive(),
topN: z.number().int().positive(),
lastDatagramAt: z.string().nullable(),
lastExporterIp: z.string().nullable(),
lastError: z.string().nullable(),
packetsReceived: z.number().int().nonnegative(),
listenerBound: z.boolean(),
listenerAddress: z.string().nullable(),
peers: z.array(flowHostPeerSchema),
})
export const trafficFlowSettingsPatchSchema = z.object({
enabled: z.boolean().optional(),
collectorIp: z.string().min(1).optional(),
flowListenPort: z.number().int().positive().optional(),
wgListenPort: z.number().int().positive().optional(),
prefix: z.string().min(1).optional(),
publicEndpoint: z.string().optional(),
hubServerId: z.number().int().positive().nullable().optional(),
retentionHours: z.number().int().positive().optional(),
topN: z.number().int().positive().max(1000).optional(),
})
export const trafficFlowOverlayRequestSchema = z.object({
serverId: z.union([z.string(), z.number()]),
})
export const trafficFlowOverlayResultSchema = z.object({
ok: z.boolean(),
serverId: z.number().int(),
interfaceName: z.string(),
address: z.string(),
publicKey: z.string(),
linuxPeerBlock: z.string(),
trafficFlow: z.boolean(),
steps: z.array(z.string()),
})
export const flowTalkerDtoSchema = z.object({
serverId: z.string(),
serverName: z.string(),
src: z.string(),
dst: z.string(),
proto: z.number().int(),
protoName: z.string(),
srcPort: z.number().int(),
dstPort: z.number().int(),
bytes: z.number().nonnegative(),
packets: z.number().nonnegative(),
bps: z.number().nonnegative(),
inIface: z.string(),
})
export const flowStatsDtoSchema = z.object({
exportersOnline: z.number().int().nonnegative(),
bytesPerMin: z.number().nonnegative(),
uniqueSrc: z.number().int().nonnegative(),
uniqueDst: z.number().int().nonnegative(),
topProto: z.string(),
talkers: z.array(flowTalkerDtoSchema),
})
export type FlowHostPeer = z.infer<typeof flowHostPeerSchema>
export type TrafficFlowSettingsDto = z.infer<typeof trafficFlowSettingsDtoSchema>
export type TrafficFlowSettingsPatch = z.infer<typeof trafficFlowSettingsPatchSchema>
export type TrafficFlowOverlayResult = z.infer<typeof trafficFlowOverlayResultSchema>
export type FlowTalkerDto = z.infer<typeof flowTalkerDtoSchema>
export type FlowStatsDto = z.infer<typeof flowStatsDtoSchema>