mirror of
https://github.com/mblanke/ThreatHunt.git
synced 2026-03-01 14:00:20 -05:00
- NetworkMap: hunt-scoped force-directed graph with click-to-inspect popover - NetworkMap: zoom/pan (wheel, drag, buttons), viewport transform - NetworkMap: clickable IP/Host/Domain/URL legend chips to filter node types - NetworkMap: brighter colors, 20% smaller nodes - DatasetViewer: IOC columns highlighted with colored headers + cell tinting - AUPScanner: hunt dropdown replacing dataset checkboxes, auto-select all - Rename 'Social Media (Personal)' theme to 'Social Media' with DB migration - Fix /api/hunts timeout: Dataset.rows lazy='noload' (was selectin cascade) - Add OS column mapping to normalizer - Full backend services, DB models, alembic migrations, new routes - New components: Dashboard, HuntManager, FileUpload, NetworkMap, etc. - Docker Compose deployment with nginx reverse proxy
68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
"""API routes for report generation and export."""
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from fastapi.responses import HTMLResponse, PlainTextResponse
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.db import get_db
|
|
from app.services.reports import report_generator
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api/reports", tags=["reports"])
|
|
|
|
|
|
@router.get(
|
|
"/hunt/{hunt_id}",
|
|
summary="Generate hunt investigation report",
|
|
description="Generate a comprehensive report for a hunt. Supports JSON, HTML, and CSV formats.",
|
|
)
|
|
async def generate_hunt_report(
|
|
hunt_id: str,
|
|
format: str = Query("json", description="Report format: json, html, csv"),
|
|
include_rows: bool = Query(False, description="Include raw data rows"),
|
|
max_rows: int = Query(500, ge=0, le=5000, description="Max rows to include"),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
result = await report_generator.generate_hunt_report(
|
|
hunt_id, db, format=format,
|
|
include_rows=include_rows, max_rows=max_rows,
|
|
)
|
|
|
|
if isinstance(result, dict) and result.get("error"):
|
|
raise HTTPException(status_code=404, detail=result["error"])
|
|
|
|
if format == "html":
|
|
return HTMLResponse(content=result, headers={
|
|
"Content-Disposition": f"inline; filename=threathunt_report_{hunt_id}.html",
|
|
})
|
|
elif format == "csv":
|
|
return PlainTextResponse(content=result, media_type="text/csv", headers={
|
|
"Content-Disposition": f"attachment; filename=threathunt_report_{hunt_id}.csv",
|
|
})
|
|
else:
|
|
return result
|
|
|
|
|
|
@router.get(
|
|
"/hunt/{hunt_id}/summary",
|
|
summary="Quick hunt summary",
|
|
description="Get a lightweight summary of the hunt for dashboard display.",
|
|
)
|
|
async def hunt_summary(
|
|
hunt_id: str,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
result = await report_generator.generate_hunt_report(
|
|
hunt_id, db, format="json", include_rows=False,
|
|
)
|
|
if isinstance(result, dict) and result.get("error"):
|
|
raise HTTPException(status_code=404, detail=result["error"])
|
|
|
|
return {
|
|
"hunt": result.get("hunt"),
|
|
"summary": result.get("summary"),
|
|
}
|