fix(netflow): согласовать проверку сетевых параметров формы и сервера

This commit is contained in:
Denozordec committed 2026-10-03 12:59:05 +07:00
1 parent 32b7812169
commit b80fa042d5
12 files changed
+56 -20

No files matched your search

+7
View File
@@ -8,3 +8,10 @@ test("NetFlow form sends selected values without defaulting invalid neighbours",
for(const raw of ["0","65536","4739x","4.5",""])assert.throws(()=>netflowDraftPatch({...emptyNetflowDraft,flowListenPort:raw},new Set(["flowListenPort"])))
for(const raw of ["0","100.01","NaN",""])assert.throws(()=>netflowDraftPatch({...emptyNetflowDraft,shareOn:true,mapServiceMinSharePct:raw},new Set(["mapServiceMinSharePct"])))
})
test("NetFlow form uses common network validation without altering valid endpoint",()=>{
for(const value of ["10.255.254.1","192.0.2.1"])assert.deepEqual(netflowDraftPatch({...emptyNetflowDraft,collectorIp:value},new Set(["collectorIp"])),{collectorIp:value})
for(const value of ["10.255.254.0/24","192.0.2.0/28"])assert.deepEqual(netflowDraftPatch({...emptyNetflowDraft,prefix:value},new Set(["prefix"])),{prefix:value})
for(const value of ["","collector","Collector.EXAMPLE.test.","192.0.2.1","2001:db8::1"])assert.deepEqual(netflowDraftPatch({...emptyNetflowDraft,publicEndpoint:value},new Set(["publicEndpoint"])),{publicEndpoint:value})
for(const [field,value] of [["collectorIp","300.1.1.1"],["collectorIp","2001:db8::1"],["prefix","10.0.0.0/33"],["publicEndpoint","https://host.test"],["publicEndpoint","host.test:51821"],["publicEndpoint","$(fixture)"],["publicEndpoint","bad_host.test"],["publicEndpoint","1.2.3.999"]] as const)assert.throws(()=>netflowDraftPatch({...emptyNetflowDraft,[field]:value},new Set([field])))
})
+5 -3
View File
@@ -1,4 +1,4 @@
import type { TrafficFlowSettingsDto, TrafficFlowSettingsPatch } from "@mmapp/contracts/traffic-flow"
import { trafficFlowSettingsPatchSchema, type TrafficFlowSettingsDto, type TrafficFlowSettingsPatch } from "@mmapp/contracts/traffic-flow"
export type NetflowDraft = { enabled: boolean; collectorIp: string; flowListenPort: string; wgListenPort: string; prefix: string; publicEndpoint: string; retentionHours: string; topN: string; shareOn: boolean; mapServiceMinSharePct: string }
export const emptyNetflowDraft: NetflowDraft = { enabled: false, collectorIp: "", flowListenPort: "", wgListenPort: "", prefix: "", publicEndpoint: "", retentionHours: "", topN: "", shareOn: false, mapServiceMinSharePct: "" }
export function netflowDraftFromSettings(data: TrafficFlowSettingsDto): NetflowDraft {
@@ -11,7 +11,7 @@ export function netflowDraftPatch(draft: NetflowDraft, dirty: ReadonlySet<keyof
if ((field !== "publicEndpoint" && !value) || /[\r\n]/.test(value)) throw new Error("Укажите корректное значение: " + field)
patch[field] = value
}
for (const [field, label, max] of [["flowListenPort","UDP IPFIX",65535],["wgListenPort","WG listen",65535],["retentionHours","Хранение (часов)",Number.MAX_SAFE_INTEGER],["topN","Top-N разговоров",1000]] as const) if (dirty.has(field)) {
for (const [field, label, max] of [["flowListenPort","UDP IPFIX",65535],["wgListenPort","WG listen",65535],["retentionHours","Хранение (часов)",2147483647],["topN","Top-N разговоров",1000]] as const) if (dirty.has(field)) {
const raw = draft[field], value = Number(raw)
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(value) || value < 1 || value > max) throw new Error(label + ": укажите целое число от 1 до " + max)
patch[field] = value
@@ -22,5 +22,7 @@ export function netflowDraftPatch(draft: NetflowDraft, dirty: ReadonlySet<keyof
if (draft.shareOn && (!/^\d+(?:\.\d+)?$/.test(draft.mapServiceMinSharePct) || !Number.isFinite(value) || value <= 0 || value > 100)) throw new Error("Порог карты: укажите долю больше 0 и не более 100%")
patch.mapServiceMinSharePct = draft.shareOn ? value : 0
}
return patch
const parsed = trafficFlowSettingsPatchSchema.safeParse(patch)
if (!parsed.success) throw new Error(parsed.error.issues[0]?.message ?? "Укажите допустимые настройки NetFlow")
return parsed.data
}
+1 -1
View File
@@ -18,7 +18,7 @@ import { requestJson } from "@/shared/api/http-client"
export type { TrafficFlowHostFile }
const flowSettingsSchema = trafficFlowSettingsDtoSchema.extend({ flowListenPort: z.number().int().min(1).max(65535), wgListenPort: z.number().int().min(1).max(65535) }).refine(data => (!data.hasHostPrivateKey || data.hostPublicKey.length > 0) && (!data.listenerBound || !!data.listenerAddress) && (data.lastDatagramAt === null || Number.isFinite(Date.parse(data.lastDatagramAt))) && new Set(data.peers.map(peer => peer.serverId)).size === data.peers.length, "Несогласованные настройки NetFlow")
const flowPatchSchema = trafficFlowSettingsPatchSchema.extend({ flowListenPort: z.number().int().min(1).max(65535).optional(), wgListenPort: z.number().int().min(1).max(65535).optional() }).strict()
const flowPatchSchema = trafficFlowSettingsPatchSchema.strict()
function flowConfirmed<T>(schema: z.ZodType<T>, raw: unknown): T {
const parsed = schema.safeParse(raw)
if (!parsed.success) throw new Error("Данные NetFlow не подтверждены: получен некорректный ответ сервера")