aps-agent/scripts/check-cross-database.mjs

98 lines
3.3 KiB
JavaScript
Raw Permalink 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.

#!/usr/bin/env node
/**
* 跨库访问检查(任务 A1.6,依据 ADR-0008)。
*
* 平台代码禁止直连 APS 库:库各自私有,平台只能经 Provider 契约访问业务系统。
* 本脚本扫平台源码,出现下面任一项即失败:
* 1. APS 侧的表名(平台侧不该认识这些表)
* 2. 指向 APS 库的连接串或库名
*
* 用法:node scripts/check-cross-database.mjs [仓库根目录]
*/
import { existsSync } from 'node:fs';
import { readFile, readdir } from 'node:fs/promises';
import { join, relative, sep } from 'node:path';
import process from 'node:process';
import { pathToFileURL } from 'node:url';
/** 平台包目录:只有这些地方的代码属于平台 */
export const PLATFORM_DIRS = ['contracts', 'server', 'client', 'providers'];
/** APS 侧的表名(ADR-0008:APS 6 张表) */
export const APS_TABLES = [
'master_records',
'project_settings',
'routing_templates',
'routing_template_steps',
'aps_schedule_runs',
'aps_schedule_results',
];
/**
* 指向 APS 库的连接串特征。
* 注意边界:平台自己的环境变量名(APS_DB_PATH、APS_DATABASE_URL)不算越界,
* 只有真正指向业务库的写法(jms_mes、aps_db 这种库名)才拦。
*/
export const APS_DATABASE_HINTS = [/aps[_-]?db\b/, /\bjms_mes\b/];
const SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs', '.cjs']);
const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', 'coverage']);
async function collectSources(dir, acc = []) {
let entries;
try {
entries = await readdir(dir, { withFileTypes: true });
} catch {
return acc;
}
for (const entry of entries) {
const full = join(dir, entry.name);
if (entry.isDirectory()) {
if (SKIP_DIRS.has(entry.name)) continue;
await collectSources(full, acc);
} else if (SOURCE_EXTENSIONS.has(entry.name.slice(entry.name.lastIndexOf('.')))) {
acc.push(full);
}
}
return acc;
}
export async function findCrossDatabaseAccess(root) {
const violations = [];
for (const dir of PLATFORM_DIRS) {
const abs = join(root, dir);
if (!existsSync(abs)) continue;
for (const file of await collectSources(abs)) {
const rel = relative(root, file).split(sep).join('/');
const text = await readFile(file, 'utf8');
for (const table of APS_TABLES) {
if (new RegExp(`\\b${table}\\b`).test(text)) {
violations.push({ file: rel, reason: `出现 APS 表名 ${table}(平台不该认识业务系统的表)` });
}
}
for (const hint of APS_DATABASE_HINTS) {
if (hint.test(text)) {
violations.push({ file: rel, reason: `出现指向 APS 库的线索:${hint}` });
}
}
}
}
return violations;
}
async function main() {
const root = process.cwd();
const violations = await findCrossDatabaseAccess(root);
if (violations.length === 0) {
console.log('跨库访问检查通过:平台代码未出现 APS 表名或 APS 库连接');
return;
}
console.error(`跨库访问检查发现 ${violations.length} 处:`);
for (const violation of violations) console.error(` ${violation.file}:${violation.reason}`);
process.exitCode = 1;
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
await main();
}