1import { z } from "zod";
2import i18n from "i18next";
3
4const numberInterval = (v: string | null | undefined) => {
5 return v ? /^[\d-]{1,32}$/.test(v) : false;
6};
7
8const packetsInterval = (v: string | null | undefined) => {
9 return v ? /^(:?tlshello|[\d-]{1,32})$/.test(v) : false;
10};
11
12export const alpnOptions = [
13 "none",
14 "h2",
15 "h3",
16 "h3,h2",
17 "http/1.1",
18 "h2,http/1.1",
19 "h3,h2,http/1.1"
20]
21
22export const HostSchema = z.object({
23 remark: z.string().min(1, "Remark is required"),
24 address: z.string().min(1, "Address is required"),
25 weight: z.coerce
26 .number().int()
27 .nullable()
28 .optional(),
29 port: z.coerce
30 .number()
31 .gte(1, "Port must be more than 1")
32 .lte(65535, "Port can not be more than 65535")
33 .or(z.string())
34 .nullable()
35 .optional(),
36 path: z.string().nullable().optional(),
37 sni: z.string().nullable().optional(),
38 host: z.string().nullable().optional(),
39 mux: z.boolean().nullable().optional(),
40 fragment: z
41 .object({
42 interval: z
43 .string()
44 .refine(
45 (v) => numberInterval(v),
46 i18n.t("page.hosts.fragment.interval-error"),
47 ),
48 length: z
49 .string()
50 .refine(
51 (v) => numberInterval(v),
52 i18n.t("page.hosts.fragment.length-error"),
53 ),
54 packets: z
55 .string()
56 .refine(
57 (v) => packetsInterval(v),
58 i18n.t("page.hosts.fragment.packets-error"),
59 ),
60 })
61 .nullable()
62 .optional(),
63 security: z
64 .enum(["inbound_default", "none", "tls"])
65 .default("inbound_default"),
66 alpn: z
67 .enum(["", ...alpnOptions])
68 .optional()
69 .default("none"),
70 allowinsecure: z.boolean().default(false).optional(),
71 fingerprint: z
72 .enum([
73 "",
74 "none",
75 "chrome",
76 "firefox",
77 "safari",
78 "ios",
79 "android",
80 "edge",
81 "360",
82 "qq",
83 "random",
84 "randomized",
85 ])
86 .optional()
87 .default("none"),
88 is_disabled: z.boolean(),
89});