feat: v0.3.1 — Go/Gin 后端重写 + gh-ost 集成

架构变更:
- Python/FastAPI → Go/Gin 重写(Python 后端保留在 backend-python/)
- GORM + SQLite(兼容现有数据库表结构)
- JWT 认证 + RBAC 权限中间件

Go 后端结构:
- cmd/server/main.go — 入口,端口 9000
- config/config.go — 环境变量配置
- internal/model/ — GORM 模型(30张表,完全兼容 SQLite)
- internal/handler/ — auth/instance/ticket/dashboard/ghost handler
- internal/middleware/ — JWT 认证 + CORS
- internal/ghost/ — gh-ost 集成模块(同步/异步执行,任务管理)
- router/router.go — API 路由注册(/api/v1/...)

gh-ost 集成:
- RunGhOst() 同步执行
- RunGhOstAsync() 异步执行 + 进度追踪
- TaskManager 管理多个并发任务
- API: POST /ghost/dry-run, /ghost/execute, GET /ghost/status/:taskId

核心 API 已对齐:
- POST /auth/login (JWT)
- GET /dashboard/stats, /dashboard/my-tickets
- GET /tickets/statistics, CRUD + approve/execute
- GET/POST/PUT/DELETE /instances
- GET /users, /audit-rules, /announcements, /settings

已验证:
- 登录 admin/admin123 ✅
- Dashboard 99工单/110查询/4实例 ✅
- 工单统计按角色过滤 ✅
This commit is contained in:
logan committed 2026-05-25 21:40:54 +08:00
1 parent 43d1f3a234
commit ca308e277d
202 files changed
+2265 -51181

No files matched your search

+89
View File
@@ -0,0 +1,89 @@
package middleware
import (
"net/http"
"strings"
"time"
"sql-platform/config"
"sql-platform/internal/model"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
)
type Claims struct {
UserID int `json:"user_id"`
Username string `json:"username"`
Role string `json:"role"`
jwt.RegisteredClaims
}
func GenerateToken(user *model.User) (string, error) {
claims := Claims{
UserID: user.ID,
Username: user.Username,
Role: user.Role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(config.C.JWTExpire) * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(config.C.JWTSecret))
}
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{"detail": "Not authenticated"})
c.Abort()
return
}
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
if tokenStr == authHeader {
c.JSON(http.StatusUnauthorized, gin.H{"detail": "Invalid token format"})
c.Abort()
return
}
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
return []byte(config.C.JWTSecret), nil
})
if err != nil || !token.Valid {
c.JSON(http.StatusUnauthorized, gin.H{"detail": "Invalid or expired token"})
c.Abort()
return
}
c.Set("user_id", claims.UserID)
c.Set("username", claims.Username)
c.Set("role", claims.Role)
c.Next()
}
}
func GetCurrentUser(c *gin.Context) (int, string, string) {
return c.GetInt("user_id"), c.GetString("username"), c.GetString("role")
}
// RBAC middleware — allow specified roles
func RequireRole(roles ...string) gin.HandlerFunc {
roleSet := make(map[string]bool)
for _, r := range roles {
roleSet[r] = true
}
return func(c *gin.Context) {
role := c.GetString("role")
if !roleSet[role] {
c.JSON(http.StatusForbidden, gin.H{"detail": "Permission denied"})
c.Abort()
return
}
c.Next()
}
}
+16
View File
@@ -0,0 +1,16 @@
package middleware
import (
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
func CORSMiddleware() gin.HandlerFunc {
return cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:5173", "http://127.0.0.1:5173"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
})
}