diff --git a/app/dto/UserDTO.py b/app/dto/UserDTO.py deleted file mode 100644 index 0a705a3..0000000 --- a/app/dto/UserDTO.py +++ /dev/null @@ -1,5 +0,0 @@ -from pydantic import BaseModel - -class UserDTO(BaseModel): - uid: str - username: str \ No newline at end of file diff --git a/app/dto/__init__.py b/app/dto/__init__.py index 8295fe7..ffc4e9e 100644 --- a/app/dto/__init__.py +++ b/app/dto/__init__.py @@ -1,4 +1,3 @@ -from .UserRegisterDTO import UserRegisterDTO from .FriendAddDTO import FriendAddDTO -from .UserDTO import UserDTO -from .PinDTO import PinDTO \ No newline at end of file +from .user import UserDTO, UserRegisterDTO +from .pin import PinDTO, PinShareDTO \ No newline at end of file diff --git a/app/dto/PinDTO.py b/app/dto/pin.py similarity index 50% rename from app/dto/PinDTO.py rename to app/dto/pin.py index 45ad39b..4180f08 100644 --- a/app/dto/PinDTO.py +++ b/app/dto/pin.py @@ -4,5 +4,8 @@ class PinDTO(BaseModel): title: str description: str location: list - files: list - user_id: str \ No newline at end of file + files: list | None = None + user_id: str | None = None + +class PinShareDTO(BaseModel): + friend_id: str \ No newline at end of file diff --git a/app/dto/UserRegisterDTO.py b/app/dto/user.py similarity index 51% rename from app/dto/UserRegisterDTO.py rename to app/dto/user.py index 512fd29..22c6865 100644 --- a/app/dto/UserRegisterDTO.py +++ b/app/dto/user.py @@ -1,5 +1,9 @@ from pydantic import BaseModel +class UserDTO(BaseModel): + uid: str + username: str + class UserRegisterDTO(BaseModel): username: str password: str \ No newline at end of file diff --git a/app/routes/pins.py b/app/routes/pins.py index 53ddecc..a710abc 100644 --- a/app/routes/pins.py +++ b/app/routes/pins.py @@ -1,10 +1,9 @@ - from bson import ObjectId import bson -from fastapi import APIRouter, HTTPException +from fastapi import APIRouter, HTTPException, status from fastapi.params import Depends import pymongo -from app.dto import PinDTO +from app.dto import PinDTO, PinShareDTO from app.models import HTTPError from app.models.user import User from .utils import get_current_user, objectid_misformatted @@ -19,6 +18,8 @@ client = pymongo.MongoClient(config.MONGODB_URL, username=config.MONGODB_USERNAM db = client[config.MONGODB_DATABASE] pins_collection = db["pins"] +friends_collection = db["friends"] +pin_permissions_collection = db["pin_permissions"] pins_router = APIRouter( prefix="/pin", @@ -32,28 +33,50 @@ pins_router = APIRouter( async def get_pin(id: str, current_user: User = Depends(get_current_user)): try: pin = pins_collection.find_one({"_id": ObjectId(id)}) + if pin is None: + raise HTTPException(status_code=404, detail="Pin not found") + + # Vérifier si l'utilisateur a la permission de voir le pin + if pin["user_id"] != current_user.uid: + permission = pin_permissions_collection.find_one({ + "pin_id": ObjectId(id), + "user_id": current_user.uid + }) + if not permission: + raise HTTPException(status_code=403, detail="You don't have permission to view this pin") + + return serializers.pin_serialize(pin) + except bson.errors.InvalidId: objectid_misformatted() - if pin is None: - raise HTTPException(status_code=404, detail="Pin not found") - - return serializers.pin_serialize(pin) - @pins_router.patch( path="/{id}", - responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} + responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}, 403: {"model": HTTPError}} ) async def update_pin(id: str, pin: PinDTO, current_user: User = Depends(get_current_user)): try: + # Vérifier si le pin existe + existing_pin = pins_collection.find_one({"_id": ObjectId(id)}) + if existing_pin is None: + raise HTTPException(status_code=404, detail="Pin not found") + + # Vérifier si l'utilisateur a la permission de modifier le pin + if existing_pin["user_id"] != current_user.uid: + permission = pin_permissions_collection.find_one({ + "pin_id": ObjectId(id), + "user_id": current_user.uid, + "can_edit": True + }) + if not permission: + raise HTTPException(status_code=403, detail="You don't have permission to edit this pin") + + # Mettre à jour le pin result = pins_collection.update_one({"_id": ObjectId(id)}, {"$set": pin.model_dump()}) + return {"message": "Pin updated"} + except bson.errors.InvalidId: objectid_misformatted() - - if result.matched_count == 0: - raise HTTPException(status_code=404, detail="Pin not found") - - return {"message": "Pin updated"} @pins_router.post( path="/add", @@ -72,22 +95,79 @@ async def list_pins(current_user: User = Depends(get_current_user)): pins = serializers.pins_serialize(pins_collection.find().to_list(), current_user.uid) return pins +@pins_router.post( + path="/{id}/share", + responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}, 403: {"model": HTTPError}} +) +async def share_pin(id: str, share_data: PinShareDTO, current_user: User = Depends(get_current_user)): + try: + # Vérifier si le pin existe et appartient à l'utilisateur courant + pin = pins_collection.find_one({"_id": ObjectId(id)}) + if pin is None: + raise HTTPException(status_code=404, detail="Pin not found") + + if pin["user_id"] != current_user.uid: + raise HTTPException(status_code=403, detail="You can only share your own pins") + + # Vérifier si l'utilisateur est ami avec la personne avec qui il veut partager + friend = friends_collection.find_one({ + "$or": [ + {"user_id": current_user.uid, "friend_user_id": share_data.friend_id, "status": "accepted"}, + {"user_id": share_data.friend_id, "friend_user_id": current_user.uid, "status": "accepted"} + ] + }) + + if friend is None: + raise HTTPException( + status_code=403, + detail="You can only share pins with your friends" + ) + + # Vérifier si la permission existe déjà + existing_permission = pin_permissions_collection.find_one({ + "pin_id": ObjectId(id), + "user_id": share_data.friend_id + }) + + if existing_permission: + raise HTTPException( + status_code=409, + detail="Pin is already shared with this user" + ) + + # Créer la permission + permission = { + "pin_id": ObjectId(id), + "user_id": share_data.friend_id, + "can_edit": True, + "can_delete": False + } + pin_permissions_collection.insert_one(permission) + + return {"message": "Pin shared successfully"} + + except bson.errors.InvalidId: + objectid_misformatted() + @pins_router.delete( path="/{id}", - responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} + responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}, 403: {"model": HTTPError}} ) async def delete_pin(id: str, current_user: User = Depends(get_current_user)): try: pin = pins_collection.find_one({"_id": ObjectId(id)}) + if pin is None: + raise HTTPException(status_code=404, detail="Pin not found") + + # Vérifier si l'utilisateur est le propriétaire du pin + if pin["user_id"] != current_user.uid: + raise HTTPException(status_code=403, detail="Only the owner can delete the pin") + + # Supprimer le pin et toutes ses permissions + pins_collection.delete_one({"_id": ObjectId(id)}) + pin_permissions_collection.delete_many({"pin_id": ObjectId(id)}) + + return {"message": "Pin deleted successfully"} + except bson.errors.InvalidId: - objectid_misformatted() - - if pin is None: - raise HTTPException(status_code=404, detail="Pin not found") - - if pin.get("user_id") != current_user.uid: - raise HTTPException(status_code=403, detail="You are not allowed to delete this pin") - - pins_collection.delete_one({"_id": ObjectId(id)}) - - return {"message": "Pin deleted"} + objectid_misformatted() \ No newline at end of file