fase(15): reporting module with pdf generation

This commit is contained in:
debian
2026-03-06 05:57:05 -05:00
parent 3ff36f0b6a
commit cffa1aeea9
64 changed files with 3462 additions and 87 deletions

View File

@@ -1,9 +1,14 @@
/**
* ReportWorker — handles 'report:generate' jobs.
* Generates reports in the background (full implementation in Phase 15).
* 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';
@@ -25,16 +30,51 @@ export interface ReportJobResult {
export function createReportJobHandler(deps: {
logger: Logger;
reportRepository: IReportRepository;
findingRepository: IFindingRepository;
}): JobHandler<ReportJobPayload, ReportJobResult> {
const htmlGen = new HTMLReportGenerator();
const jsonGen = new JSONReportGenerator();
const pdfGen = new PDFReportGenerator();
return async (payload: ReportJobPayload): Promise<ReportJobResult> => {
const log = deps.logger.child({ jobType: REPORT_JOB_TYPE, reportId: payload.reportId });
log.info({ format: payload.format }, 'Report generation job executing');
// Full implementation in Phase 15 (Reporting Module)
// For now, return a placeholder result
const filePath = `./reports/${payload.reportId}.${payload.format}`;
log.info({ filePath }, 'Report job complete');
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 };
};
}