feat(prospecting): add security audit report generation (Workstream 2B)

- SecurityReportService generates standalone branded HTML reports from
  stored audit data (grade badge, simulated hacked site, detailed
  findings, business impact, call-to-action with contact info)
- GET /security-audit/report/{prospect_id} returns HTMLResponse
- "Generate Report" button on prospect detail security tab opens
  report in new browser tab (printable to PDF)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-01 21:41:40 +02:00
parent 4c750f0268
commit 30f3dae5a3
4 changed files with 278 additions and 2 deletions

View File

@@ -10,6 +10,7 @@ catch "batch" as a string before trying to parse it as int → 422.
import logging
from fastapi import APIRouter, Depends, Path, Query
from fastapi.responses import HTMLResponse
from sqlalchemy.orm import Session
from app.api.deps import get_current_admin_api
@@ -34,6 +35,9 @@ from app.modules.prospecting.services.scoring_service import scoring_service
from app.modules.prospecting.services.security_audit_service import (
security_audit_service,
)
from app.modules.prospecting.services.security_report_service import (
security_report_service,
)
from app.modules.prospecting.services.stats_service import stats_service
from app.modules.tenancy.schemas.auth import UserContext
@@ -152,6 +156,28 @@ def compute_scores_batch(
return ScoreComputeBatchResponse(scored=count)
# ── Report endpoints ────────────────────────────────────────────────────────
@router.get("/security-audit/report/{prospect_id}", response_class=HTMLResponse)
def security_audit_report(
prospect_id: int = Path(...),
db: Session = Depends(get_db),
current_admin: UserContext = Depends(get_current_admin_api),
):
"""Generate branded HTML security audit report."""
prospect = prospect_service.get_by_id(db, prospect_id)
if not prospect.security_audit:
from app.exceptions.base import ResourceNotFoundException
raise ResourceNotFoundException("SecurityAudit", str(prospect_id))
html = security_report_service.generate_html_report(
audit=prospect.security_audit,
domain=prospect.domain_name,
)
return HTMLResponse(content=html)
# ── Single-prospect endpoints ───────────────────────────────────────────────