import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { Button, Divider, Form, Input, InputNumber, Modal, Select, Space, Switch } from 'antd'; import { MinusOutlined, PlusOutlined } from '@ant-design/icons'; import InputAddon from '@/components/InputAddon'; import { DnsQueryStrategySchema, DnsServerObjectInnerSchema, DnsServerObjectSchema, type DnsServerObject, } from '@/schemas/dns'; import { antdRule } from '@/utils/zodForm'; export type DnsServerValue = | string | (DnsServerObject & { expectIPs?: string[]; [key: string]: unknown; }); interface DnsServerModalProps { open: boolean; server: DnsServerValue | null; isEdit: boolean; onClose: () => void; onConfirm: (value: DnsServerValue) => void; } const STRATEGIES = DnsQueryStrategySchema.options; type DnsServerForm = { address: string; port: number; domains: string[]; expectedIPs: string[]; unexpectedIPs: string[]; queryStrategy: string; skipFallback: boolean; disableCache: boolean; finalQuery: boolean; tag: string; clientIP: string; serveStale: boolean; serveExpiredTTL: number; timeoutMs: number; }; function defaultFormValues(): DnsServerForm { return { address: 'localhost', port: 53, domains: [], expectedIPs: [], unexpectedIPs: [], queryStrategy: 'UseIP', skipFallback: false, disableCache: false, finalQuery: false, tag: '', clientIP: '', serveStale: false, serveExpiredTTL: 0, timeoutMs: 4000, }; } function valuesFromServer(server: DnsServerValue | null): DnsServerForm { if (server == null) return defaultFormValues(); if (typeof server === 'string') return { ...defaultFormValues(), address: server }; const parsed = DnsServerObjectSchema.safeParse(server); const data = parsed.success ? parsed.data : null; return { ...defaultFormValues(), ...(data ?? {}), address: (data?.address ?? server.address) || 'localhost', domains: data?.domains ?? server.domains ?? [], expectedIPs: data?.expectedIPs ?? server.expectedIPs ?? server.expectIPs ?? [], unexpectedIPs: data?.unexpectedIPs ?? server.unexpectedIPs ?? [], queryStrategy: data?.queryStrategy ?? server.queryStrategy ?? 'UseIP', skipFallback: data?.skipFallback ?? server.skipFallback ?? false, disableCache: data?.disableCache ?? server.disableCache ?? false, finalQuery: data?.finalQuery ?? server.finalQuery ?? false, tag: data?.tag ?? server.tag ?? '', clientIP: data?.clientIP ?? server.clientIP ?? '', serveStale: data?.serveStale ?? server.serveStale ?? false, serveExpiredTTL: data?.serveExpiredTTL ?? server.serveExpiredTTL ?? 0, timeoutMs: data?.timeoutMs ?? server.timeoutMs ?? 4000, }; } function valuesToWire(values: DnsServerForm): DnsServerValue { const isPlain = values.domains.length === 0 && values.expectedIPs.length === 0 && values.unexpectedIPs.length === 0 && values.port === 53 && values.queryStrategy === 'UseIP' && values.skipFallback === false && values.disableCache === false && values.finalQuery === false && !values.tag && !values.clientIP && values.serveStale === false && values.serveExpiredTTL === 0 && values.timeoutMs === 4000; if (isPlain) return values.address; const out: Record = { address: values.address, port: values.port, domains: values.domains.filter(Boolean), expectedIPs: values.expectedIPs.filter(Boolean), unexpectedIPs: values.unexpectedIPs.filter(Boolean), queryStrategy: values.queryStrategy, skipFallback: values.skipFallback, disableCache: values.disableCache, finalQuery: values.finalQuery, serveStale: values.serveStale, serveExpiredTTL: values.serveExpiredTTL, timeoutMs: values.timeoutMs, }; if (values.tag) out.tag = values.tag; if (values.clientIP) out.clientIP = values.clientIP; return out as DnsServerValue; } const shape = DnsServerObjectInnerSchema.shape; export default function DnsServerModal({ open, server, isEdit, onClose, onConfirm, }: DnsServerModalProps) { const { t } = useTranslation(); const [form] = Form.useForm(); useEffect(() => { if (!open) return; form.setFieldsValue(valuesFromServer(server)); }, [open, server, form]); async function submit() { const values = await form.validateFields(); onConfirm(valuesToWire(values)); } const title = isEdit ? t('pages.xray.dns.edit') : t('pages.xray.dns.add'); return (
remove(field.name)}> ))} )} {(fields, { add, remove }) => (