from datetime import datetime from typing import Literal from pydantic import BaseModel, EmailStr, Field class AdminUserOut(BaseModel): id: str email: EmailStr display_name: str | None is_admin: bool is_active: bool verified: bool # Nie angemeldet, oder seit wann nicht mehr last_seen_at: datetime | None deactivated_at: datetime | None created_at: datetime # Wie viele Listen gehoeren dem Konto, in wie vielen ist es Mitglied owned_lists: int memberships: int # Laeuft gerade eine Adressaenderung? pending_email: str | None = None class AdminStatsOut(BaseModel): total: int active: int inactive: int unverified: int admins: int # Konten, die beim naechsten Durchlauf deaktiviert bzw. geloescht # wuerden - damit die Automatik nicht ueberrascht. due_deactivation: int due_deletion: int class UserCreateIn(BaseModel): email: EmailStr display_name: str | None = Field(default=None, max_length=80) is_admin: bool = False class EmailChangeIn(BaseModel): new_email: EmailStr class DeleteUserIn(BaseModel): """Wie mit den Listen des Kontos verfahren werden soll. "refuse" bricht ab, wenn dem Konto Listen gehoeren - die Voreinstellung, damit nichts unbeabsichtigt verschwindet. "handover" uebertraegt geteilte Listen an das diensthaelteste andere Mitglied und loescht nur die, die niemand sonst nutzt. """ lists: Literal["refuse", "handover"] = "refuse" # Sicherheitsabfrage: die Adresse muss zum Konto passen confirm_email: EmailStr class DurationOut(BaseModel): """Eine einstellbare Frist samt Beschreibung. Beschriftung, Einheit und Grenzen kommen vom Server, damit die Oberflaeche sie nicht doppelt vorhalten muss - und damit nicht das eine steht und das andere gilt. """ key: str label: str unit: str value: int default: int minimum: int maximum: int # Was bedeutet 0? None = nicht erlaubt. zero_means: str | None = None note: str = "" class AdminSettingsOut(BaseModel): allow_self_registration: bool locked_by_env: bool durations: list[DurationOut] # Fristen, die ausserhalb der Anwendung liegen - nur zur Anzeige, # damit die Uebersicht nicht unvollstaendig wirkt. fixed: dict[str, str] class AdminSettingsIn(BaseModel): allow_self_registration: bool | None = None # Schluessel aus DURATIONS -> neuer Wert durations: dict[str, int] | None = None class WelcomeCompleteIn(BaseModel): token: str = Field(max_length=128) password: str = Field(min_length=12, max_length=256) display_name: str | None = Field(default=None, max_length=80) class WelcomePreviewOut(BaseModel): email: EmailStr display_name: str | None