import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Button, Divider, Form, Input, InputNumber, Modal, Select, Space, Switch } from 'antd'; import { PlusOutlined, MinusOutlined } from '@ant-design/icons'; import InputAddon from '@/components/InputAddon'; export type DnsServerValue = | string | { address: string; port?: number; domains?: string[]; expectedIPs?: string[]; expectIPs?: string[]; unexpectedIPs?: string[]; queryStrategy?: string; skipFallback?: boolean; disableCache?: boolean; finalQuery?: boolean; tag?: string; clientIP?: string; serveStale?: boolean; serveExpiredTTL?: number; timeoutMs?: number; [key: string]: unknown; }; interface DnsServerModalProps { open: boolean; server: DnsServerValue | null; isEdit: boolean; onClose: () => void; onConfirm: (value: DnsServerValue) => void; } const STRATEGIES = ['UseSystem', 'UseIP', 'UseIPv4', 'UseIPv6']; interface DnsForm { 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 defaultServer(): DnsForm { return { address: 'localhost', port: 53, domains: [], expectedIPs: [], unexpectedIPs: [], queryStrategy: 'UseIP', skipFallback: false, disableCache: false, finalQuery: false, tag: '', clientIP: '', serveStale: false, serveExpiredTTL: 0, timeoutMs: 4000, }; } export default function DnsServerModal({ open, server, isEdit, onClose, onConfirm, }: DnsServerModalProps) { const { t } = useTranslation(); const [form, setForm] = useState(defaultServer()); useEffect(() => { if (!open) return; if (server == null) { setForm(defaultServer()); return; } if (typeof server === 'string') { setForm({ ...defaultServer(), address: server }); return; } setForm({ ...defaultServer(), ...server, domains: [...(server.domains || [])], expectedIPs: [...(server.expectedIPs || server.expectIPs || [])], unexpectedIPs: [...(server.unexpectedIPs || [])], }); }, [open, server]); const update = (key: K, value: DnsForm[K]) => setForm((prev) => ({ ...prev, [key]: value })); function updateList(key: 'domains' | 'expectedIPs' | 'unexpectedIPs', mutator: (next: string[]) => void) { setForm((prev) => { const next = [...prev[key]]; mutator(next); return { ...prev, [key]: next }; }); } function submit() { const isPlain = form.domains.length === 0 && form.expectedIPs.length === 0 && form.unexpectedIPs.length === 0 && form.port === 53 && form.queryStrategy === 'UseIP' && form.skipFallback === false && form.disableCache === false && form.finalQuery === false && !form.tag && !form.clientIP && form.serveStale === false && form.serveExpiredTTL === 0 && form.timeoutMs === 4000; if (isPlain) { onConfirm(form.address); return; } const out: Record = { address: form.address, port: form.port, domains: form.domains.filter(Boolean), expectedIPs: form.expectedIPs.filter(Boolean), unexpectedIPs: form.unexpectedIPs.filter(Boolean), queryStrategy: form.queryStrategy, skipFallback: form.skipFallback, disableCache: form.disableCache, finalQuery: form.finalQuery, serveStale: form.serveStale, serveExpiredTTL: form.serveExpiredTTL, timeoutMs: form.timeoutMs, }; if (form.tag) out.tag = form.tag; if (form.clientIP) out.clientIP = form.clientIP; onConfirm(out as DnsServerValue); } const title = isEdit ? t('pages.xray.dns.edit') : t('pages.xray.dns.add'); return (
update('address', e.target.value)} /> update('port', Number(v) || 53)} /> update('tag', e.target.value)} /> update('clientIP', e.target.value)} /> updateList('domains', (d) => { d[idx] = e.target.value; })} /> updateList('domains', (d) => d.splice(idx, 1))}> ))}