#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const repoRoot = path.resolve(__dirname, '..'); const srcRoot = path.join(repoRoot, 'wwjcloud', 'src'); function isTypescriptFile(filePath) { return filePath.endsWith('.ts') && !filePath.endsWith('.d.ts') && !filePath.endsWith('.spec.ts'); } function walk(dir, collected = []) { const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { walk(fullPath, collected); } else if (entry.isFile() && isTypescriptFile(fullPath)) { collected.push(fullPath); } } return collected; } function isAdminApiControllerFile(filePath) { return filePath.includes(path.join('controllers', 'adminapi') + path.sep); } function extractControllerInfo(fileContent) { const controllerMatch = fileContent.match(/@Controller\(([^)]*)\)/); const basePathLiteral = controllerMatch ? controllerMatch[1] : ''; let basePath = ''; if (basePathLiteral) { const strMatch = basePathLiteral.match(/['"`]([^'"`]*)['"`]/); basePath = strMatch ? strMatch[1] : ''; } const classDeclIdx = fileContent.indexOf('export class'); const header = classDeclIdx > -1 ? fileContent.slice(0, classDeclIdx) : fileContent; const guardsSection = header; const hasUseGuards = /@UseGuards\(([^)]*)\)/.test(guardsSection); let guards = []; if (hasUseGuards) { const m = guardsSection.match(/@UseGuards\(([^)]*)\)/); if (m) { guards = m[1].split(',').map(s => s.trim()); } } const hasJwt = guards.some(g => /JwtAuthGuard/.test(g)); const hasRoles = guards.some(g => /RolesGuard/.test(g)); return { basePath, hasJwt, hasRoles }; } function main() { if (!fs.existsSync(srcRoot)) { console.error(`src root not found: ${srcRoot}`); process.exit(1); } const allTsFiles = walk(srcRoot); const adminControllers = allTsFiles.filter(isAdminApiControllerFile); const problems = []; for (const filePath of adminControllers) { const content = fs.readFileSync(filePath, 'utf8'); if (!/@Controller\(/.test(content)) continue; const info = extractControllerInfo(content); const rel = path.relative(repoRoot, filePath); const missing = []; if (!info.hasJwt) missing.push('JwtAuthGuard'); if (!info.hasRoles) missing.push('RolesGuard'); if (missing.length > 0) { problems.push({ file: rel, basePath: info.basePath || '', missing }); } } if (problems.length === 0) { console.log('OK: All adminapi controllers have class-level JwtAuthGuard and RolesGuard.'); return; } console.log('file,basePath,missingGuards'); for (const p of problems) { console.log(`${p.file},${p.basePath},${p.missing.join('|')}`); } } if (require.main === module) { try { main(); } catch (err) { console.error('scan-guards failed:', err); process.exit(1); } }