100 lines
3.4 KiB
TypeScript
100 lines
3.4 KiB
TypeScript
import { z } from 'zod';
|
|
import { PROTOCOLS } from './protocols.js';
|
|
|
|
export const nodeInputSchema = z
|
|
.object({
|
|
name: z.string().min(1).max(64),
|
|
protocol: z.enum(PROTOCOLS),
|
|
server: z.string().min(1),
|
|
port: z.number().int().min(1).max(65535),
|
|
network: z.enum(['tcp', 'ws']).default('tcp'),
|
|
wsPath: z.string().startsWith('/').optional().nullable(),
|
|
wsHost: z.string().optional().nullable(),
|
|
tls: z.boolean().default(false),
|
|
sni: z.string().optional().nullable(),
|
|
skipCertVerify: z.boolean().default(false),
|
|
uuid: z.string().uuid().optional().nullable(),
|
|
alterId: z.number().int().min(0).default(0),
|
|
security: z.string().default('auto'),
|
|
password: z.string().optional().nullable(),
|
|
method: z.string().optional().nullable(),
|
|
obfsType: z.enum(['salamander']).optional().nullable(),
|
|
obfsPassword: z.string().optional().nullable(),
|
|
upMbps: z.number().int().positive().optional().nullable(),
|
|
downMbps: z.number().int().positive().optional().nullable(),
|
|
region: z.string().max(8).optional().nullable(),
|
|
tags: z.array(z.string()).default([]),
|
|
enabled: z.boolean().default(true),
|
|
sortOrder: z.number().int().default(0),
|
|
})
|
|
.superRefine((v, ctx) => {
|
|
if (v.protocol === 'vmess' && !v.uuid) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['uuid'], message: 'VMess 需要 UUID' });
|
|
}
|
|
if ((v.protocol === 'trojan' || v.protocol === 'shadowsocks' || v.protocol === 'hysteria2') && !v.password) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['password'], message: '该协议需要密码' });
|
|
}
|
|
if (v.protocol === 'shadowsocks' && !v.method) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['method'], message: 'Shadowsocks 需要加密方式' });
|
|
}
|
|
if (v.network === 'ws' && !v.wsPath) {
|
|
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['wsPath'], message: 'WebSocket 传输需要路径' });
|
|
}
|
|
});
|
|
|
|
export type NodeInput = z.input<typeof nodeInputSchema>;
|
|
export type NodeParsed = z.output<typeof nodeInputSchema>;
|
|
|
|
export const groupInputSchema = z.object({
|
|
name: z.string().min(1).max(64),
|
|
type: z.enum(['select', 'url-test', 'fallback']),
|
|
testUrl: z.string().url().optional().nullable(),
|
|
interval: z.number().int().positive().optional().nullable(),
|
|
tolerance: z.number().int().positive().optional().nullable(),
|
|
filterRegex: z.string().optional().nullable(),
|
|
includeAllNodes: z.boolean().default(false),
|
|
sortOrder: z.number().int().default(0),
|
|
members: z
|
|
.array(
|
|
z.object({
|
|
kind: z.enum(['node', 'group', 'builtin']),
|
|
nodeId: z.string().optional().nullable(),
|
|
refName: z.string().optional().nullable(),
|
|
})
|
|
)
|
|
.default([]),
|
|
});
|
|
export type GroupInput = z.input<typeof groupInputSchema>;
|
|
|
|
export const RULE_TYPES = [
|
|
'RULE-SET',
|
|
'DOMAIN-SET',
|
|
'DOMAIN',
|
|
'DOMAIN-SUFFIX',
|
|
'DOMAIN-KEYWORD',
|
|
'IP-CIDR',
|
|
'IP-CIDR6',
|
|
'IP-ASN',
|
|
'GEOIP',
|
|
'PROCESS-NAME',
|
|
'USER-AGENT',
|
|
'URL-REGEX',
|
|
'AND',
|
|
'OR',
|
|
'NOT',
|
|
'FINAL',
|
|
] as const;
|
|
|
|
export const ruleInputSchema = z.object({
|
|
type: z.enum(RULE_TYPES),
|
|
value: z.string().optional().nullable(),
|
|
policy: z.string().min(1),
|
|
params: z.array(z.string()).default([]),
|
|
enabled: z.boolean().default(true),
|
|
});
|
|
export type RuleInput = z.input<typeof ruleInputSchema>;
|
|
|
|
export const tokenInputSchema = z.object({
|
|
name: z.string().min(1).max(64),
|
|
});
|