Files
ja4-platform/services/dashboard/backend/main.py
Jacquin Antoine f88b739992 feat(e2e): add distributed E2E test framework with parametric traffic generation
Add run-e2e-test.sh with CLI parameters (--hits, --http-ratio, --dns, --tls,
--src-ips, --keep-analysis, --up) for configurable traffic generation. Traffic
runs from VM endpoints with multiple source IPs (alias IPs on eth0) to produce
distinct sessions for the ML pipeline. Fix curl TLS flags (--tlsv1.2 instead
of --tls-v1-2), skip redundant local verification in distributed mode, and
fix dashboard is_available() cache that never retried after ClickHouse recovery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 00:09:32 +02:00

75 lines
2.5 KiB
Python

"""JA4 SOC Dashboard — FastAPI application."""
from __future__ import annotations
import logging
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from backend.database import ClickHouseUnavailable, is_available
from backend.routes.api import router as api_router
from backend.routes.pages import router as pages_router
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s")
_templates = Jinja2Templates(directory="backend/templates")
_PAGE_MAP = {
"/": "overview", "/detections": "detections", "/scores": "scores",
"/traffic": "traffic", "/classify": "classify", "/features": "features",
"/models": "models", "/network": "network", "/campaigns": "campaigns",
"/tactics": "tactics", "/reflists": "reflists", "/fleet": "fleet",
"/health": "health", "/browsers": "browsers", "/fingerprints": "fingerprints",
}
app = FastAPI(title="JA4 SOC Dashboard", version="1.0.0")
# CORS — allow all origins for dashboard access
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.exception_handler(ClickHouseUnavailable)
async def ch_unavailable_handler(request: Request, exc: ClickHouseUnavailable):
"""Return 503 for API calls, render degraded pages for HTML requests."""
accept = request.headers.get("accept", "")
path = request.url.path
# If the client expects JSON (API call), return 503 JSON
if "application/json" in accept or path.startswith("/api/"):
return JSONResponse(
status_code=503,
content={"detail": "ClickHouse unavailable", "error": str(exc)},
)
# For HTML pages, render the template with ch_available=False
page_name = _PAGE_MAP.get(path, "overview")
return _templates.TemplateResponse(
f"{page_name}.html",
{"request": request, "active_page": page_name, "ch_available": False},
status_code=503,
)
# Static assets
app.mount("/static", StaticFiles(directory="backend/static"), name="static")
# Routers — API first so /api/* paths match before page catch-all
app.include_router(api_router)
app.include_router(pages_router)
@app.get("/api/healthcheck")
async def healthcheck():
ch = is_available()
return {"status": "ok" if ch else "degraded", "clickhouse": "up" if ch else "down"}