diff --git a/moderation/backend-py/websocket_server.py b/moderation/backend-py/websocket_server.py index d83b805..a214d94 100644 --- a/moderation/backend-py/websocket_server.py +++ b/moderation/backend-py/websocket_server.py @@ -19,7 +19,9 @@ sio = socketio.AsyncServer( logger=True, # Включить логирование для отладки engineio_logger=True, # Включить логирование Engine.IO ping_timeout=60, - ping_interval=25 + ping_interval=25, + allow_upgrades=True, # Разрешить upgrade с polling на websocket + transports=['polling', 'websocket'] # Поддерживаемые транспорты ) # Track connected moderators @@ -38,39 +40,26 @@ def broadcast_online(): sio.emit('online', online_list, namespace='/mod-chat') -# Создаем класс для корневого namespace -class RootNamespace(socketio.AsyncNamespace): - """Root namespace handler for Socket.IO handshake""" - async def on_connect(self, sid, environ): - """Handle client connection to root namespace (Socket.IO handshake)""" - print(f"[WebSocket] 🔄 Handshake to ROOT namespace: {sid}") - print(f"[WebSocket] Environ type: {type(environ)}") - if environ: - print(f"[WebSocket] Environ keys: {list(environ.keys()) if isinstance(environ, dict) else 'N/A'}") - logger.info(f"[WebSocket] Handshake to ROOT namespace: {sid}") - # Разрешаем подключение для handshake - # В AsyncNamespace не нужно возвращать True явно, но можно - return True - - async def on_disconnect(self, sid): - """Handle disconnection from root namespace""" - print(f"[WebSocket] Client disconnected from ROOT namespace: {sid}") - logger.info(f"[WebSocket] Client disconnected from ROOT namespace: {sid}") - -# Регистрируем корневой namespace ПЕРЕД созданием ASGI app -root_ns = RootNamespace('/') -sio.register_namespace(root_ns) -print(f"[WebSocket] ✅ Корневой namespace зарегистрирован: {root_ns.namespace}") - -# Также добавляем обработчик через декоратор для надежности -@sio.on('connect', namespace='/') -async def on_connect_root_decorator(sid, environ): - """Handle client connection to root namespace (Socket.IO handshake) - декоратор""" - print(f"[WebSocket] 🔄 Handshake to ROOT namespace (decorator): {sid}") - logger.info(f"[WebSocket] Handshake to ROOT namespace (decorator): {sid}") +# Обработчик для корневого namespace (требуется для Socket.IO handshake) +# В python-socketio для ASGI режима нужно использовать декоратор БЕЗ явного namespace='/' +# или использовать namespace=None для корневого namespace +@sio.on('connect') +async def on_connect_root(sid, environ): + """Handle client connection to root namespace (Socket.IO handshake)""" + print(f"[WebSocket] 🔄 Handshake to ROOT namespace: {sid}") + print(f"[WebSocket] Environ type: {type(environ)}") + if environ: + print(f"[WebSocket] Environ keys: {list(environ.keys()) if isinstance(environ, dict) else 'N/A'}") + logger.info(f"[WebSocket] Handshake to ROOT namespace: {sid}") # Разрешаем подключение для handshake return True +@sio.on('disconnect') +async def on_disconnect_root(sid): + """Handle disconnection from root namespace""" + print(f"[WebSocket] Client disconnected from ROOT namespace: {sid}") + logger.info(f"[WebSocket] Client disconnected from ROOT namespace: {sid}") + # Namespace handlers for /mod-chat @sio.on('connect', namespace='/mod-chat') async def on_connect(sid, environ): diff --git a/moderation/frontend/src/App.jsx b/moderation/frontend/src/App.jsx index 57d89ee..c8d68f7 100644 --- a/moderation/frontend/src/App.jsx +++ b/moderation/frontend/src/App.jsx @@ -653,22 +653,27 @@ export default function App() { hasUsername: !!user.username, hasTelegramId: !!user.telegramId }); - // Socket.IO подключается к /socket.io, но использует namespace /mod-chat - // В production Socket.IO слушает на /socket.io, namespace указывается отдельно - const socketUrl = socketBase.endsWith('/socket.io') ? socketBase : `${socketBase}/socket.io`; - console.log('[Chat] Подключение к Socket.IO:', socketUrl); + // Способ 1: Подключение с явным указанием path и namespace + const socketUrl = socketBase.endsWith('/socket.io') + ? socketBase.replace('/socket.io', '') + : socketBase; + + console.log('[Chat] Подключение к Socket.IO base URL:', socketUrl); console.log('[Chat] Использование namespace: /mod-chat'); - // Подключаемся к корневому URL, но указываем namespace в опциях + // Подключаемся к base URL с указанием path и namespace const socket = io(socketUrl, { path: '/socket.io', namespace: '/mod-chat', - transports: ['websocket', 'polling'], + transports: ['polling', 'websocket'], // Пробуем сначала polling, потом websocket reconnection: true, reconnectionDelay: 1000, - reconnectionAttempts: 5, - timeout: 10000, - forceNew: false + reconnectionAttempts: 10, + timeout: 20000, + forceNew: true, // Принудительно новое соединение + autoConnect: true, + upgrade: true, + rememberUpgrade: false }); socket.on('connect', () => {