package middlewares import ( "fmt" "net/http" "strings" "time" "gauth-central/internal/services" "gauth-central/pkg/utils" "github.com/gin-gonic/gin" ) // RateLimitMiddleware creates a rate limiting middleware func RateLimitMiddleware(maxRequests int64, duration time.Duration) gin.HandlerFunc { cacheService := services.NewCacheService() settingsService := services.NewSettingsService() return func(c *gin.Context) { // Get client IP clientIP := c.ClientIP() path := c.Request.URL.Path method := c.Request.Method // Skip checks for localhost (hardcoded safety) if clientIP == "::1" || clientIP == "127.0.0.1" || clientIP == "localhost" { fmt.Printf("%s[LOCALHOST BYPASS]%s IP: %s accessed %s %s\n", utils.ColorCyan, utils.ColorReset, clientIP, method, path) c.Next() return } // 1. Check Blacklist from DB blacklist, err := settingsService.GetActiveBlacklistOrigins() if err == nil { for _, blocked := range blacklist { if blocked == clientIP || strings.Contains(blocked, clientIP) { fmt.Printf("%s[BLACKLIST BLOCKED]%s IP: %s tried to access %s %s\n", utils.ColorRed, utils.ColorReset, clientIP, method, path) c.JSON(http.StatusForbidden, gin.H{ "error": "Access denied. Your IP is blacklisted.", }) c.Abort() return } } } // 2. Check Whitelist from DB (Skip Rate Limit) whitelist, err := settingsService.GetActiveWhitelistOrigins() if err == nil { for _, allowed := range whitelist { if allowed == clientIP || strings.Contains(allowed, clientIP) { fmt.Printf("%s[WHITELIST ALLOWED]%s IP: %s accessed %s %s (Rate Limit Skipped)\n", utils.ColorGreen, utils.ColorReset, clientIP, method, path) c.Next() return } } } key := clientIP // Increment counter count, err := cacheService.IncrementRateLimit(key, duration) if err != nil { // If Redis is down, allow the request but log error fmt.Printf("%s[REDIS ERROR]%s Could not increment rate limit for %s: %v\n", utils.ColorRed, utils.ColorReset, clientIP, err) c.Next() return } remaining := maxRequests - count if remaining < 0 { remaining = 0 } // Check if limit exceeded if count > maxRequests { fmt.Printf("%s[RATE LIMIT EXCEEDED]%s IP: %s - %s %s - Limit: %d\n", utils.ColorYellow, utils.ColorReset, clientIP, method, path, maxRequests) c.JSON(http.StatusTooManyRequests, gin.H{ "error": "Too many requests. Please try again later.", }) c.Abort() return } // Log normal access with remaining limit fmt.Printf("[Rate Limit] IP: %s - %s %s - Used: %d/%d - Remaining: %d\n", clientIP, method, path, count, maxRequests, remaining) c.Next() } } // DynamicRateLimitMiddleware - Database'den ayarları okuyan rate limit middleware func DynamicRateLimitMiddleware(settingName string, settingsService *services.SettingsService) gin.HandlerFunc { cacheService := services.NewCacheService() return func(c *gin.Context) { // Get client IP clientIP := c.ClientIP() path := c.Request.URL.Path method := c.Request.Method // Skip checks for localhost if clientIP == "::1" || clientIP == "127.0.0.1" || clientIP == "localhost" { fmt.Printf("%s[LOCALHOST BYPASS]%s IP: %s accessed %s %s\n", utils.ColorCyan, utils.ColorReset, clientIP, method, path) c.Next() return } // 1. Check Blacklist from DB blacklist, err := settingsService.GetActiveBlacklistOrigins() if err == nil { for _, blocked := range blacklist { if blocked == clientIP || strings.Contains(blocked, clientIP) { fmt.Printf("%s[BLACKLIST BLOCKED]%s IP: %s tried to access %s %s\n", utils.ColorRed, utils.ColorReset, clientIP, method, path) c.JSON(http.StatusForbidden, gin.H{ "error": "Access denied. Your IP is blacklisted.", }) c.Abort() return } } } // 2. Check Whitelist from DB (Skip Rate Limit) whitelist, err := settingsService.GetActiveWhitelistOrigins() if err == nil { for _, allowed := range whitelist { if allowed == clientIP || strings.Contains(allowed, clientIP) { fmt.Printf("%s[WHITELIST ALLOWED]%s IP: %s accessed %s %s (Rate Limit Skipped)\n", utils.ColorGreen, utils.ColorReset, clientIP, method, path) c.Next() return } } } // Get rate limit settings from database/cache setting, err := settingsService.GetRateLimitSettingByName(settingName) if err != nil || setting == nil { // If error or not found, use default and allow c.Next() return } // Check if setting is active if !setting.IsActive { c.Next() return } key := settingName + ":" + clientIP // Increment counter duration := time.Duration(setting.WindowSeconds) * time.Second count, err := cacheService.IncrementRateLimit(key, duration) if err != nil { // If Redis is down, allow the request c.Next() return } remaining := setting.MaxRequests - count if remaining < 0 { remaining = 0 } // Check if limit exceeded if count > setting.MaxRequests { fmt.Printf("%s[RATE LIMIT EXCEEDED]%s IP: %s - %s %s - Limit: %d\n", utils.ColorYellow, utils.ColorReset, clientIP, method, path, setting.MaxRequests) c.JSON(http.StatusTooManyRequests, gin.H{ "error": "Too many requests. Please try again later.", "limit": setting.MaxRequests, "window": setting.WindowSeconds, "retry_after": setting.WindowSeconds, }) c.Abort() return } // Log normal access with remaining limit fmt.Printf("[Rate Limit] IP: %s - %s %s - Used: %d/%d - Remaining: %d\n", clientIP, method, path, count, setting.MaxRequests, remaining) c.Next() } } // LoginRateLimitMiddleware limits login attempts per IP func LoginRateLimitMiddleware() gin.HandlerFunc { return RateLimitMiddleware(5, 1*time.Minute) // 5 login attempts per minute } // RegisterRateLimitMiddleware limits registration attempts per IP func RegisterRateLimitMiddleware() gin.HandlerFunc { return RateLimitMiddleware(3, 5*time.Minute) // 3 registration attempts per 5 minutes } // APIRateLimitMiddleware general API rate limiting func APIRateLimitMiddleware() gin.HandlerFunc { return RateLimitMiddleware(100, 1*time.Minute) // 100 requests per minute }