wireguard.ts 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. import { z } from 'zod';
  2. // AntD InputNumber emits null (not undefined) when the user clears it, and
  3. // the form store hands that null straight to safeParse on submit — a bare
  4. // .optional() would reject it and block the save.
  5. const optionalClearedInt = (schema: z.ZodNumber) =>
  6. z.preprocess((v) => (v == null ? undefined : v), schema.optional());
  7. // Wireguard inbound is peer-based (no clients). Each peer is a client device
  8. // the server accepts; secretKey is the server-side private key and pubKey is
  9. // derived from it at runtime (not persisted on the wire). Inbound peers
  10. // optionally store the client's privateKey so the panel can render configs
  11. // for the user — outbound peers never have a privateKey.
  12. export const WireguardInboundPeerSchema = z.object({
  13. privateKey: z.string().optional(),
  14. publicKey: z.string().min(1),
  15. preSharedKey: z.string().optional(),
  16. allowedIPs: z.array(z.string()).default([]),
  17. keepAlive: optionalClearedInt(z.number().int().min(0)),
  18. // Panel-only annotation (#5168): which client/device this peer belongs to.
  19. // Rides along in the settings JSON like privateKey does; xray-core ignores
  20. // unknown peer fields.
  21. comment: z.string().optional(),
  22. });
  23. export type WireguardInboundPeer = z.infer<typeof WireguardInboundPeerSchema>;
  24. // A WireGuard inbound client (multi-client model). Each client is one peer the
  25. // server accepts: the panel stores its keypair so it can render a full .conf/QR,
  26. // and allowedIPs is the client's unique tunnel address (allocated server-side
  27. // when left blank). Keys are optional on the wire — the backend generates them
  28. // when absent.
  29. export const WireguardClientSchema = z.object({
  30. privateKey: z.string().optional(),
  31. publicKey: z.string().optional(),
  32. preSharedKey: z.string().optional(),
  33. allowedIPs: z.array(z.string()).default([]),
  34. keepAlive: optionalClearedInt(z.number().int().min(0)),
  35. email: z.string().min(1),
  36. limitIp: z.number().int().min(0).default(0),
  37. totalGB: z.number().int().min(0).default(0),
  38. expiryTime: z.number().int().default(0),
  39. enable: z.boolean().default(true),
  40. tgId: z
  41. .union([z.number(), z.string()])
  42. .transform((v) => Number(v) || 0)
  43. .default(0),
  44. subId: z.string().default(''),
  45. comment: z.string().default(''),
  46. reset: z.number().int().min(0).default(0),
  47. created_at: z.number().int().optional(),
  48. updated_at: z.number().int().optional(),
  49. });
  50. export type WireguardClient = z.infer<typeof WireguardClientSchema>;
  51. export const WireguardInboundSettingsSchema = z.object({
  52. mtu: optionalClearedInt(z.number().int().min(1)),
  53. secretKey: z.string().min(1),
  54. dns: z.string().optional(),
  55. peers: z.array(WireguardInboundPeerSchema).default([]),
  56. clients: z.array(WireguardClientSchema).default([]),
  57. noKernelTun: z.boolean().default(false),
  58. // Admin-configurable base subnet new clients are auto-allocated from —
  59. // mirrors AmneziaWG's settings.server.subnetIp/subnetCidr. Optional and
  60. // left blank by default: an inbound that never sets this keeps the
  61. // pre-existing behavior (infer from existing clients' own addresses, else
  62. // fall back to 10.0.0.0/24 server-side).
  63. subnetIp: z.string().default(''),
  64. subnetCidr: optionalClearedInt(z.number().int().min(1).max(32)),
  65. });
  66. export type WireguardInboundSettings = z.infer<typeof WireguardInboundSettingsSchema>;