From a8e2cd214cbcd13d64e854cc36a67f36467bcfa6 Mon Sep 17 00:00:00 2001 From: Sakurasan <26715255+Sakurasan@users.noreply.github.com> Date: Sat, 15 Aug 2026 20:55:45 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B4=A6=E6=88=B7:=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E6=8E=A5=E5=8F=A3=20+=20=E8=B4=A6=E6=88=B7?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 POST /auth/password: 校验旧密码(argon2id)后重哈希更新 - 前端 /console/settings 账户设置页: 个人资料卡 + 修改密码表单 Co-Authored-By: Claude --- server/internal/api/auth.go | 30 +++++++++ server/internal/api/router.go | 1 + web/src/views/console/SettingsView.vue | 84 ++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 web/src/views/console/SettingsView.vue diff --git a/server/internal/api/auth.go b/server/internal/api/auth.go index 817a052..2aafbdc 100644 --- a/server/internal/api/auth.go +++ b/server/internal/api/auth.go @@ -158,6 +158,36 @@ func (h *Handler) Logout(c *gin.Context) { resp.OK(c, gin.H{"ok": true}) } +type changePasswordReq struct { + OldPassword string `json:"old_password" binding:"required"` + NewPassword string `json:"new_password" binding:"required,min=8,max=72"` +} + +// ChangePassword POST /api/v1/auth/password — 修改密码。 +func (h *Handler) ChangePassword(c *gin.Context) { + u := sessionUser(c) + var req changePasswordReq + if err := c.ShouldBindJSON(&req); err != nil { + resp.Fail(c, http.StatusBadRequest, "invalid input: "+err.Error()) + return + } + ok, err := h.a.Hasher.VerifyPassword(u.PasswordHash, req.OldPassword) + if err != nil || !ok { + resp.Fail(c, http.StatusBadRequest, "旧密码不正确") + return + } + hash, err := h.a.Hasher.HashPassword(req.NewPassword) + if err != nil { + resp.Fail(c, http.StatusInternalServerError, "failed to hash password") + return + } + if err := h.a.DB.Model(&store.User{}).Where("id = ?", u.ID).Update("password_hash", hash).Error; err != nil { + resp.Fail(c, http.StatusInternalServerError, "failed to update password") + return + } + resp.OK(c, gin.H{"ok": true}) +} + // Me GET /api/v1/auth/me func (h *Handler) Me(c *gin.Context) { u := sessionUser(c) diff --git a/server/internal/api/router.go b/server/internal/api/router.go index 626f5fa..6d14ef4 100644 --- a/server/internal/api/router.go +++ b/server/internal/api/router.go @@ -63,6 +63,7 @@ func NewRouter(a *app.App, gw *proxy.Gateway) *gin.Engine { auth.POST("/login", h.Login) auth.POST("/refresh", h.Refresh) auth.POST("/logout", h.Logout) + auth.POST("/password", middleware.SessionAuth(a), h.ChangePassword) auth.GET("/me", middleware.SessionAuth(a), h.Me) } diff --git a/web/src/views/console/SettingsView.vue b/web/src/views/console/SettingsView.vue new file mode 100644 index 0000000..14d9e1f --- /dev/null +++ b/web/src/views/console/SettingsView.vue @@ -0,0 +1,84 @@ + + +