ea8ffb6c74
- 修复订阅模式登录时错误检查余额的问题 - 区分无限余额和永久订阅两种永久会员类型 - 修复动态代码HTTP请求返回值在JS中无法正确访问的问题 - 添加侧边栏滚动位置保持功能 - 移除developer角色相关代码,统一使用admin - 添加缺失的i18n翻译key
235 lines
5.4 KiB
Go
235 lines
5.4 KiB
Go
package middleware
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
"verification-platform-backend/internal/database"
|
|
"verification-platform-backend/internal/model"
|
|
"verification-platform-backend/pkg/jwt"
|
|
"verification-platform-backend/pkg/response"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func CheckAppStatus() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
appKey := c.Param("appKey")
|
|
if appKey == "" {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
var app model.Application
|
|
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
|
response.Error(c, 404, "应用不存在")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
if app.Status == "maintenance" {
|
|
response.Error(c, 503, "应用维护中,暂时无法访问")
|
|
c.Abort()
|
|
return
|
|
}
|
|
if app.Status == "stopped" {
|
|
response.Error(c, 403, "应用已停止运营")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Set("app", &app)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// JWT JWT中间件
|
|
func JWT() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
fmt.Printf("JWT中间件被调用: %s %s\n", c.Request.Method, c.Request.URL.Path)
|
|
|
|
var token string
|
|
|
|
authHeader := c.GetHeader("Authorization")
|
|
if authHeader != "" {
|
|
parts := strings.SplitN(authHeader, " ", 2)
|
|
if len(parts) == 2 && parts[0] == "Bearer" {
|
|
token = parts[1]
|
|
}
|
|
}
|
|
|
|
if token == "" {
|
|
token = c.Query("token")
|
|
}
|
|
|
|
if token == "" {
|
|
fmt.Printf("JWT中间件: Authorization header is required\n")
|
|
response.Error(c, http.StatusUnauthorized, "Authorization header is required")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
claims, err := jwt.ParseToken(token)
|
|
if err != nil {
|
|
fmt.Printf("JWT中间件: Invalid token: %v\n", err)
|
|
response.Error(c, http.StatusUnauthorized, "Invalid token")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
if time.Now().Unix() > claims.ExpiresAt.Unix() {
|
|
fmt.Printf("JWT中间件: Token expired\n")
|
|
response.Error(c, http.StatusUnauthorized, "Token expired")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Set("user_id", claims.UserID)
|
|
c.Set("username", claims.Username)
|
|
c.Set("role", claims.Role)
|
|
|
|
fmt.Printf("JWT中间件: 验证成功, user_id=%d, role=%s\n", claims.UserID, claims.Role)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// AdminAuth 管理员权限中间件
|
|
func AdminAuth() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
role, exists := c.Get("role")
|
|
if !exists {
|
|
response.Error(c, http.StatusForbidden, "Access denied")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
if role != "admin" {
|
|
response.Error(c, http.StatusForbidden, "Admin access required")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// AgentAuth 代理商授权中间件
|
|
func AgentAuth() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
role, exists := c.Get("role")
|
|
if !exists {
|
|
response.Error(c, http.StatusForbidden, "Access denied")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
if role != "agent" && role != "admin" {
|
|
response.Error(c, http.StatusForbidden, "Agent access required")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// PackageAuth 套餐授权中间件
|
|
func PackageAuth() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
role, _ := c.Get("role")
|
|
if role == "admin" {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
response.Error(c, 401, "未授权")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
var user model.User
|
|
if err := database.DB.First(&user, userID).Error; err != nil {
|
|
response.Error(c, 500, "获取用户信息失败")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
if user.CurrentPackageID == nil {
|
|
response.Error(c, 403, "您还没有购买套餐,请先购买套餐后再使用开发者功能")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
var userPackage model.UserPackage
|
|
if err := database.DB.Where("user_id = ? AND package_id = ? AND status = ?",
|
|
userID, user.CurrentPackageID, "active").First(&userPackage).Error; err != nil {
|
|
response.Error(c, 403, "套餐授权已失效,请续费或重新购买套餐")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
if userPackage.ExpiredAt != nil && userPackage.ExpiredAt.Before(time.Now()) {
|
|
response.Error(c, 403, "套餐已过期,请续费后再使用开发者功能")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
c.Set("user_package", &userPackage)
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// Logger 日志中间件
|
|
func Logger() gin.HandlerFunc {
|
|
return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
|
|
return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
|
|
param.ClientIP,
|
|
param.TimeStamp.Format(time.RFC1123),
|
|
param.Method,
|
|
param.Path,
|
|
param.Request.Proto,
|
|
param.StatusCode,
|
|
param.Latency,
|
|
param.Request.UserAgent(),
|
|
param.ErrorMessage,
|
|
)
|
|
})
|
|
}
|
|
|
|
// Recovery 恢复中间件
|
|
func Recovery() gin.HandlerFunc {
|
|
return gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
|
|
response.Error(c, http.StatusInternalServerError, "Internal server error")
|
|
})
|
|
}
|
|
|
|
// Cors 跨域中间件
|
|
func Cors() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
|
c.Writer.Header().Set("Access-Control-Allow-Credentials", "false")
|
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE, PATCH")
|
|
c.Writer.Header().Set("Access-Control-Max-Age", "86400")
|
|
|
|
if c.Request.Method == "OPTIONS" {
|
|
c.AbortWithStatus(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
// RateLimit 限流中间件
|
|
func RateLimit() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// 这里可以实现基于Redis的限流逻辑
|
|
// 暂时跳过实现
|
|
c.Next()
|
|
}
|
|
}
|