from __future__ import annotations from contextlib import asynccontextmanager from pathlib import Path from typing import Any from fastapi import FastAPI, HTTPException from fastapi.responses import FileResponse, HTMLResponse from fastapi.staticfiles import StaticFiles from pydantic import BaseModel from app.service import KvmSwitcherService PROJECT_ROOT = Path(__file__).resolve().parent.parent STATIC_DIR = PROJECT_ROOT / "static" INDEX_PATH = STATIC_DIR / "index.html" class SettingsPayload(BaseModel): device_port: str def create_app( service: KvmSwitcherService | None = None, manage_lifecycle: bool = True, ) -> FastAPI: active_service = service or KvmSwitcherService() lifespan = None if manage_lifecycle: @asynccontextmanager async def lifespan(app_instance: FastAPI): del app_instance active_service.start() try: yield finally: active_service.stop() app = FastAPI(title="Internal KVM Switch", version="0.1.0", lifespan=lifespan) app.state.kvm_service = active_service if STATIC_DIR.exists(): app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") @app.get("/") async def root() -> Any: if INDEX_PATH.exists(): return FileResponse(INDEX_PATH) return HTMLResponse("
Dashboard file not found.
") @app.get("/api/status") async def get_status() -> dict[str, Any]: return active_service.get_status() @app.post("/api/settings") async def post_settings(payload: SettingsPayload) -> dict[str, Any]: try: return active_service.save_settings( device_port=payload.device_port, ) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return app app = create_app()