重构子网生成逻辑
This commit is contained in:
parent
9933d56250
commit
260dcbb652
14
package-lock.json
generated
14
package-lock.json
generated
@ -61,6 +61,7 @@
|
||||
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.28.6",
|
||||
"@babel/generator": "^7.28.6",
|
||||
@ -1637,6 +1638,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz",
|
||||
"integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
@ -1719,6 +1721,7 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@ -1858,6 +1861,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@ -2003,7 +2007,8 @@
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
@ -2062,6 +2067,7 @@
|
||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@ -2230,6 +2236,7 @@
|
||||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@ -3048,6 +3055,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@ -3120,6 +3128,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -3129,6 +3138,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
||||
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@ -3514,6 +3524,7 @@
|
||||
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
@ -3635,6 +3646,7 @@
|
||||
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
@ -1,23 +1,14 @@
|
||||
import { ReactNode, useContext } from 'react';
|
||||
import { Handle, Position, NodeProps, useReactFlow} from '@xyflow/react';
|
||||
import { AppNode, AppEdge, SettingsContext, NodeData, Settings } from '../types/graph';
|
||||
import { AppNode, AppEdge, AppGraph, NodeData } from '../types/graph';
|
||||
import {Settings, SettingsContext} from '../types/settings'
|
||||
import StringBuilder from '../utils/StringBuilder';
|
||||
|
||||
import { CIDR, IPUtils } from '../utils/iputils';
|
||||
import { tryDerivePublicKey } from '../utils/wireguardConfig'
|
||||
import './CustomNode.css';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
class StringBuilder {
|
||||
private lines: string[] = [];
|
||||
|
||||
appendLine(value: string = "") {
|
||||
this.lines.push(value);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.lines.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigResult {
|
||||
constructor(
|
||||
public success: boolean,
|
||||
@ -26,32 +17,27 @@ class ConfigResult {
|
||||
) {}
|
||||
}
|
||||
|
||||
type GetEdge = (id: string) => AppEdge | undefined;
|
||||
type GetNode = (id: string) => AppNode | undefined;
|
||||
type GetEdges = () => AppEdge[];
|
||||
type GetAddress = (nodeId: string) => string | undefined;
|
||||
// function mapAddressToCIDR(nodeIds : string[], getAddress: GetAddress) : Record<string, CIDR> {
|
||||
// const nodeIdToCIDR : Record<string, CIDR> = {};
|
||||
// for(const nodeId of nodeIds) {
|
||||
// const address = getAddress(nodeId);
|
||||
// if(!address) continue;
|
||||
// const result = IPUtils.parse(address);
|
||||
// const cidr = result.cidr;
|
||||
// if(!cidr) {
|
||||
// throw new Error("节点地址无效");
|
||||
// }
|
||||
// if(cidr.version === 'IPv4') {
|
||||
// cidr.mask = 32;
|
||||
// } else if(cidr.version === 'IPv6') {
|
||||
// cidr.mask = 128;
|
||||
// }
|
||||
// nodeIdToCIDR[nodeId] = cidr;
|
||||
// }
|
||||
// return nodeIdToCIDR;
|
||||
// }
|
||||
|
||||
function mapAddressToCIDR(nodeIds : string[], getAddress: GetAddress) : Record<string, CIDR> {
|
||||
const nodeIdToCIDR : Record<string, CIDR> = {};
|
||||
for(const nodeId of nodeIds) {
|
||||
const address = getAddress(nodeId);
|
||||
if(!address) continue;
|
||||
const result = IPUtils.parse(address);
|
||||
const cidr = result.cidr;
|
||||
if(!cidr) {
|
||||
throw new Error("节点地址无效");
|
||||
}
|
||||
if(cidr.version === 'IPv4') {
|
||||
cidr.mask = 32;
|
||||
} else if(cidr.version === 'IPv6') {
|
||||
cidr.mask = 128;
|
||||
}
|
||||
nodeIdToCIDR[nodeId] = cidr;
|
||||
}
|
||||
return nodeIdToCIDR;
|
||||
}
|
||||
|
||||
function generateInterfaceConfig(settings: Settings,data: NodeData) : StringBuilder {
|
||||
function generateInterfaceConfig(settings: Settings, data: NodeData) : StringBuilder {
|
||||
const address = [data.ipv4Address, data.ipv6Address].flatMap(p => p ? [p] : []).join(', ');
|
||||
const config = new StringBuilder();
|
||||
config.appendLine(`[Interface]`);
|
||||
@ -66,20 +52,9 @@ function generateInterfaceConfig(settings: Settings,data: NodeData) : StringBuil
|
||||
return config;
|
||||
}
|
||||
|
||||
function generateConfig(settings: Settings, data: NodeData, getEdges: GetEdges, getEdge: GetEdge, getNode: GetNode) : ConfigResult {
|
||||
function generateConfig(settings: Settings, data: NodeData, graph: AppGraph) : ConfigResult {
|
||||
const config = generateInterfaceConfig(settings, data);
|
||||
|
||||
const getNearEdges = (node: AppNode) : AppEdge[] => {
|
||||
return getEdges().filter(edge => edge.source === node.id || edge.target === node.id);
|
||||
};
|
||||
|
||||
const getNextNode = (edge: AppEdge, node: AppNode) : AppNode => {
|
||||
const nextNodeId = edge.source === node.id ? edge.target : edge.source;
|
||||
return getNode(nextNodeId)!;
|
||||
};
|
||||
|
||||
const node = getNode(data.id)!;
|
||||
|
||||
const disallowCIDRs : CIDR[] = [];
|
||||
if(data.disallowIPs) {
|
||||
const disallowList = data.disallowIPs.split(',').map(ip => ip.trim()).filter(ip => ip.length > 0);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useState, ReactNode } from 'react';
|
||||
import { Settings, SubnetInfo, AppEdge, AppNode } from '../types/graph';
|
||||
import { AppEdge, AppNode } from '../types/graph';
|
||||
import {Settings, SubnetInfo} from '../types/settings'
|
||||
import { useReactFlow} from '@xyflow/react';
|
||||
import './FormEditor.css';
|
||||
import './SettingsEditor.css';
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import { Node, Edge } from '@xyflow/react';
|
||||
import { createContext } from 'react';
|
||||
import { CIDR } from '../utils/iputils';
|
||||
|
||||
export type AppNode = Node<NodeData>;
|
||||
|
||||
@ -34,22 +32,58 @@ export type EdgeDataUpdate = {
|
||||
persistentKeepalive?: number;
|
||||
}
|
||||
|
||||
export interface SubnetInfo {
|
||||
subnet: CIDR;
|
||||
nodes: Array<{nodeId: string, cidr: CIDR | undefined}>;
|
||||
}
|
||||
type GetEdges = () => AppEdge[];
|
||||
type GetNodes = () => AppNode[];
|
||||
|
||||
export interface Settings {
|
||||
listenPort: number;
|
||||
mtu: number;
|
||||
|
||||
subnets: SubnetInfo[];
|
||||
}
|
||||
export class AppGraph {
|
||||
private readonly _getNextNodeIds = new Map<string, string[]>();
|
||||
|
||||
export const initialSettings : Settings = {
|
||||
listenPort: 38894,
|
||||
mtu: 1420,
|
||||
subnets: []
|
||||
};
|
||||
constructor(
|
||||
public readonly getEdges: GetEdges,
|
||||
public readonly getNodes: GetNodes
|
||||
) {
|
||||
const getNextNodeIds = this._getNextNodeIds;
|
||||
const nodeIds = getNodes().map(node => node.id);
|
||||
|
||||
export const SettingsContext = createContext<Settings>(initialSettings);
|
||||
for(const edge of getEdges()) {
|
||||
if(!nodeIds.includes(edge.source) || !nodeIds.includes(edge.target)) {
|
||||
continue;
|
||||
}
|
||||
const sourceChildren = getNextNodeIds.get(edge.source) ?? [];
|
||||
const targetChildren = getNextNodeIds.get(edge.target) ?? [];
|
||||
getNextNodeIds.set(edge.source, [...sourceChildren, edge.target]);
|
||||
getNextNodeIds.set(edge.target, [...targetChildren, edge.source]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static checkConnected(graph: AppGraph): boolean {
|
||||
const nodes = graph.getNodes();
|
||||
if(nodes.length === 0) return true;
|
||||
|
||||
const visited = new Set<string>();
|
||||
const queue = [];
|
||||
const first = nodes[0];
|
||||
queue.push(first.id);
|
||||
visited.add(first.id);
|
||||
|
||||
const getNextNodeIds = graph._getNextNodeIds;
|
||||
while(queue.length > 0) {
|
||||
const curr = queue.shift()!;
|
||||
const next = getNextNodeIds.get(curr);
|
||||
if(!next) continue;
|
||||
for(const nextId of next) {
|
||||
if(visited.has(nextId)) continue;
|
||||
visited.add(nextId);
|
||||
queue.push(nextId);
|
||||
}
|
||||
}
|
||||
|
||||
return visited.size === nodes.length;
|
||||
}
|
||||
|
||||
getConnectedSubgraph(nodeIds: string[]): AppGraph | undefined {
|
||||
const node
|
||||
}
|
||||
}
|
||||
22
src/types/settings.ts
Normal file
22
src/types/settings.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { createContext } from 'react';
|
||||
import { CIDR } from '../utils/iputils';
|
||||
|
||||
export interface SubnetInfo {
|
||||
subnet: CIDR;
|
||||
nodes: Array<{nodeId: string, cidr: CIDR | undefined}>;
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
listenPort: number;
|
||||
mtu: number;
|
||||
|
||||
subnets: SubnetInfo[];
|
||||
}
|
||||
|
||||
export const initialSettings : Settings = {
|
||||
listenPort: 38894,
|
||||
mtu: 1420,
|
||||
subnets: []
|
||||
};
|
||||
|
||||
export const SettingsContext = createContext<Settings>(initialSettings);
|
||||
11
src/utils/StringBuilder.ts
Normal file
11
src/utils/StringBuilder.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export default class StringBuilder {
|
||||
private lines: string[] = [];
|
||||
|
||||
appendLine(value: string = "") {
|
||||
this.lines.push(value);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.lines.join('\n');
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user