69 lines
2.0 KiB
Python
69 lines
2.0 KiB
Python
"""What the invitation endpoints accept and return.
|
|
|
|
Two response shapes on purpose. `InvitationResponse` is for the administrator
|
|
who sent it and may name the role and the sender. `InvitationPreview` is for a
|
|
signed-out stranger holding a link, and says only enough to answer "am I in the
|
|
right place" — the workspace name and the address it was sent to. Anything more
|
|
would make a guessed token a way to read a workspace's staff list.
|
|
|
|
Neither ever carries the token. It exists once, in the response to creating an
|
|
invitation, and is a SHA-256 everywhere else.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
from pydantic import BaseModel, ConfigDict, EmailStr, Field
|
|
|
|
|
|
class InvitationCreate(BaseModel):
|
|
email: EmailStr
|
|
role_id: Optional[uuid.UUID] = None
|
|
first_name: Optional[str] = Field(default=None, max_length=100)
|
|
last_name: Optional[str] = Field(default=None, max_length=100)
|
|
|
|
|
|
class InvitationResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: uuid.UUID
|
|
email: str
|
|
first_name: Optional[str] = None
|
|
last_name: Optional[str] = None
|
|
role_id: Optional[uuid.UUID] = None
|
|
invited_by_id: Optional[uuid.UUID] = None
|
|
expires_at: datetime
|
|
accepted_at: Optional[datetime] = None
|
|
revoked_at: Optional[datetime] = None
|
|
created_at: datetime
|
|
state: str
|
|
|
|
|
|
class InvitationCreated(BaseModel):
|
|
invitation: InvitationResponse
|
|
acceptance_url: str
|
|
email_sent: bool
|
|
|
|
|
|
class InvitationPreview(BaseModel):
|
|
"""What a signed-out visitor holding a link is told."""
|
|
|
|
email: str
|
|
workspace_name: str
|
|
expires_at: datetime
|
|
|
|
|
|
class InvitationAccept(BaseModel):
|
|
token: str = Field(min_length=1)
|
|
password: str = Field(min_length=1)
|
|
first_name: Optional[str] = Field(default=None, max_length=100)
|
|
last_name: Optional[str] = Field(default=None, max_length=100)
|
|
|
|
|
|
class InvitationList(BaseModel):
|
|
items: List[InvitationResponse]
|
|
total: int
|