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
from fastapi import APIRouter, Depends
from src.routers import health
from src.routers import usergroups
from src.routers import dev, trail, users, auth, orgs, roles
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)],
)
v1_router.include_router(health.router, prefix="/health", tags=["health"])
# Dev Routes
v1_router.include_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)
}
// Health Check
if (pathname.startsWith('/health')) {
return NextResponse.rewrite(new URL(`/api/health`, req.url))
}
// Auth Redirects
if (pathname == '/redirect_from_auth') {
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
}
}
}