package server import ( "fmt" "log" "net/http" "runtime/debug" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" domain "github.com/haydenhargreaves/Potion/internal/domain/server" ) // DepedencyInjectionMiddleware injects the dependencies into the context set. This is a middleware // that is used to apply the required services. func DepedencyInjectionMiddleware(deps *domain.InjectedDependencies) gin.HandlerFunc { return func(ctx *gin.Context) { ctx.Set("deps", deps) ctx.Next() } } // JwtAuthMiddleWare handles collection the JWT from the browser's cookies and setting the // appropriate data. If the data is not found, this middleware will do effectively nothing, by not // setting any values. Protected routes can use this lack of a value as a sign that the user is not // logged in and direct the user to login. func JwtAuthMiddleWare(jwtSecretKey []byte) gin.HandlerFunc { return func(ctx *gin.Context) { // JWT cookie not found tokenString, err := ctx.Cookie("jwt_token") if err != nil { ctx.Next() return } claims := &domain.JwtClaims{} token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } return jwtSecretKey, nil }) // Error occurred when parsing if err != nil { ctx.Next() return } // NOTE: If we need deeper error handling // if err != nil { // if errors.Is(err, jwt.ErrSignatureInvalid) { // ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token signature"}) // } else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) { // ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Token expired or not yet valid"}) // } else { // log.Printf("JWT parsing error: %v", err) // ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"}) // } // ctx.Abort() // return // } // Token is invalid if !token.Valid { ctx.Next() return } // Found: Set the values ctx.Set("userId", claims.UserId) ctx.Set("userEmail", claims.Email) ctx.Next() } } func RecoveryMiddleware() gin.HandlerFunc { return func(ctx *gin.Context) { defer func() { if r := recover(); r != nil { // Log the panic with stack trace err := fmt.Errorf("panic recovered: %v\n%s", r, debug.Stack()) log.Printf("[PANIC RECOVERY] %s", err) ctx.JSON(http.StatusOK, gin.H{ "status": http.StatusOK, "error": "API_PANIC_RECOVERED", "message": err.Error(), }) // Determine the content type of the request for appropriate response // acceptHeader := ctx.Request.Header.Get("Accept") // // // Customize the response based on the request type (e.g., HTML vs. JSON) // if strings.Contains(acceptHeader, "text/html") { // // For browser requests (HTML), redirect to an error page or render a template // ctx.HTML(http.StatusInternalServerError, "error.html", gin.H{ // "title": "Something went wrong", // "message": "An unexpected error occurred. Please try again later.", // }) // } else if strings.Contains(acceptHeader, "application/json") || ctx.Request.Method == http.MethodPost { // // For API requests (JSON), return a JSON error response // ctx.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ // "error": "An internal server error occurred.", // "message": "We're working to fix the problem. Please try again later.", // // You might include a unique error ID here for tracking // }) // } else { // // Fallback for other content types // ctx.AbortWithStatus(http.StatusInternalServerError) // } } }() ctx.Next() } }