120 lines
3.9 KiB
Go
120 lines
3.9 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"runtime/debug"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
domain "github.com/haydenhargreaves/Potion/internal/domain/server"
|
|
"github.com/haydenhargreaves/Potion/internal/infrastructure/logging"
|
|
)
|
|
|
|
// DepedencyInjectionMiddleware injects the dependencies into the context set. This is a middleware
|
|
// that is used to apply the required services.
|
|
//
|
|
// DEPRECATED: As of September 4th, 2025.
|
|
// 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(logs []logging.Logger) 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())
|
|
logging.LogAll(logs, logging.LogLevelFatal, "[PANIC RECOVERY] %s\n", 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()
|
|
}
|
|
}
|