first commit
Some checks failed
CI/CD Pipeline / Test (push) Failing after 22m19s
CI/CD Pipeline / Security Scan (push) Failing after 5m57s
CI/CD Pipeline / Build (amd64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (amd64, linux) (push) Has been skipped
CI/CD Pipeline / Build (amd64, windows) (push) Has been skipped
CI/CD Pipeline / Build (arm64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (arm64, linux) (push) Has been skipped
CI/CD Pipeline / Build Docker Image (push) Has been skipped
CI/CD Pipeline / Create Release (push) Has been skipped

This commit is contained in:
Rogee
2025-09-28 10:05:07 +08:00
commit 7fcabe0225
481 changed files with 125127 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
package middleware
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
)
// SetupCORS 设置CORS中间件
// 配置跨域资源共享允许前端应用访问API
func SetupCORS() fiber.Handler {
return cors.New(cors.Config{
AllowOrigins: "*",
AllowMethods: "GET, POST, PUT, DELETE, OPTIONS",
AllowHeaders: "Origin, Content-Type, Accept, Authorization",
MaxAge: 86400, // 24小时
})
}

View File

@@ -0,0 +1,59 @@
package middleware
import (
"fmt"
"time"
"github.com/gofiber/fiber/v2"
"github.com/subconverter-go/internal/logging"
)
// SetupLogging 设置日志中间件
// 记录所有HTTP请求的详细信息
func SetupLogging(logger *logging.Logger) fiber.Handler {
return func(c *fiber.Ctx) error {
start := time.Now()
// 处理请求
err := c.Next()
// 计算请求耗时
duration := time.Since(start)
// 记录请求日志
logger.WithFields(map[string]interface{}{
"method": c.Method(),
"path": c.Path(),
"ip": c.IP(),
"user_agent": c.Get("User-Agent"),
"status": c.Response().StatusCode(),
"duration": duration.Milliseconds(),
"size": len(c.Response().Body()),
}).Infof("HTTP %s %s - %d (%v)", c.Method(), c.Path(), c.Response().StatusCode(), duration)
return err
}
}
// SetupRequestID 设置请求ID中间件
// 为每个请求生成唯一ID便于追踪和调试
func SetupRequestID() fiber.Handler {
return func(c *fiber.Ctx) error {
// 生成请求ID
requestID := generateRequestID()
// 设置到响应头
c.Set("X-Request-ID", requestID)
// 存储到上下文
c.Locals("requestID", requestID)
return c.Next()
}
}
// generateRequestID 生成请求ID
func generateRequestID() string {
// 使用时间戳和随机数生成唯一ID
return fmt.Sprintf("%d-%d", time.Now().UnixNano(), time.Now().Nanosecond()%1000000)
}

View File

@@ -0,0 +1,27 @@
package middleware
import (
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/limiter"
)
// SetupRateLimit 设置限流中间件
// 防止API滥用限制每个IP的请求频率
func SetupRateLimit(maxRequests int, expiration int) fiber.Handler {
return limiter.New(limiter.Config{
Max: maxRequests,
Expiration: time.Duration(expiration) * time.Second, // 过期时间(秒)
KeyGenerator: func(c *fiber.Ctx) string {
return c.IP()
},
LimitReached: func(c *fiber.Ctx) error {
return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{
"success": false,
"error": "Rate limit exceeded",
"message": "Too many requests, please try again later",
})
},
})
}

View File

@@ -0,0 +1,36 @@
package middleware
import (
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/subconverter-go/internal/logging"
)
// SetupRecovery 设置恢复中间件
// 捕获panic并记录错误日志返回友好的错误响应
func SetupRecovery(logger *logging.Logger) fiber.Handler {
return func(c *fiber.Ctx) error {
defer func() {
if r := recover(); r != nil {
// 记录panic日志
logger.WithFields(map[string]interface{}{
"method": c.Method(),
"path": c.Path(),
"ip": c.IP(),
"user_agent": c.Get("User-Agent"),
"panic": fmt.Sprintf("%v", r),
}).Error("Recovered from panic")
// 返回错误响应
c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"error": "Internal server error",
"message": "An unexpected error occurred",
})
}
}()
return c.Next()
}
}

View File

@@ -0,0 +1,87 @@
package middleware
import (
"strings"
"github.com/gofiber/fiber/v2"
"github.com/subconverter-go/internal/logging"
)
// SetupSecurity 设置安全中间件
// 添加各种安全相关的HTTP头
func SetupSecurity() fiber.Handler {
return func(c *fiber.Ctx) error {
// 设置安全相关的HTTP头
c.Set("X-Content-Type-Options", "nosniff")
c.Set("X-Frame-Options", "DENY")
c.Set("X-XSS-Protection", "1; mode=block")
c.Set("Referrer-Policy", "strict-origin-when-cross-origin")
c.Set("Content-Security-Policy", "default-src 'self'")
// 移除服务器信息
c.Set("Server", "")
return c.Next()
}
}
// SetupAuthentication 设置认证中间件
// 验证访问令牌保护API端点
func SetupAuthentication(logger *logging.Logger, validTokens []string) fiber.Handler {
allowed := make(map[string]struct{}, len(validTokens))
for _, token := range validTokens {
if token == "" {
continue
}
allowed[strings.TrimSpace(token)] = struct{}{}
}
return func(c *fiber.Ctx) error {
// 如果没有配置令牌,跳过认证
if len(allowed) == 0 {
return c.Next()
}
token := ""
authHeader := c.Get("Authorization")
if strings.HasPrefix(authHeader, "Bearer ") {
token = strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer "))
}
if token == "" {
token = strings.TrimSpace(c.Query("token"))
}
if token == "" {
logger.Warn("Missing authorization token")
return forbiddenResponse(c)
}
if _, ok := allowed[token]; !ok {
logger.WithField("token", token).Warn("Invalid authorization token")
return forbiddenResponse(c)
}
c.Locals("authenticated", true)
c.Locals("token", token)
return c.Next()
}
}
func forbiddenResponse(c *fiber.Ctx) error {
c.Set("Content-Type", "text/plain")
return c.Status(fiber.StatusForbidden).SendString("Forbidden\n")
}
// SetupCompression 设置压缩中间件
// 启用响应压缩,减少传输数据量
func SetupCompression() fiber.Handler {
return func(c *fiber.Ctx) error {
// 设置Accept-Encoding头
c.Set("Accept-Encoding", "gzip, deflate")
return c.Next()
}
}