实现全局设置编辑窗口和参数校验

This commit is contained in:
fengfeng 2026-02-09 13:06:36 +08:00
parent aeda4b5179
commit e01c91a1a3
5 changed files with 78 additions and 19 deletions

View File

@ -2,12 +2,13 @@
- [x] 完成节点的编辑窗口
- [x] 完成边的编辑窗口
- [ ] 完成全局设置编辑窗口
- [ ] 完成参数校验以及参数联动逻辑
- [x] 完成全局设置编辑窗口
- [x] 完成参数校验以及参数联动逻辑
- [ ] 实现配置生成逻辑,并验证有效
- [ ] 实现子网路由功能,并验证有效
- [ ] 实现配置保存和加载功能
- [ ] 实现加密功能(完全加密和只加密私钥)
- [ ] 添加测试用例
- [ ] 完成!

View File

@ -109,7 +109,7 @@ function FlowContent(): ReactNode {
const handleAddNode = (): void => {
const result = generateNodeData(nodes.length);
if(result == null) return;
if(!result) return;
const newNode: AppNode = {
id: result.id,
position: { x: 0, y: 0 },

View File

@ -1,6 +1,7 @@
import { useState, ReactNode } from 'react';
import { NodeData, Settings, NodeDataUpdate } from '../types/graph';
import { generateWireGuardPrivateKey } from '../utils/wireguardConfig'
import { IPNetwork} from '../utils/iputils'
import './FormEditor.css';
import Folder from './Folder'
@ -11,6 +12,43 @@ interface NodeEditorProps {
onClose: () => void;
}
class Validation {
constructor(
public readonly isValid: boolean,
public readonly errors: string[],
) {}
}
function Validate(updateData : NodeDataUpdate, settings : Settings) : Validation {
const errors: string[] = [];
const ipv4Subnet = settings.ipv4Subnet;
const ipv4Address = updateData.ipv4Address;
if(ipv4Subnet) {
const cidr = IPNetwork.parse(ipv4Subnet);
if(!ipv4Address) {
errors.push("需要设置IPv4地址");
} else if(!cidr.contains(IPNetwork.parse(`${ipv4Address}/32`))) {
errors.push("IPv4不在子网范围中");
}
}
const ipv6Subnet = settings.ipv6Subnet;
const ipv6Address = updateData.ipv6Address;
if(ipv6Subnet) {
const cidr = IPNetwork.parse(ipv6Subnet);
if(!ipv6Address) {
errors.push("需要设置IPv6地址");
} else if(!cidr.contains(IPNetwork.parse(`${ipv6Address}/128`))) {
errors.push("IPv6不在子网范围中");
}
}
const isValid : boolean = errors.length === 0;
return new Validation(isValid, errors);
}
export default function NodeEditor({
node,
settings,
@ -33,9 +71,7 @@ export default function NodeEditor({
const [notes, setNotes] = useState(node.notes)
const handleSave = (): void => {
// todo: 校验
setErrors([]);
onUpdate({
const updateData : NodeDataUpdate = {
label: label,
privateKey: privateKey,
ipv4Address: ipv4Address,
@ -47,7 +83,16 @@ export default function NodeEditor({
listenPort: listenPort,
dnsServers: dnsServers,
notes: notes
});
}
const validation = Validate(updateData, settings);
if(!validation.isValid) {
setErrors(validation.errors);
return ;
}
setErrors([]);
onUpdate(updateData);
onClose();
};

View File

@ -26,13 +26,17 @@ export default function SettingsEditor({
if(ipv4Subnet) {
const result = IPNetwork.parse(ipv4Subnet)
if(!result.isValid) {
errorInfo.push(result.error ?? "ipv4子网不合法")
errorInfo.push("IPv4子网" + (result.error ?? "ipv4子网不合法"))
} else if(result.version != 'IPv4') {
errorInfo.push("IPv4子网" + "非IPv4 CIDR");
}
}
if(ipv6Subnet) {
const result = IPNetwork.parse(ipv6Subnet)
if(!result.isValid) {
errorInfo.push(result.error ?? "ipv6子网不合法")
errorInfo.push("IPv6子网" + (result.error ?? "子网不合法"));
} else if(result.version != 'IPv6') {
errorInfo.push("IPv6子网" + "非IPv6 CIDR");
}
}
if(errorInfo.length > 0) {

View File

@ -1,18 +1,27 @@
export type IPVersion = 'IPv4' | 'IPv6' | 'invalid';
export interface IPResult {
isValid: boolean;
version: IPVersion;
binary: string;
mask: number;
error?: string;
export class CIDR {
constructor(
public isValid: boolean,
public version: IPVersion,
public binary: string,
public mask: number,
public error?: string
) {}
contains(cidr: CIDR) : boolean {
if(!cidr.isValid || !this.isValid) return false;
if(this.version !== cidr.version) return false;
if(this.mask > cidr.mask) return false;
return this.binary.slice(0, this.mask) === cidr.binary.slice(0, this.mask);
}
}
export class IPNetwork {
/**
* CIDR
*/
static parse(cidr: string): IPResult {
static parse(cidr: string): CIDR {
const parts = cidr.split('/');
if (parts.length !== 2) {
return this.invalid('格式错误,缺少掩码 (如 /24)');
@ -34,7 +43,7 @@ export class IPNetwork {
const binary = version === 'IPv4' ? this.ipv4ToBinary(ip) : this.ipv6ToBinary(ip);
if (!binary) return this.invalid('IP 地址数值非法');
return { isValid: true, version, binary, mask };
return new CIDR(true, version, binary, mask);
} catch (e) {
return this.invalid('解析过程中出错');
}
@ -84,8 +93,8 @@ export class IPNetwork {
}
}
private static invalid(msg: string): IPResult {
return { isValid: false, version: 'invalid', binary: '', mask: -1, error: msg };
private static invalid(msg: string): CIDR {
return new CIDR(false, 'invalid', '', -1, msg);
}
/**