import { useState, ReactNode } from 'react'; import { Settings, SubnetInfo, AppEdge, AppNode } from '../types/graph'; import { useReactFlow} from '@xyflow/react'; import './FormEditor.css'; import Folder from './Folder'; import toast from 'react-hot-toast'; import { IPUtils } from '../utils/iputils'; import Select from 'react-select'; interface SettingEditorProps { settings: Settings; onUpdate: (data: Settings) => void; onClose: () => void; } interface SelectionOption { value: string; label: string; } interface SubnetProps { subnets: SubnetInfo[], setSubnets: (subnets: SubnetInfo[]) => void, index: number, } function Validate(updateData: Settings) : string[] { const errors: string[] = []; const {mtu, listenPort} = updateData; if(isNaN(listenPort)) { errors.push("监听端口不是数字"); } else if(listenPort < 30000 || listenPort > 49151) { errors.push("监听端口不在范围内:[30000, 49151]"); } if(isNaN(mtu)) { errors.push("mtu不是数字"); } else if(mtu < 1200) { errors.push("mtu过小(小于1200)"); } return errors; } function SubnetItem({index, subnets, setSubnets} : SubnetProps) : ReactNode { const subnetInfo = subnets[index]; const [selectedOption, setSelectedOption] = useState(null); const [nodeSubnet, setNodeSubnet] = useState(""); const { getNodes, getNode } = useReactFlow(); const options : SelectionOption[] = getNodes().map(node => ({ value: node.id, label: node.data.label })); const handleAddNodeSubnet = () => { if(!selectedOption) { toast.error("没有选择有效的节点"); return ; } const nodeId = selectedOption.value; const nodecidr = (() => { if(nodeSubnet.includes("/")) return nodeSubnet; return `${nodeSubnet}/${nodeSubnet.includes(".") ? "32" : "128"}`; })(); const result = IPUtils.parse(nodecidr); const cidr = result.cidr; if(!cidr) { toast.error(`无法解析子网:${result.error}`); return ; } if(!subnetInfo.subnet.contains(cidr)) { toast.error("不在子网范围内"); return ; } if(subnetInfo.nodes.some(node => node.nodeId === nodeId)) { toast.error(`节点已添加`); return; } setSubnets(subnets.map((info, idx) => { if(idx === index) { return {...info, nodes: [...info.nodes, {nodeId: nodeId, cidr: cidr}]} } return info; })); } const handleRemoveNodeSubnet = (nodeId: string) => { setSubnets(subnets.map((info, idx) => { if(idx === index) { return {...info, nodes: info.nodes.filter(node => node.nodeId != nodeId)}; } return info; })); } const handleRemoveSubnet = () => { setSubnets(subnets.filter((_, idx) => idx != index)); } return (
{setNodeSubnet(e.target.value)}}/>
{subnetInfo.nodes.map((nodeInfo, _) => { const node = getNode(nodeInfo.nodeId); if(!node) return <> return (
) })}
); } export default function SettingsEditor({ settings, onUpdate, onClose }: SettingEditorProps): ReactNode { const [errors, setErrors] = useState([]); const [listenPort, setListenPort] = useState(settings.listenPort); const [mtu, setmtu] = useState(settings.mtu); const [subnets, setSubnets] = useState(settings.subnets); const [subnetInput, setSubnetInput] = useState(""); const handleAddSubnet = (cidrStr: string) => { const result = IPUtils.parse(cidrStr); const cidr = result.cidr; if(!cidr) { toast.error(`无效的CIDR格式: ${result.error}`); return; } if(subnets.some(s => IPUtils.equal(s.subnet, cidr))) { toast.error("该CIDR已存在"); return; } setSubnets([...subnets, {subnet: cidr, nodes: []}]); }; const handleSave = (): void => { const updateData = { listenPort: listenPort, mtu: mtu, subnets: subnets }; const validation = Validate(updateData); setErrors(validation); if(validation.length > 0) { return ; } onUpdate(updateData); onClose(); }; return (

全局设置

{errors.length > 0 && (
setErrors([])} // 点击清空错误数组 title="关闭提示" >×
{errors.map((error, idx) => (

• {error}

))}
)}
setListenPort(e.target.valueAsNumber)} />
setmtu(e.target.valueAsNumber)} />
setSubnetInput(e.target.value)} />
{subnets.map((_, idx) => )}
); }