// Package middleware provides HTTP middleware components for the CLI Proxy API server. // This file contains centralized error handling middleware for consistent API responses. package middleware import ( "net/http" "github.com/gin-gonic/gin" "github.com/router-for-me/CLIProxyAPI/v6/internal/domain/errors" ) // ErrorResponse represents a standardized error response type ErrorResponse struct { Success bool `json:"success"` Error *APIError `json:"error,omitempty"` RequestID string `json:"request_id,omitempty"` } // APIError represents error details type APIError struct { Code string `json:"code"` Message string `json:"message"` Field string `json:"field,omitempty"` } // ErrorHandlerMiddleware creates a Gin middleware that provides centralized // error handling. It catches errors from the context and formats them into // standardized error responses. func ErrorHandlerMiddleware() gin.HandlerFunc { return func(c *gin.Context) { c.Next() // Check if there are any errors if len(c.Errors) > 0 { err := c.Errors.Last() handleError(c, err) } } } // handleError converts various error types to standardized HTTP responses func handleError(c *gin.Context, err *gin.Error) { requestID := GetCorrelationID(c) // Check if it's a domain error if domainErr, ok := err.Err.(*errors.DomainError); ok { statusCode := domainErr.HTTPStatusCode() apiErr := &APIError{ Code: string(domainErr.Code), Message: domainErr.Message, } // Extract field from details if available if domainErr.Details != nil { if field, ok := domainErr.Details["field"].(string); ok { apiErr.Field = field } } response := ErrorResponse{ Success: false, RequestID: requestID, Error: apiErr, } c.JSON(statusCode, response) return } // Handle common HTTP status errors switch err.Type { case gin.ErrorTypeBind: c.JSON(http.StatusBadRequest, ErrorResponse{ Success: false, RequestID: requestID, Error: &APIError{ Code: "INVALID_INPUT", Message: "Invalid request format: " + err.Err.Error(), }, }) case gin.ErrorTypeRender: c.JSON(http.StatusInternalServerError, ErrorResponse{ Success: false, RequestID: requestID, Error: &APIError{ Code: "RENDER_ERROR", Message: "Failed to render response", }, }) default: c.JSON(http.StatusInternalServerError, ErrorResponse{ Success: false, RequestID: requestID, Error: &APIError{ Code: "INTERNAL_ERROR", Message: "An internal error occurred", }, }) } } // AbortWithDomainError aborts the request with a domain error func AbortWithDomainError(c *gin.Context, err *errors.DomainError) { c.Error(err) c.Abort() } // RespondWithError adds an error to the context without aborting func RespondWithError(c *gin.Context, err error) { c.Error(err) }