import logging from typing import Any, Dict from fastapi import ( BackgroundTasks, FastAPI, Form, Query, Request, ) from pydantic import AnyHttpUrl from .. import data from ..middleware import ( BroodAuthMiddleware, BugoutCORSMiddleware, EngineHTTPException, create_application_settings_cors_origin, fetch_and_set_cors_origins_cache, parse_origins_from_resources, ) from ..settings import ( BUGOUT_REQUEST_TIMEOUT_SECONDS, BUGOUT_RESOURCE_TYPE_APPLICATION_CONFIG, DOCS_TARGET_PATH, MOONSTREAM_ADMIN_ACCESS_TOKEN, MOONSTREAM_APPLICATION_ID, ) from ..settings import bugout_client as bc from ..version import VERSION logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) tags_metadata = [ {"name": "configs", "description": "Moonstream Engine API configurations"} ] whitelist_paths: Dict[str, str] = {} whitelist_paths.update( { "/configs/docs": "GET", "/configs/openapi.json": "GET", "/configs/is_origin": "GET", "/configs/origins/sync": "GET", } ) app = FastAPI( title=f"Moonstream Engine API configurations", description="Moonstream Engine API configurations endpoints.", version=VERSION, openapi_tags=tags_metadata, openapi_url="/openapi.json", docs_url=None, redoc_url=f"/{DOCS_TARGET_PATH}", ) app.add_middleware(BroodAuthMiddleware, whitelist=whitelist_paths) app.add_middleware( BugoutCORSMiddleware, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/is_origin", response_model=data.IsCORSResponse) async def is_cors_origin(origin: str = Query(...)) -> data.IsCORSResponse: is_cors_origin = data.IsCORSResponse() try: resources = bc.list_resources( token=MOONSTREAM_ADMIN_ACCESS_TOKEN, params={ "application_id": MOONSTREAM_APPLICATION_ID, "type": BUGOUT_RESOURCE_TYPE_APPLICATION_CONFIG, "setting": "cors", }, timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS, ) cors_origins: data.CORSOrigins = parse_origins_from_resources( resources.resources ) if origin in cors_origins.origins_set: for resource in cors_origins.resources: resource_origin = resource.resource_data.get("origin", "") # TODO(kompotkot): There are could be multiple creations by different users. # Add logic to show most recent updated_at and oldest created_at. if resource_origin == origin: is_cors_origin.origin = resource_origin is_cors_origin.created_at = resource.created_at is_cors_origin.updated_at = resource.updated_at except Exception as err: logger.error(repr(err)) raise EngineHTTPException(status_code=500) return is_cors_origin @app.get("/origins", response_model=data.CORSOrigins) async def get_cors_origins( request: Request, ) -> data.CORSOrigins: try: resources = bc.list_resources( token=request.state.token, params={ "application_id": MOONSTREAM_APPLICATION_ID, "type": BUGOUT_RESOURCE_TYPE_APPLICATION_CONFIG, "setting": "cors", }, timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS, ) cors_origins: data.CORSOrigins = parse_origins_from_resources( resources.resources ) except Exception as err: logger.error(repr(err)) raise EngineHTTPException(status_code=500) return cors_origins @app.get("/origins/sync", response_model=int) async def synchronize_cors_origins() -> int: cors_origins = fetch_and_set_cors_origins_cache() cors_origins_len = len(cors_origins.origins_set) return cors_origins_len @app.post("/origin", response_model=data.CORSOrigins) async def add_cors_origin( request: Request, background_tasks: BackgroundTasks, new_origin: AnyHttpUrl = Form(...), ) -> data.CORSOrigins: try: resources = bc.list_resources( token=request.state.token, params={ "application_id": MOONSTREAM_APPLICATION_ID, "type": BUGOUT_RESOURCE_TYPE_APPLICATION_CONFIG, "setting": "cors", }, timeout=BUGOUT_REQUEST_TIMEOUT_SECONDS, ) except Exception as err: logger.error(f"Unable to fetch resource from Brood, err: {repr(err)}") raise EngineHTTPException(status_code=500) cors_origins: data.CORSOrigins = parse_origins_from_resources(resources.resources) if new_origin in cors_origins.origins_set: raise EngineHTTPException( status_code=409, detail=f"Provided origin {new_origin} already set by user", ) resource = create_application_settings_cors_origin( token=request.state.token, user_id=request.state.user.id, username=request.state.user.username, origin=new_origin, ) cors_origins.origins_set.add(new_origin) cors_origins.resources.append(resource) background_tasks.add_task( fetch_and_set_cors_origins_cache, ) return cors_origins