feat: TurboSu initial release - racing telemetry dashboard
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.responses import HTMLResponse, FileResponse
|
||||
|
||||
from config.settings import get_config
|
||||
from server.api import router as api_router
|
||||
from server.websocket import ws_manager
|
||||
from server.telemetry.listener import telemetry_listener
|
||||
from utils.logger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
STATIC_DIR = BASE_DIR / "static"
|
||||
TEMPLATES_DIR = BASE_DIR / "templates"
|
||||
|
||||
os.makedirs(BASE_DIR / "logs", exist_ok=True)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
logger.info("=" * 50)
|
||||
logger.info(" TurboSu - Racing Telemetry Dashboard")
|
||||
logger.info("=" * 50)
|
||||
cfg = get_config()
|
||||
logger.info("Server config: %s:%d", cfg.get("server_host", "0.0.0.0"), cfg.get("server_port", 9527))
|
||||
await telemetry_listener.start()
|
||||
logger.info("Telemetry listener auto-started")
|
||||
yield
|
||||
telemetry_listener.stop()
|
||||
logger.info("TurboSu shutdown complete")
|
||||
|
||||
|
||||
app = FastAPI(title="TurboSu", version="1.0.0", lifespan=lifespan)
|
||||
|
||||
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
|
||||
|
||||
templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
|
||||
|
||||
|
||||
app.include_router(api_router)
|
||||
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def index(request: Request):
|
||||
return templates.TemplateResponse("index.html", {"request": request})
|
||||
|
||||
|
||||
@app.get("/dashboard/{theme_id}", response_class=HTMLResponse)
|
||||
async def dashboard_render(request: Request, theme_id: str):
|
||||
from models.dashboard import dashboard_manager
|
||||
theme = dashboard_manager.get(theme_id)
|
||||
template_html = dashboard_manager.get_template(theme_id)
|
||||
return templates.TemplateResponse("dashboard.html", {
|
||||
"request": request,
|
||||
"theme": theme,
|
||||
"template_html": template_html,
|
||||
})
|
||||
|
||||
|
||||
@app.get("/scene/{scene_id}", response_class=HTMLResponse)
|
||||
async def scene_render(request: Request, scene_id: str):
|
||||
from models.scene import scene_manager
|
||||
scene = scene_manager.get(scene_id)
|
||||
return templates.TemplateResponse("scene.html", {
|
||||
"request": request,
|
||||
"scene": scene,
|
||||
})
|
||||
|
||||
|
||||
@app.websocket("/ws")
|
||||
async def websocket_endpoint(ws: WebSocket):
|
||||
cid = await ws_manager.connect(ws)
|
||||
try:
|
||||
await ws.send_json({"type": "connected", "client_id": cid})
|
||||
while True:
|
||||
msg = await ws.receive_json()
|
||||
if msg.get("type") == "ping":
|
||||
await ws.send_json({"type": "pong"})
|
||||
except WebSocketDisconnect:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error("WS error: %s", e)
|
||||
finally:
|
||||
ws_manager.disconnect(cid)
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
cfg = get_config()
|
||||
uvicorn.run(
|
||||
"app:app",
|
||||
host=cfg.get("server_host", "0.0.0.0"),
|
||||
port=cfg.get("server_port", 9527),
|
||||
reload=False,
|
||||
log_level="info",
|
||||
)
|
||||
Reference in New Issue
Block a user