from __future__ import annotations import json import uuid from typing import Any from fastapi import APIRouter, HTTPException, Request, UploadFile, File from fastapi.responses import JSONResponse from config.settings import get_config, update_config from models.dashboard import dashboard_manager, DashboardTheme from models.scene import scene_manager, Scene, SceneCanvas, DashboardPlacement from server.telemetry.listener import telemetry_listener from server.websocket import ws_manager from utils.logger import get_logger logger = get_logger(__name__) router = APIRouter(prefix="/api") # ---- Status ---- @router.get("/status") async def get_status(): latest = telemetry_listener.latest_data cfg = get_config() return { "server_running": True, "telemetry_running": telemetry_listener.is_running, "ws_clients": ws_manager.client_count, "packet_count": telemetry_listener.packet_count, "last_packet_time": telemetry_listener.last_packet_time, "selected_game_id": cfg.get("selected_game_id"), "latest_data": latest.to_dict() if latest else None, } # ---- Config ---- @router.get("/config") async def api_get_config(): return get_config() @router.put("/config") async def api_update_config(data: dict[str, Any]): cfg = get_config() for k, v in data.items(): cfg[k] = v from config.settings import save_config save_config(cfg) if "selected_game_id" in data: telemetry_listener.set_parser_for_game(data["selected_game_id"]) if "theme" in data: cfg["theme"] = data["theme"] return {"ok": True} # ---- Telemetry ---- @router.post("/telemetry/start") async def api_start_telemetry(): ok = await telemetry_listener.start() return {"ok": ok, "running": telemetry_listener.is_running} @router.post("/telemetry/stop") async def api_stop_telemetry(): telemetry_listener.stop() return {"ok": True, "running": telemetry_listener.is_running} @router.get("/telemetry/latest") async def api_latest_telemetry(): latest = telemetry_listener.latest_data if latest: return { "data": latest.to_dict(), "raw": latest.raw, } return {"data": None, "raw": None} # ---- Dashboards ---- @router.get("/dashboards") async def api_list_dashboards(category: str = "all"): return dashboard_manager.list_all(category) @router.get("/dashboards/categories") async def api_dashboard_categories(): return dashboard_manager.get_categories() @router.get("/dashboards/{theme_id}") async def api_get_dashboard(theme_id: str): theme = dashboard_manager.get(theme_id) if not theme: raise HTTPException(404, "Dashboard not found") return theme @router.get("/dashboards/{theme_id}/template") async def api_get_dashboard_template(theme_id: str): tmpl = dashboard_manager.get_template(theme_id) return {"html": tmpl} @router.post("/dashboards") async def api_create_dashboard(data: dict[str, Any]): theme = DashboardTheme( name=data.get("name", "Untitled"), category=data.get("category", "basic"), description=data.get("description", ""), author=data.get("author", ""), config=data.get("config", {}), ) dashboard_manager.save(theme) if data.get("template_html"): dashboard_manager._save_template_file(theme.id, data["template_html"]) return theme.to_dict() @router.put("/dashboards/{theme_id}") async def api_update_dashboard(theme_id: str, data: dict[str, Any]): existing = dashboard_manager.get(theme_id) if not existing: raise HTTPException(404, "Dashboard not found") theme = DashboardTheme.from_dict(existing) for k in ["name", "category", "description", "author", "config"]: if k in data: setattr(theme, k, data[k]) dashboard_manager.save(theme) if "template_html" in data: dashboard_manager._save_template_file(theme.id, data["template_html"]) return theme.to_dict() @router.delete("/dashboards/{theme_id}") async def api_delete_dashboard(theme_id: str): ok = dashboard_manager.delete(theme_id) return {"ok": ok} @router.get("/dashboards/{theme_id}/export") async def api_export_dashboard(theme_id: str): result = dashboard_manager.export_theme(theme_id) if not result: raise HTTPException(404, "Dashboard not found") return result @router.post("/dashboards/import") async def api_import_dashboard(data: dict[str, Any]): ok = dashboard_manager.import_theme(data) return {"ok": ok} # ---- Scenes ---- @router.get("/scenes") async def api_list_scenes(game_id: str = ""): return scene_manager.list_all(game_id or None) @router.get("/scenes/{scene_id}") async def api_get_scene(scene_id: str): scene = scene_manager.get(scene_id) if not scene: raise HTTPException(404, "Scene not found") return scene @router.post("/scenes") async def api_create_scene(data: dict[str, Any]): scene = Scene( name=data.get("name", "New Scene"), game_id=data.get("game_id", ""), description=data.get("description", ""), canvases=[SceneCanvas(label="16:9", width=1920, height=1080)], ) if data.get("canvases"): scene.canvases = [ SceneCanvas( width=c.get("width", 1920), height=c.get("height", 1080), label=c.get("label", ""), placements=[DashboardPlacement(**p) for p in c.get("placements", [])], ) for c in data["canvases"] ] scene_manager.save(scene) return scene.to_dict() @router.put("/scenes/{scene_id}") async def api_update_scene(scene_id: str, data: dict[str, Any]): existing = scene_manager.get(scene_id) if not existing: raise HTTPException(404, "Scene not found") scene = Scene.from_dict(existing) for k in ["name", "game_id", "description"]: if k in data: setattr(scene, k, data[k]) if "canvases" in data: scene.canvases = [ SceneCanvas( width=c.get("width", 1920), height=c.get("height", 1080), label=c.get("label", "Custom"), placements=[DashboardPlacement(**p) for p in c.get("placements", [])], ) for c in data["canvases"] ] scene_manager.save(scene) return scene.to_dict() @router.delete("/scenes/{scene_id}") async def api_delete_scene(scene_id: str): ok = scene_manager.delete(scene_id) return {"ok": ok} @router.get("/scenes/{scene_id}/export") async def api_export_scene(scene_id: str): result = scene_manager.export_scene(scene_id) if not result: raise HTTPException(404, "Scene not found") return result @router.post("/scenes/import") async def api_import_scene(data: dict[str, Any]): ok = scene_manager.import_scene(data) return {"ok": ok} # ---- Game Plugins ---- from server.game_manager import game_plugin_manager @router.get("/games") async def api_list_games(): return game_plugin_manager.list_all() @router.get("/games/{plugin_id}") async def api_get_game(plugin_id: str): gp = game_plugin_manager.get(plugin_id) if not gp: raise HTTPException(404, "Game plugin not found") return gp.to_dict() @router.post("/games/install") async def api_install_game_plugin(data: dict[str, Any]): ok = game_plugin_manager.install_plugin(data) return {"ok": ok} @router.delete("/games/{plugin_id}") async def api_remove_game_plugin(plugin_id: str): ok = game_plugin_manager.remove_plugin(plugin_id) return {"ok": ok} @router.get("/games/{plugin_id}/export") async def api_export_game_plugin(plugin_id: str): result = game_plugin_manager.export_plugin(plugin_id) if not result: raise HTTPException(404, "Game plugin not found") return result @router.post("/games/reload") async def api_reload_game_plugins(): game_plugin_manager.reload() return {"ok": True, "count": len(game_plugin_manager.list_all())}