package middleware import ( "finance/backend/internal/logger" "net/http" "runtime/debug" "time" "github.com/gin-gonic/gin" ) // ErrorResponse represents a standardized error response type ErrorResponse struct { Status int `json:"status"` Message string `json:"message"` Error string `json:"error,omitempty"` } // ErrorHandler middleware recovers from panics and logs errors func ErrorHandler() gin.HandlerFunc { return func(c *gin.Context) { defer func() { if err := recover(); err != nil { // Get logger from the context or create a new one log := getLoggerFromContext(c) // Log the stack trace log.Errorf("PANIC RECOVERED: %v\n%s", err, debug.Stack()) // Return a 500 Internal Server Error response c.JSON(http.StatusInternalServerError, ErrorResponse{ Status: http.StatusInternalServerError, Message: "An unexpected error occurred", }) // Abort the request c.Abort() } }() c.Next() } } // RequestLogger middleware logs information about each request func RequestLogger(log *logger.Logger) gin.HandlerFunc { return func(c *gin.Context) { // Store logger in context for other middleware to use c.Set("logger", log) // Start timer startTime := time.Now() // Process request c.Next() // Calculate latency latency := time.Since(startTime) // Get request details method := c.Request.Method path := c.Request.URL.Path statusCode := c.Writer.Status() ip := c.ClientIP() userAgent := c.Request.UserAgent() // Log request info based on status code logMessage := logger.FormatRequestLog(method, path, ip, userAgent, statusCode, latency) if statusCode >= 500 { log.Error(logMessage) } else if statusCode >= 400 { log.Warn(logMessage) } else { log.Info(logMessage) } } } // NotFoundHandler handles 404 errors func NotFoundHandler() gin.HandlerFunc { return func(c *gin.Context) { // Get logger from the context or create a new one log := getLoggerFromContext(c) log.Warnf("404 Not Found: %s %s", c.Request.Method, c.Request.URL.Path) c.JSON(http.StatusNotFound, ErrorResponse{ Status: http.StatusNotFound, Message: "The requested resource was not found", }) } } // MethodNotAllowedHandler handles 405 errors func MethodNotAllowedHandler() gin.HandlerFunc { return func(c *gin.Context) { // Get logger from the context or create a new one log := getLoggerFromContext(c) log.Warnf("405 Method Not Allowed: %s %s", c.Request.Method, c.Request.URL.Path) c.JSON(http.StatusMethodNotAllowed, ErrorResponse{ Status: http.StatusMethodNotAllowed, Message: "The requested method is not allowed", }) } } // getLoggerFromContext retrieves the logger from context or creates a default one func getLoggerFromContext(c *gin.Context) *logger.Logger { loggerInterface, exists := c.Get("logger") if exists { if log, ok := loggerInterface.(*logger.Logger); ok { return log } } // If no logger in context, create a new one return logger.NewLogger(nil, logger.INFO) }