Initial commit: NetMesh smart ops platform with 3D topology, inspection reports, RCA and AI ops assistant
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
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
This commit is contained in:
290
electron/bridges/inspection/deviceStore.cjs
Normal file
290
electron/bridges/inspection/deviceStore.cjs
Normal file
@@ -0,0 +1,290 @@
|
||||
"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,
|
||||
};
|
||||
Reference in New Issue
Block a user