"""Invitation endpoints.""" from typing import Optional from uuid import UUID from fastapi import APIRouter, Depends, Query from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.dependencies import get_current_user, require_admin, require_manager from app.models import User from app.schemas import InvitationCreate, InvitationOut, AcceptInvitationRequest, UserOut from app.services.invitation_service import InvitationService router = APIRouter(prefix="/invitations", tags=["Invitations"]) @router.get("", response_model=list[InvitationOut]) async def list_invitations( tenant_id: Optional[UUID] = Query(None), pending_only: bool = Query(True), skip: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=500), current_user: User = Depends(require_manager), db: AsyncSession = Depends(get_db), ): service = InvitationService(db) effective_tenant = tenant_id if not current_user.is_superuser and current_user.role.value == "manager": effective_tenant = current_user.tenant_id items, total = await service.list_invitations( tenant_id=effective_tenant, pending_only=pending_only, skip=skip, limit=limit, ) return items @router.post("", response_model=InvitationOut, status_code=201) async def create_invitation( data: InvitationCreate, current_user: User = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = InvitationService(db) invitation = await service.create_invitation(data, invited_by=current_user) return InvitationOut.model_validate(invitation) @router.post("/accept", response_model=UserOut) async def accept_invitation( data: AcceptInvitationRequest, db: AsyncSession = Depends(get_db), ): service = InvitationService(db) user = await service.accept_invitation(data) return UserOut.model_validate(user) @router.get("/validate/{token}") async def validate_invitation( token: str, db: AsyncSession = Depends(get_db), ): service = InvitationService(db) invitation = await service.get_by_token(token) if invitation is None: return {"valid": False, "reason": "not_found"} if invitation.accepted_at is not None: return {"valid": False, "reason": "already_accepted"} if invitation.expires_at < datetime.utcnow(): return {"valid": False, "reason": "expired"} return { "valid": True, "email": invitation.email, "role": invitation.role.value, "expires_at": invitation.expires_at.isoformat(), } @router.delete("/{invitation_id}", status_code=204) async def revoke_invitation( invitation_id: UUID, current_user: User = Depends(require_admin), db: AsyncSession = Depends(get_db), ): service = InvitationService(db) await service.revoke_invitation(invitation_id, actor=current_user) return None from datetime import datetime