"""Routes für Audit-Walk-Recorder (POST /scan-audit-walk + Video-Serve).""" from __future__ import annotations import os from pathlib import Path from fastapi import APIRouter, HTTPException from fastapi.responses import FileResponse from pydantic import BaseModel from services.audit_walk_recorder import WALK_ROOT, record_audit_walk from services.finding_annotator import annotate_findings router = APIRouter() class AuditWalkReq(BaseModel): url: str dwell_s: float = 5.0 max_links: int = 8 class AnnotateReq(BaseModel): findings: list[dict] home_url: str @router.post("/scan-audit-walk") async def scan_audit_walk(req: AuditWalkReq) -> dict: if not req.url or not req.url.startswith(("http://", "https://")): raise HTTPException(400, "invalid url") walk = await record_audit_walk( req.url, dwell_s=max(1.0, min(req.dwell_s, 10.0)), max_links=max(1, min(req.max_links, 12)), ) return walk @router.get("/audit-walks/{walk_id}/video.webm") async def serve_walk_video(walk_id: str): # Basic path-traversal guard if not walk_id.isalnum() or len(walk_id) > 32: raise HTTPException(400, "invalid walk_id") path = Path(WALK_ROOT) / walk_id / "video.webm" if not path.exists(): raise HTTPException(404, "walk video not found") return FileResponse(str(path), media_type="video/webm") @router.get("/audit-walks/{walk_id}/walk.json") async def serve_walk_meta(walk_id: str): if not walk_id.isalnum() or len(walk_id) > 32: raise HTTPException(400, "invalid walk_id") path = Path(WALK_ROOT) / walk_id / "walk.json" if not path.exists(): raise HTTPException(404, "walk.json not found") return FileResponse(str(path), media_type="application/json") @router.post("/annotate-findings") async def annotate_findings_route(req: AnnotateReq) -> dict: """Produce annotated screenshots per finding.""" if not req.home_url.startswith(("http://", "https://")): raise HTTPException(400, "invalid home_url") out = await annotate_findings(req.findings, req.home_url) return {"annotations": out, "count": len(out)}