// Package middleware provides HTTP middleware components for the CLI Proxy API server. // This file contains panic recovery middleware for graceful error handling. package middleware import ( "fmt" "net/http" "runtime/debug" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "github.com/router-for-me/CLIProxyAPI/v6/internal/application/dto" ) // RecoveryMiddleware creates a Gin middleware that recovers from panics // and returns a graceful error response instead of crashing the server. // It logs the panic details including stack trace for debugging. func RecoveryMiddleware(logger *logrus.Logger) gin.HandlerFunc { return func(c *gin.Context) { defer func() { if r := recover(); r != nil { requestID := c.GetString("request_id") // Log the panic with stack trace if logger is available if logger != nil { logger.WithFields(logrus.Fields{ "panic": r, "stacktrace": string(debug.Stack()), "path": c.Request.URL.Path, "method": c.Request.Method, "client_ip": c.ClientIP(), "request_id": requestID, }).Error("Panic recovered in HTTP handler") } // Return graceful error response c.AbortWithStatusJSON(http.StatusInternalServerError, dto.InternalErrorResponse{ Success: false, RequestID: requestID, Error: "An internal server error occurred", }) } }() c.Next() } } // SafeHandler wraps a handler function to catch panics at the handler level. // This provides an additional layer of protection for critical endpoints. func SafeHandler(handler gin.HandlerFunc) gin.HandlerFunc { return func(c *gin.Context) { defer func() { if r := recover(); r != nil { requestID := c.GetString("request_id") // Log the panic logrus.WithFields(logrus.Fields{ "panic": fmt.Sprintf("%v", r), "path": c.Request.URL.Path, "method": c.Request.Method, "request_id": requestID, }).Error("Panic recovered in handler") c.AbortWithStatusJSON(http.StatusInternalServerError, dto.InternalErrorResponse{ Success: false, RequestID: requestID, Error: "An internal server error occurred", }) } }() handler(c) } }