544 lines
14 KiB
JavaScript
544 lines
14 KiB
JavaScript
|
|
"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) {
|
|||
|
|
try { return JSON.parse(text); }
|
|||
|
|
catch (e) { 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;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------- 重置(测试用) ----------
|
|||
|
|
function _resetForTest() {
|
|||
|
|
_rules = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = {
|
|||
|
|
listRules,
|
|||
|
|
getRule,
|
|||
|
|
createRule,
|
|||
|
|
updateRule,
|
|||
|
|
deleteRule,
|
|||
|
|
toggleRule,
|
|||
|
|
evaluateRule,
|
|||
|
|
getRulesForDevice,
|
|||
|
|
_resetForTest,
|
|||
|
|
};
|