'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} perms module -> 位掩码 * @returns {Promise} */ 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 };