_audit-apig-mapping.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /**
  2. * 审计所有 api-config.json 的 endpoint 路径 vs 当前配置的 apigId
  3. * 规则:
  4. * /api/voc-social/* → apigId=Vo3ROWEvDy
  5. * /api/voc-ecom/forward/* → apigId=7HwdQZk55B
  6. * /api/volcengine/jimeng/* → apigId=? (暂标未知)
  7. * 其他 → 打印出来让人工判断
  8. */
  9. const fs = require('fs');
  10. const path = require('path');
  11. const ROOT = path.resolve(__dirname, '..', '..');
  12. const EXPECTED = {
  13. '/api/voc-social/': 'Vo3ROWEvDy',
  14. '/api/voc-ecom/': '7HwdQZk55B',
  15. };
  16. function walk(d, r = []) {
  17. for (const e of fs.readdirSync(d, { withFileTypes: true })) {
  18. if (e.name === 'node_modules' || e.name === '.git' || e.name === 'dist') continue;
  19. const p = path.join(d, e.name);
  20. if (e.isDirectory()) walk(p, r);
  21. else if (e.name === 'api-config.json') r.push(p);
  22. }
  23. return r;
  24. }
  25. const files = walk(ROOT);
  26. const results = { match: [], mismatch: [], orchestration: [], unknown: [] };
  27. for (const f of files) {
  28. let cfg;
  29. try { cfg = JSON.parse(fs.readFileSync(f, 'utf-8')); } catch { continue; }
  30. const curApigId = cfg.tokenConfig?.apigId || null;
  31. const rel = path.relative(ROOT, f).replace(/\\/g, '/');
  32. // 直接 endpoint
  33. const url = cfg.endpoint?.url;
  34. if (url) {
  35. const u = new URL(url);
  36. const pathOnly = u.pathname;
  37. let expected = null;
  38. for (const [prefix, apig] of Object.entries(EXPECTED)) {
  39. if (pathOnly.startsWith(prefix)) { expected = apig; break; }
  40. }
  41. if (expected) {
  42. if (curApigId === expected) {
  43. results.match.push({ file: rel, pathOnly, apigId: curApigId });
  44. } else {
  45. results.mismatch.push({ file: rel, pathOnly, current: curApigId, expected });
  46. }
  47. } else {
  48. results.unknown.push({ file: rel, pathOnly, current: curApigId });
  49. }
  50. continue;
  51. }
  52. // orchestration 型:检查 pipeline steps 的 endpoint
  53. const steps = cfg.pipeline || [];
  54. const stepUrls = [];
  55. function collectUrls(node) {
  56. if (!node || typeof node !== 'object') return;
  57. if (Array.isArray(node)) return node.forEach(collectUrls);
  58. if (node.endpoint && typeof node.endpoint === 'string') stepUrls.push(node.endpoint);
  59. for (const v of Object.values(node)) collectUrls(v);
  60. }
  61. collectUrls(steps);
  62. if (stepUrls.length > 0) {
  63. const pathsFound = stepUrls.map(u => { try { return new URL(u).pathname; } catch { return u; } });
  64. const distinct = [...new Set(pathsFound.map(p => {
  65. for (const prefix of Object.keys(EXPECTED)) if (p.startsWith(prefix)) return prefix;
  66. return p;
  67. }))];
  68. results.orchestration.push({ file: rel, current: curApigId, distinctPathPrefixes: distinct });
  69. }
  70. }
  71. console.log('\n═════════════════════════ 审计结果 ═════════════════════════\n');
  72. console.log(`✅ apigId 正确 (${results.match.length})`);
  73. const byApig = {};
  74. results.match.forEach(m => { byApig[m.apigId] = (byApig[m.apigId] || 0) + 1; });
  75. console.log(' 分布:', JSON.stringify(byApig));
  76. console.log(`\n❌ apigId 错误 (${results.mismatch.length})`);
  77. results.mismatch.forEach(m => {
  78. console.log(` ${m.file}`);
  79. console.log(` 当前=${m.current} 应该=${m.expected} path=${m.pathOnly}`);
  80. });
  81. console.log(`\n⚠️ orchestration (pipeline) 类 (${results.orchestration.length})`);
  82. results.orchestration.forEach(o => {
  83. console.log(` ${o.file} current=${o.current} paths=${JSON.stringify(o.distinctPathPrefixes)}`);
  84. });
  85. console.log(`\n❓ 未知路径 (${results.unknown.length})`);
  86. results.unknown.forEach(u => {
  87. console.log(` ${u.file} current=${u.current} path=${u.pathOnly}`);
  88. });