"""Token server + sessions REST API — FastAPI.""" import asyncio import os from datetime import timedelta import uvicorn from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from livekit import api as livekit_api from pydantic import BaseModel import store LIVEKIT_API_KEY = os.environ["LIVEKIT_API_KEY"] LIVEKIT_API_SECRET = os.environ["LIVEKIT_API_SECRET"] LIVEKIT_PUBLIC_URL = os.getenv("LIVEKIT_PUBLIC_URL", "wss://voice.hobbs.farm") app = FastAPI(title="VoiceCoach API") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) class TokenRequest(BaseModel): identity: str = "jeremy" @app.on_event("startup") async def startup(): await store.init_db() @app.get("/health") async def health(): return {"status": "ok", "version": "2.0"} @app.post("/api/token") async def get_token(req: TokenRequest): token = livekit_api.AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET) token.with_grants(livekit_api.VideoGrants( room_join=True, room="voicecoach", can_publish=True, can_subscribe=True, )) token.identity = req.identity token.ttl = timedelta(hours=8) return {"token": token.to_jwt(), "url": LIVEKIT_PUBLIC_URL} @app.get("/api/sessions") async def list_sessions(limit: int = 20): return await store.get_sessions(limit=limit) @app.get("/api/sessions/{session_id}/messages") async def get_messages(session_id: str): msgs = await store.get_session_messages(session_id) if not msgs: raise HTTPException(status_code=404, detail="Session not found") return msgs @app.get("/api/profile/{key}") async def get_profile(key: str): value = await store.get_profile(key) if value is None: raise HTTPException(status_code=404, detail="Key not found") return {"key": key, "value": value} @app.put("/api/profile/{key}") async def set_profile_key(key: str, body: dict): await store.set_profile(key, str(body.get("value", ""))) return {"ok": True} def run(): uvicorn.run(app, host="0.0.0.0", port=8080, log_level="info")