routes.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import asyncio
  2. from fastapi import APIRouter, Depends, Query, Request, Response
  3. from sqlalchemy import text
  4. from sqlalchemy.orm import Session
  5. from core_domain import ServiceDescriptor, ServiceHealth
  6. from app.bootstrap.settings import ApiGatewaySettings
  7. from app.db.session import get_db
  8. from app.domain.repositories import GatewayRequestAuditRepository
  9. from app.infrastructure.proxy import ProxyServiceName, ProxyTarget, ServiceProxy
  10. from app.schemas.gateway import GatewayRequestAuditResponse, GatewayServicesHealthResponse
  11. router = APIRouter()
  12. @router.get("/health", response_model=ServiceDescriptor)
  13. def health_check(db: Session = Depends(get_db)) -> ServiceDescriptor:
  14. db.execute(text("SELECT 1"))
  15. return ServiceDescriptor(name="api-gateway")
  16. @router.get("/ready", response_model=ServiceHealth)
  17. def readiness_check(db: Session = Depends(get_db)) -> ServiceHealth:
  18. db.execute(text("SELECT 1"))
  19. return ServiceHealth(service="api-gateway", status="ok", database="ok")
  20. @router.get("/gateway/audits", response_model=list[GatewayRequestAuditResponse])
  21. def list_gateway_audits(
  22. tenant_id: str = Query(...),
  23. request_id: str | None = Query(default=None),
  24. target_service: str | None = Query(default=None),
  25. limit: int = Query(default=100, ge=1, le=500),
  26. db: Session = Depends(get_db),
  27. ) -> list[GatewayRequestAuditResponse]:
  28. items = GatewayRequestAuditRepository(db).list_by_scope(
  29. tenant_id=tenant_id,
  30. request_id=request_id,
  31. target_service=target_service,
  32. limit=limit,
  33. )
  34. return [GatewayRequestAuditResponse.from_entity(item) for item in items]
  35. def get_gateway_settings() -> ApiGatewaySettings:
  36. return ApiGatewaySettings()
  37. def get_service_proxy(settings: ApiGatewaySettings = Depends(get_gateway_settings)) -> ServiceProxy:
  38. return ServiceProxy(timeout_seconds=settings.proxy_timeout_seconds)
  39. def build_proxy_targets(settings: ApiGatewaySettings) -> dict[ProxyServiceName, ProxyTarget]:
  40. return {
  41. "workflow-service": ProxyTarget(
  42. service_name="workflow-service",
  43. base_url=settings.workflow_service_url,
  44. path_prefix="/workflows",
  45. health_path="/workflows/health",
  46. ),
  47. "session-service": ProxyTarget(
  48. service_name="session-service",
  49. base_url=settings.session_service_url,
  50. path_prefix="/sessions",
  51. health_path="/sessions/health",
  52. ),
  53. "runtime-service": ProxyTarget(
  54. service_name="runtime-service",
  55. base_url=settings.runtime_service_url,
  56. path_prefix="/runtime",
  57. health_path="/runtime/health",
  58. ),
  59. "tool-service": ProxyTarget(
  60. service_name="tool-service",
  61. base_url=settings.tool_service_url,
  62. path_prefix="/tools",
  63. health_path="/tools/health",
  64. ),
  65. "model-gateway-service": ProxyTarget(
  66. service_name="model-gateway-service",
  67. base_url=settings.model_gateway_service_url,
  68. path_prefix="/models",
  69. health_path="/models/health",
  70. ),
  71. "code-runner-service": ProxyTarget(
  72. service_name="code-runner-service",
  73. base_url=settings.code_runner_service_url,
  74. path_prefix="/code",
  75. health_path="/code/health",
  76. ),
  77. }
  78. @router.get("/gateway/services/health", response_model=GatewayServicesHealthResponse)
  79. async def downstream_health_check(
  80. settings: ApiGatewaySettings = Depends(get_gateway_settings),
  81. ) -> GatewayServicesHealthResponse:
  82. targets = build_proxy_targets(settings)
  83. health_proxy = ServiceProxy(timeout_seconds=settings.downstream_health_timeout_seconds)
  84. downstream_services = await asyncio.gather(
  85. *[health_proxy.check_health(target) for target in targets.values()]
  86. )
  87. status = "ok" if all(item.status == "ok" for item in downstream_services) else "degraded"
  88. return GatewayServicesHealthResponse(
  89. status=status,
  90. downstream_services=downstream_services,
  91. )
  92. @router.api_route(
  93. "/gateway/workflows",
  94. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  95. )
  96. @router.api_route(
  97. "/gateway/workflows/{path:path}",
  98. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  99. )
  100. async def proxy_workflow_service(
  101. request: Request,
  102. path: str = "",
  103. settings: ApiGatewaySettings = Depends(get_gateway_settings),
  104. proxy: ServiceProxy = Depends(get_service_proxy),
  105. ) -> Response:
  106. return await proxy.forward(
  107. request=request,
  108. target=build_proxy_targets(settings)["workflow-service"],
  109. path=path,
  110. )
  111. @router.api_route(
  112. "/gateway/sessions",
  113. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  114. )
  115. @router.api_route(
  116. "/gateway/sessions/{path:path}",
  117. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  118. )
  119. async def proxy_session_service(
  120. request: Request,
  121. path: str = "",
  122. settings: ApiGatewaySettings = Depends(get_gateway_settings),
  123. proxy: ServiceProxy = Depends(get_service_proxy),
  124. ) -> Response:
  125. return await proxy.forward(
  126. request=request,
  127. target=build_proxy_targets(settings)["session-service"],
  128. path=path,
  129. )
  130. @router.api_route(
  131. "/gateway/runtime",
  132. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  133. )
  134. @router.api_route(
  135. "/gateway/runtime/{path:path}",
  136. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  137. )
  138. async def proxy_runtime_service(
  139. request: Request,
  140. path: str = "",
  141. settings: ApiGatewaySettings = Depends(get_gateway_settings),
  142. proxy: ServiceProxy = Depends(get_service_proxy),
  143. ) -> Response:
  144. return await proxy.forward(
  145. request=request,
  146. target=build_proxy_targets(settings)["runtime-service"],
  147. path=path,
  148. )
  149. @router.api_route(
  150. "/gateway/tools",
  151. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  152. )
  153. @router.api_route(
  154. "/gateway/tools/{path:path}",
  155. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  156. )
  157. async def proxy_tool_service(
  158. request: Request,
  159. path: str = "",
  160. settings: ApiGatewaySettings = Depends(get_gateway_settings),
  161. proxy: ServiceProxy = Depends(get_service_proxy),
  162. ) -> Response:
  163. return await proxy.forward(
  164. request=request,
  165. target=build_proxy_targets(settings)["tool-service"],
  166. path=path,
  167. )
  168. @router.api_route(
  169. "/gateway/models",
  170. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  171. )
  172. @router.api_route(
  173. "/gateway/models/{path:path}",
  174. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  175. )
  176. async def proxy_model_gateway_service(
  177. request: Request,
  178. path: str = "",
  179. settings: ApiGatewaySettings = Depends(get_gateway_settings),
  180. proxy: ServiceProxy = Depends(get_service_proxy),
  181. ) -> Response:
  182. return await proxy.forward(
  183. request=request,
  184. target=build_proxy_targets(settings)["model-gateway-service"],
  185. path=path,
  186. )
  187. @router.api_route(
  188. "/gateway/code",
  189. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  190. )
  191. @router.api_route(
  192. "/gateway/code/{path:path}",
  193. methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
  194. )
  195. async def proxy_code_runner_service(
  196. request: Request,
  197. path: str = "",
  198. settings: ApiGatewaySettings = Depends(get_gateway_settings),
  199. proxy: ServiceProxy = Depends(get_service_proxy),
  200. ) -> Response:
  201. return await proxy.forward(
  202. request=request,
  203. target=build_proxy_targets(settings)["code-runner-service"],
  204. path=path,
  205. )