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