aboutsummaryrefslogtreecommitdiffstats
path: root/backend/internal/middleware
diff options
context:
space:
mode:
Diffstat (limited to 'backend/internal/middleware')
-rw-r--r--backend/internal/middleware/error_handler.go119
1 files changed, 119 insertions, 0 deletions
diff --git a/backend/internal/middleware/error_handler.go b/backend/internal/middleware/error_handler.go
new file mode 100644
index 0000000..4e7eedf
--- /dev/null
+++ b/backend/internal/middleware/error_handler.go
@@ -0,0 +1,119 @@
+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)
+}