feat: better healthcheck

This commit is contained in:
swve 2024-11-23 20:52:24 +01:00
parent 6cd1cf7e9c
commit 46f016f661
8 changed files with 112 additions and 9 deletions

View file

@ -1,5 +1,6 @@
import os import os
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from src.routers import health
from src.routers import usergroups from src.routers import usergroups
from src.routers import dev, trail, users, auth, orgs, roles from src.routers import dev, trail, users, auth, orgs, roles
from src.routers.ai import ai from src.routers.ai import ai
@ -42,6 +43,8 @@ if os.environ.get("CLOUD_INTERNAL_KEY"):
dependencies=[Depends(cloud_internal.check_internal_cloud_key)], dependencies=[Depends(cloud_internal.check_internal_cloud_key)],
) )
v1_router.include_router(health.router, prefix="/health", tags=["health"])
# Dev Routes # Dev Routes
v1_router.include_router( v1_router.include_router(
dev.router, dev.router,

View file

@ -0,0 +1,11 @@
from fastapi import Depends, APIRouter
from sqlmodel import Session
from src.services.health.health import check_health
from src.core.events.database import get_db_session
router = APIRouter()
@router.get("")
async def health(db_session: Session = Depends(get_db_session)):
return await check_health(db_session)

View file

View file

@ -0,0 +1,21 @@
from fastapi import HTTPException
from sqlmodel import Session, select
from src.db.organizations import Organization
async def check_database_health(db_session: Session) -> bool:
statement = select(Organization)
result = db_session.exec(statement)
if not result:
return False
return True
async def check_health(db_session: Session) -> bool:
# Check database health
database_healthy = await check_database_health(db_session)
if not database_healthy:
raise HTTPException(status_code=503, detail="Database is not healthy")
return True

View file

@ -0,0 +1,39 @@
export const dynamic = 'force-dynamic' // defaults to auto
export const revalidate = 0
import { NextResponse } from 'next/server';
import { checkHealth } from '@services/utils/health';
export async function GET() {
const health = await checkHealth()
if (health.success === true) {
return NextResponse.json(
{
status: 'healthy',
timestamp: new Date().toISOString(),
health: health.data,
},
{
status: 200,
headers: {
'Content-Type': 'application/json',
},
}
)
} else {
return NextResponse.json(
{
status: 'unhealthy',
timestamp: new Date().toISOString(),
health: null,
error: health.HTTPmessage,
},
{
status: 503,
headers: {
'Content-Type': 'application/json',
},
}
);
}
}

View file

@ -1,9 +0,0 @@
import React from 'react'
function HealthPage() {
return (
<div>OK</div>
)
}
export default HealthPage

View file

@ -102,6 +102,11 @@ export default async function middleware(req: NextRequest) {
return NextResponse.rewrite(redirectUrl) return NextResponse.rewrite(redirectUrl)
} }
// Health Check
if (pathname.startsWith('/health')) {
return NextResponse.rewrite(new URL(`/api/health`, req.url))
}
// Auth Redirects // Auth Redirects
if (pathname == '/redirect_from_auth') { if (pathname == '/redirect_from_auth') {
if (cookie_orgslug) { if (cookie_orgslug) {

View file

@ -0,0 +1,33 @@
import { getAPIUrl } from '@services/config/config'
import {
RequestBody,
getResponseMetadata,
} from '@services/utils/ts/requests'
export async function checkHealth() {
try {
const result = await fetch(
`${getAPIUrl()}health`,
RequestBody('GET', null, null)
)
if (!result.ok) {
return {
success: false,
status: result.status,
HTTPmessage: result.statusText,
data: null
}
}
const res = await getResponseMetadata(result)
return res
} catch (error) {
return {
success: false,
status: 503,
HTTPmessage: 'Service unavailable',
data: null
}
}
}