import asyncio from fastapi import APIRouter, Depends, Query, Request, Response from sqlalchemy import text from sqlalchemy.orm import Session from core_domain import ServiceDescriptor, ServiceHealth from app.bootstrap.settings import ApiGatewaySettings from app.db.session import get_db from app.domain.repositories import GatewayRequestAuditRepository from app.infrastructure.proxy import ProxyServiceName, ProxyTarget, ServiceProxy from app.schemas.gateway import GatewayRequestAuditResponse, GatewayServicesHealthResponse router = APIRouter() @router.get("/health", response_model=ServiceDescriptor) def health_check(db: Session = Depends(get_db)) -> ServiceDescriptor: db.execute(text("SELECT 1")) return ServiceDescriptor(name="api-gateway") @router.get("/ready", response_model=ServiceHealth) def readiness_check(db: Session = Depends(get_db)) -> ServiceHealth: db.execute(text("SELECT 1")) return ServiceHealth(service="api-gateway", status="ok", database="ok") @router.get("/gateway/audits", response_model=list[GatewayRequestAuditResponse]) def list_gateway_audits( tenant_id: str = Query(...), request_id: str | None = Query(default=None), target_service: str | None = Query(default=None), limit: int = Query(default=100, ge=1, le=500), db: Session = Depends(get_db), ) -> list[GatewayRequestAuditResponse]: items = GatewayRequestAuditRepository(db).list_by_scope( tenant_id=tenant_id, request_id=request_id, target_service=target_service, limit=limit, ) return [GatewayRequestAuditResponse.from_entity(item) for item in items] def get_gateway_settings() -> ApiGatewaySettings: return ApiGatewaySettings() def get_service_proxy(settings: ApiGatewaySettings = Depends(get_gateway_settings)) -> ServiceProxy: return ServiceProxy(timeout_seconds=settings.proxy_timeout_seconds) def build_proxy_targets(settings: ApiGatewaySettings) -> dict[ProxyServiceName, ProxyTarget]: return { "workflow-service": ProxyTarget( service_name="workflow-service", base_url=settings.workflow_service_url, path_prefix="/workflows", health_path="/workflows/health", ), "session-service": ProxyTarget( service_name="session-service", base_url=settings.session_service_url, path_prefix="/sessions", health_path="/sessions/health", ), "runtime-service": ProxyTarget( service_name="runtime-service", base_url=settings.runtime_service_url, path_prefix="/runtime", health_path="/runtime/health", ), "tool-service": ProxyTarget( service_name="tool-service", base_url=settings.tool_service_url, path_prefix="/tools", health_path="/tools/health", ), "model-gateway-service": ProxyTarget( service_name="model-gateway-service", base_url=settings.model_gateway_service_url, path_prefix="/models", health_path="/models/health", ), "code-runner-service": ProxyTarget( service_name="code-runner-service", base_url=settings.code_runner_service_url, path_prefix="/code", health_path="/code/health", ), } @router.get("/gateway/services/health", response_model=GatewayServicesHealthResponse) async def downstream_health_check( settings: ApiGatewaySettings = Depends(get_gateway_settings), ) -> GatewayServicesHealthResponse: targets = build_proxy_targets(settings) health_proxy = ServiceProxy(timeout_seconds=settings.downstream_health_timeout_seconds) downstream_services = await asyncio.gather( *[health_proxy.check_health(target) for target in targets.values()] ) status = "ok" if all(item.status == "ok" for item in downstream_services) else "degraded" return GatewayServicesHealthResponse( status=status, downstream_services=downstream_services, ) @router.api_route( "/gateway/workflows", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) @router.api_route( "/gateway/workflows/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) async def proxy_workflow_service( request: Request, path: str = "", settings: ApiGatewaySettings = Depends(get_gateway_settings), proxy: ServiceProxy = Depends(get_service_proxy), ) -> Response: return await proxy.forward( request=request, target=build_proxy_targets(settings)["workflow-service"], path=path, ) @router.api_route( "/gateway/sessions", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) @router.api_route( "/gateway/sessions/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) async def proxy_session_service( request: Request, path: str = "", settings: ApiGatewaySettings = Depends(get_gateway_settings), proxy: ServiceProxy = Depends(get_service_proxy), ) -> Response: return await proxy.forward( request=request, target=build_proxy_targets(settings)["session-service"], path=path, ) @router.api_route( "/gateway/runtime", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) @router.api_route( "/gateway/runtime/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) async def proxy_runtime_service( request: Request, path: str = "", settings: ApiGatewaySettings = Depends(get_gateway_settings), proxy: ServiceProxy = Depends(get_service_proxy), ) -> Response: return await proxy.forward( request=request, target=build_proxy_targets(settings)["runtime-service"], path=path, ) @router.api_route( "/gateway/tools", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) @router.api_route( "/gateway/tools/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) async def proxy_tool_service( request: Request, path: str = "", settings: ApiGatewaySettings = Depends(get_gateway_settings), proxy: ServiceProxy = Depends(get_service_proxy), ) -> Response: return await proxy.forward( request=request, target=build_proxy_targets(settings)["tool-service"], path=path, ) @router.api_route( "/gateway/models", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) @router.api_route( "/gateway/models/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) async def proxy_model_gateway_service( request: Request, path: str = "", settings: ApiGatewaySettings = Depends(get_gateway_settings), proxy: ServiceProxy = Depends(get_service_proxy), ) -> Response: return await proxy.forward( request=request, target=build_proxy_targets(settings)["model-gateway-service"], path=path, ) @router.api_route( "/gateway/code", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) @router.api_route( "/gateway/code/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"], ) async def proxy_code_runner_service( request: Request, path: str = "", settings: ApiGatewaySettings = Depends(get_gateway_settings), proxy: ServiceProxy = Depends(get_service_proxy), ) -> Response: return await proxy.forward( request=request, target=build_proxy_targets(settings)["code-runner-service"], path=path, )