136 lines
4.9 KiB
Python
136 lines
4.9 KiB
Python
from datetime import datetime
|
|
from typing import Optional
|
|
from pydantic import BaseModel, EmailStr, Field, field_validator, model_validator, ConfigDict
|
|
import re
|
|
|
|
class UserBase(BaseModel):
|
|
username: str = Field(..., min_length=3, max_length=50)
|
|
email: EmailStr
|
|
|
|
@field_validator('username')
|
|
@classmethod
|
|
def validate_username(cls, v: str) -> str:
|
|
if not re.match(r'^[a-zA-Z0-9_-]+$', v):
|
|
raise ValueError('Username must contain only alphanumeric characters, underscores, and dashes')
|
|
return v
|
|
|
|
class UserCreate(UserBase):
|
|
password: str = Field(..., min_length=8, max_length=128)
|
|
|
|
@field_validator('password')
|
|
@classmethod
|
|
def validate_password_strength(cls, v: str) -> str:
|
|
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 User(UserBase):
|
|
id: int
|
|
is_active: bool
|
|
is_superuser: bool
|
|
can_use_local_model: bool
|
|
can_use_nsfw: bool = False
|
|
created_at: datetime
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
class UserCreateByAdmin(UserBase):
|
|
password: str = Field(..., min_length=8, max_length=128)
|
|
is_superuser: bool = False
|
|
can_use_local_model: bool = False
|
|
can_use_nsfw: bool = False
|
|
|
|
@field_validator('password')
|
|
@classmethod
|
|
def validate_password_strength(cls, v: str) -> str:
|
|
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 UserUpdate(BaseModel):
|
|
username: Optional[str] = Field(None, min_length=3, max_length=50)
|
|
email: Optional[EmailStr] = None
|
|
password: Optional[str] = Field(None, min_length=8, max_length=128)
|
|
is_active: Optional[bool] = None
|
|
is_superuser: Optional[bool] = None
|
|
can_use_local_model: Optional[bool] = None
|
|
can_use_nsfw: Optional[bool] = None
|
|
|
|
@field_validator('username')
|
|
@classmethod
|
|
def validate_username(cls, v: Optional[str]) -> Optional[str]:
|
|
if v is not None and not re.match(r'^[a-zA-Z0-9_-]+$', v):
|
|
raise ValueError('Username must contain only alphanumeric characters, underscores, and dashes')
|
|
return v
|
|
|
|
@field_validator('password')
|
|
@classmethod
|
|
def validate_password_strength(cls, v: Optional[str]) -> Optional[str]:
|
|
if v is not None:
|
|
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 UserListResponse(BaseModel):
|
|
users: list[User]
|
|
total: int
|
|
skip: int
|
|
limit: int
|
|
|
|
class Token(BaseModel):
|
|
access_token: str
|
|
token_type: str
|
|
|
|
class TokenData(BaseModel):
|
|
username: Optional[str] = None
|
|
|
|
class PasswordChange(BaseModel):
|
|
current_password: str = Field(..., min_length=1)
|
|
new_password: str = Field(..., min_length=8, max_length=128)
|
|
confirm_password: str = Field(..., min_length=8, max_length=128)
|
|
|
|
@field_validator('new_password')
|
|
@classmethod
|
|
def validate_password_strength(cls, v: str) -> str:
|
|
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
|
|
|
|
@model_validator(mode='after')
|
|
def passwords_match(self) -> 'PasswordChange':
|
|
if self.new_password != self.confirm_password:
|
|
raise ValueError('Passwords do not match')
|
|
return self
|
|
|
|
class AliyunKeyUpdate(BaseModel):
|
|
api_key: str = Field(..., min_length=1, max_length=500)
|
|
|
|
class AliyunKeyVerifyResponse(BaseModel):
|
|
valid: bool
|
|
message: str
|
|
|
|
class UserPreferences(BaseModel):
|
|
default_backend: str = Field(default="aliyun", pattern="^(local|aliyun)$")
|
|
onboarding_completed: bool = Field(default=False)
|
|
|
|
class UserPreferencesResponse(BaseModel):
|
|
default_backend: str = Field(default="aliyun", pattern="^(local|aliyun)$")
|
|
onboarding_completed: bool = Field(default=False)
|
|
available_backends: list[str] = Field(default=["aliyun"])
|