# app/api/v1/endpoints/invites.py import logging from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_transactional_session from app.auth import current_active_user from app.models import User as UserModel, UserRoleEnum from app.schemas.invite import InviteAccept from app.schemas.message import Message from app.schemas.group import GroupPublic from app.crud import invite as crud_invite from app.crud import group as crud_group from app.core.exceptions import ( InviteNotFoundError, InviteExpiredError, InviteAlreadyUsedError, InviteCreationError, GroupNotFoundError, GroupMembershipError, GroupOperationError ) logger = logging.getLogger(__name__) router = APIRouter() @router.post( "/accept", # Route relative to prefix "/invites" response_model=GroupPublic, summary="Accept Group Invite", tags=["Invites"] ) async def accept_invite( invite_in: InviteAccept, db: AsyncSession = Depends(get_transactional_session), current_user: UserModel = Depends(current_active_user), ): """Accepts a group invite using the provided invite code.""" logger.info(f"User {current_user.email} attempting to accept invite code: {invite_in.code}") # Get the invite - this function should only return valid, active invites invite = await crud_invite.get_active_invite_by_code(db, code=invite_in.code) if not invite: logger.warning(f"Invalid or inactive invite code attempted by user {current_user.email}: {invite_in.code}") # We can use a more generic error or a specific one. InviteNotFound is reasonable. raise InviteNotFoundError(invite_in.code) # Check if group still exists group = await crud_group.get_group_by_id(db, group_id=invite.group_id) if not group: logger.error(f"Group {invite.group_id} not found for invite {invite_in.code}") raise GroupNotFoundError(invite.group_id) # Check if user is already a member is_member = await crud_group.is_user_member(db, group_id=invite.group_id, user_id=current_user.id) if is_member: logger.warning(f"User {current_user.email} already a member of group {invite.group_id}") raise GroupMembershipError(invite.group_id, "join (already a member)") # Add user to the group added_to_group = await crud_group.add_user_to_group(db, group_id=invite.group_id, user_id=current_user.id) if not added_to_group: logger.error(f"Failed to add user {current_user.email} to group {invite.group_id} during invite acceptance.") # This could be a race condition or other issue, treat as an operational error. raise GroupOperationError("Failed to add user to group.") # Deactivate the invite so it cannot be used again await crud_invite.deactivate_invite(db, invite=invite) logger.info(f"User {current_user.email} successfully joined group {invite.group_id} via invite {invite_in.code}") # Re-fetch the group to get the updated member list updated_group = await crud_group.get_group_by_id(db, group_id=invite.group_id) if not updated_group: # This should ideally not happen as we found it before logger.error(f"Could not re-fetch group {invite.group_id} after user {current_user.email} joined.") raise GroupNotFoundError(invite.group_id) return updated_group