|
| 1 | +import logging |
1 | 2 | import os |
2 | | -from fastapi import FastAPI |
| 3 | + |
| 4 | +from fastapi import FastAPI, status |
3 | 5 | from fastapi.middleware.cors import CORSMiddleware |
4 | | -from fastapi.responses import HTMLResponse |
5 | | -from starlette.requests import Request |
| 6 | +from fastapi.responses import HTMLResponse, JSONResponse |
| 7 | +from sqlalchemy import text |
6 | 8 | import sentry_sdk |
7 | 9 | from sentry_sdk.integrations.fastapi import FastApiIntegration |
8 | | -from sentry_sdk.integrations.starlette import StarletteIntegration |
9 | 10 | from sentry_sdk.integrations.logging import LoggingIntegration |
10 | 11 | from sentry_sdk.integrations.redis import RedisIntegration |
11 | 12 | from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration |
| 13 | +from sentry_sdk.integrations.starlette import StarletteIntegration |
12 | 14 | from sentry_sdk.integrations.threading import ThreadingIntegration |
13 | | -import logging |
| 15 | +from starlette.requests import Request |
14 | 16 |
|
| 17 | +from src.database.database import engine |
| 18 | +from src.dependencies.basic import get_redis_client |
15 | 19 | from src.routers.server import router |
16 | | -from src.schemas.basic import TextOnly |
| 20 | +from src.schemas.basic import TextOnly, HealthCheck, ServiceStatus |
17 | 21 | from src.utils.swagger import custom_swagger_ui_html |
18 | 22 |
|
19 | 23 | # Initialize Sentry |
@@ -98,6 +102,52 @@ async def root(): |
98 | 102 | return TextOnly(text="Hello World") |
99 | 103 |
|
100 | 104 |
|
| 105 | +@app.get("/healthz", response_model=HealthCheck, tags=["Health"]) |
| 106 | +async def health_check(): |
| 107 | + """ |
| 108 | + Health check endpoint that verifies MySQL and Redis connectivity. |
| 109 | + Returns 200 if all services are healthy, 503 if any service is unhealthy. |
| 110 | + """ |
| 111 | + mysql_status = ServiceStatus(status="healthy", message="Connected") |
| 112 | + redis_status = ServiceStatus(status="healthy", message="Connected") |
| 113 | + overall_healthy = True |
| 114 | + |
| 115 | + # Check MySQL connection |
| 116 | + try: |
| 117 | + with engine.connect() as connection: |
| 118 | + connection.execute(text("SELECT 1")) |
| 119 | + except Exception as e: |
| 120 | + mysql_status = ServiceStatus(status="unhealthy", message=f"Connection failed: {str(e)}") |
| 121 | + overall_healthy = False |
| 122 | + |
| 123 | + # Check Redis connection |
| 124 | + try: |
| 125 | + redis_gen = get_redis_client() |
| 126 | + redis_client = next(redis_gen) |
| 127 | + redis_client.ping() |
| 128 | + try: |
| 129 | + next(redis_gen) # Trigger the finally block to close the connection |
| 130 | + except StopIteration: |
| 131 | + pass |
| 132 | + except Exception as e: |
| 133 | + redis_status = ServiceStatus(status="unhealthy", message=f"Connection failed: {str(e)}") |
| 134 | + overall_healthy = False |
| 135 | + |
| 136 | + health_response = HealthCheck( |
| 137 | + status="healthy" if overall_healthy else "unhealthy", |
| 138 | + mysql=mysql_status, |
| 139 | + redis=redis_status |
| 140 | + ) |
| 141 | + |
| 142 | + if overall_healthy: |
| 143 | + return health_response |
| 144 | + else: |
| 145 | + return JSONResponse( |
| 146 | + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, |
| 147 | + content=health_response.model_dump() |
| 148 | + ) |
| 149 | + |
| 150 | + |
101 | 151 | @app.get("/elements", include_in_schema=False) |
102 | 152 | async def api_documentation(request: Request): |
103 | 153 | return HTMLResponse(""" |
|
0 commit comments