from datetime import datetime from typing import Optional from pydantic import BaseModel, Field, EmailStr from bson import ObjectId class PyObjectId(ObjectId): """Custom ObjectId type for Pydantic""" @classmethod def __get_validators__(cls): yield cls.validate @classmethod def validate(cls, v): if not ObjectId.is_valid(v): raise ValueError("Invalid ObjectId") return ObjectId(v) @classmethod def __get_pydantic_json_schema__(cls, field_schema): field_schema.update(type="string") class User(BaseModel): """User data model for authentication and authorization""" id: Optional[PyObjectId] = Field(default=None, alias="_id") username: str = Field(..., min_length=3, max_length=50) email: EmailStr = Field(...) hashed_password: str = Field(...) full_name: str = Field(..., min_length=1, max_length=100) role: str = Field(default="viewer", description="Role: admin, editor, viewer") disabled: bool = Field(default=False) created_at: datetime = Field(default_factory=datetime.utcnow) last_login: Optional[datetime] = None class Config: populate_by_name = True arbitrary_types_allowed = True json_encoders = {ObjectId: str} json_schema_extra = { "example": { "username": "johndoe", "email": "johndoe@example.com", "full_name": "John Doe", "role": "editor", "disabled": False } }