Files
boc/landvex-admin-backend/app/routers/invitations.py
T

94 lines
2.9 KiB
Python
Raw Normal View History

"""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