import asyncio
from fastapi import FastAPI, Form, HTTPException, Request
from fastapi.responses import HTMLResponse, JSONResponse
import uvicorn
HTML_TEMPLATE = """
SmartWave Technician Portal
"""
def create_app(get_logs_cb, telemetry_cb, is_tech_mode_cb) -> FastAPI:
"""Factory creating FastAPI app with injected handler callbacks."""
app = FastAPI(title="Technician Portal")
# Attach callbacks to app.state
app.state.get_systemd_logs = get_logs_cb
app.state.handle_telemetry_request = telemetry_cb
app.state.is_tech_mode = is_tech_mode_cb
# Middleware to block traffic when TECHNICIAN_MODE is False
@app.middleware("http")
async def enforce_technician_mode(request: Request, call_next):
if not request.app.state.is_tech_mode():
return JSONResponse(
status_code=503,
content={"detail": "Technician mode is disabled."}
)
return await call_next(request)
@app.get("/", response_class=HTMLResponse)
async def serve_dashboard():
return HTML_TEMPLATE
@app.get("/api/logs")
async def api_get_logs(request: Request):
try:
logs = await request.app.state.get_systemd_logs(lines=200, service_name="smartwave")
return JSONResponse(content=logs)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/telemetry")
async def api_trigger_telemetry(request: Request, endpoint: str = Form(...)):
try:
await request.app.state.handle_telemetry_request(endpoint=endpoint, do_timeout=True)
return {"status": "ok", "message": f"Telemetry request sent to {endpoint}"}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Telemetry trigger failed: {str(e)}")
return app
async def start_technician_web_server(get_logs_cb, telemetry_cb, is_tech_mode_cb, host="192.168.50.1", port=8080):
"""Asynchronous server launcher meant to run within main.py's event loop."""
app = create_app(get_logs_cb, telemetry_cb, is_tech_mode_cb)
config = uvicorn.Config(app, host=host, port=port, log_level="info")
server = uvicorn.Server(config)
await server.serve()