refactor(alerts): consolidate tunnel alert settings into a single structure with enhanced UI controls
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m47s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m47s
This commit is contained in:
+120
-82
@@ -18,10 +18,8 @@ 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 },
|
||||
/** Порог по пингу туннелей (карта сети) */
|
||||
tunnelHighPing: { enabled: false, thresholdMs: 0 },
|
||||
/** Пороги по скорости туннелей (карта сети) */
|
||||
tunnelLowSpeed: { enabled: false, minDownloadMbps: 0, minUploadMbps: 0 },
|
||||
/** Индивидуальные пороги по туннелям (карта сети) */
|
||||
tunnelThresholds: [],
|
||||
};
|
||||
|
||||
function getAlertSettings(uiSettings) {
|
||||
@@ -32,8 +30,7 @@ function getAlertSettings(uiSettings) {
|
||||
highCpu: { ...DEFAULT_ALERT_SETTINGS.highCpu, ...raw.highCpu },
|
||||
highRam: { ...DEFAULT_ALERT_SETTINGS.highRam, ...raw.highRam },
|
||||
highHdd: { ...DEFAULT_ALERT_SETTINGS.highHdd, ...raw.highHdd },
|
||||
tunnelHighPing: { ...DEFAULT_ALERT_SETTINGS.tunnelHighPing, ...raw.tunnelHighPing },
|
||||
tunnelLowSpeed: { ...DEFAULT_ALERT_SETTINGS.tunnelLowSpeed, ...raw.tunnelLowSpeed },
|
||||
tunnelThresholds: Array.isArray(raw.tunnelThresholds) ? raw.tunnelThresholds : [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -193,13 +190,15 @@ async function getAlerts(req, res) {
|
||||
}
|
||||
});
|
||||
|
||||
// 3) Туннели (карта сети): высокий пинг и/или низкая скорость
|
||||
const tunnelPingSettings = settings.tunnelHighPing || {};
|
||||
const tunnelSpeedSettings = settings.tunnelLowSpeed || {};
|
||||
const tunnelPingEnabled = Boolean(tunnelPingSettings.enabled);
|
||||
const tunnelSpeedEnabled = Boolean(tunnelSpeedSettings.enabled);
|
||||
// 3) Туннели (карта сети): индивидуальные пороги по пингу и скорости
|
||||
const rawTunnelThresholds = Array.isArray(settings.tunnelThresholds)
|
||||
? settings.tunnelThresholds
|
||||
: [];
|
||||
const tunnelThresholds = rawTunnelThresholds.filter(
|
||||
(t) => t && (t.fromKey || t.toKey) && t.enabled !== false
|
||||
);
|
||||
|
||||
if (tunnelPingEnabled || tunnelSpeedEnabled) {
|
||||
if (tunnelThresholds.length > 0) {
|
||||
let cache = null;
|
||||
let connectionsData = null;
|
||||
try {
|
||||
@@ -221,88 +220,127 @@ async function getAlerts(req, res) {
|
||||
const pingMap = cache.pingMap && typeof cache.pingMap === 'object' ? cache.pingMap : {};
|
||||
const speedMap = cache.speedMap && typeof cache.speedMap === 'object' ? cache.speedMap : {};
|
||||
|
||||
const thPingMs = Math.max(1, Number(tunnelPingSettings.thresholdMs) || 0);
|
||||
const minDownMbps = Math.max(0, Number(tunnelSpeedSettings.minDownloadMbps) || 0);
|
||||
const minUpMbps = Math.max(0, Number(tunnelSpeedSettings.minUploadMbps) || 0);
|
||||
const serversByIp = new Map();
|
||||
servers.forEach((s) => {
|
||||
if (s && s.ip) {
|
||||
serversByIp.set(String(s.ip), s);
|
||||
}
|
||||
});
|
||||
|
||||
if (thPingMs > 0 || minDownMbps > 0 || minUpMbps > 0) {
|
||||
const serversByIp = new Map();
|
||||
servers.forEach((s) => {
|
||||
if (s && s.ip) {
|
||||
serversByIp.set(String(s.ip), s);
|
||||
}
|
||||
const thresholdsByKey = new Map();
|
||||
const makeKey = (fromKey, toKey, iface) => {
|
||||
const a = String(fromKey || '');
|
||||
const b = String(toKey || '');
|
||||
const pair = [a, b].sort().join('__');
|
||||
return `${pair}::${iface || ''}`;
|
||||
};
|
||||
|
||||
tunnelThresholds.forEach((t) => {
|
||||
const key = makeKey(t.fromKey, t.toKey, t.interfaceName || '');
|
||||
const maxPingMs =
|
||||
typeof t.maxPingMs === 'number'
|
||||
? t.maxPingMs
|
||||
: t.thresholdMs != null
|
||||
? Number(t.thresholdMs) || 0
|
||||
: 0;
|
||||
const minDownloadMbps =
|
||||
typeof t.minDownloadMbps === 'number'
|
||||
? t.minDownloadMbps
|
||||
: t.minDownMbps != null
|
||||
? Number(t.minDownMbps) || 0
|
||||
: 0;
|
||||
const minUploadMbps =
|
||||
typeof t.minUploadMbps === 'number'
|
||||
? t.minUploadMbps
|
||||
: t.minUpMbps != null
|
||||
? Number(t.minUpMbps) || 0
|
||||
: 0;
|
||||
thresholdsByKey.set(key, {
|
||||
maxPingMs,
|
||||
minDownloadMbps,
|
||||
minUploadMbps,
|
||||
});
|
||||
});
|
||||
|
||||
connectionsData.connections.forEach((c) => {
|
||||
const fromIp = String(c.from);
|
||||
const toIp = String(c.to);
|
||||
if (!fromIp || !toIp || fromIp === toIp) return;
|
||||
connectionsData.connections.forEach((c) => {
|
||||
const fromIp = String(c.from);
|
||||
const toIp = String(c.to);
|
||||
if (!fromIp || !toIp || fromIp === toIp) return;
|
||||
|
||||
const fromServer = serversByIp.get(fromIp) || null;
|
||||
const toServer = serversByIp.get(toIp) || null;
|
||||
const fromName = fromServer?.name || fromServer?.dns || fromIp;
|
||||
const toName = toServer?.name || toServer?.dns || toIp;
|
||||
const fromServer = serversByIp.get(fromIp) || null;
|
||||
const toServer = serversByIp.get(toIp) || null;
|
||||
const fromName = fromServer?.name || fromServer?.dns || fromIp;
|
||||
const toName = toServer?.name || toServer?.dns || toIp;
|
||||
|
||||
const pingMapKey = edgePingKey(fromIp, toIp);
|
||||
const pingMs = typeof pingMap[pingMapKey] === 'number' ? pingMap[pingMapKey] : null;
|
||||
const fromKey = c.fromKey || fromIp;
|
||||
const toKey = c.toKey || toIp;
|
||||
const key = makeKey(fromKey, toKey, c.interfaceName || '');
|
||||
const th = thresholdsByKey.get(key);
|
||||
if (!th) return;
|
||||
|
||||
if (tunnelPingEnabled && thPingMs > 0 && pingMs != null && pingMs >= thPingMs) {
|
||||
alerts.push({
|
||||
id: `tunnel-ping-${pingMapKey}-${thPingMs}`,
|
||||
type: 'tunnel_high_ping',
|
||||
severity: 'warning',
|
||||
title: 'Высокий пинг по туннелю',
|
||||
description: `Пинг между ${fromName} и ${toName}: ${pingMs} ms (порог ${thPingMs} ms).`,
|
||||
entity: `${fromName} ⇄ ${toName}`,
|
||||
entityId: `${c.fromKey || fromIp}__${c.toKey || toIp}__${c.interfaceName || ''}`,
|
||||
link: '/network-map',
|
||||
at: now,
|
||||
});
|
||||
}
|
||||
const maxPingMs = Math.max(0, Number(th.maxPingMs) || 0);
|
||||
const minDownMbps = Math.max(0, Number(th.minDownloadMbps) || 0);
|
||||
const minUpMbps = Math.max(0, Number(th.minUploadMbps) || 0);
|
||||
|
||||
if (tunnelSpeedEnabled && (minDownMbps > 0 || minUpMbps > 0) && c.fromKey && c.toKey) {
|
||||
const sk = speedKey(c.fromKey, c.toKey);
|
||||
const speed = speedMap[sk];
|
||||
if (speed && typeof speed === 'object') {
|
||||
const downMbps =
|
||||
typeof speed.tcpDownloadBps === 'number' ? speed.tcpDownloadBps / 1e6 : null;
|
||||
const upMbps =
|
||||
typeof speed.tcpUploadBps === 'number' ? speed.tcpUploadBps / 1e6 : null;
|
||||
const pingMapKey = edgePingKey(fromIp, toIp);
|
||||
const pingMs = typeof pingMap[pingMapKey] === 'number' ? pingMap[pingMapKey] : null;
|
||||
|
||||
const downTooLow = minDownMbps > 0 && (downMbps == null || downMbps < minDownMbps);
|
||||
const upTooLow = minUpMbps > 0 && (upMbps == null || upMbps < minUpMbps);
|
||||
if (maxPingMs > 0 && pingMs != null && pingMs >= maxPingMs) {
|
||||
alerts.push({
|
||||
id: `tunnel-ping-${pingMapKey}-${maxPingMs}`,
|
||||
type: 'tunnel_high_ping',
|
||||
severity: 'warning',
|
||||
title: 'Высокий пинг по туннелю',
|
||||
description: `Пинг между ${fromName} и ${toName}: ${pingMs} ms (порог ${maxPingMs} ms).`,
|
||||
entity: `${fromName} ⇄ ${toName}`,
|
||||
entityId: `${c.fromKey || fromIp}__${c.toKey || toIp}__${c.interfaceName || ''}`,
|
||||
link: '/network-map',
|
||||
at: now,
|
||||
});
|
||||
}
|
||||
|
||||
if (downTooLow || upTooLow) {
|
||||
const parts = [];
|
||||
if (downTooLow) {
|
||||
parts.push(
|
||||
`↓ ${downMbps != null ? downMbps.toFixed(1) : '—'} Мбит/с (порог ${minDownMbps} Мбит/с)`
|
||||
);
|
||||
}
|
||||
if (upTooLow) {
|
||||
parts.push(
|
||||
`↑ ${upMbps != null ? upMbps.toFixed(1) : '—'} Мбит/с (порог ${minUpMbps} Мбит/с)`
|
||||
);
|
||||
}
|
||||
const ifaceLabel = c.interfaceName ? `, интерфейс ${c.interfaceName}` : '';
|
||||
alerts.push({
|
||||
id: `tunnel-speed-${sk}-${minDownMbps}-${minUpMbps}`,
|
||||
type: 'tunnel_low_speed',
|
||||
severity: 'warning',
|
||||
title: 'Низкая скорость по туннелю',
|
||||
description: `Туннель между ${fromName} и ${toName}${ifaceLabel}: ${parts.join(
|
||||
'; '
|
||||
)}.`,
|
||||
entity: `${fromName} ⇄ ${toName}`,
|
||||
entityId: `${c.fromKey || fromIp}__${c.toKey || toIp}__${c.interfaceName || ''}`,
|
||||
link: '/network-map',
|
||||
at: now,
|
||||
});
|
||||
if ((minDownMbps > 0 || minUpMbps > 0) && c.fromKey && c.toKey) {
|
||||
const sk = speedKey(c.fromKey, c.toKey);
|
||||
const speed = speedMap[sk];
|
||||
if (speed && typeof speed === 'object') {
|
||||
const downMbps =
|
||||
typeof speed.tcpDownloadBps === 'number' ? speed.tcpDownloadBps / 1e6 : null;
|
||||
const upMbps =
|
||||
typeof speed.tcpUploadBps === 'number' ? speed.tcpUploadBps / 1e6 : null;
|
||||
|
||||
const downTooLow = minDownMbps > 0 && (downMbps == null || downMbps < minDownMbps);
|
||||
const upTooLow = minUpMbps > 0 && (upMbps == null || upMbps < minUpMbps);
|
||||
|
||||
if (downTooLow || upTooLow) {
|
||||
const parts = [];
|
||||
if (downTooLow) {
|
||||
parts.push(
|
||||
`↓ ${downMbps != null ? downMbps.toFixed(1) : '—'} Мбит/с (порог ${minDownMbps} Мбит/с)`
|
||||
);
|
||||
}
|
||||
if (upTooLow) {
|
||||
parts.push(
|
||||
`↑ ${upMbps != null ? upMbps.toFixed(1) : '—'} Мбит/с (порог ${minUpMbps} Мбит/с)`
|
||||
);
|
||||
}
|
||||
const ifaceLabel = c.interfaceName ? `, интерфейс ${c.interfaceName}` : '';
|
||||
alerts.push({
|
||||
id: `tunnel-speed-${sk}-${minDownMbps}-${minUpMbps}`,
|
||||
type: 'tunnel_low_speed',
|
||||
severity: 'warning',
|
||||
title: 'Низкая скорость по туннелю',
|
||||
description: `Туннель между ${fromName} и ${toName}${ifaceLabel}: ${parts.join(
|
||||
'; '
|
||||
)}.`,
|
||||
entity: `${fromName} ⇄ ${toName}`,
|
||||
entityId: `${c.fromKey || fromIp}__${c.toKey || toIp}__${c.interfaceName || ''}`,
|
||||
link: '/network-map',
|
||||
at: now,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user