41 lines
4.0 KiB
TypeScript
41 lines
4.0 KiB
TypeScript
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]!
|
||
}
|