fase(5): findings module complete
Some checks failed
ABE Exploratory Testing / explore (push) Has been cancelled
Some checks failed
ABE Exploratory Testing / explore (push) Has been cancelled
This commit is contained in:
131
dist/modules/findings/infrastructure/http/FindingsController.js
vendored
Normal file
131
dist/modules/findings/infrastructure/http/FindingsController.js
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createFindingsRouter = createFindingsRouter;
|
||||
const express_1 = require("express");
|
||||
const MarkdownExporter_1 = require("../exporters/MarkdownExporter");
|
||||
const JSONExporter_1 = require("../exporters/JSONExporter");
|
||||
const PlaywrightScriptExporter_1 = require("../exporters/PlaywrightScriptExporter");
|
||||
const path_1 = __importDefault(require("path"));
|
||||
function createFindingsRouter(deps) {
|
||||
const router = (0, express_1.Router)();
|
||||
const markdownExporter = new MarkdownExporter_1.MarkdownExporter();
|
||||
const jsonExporter = new JSONExporter_1.JSONExporter();
|
||||
const playwrightExporter = new PlaywrightScriptExporter_1.PlaywrightScriptExporter();
|
||||
// GET /api/findings — list findings with filters
|
||||
router.get('/', async (req, res) => {
|
||||
const { sessionId, severity, type, status, search } = req.query;
|
||||
const result = await deps.listFindings.execute({ sessionId, severity, type, status, search });
|
||||
if (!result.ok) {
|
||||
res.status(500).json({ error: result.error });
|
||||
return;
|
||||
}
|
||||
const { findings, total } = result.value;
|
||||
res.json({
|
||||
findings: findings.map(f => toDTO(f)),
|
||||
total,
|
||||
});
|
||||
});
|
||||
// GET /api/findings/stats — aggregate stats
|
||||
router.get('/stats', async (req, res) => {
|
||||
const { sessionId } = req.query;
|
||||
const result = await deps.findingStats.execute({ sessionId });
|
||||
if (!result.ok) {
|
||||
res.status(500).json({ error: result.error });
|
||||
return;
|
||||
}
|
||||
res.json(result.value);
|
||||
});
|
||||
// GET /api/findings/:id — finding detail
|
||||
router.get('/:id', async (req, res) => {
|
||||
const findingId = req.params['id'];
|
||||
const result = await deps.getFinding.execute({ findingId });
|
||||
if (!result.ok) {
|
||||
res.status(404).json({ error: result.error });
|
||||
return;
|
||||
}
|
||||
res.json(toDTO(result.value));
|
||||
});
|
||||
// PATCH /api/findings/:id/status — update status
|
||||
router.patch('/:id/status', async (req, res) => {
|
||||
const findingId = req.params['id'];
|
||||
const { action } = req.body;
|
||||
if (!action || !['resolve', 'close', 'investigate'].includes(action)) {
|
||||
res.status(400).json({ error: 'action must be one of: resolve, close, investigate' });
|
||||
return;
|
||||
}
|
||||
const result = await deps.resolveFinding.execute({ findingId, action });
|
||||
if (!result.ok) {
|
||||
res.status(404).json({ error: result.error });
|
||||
return;
|
||||
}
|
||||
res.json(result.value);
|
||||
});
|
||||
// POST /api/findings/:id/enrich — trigger AI enrichment
|
||||
router.post('/:id/enrich', async (req, res) => {
|
||||
const findingId = req.params['id'];
|
||||
const result = await deps.enrichFinding.execute({ findingId });
|
||||
if (!result.ok) {
|
||||
res.status(422).json({ error: result.error });
|
||||
return;
|
||||
}
|
||||
res.json(result.value);
|
||||
});
|
||||
// GET /api/findings/:id/export/markdown — download as Markdown
|
||||
router.get('/:id/export/markdown', async (req, res) => {
|
||||
const findingId = req.params['id'];
|
||||
const result = await deps.getFinding.execute({ findingId });
|
||||
if (!result.ok) {
|
||||
res.status(404).json({ error: result.error });
|
||||
return;
|
||||
}
|
||||
const outputDir = path_1.default.join(process.cwd(), 'reports', findingId);
|
||||
const filePath = await markdownExporter.export(result.value, outputDir);
|
||||
res.download(filePath, `finding-${findingId}.md`);
|
||||
});
|
||||
// GET /api/findings/:id/export/json — download as JSON
|
||||
router.get('/:id/export/json', async (req, res) => {
|
||||
const findingId = req.params['id'];
|
||||
const result = await deps.getFinding.execute({ findingId });
|
||||
if (!result.ok) {
|
||||
res.status(404).json({ error: result.error });
|
||||
return;
|
||||
}
|
||||
const outputDir = path_1.default.join(process.cwd(), 'reports', findingId);
|
||||
const filePath = await jsonExporter.export(result.value, outputDir);
|
||||
res.download(filePath, `finding-${findingId}.json`);
|
||||
});
|
||||
// GET /api/findings/:id/export/playwright — download Playwright script
|
||||
router.get('/:id/export/playwright', async (req, res) => {
|
||||
const findingId = req.params['id'];
|
||||
const result = await deps.getFinding.execute({ findingId });
|
||||
if (!result.ok) {
|
||||
res.status(404).json({ error: result.error });
|
||||
return;
|
||||
}
|
||||
const script = playwrightExporter.generate(result.value);
|
||||
res.setHeader('Content-Type', 'text/javascript');
|
||||
res.setHeader('Content-Disposition', `attachment; filename="finding-${findingId}.spec.js"`);
|
||||
res.send(script);
|
||||
});
|
||||
return router;
|
||||
}
|
||||
function toDTO(f) {
|
||||
return {
|
||||
id: f.id.toString(),
|
||||
sessionId: f.sessionId,
|
||||
type: f.type.value,
|
||||
severity: f.severity.value,
|
||||
description: f.description,
|
||||
status: f.status.value,
|
||||
browser: f.browser,
|
||||
browserVersion: f.browserVersion,
|
||||
actionTraceLength: f.actionTrace.length,
|
||||
evidence: f.evidence.toJSON(),
|
||||
aiEnrichment: f.aiEnrichment ?? null,
|
||||
createdAt: f.createdAt.toISOString(),
|
||||
resolvedAt: f.resolvedAt?.toISOString() ?? null,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user