from pydantic import BaseModel, EmailStr, Field, model_validator class RegisterRequest(BaseModel): company_name: str = Field(min_length=2, max_length=255) first_name: str = Field(min_length=1, max_length=100) last_name: str = Field(min_length=1, max_length=100) email: EmailStr password: str = Field(min_length=8, max_length=128) @model_validator(mode="after") def password_strength(self): pw = self.password if not any(c.isupper() for c in pw): raise ValueError("Password must contain at least one uppercase letter") if not any(c.isdigit() for c in pw): raise ValueError("Password must contain at least one digit") return self class LoginRequest(BaseModel): # Login-Kennung: i.d.R. eine E-Mail, kann aber auch eine intern erzeugte # Kennung sein (z.B. Mandanten-Admin ohne E-Mail → '@.local'). # Daher kein EmailStr-Zwang – das Passwort ist das eigentliche Gate. email: str = Field(min_length=1, max_length=255) password: str class RefreshRequest(BaseModel): refresh_token: str | None = None class PasswordResetRequest(BaseModel): email: EmailStr class PasswordResetConfirm(BaseModel): token: str new_password: str = Field(min_length=8, max_length=128) @model_validator(mode="after") def password_strength(self): pw = self.new_password if not any(c.isupper() for c in pw): raise ValueError("Password must contain at least one uppercase letter") if not any(c.isdigit() for c in pw): raise ValueError("Password must contain at least one digit") return self class TokenResponse(BaseModel): access_token: str refresh_token: str | None = None # Nur für API-Clients; Browser nutzt HttpOnly-Cookie token_type: str = "bearer" totp_required: bool = False partial_token: str | None = None class TotpSetupResponse(BaseModel): secret: str # base32 secret for manual entry otpauth_uri: str # otpauth://totp/... für QR-Code class TotpConfirmRequest(BaseModel): code: str = Field(min_length=6, max_length=6) class TotpLoginRequest(BaseModel): partial_token: str code: str = Field(min_length=6, max_length=6) class TotpDisableRequest(BaseModel): password: str code: str = Field(min_length=6, max_length=6) class AccessTokenResponse(BaseModel): access_token: str token_type: str = "bearer" class MessageResponse(BaseModel): message: str