Files
SenMeshNet/electron/bridges/inspection/deviceStore.cjs
zhaolei 2f9f8da48e
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
Initial commit: NetMesh smart ops platform with 3D topology, inspection reports, RCA and AI ops assistant
2026-10-15 13:53:04 +08:00

291 lines
7.5 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";
/**
* 设备扩展数据存储(巡检相关配置)
*
* 关联 NetMesh 的主机数据,扩展巡检相关字段:
* - 设备类型(catalog_id)
* - 设备层级(core/aggregation/access)
* - 巡检周期
* - 启用状态
* - 自定义告警阈值
* - 上次巡检信息
*
* 周期优先级(从高到低):
* 1. 设备独立设置 inspection_interval
* 2. 设备层级的默认周期 DEVICE_TIER_DEFAULT_INTERVAL
* 3. 系统默认周期 SYS_CONFIG_DEFAULTS.inspection_default_interval
*/
const {
STORAGE_KEY_DEVICE_EXTENSION,
STORAGE_KEY_SYS_CONFIG,
} = require("./storageKeys.cjs");
const {
DEVICE_TIERS,
DEVICE_TIER_LABELS,
DEVICE_TIER_DEFAULT_INTERVAL,
SYS_CONFIG_DEFAULTS,
} = require("./types.cjs");
const { getPresetCatalog } = require("./deviceCatalogStore.cjs");
// ---------- 内存缓存 ----------
let _extensions = null; // { [hostId]: { ... } }
let _sysConfig = null; // { inspection_default_interval: 5, ... }
let _catalog = null; // 设备类型列表
/**
* 从存储加载数据(首次调用时加载,之后用缓存)
* @param {function} readFn - 读取函数 (key) => rawString
*/
function _ensureLoaded(readFn) {
if (_extensions === null) {
const raw = readFn(STORAGE_KEY_DEVICE_EXTENSION);
_extensions = raw ? _safeParse(raw, {}) : {};
}
if (_sysConfig === null) {
const raw = readFn(STORAGE_KEY_SYS_CONFIG);
_sysConfig = raw ? _safeParse(raw, {}) : { ...SYS_CONFIG_DEFAULTS };
// 合并默认值
for (const k of Object.keys(SYS_CONFIG_DEFAULTS)) {
if (_sysConfig[k] === undefined) _sysConfig[k] = SYS_CONFIG_DEFAULTS[k];
}
}
if (_catalog === null) {
_catalog = getPresetCatalog();
}
}
function _safeParse(text, def) {
if (text === null || text === undefined) return def;
let parsed;
try { parsed = JSON.parse(text); }
catch (e) { return def; }
// 防御二次/三次 stringify:inspectionStorage.writeStorage 先 JSON.stringify 一次,
// _save 又 JSON.stringify 一次 → 文件里值是 "\"{...}\"" 这种嵌套字符串
// 连续 parse 直到拿到真正的对象/数组(最多 3 层避免死循环)
let depth = 0;
while (typeof parsed === "string" && parsed.length > 0 && depth < 3) {
let next;
try { next = JSON.parse(parsed); } catch { break; }
if (typeof next === "string" && next === parsed) break;
parsed = next;
depth++;
}
return (parsed !== null && typeof parsed === "object") ? parsed : def;
}
function _save(writeFn, key, data) {
writeFn(key, JSON.stringify(data));
}
// ---------- 系统配置 ----------
/**
* 获取系统配置
*/
function getSysConfig(readFn) {
_ensureLoaded(readFn);
return { ..._sysConfig };
}
/**
* 更新系统配置
*/
function updateSysConfig(readFn, writeFn, updates) {
_ensureLoaded(readFn);
_sysConfig = { ..._sysConfig, ...updates };
_save(writeFn, STORAGE_KEY_SYS_CONFIG, _sysConfig);
return { ..._sysConfig };
}
// ---------- 设备扩展 ----------
/**
* 获取所有设备扩展配置
*/
function getAllExtensions(readFn) {
_ensureLoaded(readFn);
return { ..._extensions };
}
/**
* 获取单个设备的扩展配置
*/
function getExtension(readFn, hostId) {
_ensureLoaded(readFn);
return _extensions[hostId] || null;
}
/**
* 创建/更新设备扩展配置
* @param {string} hostId - NetMesh 主机 ID
* @param {object} data - 扩展配置
* @param {string} data.catalog_id - 设备类型 ID
* @param {string} [data.tier] - 设备层级 core/aggregation/access
* @param {number} [data.inspection_interval] - 巡检周期(分钟),null 表示继承
* @param {boolean} [data.inspection_enabled] - 是否启用巡检
* @param {object} [data.custom_thresholds] - 自定义告警阈值
* @param {string[]} [data.inspection_tags] - 巡检标签
* @param {string} [data.hostname] - 主机名(IP 或域名),用于调度器关联 vault host SSH 配置
*/
function upsertExtension(readFn, writeFn, hostId, data) {
_ensureLoaded(readFn);
const existing = _extensions[hostId] || {
host_id: hostId,
hostname: null,
catalog_id: "",
tier: null,
inspection_interval: null,
inspection_enabled: false,
custom_thresholds: [],
inspection_tags: [],
last_scan_at: null,
last_scan_status: null,
last_scan_error: null,
};
_extensions[hostId] = {
...existing,
...data,
updated_at: Date.now(),
};
_save(writeFn, STORAGE_KEY_DEVICE_EXTENSION, _extensions);
return { ..._extensions[hostId] };
}
/**
* 删除设备扩展配置
*/
function deleteExtension(readFn, writeFn, hostId) {
_ensureLoaded(readFn);
if (_extensions[hostId]) {
delete _extensions[hostId];
_save(writeFn, STORAGE_KEY_DEVICE_EXTENSION, _extensions);
return true;
}
return false;
}
/**
* 计算设备的实际巡检周期(考虑三级继承)
* @param {string} hostId
* @returns {number} 周期(分钟)
*/
function getEffectiveInterval(readFn, hostId) {
_ensureLoaded(readFn);
const ext = _extensions[hostId];
if (!ext) return _sysConfig.inspection_default_interval;
// 1. 设备独立设置
if (typeof ext.inspection_interval === "number" && ext.inspection_interval > 0) {
return ext.inspection_interval;
}
// 2. 设备层级默认
if (ext.tier && DEVICE_TIER_DEFAULT_INTERVAL[ext.tier]) {
return DEVICE_TIER_DEFAULT_INTERVAL[ext.tier];
}
// 3. 系统默认
return _sysConfig.inspection_default_interval;
}
/**
* 获取所有启用了巡检的设备列表(带有效周期)
*/
function getEnabledDevices(readFn) {
_ensureLoaded(readFn);
const result = [];
for (const hostId of Object.keys(_extensions)) {
const ext = _extensions[hostId];
if (ext.inspection_enabled) {
result.push({
...ext,
effective_interval: getEffectiveInterval(readFn, hostId),
});
}
}
return result;
}
/**
* 更新上次巡检信息
*/
function updateLastScan(readFn, writeFn, hostId, status, error = null) {
_ensureLoaded(readFn);
if (!_extensions[hostId]) return false;
_extensions[hostId].last_scan_at = Date.now();
_extensions[hostId].last_scan_status = status;
_extensions[hostId].last_scan_error = error;
_save(writeFn, STORAGE_KEY_DEVICE_EXTENSION, _extensions);
return true;
}
// ---------- 设备类型 ----------
/**
* 获取设备类型列表
*/
function getCatalog() {
if (_catalog === null) {
_catalog = getPresetCatalog();
}
return [..._catalog];
}
/**
* 根据 ID 获取设备类型
*/
function getCatalogById(catalogId) {
const list = getCatalog();
return list.find((c) => c.id === catalogId) || null;
}
// ---------- 设备层级 ----------
/**
* 获取设备层级列表(带中文名称和默认周期)
*/
function getTiers() {
return [
{ key: DEVICE_TIERS.CORE, label: DEVICE_TIER_LABELS.core, default_interval: DEVICE_TIER_DEFAULT_INTERVAL.core },
{ key: DEVICE_TIERS.AGGREGATION, label: DEVICE_TIER_LABELS.aggregation, default_interval: DEVICE_TIER_DEFAULT_INTERVAL.aggregation },
{ key: DEVICE_TIERS.ACCESS, label: DEVICE_TIER_LABELS.access, default_interval: DEVICE_TIER_DEFAULT_INTERVAL.access },
];
}
// ---------- 重置(测试用) ----------
function _resetForTest() {
_extensions = null;
_sysConfig = null;
_catalog = null;
}
module.exports = {
// 系统配置
getSysConfig,
updateSysConfig,
// 设备扩展
getAllExtensions,
getExtension,
upsertExtension,
deleteExtension,
getEffectiveInterval,
getEnabledDevices,
updateLastScan,
// 设备类型
getCatalog,
getCatalogById,
// 设备层级
getTiers,
// 内部
_resetForTest,
};