Spaces:
Running
Running
| from __future__ import annotations | |
| import re | |
| from datetime import datetime | |
| from typing import Any, Optional | |
| from pydantic import BaseModel, EmailStr, Field, field_validator | |
| def _validate_password(v: str) -> str: | |
| if len(v) < 8: | |
| raise ValueError("Password must be at least 8 characters") | |
| if len(v) > 128: | |
| raise ValueError("Password must not exceed 128 characters") | |
| if not re.search(r"[A-Z]", v): | |
| raise ValueError("Password must contain at least one uppercase letter") | |
| if not re.search(r"[a-z]", v): | |
| raise ValueError("Password must contain at least one lowercase letter") | |
| if not re.search(r"\d", v): | |
| raise ValueError("Password must contain at least one digit") | |
| return v | |
| class RegisterSchema(BaseModel): | |
| email: EmailStr | |
| password: str | |
| username: Optional[str] = Field(None, min_length=2, max_length=50, pattern=r"^[a-zA-Z0-9_]+$") | |
| full_name: Optional[str] = Field(None, min_length=1, max_length=100) | |
| _validate_password = field_validator("password")(_validate_password) | |
| class LoginSchema(BaseModel): | |
| email: EmailStr | |
| password: str | |
| class TokenData(BaseModel): | |
| access_token: str | |
| refresh_token: str | |
| token_type: str = "bearer" | |
| class TokenRefreshSchema(BaseModel): | |
| refresh_token: str | |
| class UserProfile(BaseModel): | |
| id: str | |
| email: str | |
| username: Optional[str] = None | |
| full_name: Optional[str] = None | |
| is_active: bool | |
| is_verified: bool | |
| roles: list[str] | |
| created_at: datetime | |
| def _extract_role_names(cls, v: Any) -> list[str]: | |
| if not v: | |
| return [] | |
| if isinstance(v[0], str): | |
| return v | |
| return [r.name for r in v] | |
| model_config = {"from_attributes": True} | |
| class UpdateProfileSchema(BaseModel): | |
| username: Optional[str] = Field(None, min_length=2, max_length=50, pattern=r"^[a-zA-Z0-9_]+$") | |
| full_name: Optional[str] = Field(None, min_length=1, max_length=100) | |
| class ChangePasswordSchema(BaseModel): | |
| current_password: str | |
| new_password: str | |
| _validate_new_password = field_validator("new_password")(_validate_password) | |
| class ForgotPasswordSchema(BaseModel): | |
| email: EmailStr | |
| class ResetPasswordSchema(BaseModel): | |
| token: str | |
| new_password: str | |
| _validate_new_password = field_validator("new_password")(_validate_password) | |
| class SessionOut(BaseModel): | |
| id: str | |
| device_info: Optional[str] = None | |
| ip_address: Optional[str] = None | |
| created_at: datetime | |
| expires_at: datetime | |
| model_config = {"from_attributes": True} | |
| class MessageResponse(BaseModel): | |
| message: str | |
| class UserSchemaField(BaseModel): | |
| field: str | |
| type: str | |
| required: bool | |
| description: str | |
| constraints: Optional[str] = None | |
| class UserSchemaResponse(BaseModel): | |
| table_name: str = "users" | |
| columns: list[UserSchemaField] | |
| # --- Wrapped API response schemas --- | |
| class ApiResponse(BaseModel): | |
| success: bool = True | |
| class DataResponse(ApiResponse): | |
| data: dict | |
| application_id: Optional[str] = None | |
| warning: Optional[dict] = None | |
| class TokenResponse(ApiResponse): | |
| data: TokenData | |
| application_id: Optional[str] = None | |
| warning: Optional[dict] = None | |
| class ProfileResponse(ApiResponse): | |
| data: UserProfile | |
| application_id: Optional[str] = None | |
| warning: Optional[dict] = None | |
| class MessageDataResponse(ApiResponse): | |
| data: MessageResponse | |
| application_id: Optional[str] = None | |
| warning: Optional[dict] = None | |
| class SchemaResponse(ApiResponse): | |
| data: UserSchemaResponse | |
| application_id: Optional[str] = None | |
| warning: Optional[dict] = None | |
| class SessionListResponse(ApiResponse): | |
| data: list[SessionOut] | |
| application_id: Optional[str] = None | |
| warning: Optional[dict] = None | |