diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ae15b3d..acd4d99 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -17,7 +17,7 @@ "react-bootstrap": "^2.10.10", "react-dom": "^19.1.0", "react-router-dom": "^6.30.1", - "react-vis-network-graph": "^3.0.1" + "react-sigma": "^1.2.35" }, "devDependencies": { "@eslint/js": "^9.29.0", @@ -296,19 +296,6 @@ "node": ">=6.9.0" } }, - "node_modules/@egjs/hammerjs": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", - "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", - "license": "MIT", - "peer": true, - "dependencies": { - "@types/hammerjs": "^2.0.36" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/@esbuild/win32-x64": { "version": "0.25.5", "cpu": [ @@ -772,13 +759,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/hammerjs": { - "version": "2.0.46", - "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", - "integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==", - "license": "MIT", - "peer": true - }, "node_modules/@types/json-schema": { "version": "7.0.15", "dev": true, @@ -1063,16 +1043,6 @@ "node": ">= 10" } }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/concat-map": { "version": "0.0.1", "dev": true, @@ -2188,13 +2158,6 @@ "node": ">=6" } }, - "node_modules/keycharm": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.3.1.tgz", - "integrity": "sha512-zn47Ti4FJT9zdF+YBBLWJsfKF/fYQHkrYlBeB5Ez5e2PjW7SoIxr43yehAne2HruulIoid4NKZZxO0dHBygCtQ==", - "license": "(Apache-2.0 OR MIT)", - "peer": true - }, "node_modules/keyv": { "version": "4.5.4", "dev": true, @@ -2229,12 +2192,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "dev": true, @@ -2295,16 +2252,6 @@ "node": "*" } }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "license": "MIT", - "peer": true, - "engines": { - "node": "*" - } - }, "node_modules/ms": { "version": "2.1.3", "dev": true, @@ -2604,6 +2551,19 @@ "react-dom": ">=16.8" } }, + "node_modules/react-sigma": { + "version": "1.2.35", + "resolved": "https://registry.npmjs.org/react-sigma/-/react-sigma-1.2.35.tgz", + "integrity": "sha512-amjKjaQusefDp8dD9er3wE/lfubSQT5fJSRF4SsS0LjxRue4bpSapAQ1WpmSM6nQEgawbrKel+bQBmatl7MAIw==", + "license": "MIT", + "engines": { + "node": ">=8.0" + }, + "peerDependencies": { + "react": ">=15.3", + "react-dom": ">=15.3" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -2620,41 +2580,6 @@ "react-dom": ">=16.6.0" } }, - "node_modules/react-vis-network-graph": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/react-vis-network-graph/-/react-vis-network-graph-3.0.1.tgz", - "integrity": "sha512-gxUOJDPb+w7sZxvSzIrV7YpOqoJiTF6YrPsXS3RGnrmZnWyGhpCpUcaPli4PDsqpI74Q4f5AICg0cdzAEVbmIQ==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.15", - "prop-types": "^15.5.10", - "react": "16.x.x", - "uuid": "^2.0.1", - "vis-data": "^6.5.1", - "vis-network": "^7.6.4" - } - }, - "node_modules/react-vis-network-graph/node_modules/react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-vis-network-graph/node_modules/uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", - "integrity": "sha512-FULf7fayPdpASncVy4DLh3xydlXEJJpvIELjYjNeQWYUZ9pclcpvCZSr2gkmN2FrrGcI7G/cJsIEwk5/8vfXpg==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "license": "MIT" - }, "node_modules/resolve-from": { "version": "4.0.0", "dev": true, @@ -2780,13 +2705,6 @@ "node": ">=8" } }, - "node_modules/timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", - "license": "MIT", - "peer": true - }, "node_modules/tinyglobby": { "version": "0.2.14", "dev": true, @@ -2871,67 +2789,6 @@ "punycode": "^2.1.0" } }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "peer": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vis-data": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-6.6.1.tgz", - "integrity": "sha512-xmujDB2Dzf8T04rGFJ9OP4OA6zRVrz8R9hb0CVKryBrZRCljCga9JjSfgctA8S7wdZu7otDtUIwX4ZOgfV/57w==", - "hasInstallScript": true, - "license": "(Apache-2.0 OR MIT)", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/visjs" - }, - "peerDependencies": { - "moment": "^2.24.0", - "uuid": "^7.0.0 || ^8.0.0", - "vis-util": "^4.0.0" - } - }, - "node_modules/vis-network": { - "version": "7.10.2", - "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-7.10.2.tgz", - "integrity": "sha512-KDx2agbDnaiE0Bye4AcCRqTn5mxzDKhdUNpKkzSn0AOLBmdhNtPGjxAFluAmvFVyiSK5R6Q5KIWdLjeIMu/PAQ==", - "hasInstallScript": true, - "license": "(Apache-2.0 OR MIT)", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/visjs" - }, - "peerDependencies": { - "@egjs/hammerjs": "^2.0.0", - "component-emitter": "^1.3.0", - "keycharm": "^0.2.0 || ^0.3.0", - "moment": "^2.24.0", - "timsort": "^0.3.0", - "uuid": "^3.4.0 || ^7.0.0 || ^8.0.0", - "vis-data": "^6.2.1", - "vis-util": "^3.0.0 || ^4.0.0" - } - }, - "node_modules/vis-util": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-4.3.4.tgz", - "integrity": "sha512-hJIZNrwf4ML7FYjs+m+zjJfaNvhjk3/1hbMdQZVnwwpOFJS/8dMG8rdbOHXcKoIEM6U5VOh3HNpaDXxGkOZGpw==", - "license": "(Apache-2.0 OR MIT)", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/visjs" - } - }, "node_modules/vite": { "version": "7.0.2", "dev": true, diff --git a/frontend/package.json b/frontend/package.json index 2a67432..a94bd56 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,7 +19,7 @@ "react-bootstrap": "^2.10.10", "react-dom": "^19.1.0", "react-router-dom": "^6.30.1", - "react-vis-network-graph": "^3.0.1" + "react-sigma": "^1.2.35" }, "devDependencies": { "@eslint/js": "^9.29.0", diff --git a/frontend/src/GraphView.jsx b/frontend/src/GraphView.jsx index b36d8b4..934568d 100644 --- a/frontend/src/GraphView.jsx +++ b/frontend/src/GraphView.jsx @@ -1,124 +1,43 @@ -import React, { useMemo } from 'react'; -import { Network } from 'react-vis-network-graph'; +import React, { useState, useEffect, useRef } from 'react'; function GraphView({ servers, connections }) { - const graph = useMemo(() => { - const nodes = servers.map(server => ({ - id: server.ip, - label: `${server.dns}\n${server.ip}`, - title: `${server.dns} (${server.ip})\n${server.country} - ${server.provider}`, - color: { - background: '#3b82f6', - border: '#1e40af', - highlight: { - background: '#60a5fa', - border: '#3b82f6' - } - }, - font: { - color: '#ffffff', - size: 14, - face: 'Arial' - }, - shape: 'box', - borderWidth: 2, - shadow: true - })); + const [positions, setPositions] = useState({}); + const [dimensions, setDimensions] = useState({ width: 800, height: 500 }); + const svgRef = useRef(null); - const edges = connections.map((connection, index) => ({ - id: index, - from: connection.from, - to: connection.to, - label: `${connection.tunnelType}\n${connection.ipA} ⇄ ${connection.ipB}`, - title: `${connection.tunnelType} туннель\n${connection.ipA} ⇄ ${connection.ipB}`, - color: { - color: '#64748b', - highlight: '#3b82f6', - hover: '#3b82f6' - }, - font: { - color: '#374151', - size: 12, - face: 'Arial', - align: 'middle' - }, - width: 2, - arrows: { - to: { - enabled: true, - scaleFactor: 0.8 - } - }, - smooth: { - type: 'curvedCW', - roundness: 0.2 - } - })); + // Вычисляем позиции узлов + useEffect(() => { + if (servers.length === 0) return; - return { nodes, edges }; - }, [servers, connections]); + const newPositions = {}; + const centerX = dimensions.width / 2; + const centerY = dimensions.height / 2; + const radius = Math.min(dimensions.width, dimensions.height) * 0.3; - const options = { - nodes: { - shape: 'box', - margin: 10, - shadow: true, - font: { - size: 14, - face: 'Arial' - } - }, - edges: { - font: { - size: 12, - align: 'middle' - }, - smooth: { - type: 'curvedCW', - roundness: 0.2 - } - }, - physics: { - enabled: true, - solver: 'forceAtlas2Based', - forceAtlas2Based: { - gravitationalConstant: -50, - centralGravity: 0.01, - springLength: 200, - springConstant: 0.08, - damping: 0.4 - }, - stabilization: { - enabled: true, - iterations: 1000, - updateInterval: 100 - } - }, - interaction: { - hover: true, - tooltipDelay: 200, - zoomView: true, - dragView: true - }, - layout: { - improvedLayout: true, - hierarchical: { - enabled: false - } - } - }; + servers.forEach((server, index) => { + const angle = (index / servers.length) * 2 * Math.PI; + newPositions[server.ip] = { + x: centerX + radius * Math.cos(angle), + y: centerY + radius * Math.sin(angle) + }; + }); - const events = { - select: function(event) { - const { nodes, edges } = event; - console.log('Selected nodes:', nodes); - console.log('Selected edges:', edges); - }, - hoverNode: function(event) { - const { node } = event; - console.log('Hovered node:', node); - } - }; + setPositions(newPositions); + }, [servers, dimensions]); + + // Обработчик изменения размера + useEffect(() => { + const handleResize = () => { + if (svgRef.current) { + const rect = svgRef.current.getBoundingClientRect(); + setDimensions({ width: rect.width, height: 500 }); + } + }; + + handleResize(); + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); if (servers.length === 0) { return ( @@ -132,13 +51,129 @@ function GraphView({ servers, connections }) { } return ( -
- +
+ + {/* Связи */} + {connections.map((connection, index) => { + const fromPos = positions[connection.from]; + const toPos = positions[connection.to]; + + if (!fromPos || !toPos) return null; + + return ( + + {/* Линия связи */} + + + {/* Подпись связи */} + + {connection.tunnelType} + + + {connection.ipA} ⇄ {connection.ipB} + + + ); + })} + + {/* Узлы (серверы) */} + {servers.map(server => { + const pos = positions[server.ip]; + if (!pos) return null; + + return ( + + {/* Круг узла */} + + + {/* DNS имя */} + + {server.dns} + + + {/* IP адрес */} + + {server.ip} + + + {/* Страна */} + + {server.country} + + + ); + })} + + {/* Определение стрелки */} + + + + + +
); } diff --git a/frontend/src/ServerManager.jsx b/frontend/src/ServerManager.jsx index c8016aa..bfa99b3 100644 --- a/frontend/src/ServerManager.jsx +++ b/frontend/src/ServerManager.jsx @@ -17,7 +17,7 @@ import { IconChevronDown, IconLink } from '@tabler/icons-react'; -import GraphView from './GraphView'; +// import GraphView from './GraphView'; const API_URL = '/api'; @@ -668,14 +668,15 @@ function ServerManager() { error={error} /> -
+ {/* Временно отключаем граф для отладки */} + {/*

Граф связей между серверами

-
+
*/}

Добавить связь между серверами