package middleware import ( "net/http" "os" "strconv" "sync" "time" "github.com/gin-gonic/gin" ) // DynamicCORS CORS davranisini ortama gore dinamik ayarlar. func DynamicCORS() gin.HandlerFunc { allowOrigin := os.Getenv("CORS_ALLOW_ORIGIN") if allowOrigin == "" { allowOrigin = "*" } return func(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Origin", allowOrigin) c.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type") c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS") if c.Request.Method == http.MethodOptions { c.AbortWithStatus(http.StatusNoContent) return } c.Next() } } type clientWindow struct { count int windowEnds time.Time } // DynamicRateLimit IP bazli basit bir dakika penceresi limiti uygular. func DynamicRateLimit() gin.HandlerFunc { limit := 120 if v := os.Getenv("RATE_LIMIT_RPM"); v != "" { if parsed, err := strconv.Atoi(v); err == nil && parsed > 0 { limit = parsed } } var mu sync.Mutex clients := map[string]*clientWindow{} return func(c *gin.Context) { ip := c.ClientIP() now := time.Now() mu.Lock() entry, ok := clients[ip] if !ok || now.After(entry.windowEnds) { entry = &clientWindow{count: 0, windowEnds: now.Add(time.Minute)} clients[ip] = entry } entry.count++ remaining := limit - entry.count resetIn := int(time.Until(entry.windowEnds).Seconds()) mu.Unlock() c.Header("X-RateLimit-Limit", strconv.Itoa(limit)) if remaining < 0 { remaining = 0 } c.Header("X-RateLimit-Remaining", strconv.Itoa(remaining)) c.Header("X-RateLimit-Reset", strconv.Itoa(resetIn)) if entry.count > limit { c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{"error": "rate limit asildi"}) return } c.Next() } }