110 lines
2.9 KiB
Python
110 lines
2.9 KiB
Python
|
|
import os
|
||
|
|
import asyncio
|
||
|
|
from datetime import datetime, timedelta
|
||
|
|
from typing import Optional
|
||
|
|
from contextlib import asynccontextmanager
|
||
|
|
|
||
|
|
from fastapi import FastAPI, Request
|
||
|
|
from fastapi.responses import HTMLResponse
|
||
|
|
from fastapi.templating import Jinja2Templates
|
||
|
|
|
||
|
|
from monitors import ServiceMonitor, ServiceStatus
|
||
|
|
|
||
|
|
|
||
|
|
# Configuration
|
||
|
|
BACKEND_URL = os.getenv("BACKEND_URL", "http://backend:8000")
|
||
|
|
FRONTEND_URL = os.getenv("FRONTEND_URL", "http://frontend:80")
|
||
|
|
BOT_URL = os.getenv("BOT_URL", "http://bot:8080")
|
||
|
|
CHECK_INTERVAL = int(os.getenv("CHECK_INTERVAL", "30"))
|
||
|
|
|
||
|
|
# Initialize monitor
|
||
|
|
monitor = ServiceMonitor()
|
||
|
|
|
||
|
|
# Background task reference
|
||
|
|
background_task: Optional[asyncio.Task] = None
|
||
|
|
|
||
|
|
|
||
|
|
async def periodic_health_check():
|
||
|
|
"""Background task to check services periodically"""
|
||
|
|
while True:
|
||
|
|
await monitor.check_all_services(
|
||
|
|
backend_url=BACKEND_URL,
|
||
|
|
frontend_url=FRONTEND_URL,
|
||
|
|
bot_url=BOT_URL
|
||
|
|
)
|
||
|
|
await asyncio.sleep(CHECK_INTERVAL)
|
||
|
|
|
||
|
|
|
||
|
|
@asynccontextmanager
|
||
|
|
async def lifespan(app: FastAPI):
|
||
|
|
"""Startup and shutdown events"""
|
||
|
|
global background_task
|
||
|
|
# Start background health checks
|
||
|
|
background_task = asyncio.create_task(periodic_health_check())
|
||
|
|
yield
|
||
|
|
# Cancel background task on shutdown
|
||
|
|
if background_task:
|
||
|
|
background_task.cancel()
|
||
|
|
try:
|
||
|
|
await background_task
|
||
|
|
except asyncio.CancelledError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
|
||
|
|
app = FastAPI(
|
||
|
|
title="Status Monitor",
|
||
|
|
description="Service health monitoring",
|
||
|
|
lifespan=lifespan
|
||
|
|
)
|
||
|
|
|
||
|
|
templates = Jinja2Templates(directory="templates")
|
||
|
|
|
||
|
|
|
||
|
|
@app.get("/", response_class=HTMLResponse)
|
||
|
|
async def status_page(request: Request):
|
||
|
|
"""Main status page"""
|
||
|
|
services = monitor.get_all_statuses()
|
||
|
|
overall_status = monitor.get_overall_status()
|
||
|
|
|
||
|
|
return templates.TemplateResponse(
|
||
|
|
"index.html",
|
||
|
|
{
|
||
|
|
"request": request,
|
||
|
|
"services": services,
|
||
|
|
"overall_status": overall_status,
|
||
|
|
"last_check": monitor.last_check,
|
||
|
|
"check_interval": CHECK_INTERVAL
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@app.get("/api/status")
|
||
|
|
async def api_status():
|
||
|
|
"""API endpoint for service statuses"""
|
||
|
|
services = monitor.get_all_statuses()
|
||
|
|
overall_status = monitor.get_overall_status()
|
||
|
|
|
||
|
|
return {
|
||
|
|
"overall_status": overall_status,
|
||
|
|
"services": {name: status.to_dict() for name, status in services.items()},
|
||
|
|
"last_check": monitor.last_check.isoformat() if monitor.last_check else None,
|
||
|
|
"check_interval_seconds": CHECK_INTERVAL
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
@app.get("/api/health")
|
||
|
|
async def health():
|
||
|
|
"""Health check for this service"""
|
||
|
|
return {"status": "ok", "service": "status-monitor"}
|
||
|
|
|
||
|
|
|
||
|
|
@app.post("/api/refresh")
|
||
|
|
async def refresh_status():
|
||
|
|
"""Force refresh all service statuses"""
|
||
|
|
await monitor.check_all_services(
|
||
|
|
backend_url=BACKEND_URL,
|
||
|
|
frontend_url=FRONTEND_URL,
|
||
|
|
bot_url=BOT_URL
|
||
|
|
)
|
||
|
|
return {"status": "refreshed"}
|