🐐
This commit is contained in:
134
backend/routers/friends.py
Normal file
134
backend/routers/friends.py
Normal file
@@ -0,0 +1,134 @@
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
|
||||
from services import notification_manager
|
||||
from core.database import get_db
|
||||
from core.dependencies import get_current_user, get_user_id_from_request, limiter
|
||||
from core.models import Friendship as FriendshipModel
|
||||
from core.models import Notification as NotificationModel
|
||||
from core.models import User as UserModel
|
||||
from routers.notifications import _serialize_notification
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/users/{username}/friend-request")
|
||||
@limiter.limit("10/minute", key_func=get_user_id_from_request)
|
||||
async def send_friend_request(request: Request, username: str, user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
addressee = db.query(UserModel).filter(UserModel.username == username).first()
|
||||
if not addressee:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
if addressee.id == user.id:
|
||||
raise HTTPException(status_code=400, detail="Cannot send friend request to yourself")
|
||||
|
||||
# Check for any existing friendship in either direction
|
||||
existing = db.query(FriendshipModel).filter(
|
||||
((FriendshipModel.requester_id == user.id) & (FriendshipModel.addressee_id == addressee.id)) |
|
||||
((FriendshipModel.requester_id == addressee.id) & (FriendshipModel.addressee_id == user.id)),
|
||||
).first()
|
||||
if existing and existing.status != "declined":
|
||||
raise HTTPException(status_code=400, detail="Friend request already exists or already friends")
|
||||
# Clear stale declined row so the unique constraint allows re-requesting
|
||||
if existing:
|
||||
db.delete(existing)
|
||||
db.flush()
|
||||
|
||||
friendship = FriendshipModel(requester_id=user.id, addressee_id=addressee.id, status="pending")
|
||||
db.add(friendship)
|
||||
db.flush() # get friendship.id before notification
|
||||
|
||||
notif = NotificationModel(
|
||||
user_id=addressee.id,
|
||||
type="friend_request",
|
||||
payload={"friendship_id": str(friendship.id), "from_username": user.username},
|
||||
)
|
||||
db.add(notif)
|
||||
db.commit()
|
||||
|
||||
await notification_manager.send_notification(str(addressee.id), _serialize_notification(notif))
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@router.post("/friendships/{friendship_id}/accept")
|
||||
def accept_friend_request(friendship_id: str, user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
friendship = db.query(FriendshipModel).filter(FriendshipModel.id == uuid.UUID(friendship_id)).first()
|
||||
if not friendship:
|
||||
raise HTTPException(status_code=404, detail="Friendship not found")
|
||||
if friendship.addressee_id != user.id:
|
||||
raise HTTPException(status_code=403, detail="Not authorized")
|
||||
if friendship.status != "pending":
|
||||
raise HTTPException(status_code=400, detail="Friendship is not pending")
|
||||
friendship.status = "accepted"
|
||||
db.commit()
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@router.post("/friendships/{friendship_id}/decline")
|
||||
def decline_friend_request(friendship_id: str, user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
friendship = db.query(FriendshipModel).filter(FriendshipModel.id == uuid.UUID(friendship_id)).first()
|
||||
if not friendship:
|
||||
raise HTTPException(status_code=404, detail="Friendship not found")
|
||||
if friendship.addressee_id != user.id:
|
||||
raise HTTPException(status_code=403, detail="Not authorized")
|
||||
if friendship.status != "pending":
|
||||
raise HTTPException(status_code=400, detail="Friendship is not pending")
|
||||
friendship.status = "declined"
|
||||
# Clean up the associated notification so it disappears from the bell
|
||||
db.query(NotificationModel).filter(
|
||||
NotificationModel.user_id == user.id,
|
||||
NotificationModel.type == "friend_request",
|
||||
NotificationModel.payload["friendship_id"].astext == friendship_id,
|
||||
).delete(synchronize_session=False)
|
||||
db.commit()
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@router.get("/friends")
|
||||
def get_friends(user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
friendships = db.query(FriendshipModel).options(
|
||||
joinedload(FriendshipModel.requester),
|
||||
joinedload(FriendshipModel.addressee),
|
||||
).filter(
|
||||
(FriendshipModel.requester_id == user.id) | (FriendshipModel.addressee_id == user.id),
|
||||
FriendshipModel.status == "accepted",
|
||||
).all()
|
||||
result = []
|
||||
for f in friendships:
|
||||
other = f.addressee if f.requester_id == user.id else f.requester
|
||||
result.append({"id": str(other.id), "username": other.username, "friendship_id": str(f.id)})
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/friendship-status/{username}")
|
||||
def get_friendship_status(username: str, user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
"""Returns the friendship status between the current user and the given username."""
|
||||
other = db.query(UserModel).filter(UserModel.username == username).first()
|
||||
if not other:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
friendship = db.query(FriendshipModel).filter(
|
||||
((FriendshipModel.requester_id == user.id) & (FriendshipModel.addressee_id == other.id)) |
|
||||
((FriendshipModel.requester_id == other.id) & (FriendshipModel.addressee_id == user.id)),
|
||||
FriendshipModel.status != "declined",
|
||||
).first()
|
||||
if not friendship:
|
||||
return {"status": "none"}
|
||||
if friendship.status == "accepted":
|
||||
return {"status": "friends", "friendship_id": str(friendship.id)}
|
||||
# pending: distinguish sent vs received
|
||||
if friendship.requester_id == user.id:
|
||||
return {"status": "pending_sent", "friendship_id": str(friendship.id)}
|
||||
return {"status": "pending_received", "friendship_id": str(friendship.id)}
|
||||
|
||||
|
||||
@router.delete("/friendships/{friendship_id}")
|
||||
def remove_friend(friendship_id: str, user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
friendship = db.query(FriendshipModel).filter(FriendshipModel.id == uuid.UUID(friendship_id)).first()
|
||||
if not friendship:
|
||||
raise HTTPException(status_code=404, detail="Friendship not found")
|
||||
if friendship.requester_id != user.id and friendship.addressee_id != user.id:
|
||||
raise HTTPException(status_code=403, detail="Not authorized")
|
||||
db.delete(friendship)
|
||||
db.commit()
|
||||
return {"ok": True}
|
||||
Reference in New Issue
Block a user