包含前端(Vue3 + VueFlow 画布)、后端(Go)、文档体系。 - 工作台画布:节点拖放、连线模式、右键菜单、AI 助手 - 后端:连接器 API、专员种子数据 - 导航:左侧导航、工坊、市场、控制台
104 lines
3.5 KiB
Go
104 lines
3.5 KiB
Go
package middleware
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"eaisalestrain/backend/internal/auth"
|
|
"eaisalestrain/backend/internal/config"
|
|
"eaisalestrain/backend/internal/model"
|
|
"eaisalestrain/backend/internal/store"
|
|
"eaisalestrain/backend/internal/web"
|
|
)
|
|
|
|
// Auth 解析 JWT → 校验用户 → 注入 current_user
|
|
func Auth(cfg *config.Config) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
tokenStr := extractBearer(c)
|
|
if tokenStr == "" {
|
|
web.Fail(c, web.NewAuthError("缺少 Authorization Bearer 令牌"))
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
claims, err := auth.ParseToken(tokenStr, cfg.JWTSecret)
|
|
if err != nil {
|
|
web.Fail(c, web.NewAuthError("令牌无效或已过期"))
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
username, _ := claims["sub"].(string)
|
|
if username == "" {
|
|
web.Fail(c, web.NewAuthError("令牌中缺少用户标识"))
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
var user model.User
|
|
if err := store.DB.Where("username = ?", username).First(&user).Error; err != nil {
|
|
web.Fail(c, web.NewAuthError("用户不存在"))
|
|
c.Abort()
|
|
return
|
|
}
|
|
if user.Status != "active" {
|
|
web.Fail(c, web.NewAuthError("账号已禁用"))
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Set("current_user", &user)
|
|
// #region debug-point C:auth-success
|
|
go func(path, method, username, role string, userID uint) { _, _ = http.Post("http://127.0.0.1:7777/event", "application/json", bytes.NewBuffer(mustJSON(map[string]any{"sessionId": "admin-auth-misjudge", "runId": "pre-fix", "hypothesisId": "C", "location": "internal/middleware/auth.go:Auth", "msg": "[DEBUG] auth accepted request", "data": map[string]any{"path": path, "method": method, "username": username, "role": role, "user_id": userID, "token_present": true}, "ts": time.Now().UnixMilli()}))) }(c.Request.URL.Path, c.Request.Method, user.Username, user.Role, user.ID)
|
|
// #endregion
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// RequireAdmin 管理员角色守卫(须在 Auth 之后)
|
|
func RequireAdmin() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
u := CurrentUser(c)
|
|
if u == nil || u.Role != "admin" {
|
|
// #region debug-point D:require-admin-reject
|
|
go func(path, method string, u *model.User) { payload := map[string]any{"sessionId": "admin-auth-misjudge", "runId": "pre-fix", "hypothesisId": "D", "location": "internal/middleware/auth.go:RequireAdmin", "msg": "[DEBUG] require admin rejected request", "data": map[string]any{"path": path, "method": method, "user_nil": u == nil}, "ts": time.Now().UnixMilli()}; if u != nil { payload["data"] = map[string]any{"path": path, "method": method, "user_nil": false, "username": u.Username, "role": u.Role, "user_id": u.ID} }; _, _ = http.Post("http://127.0.0.1:7777/event", "application/json", bytes.NewBuffer(mustJSON(payload))) }(c.Request.URL.Path, c.Request.Method, u)
|
|
// #endregion
|
|
web.Fail(c, web.NewForbiddenError("需要管理员权限"))
|
|
c.Abort()
|
|
return
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// CurrentUser 从 context 取当前用户(可能为 nil)
|
|
func CurrentUser(c *gin.Context) *model.User {
|
|
if v, ok := c.Get("current_user"); ok {
|
|
if u, ok := v.(*model.User); ok {
|
|
return u
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func extractBearer(c *gin.Context) string {
|
|
h := c.GetHeader("Authorization")
|
|
if strings.HasPrefix(h, "Bearer ") {
|
|
return strings.TrimSpace(h[len("Bearer "):])
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func mustJSON(v any) []byte {
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
return []byte(`{"sessionId":"admin-auth-misjudge","runId":"pre-fix","hypothesisId":"D","location":"internal/middleware/auth.go:mustJSON","msg":"[DEBUG] marshal failed","ts":0}`)
|
|
}
|
|
return b
|
|
}
|