package middleware import ( "net/http" "strings" ) var ( allowedOriginSuffixes = []string{ ".wavult.com", ".aamos.ai", } allowedMethods = "GET, POST, PUT, PATCH, DELETE, OPTIONS" allowedHeaders = "Accept, Authorization, Content-Type, X-Request-ID" exposeHeaders = "X-Request-ID" ) // CORS returns a middleware that allows cross-origin requests from: // - any localhost origin (http://localhost:*, https://localhost:*) // - any *.wavult.com subdomain // - any *.aamos.ai subdomain // // Exact-origin matching is used; wildcard Access-Control-Allow-Origin is // intentionally avoided so credentials (cookies, Authorization headers) work. func CORS(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get("Origin") if origin != "" && isAllowedOrigin(origin) { w.Header().Set("Access-Control-Allow-Origin", origin) w.Header().Set("Access-Control-Allow-Credentials", "true") w.Header().Set("Vary", "Origin") if r.Method == http.MethodOptions { // Preflight — respond without calling the next handler. w.Header().Set("Access-Control-Allow-Methods", allowedMethods) w.Header().Set("Access-Control-Allow-Headers", allowedHeaders) w.Header().Set("Access-Control-Max-Age", "86400") w.WriteHeader(http.StatusNoContent) return } w.Header().Set("Access-Control-Expose-Headers", exposeHeaders) } next.ServeHTTP(w, r) }) } // isAllowedOrigin reports whether origin is permitted. func isAllowedOrigin(origin string) bool { // Strip scheme for host matching. host := origin if i := strings.Index(host, "://"); i != -1 { host = host[i+3:] } // Strip port suffix if present. bare := host if i := strings.LastIndex(host, ":"); i != -1 { bare = host[:i] } // localhost — any port, http or https. if bare == "localhost" || bare == "127.0.0.1" || bare == "::1" { return true } // *.wavult.com and *.aamos.ai for _, suffix := range allowedOriginSuffixes { if strings.HasSuffix(bare, suffix) { return true } } return false }