Files
SenMeshNet/electron/bridges/inspection/alertRuleStore.cjs
zhaolei 4a02370006
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
feat: add monitoring settings tab, workbench view, and inspection alert improvements
2026-10-30 12:12:04 +08:00

554 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use strict";
/**
* 告警规则数据存储
*
* 告警规则结构:
* {
* id: "rule_xxx",
* name: "CPU 使用率过高",
* metric: "cpu_usage",
* operator: ">",
* threshold: "80",
* level: "critical",
* duration: 1, // 连续超标次数(防抖动)
* scope_type: "all", // all / tier / device / catalog
* scope_value: null, // 范围值(tier_key 或 hostId 数组 或 catalog_id)
* enabled: true,
* created_at: timestamp,
* updated_at: timestamp,
* }
*/
const { STORAGE_KEY_ALERT_RULES } = require("./storageKeys.cjs");
const { OPERATORS, ALERT_LEVELS } = require("./types.cjs");
let _rules = null;
function _ensureLoaded(readFn) {
if (_rules === null) {
const raw = readFn(STORAGE_KEY_ALERT_RULES);
_rules = raw ? _safeParse(raw, []) : _getDefaultRules();
}
}
function _safeParse(text, def) {
if (text === null || text === undefined) return def;
if (typeof text === "object") return text;
try { return JSON.parse(text); }
catch { return def; }
}
function _save(writeFn) {
writeFn(STORAGE_KEY_ALERT_RULES, JSON.stringify(_rules));
}
/**
* 默认告警规则(预置)
*/
function _getDefaultRules() {
return [
{
id: "rule_cpu_critical",
name: "CPU 使用率过高",
metric: "cpu_usage",
operator: OPERATORS.GT,
threshold: "80",
level: ALERT_LEVELS.CRITICAL,
duration: 1,
scope_type: "all",
scope_value: null,
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_cpu_warning",
name: "CPU 使用率异常",
metric: "cpu_usage",
operator: OPERATORS.GT,
threshold: "70",
level: ALERT_LEVELS.WARNING,
duration: 2,
scope_type: "all",
scope_value: null,
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_memory_critical",
name: "内存使用率过高",
metric: "memory_usage",
operator: OPERATORS.GT,
threshold: "85",
level: ALERT_LEVELS.CRITICAL,
duration: 1,
scope_type: "all",
scope_value: null,
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_memory_warning",
name: "内存使用率异常",
metric: "memory_usage",
operator: OPERATORS.GT,
threshold: "75",
level: ALERT_LEVELS.WARNING,
duration: 2,
scope_type: "all",
scope_value: null,
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_offline",
name: "设备离线",
metric: "online",
operator: OPERATORS.EQ,
threshold: "false",
level: ALERT_LEVELS.CRITICAL,
duration: 1,
scope_type: "all",
scope_value: null,
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
// ---------- 磁盘使用率 ----------
{
id: "rule_disk_critical",
name: "磁盘使用率过高",
metric: "disk_usage",
operator: OPERATORS.GT,
threshold: "90",
level: ALERT_LEVELS.CRITICAL,
duration: 1,
scope_type: "all",
scope_value: null,
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_disk_warning",
name: "磁盘使用率异常",
metric: "disk_usage",
operator: OPERATORS.GT,
threshold: "80",
level: ALERT_LEVELS.WARNING,
duration: 2,
scope_type: "all",
scope_value: null,
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
// ---------- 网络设备通用(交换机/防火墙) ----------
{
id: "rule_netdev_temp_critical",
name: "设备温度过高",
metric: "temperature",
operator: OPERATORS.GT,
threshold: "60",
level: ALERT_LEVELS.CRITICAL,
duration: 2,
scope_type: "catalog",
scope_value: "huawei_h3c_switch",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_netdev_power_critical",
name: "电源模块故障",
metric: "power_failure_count",
operator: OPERATORS.GT,
threshold: "0",
level: ALERT_LEVELS.CRITICAL,
duration: 1,
scope_type: "catalog",
scope_value: "huawei_h3c_switch",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_netdev_fan_warning",
name: "风扇模块故障",
metric: "fan_failure_count",
operator: OPERATORS.GT,
threshold: "0",
level: ALERT_LEVELS.WARNING,
duration: 1,
scope_type: "catalog",
scope_value: "huawei_h3c_switch",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_netdev_portdown_warning",
name: "端口 Down 数过多",
metric: "port_down_count",
operator: OPERATORS.GT,
threshold: "2",
level: ALERT_LEVELS.WARNING,
duration: 2,
scope_type: "catalog",
scope_value: "huawei_h3c_switch",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_netdev_cpu_warning",
name: "交换机 CPU 使用率过高",
metric: "cpu_usage",
operator: OPERATORS.GT,
threshold: "75",
level: ALERT_LEVELS.WARNING,
duration: 2,
scope_type: "catalog",
scope_value: "huawei_h3c_switch",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_netdev_memory_warning",
name: "交换机内存使用率过高",
metric: "memory_usage",
operator: OPERATORS.GT,
threshold: "80",
level: ALERT_LEVELS.WARNING,
duration: 2,
scope_type: "catalog",
scope_value: "huawei_h3c_switch",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
// ---------- Cisco 交换机 ----------
{
id: "rule_cisco_temp_critical",
name: "Cisco 交换机温度过高",
metric: "temperature",
operator: OPERATORS.GT,
threshold: "60",
level: ALERT_LEVELS.CRITICAL,
duration: 2,
scope_type: "catalog",
scope_value: "cisco_switch",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_cisco_power_critical",
name: "Cisco 电源模块故障",
metric: "power_failure_count",
operator: OPERATORS.GT,
threshold: "0",
level: ALERT_LEVELS.CRITICAL,
duration: 1,
scope_type: "catalog",
scope_value: "cisco_switch",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_cisco_fan_warning",
name: "Cisco 风扇模块故障",
metric: "fan_failure_count",
operator: OPERATORS.GT,
threshold: "0",
level: ALERT_LEVELS.WARNING,
duration: 1,
scope_type: "catalog",
scope_value: "cisco_switch",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
// ---------- 华为防火墙 ----------
{
id: "rule_hwfw_temp_critical",
name: "华为防火墙温度过高",
metric: "temperature",
operator: OPERATORS.GT,
threshold: "60",
level: ALERT_LEVELS.CRITICAL,
duration: 2,
scope_type: "catalog",
scope_value: "huawei_firewall",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_hwfw_power_critical",
name: "华为防火墙电源故障",
metric: "power_failure_count",
operator: OPERATORS.GT,
threshold: "0",
level: ALERT_LEVELS.CRITICAL,
duration: 1,
scope_type: "catalog",
scope_value: "huawei_firewall",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_hwfw_fan_warning",
name: "华为防火墙风扇故障",
metric: "fan_failure_count",
operator: OPERATORS.GT,
threshold: "0",
level: ALERT_LEVELS.WARNING,
duration: 1,
scope_type: "catalog",
scope_value: "huawei_firewall",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
// ---------- 飞塔防火墙 ----------
{
id: "rule_fortinet_temp_critical",
name: "飞塔防火墙温度过高",
metric: "temperature",
operator: OPERATORS.GT,
threshold: "60",
level: ALERT_LEVELS.CRITICAL,
duration: 2,
scope_type: "catalog",
scope_value: "fortinet_firewall",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_fortinet_power_critical",
name: "飞塔防火墙电源故障",
metric: "power_failure_count",
operator: OPERATORS.GT,
threshold: "0",
level: ALERT_LEVELS.CRITICAL,
duration: 1,
scope_type: "catalog",
scope_value: "fortinet_firewall",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
{
id: "rule_fortinet_fan_warning",
name: "飞塔防火墙风扇故障",
metric: "fan_failure_count",
operator: OPERATORS.GT,
threshold: "0",
level: ALERT_LEVELS.WARNING,
duration: 1,
scope_type: "catalog",
scope_value: "fortinet_firewall",
enabled: true,
preset: true,
created_at: Date.now(),
updated_at: Date.now(),
},
];
}
// ---------- CRUD ----------
function listRules(readFn) {
_ensureLoaded(readFn);
return [..._rules];
}
function getRule(readFn, ruleId) {
_ensureLoaded(readFn);
return _rules.find((r) => r.id === ruleId) || null;
}
function createRule(readFn, writeFn, data) {
_ensureLoaded(readFn);
const rule = {
id: `rule_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
name: data.name,
metric: data.metric,
operator: data.operator,
threshold: String(data.threshold),
level: data.level,
duration: data.duration || 1,
scope_type: data.scope_type || "all",
scope_value: data.scope_value || null,
enabled: data.enabled !== false,
preset: false,
created_at: Date.now(),
updated_at: Date.now(),
};
_rules.push(rule);
_save(writeFn);
return { ...rule };
}
function updateRule(readFn, writeFn, ruleId, updates) {
_ensureLoaded(readFn);
const idx = _rules.findIndex((r) => r.id === ruleId);
if (idx === -1) return null;
_rules[idx] = {
..._rules[idx],
...updates,
updated_at: Date.now(),
};
_save(writeFn);
return { ..._rules[idx] };
}
function deleteRule(readFn, writeFn, ruleId) {
_ensureLoaded(readFn);
const idx = _rules.findIndex((r) => r.id === ruleId);
if (idx === -1) return false;
_rules.splice(idx, 1);
_save(writeFn);
return true;
}
function toggleRule(readFn, writeFn, ruleId, enabled) {
return updateRule(readFn, writeFn, ruleId, { enabled });
}
// ---------- 告警判断 ----------
/**
* 判断指标值是否触发规则
* @param {number|string} value - 指标值
* @param {string} operator - 比较符
* @param {string} threshold - 阈值
* @returns {boolean}
*/
function evaluateRule(value, operator, threshold) {
if (value === null || value === undefined) return false;
// disk_usage 是数组,检查任意挂载点是否超标
if (Array.isArray(value)) {
const numThreshold = parseFloat(threshold);
if (isNaN(numThreshold)) return false;
// 数组中每个元素是 { filesystem, usage_percent, mount }
return value.some((d) => {
const usage = typeof d === "object" ? d.usage_percent : parseFloat(d);
return !isNaN(usage) && usage > numThreshold;
});
}
// 数字比较
const numValue = parseFloat(value);
const numThreshold = parseFloat(threshold);
if (!isNaN(numValue) && !isNaN(numThreshold)) {
switch (operator) {
case OPERATORS.GT: return numValue > numThreshold;
case OPERATORS.LT: return numValue < numThreshold;
case OPERATORS.GTE: return numValue >= numThreshold;
case OPERATORS.LTE: return numValue <= numThreshold;
case OPERATORS.EQ: return numValue === numThreshold;
case OPERATORS.NE: return numValue !== numThreshold;
}
}
// 字符串比较
const strValue = String(value);
const strThreshold = String(threshold);
switch (operator) {
case OPERATORS.EQ: return strValue === strThreshold;
case OPERATORS.NE: return strValue !== strThreshold;
default: return false;
}
}
/**
* 获取设备适用的所有规则
* @param {object} deviceExt - 设备扩展信息
* @returns {Array} 适用的规则列表
*/
function getRulesForDevice(readFn, deviceExt) {
_ensureLoaded(readFn);
return _rules.filter((rule) => {
if (!rule.enabled) return false;
switch (rule.scope_type) {
case "all":
return true;
case "tier":
return deviceExt.tier && deviceExt.tier === rule.scope_value;
case "device":
return Array.isArray(rule.scope_value) && rule.scope_value.includes(deviceExt.host_id);
case "catalog":
return deviceExt.catalog_id === rule.scope_value;
default:
return false;
}
});
}
// ---------- 批量保存(远程 HTTP 用) ----------
function saveRules(readFn, writeFn, rules) {
_rules = Array.isArray(rules) ? rules : [];
_save(writeFn);
return _rules;
}
// ---------- 重置(测试用) ----------
function _resetForTest() {
_rules = null;
}
module.exports = {
listRules,
getRule,
createRule,
updateRule,
deleteRule,
toggleRule,
evaluateRule,
getRulesForDevice,
saveRules,
_resetForTest,
};