feat(network-map): add network speed thresholds for alerts and enhance settings UI for configuration
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m32s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m32s
This commit is contained in:
@@ -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 : [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Карта сети: глобальные пороги скорости */}
|
||||
<div className="list-group-item d-flex flex-column flex-md-row align-items-stretch align-items-md-center gap-3 py-3">
|
||||
<div className="flex-grow-1 min-w-0">
|
||||
<div className="fw-semibold d-flex align-items-center gap-2">
|
||||
<IconNetwork size={18} className="text-azure" />
|
||||
Карта сети: пороги скорости
|
||||
</div>
|
||||
<div className="text-muted small">
|
||||
Единые пороги для аналитики узлов на карте и классификации: «Низкая скорость», «Скорость ниже нормы», «Норма».
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-wrap align-items-center gap-2">
|
||||
<div className="input-group input-group-sm" style={{ width: 132 }}>
|
||||
<span className="input-group-text">Низкая</span>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
min={1}
|
||||
max={100000}
|
||||
value={alertMapLowSpeedMbps}
|
||||
onChange={(e) => setAlertMapLowSpeedMbps(e.target.value)}
|
||||
disabled={saving}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-group input-group-sm" style={{ width: 162 }}>
|
||||
<span className="input-group-text">Ниже нормы</span>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
min={1}
|
||||
max={100000}
|
||||
value={alertMapBelowNormSpeedMbps}
|
||||
onChange={(e) => setAlertMapBelowNormSpeedMbps(e.target.value)}
|
||||
disabled={saving}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-group input-group-sm" style={{ width: 120 }}>
|
||||
<span className="input-group-text">Норма</span>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control"
|
||||
min={1}
|
||||
max={100000}
|
||||
value={alertMapNormalSpeedMbps}
|
||||
onChange={(e) => setAlertMapNormalSpeedMbps(e.target.value)}
|
||||
disabled={saving}
|
||||
/>
|
||||
<span className="input-group-text">Мбит/с</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Туннели (карта сети): индивидуальные пороги */}
|
||||
<div className="list-group-item py-3">
|
||||
<div className="fw-semibold mb-1">Туннели (карта сети)</div>
|
||||
|
||||
Reference in New Issue
Block a user