"""GuardDog Nexus — FastAPI application entry point.""" import os from contextlib import asynccontextmanager import uvicorn from fastapi import FastAPI, Request from fastapi.staticfiles import StaticFiles from starlette.middleware.base import BaseHTTPMiddleware from guarddog_nexus.config import config from guarddog_nexus.constants import ( APP_DESCRIPTION, APP_NAME, APP_PACKAGE, APP_VERSION, STATIC_MOUNT_PATH, ) from guarddog_nexus.db.engine import init_db from guarddog_nexus.i18n import DEFAULT_LANG, LANGUAGES from guarddog_nexus.logging_setup import log from guarddog_nexus.routes import api_findings, api_packages, api_scans from guarddog_nexus.routes.metrics import router as metrics_router from guarddog_nexus.routes.web import router as web_router from guarddog_nexus.routes.webhooks import router as webhook_router STATIC_DIR = os.path.join(os.path.dirname(__file__), "web", "static") class LangMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): # Cookie takes priority, then query param, then default cookie_lang = request.cookies.get("lang") query_lang = request.query_params.get("lang") if query_lang and query_lang in LANGUAGES and query_lang != cookie_lang: lang = query_lang elif cookie_lang and cookie_lang in LANGUAGES: lang = cookie_lang else: lang = DEFAULT_LANG request.state.lang = lang response = await call_next(request) if query_lang and query_lang in LANGUAGES: response.set_cookie("lang", query_lang, max_age=365 * 24 * 3600, httponly=True) return response @asynccontextmanager async def lifespan(app: FastAPI): await init_db() log.info("%s started on %s:%s", APP_NAME, config.host, config.port) yield log.info("%s shutting down", APP_NAME) app = FastAPI( title=APP_NAME, version=APP_VERSION, description=APP_DESCRIPTION, lifespan=lifespan, ) app.add_middleware(LangMiddleware) app.include_router(webhook_router) app.include_router(metrics_router) app.include_router(api_scans.router) app.include_router(api_packages.router) app.include_router(api_findings.router) app.include_router(web_router) if os.path.isdir(STATIC_DIR): app.mount(STATIC_MOUNT_PATH, StaticFiles(directory=STATIC_DIR), name="static") @app.get("/health") async def health(): return {"status": "ok", "version": APP_VERSION} def main(): uvicorn.run( f"{APP_PACKAGE}.main:app", host=config.host, port=config.port, log_level=config.log_level.lower(), reload=False, ) if __name__ == "__main__": main()