This commit is contained in:
jlimolina 2026-08-28 21:10:02 +02:00
parent 3991237685
commit a01050bf85
46 changed files with 3636 additions and 652 deletions

View file

@ -1,12 +1,17 @@
package middleware
import (
"fmt"
"net/http"
"strings"
"sync"
"time"
"github.com/gin-gonic/gin"
"github.com/rss2/backend/internal/auth"
"github.com/rss2/backend/internal/config"
"github.com/rss2/backend/internal/logger"
"golang.org/x/time/rate"
)
func AuthRequired() gin.HandlerFunc {
@ -63,23 +68,19 @@ func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
origin := c.Request.Header.Get("Origin")
allowed := false
for _, o := range allowedOrigins {
o = strings.TrimSpace(o)
if o == "*" || o == origin {
if o == origin {
allowed = true
break
}
}
if allowed {
if cfg.AllowedOrigins == "*" {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
} else {
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
}
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
}
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")
@ -96,17 +97,101 @@ func CORSMiddleware() gin.HandlerFunc {
func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
raw := c.Request.URL.RawQuery
requestID := c.GetHeader("X-Request-ID")
if requestID == "" {
requestID = c.GetString("request_id")
}
c.Next()
latency := time.Since(start)
status := c.Writer.Status()
if status >= 400 {
// Log error responses
clientIP := c.ClientIP()
method := c.Request.Method
if raw != "" {
path = path + "?" + raw
}
log := logger.GetLogger().With().
Str("method", method).
Str("path", path).
Int("status", status).
Str("client_ip", clientIP).
Dur("latency", latency).
Logger()
if requestID != "" {
log = log.With().Str("request_id", requestID).Logger()
}
if status >= 500 {
log.Error().Msg("Server error")
} else if status >= 400 {
log.Warn().Msg("Client error")
} else {
log.Info().Msg("Request completed")
}
}
}
type clientLimiter struct {
limiter *rate.Limiter
lastSeen time.Time
}
var (
clientLimiters = make(map[string]*clientLimiter)
limitersMu sync.Mutex
)
func RateLimitMiddleware(requestsPerMinute int) gin.HandlerFunc {
limit := rate.Limit(requestsPerMinute) / 60
burst := requestsPerMinute
return func(c *gin.Context) {
ip := c.ClientIP()
limitersMu.Lock()
cl, exists := clientLimiters[ip]
if !exists {
cl = &clientLimiter{limiter: rate.NewLimiter(limit, burst)}
clientLimiters[ip] = cl
}
cl.lastSeen = time.Now()
limiter := cl.limiter
limitersMu.Unlock()
if !limiter.Allow() {
c.JSON(http.StatusTooManyRequests, gin.H{"error": "Rate limit exceeded"})
c.Abort()
return
}
go func() {
time.Sleep(10 * time.Minute)
limitersMu.Lock()
if cl, ok := clientLimiters[ip]; ok && time.Since(cl.lastSeen) > 10*time.Minute {
delete(clientLimiters, ip)
}
limitersMu.Unlock()
}()
c.Next()
}
}
func RequestIDMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
requestID := c.GetHeader("X-Request-ID")
if requestID == "" {
requestID = fmt.Sprintf("%d", time.Now().UnixNano())
}
c.Set("request_id", requestID)
c.Header("X-Request-ID", requestID)
c.Next()
}
}