OOB-Dashboard/app/schemas.py

85 lines
2.2 KiB
Python

"""Request and response bodies.
Declaring these as pydantic models is also a CSRF control: FastAPI then rejects
form-encoded content types with a 422, and an HTML form can only produce those.
A cross-site form therefore cannot reach any of these endpoints even before the
SameSite cookie attribute is considered.
"""
from __future__ import annotations
from datetime import datetime
from pydantic import BaseModel, EmailStr, Field, field_validator
from app.config import get_settings
def _password_field() -> Field:
s = get_settings()
return Field(min_length=s.MIN_PASSWORD_LENGTH, max_length=s.MAX_PASSWORD_LENGTH)
class SignupIn(BaseModel):
email: EmailStr
password: str = _password_field()
name: str = Field(default="", max_length=120)
@field_validator("name")
@classmethod
def _tidy(cls, v: str) -> str:
return v.strip()
class LoginIn(BaseModel):
email: EmailStr
password: str = Field(min_length=1, max_length=256)
class EmailIn(BaseModel):
email: EmailStr
class TokenIn(BaseModel):
token: str = Field(min_length=8, max_length=256)
class ResetIn(TokenIn):
password: str = _password_field()
class SettingsIn(BaseModel):
"""The modelling assumptions the dashboard sends with /api/analyze.
`roas` is a string because the settings dialog sends "" for "use the
account average", and the pipeline already treats falsy as absent.
"""
roas: str | float | None = None
haircut: float = Field(default=0.7, ge=0, le=1)
cap: float = Field(default=3, gt=0, le=100)
merge_gap: int = Field(default=5, ge=0, le=720)
class UserOut(BaseModel):
id: int
email: str
name: str
is_admin: bool
is_active: bool
verified: bool
created_at: datetime | None = None
last_login_at: datetime | None = None
@classmethod
def of(cls, user) -> "UserOut":
return cls(id=user.id, email=user.email, name=user.name,
is_admin=user.is_admin, is_active=user.is_active,
verified=user.is_verified, created_at=user.created_at,
last_login_at=user.last_login_at)
class Ok(BaseModel):
ok: bool = True
message: str | None = None