package middleware import ( "context" "net/http" "github.com/google/uuid" "git.threesix.ai/jordan/sp4-verify-1770325799/pkg/httpcontext" "git.threesix.ai/jordan/sp4-verify-1770325799/pkg/logging" ) // RequestIDHeader is the header name for request IDs. const RequestIDHeader = "X-Request-ID" // RequestID returns middleware that generates/extracts request IDs. // // If X-Request-ID header is present in the incoming request, uses that value. // This allows clients to set their own request IDs for tracking purposes. // Otherwise generates a new UUID. // // The request ID is stored in context using httpcontext.SetRequestID and // also set in the X-Request-ID response header for client correlation. // // This middleware is idempotent - if a request ID is already present in the // context (from a previous middleware), it will not be overwritten. // // Usage: // // r.Use(middleware.RequestID()) // r.Use(middleware.RequestLogger(logger)) // // In handlers, retrieve the request ID: // // requestID, ok := httpcontext.GetRequestID(r.Context()) func RequestID() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Check if request ID already in context (idempotent) if existingID, ok := httpcontext.GetRequestID(r.Context()); ok && existingID != "" { // Already set, set response header and continue w.Header().Set(RequestIDHeader, existingID) next.ServeHTTP(w, r) return } // Try to get request ID from incoming header requestID := r.Header.Get(RequestIDHeader) // If not present, generate a new UUID if requestID == "" { requestID = uuid.New().String() } // Store in context (both httpcontext and logging) ctx := httpcontext.SetRequestID(r.Context(), requestID) ctx = logging.WithRequestID(ctx, requestID) // Set response header for client correlation w.Header().Set(RequestIDHeader, requestID) // Continue with request ID in context next.ServeHTTP(w, r.WithContext(ctx)) }) } } // GetRequestID returns the request ID from context. // This is a convenience wrapper around httpcontext.GetRequestID. // // Returns the request ID string if present, empty string if not found. func GetRequestID(ctx context.Context) string { requestID, _ := httpcontext.GetRequestID(ctx) return requestID }