132 lines
5.4 KiB
JavaScript
132 lines
5.4 KiB
JavaScript
"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,
|
|
};
|
|
}
|