From fa971e1d24f39550b4d1e176ee494285428fbc2a Mon Sep 17 00:00:00 2001 From: akhdanre Date: Mon, 5 May 2025 19:01:52 +0700 Subject: [PATCH] feat; websocket preparation --- app/blueprints/socket.py | 0 app/controllers/__init__.py | 2 + app/controllers/socket_conroller.py | 82 +++++++++++++++++++++++++++++ app/main.py | 27 ++++++---- 4 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 app/blueprints/socket.py create mode 100644 app/controllers/socket_conroller.py diff --git a/app/blueprints/socket.py b/app/blueprints/socket.py new file mode 100644 index 0000000..e69de29 diff --git a/app/controllers/__init__.py b/app/controllers/__init__.py index 02844fe..abf1a74 100644 --- a/app/controllers/__init__.py +++ b/app/controllers/__init__.py @@ -3,6 +3,7 @@ from .user_controller import UserController from .quiz_controller import QuizController from .history_controller import HistoryController from .subject_controller import SubjectController +from .socket_conroller import SocketController __all__ = [ "AuthController", @@ -10,4 +11,5 @@ __all__ = [ "QuizController", "HistoryController", "SubjectController", + "SocketController", ] diff --git a/app/controllers/socket_conroller.py b/app/controllers/socket_conroller.py new file mode 100644 index 0000000..370f8a1 --- /dev/null +++ b/app/controllers/socket_conroller.py @@ -0,0 +1,82 @@ +from flask_socketio import SocketIO, emit, join_room, leave_room +from flask import request + + +class SocketController: + def __init__(self, socketio: SocketIO): + self.socketio = socketio + self.rooms = {} # room_name -> set of sids + self._register_events() + + def _register_events(self): + @self.socketio.on("connect") + def on_connect(): + print(f"Client connected: {request.sid}") + emit("connection_response", {"status": "connected", "sid": request.sid}) + + @self.socketio.on("disconnect") + def on_disconnect(): + sid = request.sid + # Remove user from all rooms they joined + for room, members in self.rooms.items(): + if sid in members: + members.remove(sid) + emit( + "room_message", + {"message": f"A user has disconnected.", "room": room}, + room=room, + ) + print(f"Client disconnected: {sid}") + + @self.socketio.on("join_room") + def handle_join_room(data): + if not isinstance(data, dict): + emit("error", {"message": "Invalid data format"}) + return + + room = data.get("room") + username = data.get("username", "anonymous") + sid = request.sid + + # Create room if it doesn't exist + if room not in self.rooms: + self.rooms[room] = set() + + # Check if room has space + if len(self.rooms[room]) >= 2: + emit("room_full", {"message": "Room is full. Max 2 users allowed."}) + return + + # Join room + self.rooms[room].add(sid) + join_room(room) + emit( + "room_message", + {"message": f"{username} has joined the room.", "room": room}, + room=room, + ) + + @self.socketio.on("leave_room") + def handle_leave_room(data): + room = data.get("room") + username = data.get("username", "anonymous") + sid = request.sid + + if room in self.rooms and sid in self.rooms[room]: + self.rooms[room].remove(sid) + + leave_room(room) + print(f"{username} left room {room}") + emit( + "room_message", + {"message": f"{username} has left the room.", "room": room}, + room=room, + ) + + @self.socketio.on("send_message") + def on_send_message(data): + room = data.get("room") + message = data.get("message") + username = data.get("username", "anonymous") + print(f"[{room}] {username}: {message}") + emit("receive_message", {"message": message, "from": username}, room=room) diff --git a/app/main.py b/app/main.py index 6ce5145..5cb723c 100644 --- a/app/main.py +++ b/app/main.py @@ -1,11 +1,17 @@ +import eventlet + +eventlet.monkey_patch() + import sys import os +import logging +from flask import Flask, request +from flask_socketio import SocketIO sys.path.append(os.path.dirname(__file__)) from di_container import Container from configs import Config, LoggerConfig -from flask import Flask from blueprints import ( auth_blueprint, user_blueprint, @@ -13,9 +19,13 @@ from blueprints import ( default_blueprint, history_blueprint, subject_blueprint, + socket, ) from database import init_db -import logging +from controllers import SocketController + + +socketio = SocketIO(cors_allowed_origins="*") def createApp() -> Flask: @@ -28,13 +38,15 @@ def createApp() -> Flask: LoggerConfig.init_logger(app) container = Container() - app.container = container mongo = init_db(app) if mongo is not None: container.mongo.override(mongo) + SocketController(socketio) + socketio.init_app(app) + container.wire( modules=[ "blueprints.auth", @@ -45,7 +57,6 @@ def createApp() -> Flask: ] ) - # Register Blueprints app.register_blueprint(default_blueprint) app.register_blueprint(auth_blueprint, url_prefix="/api") app.register_blueprint(user_blueprint, url_prefix="/api") @@ -53,14 +64,10 @@ def createApp() -> Flask: app.register_blueprint(history_blueprint, url_prefix="/api/history") app.register_blueprint(subject_blueprint, url_prefix="/api/subject") - # for rule in app.url_map.iter_rules(): - # print(f"Route: {rule} -> Methods: {rule.methods}") - return app -# app = createApp() - if __name__ == "__main__": app = createApp() - app.run(host="0.0.0.0", debug=Config.DEBUG) + + socketio.run(app, host="0.0.0.0", port=5000, debug=Config.DEBUG)