package api import ( "log" "net/http" "strings" "time" ) // CORSMiddleware adds CORS headers to enable browser requests func CORSMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get("Origin") // In development mode, allow all localhost origins including demo sites if isLocalhostOrigin(origin) { w.Header().Set("Access-Control-Allow-Origin", origin) } else { // Fallback to wildcard for development (can be restricted in production) w.Header().Set("Access-Control-Allow-Origin", "*") } w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") w.Header().Set("Access-Control-Allow-Credentials", "true") // Note: Explicit OPTIONS handling is done via routes, not here next.ServeHTTP(w, r) }) } // LoggingMiddleware logs HTTP requests func LoggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() // Create a response writer wrapper to capture status code wrapper := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK} next.ServeHTTP(wrapper, r) log.Printf("%s %s %d %v", r.Method, r.URL.Path, wrapper.statusCode, time.Since(start)) }) } // responseWriter wraps http.ResponseWriter to capture status code type responseWriter struct { http.ResponseWriter statusCode int } func (rw *responseWriter) WriteHeader(code int) { rw.statusCode = code rw.ResponseWriter.WriteHeader(code) } // ContentTypeMiddleware ensures JSON responses have proper content type func ContentTypeMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Set default content type for API responses if r.URL.Path != "/" && (r.Method == "GET" || r.Method == "POST" || r.Method == "PUT") { w.Header().Set("Content-Type", "application/json") } next.ServeHTTP(w, r) }) } // HealthMiddleware provides a simple health check endpoint func HealthMiddleware() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(`{"status":"healthy","service":"insertr-server"}`)) } } // CORSPreflightHandler handles CORS preflight requests (OPTIONS) func CORSPreflightHandler(w http.ResponseWriter, r *http.Request) { origin := r.Header.Get("Origin") // Allow localhost and 127.0.0.1 on common development ports allowedOrigins := []string{ "http://localhost:3000", "http://localhost:8080", "http://127.0.0.1:3000", "http://127.0.0.1:8080", } // Check if origin is allowed originAllowed := false for _, allowed := range allowedOrigins { if origin == allowed { originAllowed = true break } } if originAllowed { w.Header().Set("Access-Control-Allow-Origin", origin) } else { // Fallback to wildcard for development w.Header().Set("Access-Control-Allow-Origin", "*") } w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") w.Header().Set("Access-Control-Allow-Credentials", "true") w.Header().Set("Access-Control-Max-Age", "86400") // Cache preflight for 24 hours w.WriteHeader(http.StatusOK) } // isLocalhostOrigin checks if an origin is a localhost/127.0.0.1 development origin func isLocalhostOrigin(origin string) bool { if origin == "" { return false } // Allow localhost and 127.0.0.1 on any port for development return strings.HasPrefix(origin, "http://localhost:") || strings.HasPrefix(origin, "https://localhost:") || strings.HasPrefix(origin, "http://127.0.0.1:") || strings.HasPrefix(origin, "https://127.0.0.1:") || origin == "http://localhost" || origin == "https://localhost" || origin == "http://127.0.0.1" || origin == "https://127.0.0.1" }