Files
MikrotikManager/backend/src/services/gre-mutation-input.ts
T

41 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { isIP } from "node:net"
import { z } from "zod"
import { canonicalGreSnapshot, greAddressPath } from "./entity-snapshots.js"
import { formatKeepalive } from "./gre-live.js"
import { readGreComment, writeGreComment } from "./gre-comment.js"
const name = z.string().trim().min(1).max(100)
const outer = z.string().refine(value => isIP(value) === 4, "Требуется IPv4 endpoint GRE")
const inner = z.string().max(100).refine(value => { if (!value) return true; try { greAddressPath(value); return true } catch { return false } }, "Требуется IPv4/IPv6 с префиксом")
export const greNativeFieldsSchema = z.object({name:name.optional(),comment:z.string().max(1000).optional(),disabled:z.boolean().optional(),"local-address":z.string().max(1000).optional(),"remote-address":name.optional(),mtu:z.number().int().min(68).max(65535).optional(),keepalive:z.string().max(1000).optional()}).strict()
const fields = {
name, localAddress: z.union([z.literal(""), outer]).optional(), remoteAddress: outer,
localInnerIp: inner.optional(), remoteInnerIp: z.string().max(100).optional(), comment: z.string().max(1000).optional(), enabled: z.boolean().optional(),
mtu: z.number().int().min(68).max(65535).optional(), keepaliveInterval: z.number().int().min(1).max(4294967295).optional(), keepaliveRetries: z.number().int().min(1).max(4294967295).optional(),
dscp: z.union([z.literal("inherit"), z.number().int().min(0).max(63), z.string().regex(/^\d{1,2}$/).refine(value => Number(value) <= 63)]).optional(),
clampTcpMss: z.boolean().optional(), allowFastPath: z.boolean().optional(), ipsecSecret: z.string().max(1024).optional(),
}
export const greCreateFieldsSchema = z.object(fields).strict()
export const greUpdateFieldsSchema = greCreateFieldsSchema.omit({ name: true }).partial().strict()
export const greMutationSchema = z.discriminatedUnion("operation", [
z.object({ operation: z.literal("create"), fields: greCreateFieldsSchema }).strict(),
z.object({ operation: z.literal("update"), rosId: z.string().regex(/^\*[A-Za-z0-9]+$/).optional(), name: name.optional(), fields: greUpdateFieldsSchema }).strict().refine(value => !!(value.rosId || value.name), "Требуется native ID или имя GRE"),
z.object({ operation: z.literal("delete"), rosId: z.string().regex(/^\*[A-Za-z0-9]+$/).optional(), name: name.optional() }).strict().refine(value => !!(value.rosId || value.name), "Требуется native ID или имя GRE"),
]).superRefine((value, context) => {
if (value.operation !== "delete" && value.fields.ipsecSecret?.trim() && value.fields.allowFastPath === true) context.addIssue({code:"custom",message:"IPsec несовместим с FastPath",path:["fields","allowFastPath"]})
})
export type GreMutation = z.infer<typeof greMutationSchema>
export const GreTunnelWriteSchema = greCreateFieldsSchema.extend({ serverId: z.string().min(1), rosId: z.string().optional() }).strip()
export const GreTunnelKeySchema = z.object({ serverId: z.string().min(1), rosId: z.string().optional(), name: z.string().optional(), disabled: z.boolean().optional() })
export function greTunnelFromBody(body: z.infer<typeof greCreateFieldsSchema>) {
if (body.ipsecSecret?.trim() && body.allowFastPath === true) throw Object.assign(new Error("IPsec несовместим с FastPath"), { statusCode: 400 })
return canonicalGreSnapshot({ tunnels: [{
name: body.name, localAddress: body.localAddress ?? "", remoteAddress: body.remoteAddress, localInnerIp: body.localInnerIp ?? "",
remoteInnerIp: body.remoteInnerIp ?? readGreComment(body.comment).remoteInnerIp,
comment: body.remoteInnerIp === undefined ? body.comment ?? "" : writeGreComment(body.comment ?? "", body.remoteInnerIp), disabled: body.enabled === false,
mtu: body.mtu ?? 1476, keepalive: formatKeepalive(body.keepaliveInterval ?? 10, body.keepaliveRetries ?? 10), dscp: body.dscp === undefined ? "inherit" : String(body.dscp),
clampTcpMss: body.clampTcpMss ?? true, allowFastPath: body.allowFastPath ?? !body.ipsecSecret?.trim(), ipsecSecret: body.ipsecSecret ?? "",
}] }).tunnels[0]!
}