const fs = require('node:fs');
|
const path = require('node:path');
|
const root = path.resolve(__dirname, '..');
|
function walk(dir) {
|
if (!fs.existsSync(dir)) return [];
|
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => entry.isDirectory() ? walk(path.join(dir, entry.name)) : [path.join(dir, entry.name)]);
|
}
|
const permissionPattern = /['"]((?:system|infra|bpm|crm|erp|hrm|mdm|mes|srm|im|ai|bi|aftersales|member|report|wms)(?::[a-z][a-z0-9-]*){1,5})['"]/g;
|
const backend = new Map();
|
const frontend = new Map();
|
function add(map, permission, file) {
|
if (!map.has(permission)) map.set(permission, []);
|
map.get(permission).push(path.relative(root, file).replaceAll('\\', '/'));
|
}
|
for (const module of fs.readdirSync(root).filter((name) => name.startsWith('yudao-module-'))) {
|
for (const file of walk(path.join(root, module, 'src/main/java')).filter((name) => name.endsWith('Controller.java'))) {
|
const source = fs.readFileSync(file, 'utf8').replace(/\/\*[\s\S]*?\*\//g, '').replace(/^\s*\/\/.*$/gm, '');
|
for (const annotation of source.matchAll(/@PreAuthorize\(\s*"([^"]+)"\s*\)/g)) {
|
for (const match of annotation[1].matchAll(permissionPattern)) add(backend, match[1], file);
|
}
|
}
|
}
|
for (const file of walk(path.join(root, 'mom-pro2-before/src/views')).filter((name) => /\.(vue|ts)$/.test(name))) {
|
const source = fs.readFileSync(file, 'utf8').replace(/<!--[^]*?-->/g, '').replace(/\/\*[^]*?\*\//g, '');
|
for (const match of source.matchAll(permissionPattern)) add(frontend, match[1], file);
|
}
|
const menus = JSON.parse(fs.readFileSync(path.join(__dirname, 'permission-menu-snapshot.json'), 'utf8'));
|
const active = menus.filter((row) => !row.deleted && row.status === 0);
|
const db = new Set(active.map((row) => row.permission).filter(Boolean));
|
const report = {
|
counts: { backend: backend.size, frontend: frontend.size, database: db.size },
|
frontendWithoutBackend: [...frontend].filter(([key]) => !backend.has(key)).map(([key, files]) => ({ key, files, menus: active.filter((m) => m.permission === key).map((m) => ({ id: m.id, name: m.name, parent_id: m.parent_id })) })),
|
frontendWithoutMenu: [...frontend].filter(([key]) => !db.has(key)).map(([key, files]) => ({ key, files })),
|
backendWithoutMenu: [...backend].filter(([key]) => !db.has(key)).map(([key, files]) => ({ key, files: [...new Set(files)], usedInFrontend: frontend.has(key) })),
|
menuWithoutBackend: active.filter((m) => m.permission && !backend.has(m.permission)),
|
duplicatePermissions: [...db].map((key) => ({ key, menus: active.filter((m) => m.permission === key).map((m) => m.id) })).filter((m) => m.menus.length > 1),
|
};
|
fs.writeFileSync(path.join(__dirname, 'permission-audit-report.json'), JSON.stringify(report, null, 2));
|
console.log(JSON.stringify({ counts: report.counts, frontendWithoutBackend: report.frontendWithoutBackend.map((x) => x.key), frontendWithoutMenu: report.frontendWithoutMenu.map((x) => x.key), backendWithoutMenuUsedInFrontend: report.backendWithoutMenu.filter((x) => x.usedInFrontend).map((x) => x.key), menuWithoutBackend: report.menuWithoutBackend.map((x) => ({ id: x.id, permission: x.permission, parent: x.parent_id })), duplicatePermissions: report.duplicatePermissions }, null, 2));
|