import { useState } from 'react' import { Settings, Server, Network, Cpu, Save, RotateCcw, AlertCircle, Dice5, Plus, Trash2, Layers, } from 'lucide-react' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' import { Switch } from '@/components/ui/switch' import { Slider } from '@/components/ui/slider' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' import { Tabs, TabsContent, TabsList, TabsTrigger, } from '@/components/ui/tabs' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel, } from '@/components/ui/field' import { toast } from '@/components/ui/toast' import { useKlalbConfig } from '@/hooks/use-klalb-config' import type { KLALBControllerConfig } from '@/types/api' const LANGUAGE_ITEMS = [ { value: 'ZH_CN', label: '简体中文 (ZH_CN)' }, { value: 'EN_US', label: 'English (EN_US)' }, ] const CONGESTION_ITEMS = [ { value: 'BBR', label: 'BBR (高吞吐低延迟,推荐)' }, { value: 'Vegas2', label: 'Vegas2 (基于延迟的平滑算法)' }, { value: 'DCTCP', label: 'DCTCP (数据中心低时延拥塞控制)' }, ] const PERFORMANCE_ITEMS = [ { value: 'multifill', label: '多核按顺序填充 (multifill - 推荐通用)' }, { value: 'multiscatter', label: '多核均匀打散 (multiscatter - 适合多路NUMA)' }, { value: 'singlecore', label: '单核节能优先 (singlecore - 低功耗/云机)' }, ] // Random IPv6 generator compatible with KLALB format function generateRandomIPv6(): string { const hex = () => Math.floor(Math.random() * 0x10000).toString(16) return `2486:${hex()}:${hex()}:${hex()}:${hex()}:${hex()}:${hex()}:${hex()}` } // Helper to safely extract address string whether it's a string or an object with host/port function toAddrString(item: unknown): string { if (!item) return '' if (typeof item === 'string') return item if (typeof item === 'object' && item !== null) { const obj = item as Record if (obj.host && obj.port) { const proto = obj.protocol ? `${obj.protocol}://` : '' return `${proto}${obj.host}:${obj.port}` } if (obj.name && typeof obj.name === 'string') return obj.name if (obj.address && typeof obj.address === 'string') return obj.address } return String(item) } function SettingsForm({ initialConfig, onSave, onReset, isSaving, }: { initialConfig: KLALBControllerConfig onSave: (config: KLALBControllerConfig) => Promise onReset: () => void isSaving: boolean }) { const [form, setForm] = useState(() => ({ ...initialConfig, TCPListen: toAddrString(initialConfig.TCPListen), UDPListen: toAddrString(initialConfig.UDPListen), webListen: initialConfig.webListen ?? (initialConfig.webPort ? `http://0.0.0.0:${initialConfig.webPort}` : 'http://0.0.0.0:4665'), })) const [tunEnabled, setTunEnabled] = useState(() => initialConfig.enableTUN ?? (initialConfig.TUNName !== null && initialConfig.TUNName !== undefined && initialConfig.TUNName.trim() !== '') ) const [autoConnections, setAutoConnections] = useState(() => ( Array.isArray(initialConfig.autoConnections) ? initialConfig.autoConnections : Array.isArray(initialConfig.ConnectLineTable) ? initialConfig.ConnectLineTable : [] ).map(toAddrString) ) const [externalEndpoints, setExternalEndpoints] = useState(() => ( Array.isArray(initialConfig.externalEndpoints) ? initialConfig.externalEndpoints : Array.isArray(initialConfig.openConnections) ? initialConfig.openConnections : Array.isArray(initialConfig.LineTable) ? initialConfig.LineTable : [] ).map(toAddrString) ) const [ntpServers, setNtpServers] = useState(() => ( Array.isArray(initialConfig.ntpServers) ? initialConfig.ntpServers : Array.isArray(initialConfig.ntpServerTable) ? initialConfig.ntpServerTable : [] ).map(toAddrString) ) const [dnsList, setDnsList] = useState(() => (Array.isArray(initialConfig.DNS) ? initialConfig.DNS : []).map(toAddrString) ) const [extraRoutes, setExtraRoutes] = useState(() => (Array.isArray(initialConfig.ExtraRoutes) ? initialConfig.ExtraRoutes : []).map(toAddrString) ) const [interfaceExcepts, setInterfaceExcepts] = useState(() => ( Array.isArray(initialConfig.NetworkInterfaceExcepts) ? initialConfig.NetworkInterfaceExcepts : [] ).map(toAddrString) ) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() const updatedAutoConnections = autoConnections .filter((address) => address.trim() !== '') .map((address) => address.trim()) const updatedExternalEndpoints = externalEndpoints .filter((address) => address.trim() !== '') .map((address) => address.trim()) // 保存时同时携带新键名与历史别名,兼容新旧后端 const denyQuery = form.denyExternalEndpointQuery ?? form.denyConnectionQuery ?? form.denyLineTableQuery ?? false const denyBroadcast = form.denyExternalEndpointBroadcast ?? form.denyConnectionBroadcast ?? form.denyLineTableBroadcast ?? false const webListen = form.webListen?.trim() || 'http://0.0.0.0:4665' let webPort = 4665 try { const parsedPort = new URL(webListen).port if (parsedPort) webPort = parseInt(parsedPort, 10) } catch { // Java backend validates the canonical listen address. } const updatedConfig: Record = { ...form, webListen, webPort, externalEndpoints: updatedExternalEndpoints, openConnections: updatedExternalEndpoints, autoConnections: updatedAutoConnections, LineTable: updatedExternalEndpoints, ConnectLineTable: updatedAutoConnections, ntpServers: ntpServers.filter((s) => s.trim() !== ''), ntpServerTable: ntpServers.filter((s) => s.trim() !== ''), DNS: dnsList.filter((s) => s.trim() !== ''), ExtraRoutes: extraRoutes.filter((s) => s.trim() !== ''), NetworkInterfaceExcepts: interfaceExcepts.filter((s) => s.trim() !== ''), enableTUN: tunEnabled, TUNName: form.TUNName?.trim() || 'KLALB_SRv6', denyExternalEndpointQuery: denyQuery, denyExternalEndpointBroadcast: denyBroadcast, denyConnectionQuery: denyQuery, denyConnectionBroadcast: denyBroadcast, Type: 'KLALBController', } await onSave(updatedConfig as KLALBControllerConfig) } // --- Generic Dynamic List Handlers --- const addListItem = (setter: React.Dispatch>) => { setter((prev) => [...prev, '']) } const updateListItem = ( index: number, value: string, setter: React.Dispatch> ) => { setter((prev) => { const next = [...prev] next[index] = value return next }) } const removeListItem = ( index: number, setter: React.Dispatch> ) => { setter((prev) => prev.filter((_, i) => i !== index)) } return (
{/* Header Banner */}

系统配置

管理 KLALB SRv6 路由控制核心、网络寻址、连接与性能调度参数

{/* Main Tabs Container */} 常规与设备 网络与寻址 连接与同步列表 性能与流控 {/* Tab 1: 常规与设备 (General) */} 节点基本身份 设置该节点在 KLALB 全网拓扑中呈现的设备标识与语言偏好 设备名称 setForm((prev) => ({ ...prev, DeviceName: e.target.value })) } /> 全网路由广播与拓扑图中显示的节点名称 系统语言 本地界面与日志消息的默认语言 设备描述