package response import ( "net/http" "time" apperrors "veza-backend-api/internal/errors" "github.com/gin-gonic/gin" ) // APIResponse is the unified response envelope type APIResponse struct { Success bool `json:"success"` Data interface{} `json:"data,omitempty"` Error interface{} `json:"error,omitempty"` } // Success sends a successful JSON response func Success(c *gin.Context, data interface{}, message ...string) { response := gin.H{ "success": true, "data": data, } if len(message) > 0 { response["message"] = message[0] } c.JSON(http.StatusOK, response) } // Created sends a 201 Created response func Created(c *gin.Context, data interface{}, message ...string) { response := gin.H{ "success": true, "data": data, } if len(message) > 0 { response["message"] = message[0] } c.JSON(http.StatusCreated, response) } // BadRequest sends a 400 Bad Request response // P0: Migré vers format AppError standardisé func BadRequest(c *gin.Context, message string) { Error(c, http.StatusBadRequest, message) } // Unauthorized sends a 401 Unauthorized response // P0: Migré vers format AppError standardisé func Unauthorized(c *gin.Context, message string) { Error(c, http.StatusUnauthorized, message) } // Forbidden sends a 403 Forbidden response // P0: Migré vers format AppError standardisé func Forbidden(c *gin.Context, message string) { Error(c, http.StatusForbidden, message) } // NotFound sends a 404 Not Found response // P0: Migré vers format AppError standardisé func NotFound(c *gin.Context, message string) { Error(c, http.StatusNotFound, message) } // InternalServerError sends a 500 Internal Server Error response // P0: Migré vers format AppError standardisé func InternalServerError(c *gin.Context, message string) { Error(c, http.StatusInternalServerError, message) } // Error sends a custom error response with specified status code // P0: Migré vers format AppError standardisé func Error(c *gin.Context, status int, message string) { var errorCode apperrors.ErrorCode switch status { case http.StatusBadRequest: errorCode = apperrors.ErrCodeValidation case http.StatusUnauthorized: errorCode = apperrors.ErrCodeUnauthorized case http.StatusForbidden: errorCode = apperrors.ErrCodeForbidden case http.StatusNotFound: errorCode = apperrors.ErrCodeNotFound case http.StatusConflict: errorCode = apperrors.ErrCodeConflict case http.StatusTooManyRequests: errorCode = apperrors.ErrCodeRateLimitExceeded case http.StatusServiceUnavailable: errorCode = apperrors.ErrCodeServiceUnavailable default: errorCode = apperrors.ErrCodeInternal } appErr := apperrors.New(errorCode, message) RespondWithAppError(c, appErr) } // RespondWithAppError répond avec une AppError au format standardisé ORIGIN_API_SPECIFICATION // Le code HTTP est dérivé automatiquement du ErrorCode func RespondWithAppError(c *gin.Context, appErr *apperrors.AppError) { statusCode := mapErrorCodeToHTTPStatus(appErr.Code) errorData := struct { Code int `json:"code"` Message string `json:"message"` Details []apperrors.ErrorDetail `json:"details,omitempty"` RequestID string `json:"request_id,omitempty"` Timestamp string `json:"timestamp"` Context map[string]interface{} `json:"context,omitempty"` }{ Code: int(appErr.Code), Message: appErr.Message, Details: appErr.Details, RequestID: c.GetString("request_id"), Timestamp: time.Now().UTC().Format(time.RFC3339), Context: appErr.Context, } c.JSON(statusCode, APIResponse{ Success: false, Data: nil, Error: errorData, }) } func mapErrorCodeToHTTPStatus(code apperrors.ErrorCode) int { if code >= 1000 && code < 2000 { switch code { case apperrors.ErrCodeForbidden, apperrors.ErrCodeQuotaExceeded: return http.StatusForbidden default: return http.StatusUnauthorized } } if code >= 2000 && code < 3000 { return http.StatusBadRequest } if code >= 3000 && code < 4000 { switch code { case apperrors.ErrCodeNotFound: return http.StatusNotFound case apperrors.ErrCodeAlreadyExists, apperrors.ErrCodeConflict: return http.StatusConflict case apperrors.ErrCodeLocked: return http.StatusLocked default: return http.StatusNotFound } } if code >= 4000 && code < 5000 { return http.StatusUnprocessableEntity } if code >= 5000 && code < 5100 { return http.StatusTooManyRequests } if code >= 6000 && code < 7000 { if code == apperrors.ErrCodeServiceUnavailable { return http.StatusServiceUnavailable } return http.StatusBadGateway } if code >= 9000 && code < 10000 { return http.StatusInternalServerError } return http.StatusInternalServerError } // ValidationError sends a 400 Bad Request response with detailed validation errors func ValidationError(c *gin.Context, message string, details map[string]string) { errorDetails := make([]apperrors.ErrorDetail, 0, len(details)) for field, msg := range details { errorDetails = append(errorDetails, apperrors.ErrorDetail{ Field: field, Message: msg, }) } appErr := apperrors.NewValidationError(message, errorDetails...) RespondWithAppError(c, appErr) }