org
This commit is contained in:
360
kms/internal/errors/errors.go
Normal file
360
kms/internal/errors/errors.go
Normal file
@ -0,0 +1,360 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ErrorCode represents different types of errors in the system
|
||||
type ErrorCode string
|
||||
|
||||
const (
|
||||
// Authentication and Authorization errors
|
||||
ErrUnauthorized ErrorCode = "UNAUTHORIZED"
|
||||
ErrForbidden ErrorCode = "FORBIDDEN"
|
||||
ErrInvalidToken ErrorCode = "INVALID_TOKEN"
|
||||
ErrTokenExpired ErrorCode = "TOKEN_EXPIRED"
|
||||
ErrInvalidCredentials ErrorCode = "INVALID_CREDENTIALS"
|
||||
|
||||
// Validation errors
|
||||
ErrValidationFailed ErrorCode = "VALIDATION_FAILED"
|
||||
ErrInvalidInput ErrorCode = "INVALID_INPUT"
|
||||
ErrMissingField ErrorCode = "MISSING_FIELD"
|
||||
ErrInvalidFormat ErrorCode = "INVALID_FORMAT"
|
||||
|
||||
// Resource errors
|
||||
ErrNotFound ErrorCode = "NOT_FOUND"
|
||||
ErrAlreadyExists ErrorCode = "ALREADY_EXISTS"
|
||||
ErrConflict ErrorCode = "CONFLICT"
|
||||
|
||||
// System errors
|
||||
ErrInternal ErrorCode = "INTERNAL_ERROR"
|
||||
ErrDatabase ErrorCode = "DATABASE_ERROR"
|
||||
ErrExternal ErrorCode = "EXTERNAL_SERVICE_ERROR"
|
||||
ErrTimeout ErrorCode = "TIMEOUT"
|
||||
ErrRateLimit ErrorCode = "RATE_LIMIT_EXCEEDED"
|
||||
|
||||
// Business logic errors
|
||||
ErrInsufficientPermissions ErrorCode = "INSUFFICIENT_PERMISSIONS"
|
||||
ErrApplicationNotFound ErrorCode = "APPLICATION_NOT_FOUND"
|
||||
ErrTokenNotFound ErrorCode = "TOKEN_NOT_FOUND"
|
||||
ErrPermissionNotFound ErrorCode = "PERMISSION_NOT_FOUND"
|
||||
ErrInvalidApplication ErrorCode = "INVALID_APPLICATION"
|
||||
ErrTokenCreationFailed ErrorCode = "TOKEN_CREATION_FAILED"
|
||||
)
|
||||
|
||||
// AppError represents an application error with context
|
||||
type AppError struct {
|
||||
Code ErrorCode `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Details string `json:"details,omitempty"`
|
||||
StatusCode int `json:"-"`
|
||||
Internal error `json:"-"`
|
||||
Context map[string]interface{} `json:"context,omitempty"`
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (e *AppError) Error() string {
|
||||
if e.Internal != nil {
|
||||
return fmt.Sprintf("%s: %s (internal: %v)", e.Code, e.Message, e.Internal)
|
||||
}
|
||||
return fmt.Sprintf("%s: %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
// WithContext adds context information to the error
|
||||
func (e *AppError) WithContext(key string, value interface{}) *AppError {
|
||||
if e.Context == nil {
|
||||
e.Context = make(map[string]interface{})
|
||||
}
|
||||
e.Context[key] = value
|
||||
return e
|
||||
}
|
||||
|
||||
// WithDetails adds additional details to the error
|
||||
func (e *AppError) WithDetails(details string) *AppError {
|
||||
e.Details = details
|
||||
return e
|
||||
}
|
||||
|
||||
// WithInternal adds the underlying error
|
||||
func (e *AppError) WithInternal(err error) *AppError {
|
||||
e.Internal = err
|
||||
return e
|
||||
}
|
||||
|
||||
// New creates a new application error
|
||||
func New(code ErrorCode, message string) *AppError {
|
||||
return &AppError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
StatusCode: getHTTPStatusCode(code),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap wraps an existing error with application error context
|
||||
func Wrap(err error, code ErrorCode, message string) *AppError {
|
||||
return &AppError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
StatusCode: getHTTPStatusCode(code),
|
||||
Internal: err,
|
||||
}
|
||||
}
|
||||
|
||||
// getHTTPStatusCode maps error codes to HTTP status codes
|
||||
func getHTTPStatusCode(code ErrorCode) int {
|
||||
switch code {
|
||||
case ErrUnauthorized, ErrInvalidToken, ErrTokenExpired, ErrInvalidCredentials:
|
||||
return http.StatusUnauthorized
|
||||
case ErrForbidden, ErrInsufficientPermissions:
|
||||
return http.StatusForbidden
|
||||
case ErrValidationFailed, ErrInvalidInput, ErrMissingField, ErrInvalidFormat:
|
||||
return http.StatusBadRequest
|
||||
case ErrNotFound, ErrApplicationNotFound, ErrTokenNotFound, ErrPermissionNotFound:
|
||||
return http.StatusNotFound
|
||||
case ErrAlreadyExists, ErrConflict:
|
||||
return http.StatusConflict
|
||||
case ErrRateLimit:
|
||||
return http.StatusTooManyRequests
|
||||
case ErrTimeout:
|
||||
return http.StatusRequestTimeout
|
||||
case ErrInternal, ErrDatabase, ErrExternal, ErrTokenCreationFailed:
|
||||
return http.StatusInternalServerError
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
|
||||
// IsRetryable determines if an error is retryable
|
||||
func (e *AppError) IsRetryable() bool {
|
||||
switch e.Code {
|
||||
case ErrTimeout, ErrExternal, ErrDatabase:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsClientError determines if an error is a client error (4xx)
|
||||
func (e *AppError) IsClientError() bool {
|
||||
return e.StatusCode >= 400 && e.StatusCode < 500
|
||||
}
|
||||
|
||||
// IsServerError determines if an error is a server error (5xx)
|
||||
func (e *AppError) IsServerError() bool {
|
||||
return e.StatusCode >= 500
|
||||
}
|
||||
|
||||
// Common error constructors for frequently used errors
|
||||
|
||||
// NewUnauthorizedError creates an unauthorized error
|
||||
func NewUnauthorizedError(message string) *AppError {
|
||||
return New(ErrUnauthorized, message)
|
||||
}
|
||||
|
||||
// NewForbiddenError creates a forbidden error
|
||||
func NewForbiddenError(message string) *AppError {
|
||||
return New(ErrForbidden, message)
|
||||
}
|
||||
|
||||
// NewValidationError creates a validation error
|
||||
func NewValidationError(message string) *AppError {
|
||||
return New(ErrValidationFailed, message)
|
||||
}
|
||||
|
||||
// NewNotFoundError creates a not found error
|
||||
func NewNotFoundError(resource string) *AppError {
|
||||
return New(ErrNotFound, fmt.Sprintf("%s not found", resource))
|
||||
}
|
||||
|
||||
// NewAlreadyExistsError creates an already exists error
|
||||
func NewAlreadyExistsError(resource string) *AppError {
|
||||
return New(ErrAlreadyExists, fmt.Sprintf("%s already exists", resource))
|
||||
}
|
||||
|
||||
// NewInternalError creates an internal server error
|
||||
func NewInternalError(message string) *AppError {
|
||||
return New(ErrInternal, message)
|
||||
}
|
||||
|
||||
// NewDatabaseError creates a database error
|
||||
func NewDatabaseError(operation string, err error) *AppError {
|
||||
return Wrap(err, ErrDatabase, fmt.Sprintf("Database operation failed: %s", operation))
|
||||
}
|
||||
|
||||
// NewTokenError creates a token-related error
|
||||
func NewTokenError(message string) *AppError {
|
||||
return New(ErrInvalidToken, message)
|
||||
}
|
||||
|
||||
// NewApplicationError creates an application-related error
|
||||
func NewApplicationError(message string) *AppError {
|
||||
return New(ErrInvalidApplication, message)
|
||||
}
|
||||
|
||||
// NewPermissionError creates a permission-related error
|
||||
func NewPermissionError(message string) *AppError {
|
||||
return New(ErrInsufficientPermissions, message)
|
||||
}
|
||||
|
||||
// NewAuthenticationError creates an authentication error
|
||||
func NewAuthenticationError(message string) *AppError {
|
||||
return New(ErrUnauthorized, message)
|
||||
}
|
||||
|
||||
// NewConfigurationError creates a configuration error
|
||||
func NewConfigurationError(message string) *AppError {
|
||||
return New(ErrInternal, message)
|
||||
}
|
||||
|
||||
// ErrorResponse represents the JSON error response format
|
||||
type ErrorResponse struct {
|
||||
Error string `json:"error"`
|
||||
Message string `json:"message"`
|
||||
Code ErrorCode `json:"code"`
|
||||
Details string `json:"details,omitempty"`
|
||||
Context map[string]interface{} `json:"context,omitempty"`
|
||||
}
|
||||
|
||||
// ToResponse converts an AppError to an ErrorResponse
|
||||
func (e *AppError) ToResponse() ErrorResponse {
|
||||
return ErrorResponse{
|
||||
Error: string(e.Code),
|
||||
Message: e.Message,
|
||||
Code: e.Code,
|
||||
Details: e.Details,
|
||||
Context: e.Context,
|
||||
}
|
||||
}
|
||||
|
||||
// Recovery handles panic recovery and converts to appropriate errors
|
||||
func Recovery(recovered interface{}) *AppError {
|
||||
switch v := recovered.(type) {
|
||||
case *AppError:
|
||||
return v
|
||||
case error:
|
||||
return Wrap(v, ErrInternal, "Internal server error occurred")
|
||||
case string:
|
||||
return New(ErrInternal, v)
|
||||
default:
|
||||
return New(ErrInternal, "Unknown internal error occurred")
|
||||
}
|
||||
}
|
||||
|
||||
// Chain represents a chain of errors for better error tracking
|
||||
type Chain struct {
|
||||
errors []*AppError
|
||||
}
|
||||
|
||||
// NewChain creates a new error chain
|
||||
func NewChain() *Chain {
|
||||
return &Chain{
|
||||
errors: make([]*AppError, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds an error to the chain
|
||||
func (c *Chain) Add(err *AppError) *Chain {
|
||||
c.errors = append(c.errors, err)
|
||||
return c
|
||||
}
|
||||
|
||||
// HasErrors returns true if the chain has any errors
|
||||
func (c *Chain) HasErrors() bool {
|
||||
return len(c.errors) > 0
|
||||
}
|
||||
|
||||
// First returns the first error in the chain
|
||||
func (c *Chain) First() *AppError {
|
||||
if len(c.errors) == 0 {
|
||||
return nil
|
||||
}
|
||||
return c.errors[0]
|
||||
}
|
||||
|
||||
// Last returns the last error in the chain
|
||||
func (c *Chain) Last() *AppError {
|
||||
if len(c.errors) == 0 {
|
||||
return nil
|
||||
}
|
||||
return c.errors[len(c.errors)-1]
|
||||
}
|
||||
|
||||
// All returns all errors in the chain
|
||||
func (c *Chain) All() []*AppError {
|
||||
return c.errors
|
||||
}
|
||||
|
||||
// Error implements the error interface for the chain
|
||||
func (c *Chain) Error() string {
|
||||
if len(c.errors) == 0 {
|
||||
return "no errors"
|
||||
}
|
||||
if len(c.errors) == 1 {
|
||||
return c.errors[0].Error()
|
||||
}
|
||||
return fmt.Sprintf("multiple errors: %s (and %d more)", c.errors[0].Error(), len(c.errors)-1)
|
||||
}
|
||||
|
||||
// Helper functions to check error types
|
||||
|
||||
// IsNotFound checks if an error is a not found error
|
||||
func IsNotFound(err error) bool {
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
return appErr.Code == ErrNotFound || appErr.Code == ErrApplicationNotFound ||
|
||||
appErr.Code == ErrTokenNotFound || appErr.Code == ErrPermissionNotFound
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsValidationError checks if an error is a validation error
|
||||
func IsValidationError(err error) bool {
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
return appErr.Code == ErrValidationFailed || appErr.Code == ErrInvalidInput ||
|
||||
appErr.Code == ErrMissingField || appErr.Code == ErrInvalidFormat
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsAuthenticationError checks if an error is an authentication error
|
||||
func IsAuthenticationError(err error) bool {
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
return appErr.Code == ErrUnauthorized || appErr.Code == ErrInvalidToken ||
|
||||
appErr.Code == ErrTokenExpired || appErr.Code == ErrInvalidCredentials
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsAuthorizationError checks if an error is an authorization error
|
||||
func IsAuthorizationError(err error) bool {
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
return appErr.Code == ErrForbidden || appErr.Code == ErrInsufficientPermissions
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsConflictError checks if an error is a conflict error
|
||||
func IsConflictError(err error) bool {
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
return appErr.Code == ErrAlreadyExists || appErr.Code == ErrConflict
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInternalError checks if an error is an internal server error
|
||||
func IsInternalError(err error) bool {
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
return appErr.Code == ErrInternal || appErr.Code == ErrDatabase ||
|
||||
appErr.Code == ErrExternal || appErr.Code == ErrTokenCreationFailed
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsConfigurationError checks if an error is a configuration error
|
||||
func IsConfigurationError(err error) bool {
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
// Configuration errors are typically mapped to internal errors
|
||||
return appErr.Code == ErrInternal && appErr.Message != ""
|
||||
}
|
||||
return false
|
||||
}
|
||||
267
kms/internal/errors/secure_responses.go
Normal file
267
kms/internal/errors/secure_responses.go
Normal file
@ -0,0 +1,267 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// SecureErrorResponse represents a sanitized error response for clients
|
||||
type SecureErrorResponse struct {
|
||||
Error string `json:"error"`
|
||||
Message string `json:"message"`
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
Code int `json:"code"`
|
||||
}
|
||||
|
||||
// ErrorHandler provides secure error handling for HTTP responses
|
||||
type ErrorHandler struct {
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewErrorHandler creates a new secure error handler
|
||||
func NewErrorHandler(logger *zap.Logger) *ErrorHandler {
|
||||
return &ErrorHandler{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleError handles errors securely by logging detailed information and returning sanitized responses
|
||||
func (eh *ErrorHandler) HandleError(c *gin.Context, err error, userMessage string) {
|
||||
requestID := eh.getOrGenerateRequestID(c)
|
||||
|
||||
// Log detailed error information for internal debugging
|
||||
eh.logger.Error("HTTP request error",
|
||||
zap.String("request_id", requestID),
|
||||
zap.String("path", c.Request.URL.Path),
|
||||
zap.String("method", c.Request.Method),
|
||||
zap.String("user_agent", c.Request.UserAgent()),
|
||||
zap.String("remote_addr", c.ClientIP()),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
// Determine appropriate HTTP status code and error type
|
||||
statusCode, errorType := eh.determineErrorResponse(err)
|
||||
|
||||
// Create sanitized response
|
||||
response := SecureErrorResponse{
|
||||
Error: errorType,
|
||||
Message: eh.sanitizeErrorMessage(userMessage, err),
|
||||
RequestID: requestID,
|
||||
Code: statusCode,
|
||||
}
|
||||
|
||||
c.JSON(statusCode, response)
|
||||
}
|
||||
|
||||
// HandleValidationError handles input validation errors
|
||||
func (eh *ErrorHandler) HandleValidationError(c *gin.Context, field string, message string) {
|
||||
requestID := eh.getOrGenerateRequestID(c)
|
||||
|
||||
eh.logger.Warn("Validation error",
|
||||
zap.String("request_id", requestID),
|
||||
zap.String("field", field),
|
||||
zap.String("path", c.Request.URL.Path),
|
||||
zap.String("method", c.Request.Method),
|
||||
)
|
||||
|
||||
response := SecureErrorResponse{
|
||||
Error: "validation_error",
|
||||
Message: "Invalid input provided",
|
||||
RequestID: requestID,
|
||||
Code: http.StatusBadRequest,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusBadRequest, response)
|
||||
}
|
||||
|
||||
// HandleAuthenticationError handles authentication failures
|
||||
func (eh *ErrorHandler) HandleAuthenticationError(c *gin.Context, err error) {
|
||||
requestID := eh.getOrGenerateRequestID(c)
|
||||
|
||||
eh.logger.Warn("Authentication error",
|
||||
zap.String("request_id", requestID),
|
||||
zap.String("path", c.Request.URL.Path),
|
||||
zap.String("method", c.Request.Method),
|
||||
zap.String("remote_addr", c.ClientIP()),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
response := SecureErrorResponse{
|
||||
Error: "authentication_failed",
|
||||
Message: "Authentication required",
|
||||
RequestID: requestID,
|
||||
Code: http.StatusUnauthorized,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusUnauthorized, response)
|
||||
}
|
||||
|
||||
// HandleAuthorizationError handles authorization failures
|
||||
func (eh *ErrorHandler) HandleAuthorizationError(c *gin.Context, resource string) {
|
||||
requestID := eh.getOrGenerateRequestID(c)
|
||||
|
||||
eh.logger.Warn("Authorization error",
|
||||
zap.String("request_id", requestID),
|
||||
zap.String("resource", resource),
|
||||
zap.String("path", c.Request.URL.Path),
|
||||
zap.String("method", c.Request.Method),
|
||||
zap.String("remote_addr", c.ClientIP()),
|
||||
)
|
||||
|
||||
response := SecureErrorResponse{
|
||||
Error: "access_denied",
|
||||
Message: "Insufficient permissions",
|
||||
RequestID: requestID,
|
||||
Code: http.StatusForbidden,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusForbidden, response)
|
||||
}
|
||||
|
||||
// HandleInternalError handles internal server errors
|
||||
func (eh *ErrorHandler) HandleInternalError(c *gin.Context, err error) {
|
||||
requestID := eh.getOrGenerateRequestID(c)
|
||||
|
||||
eh.logger.Error("Internal server error",
|
||||
zap.String("request_id", requestID),
|
||||
zap.String("path", c.Request.URL.Path),
|
||||
zap.String("method", c.Request.Method),
|
||||
zap.String("remote_addr", c.ClientIP()),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
response := SecureErrorResponse{
|
||||
Error: "internal_error",
|
||||
Message: "An internal error occurred",
|
||||
RequestID: requestID,
|
||||
Code: http.StatusInternalServerError,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusInternalServerError, response)
|
||||
}
|
||||
|
||||
// HandleNotFoundError handles resource not found errors
|
||||
func (eh *ErrorHandler) HandleNotFoundError(c *gin.Context, resource string, message string) {
|
||||
requestID := eh.getOrGenerateRequestID(c)
|
||||
|
||||
eh.logger.Warn("Resource not found",
|
||||
zap.String("request_id", requestID),
|
||||
zap.String("resource", resource),
|
||||
zap.String("path", c.Request.URL.Path),
|
||||
zap.String("method", c.Request.Method),
|
||||
zap.String("remote_addr", c.ClientIP()),
|
||||
)
|
||||
|
||||
response := SecureErrorResponse{
|
||||
Error: "resource_not_found",
|
||||
Message: message,
|
||||
RequestID: requestID,
|
||||
Code: http.StatusNotFound,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusNotFound, response)
|
||||
}
|
||||
|
||||
// determineErrorResponse determines the appropriate HTTP status and error type
|
||||
func (eh *ErrorHandler) determineErrorResponse(err error) (int, string) {
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
return appErr.StatusCode, eh.getErrorTypeFromCode(appErr.Code)
|
||||
}
|
||||
|
||||
// For unknown errors, log as internal error but don't expose details
|
||||
return http.StatusInternalServerError, "internal_error"
|
||||
}
|
||||
|
||||
// sanitizeErrorMessage removes sensitive information from error messages
|
||||
func (eh *ErrorHandler) sanitizeErrorMessage(userMessage string, err error) string {
|
||||
if userMessage != "" {
|
||||
return userMessage
|
||||
}
|
||||
|
||||
// Provide generic messages for different error types
|
||||
if appErr, ok := err.(*AppError); ok {
|
||||
return eh.getGenericMessageFromCode(appErr.Code)
|
||||
}
|
||||
|
||||
return "An error occurred"
|
||||
}
|
||||
|
||||
// getErrorTypeFromCode converts an error code to a sanitized error type string
|
||||
func (eh *ErrorHandler) getErrorTypeFromCode(code ErrorCode) string {
|
||||
switch code {
|
||||
case ErrValidationFailed, ErrInvalidInput, ErrMissingField, ErrInvalidFormat:
|
||||
return "validation_error"
|
||||
case ErrUnauthorized, ErrInvalidToken, ErrTokenExpired, ErrInvalidCredentials:
|
||||
return "authentication_failed"
|
||||
case ErrForbidden, ErrInsufficientPermissions:
|
||||
return "access_denied"
|
||||
case ErrNotFound, ErrApplicationNotFound, ErrTokenNotFound, ErrPermissionNotFound:
|
||||
return "resource_not_found"
|
||||
case ErrAlreadyExists, ErrConflict:
|
||||
return "resource_conflict"
|
||||
case ErrRateLimit:
|
||||
return "rate_limit_exceeded"
|
||||
case ErrTimeout:
|
||||
return "timeout"
|
||||
default:
|
||||
return "internal_error"
|
||||
}
|
||||
}
|
||||
|
||||
// getGenericMessageFromCode provides generic user-safe messages for error codes
|
||||
func (eh *ErrorHandler) getGenericMessageFromCode(code ErrorCode) string {
|
||||
switch code {
|
||||
case ErrValidationFailed, ErrInvalidInput, ErrMissingField, ErrInvalidFormat:
|
||||
return "Invalid input provided"
|
||||
case ErrUnauthorized, ErrInvalidToken, ErrTokenExpired, ErrInvalidCredentials:
|
||||
return "Authentication required"
|
||||
case ErrForbidden, ErrInsufficientPermissions:
|
||||
return "Access denied"
|
||||
case ErrNotFound, ErrApplicationNotFound, ErrTokenNotFound, ErrPermissionNotFound:
|
||||
return "Resource not found"
|
||||
case ErrAlreadyExists, ErrConflict:
|
||||
return "Resource conflict"
|
||||
case ErrRateLimit:
|
||||
return "Rate limit exceeded"
|
||||
case ErrTimeout:
|
||||
return "Request timeout"
|
||||
default:
|
||||
return "An error occurred"
|
||||
}
|
||||
}
|
||||
|
||||
// getOrGenerateRequestID gets or generates a request ID for tracking
|
||||
func (eh *ErrorHandler) getOrGenerateRequestID(c *gin.Context) string {
|
||||
// Try to get existing request ID from context
|
||||
if requestID, exists := c.Get("request_id"); exists {
|
||||
if id, ok := requestID.(string); ok {
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get from header
|
||||
requestID := c.GetHeader("X-Request-ID")
|
||||
if requestID != "" {
|
||||
return requestID
|
||||
}
|
||||
|
||||
// Generate a simple request ID (in production, use a proper UUID library)
|
||||
return generateSimpleID()
|
||||
}
|
||||
|
||||
// generateSimpleID generates a simple request ID
|
||||
func generateSimpleID() string {
|
||||
// Simple implementation - in production use proper UUID generation
|
||||
bytes := make([]byte, 8)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
// Fallback to timestamp-based ID
|
||||
return fmt.Sprintf("req_%d", time.Now().UnixNano())
|
||||
}
|
||||
return "req_" + hex.EncodeToString(bytes)
|
||||
}
|
||||
Reference in New Issue
Block a user