From 71883d32c09b24c076f144463029d506e3a07a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8E=E5=B0=8F=E4=B8=98?= Date: Sat, 14 Feb 2026 15:11:56 +0800 Subject: [PATCH] feat: add PATCH /user/settings/password endpoint for changing password Register the fixed /password route before the wildcard /{option} to prevent FastAPI from matching it as a path parameter. Co-Authored-By: Claude Opus 4.6 --- routers/api/v1/user/settings/__init__.py | 38 ++++++++++++++++++++++++ sqlmodels/__init__.py | 1 + sqlmodels/auth_identity.py | 10 +++++++ 3 files changed, 49 insertions(+) diff --git a/routers/api/v1/user/settings/__init__.py b/routers/api/v1/user/settings/__init__.py index e4ea7da..19a39c6 100644 --- a/routers/api/v1/user/settings/__init__.py +++ b/routers/api/v1/user/settings/__init__.py @@ -11,6 +11,7 @@ from sqlmodels import ( BUILTIN_DEFAULT_COLORS, ThemePreset, UserThemeUpdateRequest, SettingOption, UserSettingUpdateRequest, AuthIdentity, AuthIdentityResponse, AuthProviderType, BindIdentityRequest, + ChangePasswordRequest, AuthnDetailResponse, AuthnRenameRequest, ) from sqlmodels.color import ThemeColorsBase @@ -226,6 +227,43 @@ async def router_user_settings_theme( await user.save(session) +@user_settings_router.patch( + path='/password', + summary='修改密码', + status_code=status.HTTP_204_NO_CONTENT, +) +async def router_user_settings_change_password( + session: SessionDep, + user: Annotated[sqlmodels.user.User, Depends(auth_required)], + request: ChangePasswordRequest, +) -> None: + """ + 修改当前用户密码 + + 请求体: + - old_password: 当前密码 + - new_password: 新密码(至少 8 位) + + 错误处理: + - 400: 用户没有邮箱密码认证身份 + - 403: 当前密码错误 + """ + email_identity: AuthIdentity | None = await AuthIdentity.get( + session, + (AuthIdentity.user_id == user.id) + & (AuthIdentity.provider == AuthProviderType.EMAIL_PASSWORD), + ) + if not email_identity or not email_identity.credential: + http_exceptions.raise_bad_request("未找到邮箱密码认证身份") + + verify_result = Password.verify(email_identity.credential, request.old_password) + if verify_result == PasswordStatus.INVALID: + http_exceptions.raise_forbidden("当前密码错误") + + email_identity.credential = Password.hash(request.new_password) + await email_identity.save(session) + + @user_settings_router.patch( path='/{option}', summary='更新用户设定', diff --git a/sqlmodels/__init__.py b/sqlmodels/__init__.py index e87d2a7..4f6124c 100644 --- a/sqlmodels/__init__.py +++ b/sqlmodels/__init__.py @@ -3,6 +3,7 @@ from .auth_identity import ( AuthIdentityResponse, AuthProviderType, BindIdentityRequest, + ChangePasswordRequest, ) from .user import ( BatchDeleteRequest, diff --git a/sqlmodels/auth_identity.py b/sqlmodels/auth_identity.py index bab8741..de1353b 100644 --- a/sqlmodels/auth_identity.py +++ b/sqlmodels/auth_identity.py @@ -81,6 +81,16 @@ class BindIdentityRequest(SQLModelBase): """OAuth 回调地址""" +class ChangePasswordRequest(SQLModelBase): + """修改密码请求 DTO""" + + old_password: str = Field(min_length=1) + """当前密码""" + + new_password: str = Field(min_length=8, max_length=128) + """新密码(至少 8 位)""" + + # ==================== 数据库模型 ==================== class AuthIdentity(SQLModelBase, UUIDTableBaseMixin):