aee0f09db8
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
148 lines
3.9 KiB
Python
148 lines
3.9 KiB
Python
"""Pydantic-scheman för request/response-validering."""
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
from uuid import UUID
|
|
|
|
from pydantic import BaseModel, EmailStr, Field, ConfigDict
|
|
|
|
from app.models import UserRole, UserStatus
|
|
|
|
|
|
# ─── Shared ───────────────────────────────────────────
|
|
|
|
class UserBase(BaseModel):
|
|
email: EmailStr
|
|
first_name: str = Field(..., min_length=1, max_length=100)
|
|
last_name: str = Field(..., min_length=1, max_length=100)
|
|
role: UserRole = UserRole.VIEWER
|
|
|
|
|
|
class TenantBase(BaseModel):
|
|
name: str = Field(..., min_length=1, max_length=255)
|
|
slug: str = Field(..., min_length=1, max_length=100)
|
|
description: Optional[str] = None
|
|
|
|
|
|
# ─── Tenant ───────────────────────────────────────────
|
|
|
|
class TenantCreate(TenantBase):
|
|
pass
|
|
|
|
|
|
class TenantUpdate(BaseModel):
|
|
name: Optional[str] = Field(None, min_length=1, max_length=255)
|
|
description: Optional[str] = None
|
|
|
|
|
|
class TenantOut(TenantBase):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: UUID
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
# ─── User ─────────────────────────────────────────────
|
|
|
|
class UserCreate(UserBase):
|
|
password: Optional[str] = Field(None, min_length=8)
|
|
tenant_id: Optional[UUID] = None
|
|
|
|
|
|
class UserUpdate(BaseModel):
|
|
email: Optional[EmailStr] = None
|
|
first_name: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
last_name: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
role: Optional[UserRole] = None
|
|
status: Optional[UserStatus] = None
|
|
tenant_id: Optional[UUID] = None
|
|
|
|
|
|
class UserOut(UserBase):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: UUID
|
|
status: UserStatus
|
|
is_superuser: bool
|
|
last_login_at: Optional[datetime] = None
|
|
tenant_id: Optional[UUID] = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class UserDetailOut(UserOut):
|
|
tenant: Optional[TenantOut] = None
|
|
|
|
|
|
class UserListOut(BaseModel):
|
|
items: list[UserOut]
|
|
total: int
|
|
|
|
|
|
# ─── Auth ─────────────────────────────────────────────
|
|
|
|
class Token(BaseModel):
|
|
access_token: str
|
|
refresh_token: str
|
|
token_type: str = "bearer"
|
|
expires_in: int
|
|
|
|
|
|
class TokenPayload(BaseModel):
|
|
sub: Optional[str] = None
|
|
type: Optional[str] = None
|
|
|
|
|
|
class LoginRequest(BaseModel):
|
|
email: EmailStr
|
|
password: str
|
|
|
|
|
|
class RefreshRequest(BaseModel):
|
|
refresh_token: str
|
|
|
|
|
|
class ChangePasswordRequest(BaseModel):
|
|
current_password: str
|
|
new_password: str = Field(..., min_length=8)
|
|
|
|
|
|
# ─── Invitation ───────────────────────────────────────
|
|
|
|
class InvitationCreate(BaseModel):
|
|
email: EmailStr
|
|
role: UserRole = UserRole.VIEWER
|
|
tenant_id: Optional[UUID] = None
|
|
|
|
|
|
class InvitationOut(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: UUID
|
|
email: str
|
|
role: UserRole
|
|
token: str
|
|
expires_at: datetime
|
|
accepted_at: Optional[datetime] = None
|
|
created_at: datetime
|
|
invited_by_id: UUID
|
|
tenant_id: Optional[UUID] = None
|
|
|
|
|
|
class AcceptInvitationRequest(BaseModel):
|
|
token: str
|
|
first_name: str = Field(..., min_length=1, max_length=100)
|
|
last_name: str = Field(..., min_length=1, max_length=100)
|
|
password: str = Field(..., min_length=8)
|
|
|
|
|
|
# ─── Password reset (future) ──────────────────────────
|
|
|
|
class PasswordResetRequest(BaseModel):
|
|
email: EmailStr
|
|
|
|
|
|
class PasswordResetConfirm(BaseModel):
|
|
token: str
|
|
new_password: str = Field(..., min_length=8)
|