diff --git a/src/components/CustomNode.css b/src/components/CustomNode.css
index 7d7e674..7897c2a 100644
--- a/src/components/CustomNode.css
+++ b/src/components/CustomNode.css
@@ -98,4 +98,12 @@
transform: translateY(1px) scale(0.995);
background-color: #0f62d6; /* slightly darker on press */
box-shadow: inset 0 1px 2px rgba(0,0,0,0.12);
+}
+
+.empty-tip {
+ font-size: 8px;
+ color: #999;
+ text-align: center;
+ margin-top: 10px;
+ margin-bottom: 10px;
}
\ No newline at end of file
diff --git a/src/components/CustomNode.tsx b/src/components/CustomNode.tsx
index 404d411..597c32a 100644
--- a/src/components/CustomNode.tsx
+++ b/src/components/CustomNode.tsx
@@ -216,6 +216,7 @@ export default function CustomNode({
}
};
+ const empty = !(data.ipv4Address || data.ipv6Address);
return (
@@ -224,20 +225,22 @@ export default function CustomNode({
-
+ {empty &&
未配置地址信息
}
+
+ {data.ipv4Address &&
IPv4地址:
{data.ipv4Address || "未设置"}
-
+
}
-
+ {data.ipv6Address &&
IPv6地址:
{data.ipv6Address || "未设置"}
-
+
}
-
+ {data.listenAddress &&
监听地址:
{data.listenAddress || "未设置"}
-
+
}
diff --git a/src/components/SettingsEditor.tsx b/src/components/SettingsEditor.tsx
index 790de78..9e1e2c6 100644
--- a/src/components/SettingsEditor.tsx
+++ b/src/components/SettingsEditor.tsx
@@ -1,7 +1,9 @@
import { useState, ReactNode } from 'react';
-import { Settings } from '../types/graph';
+import { Settings, SubnetInfo } from '../types/graph';
import './FormEditor.css';
-import {IPUtils} from '../utils/iputils'
+import Folder from './Folder';
+import toast from 'react-hot-toast';
+import { IPUtils } from '../utils/iputils';
interface SettingEditorProps {
settings: Settings;
@@ -38,11 +40,28 @@ export default function SettingsEditor({
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);
@@ -100,6 +119,49 @@ export default function SettingsEditor({
/>
+
+
+
+ setSubnetInput(e.target.value)}
+ />
+
+
+
+
+
+ {subnets.map((subnetInfo, idx) => (
+
+
+
+
+
+
+
+
+ -
+ 节点ID: example-node-1, IP:
+
+
+
+
+ ))}
+
+
+
+
diff --git a/src/types/graph.ts b/src/types/graph.ts
index 6ada0f5..fb4043c 100644
--- a/src/types/graph.ts
+++ b/src/types/graph.ts
@@ -1,5 +1,6 @@
import { Node, Edge } from '@xyflow/react';
import { createContext } from 'react';
+import { CIDR } from '../utils/iputils';
export type AppNode = Node;
@@ -33,14 +34,22 @@ export type EdgeDataUpdate = {
persistentKeepalive?: number;
}
+export interface SubnetInfo {
+ subnet: CIDR;
+ nodes: Record;
+}
+
export interface Settings {
listenPort: number;
mtu: number;
+
+ subnets: SubnetInfo[];
}
export const initialSettings : Settings = {
listenPort: 38894,
mtu: 1420,
+ subnets: []
};
export const SettingsContext = createContext(initialSettings);
\ No newline at end of file
diff --git a/src/utils/iputils.ts b/src/utils/iputils.ts
index 370ee7b..e92438d 100644
--- a/src/utils/iputils.ts
+++ b/src/utils/iputils.ts
@@ -125,6 +125,10 @@ export class IPUtils {
];
}
+ static equal(c1: CIDR, c2: CIDR): boolean {
+ return c1.version === c2.version && c1.binary === c2.binary && c1.mask === c2.mask;
+ }
+
static parse(cidrStr: string): CIDRParseResult {
const parts = cidrStr.split('/');
if (parts.length !== 2) return { error: 'Invalid CIDR format' };