155 lines
3.8 KiB
TypeScript
155 lines
3.8 KiB
TypeScript
import { AppNode, AppEdge, NodeData } from "../types/graph";
|
|
|
|
|
|
interface ValidationResult {
|
|
isValid: boolean;
|
|
errors: string[];
|
|
}
|
|
|
|
/**
|
|
* 生成WireGuard格式的配置文件
|
|
*/
|
|
export function generateWireGuardConfig(
|
|
node: AppNode,
|
|
allNodes: AppNode[],
|
|
allEdges: AppEdge[]
|
|
): string {
|
|
const config: string[] = [];
|
|
|
|
// [Interface] 部分
|
|
config.push('[Interface]');
|
|
config.push(`PrivateKey = ${node.data.privateKey}`);
|
|
config.push(`Address = ${node.data.ipAddress}/32`);
|
|
|
|
if (node.data.listenPort) {
|
|
config.push(`ListenPort = ${node.data.listenPort}`);
|
|
}
|
|
|
|
if (node.data.dnsServers) {
|
|
config.push(`DNS = ${node.data.dnsServers}`);
|
|
}
|
|
|
|
config.push('');
|
|
|
|
// 找到与该节点相连的所有节点
|
|
const connectedNodeIds = new Set<string>();
|
|
allEdges.forEach(edge => {
|
|
if (edge.source === node.id) {
|
|
connectedNodeIds.add(edge.target);
|
|
}
|
|
if (edge.target === node.id) {
|
|
connectedNodeIds.add(edge.source);
|
|
}
|
|
});
|
|
|
|
// 为每个连接的节点添加 [Peer] 部分
|
|
connectedNodeIds.forEach(peerId => {
|
|
const peerNode = allNodes.find(n => n.id === peerId);
|
|
if (peerNode && peerNode.data.publicKey && peerNode.data.ipAddress) {
|
|
config.push('[Peer]');
|
|
config.push(`PublicKey = ${peerNode.data.publicKey}`);
|
|
config.push(`AllowedIPs = ${peerNode.data.ipAddress}/32`);
|
|
|
|
if (peerNode.data.endpoint) {
|
|
config.push(`Endpoint = ${peerNode.data.endpoint}`);
|
|
}
|
|
|
|
if (peerNode.data.persistentKeepalive) {
|
|
config.push(`PersistentKeepalive = ${peerNode.data.persistentKeepalive}`);
|
|
}
|
|
|
|
config.push('');
|
|
}
|
|
});
|
|
|
|
return config.join('\n');
|
|
}
|
|
|
|
/**
|
|
* 生成所有节点的配置
|
|
*/
|
|
export function generateAllConfigs(
|
|
nodes: AppNode[],
|
|
edges: AppEdge[]
|
|
): Record<string, { name: string; config: string }> {
|
|
const configs: Record<string, { name: string; config: string }> = {};
|
|
|
|
nodes.forEach(node => {
|
|
if (node.id && node.data.privateKey && node.data.ipAddress) {
|
|
configs[node.id] = {
|
|
name: node.data.label || node.id,
|
|
config: generateWireGuardConfig(node, nodes, edges)
|
|
};
|
|
}
|
|
});
|
|
|
|
return configs;
|
|
}
|
|
|
|
/**
|
|
* 下载配置文件
|
|
*/
|
|
export function downloadConfig(
|
|
nodeId: string,
|
|
nodeName: string,
|
|
configContent: string
|
|
): void {
|
|
const element = document.createElement('a');
|
|
element.setAttribute(
|
|
'href',
|
|
'data:text/plain;charset=utf-8,' + encodeURIComponent(configContent)
|
|
);
|
|
element.setAttribute('download', `${nodeId}-${nodeName}.conf`);
|
|
element.style.display = 'none';
|
|
|
|
document.body.appendChild(element);
|
|
element.click();
|
|
document.body.removeChild(element);
|
|
}
|
|
|
|
/**
|
|
* 验证节点配置是否完整
|
|
*/
|
|
export function validateNodeConfig(node: Partial<NodeData>): ValidationResult {
|
|
const errors: string[] = [];
|
|
|
|
if (!node.label || node.label.trim() === '') {
|
|
errors.push('节点名称不能为空');
|
|
}
|
|
|
|
if (!node.ipAddress || node.ipAddress.trim() === '') {
|
|
errors.push('IP地址不能为空');
|
|
} else if (!isValidIP(node.ipAddress)) {
|
|
errors.push('IP地址格式无效');
|
|
}
|
|
|
|
if (!node.privateKey || node.privateKey.trim() === '') {
|
|
errors.push('私钥不能为空');
|
|
}
|
|
|
|
if (!node.publicKey || node.publicKey.trim() === '') {
|
|
errors.push('公钥不能为空');
|
|
}
|
|
|
|
return {
|
|
isValid: errors.length === 0,
|
|
errors
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 验证IP地址格式
|
|
*/
|
|
function isValidIP(ip: string): boolean {
|
|
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
if (!ipv4Regex.test(ip)) {
|
|
return false;
|
|
}
|
|
|
|
const parts = ip.split('.');
|
|
return parts.every(part => {
|
|
const num = parseInt(part, 10);
|
|
return num >= 0 && num <= 255;
|
|
});
|
|
}
|