server: 平台抽象层+bilibili、服务端加密凭据库、角色中间件;web: 精简页面/路由、macOS 客户端(Swift)与多份方案文档;移除误入库的编译产物

This commit is contained in:
Qiufeng
2026-08-21 10:53:32 +08:00
parent 0daa9782c9
commit 3585c39bab
103 changed files with 5842 additions and 3197 deletions
+74 -2
View File
@@ -2,6 +2,7 @@ package handlers
import (
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
@@ -42,6 +43,11 @@ type logoutReq struct {
RefreshToken string `json:"refreshToken"`
}
type changePasswordReq struct {
CurrentPassword string `json:"currentPassword" binding:"required"`
NewPassword string `json:"newPassword" binding:"required,min=8"`
}
// Register 注册:创建用户 + 默认工作空间 + owner 成员
func (h *AuthHandler) Register(c *gin.Context) {
var req registerReq
@@ -177,8 +183,13 @@ func (h *AuthHandler) Refresh(c *gin.Context) {
response.Fail(c, http.StatusUnauthorized, 2003, "用户不存在")
return
}
workspaceID := claims.WorkspaceID
var member models.Member
if err = h.DB.Where("user_id = ?", user.ID).Order("id asc").First(&member).Error; err != nil {
memberQuery := h.DB.Where("user_id = ?", user.ID)
if workspaceID != 0 {
memberQuery = memberQuery.Where("workspace_id = ?", workspaceID)
}
if err = memberQuery.Order("id asc").First(&member).Error; err != nil {
response.Fail(c, http.StatusInternalServerError, 5000, "工作空间数据缺失")
return
}
@@ -203,6 +214,39 @@ func (h *AuthHandler) Logout(c *gin.Context) {
response.OK(c, nil)
}
func (h *AuthHandler) ChangePassword(c *gin.Context) {
var req changePasswordReq
if err := c.ShouldBindJSON(&req); err != nil {
response.Fail(c, http.StatusBadRequest, 1001, "密码格式不合法")
return
}
var user models.User
if err := h.DB.First(&user, c.GetUint64("uid")).Error; err != nil {
response.Fail(c, http.StatusUnauthorized, 1002, "用户不存在")
return
}
ok, err := auth.VerifyPassword(req.CurrentPassword, user.PasswordHash)
if err != nil || !ok {
response.Fail(c, http.StatusBadRequest, 2002, "当前密码错误")
return
}
if req.CurrentPassword == req.NewPassword {
response.Fail(c, http.StatusBadRequest, 1001, "新密码不能与当前密码相同")
return
}
hash, err := auth.HashPassword(req.NewPassword)
if err != nil {
response.Fail(c, http.StatusInternalServerError, 5000, "密码更新失败")
return
}
if err = h.DB.Model(&user).Update("password_hash", hash).Error; err != nil {
response.Fail(c, http.StatusInternalServerError, 5000, "密码更新失败")
return
}
response.Audit(c, "auth.password_change", "user:"+itoa(user.ID), nil)
response.OK(c, nil)
}
// Me 当前用户信息
func (h *AuthHandler) Me(c *gin.Context) {
var user models.User
@@ -213,6 +257,34 @@ func (h *AuthHandler) Me(c *gin.Context) {
response.OK(c, gin.H{"user": userDTO(user), "workspaceId": c.GetUint64("wsid"), "memberRole": c.GetString("mrole")})
}
// SwitchWorkspace issues a fresh access/refresh pair for another workspace
// where the current user is a member. The old token remains valid until its
// normal expiry, but all resource handlers use the newly selected workspace.
func (h *AuthHandler) SwitchWorkspace(c *gin.Context) {
wsid, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.Fail(c, http.StatusBadRequest, 1001, "参数不合法")
return
}
var member models.Member
if err = h.DB.Where("workspace_id = ? and user_id = ?", wsid, c.GetUint64("uid")).First(&member).Error; err != nil {
response.Fail(c, http.StatusForbidden, 1003, "不是该工作空间成员")
return
}
var user models.User
if err = h.DB.First(&user, c.GetUint64("uid")).Error; err != nil || user.Status != "active" {
response.Fail(c, http.StatusUnauthorized, 1002, "用户不可用")
return
}
access, refresh, err := h.issuePair(user.ID, wsid, user.Role, member.Role)
if err != nil {
response.Fail(c, http.StatusInternalServerError, 5000, "签发令牌失败")
return
}
response.Audit(c, "workspace.switch", "workspace:"+itoa(wsid), nil)
response.OK(c, gin.H{"accessToken": access, "refreshToken": refresh, "workspace": gin.H{"id": wsid}, "memberRole": member.Role})
}
// issuePair 签发令牌对并把 refresh jti 登记到 Redis
func (h *AuthHandler) issuePair(uid, wsid uint64, role, mrole string) (string, string, error) {
access, err := auth.IssueAccess(h.Cfg.JWTSecret, uid, wsid, role, mrole)
@@ -220,7 +292,7 @@ func (h *AuthHandler) issuePair(uid, wsid uint64, role, mrole string) (string, s
return "", "", err
}
jti := uuid.NewString()
refresh, err := auth.IssueRefresh(h.Cfg.JWTSecret, uid, jti)
refresh, err := auth.IssueRefresh(h.Cfg.JWTSecret, uid, wsid, jti)
if err != nil {
return "", "", err
}