From b761372e961c1709792de3a408894f01838d90bc Mon Sep 17 00:00:00 2001 From: DarkSlein Date: Sat, 22 Feb 2025 21:13:40 +0300 Subject: [PATCH] Added Socket.io support --- requirements.txt | 7 ++++++ src/app.py | 8 ++++--- src/controllers/health_controller.py | 7 ++++++ src/controllers/reservation_controller.py | 19 +++++++++++----- src/controllers/websocket_controller.py | 27 +++++++++++++++++++++++ src/infra/server.py | 5 +++++ src/models/reservation_model.py | 2 +- src/repos/reservation_repo.py | 15 +++++++++++-- 8 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 src/controllers/health_controller.py create mode 100644 src/controllers/websocket_controller.py create mode 100644 src/infra/server.py diff --git a/requirements.txt b/requirements.txt index 6d32ab6..57e19d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ annotated-types==0.7.0 +bidict==0.23.1 blinker==1.8.2 bson==0.5.10 click==8.1.8 @@ -7,6 +8,8 @@ dnspython==2.7.0 email_validator==2.2.0 Flask==3.0.3 Flask-JWT-Extended==4.7.1 +Flask-SocketIO==5.5.1 +h11==0.14.0 idna==3.10 importlib_metadata==8.5.0 itsdangerous==2.2.0 @@ -17,7 +20,11 @@ pydantic_core==2.27.2 PyJWT==2.10.1 pymongo==4.11.1 python-dateutil==2.9.0.post0 +python-engineio==4.11.2 +python-socketio==5.12.1 +simple-websocket==1.1.0 six==1.17.0 typing_extensions==4.12.2 Werkzeug==3.0.6 +wsproto==1.2.0 zipp==3.20.2 \ No newline at end of file diff --git a/src/app.py b/src/app.py index f6a5b3a..1468e8a 100644 --- a/src/app.py +++ b/src/app.py @@ -1,18 +1,19 @@ -from flask import Flask from pymongo import MongoClient from flask_jwt_extended import JWTManager from config import Config from singletons.database_singleton import DatabaseSingleton +from infra.server import app, socketio from controllers.user_controller import user_blueprint from controllers.reservation_controller import reservation_blueprint +from controllers.health_controller import health_blueprint -app = Flask(__name__) app.config['JWT_SECRET_KEY'] = Config.JWT_SECRET_KEY app.register_blueprint(user_blueprint, url_prefix='/user') app.register_blueprint(reservation_blueprint, url_prefix='/reservation') +app.register_blueprint(health_blueprint, url_prefix='/health') jwt = JWTManager(app) client = MongoClient(Config.MONGO_URI) @@ -24,4 +25,5 @@ else: print('Failed to connect to MongoDB') if __name__ == '__main__': - app.run(debug=True) + #app.run(debug=True) + socketio.run(app, debug=False) diff --git a/src/controllers/health_controller.py b/src/controllers/health_controller.py new file mode 100644 index 0000000..aad2a70 --- /dev/null +++ b/src/controllers/health_controller.py @@ -0,0 +1,7 @@ +from flask import Blueprint, request, jsonify + +health_blueprint = Blueprint('health', __name__) + +@health_blueprint.route('/', methods=['GET']) +def health_check(): + return jsonify({"status": "OK"}), 200 \ No newline at end of file diff --git a/src/controllers/reservation_controller.py b/src/controllers/reservation_controller.py index 13e6393..512acfd 100644 --- a/src/controllers/reservation_controller.py +++ b/src/controllers/reservation_controller.py @@ -1,6 +1,8 @@ from flask import Blueprint, request, jsonify +from flask_socketio import emit from flask_jwt_extended import jwt_required, get_jwt_identity +from controllers.websocket_controller import notify_clients from middlewares.validate_request import validate_request from dtos.reservation_dtos import CreateReservationDTO, UpdateReservationDTO from models.reservation_model import Reservation @@ -10,28 +12,33 @@ reservation_blueprint = Blueprint('reservation', __name__) @reservation_blueprint.route('/', methods=['POST']) @validate_request(CreateReservationDTO) -@jwt_required() +#@jwt_required() def create_reservation(): - current_user = get_jwt_identity() + current_user = ""#get_jwt_identity() reservation = Reservation(**request.json, creator=current_user) id = ReservationRepository.insert(reservation) + notify_clients(reservation.room_id) return jsonify({"message": "Reservation created successfully", "id": id}) @reservation_blueprint.route('/', methods=['DELETE']) -@jwt_required() +#@jwt_required() def cancel_reservation(reservation_id): + reservation = ReservationRepository.get_by_id(reservation_id) result = ReservationRepository.delete(reservation_id) + notify_clients(reservation["room_id"]) if not result or result.deleted_count == 0: return jsonify({"error": "Reservation not found"}), 404 return jsonify({"message": "Reservation cancelled"}) @reservation_blueprint.route('/', methods=['PUT']) @validate_request(UpdateReservationDTO) -@jwt_required() +#@jwt_required() def change_reservation(reservation_id): data = request.json try: + reservation = ReservationRepository.get_by_id(reservation_id) result = ReservationRepository.update(reservation_id, data) + notify_clients(reservation["room_id"]) if not result or result.matched_count == 0: return jsonify({"error": "Reservation not found"}), 404 except: @@ -40,7 +47,7 @@ def change_reservation(reservation_id): return jsonify({"message": "Reservation updated"}) @reservation_blueprint.route('/', methods=['GET']) -@jwt_required() +#@jwt_required() def get_reservation(reservation_id): try: reservation = ReservationRepository.get_by_id(reservation_id) @@ -52,7 +59,7 @@ def get_reservation(reservation_id): return jsonify(reservation) @reservation_blueprint.route('/', methods=['GET']) -@jwt_required() +#@jwt_required() def list_reservations(): filters = { "date": request.args.get("date"), diff --git a/src/controllers/websocket_controller.py b/src/controllers/websocket_controller.py new file mode 100644 index 0000000..f72b44f --- /dev/null +++ b/src/controllers/websocket_controller.py @@ -0,0 +1,27 @@ +from flask_jwt_extended import get_jwt_identity, jwt_required +from flask_socketio import emit + +from infra.server import socketio +from repos.reservation_repo import ReservationRepository + +@socketio.on('connect') +#@jwt_required() +def handle_connect(): + #current_user = get_jwt_identity() + #print(f'Client connected: {current_user}') + emit('status', {'message': 'Connected'}) + +@socketio.on('disconnect') +def handle_disconnect(): + print('Client disconnected') + +@socketio.on('subscribe_reservations') +#@jwt_required() +def handle_subscribe(data): + room_id = data.get('room_id') + reservations = [] # ReservationRepository.list_all({'room_id': room_id}) + emit('reservations_update', reservations) + +def notify_clients(room_id): + reservations = [] # ReservationRepository.list_all({'room_id': room_id}) + socketio.emit('reservations_update', reservations, room=room_id) diff --git a/src/infra/server.py b/src/infra/server.py new file mode 100644 index 0000000..295151e --- /dev/null +++ b/src/infra/server.py @@ -0,0 +1,5 @@ +from flask import Flask +from flask_socketio import SocketIO + +app = Flask(__name__) +socketio = SocketIO(app, cors_allowed_origins="*") \ No newline at end of file diff --git a/src/models/reservation_model.py b/src/models/reservation_model.py index 3522464..0169d91 100644 --- a/src/models/reservation_model.py +++ b/src/models/reservation_model.py @@ -1,7 +1,7 @@ from models.time_model import Time class Reservation: - def __init__(self, title, room_id, creator, date, start_time, finish_time, color, description=None): + def __init__(self, title, room_id, creator, date, start_time, finish_time, color, description=""): self.title = title self.description = description self.room_id = room_id diff --git a/src/repos/reservation_repo.py b/src/repos/reservation_repo.py index e7dfafb..293c973 100644 --- a/src/repos/reservation_repo.py +++ b/src/repos/reservation_repo.py @@ -28,14 +28,25 @@ class ReservationRepository: @classmethod def get_by_id(cls, reservation_id): - return list(cls.db.reservation.find({"_id": ObjectId(reservation_id)})) + reservations = list(cls.db.reservation.find({"_id": ObjectId(reservation_id)})) + + print(reservations) + if not reservations or len(reservations) < 1: + return None + + reservation = reservations[0] + + reservation["id"] = str(reservation["_id"]) + del reservation["_id"] + + return reservation @classmethod def list_all(cls, filters=None): query = {} if filters: if "date" in filters: - query["date"] = {"$gte": filters["date"]} + query["date"] = filters["date"] if "room_id" in filters: query["room_id"] = filters["room_id"]