import logging import os from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from models.schemas import HealthCheck from routers import drives, enclosures, leds, overview from services.smart import sg_ses_available, smartctl_available logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", ) logger = logging.getLogger(__name__) app = FastAPI( title="JBOD Monitor", description="Drive health monitoring for JBOD enclosures", version="0.1.0", ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) app.include_router(enclosures.router) app.include_router(drives.router) app.include_router(leds.router) app.include_router(overview.router) _tool_status: dict[str, bool] = {} @app.on_event("startup") async def check_dependencies(): _tool_status["smartctl"] = smartctl_available() _tool_status["sg_ses"] = sg_ses_available() if not _tool_status["smartctl"]: logger.warning("smartctl not found — install smartmontools for SMART data") if not _tool_status["sg_ses"]: logger.warning("sg_ses not found — install sg3-utils for enclosure SES data") if os.geteuid() != 0: logger.warning("Not running as root — smartctl may fail on some devices") @app.get("/api/health", response_model=HealthCheck, tags=["health"]) async def health(): return HealthCheck(status="ok", tools=_tool_status) # Serve built frontend static files — mounted last so /api routes take priority. STATIC_DIR = Path(__file__).parent / "static" if STATIC_DIR.exists(): app.mount("/", StaticFiles(directory=STATIC_DIR, html=True), name="static")