fix(chat): 修复多个聊天错误 消息格式错误导致断连、新消息会通知到发件人、加强消息发送的成员验证

This commit is contained in:
xingc
2025-05-14 21:19:46 +08:00
parent 854f5bcbea
commit 99f15cff84
4 changed files with 33 additions and 12 deletions

View File

@@ -14,6 +14,7 @@ from core.db.models import ReadStatus, User, Chat, Message
from .managers import SocketManager from .managers import SocketManager
from . import schema from . import schema
from .services import get_chat
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
socket_manager = SocketManager() socket_manager = SocketManager()
@@ -28,7 +29,8 @@ async def send_new_message(
incoming_message: schema.WSMessage, incoming_message: schema.WSMessage,
): ):
chat_guid: str = str(incoming_message.chat_guid) chat_guid: str = str(incoming_message.chat_guid)
chat: Chat | None = await CRUD(db_session, Chat).get(filters={'guid': chat_guid}) # chat: Chat | None = await CRUD(db_session, Chat).get(filters={'guid': chat_guid, 'is_deleted': False}
chat: Chat | None = await get_chat(chat_guid, current_user, db_session)
# 判断是否为新聊天 # 判断是否为新聊天
if not chats or chat_guid not in chats: if not chats or chat_guid not in chats:
@@ -56,8 +58,6 @@ async def send_new_message(
await db_session.flush() await db_session.flush()
await db_session.commit() await db_session.commit()
# await db_session.refresh(message, attribute_names=["user", "chat"])
# await db_session.refresh(chat, attribute_names=["users"])
except Exception as exc_info: except Exception as exc_info:
await db_session.rollback() await db_session.rollback()

View File

@@ -11,6 +11,7 @@ import redis.asyncio as aioredis
import ujson import ujson
from fastapi import WebSocket from fastapi import WebSocket
from fastapi.websockets import WebSocketState from fastapi.websockets import WebSocketState
from simple_spider_tool import jsonpath
from core.db.engine import get_cache from core.db.engine import get_cache
@@ -104,7 +105,9 @@ class SocketManager:
if socket.client_state != WebSocketState.CONNECTED: if socket.client_state != WebSocketState.CONNECTED:
continue continue
data = message['data'] data = message['data']
await socket.send_text(data) if f',"user_id":{socket.user_id}' in data:
continue
await socket.send_json(data)
else: else:
if self._task_cancel is False: if self._task_cancel is False:
break break

View File

@@ -239,6 +239,7 @@ async def get_chat_websocket(
await socket_manager.connect_socket(websocket=websocket) await socket_manager.connect_socket(websocket=websocket)
logger.info(f'Websocket connection is established: {login_user.user_id}') logger.info(f'Websocket connection is established: {login_user.user_id}')
user_id = str(login_user.user_id) user_id = str(login_user.user_id)
websocket.user_id = login_user.user_id
await socket_manager.add_user_socket_connection(user_id, websocket) await socket_manager.add_user_socket_connection(user_id, websocket)
ratelimit = WebSocketRateLimiter(times=50, seconds=10, callback=websocket_callback) ratelimit = WebSocketRateLimiter(times=50, seconds=10, callback=websocket_callback)
@@ -258,13 +259,13 @@ async def get_chat_websocket(
try: try:
message = schema.WSMessage(**incoming_message) message = schema.WSMessage(**incoming_message)
except ValueError: except ValueError:
await socket_manager.send_error(websocket, 'The message type or format you sent is invalid') await socket_manager.send_error('The message type or format you sent is invalid', websocket)
continue continue
handler = socket_manager.handlers.get(message.type) handler = socket_manager.handlers.get(message.type)
if not handler: if not handler:
logger.error(f'No handler [{message.type}] exists') logger.error(f'No handler [{message.type}] exists')
await socket_manager.send_error(websocket, f'Type: {message.type} was not found') await socket_manager.send_error(f'Type: {message.type} was not found', websocket)
continue continue
await handler( await handler(
@@ -277,14 +278,14 @@ async def get_chat_websocket(
except (JSONDecodeError, AttributeError) as exc: except (JSONDecodeError, AttributeError) as exc:
logger.exception(f'Websocket error, detail: {exc}') logger.exception(f'Websocket error, detail: {exc}')
await websocket.send_json({'status': 'error', 'message': 'Wrong message format'}) await socket_manager.send_error('Wrong message format', websocket)
except ValueError as exc: except ValueError as exc:
logger.exception(f'Websocket error, detail: {exc}') logger.exception(f'Websocket error, detail: {exc}')
await websocket.send_json({'status': 'error', 'message': 'Could not validate incoming message'}) await socket_manager.send_error('Could not validate incoming message', websocket)
except WebsocketTooManyRequests: except WebsocketTooManyRequests:
logger.exception(f'User: {login_user} sent too many ws requests') logger.exception(f'User: {login_user} sent too many ws requests')
await websocket.send_json({'status': 'error', 'message': 'You have sent too many requests'}) await socket_manager.send_error('You have sent too many requests', websocket)
except WebSocketDisconnect: except WebSocketDisconnect:
logging.info(f'Websocket is disconnected: {login_user.user_id}') logging.info(f'Websocket is disconnected: {login_user.user_id}')

View File

@@ -11,12 +11,10 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import aliased, selectinload from sqlalchemy.orm import aliased, selectinload
from core.base.exceptions import WebsocketTooManyRequests from core.base.exceptions import WebsocketTooManyRequests
from core.db.models import User, Chat, ReadStatus, Message from core.db.models import User, Chat, ReadStatus, Message, chat_participant
from core.routers.auth.schema import LoginUser from core.routers.auth.schema import LoginUser
from core.utils.helper import format_date from core.utils.helper import format_date
from .schema import ChatUnreadOut
async def websocket_callback(ws, pexpire): async def websocket_callback(ws, pexpire):
raise WebsocketTooManyRequests("Too many requests") raise WebsocketTooManyRequests("Too many requests")
@@ -91,3 +89,22 @@ async def get_unread_messages_per_chat(
)) ))
return results return results
async def get_chat(chat_guid, login_user: User, db_session: AsyncSession) -> Chat | None:
subquery = (
select(chat_participant.c.chat_id)
.where(chat_participant.c.user_id == login_user.user_id)
).scalar_subquery()
query = (
select(Chat)
.where(and_(
Chat.id.in_(subquery),
Chat.guid == chat_guid,
Chat.is_deleted == False
))
)
result = await db_session.execute(query)
await db_session.commit()
return result.scalar_one_or_none()