Newer
Older
gnexus-creds / gnexus_creds / main.py
@Eugene Sukhodolskiy Eugene Sukhodolskiy 4 days ago 2 KB Improve UI layout and serve kit fonts
"""FastAPI application entrypoint."""

from pathlib import Path

from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from sqlalchemy import text

from gnexus_creds.api import admin_router
from gnexus_creds.api import router as api_router
from gnexus_creds.config import get_settings
from gnexus_creds.db import SessionLocal
from gnexus_creds.errors import AppError, app_error_handler, validation_error_handler
from gnexus_creds.logging import configure_logging
from gnexus_creds.mcp import router as mcp_router
from gnexus_creds.oauth import router as auth_router
from gnexus_creds.oauth import webhook_router

PROJECT_ROOT = Path(__file__).resolve().parent.parent
FRONTEND_DIST = PROJECT_ROOT / "frontend" / "dist"
UI_KIT_ASSETS = PROJECT_ROOT / "frontend" / "node_modules" / "gnexus-ui-kit" / "dist" / "assets"


def create_app() -> FastAPI:
    configure_logging()
    settings = get_settings()
    app = FastAPI(title=settings.app_name, version="0.1.0")
    app.add_exception_handler(AppError, app_error_handler)
    app.add_exception_handler(RequestValidationError, validation_error_handler)
    app.include_router(auth_router)
    app.include_router(webhook_router)
    app.include_router(api_router)
    app.include_router(admin_router)
    app.include_router(mcp_router)

    @app.get("/health", tags=["system"])
    async def health() -> dict[str, str]:
        return {"status": "ok"}

    @app.get("/ready", tags=["system"])
    async def ready() -> dict[str, str]:
        with SessionLocal() as db:
            db.execute(text("select 1"))
        return {"status": "ok"}

    if FRONTEND_DIST.exists():
        ui_kit_fonts_dir = UI_KIT_ASSETS / "fonts"
        if ui_kit_fonts_dir.exists():
            app.mount("/assets/fonts", StaticFiles(directory=ui_kit_fonts_dir), name="ui-kit-fonts")

        assets_dir = FRONTEND_DIST / "assets"
        if assets_dir.exists():
            app.mount("/assets", StaticFiles(directory=assets_dir), name="assets")

        @app.get("/", include_in_schema=False)
        async def index() -> FileResponse:
            return FileResponse(FRONTEND_DIST / "index.html")

        @app.get("/{path:path}", include_in_schema=False)
        async def spa_fallback(path: str) -> FileResponse:
            if path.startswith(("api/", "auth/", "mcp/", "webhooks/")):
                raise AppError("not_found", "Not found.", status_code=404)
            return FileResponse(FRONTEND_DIST / "index.html")

    return app


app = create_app()