Source code for app.main

import logging
import subprocess
from contextlib import asynccontextmanager

import sentry_sdk
import toml
from alembic import command
from alembic.config import Config
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
from fastapi.responses import RedirectResponse
from starlette.middleware.cors import CORSMiddleware

from app.api.v1.routes import routers as v1_routers
from app.core.config import configs
from app.core.container import Container
from app.util.class_object import singleton

logger = logging.getLogger(__name__)


[docs] def run_migrations(): """Run alembic upgrade head on startup. Creates all tables if the DB is fresh, or upgrades to the latest migration if the DB is behind. Safe to call multiple times (idempotent). """ try: alembic_cfg = Config("alembic.ini") command.upgrade(alembic_cfg, "head") logger.info("Alembic migrations applied successfully.") except Exception as e: logger.error("Alembic migration failed: %s", e) raise
[docs] @asynccontextmanager async def lifespan(app: FastAPI): run_migrations() yield
[docs] def get_project_data(): """ Retrieves project data from the pyproject.toml file. Returns: dict: Project data from the toml file. """ pyproject_path = "pyproject.toml" with open(pyproject_path, "r") as pyproject_file: pyproject_content = toml.load(pyproject_file) return pyproject_content["tool"]["poetry"]
project_data = get_project_data()
[docs] def get_swagger_oauth_config() -> dict: """ Builds Swagger UI OAuth init configuration from environment-based settings. Returns: dict: Swagger OAuth init configuration. """ oauth_config = {} if configs.KEYCLOAK_CLIENT_ID: oauth_config["clientId"] = configs.KEYCLOAK_CLIENT_ID if configs.KEYCLOAK_CLIENT_SECRET: oauth_config["clientSecret"] = configs.KEYCLOAK_CLIENT_SECRET return oauth_config
[docs] def custom_openapi(): """ Customizes the OpenAPI schema for the FastAPI application. Returns: dict: The customized OpenAPI schema. """ servers = [{"url": configs.API_V1_STR, "description": "Local"}] extra_server_url = configs.EXTRA_SERVER_URL extra_server_description = configs.EXTRA_SERVER_DESCRIPTION if extra_server_url: servers.append( {"url": extra_server_url, "description": extra_server_description} ) if app.openapi_schema: return app.openapi_schema openapi_schema = get_openapi( title=project_data["name"], version=project_data["version"], description=project_data["description"], routes=app.routes, servers=servers, ) for path in list(openapi_schema["paths"].keys()): if path.startswith("/api/v1"): openapi_schema["paths"][path[7:]] = openapi_schema["paths"].pop(path) app.openapi_schema = openapi_schema return app.openapi_schema
[docs] def get_git_commit_hash() -> str: """ Returns the current git commit hash, or "unknown" if not available. Returns: str: The current git commit hash, or "unknown" if not available. """ try: commit_hash = ( subprocess.check_output(["git", "rev-parse", "HEAD"]) .decode("ascii") .strip() ) except Exception: commit_hash = "unknown" return commit_hash
@singleton class AppCreator: """ Singleton class to create and configure the FastAPI application. """ def __init__(self): if configs.SENTRY_DSN: sentry_sdk.init( dsn=configs.SENTRY_DSN, environment=configs.SENTRY_ENVIRONMENT, release=configs.SENTRY_RELEASE, send_default_pii=True, traces_sample_rate=1.0, _experiments={ "continuous_profiling_auto_start": True, }, ) self.app = FastAPI( lifespan=lifespan, root_path=configs.ROOT_PATH, title=project_data["name"], version=project_data["version"], description=project_data["description"], license_info={"name": project_data["license"]}, contact={ "name": project_data["authors"][0], "email": project_data["authors"][0], }, redoc_url="/redocs", docs_url="/docs", servers=[{"url": configs.API_V1_STR, "description": "Local"}], swagger_ui_init_oauth=get_swagger_oauth_config(), ) self.app.openapi = custom_openapi self.container = Container() self.db = self.container.db() if configs.BACKEND_CORS_ORIGINS: self.app.add_middleware( CORSMiddleware, allow_origins=[str(origin) for origin in configs.BACKEND_CORS_ORIGINS], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @self.app.get("/", include_in_schema=False) def read_root(): """ Redirect to /docs return: RedirectResponse: Redirect to /docs """ return RedirectResponse(url="/docs") self.app.include_router(v1_routers, prefix=configs.API_V1_STR) app_creator = AppCreator() app = app_creator.app db = app_creator.db container = app_creator.container