/** * ReportWorker — handles 'report:generate' jobs. * Generates HTML, JSON, or PDF reports in the background. */ import { JobHandler } from '../JobQueue'; import { Logger } from '../../shared/infrastructure/Logger'; import { IReportRepository } from '../../modules/reporting/domain/ports/IReportRepository'; import { IFindingRepository } from '../../modules/findings/domain/ports/IFindingRepository'; import { HTMLReportGenerator } from '../../modules/reporting/infrastructure/generators/HTMLReportGenerator'; import { JSONReportGenerator } from '../../modules/reporting/infrastructure/generators/JSONReportGenerator'; import { PDFReportGenerator } from '../../modules/reporting/infrastructure/generators/PDFReportGenerator'; export const REPORT_JOB_TYPE = 'report:generate'; export interface ReportJobPayload { reportId: string; format: 'html' | 'pdf' | 'json'; filters?: { sessionId?: string; severity?: string; fromDate?: string; toDate?: string; }; } export interface ReportJobResult { reportId: string; filePath: string; } export function createReportJobHandler(deps: { logger: Logger; reportRepository: IReportRepository; findingRepository: IFindingRepository; }): JobHandler { const htmlGen = new HTMLReportGenerator(); const jsonGen = new JSONReportGenerator(); const pdfGen = new PDFReportGenerator(); return async (payload: ReportJobPayload): Promise => { const log = deps.logger.child({ jobType: REPORT_JOB_TYPE, reportId: payload.reportId }); log.info({ format: payload.format }, 'Report generation job executing'); const report = await deps.reportRepository.findById(payload.reportId); if (!report) { throw new Error(`Report not found: ${payload.reportId}`); } report.markGenerating(); await deps.reportRepository.update(report); // Load findings with filters from report const findings = await deps.findingRepository.findAll({ sessionId: report.filters.sessionId, severity: report.filters.severity, }); let filePath: string; try { if (payload.format === 'pdf') { filePath = await pdfGen.generate(report, findings); } else if (payload.format === 'json') { filePath = await jsonGen.generate(report, findings); } else { filePath = await htmlGen.generate(report, findings); } } catch (err) { const msg = err instanceof Error ? err.message : String(err); report.markFailed(msg); await deps.reportRepository.update(report); throw err; } report.markReady(filePath, findings.length); await deps.reportRepository.update(report); log.info({ filePath, totalFindings: findings.length }, 'Report job complete'); return { reportId: payload.reportId, filePath }; }; }