Agent/server/src/modules/modules.js

62 lines
2.2 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';
const { MODULE_REGISTRY, validateRegistry } = require('@profile/shared');
const db = require('../infra/db');
const audit = require('../audit/audit');
/**
* 启动一致性校验migration-plan §5.4)。
* 在 app.listen 前调用一次。
*/
async function syncModulesOnBoot() {
const errors = validateRegistry();
if (errors.length > 0) {
throw new Error(`MODULE_REGISTRY invalid:\n${errors.join('\n')}`);
}
const dbRows = await db.query('SELECT `key`, status FROM modules');
const dbKeys = new Set(dbRows.map((r) => r.key));
const codeKeys = new Set(MODULE_REGISTRY.map((m) => m.key));
// code 有但 DB 无 → 自动 INSERT
for (const m of MODULE_REGISTRY) {
if (!dbKeys.has(m.key)) {
await db.execute('INSERT INTO modules (`key`, status, notes) VALUES (?, ?, ?)', [m.key, 'active', m.label]);
console.log(`[modules] auto-registered: ${m.key}`);
await audit.log({ clientType: 'system', action: 'module_auto_register', module: m.key });
}
}
// DB 有但 code 无 → orphan 告警 + 审计,不阻塞
const orphans = dbRows.filter((r) => !codeKeys.has(r.key)).map((r) => r.key);
if (orphans.length > 0) {
console.warn(`[modules] orphan in DB: ${orphans.join(',')}`);
await audit.log({ clientType: 'system', action: 'module_orphan_detected', payload: { orphans } });
}
}
/**
* 合并 code descriptor + DB status/override过滤 disabled 与无 READ 权限的模块。
* @param {Record<string, number>} perms module -> 位掩码
* @returns {Promise<Object[]>}
*/
async function getVisible(perms) {
const dbRows = await db.query('SELECT `key`, status, override FROM modules');
const statusMap = new Map(dbRows.map((r) => [r.key, r]));
return MODULE_REGISTRY.filter((m) => {
const row = statusMap.get(m.key);
if (row && row.status === 'disabled') return false;
return ((perms[m.rbacModule] || 0) & 1) === 1; // READ
}).map((m) => {
const row = statusMap.get(m.key);
let override = {};
if (row && row.override) {
override = typeof row.override === 'string' ? JSON.parse(row.override) : row.override;
}
return { ...m, ...override };
});
}
module.exports = { syncModulesOnBoot, getVisible };