package middleware import ( "net/http" "strings" "github.com/gin-gonic/gin" "goaresv3/app/settings/models" "goaresv3/config" ) // DynamicCORS applies CORS policy from DB-backed whitelist/blacklist tables. func DynamicCORS() gin.HandlerFunc { debug := envBool("CORS_DEBUG", false) return func(c *gin.Context) { // Defaults for downstream middlewares (e.g. rate limit) c.Set("origin_whitelisted", false) c.Set("origin_blacklisted", false) origin := strings.TrimSpace(c.GetHeader("Origin")) if origin == "" { policyLogf(debug, "[cors] skip: no origin method=%s path=%s", c.Request.Method, c.Request.URL.Path) c.Next() return } if config.DB == nil { c.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{"error": "database is not connected"}) return } var blocked models.CorsBlacklist if err := config.DB. Where("origin = ? AND is_active = ?", origin, true). First(&blocked).Error; err == nil { c.Set("origin_blacklisted", true) policyLogf(debug, "[cors] blocked origin=%s method=%s path=%s", origin, c.Request.Method, c.Request.URL.Path) c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "origin is blocked"}) return } var whitelisted models.CorsWhitelist if err := config.DB. Where("origin = ? AND is_active = ?", origin, true). First(&whitelisted).Error; err == nil { c.Set("origin_whitelisted", true) policyLogf(debug, "[cors] whitelisted origin=%s method=%s path=%s", origin, c.Request.Method, c.Request.URL.Path) } else { policyLogf(debug, "[cors] pass(non-listed) origin=%s method=%s path=%s", origin, c.Request.Method, c.Request.URL.Path) } c.Header("Access-Control-Allow-Origin", origin) c.Header("Vary", "Origin") c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS") c.Header("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept") c.Header("Access-Control-Allow-Credentials", "true") if c.Request.Method == http.MethodOptions { policyLogf(debug, "[cors] preflight origin=%s path=%s", origin, c.Request.URL.Path) c.AbortWithStatus(http.StatusNoContent) return } c.Next() } }