From d68b3aa104379513b17bb0cc00d2edfaa1814571 Mon Sep 17 00:00:00 2001 From: shats Date: Wed, 25 Feb 2026 02:15:10 +0700 Subject: [PATCH] feat(network-map): add network speed thresholds for alerts and enhance settings UI for configuration --- backend/routes/alertsRoutes.js | 12 +++++ frontend/src/NetworkMapDashboard.jsx | 7 +++ frontend/src/NetworkMapUnifi.jsx | 22 ++++++-- frontend/src/SettingsPage.jsx | 78 ++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 3 deletions(-) diff --git a/backend/routes/alertsRoutes.js b/backend/routes/alertsRoutes.js index 70f6333..3a02fd1 100644 --- a/backend/routes/alertsRoutes.js +++ b/backend/routes/alertsRoutes.js @@ -18,6 +18,12 @@ const DEFAULT_ALERT_SETTINGS = { highCpu: { enabled: true, thresholdPercent: 85, durationMinutes: 10 }, highRam: { enabled: true, thresholdPercent: 85, durationMinutes: 10 }, highHdd: { enabled: true, thresholdPercent: 90, durationMinutes: 10 }, + /** Глобальные пороги карты сети для UI-аналитики */ + networkMapNodeThresholds: { + lowSpeedMbps: 40, + belowNormSpeedMbps: 80, + normalSpeedMbps: 120, + }, /** Индивидуальные пороги по туннелям (карта сети) */ tunnelThresholds: [], }; @@ -30,6 +36,12 @@ function getAlertSettings(uiSettings) { highCpu: { ...DEFAULT_ALERT_SETTINGS.highCpu, ...raw.highCpu }, highRam: { ...DEFAULT_ALERT_SETTINGS.highRam, ...raw.highRam }, highHdd: { ...DEFAULT_ALERT_SETTINGS.highHdd, ...raw.highHdd }, + networkMapNodeThresholds: { + ...DEFAULT_ALERT_SETTINGS.networkMapNodeThresholds, + ...(raw.networkMapNodeThresholds && typeof raw.networkMapNodeThresholds === 'object' + ? raw.networkMapNodeThresholds + : {}), + }, tunnelThresholds: Array.isArray(raw.tunnelThresholds) ? raw.tunnelThresholds : [], }; } diff --git a/frontend/src/NetworkMapDashboard.jsx b/frontend/src/NetworkMapDashboard.jsx index bbc8d68..033ce8d 100644 --- a/frontend/src/NetworkMapDashboard.jsx +++ b/frontend/src/NetworkMapDashboard.jsx @@ -76,6 +76,7 @@ export default function NetworkMapDashboard() { const lastSuccessfulPingRef = useRef({}); const lastSuccessfulSpeedRef = useRef({}); const [pingCacheSeconds, setPingCacheSeconds] = useState(0); + const [alertSettings, setAlertSettings] = useState({}); const [loading, setLoading] = useState(true); const [pingLoading, setPingLoading] = useState(false); const [speedLoading, setSpeedLoading] = useState(false); @@ -102,12 +103,17 @@ export default function NetworkMapDashboard() { const config = configRes?.data || {}; const tunnelInterfaces = Array.isArray(config.tunnelInterfaces) ? config.tunnelInterfaces : []; const uiSettings = uiSettingsRes?.data || {}; + const uiAlertSettings = + uiSettings.alertSettings && typeof uiSettings.alertSettings === 'object' + ? uiSettings.alertSettings + : {}; const cache = cacheRes?.data || {}; const ttlSeconds = Math.max( 0, parseInt(uiSettings.networkMapPingCacheSeconds, 10) || 0 ); setPingCacheSeconds(ttlSeconds); + setAlertSettings(uiAlertSettings); const getServer = (serverId) => serversList.find((s) => s.id === serverId || s.ip === serverId || s.dns === serverId); @@ -462,6 +468,7 @@ export default function NetworkMapDashboard() { onRefreshSpeedForConnection={handleRefreshSpeedForConnection} pingLoading={pingLoading} speedLoading={speedLoading} + alertSettings={alertSettings} /> )} diff --git a/frontend/src/NetworkMapUnifi.jsx b/frontend/src/NetworkMapUnifi.jsx index c5756e0..2252940 100644 --- a/frontend/src/NetworkMapUnifi.jsx +++ b/frontend/src/NetworkMapUnifi.jsx @@ -144,6 +144,7 @@ export default function NetworkMapUnifi({ onRefreshSpeedForConnection, pingLoading = false, speedLoading = false, + alertSettings = {}, }) { const containerRef = useRef(null); const svgRef = useRef(null); @@ -154,6 +155,18 @@ export default function NetworkMapUnifi({ const [hoveredEdgeKey, setHoveredEdgeKey] = useState(null); const [selectedEdge, setSelectedEdge] = useState(null); + const mapSpeedThresholds = useMemo(() => { + const raw = + alertSettings?.networkMapNodeThresholds && + typeof alertSettings.networkMapNodeThresholds === 'object' + ? alertSettings.networkMapNodeThresholds + : {}; + const low = Math.max(1, Number(raw.lowSpeedMbps) || 40); + const belowNorm = Math.max(low, Number(raw.belowNormSpeedMbps) || 80); + const normal = Math.max(belowNorm, Number(raw.normalSpeedMbps) || 120); + return { low, belowNorm, normal }; + }, [alertSettings]); + useEffect(() => { const el = containerRef.current; if (!el) return; @@ -532,12 +545,15 @@ export default function NetworkMapUnifi({ } if (n.minSpeedMbps != null) { - if (n.minSpeedMbps < 40) { + if (n.minSpeedMbps < mapSpeedThresholds.low) { issues.push('Низкая скорость'); riskScore += 3; - } else if (n.minSpeedMbps < 80) { + } else if (n.minSpeedMbps < mapSpeedThresholds.belowNorm) { issues.push('Скорость ниже нормы'); riskScore += 2; + } else if (n.minSpeedMbps < mapSpeedThresholds.normal) { + issues.push('Норма скорости не достигнута'); + riskScore += 1; } } @@ -564,7 +580,7 @@ export default function NetworkMapUnifi({ (b.maxPingMs ?? -1) - (a.maxPingMs ?? -1) || (a.minSpeedMbps ?? Infinity) - (b.minSpeedMbps ?? Infinity) ); - }, [servers, connections, routedEdges]); + }, [servers, connections, routedEdges, mapSpeedThresholds]); const criticalNodes = useMemo( () => problematicNodes.filter((n) => n.riskScore >= 4).slice(0, 8), diff --git a/frontend/src/SettingsPage.jsx b/frontend/src/SettingsPage.jsx index 3ad5207..cdedbca 100644 --- a/frontend/src/SettingsPage.jsx +++ b/frontend/src/SettingsPage.jsx @@ -114,6 +114,9 @@ export default function SettingsPage() { const [alertHighHddEnabled, setAlertHighHddEnabled] = useState(true); const [alertHighHddThreshold, setAlertHighHddThreshold] = useState('90'); const [alertHighHddDurationMinutes, setAlertHighHddDurationMinutes] = useState('10'); + const [alertMapLowSpeedMbps, setAlertMapLowSpeedMbps] = useState('40'); + const [alertMapBelowNormSpeedMbps, setAlertMapBelowNormSpeedMbps] = useState('80'); + const [alertMapNormalSpeedMbps, setAlertMapNormalSpeedMbps] = useState('120'); const [serversList, setServersList] = useState([]); const [tunnelConnections, setTunnelConnections] = useState([]); const [tunnelThresholds, setTunnelThresholds] = useState([]); @@ -385,6 +388,21 @@ export default function SettingsPage() { setAlertHighHddDurationMinutes( a.highHdd?.durationMinutes != null ? String(a.highHdd.durationMinutes) : '10' ); + setAlertMapLowSpeedMbps( + a.networkMapNodeThresholds?.lowSpeedMbps != null + ? String(a.networkMapNodeThresholds.lowSpeedMbps) + : '40' + ); + setAlertMapBelowNormSpeedMbps( + a.networkMapNodeThresholds?.belowNormSpeedMbps != null + ? String(a.networkMapNodeThresholds.belowNormSpeedMbps) + : '80' + ); + setAlertMapNormalSpeedMbps( + a.networkMapNodeThresholds?.normalSpeedMbps != null + ? String(a.networkMapNodeThresholds.normalSpeedMbps) + : '120' + ); const rawTunnelThresholds = Array.isArray(a.tunnelThresholds) ? a.tunnelThresholds : []; setTunnelThresholds( @@ -510,6 +528,9 @@ export default function SettingsPage() { setSaving(true); try { + const parsedLow = Math.max(1, parseInt(alertMapLowSpeedMbps, 10) || 40); + const parsedBelowNorm = Math.max(parsedLow, parseInt(alertMapBelowNormSpeedMbps, 10) || 80); + const parsedNormal = Math.max(parsedBelowNorm, parseInt(alertMapNormalSpeedMbps, 10) || 120); const mergedSettings = { ...rawSettings, dohServer: String(dohServer || '').trim(), @@ -617,6 +638,11 @@ export default function SettingsPage() { Math.min(1440, parseInt(alertHighHddDurationMinutes, 10) || 10) ), }, + networkMapNodeThresholds: { + lowSpeedMbps: Math.min(100000, parsedLow), + belowNormSpeedMbps: Math.min(100000, parsedBelowNorm), + normalSpeedMbps: Math.min(100000, parsedNormal), + }, tunnelThresholds: Array.isArray(tunnelThresholds) ? tunnelThresholds .filter((t) => { @@ -1489,6 +1515,58 @@ export default function SettingsPage() { + {/* Карта сети: глобальные пороги скорости */} +
+
+
+ + Карта сети: пороги скорости +
+
+ Единые пороги для аналитики узлов на карте и классификации: «Низкая скорость», «Скорость ниже нормы», «Норма». +
+
+
+
+ Низкая + setAlertMapLowSpeedMbps(e.target.value)} + disabled={saving} + /> +
+
+ Ниже нормы + setAlertMapBelowNormSpeedMbps(e.target.value)} + disabled={saving} + /> +
+
+ Норма + setAlertMapNormalSpeedMbps(e.target.value)} + disabled={saving} + /> + Мбит/с +
+
+
+ {/* Туннели (карта сети): индивидуальные пороги */}
Туннели (карта сети)