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"}