diff --git a/packages/gateway/config/kong.yml b/packages/gateway/config/kong.yml index a219503..7e4b8fd 100644 --- a/packages/gateway/config/kong.yml +++ b/packages/gateway/config/kong.yml @@ -186,7 +186,14 @@ plugins: - exp - name: jwt - service: voice-service + route: voice-api + config: + key_claim_name: kid + claims_to_verify: + - exp + + - name: jwt + route: twilio-webhook config: key_claim_name: kid claims_to_verify: diff --git a/packages/services/voice-service/src/api/main.py b/packages/services/voice-service/src/api/main.py index 2842d10..f4a3be4 100644 --- a/packages/services/voice-service/src/api/main.py +++ b/packages/services/voice-service/src/api/main.py @@ -31,6 +31,21 @@ app.include_router(session_router, prefix="/api/v1/voice", tags=["sessions"]) app.include_router(twilio_router, prefix="/api/v1/twilio", tags=["twilio"]) +# --------------------------------------------------------------------------- +# Top-level WebSocket route for Kong's voice-ws route (/ws/voice/*) +# --------------------------------------------------------------------------- +# Kong forwards the full path /ws/voice/{session_id} without JWT. +# We import the handler from session_router and re-expose it here so the +# path matches exactly. +from fastapi import WebSocket as _WS # noqa: E402 + +@app.websocket("/ws/voice/{session_id}") +async def voice_ws_proxy(websocket: _WS, session_id: str): + """Proxy route that delegates to the session_router WebSocket handler.""" + from .session_router import voice_websocket + await voice_websocket(websocket, session_id) + + async def _session_cleanup_loop(): """Periodically remove sessions that have been disconnected longer than session_ttl. diff --git a/packages/services/voice-service/src/api/session_router.py b/packages/services/voice-service/src/api/session_router.py index d81c4c9..6915903 100644 --- a/packages/services/voice-service/src/api/session_router.py +++ b/packages/services/voice-service/src/api/session_router.py @@ -83,7 +83,7 @@ async def create_session(request: CreateSessionRequest, req: Request): "task": None, } - websocket_url = f"/api/v1/voice/ws/{session_id}" + websocket_url = f"/ws/voice/{session_id}" return SessionResponse( session_id=session_id, @@ -155,7 +155,7 @@ async def reconnect_session(session_id: str, req: Request): content={"error": "Session expired", "session_id": session_id}, ) - websocket_url = f"/api/v1/voice/ws/{session_id}" + websocket_url = f"/ws/voice/{session_id}" return SessionResponse( session_id=session_id, status="disconnected",