import pathlib from functools import lru_cache import toml from fastapi import FastAPI from fastapi.encoders import jsonable_encoder from fastapi.exceptions import RequestValidationError from fastapi.openapi.docs import get_redoc_html from fastapi.openapi.utils import get_openapi from fastapi.staticfiles import StaticFiles from starlette import status from starlette.middleware.cors import CORSMiddleware from starlette.responses import JSONResponse from config import conf from core.app.context import useAppContext from core.app.docs import get_spotlight_docs app = FastAPI(debug=True, root_path=conf.server.base_api_doc_url) app.state = useAppContext() # mount and map static directory for _route, _dir in conf.static_files.items(): app.mount(f'{conf.server.prefix_url}/{_route}', StaticFiles(directory=_dir), name=_dir) def _parse_toml(pyproject_path): pyproject_text = pyproject_path.read_text() pyproject_data = toml.loads(pyproject_text) info = pyproject_data['tool']['poetry'] setup_data = { 'name': info.get('name', 'No Name'), 'version': info.get('version', 'No version'), 'description': info.get('description', 'No description'), 'authors': info.get('authors', ['None']), } return setup_data def get_pkg_info(): pyproject_path = pathlib.Path('pyproject.toml') return _parse_toml(pyproject_path) @app.exception_handler(RequestValidationError) async def validation_exception_handler(request, exc: RequestValidationError): return JSONResponse( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content=jsonable_encoder({"data": exc.errors(), "message": 'Validation error', 'code': 422}), ) @app.get('/ping') def ping(): return {"message": "pong", 'api_version': 2, "production": conf.server.is_prod, **get_pkg_info()} def custom_openapi(): if app.openapi_schema: return app.openapi_schema info = get_pkg_info() openapi_schema = get_openapi( title=f'{info.get("name")} Api Documents', version=info.get("version"), description=info.get("description"), routes=app.routes, ) openapi_schema["info"]["x-logo"] = { "url": conf.server.api_logo } app.openapi_schema = openapi_schema return app.openapi_schema @app.get("/redoc", include_in_schema=False) async def redoc_html(): info = get_pkg_info() return get_redoc_html( openapi_url=app.openapi_url, title=info.get('name') + " - ReDoc", ) @app.get("/docu", include_in_schema=False) async def docu(): info = get_pkg_info() return get_spotlight_docs( openapi_url=app.openapi_url, title=info.get('name') + " - Docu", ) app.openapi = custom_openapi @lru_cache() def load_origins(): return conf.cors.origins or [] app.add_middleware( CORSMiddleware, allow_origins=load_origins(), allow_credentials=conf.cors.allow_credentials, allow_methods=conf.cors.allow_methods, allow_headers=conf.cors.allow_headers, ) app.router.redirect_slashes = False