Fixed WebSocket message validation by ensuring all messages include data field
- Updated server to send status_ok messages with data field instead of direct timestamp - Modified voicebot _send_message to always include data field (empty dict if no data) - Fixed all error message formats in server to use proper {type, data: {error}} structure - Updated client join message to include empty data field for consistency - All WebSocket messages now comply with WebSocketMessageModel requiring data field - Resolved Pydantic validation errors for status_ok and error messages
This commit is contained in:
parent
a3b9e7fa39
commit
bd5e5e4d8f
@ -953,7 +953,7 @@ const MediaAgent = (props: MediaAgentProps) => {
|
||||
if (media && joinStatus.status === "Not joined" && readyState === ReadyState.OPEN) {
|
||||
console.log(`media-agent - Initiating media join for ${session.name}`);
|
||||
setJoinStatus({ status: "Joining" });
|
||||
sendJsonMessage({ type: "join" });
|
||||
sendJsonMessage({ type: "join", data: {} });
|
||||
}
|
||||
}, [media, joinStatus.status, sendJsonMessage, readyState, session.name]);
|
||||
|
||||
|
@ -1727,13 +1727,13 @@ async def lobby_join(
|
||||
await websocket.accept()
|
||||
if lobby_id is None:
|
||||
await websocket.send_json(
|
||||
{"type": "error", "error": "Invalid or missing lobby"}
|
||||
{"type": "error", "data": {"error": "Invalid or missing lobby"}}
|
||||
)
|
||||
await websocket.close()
|
||||
return
|
||||
if session_id is None:
|
||||
await websocket.send_json(
|
||||
{"type": "error", "error": "Invalid or missing session"}
|
||||
{"type": "error", "data": {"error": "Invalid or missing session"}}
|
||||
)
|
||||
await websocket.close()
|
||||
return
|
||||
@ -1741,7 +1741,7 @@ async def lobby_join(
|
||||
if not session:
|
||||
# logger.error(f"Invalid session ID {session_id}")
|
||||
await websocket.send_json(
|
||||
{"type": "error", "error": f"Invalid session ID {session_id}"}
|
||||
{"type": "error", "data": {"error": f"Invalid session ID {session_id}"}}
|
||||
)
|
||||
await websocket.close()
|
||||
return
|
||||
@ -1750,7 +1750,7 @@ async def lobby_join(
|
||||
try:
|
||||
lobby = getLobby(lobby_id)
|
||||
except Exception as e:
|
||||
await websocket.send_json({"type": "error", "error": str(e)})
|
||||
await websocket.send_json({"type": "error", "data": {"error": str(e)}})
|
||||
await websocket.close()
|
||||
return
|
||||
|
||||
@ -1815,7 +1815,7 @@ async def lobby_join(
|
||||
data: dict[str, Any] | None = packet.get("data", None)
|
||||
if not type:
|
||||
logger.error(f"{session.getName()} - Invalid request: {packet}")
|
||||
await websocket.send_json({"type": "error", "error": "Invalid request"})
|
||||
await websocket.send_json({"type": "error", "data": {"error": "Invalid request"}})
|
||||
continue
|
||||
# logger.info(f"{session.getName()} <- RAW Rx: {data}")
|
||||
match type:
|
||||
@ -1823,7 +1823,7 @@ async def lobby_join(
|
||||
if not data:
|
||||
logger.error(f"{session.getName()} - set_name missing data")
|
||||
await websocket.send_json(
|
||||
{"type": "error", "error": "set_name missing data"}
|
||||
{"type": "error", "data": {"error": "set_name missing data"}}
|
||||
)
|
||||
continue
|
||||
name = data.get("name")
|
||||
@ -1832,7 +1832,7 @@ async def lobby_join(
|
||||
if not name:
|
||||
logger.error(f"{session.getName()} - Name required")
|
||||
await websocket.send_json(
|
||||
{"type": "error", "error": "Name required"}
|
||||
{"type": "error", "data": {"error": "Name required"}}
|
||||
)
|
||||
continue
|
||||
# Name takeover / password logic
|
||||
@ -1868,7 +1868,7 @@ async def lobby_join(
|
||||
f"{session.getName()} - Name already taken (no password set)"
|
||||
)
|
||||
await websocket.send_json(
|
||||
{"type": "error", "error": "Name already taken"}
|
||||
{"type": "error", "data": {"error": "Name already taken"}}
|
||||
)
|
||||
continue
|
||||
|
||||
@ -2030,7 +2030,7 @@ async def lobby_join(
|
||||
f"{session.getName()} - relayICECandidate missing data"
|
||||
)
|
||||
await websocket.send_json(
|
||||
{"type": "error", "error": "relayICECandidate missing data"}
|
||||
{"type": "error", "data": {"error": "relayICECandidate missing data"}}
|
||||
)
|
||||
continue
|
||||
|
||||
@ -2043,7 +2043,7 @@ async def lobby_join(
|
||||
f"{session.short}:{session.name} <- relayICECandidate - Not an RTC peer ({session.id})"
|
||||
)
|
||||
await websocket.send_json(
|
||||
{"type": "error", "error": "Not joined to lobby"}
|
||||
{"type": "error", "data": {"error": "Not joined to lobby"}}
|
||||
)
|
||||
continue
|
||||
session_peers = session.lobby_peers[lobby.id]
|
||||
@ -2109,7 +2109,7 @@ async def lobby_join(
|
||||
f"{session.short}:{session.name} <- relaySessionDescription - Not an RTC peer ({session.id})"
|
||||
)
|
||||
await websocket.send_json(
|
||||
{"type": "error", "error": "Not joined to lobby"}
|
||||
{"type": "error", "data": {"error": "Not joined to lobby"}}
|
||||
)
|
||||
continue
|
||||
|
||||
@ -2168,7 +2168,7 @@ async def lobby_join(
|
||||
# Simple status check - just respond with success to keep connection alive
|
||||
logger.debug(f"{session.getName()} <- status_check")
|
||||
await websocket.send_json(
|
||||
{"type": "status_ok", "timestamp": time.time()}
|
||||
{"type": "status_ok", "data": {"timestamp": time.time()}}
|
||||
)
|
||||
|
||||
case _:
|
||||
|
@ -194,7 +194,7 @@ class WebSocketMessageModel(BaseModel):
|
||||
"""Base model for all WebSocket messages"""
|
||||
|
||||
type: str
|
||||
data: (
|
||||
data: Optional[
|
||||
JoinStatusModel
|
||||
| UserJoinedModel
|
||||
| LobbyStateModel
|
||||
@ -209,7 +209,7 @@ class WebSocketMessageModel(BaseModel):
|
||||
| ChatMessagesListModel
|
||||
| WebSocketErrorModel
|
||||
| Dict[str, str]
|
||||
) # Generic dict for simple messages
|
||||
] = None # Generic dict for simple messages, optional for status messages
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
@ -4,8 +4,8 @@ import logging
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
|
||||
#logging_level = os.getenv("LOGGING_LEVEL", "INFO").upper()
|
||||
logging_level = os.getenv("LOGGING_LEVEL", "DEBUG").upper()
|
||||
logging_level = os.getenv("LOGGING_LEVEL", "INFO").upper()
|
||||
#logging_level = os.getenv("LOGGING_LEVEL", "DEBUG").upper()
|
||||
|
||||
|
||||
class RelativePathFormatter(logging.Formatter):
|
||||
|
@ -469,9 +469,11 @@ class WebRTCSignalingClient:
|
||||
ws = cast(WebSocketProtocol, self.websocket)
|
||||
|
||||
# Build message with explicit type to avoid type narrowing
|
||||
message: dict[str, object] = {"type": message_type}
|
||||
if data is not None:
|
||||
message["data"] = data
|
||||
# Always include data field to match WebSocketMessageModel
|
||||
message: dict[str, object] = {
|
||||
"type": message_type,
|
||||
"data": data if data is not None else {}
|
||||
}
|
||||
|
||||
try:
|
||||
logger.debug(f"_send_message: Sending {message_type} with data: {data}")
|
||||
@ -563,6 +565,9 @@ class WebRTCSignalingClient:
|
||||
)
|
||||
|
||||
if msg_type == "addPeer":
|
||||
if data is None:
|
||||
logger.error("addPeer message missing required data")
|
||||
return
|
||||
try:
|
||||
validated = AddPeerModel.model_validate(data)
|
||||
except ValidationError as e:
|
||||
@ -623,11 +628,11 @@ class WebRTCSignalingClient:
|
||||
logger.info(f"Received update message: {validated}")
|
||||
elif msg_type == "status_check":
|
||||
# Handle status check messages - these are used to verify connection
|
||||
logger.debug(f"Received status check message: {data}")
|
||||
logger.debug(f"Received status check message with data: {data}")
|
||||
# No special processing needed for status checks, just acknowledge receipt
|
||||
elif msg_type == "status_ok":
|
||||
# Handle status_ok response from server
|
||||
logger.debug(f"Received status_ok from server: {data}")
|
||||
logger.debug(f"Received status_ok from server with data: {data}")
|
||||
# This confirms the connection is healthy
|
||||
elif msg_type == "chat_message":
|
||||
logger.info("Received chat message")
|
||||
|
Loading…
x
Reference in New Issue
Block a user