diff --git a/frontend/src/NetworkMapUnifi.jsx b/frontend/src/NetworkMapUnifi.jsx index 2252940..40dd66a 100644 --- a/frontend/src/NetworkMapUnifi.jsx +++ b/frontend/src/NetworkMapUnifi.jsx @@ -167,6 +167,21 @@ export default function NetworkMapUnifi({ return { low, belowNorm, normal }; }, [alertSettings]); + const nodeMetaByIp = useMemo(() => { + const byIp = new Map(); + servers.forEach((s) => { + const ip = String(s.ip || ''); + if (!ip) return; + byIp.set(ip, { + ip, + dns: s.dns || '', + type: String(s.type || '').toLowerCase(), + nodeLabel: s.dns ? String(s.dns).split('.')[0] : ip, + }); + }); + return byIp; + }, [servers]); + useEffect(() => { const el = containerRef.current; if (!el) return; @@ -501,7 +516,9 @@ export default function NetworkMapUnifi({ linkCount: 0, staleCount: 0, maxPingMs: null, + maxPingPeerLabel: null, minSpeedMbps: null, + minSpeedPeerLabel: null, }); }); @@ -516,15 +533,24 @@ export default function NetworkMapUnifi({ [e.from, e.to].forEach((nodeIp) => { const node = byIp.get(String(nodeIp)); if (!node) return; + const peerIp = String(nodeIp) === String(e.from) ? String(e.to) : String(e.from); + const peerMeta = nodeMetaByIp.get(peerIp); + const peerLabel = peerMeta?.nodeLabel || peerIp; if (e.stale) node.staleCount += 1; if (typeof e.pingMs === 'number') { - node.maxPingMs = node.maxPingMs == null ? e.pingMs : Math.max(node.maxPingMs, e.pingMs); + if (node.maxPingMs == null || e.pingMs > node.maxPingMs) { + node.maxPingMs = e.pingMs; + node.maxPingPeerLabel = peerLabel; + } } const down = e.speed?.tcpDownloadBps != null ? e.speed.tcpDownloadBps / 1_000_000 : null; const up = e.speed?.tcpUploadBps != null ? e.speed.tcpUploadBps / 1_000_000 : null; const edgeMin = down != null && up != null ? Math.min(down, up) : (down ?? up); if (edgeMin != null && Number.isFinite(edgeMin)) { - node.minSpeedMbps = node.minSpeedMbps == null ? edgeMin : Math.min(node.minSpeedMbps, edgeMin); + if (node.minSpeedMbps == null || edgeMin < node.minSpeedMbps) { + node.minSpeedMbps = edgeMin; + node.minSpeedPeerLabel = peerLabel; + } } }); }); @@ -536,23 +562,23 @@ export default function NetworkMapUnifi({ if (n.maxPingMs != null) { if (n.maxPingMs >= 120) { - issues.push('Очень высокий ping'); + issues.push(`Очень высокий ping${n.maxPingPeerLabel ? ` (с ${n.maxPingPeerLabel})` : ''}`); riskScore += 3; } else if (n.maxPingMs >= 80) { - issues.push('Повышенный ping'); + issues.push(`Повышенный ping${n.maxPingPeerLabel ? ` (с ${n.maxPingPeerLabel})` : ''}`); riskScore += 2; } } if (n.minSpeedMbps != null) { if (n.minSpeedMbps < mapSpeedThresholds.low) { - issues.push('Низкая скорость'); + issues.push(`Низкая скорость${n.minSpeedPeerLabel ? ` (с ${n.minSpeedPeerLabel})` : ''}`); riskScore += 3; } else if (n.minSpeedMbps < mapSpeedThresholds.belowNorm) { - issues.push('Скорость ниже нормы'); + issues.push(`Скорость ниже нормы${n.minSpeedPeerLabel ? ` (с ${n.minSpeedPeerLabel})` : ''}`); riskScore += 2; } else if (n.minSpeedMbps < mapSpeedThresholds.normal) { - issues.push('Норма скорости не достигнута'); + issues.push(`Норма скорости не достигнута${n.minSpeedPeerLabel ? ` (с ${n.minSpeedPeerLabel})` : ''}`); riskScore += 1; } } @@ -572,6 +598,10 @@ export default function NetworkMapUnifi({ issues, riskScore, nodeLabel: n.dns ? String(n.dns).split('.')[0] : n.ip, + affectedLink: + (n.maxPingPeerLabel && `${n.dns ? String(n.dns).split('.')[0] : n.ip} ↔ ${n.maxPingPeerLabel}`) || + (n.minSpeedPeerLabel && `${n.dns ? String(n.dns).split('.')[0] : n.ip} ↔ ${n.minSpeedPeerLabel}`) || + '—', }; }) .filter((n) => n.issues.length > 0) @@ -580,7 +610,7 @@ export default function NetworkMapUnifi({ (b.maxPingMs ?? -1) - (a.maxPingMs ?? -1) || (a.minSpeedMbps ?? Infinity) - (b.minSpeedMbps ?? Infinity) ); - }, [servers, connections, routedEdges, mapSpeedThresholds]); + }, [servers, connections, routedEdges, mapSpeedThresholds, nodeMetaByIp]); const criticalNodes = useMemo( () => problematicNodes.filter((n) => n.riskScore >= 4).slice(0, 8), @@ -1078,13 +1108,14 @@ export default function NetworkMapUnifi({ Ping max Speed min Связи + Проблемная связь Проблемы {criticalNodes.length === 0 ? ( - Критичных узлов не найдено + Критичных узлов не найдено ) : ( criticalNodes.map((n) => ( @@ -1101,6 +1132,9 @@ export default function NetworkMapUnifi({ {n.maxPingMs != null ? `${n.maxPingMs} ms` : '—'} {n.minSpeedMbps != null ? `${n.minSpeedMbps.toFixed(1)} Мбит/с` : '—'} {n.linkCount} + + {n.affectedLink} + {n.issues.slice(0, 2).join(', ')} @@ -1130,13 +1164,14 @@ export default function NetworkMapUnifi({ Ping max Speed min Stale + Проблемная связь Проблемы {warningNodes.length === 0 ? ( - Узлов с предупреждениями не найдено + Узлов с предупреждениями не найдено ) : ( warningNodes.map((n) => ( @@ -1152,6 +1187,9 @@ export default function NetworkMapUnifi({ {n.staleCount} + + {n.affectedLink} + {n.issues.slice(0, 2).join(', ')}