fase(7): api server refactor with composition root

This commit is contained in:
debian
2026-03-05 09:36:28 -05:00
parent e746dc0497
commit f01acfe985
20 changed files with 861 additions and 2 deletions

140
dist/main.js vendored Normal file
View File

@@ -0,0 +1,140 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/**
* ABE — composition root.
* Wires all modules together and starts the HTTP + WebSocket server.
*/
const http_1 = __importDefault(require("http"));
const socket_io_1 = require("socket.io");
const Config_1 = require("./shared/infrastructure/Config");
const Logger_1 = require("./shared/infrastructure/Logger");
const DatabaseConnection_1 = require("./shared/infrastructure/DatabaseConnection");
const InProcessEventBus_1 = require("./shared/infrastructure/InProcessEventBus");
const migrator_1 = require("./db/migrator");
// Crawling module
const KyselyCrawlSessionRepository_1 = require("./modules/crawling/infrastructure/repositories/KyselyCrawlSessionRepository");
const KyselyStateRepository_1 = require("./modules/crawling/infrastructure/repositories/KyselyStateRepository");
const StartCrawlCommand_1 = require("./modules/crawling/application/commands/StartCrawlCommand");
const StopCrawlCommand_1 = require("./modules/crawling/application/commands/StopCrawlCommand");
const GetSessionQuery_1 = require("./modules/crawling/application/queries/GetSessionQuery");
const ListSessionsQuery_1 = require("./modules/crawling/application/queries/ListSessionsQuery");
// Findings module
const KyselyFindingRepository_1 = require("./modules/findings/infrastructure/repositories/KyselyFindingRepository");
const CreateFindingCommand_1 = require("./modules/findings/application/commands/CreateFindingCommand");
const EnrichFindingCommand_1 = require("./modules/findings/application/commands/EnrichFindingCommand");
const ResolveFindingCommand_1 = require("./modules/findings/application/commands/ResolveFindingCommand");
const GetFindingQuery_1 = require("./modules/findings/application/queries/GetFindingQuery");
const ListFindingsQuery_1 = require("./modules/findings/application/queries/ListFindingsQuery");
const FindingStatsQuery_1 = require("./modules/findings/application/queries/FindingStatsQuery");
const OnAnomalyDetected_1 = require("./modules/findings/application/event-handlers/OnAnomalyDetected");
const NullAIEnricher_1 = require("./modules/findings/infrastructure/NullAIEnricher");
// Fuzzing module
const FuzzingEngineAdapter_1 = require("./modules/fuzzing/infrastructure/adapters/FuzzingEngineAdapter");
const RunFuzzCommand_1 = require("./modules/fuzzing/application/commands/RunFuzzCommand");
const OnActionExecuted_1 = require("./modules/fuzzing/application/event-handlers/OnActionExecuted");
const InMemoryFuzzSessionRepository_1 = require("./modules/fuzzing/infrastructure/repositories/InMemoryFuzzSessionRepository");
// API + Realtime
const server_1 = require("./api/server");
const SocketGateway_1 = require("./realtime/SocketGateway");
async function bootstrap() {
// 1. Config
const config = (0, Config_1.loadConfig)();
// 2. Logger
const logger = (0, Logger_1.createLogger)({ level: config.log.level, nodeEnv: config.nodeEnv });
logger.info({ port: config.port, env: config.nodeEnv }, 'Starting ABE...');
// 3. Database + migrations
const db = (0, DatabaseConnection_1.createDatabase)(config.db);
await (0, migrator_1.runMigrations)(db);
logger.info('Database migrations applied');
// 4. Event bus
const eventBus = new InProcessEventBus_1.InProcessEventBus(logger);
// 5. Repositories
const sessionRepo = new KyselyCrawlSessionRepository_1.KyselyCrawlSessionRepository(db);
const stateRepo = new KyselyStateRepository_1.KyselyStateRepository(db);
const findingRepo = new KyselyFindingRepository_1.KyselyFindingRepository(db);
const fuzzRepo = new InMemoryFuzzSessionRepository_1.InMemoryFuzzSessionRepository();
// Suppress unused warning for stateRepo — used by crawling infrastructure
void stateRepo;
// 6. Crawling use cases
const startCrawl = new StartCrawlCommand_1.StartCrawlCommand(sessionRepo, eventBus);
const stopCrawl = new StopCrawlCommand_1.StopCrawlCommand(sessionRepo, eventBus);
const getSession = new GetSessionQuery_1.GetSessionQuery(sessionRepo);
const listSessions = new ListSessionsQuery_1.ListSessionsQuery(sessionRepo);
// 7. Findings use cases
const createFinding = new CreateFindingCommand_1.CreateFindingCommand(findingRepo, eventBus);
const enricher = new NullAIEnricher_1.NullAIEnricher();
const enrichFinding = new EnrichFindingCommand_1.EnrichFindingCommand(findingRepo, enricher, eventBus);
const resolveFinding = new ResolveFindingCommand_1.ResolveFindingCommand(findingRepo, eventBus);
const getFinding = new GetFindingQuery_1.GetFindingQuery(findingRepo);
const listFindings = new ListFindingsQuery_1.ListFindingsQuery(findingRepo);
const findingStats = new FindingStatsQuery_1.FindingStatsQuery(findingRepo);
// 8. Fuzzing use cases
const fuzzerEngine = new FuzzingEngineAdapter_1.FuzzingEngineAdapter({ intensity: 'low', seed: 42 });
const runFuzz = new RunFuzzCommand_1.RunFuzzCommand(fuzzerEngine, fuzzRepo, eventBus);
// 9. Event handlers — subscribe to EventBus
const onAnomalyDetected = new OnAnomalyDetected_1.OnAnomalyDetected(createFinding);
eventBus.subscribe('crawling.anomaly_detected', onAnomalyDetected);
const onActionExecuted = new OnActionExecuted_1.OnActionExecuted(runFuzz);
eventBus.subscribe('crawling.action_executed', onActionExecuted);
// 10. HTTP server
const app = (0, server_1.createServer)({
config,
logger,
db,
crawlingDeps: { startCrawl, stopCrawl, getSession, listSessions },
findingsDeps: { getFinding, listFindings, findingStats, resolveFinding, enrichFinding },
fuzzingDeps: { runFuzz, repository: fuzzRepo },
});
const httpServer = http_1.default.createServer(app);
// 11. Socket.io + gateway
const io = new socket_io_1.Server(httpServer, {
cors: { origin: config.cors.origin, credentials: true },
});
const gateway = new SocketGateway_1.SocketGateway(io, eventBus, logger);
gateway.start();
// 12. Start listening
await new Promise((resolve) => {
httpServer.listen(config.port, config.host, resolve);
});
logger.info({ port: config.port, host: config.host }, 'ABE server ready');
// 13. Graceful shutdown
let shuttingDown = false;
async function shutdown(signal) {
if (shuttingDown)
return;
shuttingDown = true;
logger.info({ signal }, 'Shutting down...');
// Stop accepting new connections
httpServer.close();
// Close socket.io
io.close();
// Close database
try {
await db.destroy();
}
catch (err) {
logger.warn({ err }, 'Error closing database');
}
logger.info('Shutdown complete');
process.exit(0);
}
// Force-exit if graceful shutdown takes too long
function forceExit(signal) {
void shutdown(signal).catch(() => {
process.exit(1);
});
setTimeout(() => {
logger.error('Forced shutdown after 30s');
process.exit(1);
}, 30000).unref();
}
process.on('SIGTERM', () => forceExit('SIGTERM'));
process.on('SIGINT', () => forceExit('SIGINT'));
}
bootstrap().catch((err) => {
console.error('Fatal: failed to start ABE', err);
process.exit(1);
});