File size: 7,018 Bytes
a6fd1a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
from pydantic import BaseModel, EmailStr, Field, field_validator, HttpUrl
from typing import List, Optional
import re
from enum import Enum

# Define UserRole as an Enum
class UserRole(str, Enum):
    USER = "user"
    ADMIN = "admin"

class RegisterRequest(BaseModel):
    username: str = Field(
        ...,
        min_length=4,
        max_length=30,
        description="Tên đăng nhập của người dùng, chỉ chấp nhận chữ cái, số và dấu gạch dưới",
        example="john_doe123"
    )

    password: str = Field(
        ...,
        min_length=8,
        max_length=64,
        description="Mật khẩu của người dùng, yêu cầu ít nhất 8 ký tự, bao gồm chữ hoa, chữ thường, số và ký tự đặc biệt",
        example="StrongP@ss123"
    )

    email: EmailStr = Field(
        ...,
        description="Địa chỉ email hợp lệ của người dùng",
        example="[email protected]",
        pattern=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
    )

    role: UserRole = Field(
        default=UserRole.USER,
        description="Vai trò của người dùng trong hệ thống",
        example=UserRole.USER
    )

    avatar_url: Optional[HttpUrl] = Field(
        None,
        description="URL ảnh đại diện của người dùng",
        example="https://example.com/avatars/default.png"
    )

    is_active: bool = Field(
        default=True,
        description="Trạng thái hoạt động của người dùng",
        example=True
    )

    # Custom validators
    @field_validator("username")
    def username_alphanumeric(cls, v):
        if not re.match(r'^[a-zA-Z0-9_]+$', v):
            raise ValueError('Tên đăng nhập chỉ được chứa chữ cái, số và dấu gạch dưới')
        return v

    @field_validator('password')
    def password_strength(cls, v):
        if not re.search(r'[A-Z]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một ký tự viết hoa')
        if not re.search(r'[a-z]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một ký tự viết thường')
        if not re.search(r'[0-9]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một chữ số')
        if not re.search(r'[^a-zA-Z0-9]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một ký tự đặc biệt')
        return v

    @field_validator('role')
    def validate_role(cls, v):
        if v not in UserRole:
            raise ValueError(f'Vai trò không hợp lệ. Các vai trò được hỗ trợ: {", ".join([role.value for role in UserRole])}')
        return v

    @field_validator('avatar_url')
    def validate_avatar_url(cls, v):
        if v is None:
            return v
        try:
            from urllib.parse import urlparse
            parsed = urlparse(str(v))
            if not all([parsed.scheme, parsed.netloc]):
                raise ValueError('URL ảnh đại diện không hợp lệ')
        except Exception:
            raise ValueError('URL ảnh đại diện không hợp lệ')
        return v
class RegisterResponse(BaseModel):
    message: str

class LoginRequest(BaseModel):
    email: str
    password: str

class UserResponse(BaseModel):
    email: EmailStr
    username: str = "N/A"
    role: str
    avatar_url: Optional[str] = None

class LoginResponse(BaseModel):
    # accessToken: str
    # token_type: str = "bearer"
    user: UserResponse
    message: str


class VerifyLoginRequest(BaseModel):
    email: str
    code: str

class VerifyForgotPassRequest(BaseModel):
    email: str
    code: str

class UserOut(BaseModel):
    username: str
    email: EmailStr
    role: UserRole = Field(default=UserRole.USER, description="User role", example=UserRole.USER)
    avatar_url: Optional[str] = None
    is_active:bool = Field(default=True)

class RefreshTokenRequest(BaseModel):
    refresh_token: str

class PaginationMetadata(BaseModel):
    total: int
    page: int
    page_size: int
    pages: int
    has_more: bool

class PaginatedResponse(BaseModel):
    items: List[UserOut]
    metadata: PaginationMetadata

class ProfileResponse(BaseModel):
    username: str
    email: EmailStr
    role: UserRole = Field(default=UserRole.USER, description="User role", example=UserRole.USER)
    avatar_url: Optional[str] = Field(
        None,
        description="URL ảnh đại diện của người dùng",
        example="https://example.com/avatars/default.png"
    )
    is_active: bool = Field(
        default=True,
        description="Trạng thái hoạt động của người dùng",
    )

class ChangePasswordRequest(BaseModel):
    current_password: str = Field(..., min_length=8, description="Mật khẩu hiện tại", example="CurrentPassword123@")
    new_password: str = Field(
        ...,
        min_length=8,
        max_length=64,
        description="Mật khẩu của người dùng, yêu cầu ít nhất 8 ký tự, bao gồm chữ hoa, chữ thường, số và ký tự đặc biệt",
        example="NewPassword123@"
    )

    @field_validator('new_password')
    def password_strength(cls, v):
        if not re.search(r'[A-Z]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một ký tự viết hoa')
        if not re.search(r'[a-z]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một ký tự viết thường')
        if not re.search(r'[0-9]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một chữ số')
        if not re.search(r'[^a-zA-Z0-9]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một ký tự đặc biệt')
        return v

class PasswordResetRequest(BaseModel):
    email: EmailStr

class PasswordReset(BaseModel):
    code: str
    newPassword: str = Field(
        ...,
        min_length=8,
        max_length=64,
        description="Mật khẩu của người dùng, yêu cầu ít nhất 8 ký tự, bao gồm chữ hoa, chữ thường, số và ký tự đặc biệt",
        example="NewPassword123@"
    )

    @field_validator('newPassword')
    def password_strength(cls, v):
        if not re.search(r'[A-Z]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một ký tự viết hoa')
        if not re.search(r'[a-z]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một ký tự viết thường')
        if not re.search(r'[0-9]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một chữ số')
        if not re.search(r'[^a-zA-Z0-9]', v):
            raise ValueError('Mật khẩu phải chứa ít nhất một ký tự đặc biệt')
        return v

class ResentVerifyCode(BaseModel):
    email: str

class TokenValidationRequest(BaseModel):
    token: str

# Response model
class TokenValidationResponse(BaseModel):
    valid: bool
    message: str = None