云
2 天以前 657698fd2fb32794932474648dbaee9e284e38c8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const fs = require('node:fs');
const path = require('node:path');
const menus = require('./permission-menu-snapshot.json');
const endpoints = require('./permission-endpoints.json');
const report = require('./permission-audit-report.json');
function walk(p) {
  if (!fs.existsSync(p)) return [];
  if (fs.statSync(p).isFile()) return [p];
  return fs.readdirSync(p, { withFileTypes: true }).flatMap((e) => e.isDirectory() ? walk(path.join(p, e.name)) : [path.join(p, e.name)]);
}
const checks = [];
for (const menu of menus.filter((v) => v.type === 2 && !v.deleted && !v.status && v.component)) {
  const dir = 'mom-pro2-before/src/views/' + menu.component.replace(/\/index(?:\.vue)?$/, '').replace(/\.vue$/, '');
  for (const file of walk(dir).filter((f) => /\.(vue|ts)$/.test(f))) {
    const s = fs.readFileSync(file, 'utf8');
    for (const imp of s.matchAll(/import\s+(?!type\b)\{([^]*?)\}\s+from\s+['"](#\/api\/[^'"]+)['"]/g)) {
      let api = imp[2].replace('#/', 'mom-pro2-before/src/');
      api = fs.existsSync(api + '.ts') ? api + '.ts' : api + '/index.ts';
      if (!fs.existsSync(api)) continue;
      const blocks = [...fs.readFileSync(api, 'utf8').matchAll(/export\s+(?:async\s+)?function\s+(\w+)\b([^]*?)(?=\nexport|$)/g)];
      for (const name of imp[1].split(',').map((x) => x.trim().split(/\s+as\s+/)[0]).filter((x) => /^\w+$/.test(x))) {
        const block = blocks.find((x) => x[1] === name);
        if (!block) continue;
        const route = block[2].match(/requestClient\.(get|post|put|delete|patch|download|upload)[^]*?(['"`])(\/[^'"`]+)\2/);
        if (!route) continue;
        const method = route[1] === 'download' ? 'GET' : route[1] === 'upload' ? 'POST' : route[1].toUpperCase();
        const url = route[3].split('?')[0];
        const endpoint = endpoints.find((e) => e.method === method && e.url === url);
        checks.push({ menu: menu.id, page: menu.name, source: file, api, function: name, method, url, permissions: endpoint?.permissions ?? null });
      }
    }
  }
}
const unique = [...new Map(checks.map((c) => [c.menu + ':' + c.function + ':' + c.url, c])).values()];
fs.writeFileSync('scripts/permission-api-audit.json', JSON.stringify(unique, null, 2));
const missing = unique.filter((c) => c.permissions?.some((p) => report.backendWithoutMenu.some((x) => x.key === p)));
console.log('Active API calls:', unique.length);
console.log('Missing menu permissions:', [...new Set(missing.flatMap((c) => c.permissions))]);
console.log('Unmatched endpoints:', unique.filter((c) => c.permissions === null).map((c) => ({ page: c.page, function: c.function, url: c.url })));