"""FastAPI entry point for NetBird MSP Appliance.""" import logging import os from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from fastapi.staticfiles import StaticFiles from slowapi import _rate_limit_exceeded_handler from slowapi.errors import RateLimitExceeded from app.database import init_db from app.limiter import limiter from app.routers import auth, customers, deployments, monitoring, settings, users # --------------------------------------------------------------------------- # Logging # --------------------------------------------------------------------------- LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO").upper() logging.basicConfig( level=getattr(logging, LOG_LEVEL, logging.INFO), format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", ) logger = logging.getLogger(__name__) # --------------------------------------------------------------------------- # Application # --------------------------------------------------------------------------- # --------------------------------------------------------------------------- # Application # --------------------------------------------------------------------------- app = FastAPI( title="NetBird MSP Appliance", description="Multi-tenant NetBird management platform for MSPs", version="1.0.0", docs_url="/api/docs", redoc_url="/api/redoc", openapi_url="/api/openapi.json", ) # Attach limiter to app state and register the 429 exception handler app.state.limiter = limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) # CORS — allow same-origin; adjust if needed app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # --------------------------------------------------------------------------- # Routers # --------------------------------------------------------------------------- app.include_router(auth.router, prefix="/api/auth", tags=["Authentication"]) app.include_router(settings.router, prefix="/api/settings", tags=["Settings"]) app.include_router(customers.router, prefix="/api/customers", tags=["Customers"]) app.include_router(deployments.router, prefix="/api/customers", tags=["Deployments"]) app.include_router(monitoring.router, prefix="/api/monitoring", tags=["Monitoring"]) app.include_router(users.router, prefix="/api/users", tags=["Users"]) # --------------------------------------------------------------------------- # Static files — serve the frontend SPA # --------------------------------------------------------------------------- STATIC_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "static") if os.path.isdir(STATIC_DIR): app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") # Serve index.html at root from fastapi.responses import FileResponse @app.get("/", include_in_schema=False) async def serve_index(): """Serve the main dashboard.""" index_path = os.path.join(STATIC_DIR, "index.html") if os.path.isfile(index_path): return FileResponse(index_path) return JSONResponse({"message": "NetBird MSP Appliance API is running."}) # --------------------------------------------------------------------------- # Health endpoint (unauthenticated) # --------------------------------------------------------------------------- @app.get("/api/health", tags=["Health"]) async def health_check(): """Simple health check endpoint for Docker HEALTHCHECK.""" return {"status": "ok", "service": "netbird-msp-appliance"} # --------------------------------------------------------------------------- # Startup event # --------------------------------------------------------------------------- @app.on_event("startup") async def startup_event(): """Initialize database tables on startup.""" logger.info("Starting NetBird MSP Appliance...") init_db() logger.info("Database initialized.")