feat: Add GraphView component for visualizing server connections and implement connection management in ServerManager
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 8m19s

This commit is contained in:
2025-07-15 19:24:45 +07:00
parent 1a31b37677
commit 2590b08262
4 changed files with 797 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
import React, { useRef, useEffect } from 'react';
import ForceGraph2D from 'react-force-graph-2d';
function GraphView({ servers, connections }) {
// Преобразуем данные в формат, подходящий для ForceGraph
const nodes = servers.map(s => ({ id: s.ip, label: s.dns || s.ip }));
const links = connections.map(c => ({
source: c.from,
target: c.to,
tunnelType: c.tunnelType,
ipA: c.ipA,
ipB: c.ipB
}));
// Кастомный рендер связей с подписями
const fgRef = useRef();
useEffect(() => {
if (fgRef.current) {
fgRef.current.d3ReheatSimulation();
}
}, [nodes.length, links.length]);
return (
<div style={{ width: '100%', height: 400 }}>
<ForceGraph2D
ref={fgRef}
graphData={{ nodes, links }}
nodeLabel={node => node.label}
nodeAutoColorBy="id"
linkDirectionalArrowLength={6}
linkDirectionalArrowRelPos={1}
linkLabel={l => `${l.tunnelType}: ${l.ipA}${l.ipB}`}
linkWidth={2}
linkColor={() => '#888'}
nodeCanvasObject={(node, ctx, globalScale) => {
const label = node.label;
const fontSize = 14/globalScale;
ctx.font = `${fontSize}px sans-serif`;
ctx.fillStyle = '#1e293b';
ctx.beginPath();
ctx.arc(node.x, node.y, 10, 0, 2 * Math.PI, false);
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
ctx.fillStyle = '#fff';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(label, node.x, node.y - 18);
}}
linkCanvasObjectMode={() => 'after'}
linkCanvasObject={(link, ctx, globalScale) => {
const label = `${link.tunnelType}: ${link.ipA}${link.ipB}`;
if (!label) return;
const start = link.source;
const end = link.target;
if (typeof start !== 'object' || typeof end !== 'object') return;
const textPos = {
x: (start.x + end.x) / 2,
y: (start.y + end.y) / 2
};
ctx.save();
ctx.font = `${12/globalScale}px sans-serif`;
ctx.fillStyle = '#2563eb';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(label, textPos.x, textPos.y);
ctx.restore();
}}
/>
</div>
);
}
export default GraphView;