Spaces:
Sleeping
Sleeping
| """FastAPI application entry point. | |
| Run with: | |
| uvicorn api.main:app --reload --host 0.0.0.0 --port 8000 | |
| Environment variables (see .env.example): | |
| MODEL_PATH β path to ONNX weights file | |
| VIDEO_SOURCE β default video source (file path, webcam index, RTSP URL) | |
| CONFIDENCE_THRESHOLD β detection confidence threshold (default 0.35) | |
| IOU_THRESHOLD β NMS IoU threshold (default 0.45) | |
| CORS_ORIGINS β comma-separated allowed CORS origins (default *) | |
| SERVE_FRONTEND β set to "1" to serve the built React app from dashboard/dist | |
| (used in HF Spaces single-container deployment) | |
| """ | |
| from __future__ import annotations | |
| import os | |
| from contextlib import asynccontextmanager | |
| from pathlib import Path | |
| from fastapi import FastAPI | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.staticfiles import StaticFiles | |
| from api.database import init_db | |
| from api.routes import analytics, health, stream | |
| # ββ Lifespan ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async def lifespan(app: FastAPI): | |
| # Startup | |
| await init_db() | |
| print("[API] Database initialised.") | |
| # Auto-start pipeline if VIDEO_SOURCE env var is set | |
| source = os.environ.get("VIDEO_SOURCE", "").strip() | |
| if source: | |
| from api.pipeline_manager import manager | |
| from core.pipeline import PipelineConfig | |
| try: | |
| src: str | int = int(source) | |
| except ValueError: | |
| src = source | |
| config = PipelineConfig( | |
| source=src, | |
| model_path=os.environ.get("MODEL_PATH") or None, | |
| confidence_threshold=float(os.environ.get("CONFIDENCE_THRESHOLD", "0.35")), | |
| iou_threshold=float(os.environ.get("IOU_THRESHOLD", "0.45")), | |
| ) | |
| await manager.start(config) | |
| print(f"[API] Pipeline auto-started with source={src!r}") | |
| yield | |
| # Shutdown | |
| from api.pipeline_manager import manager | |
| await manager.stop() | |
| print("[API] Pipeline stopped.") | |
| # ββ App βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app = FastAPI( | |
| title="TrafficVision API", | |
| description="Real-time vehicle detection, tracking, and traffic analytics.", | |
| version="1.0.0", | |
| lifespan=lifespan, | |
| ) | |
| # CORS β allow React dev server by default; tighten in production | |
| _origins_env = os.environ.get("CORS_ORIGINS", "*") | |
| _origins = [o.strip() for o in _origins_env.split(",")] | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=_origins, | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # ββ Routes ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app.include_router(health.router, prefix="/api") | |
| app.include_router(stream.router, prefix="/api") | |
| app.include_router(analytics.router, prefix="/api") | |
| # ββ Frontend static files (HF Spaces single-container mode) ββββββββββββββββββ | |
| # Set SERVE_FRONTEND=1 to have FastAPI serve the built React app. | |
| # In local dev the Vite dev server handles this instead. | |
| _dist = Path(__file__).parent.parent / "dashboard" / "dist" | |
| if os.environ.get("SERVE_FRONTEND", "").strip() == "1" and _dist.is_dir(): | |
| # Mount the entire dist folder last β StaticFiles with html=True serves | |
| # index.html as the fallback for any path not matched by the API routes above. | |
| # Because this is mounted after include_router calls, API routes always win. | |
| app.mount("/", StaticFiles(directory=_dist, html=True), name="spa") | |
| else: | |
| async def root(): | |
| return {"message": "TrafficVision API", "docs": "/docs"} | |