init: 数字员工平台初始代码

包含前端(Vue3 + VueFlow 画布)、后端(Go)、文档体系。
- 工作台画布:节点拖放、连线模式、右键菜单、AI 助手
- 后端:连接器 API、专员种子数据
- 导航:左侧导航、工坊、市场、控制台
This commit is contained in:
eaiadmin
2026-08-18 20:19:58 +08:00
commit 4e8817d768
239 changed files with 48631 additions and 0 deletions
@@ -0,0 +1,103 @@
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
}