diff --git a/backend/server.js b/backend/server.js index a477145..17b60c3 100644 --- a/backend/server.js +++ b/backend/server.js @@ -305,6 +305,20 @@ const serversRoutes = createJsonDataRoutes('servers.json', (server, i) => { app.get('/api/servers', serversRoutes.get); app.post('/api/servers', serversRoutes.post); +// Server connections for topology graph +const serverConnectionsRoutes = createJsonDataRoutes('server-connections.json', (conn, i) => { + if (!conn.from || !conn.to || !conn.tunnelType) { + return `Connection at index ${i} is missing required fields: from, to, tunnelType`; + } + if (conn.from === conn.to) { + return `Connection at index ${i} has identical from/to values`; + } + return null; +}); + +app.get('/api/server-connections', serverConnectionsRoutes.get); +app.post('/api/server-connections', serverConnectionsRoutes.post); + // Billing const billingRoutes = createJsonDataRoutes('servers-billing.json', (item, i) => { if (!item.hostName || !item.country || !item.provider) { diff --git a/frontend/src/App.css b/frontend/src/App.css index 13a90cd..c0aea78 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -520,4 +520,40 @@ button:focus-visible, 100% { transform: scale(1); } +} + +/* ===== GraphView / ReactFlow custom styling ===== */ + +/* Плавные, аккуратные линии графа */ +.react-flow__edge-path { + stroke-linecap: round; + transition: stroke 0.15s ease, stroke-width 0.15s ease, filter 0.15s ease; +} + +/* Лёгкий ховер по связи, без агрессивной анимации */ +.react-flow__edge:hover .react-flow__edge-path { + stroke-width: 3.2px; + filter: drop-shadow(0 0 4px rgba(15, 23, 42, 0.25)); +} + +/* Базовая прозрачность для всех связей, чтобы граф не выглядел «забитым» */ +.react-flow__edge .react-flow__edge-path { + stroke-opacity: 0.9; +} + +/* Небольшие отличия по типу туннеля (на будущее, сейчас основное зашито в JS) */ +.react-flow__edge.edge-tunnel-gre .react-flow__edge-path { + /* GRE — базовый синий, уже задан в JS, здесь только подчёркиваем плавность */ +} + +.react-flow__edge.edge-tunnel-ipsec .react-flow__edge-path { + stroke-dasharray: 6 4; +} + +.react-flow__edge.edge-tunnel-wireguard .react-flow__edge-path { + stroke-width: 3px; +} + +.react-flow__edge.edge-tunnel-openvpn .react-flow__edge-path { + stroke-dasharray: 3 3; } \ No newline at end of file diff --git a/frontend/src/GraphView.jsx b/frontend/src/GraphView.jsx index 2f549fb..b50683d 100644 --- a/frontend/src/GraphView.jsx +++ b/frontend/src/GraphView.jsx @@ -77,6 +77,7 @@ function GraphView({ servers, connections, onCreateConnection }) { source: String(c.from), target: String(c.to), label: ipLabel ? `${baseLabel} · ${ipLabel}` : baseLabel, + type: 'smoothstep', style: { stroke: style.color, strokeWidth: style.width, @@ -89,7 +90,9 @@ function GraphView({ servers, connections, onCreateConnection }) { fontWeight: 500, fill: '#0f172a', }, - markerEnd: { type: MarkerType.ArrowClosed, color: style.color, width: 20, height: 20 }, + className: c.tunnelType ? `edge-tunnel-${String(c.tunnelType).toLowerCase()}` : 'edge-tunnel-default', + markerStart: { type: MarkerType.ArrowClosed, color: style.color, width: 16, height: 16 }, + markerEnd: { type: MarkerType.ArrowClosed, color: style.color, width: 16, height: 16 }, animated: false, }; }); @@ -251,8 +254,7 @@ function GraphView({ servers, connections, onCreateConnection }) { nodeTypes={nodeTypes} fitView defaultEdgeOptions={{ - type: 'default', - markerEnd: { type: MarkerType.ArrowClosed }, + type: 'smoothstep', }} onInit={onInit} style={{ width: '100%', height: '100%', background: '#f8fafc' }} @@ -267,46 +269,54 @@ function GraphView({ servers, connections, onCreateConnection }) { {/* Своя панель управления: -, +, Fit, Fullscreen */}