diff --git a/internal/app/server/authentication.go b/internal/app/server/authentication.go index edb791f..81d2283 100644 --- a/internal/app/server/authentication.go +++ b/internal/app/server/authentication.go @@ -14,12 +14,17 @@ type AuthenticatedFunc func(ctx *gin.Context, user *domain.User) // the function will return an error with a 401 status. // // BUG: This is probably not very effecient, since we hit the DB on every single protected request. -// If this ends up being a bottle neck we could simply hit the context for the userId, since -// that is usually all we need...Or maybe have two methods, for those that need the whole user -// and those that just need the ID. +// +// If this ends up being a bottle neck we could simply hit the context for the userId, since +// that is usually all we need...Or maybe have two methods, for those that need the whole user +// and those that just need the ID. func (s *Server) withAuthenticatedUser(ctx *gin.Context, handler AuthenticatedFunc) { user := s.deps.UserService.GetAuthenicatedUser(ctx) if user == nil { + // User is stale, ensure they are logged out so they can be prompted to log back in + s.SetCookie(ctx, "jwt_token", "", -1) + // s.SetCookie(ctx, "search-filters", "", -1) // TODO: Might need this again + ctx.JSON(http.StatusUnauthorized, gin.H{ "status": http.StatusUnauthorized, "message": "[UNAUTHORIZED] Could not fetch authenticated user.", @@ -29,17 +34,16 @@ func (s *Server) withAuthenticatedUser(ctx *gin.Context, handler AuthenticatedFu handler(ctx, user) } - // getUserId retrieves the userId from the context and returns a pointer to it. A nil // pointer can be returned and will if the userId does not exist. func getUserId(ctx *gin.Context) *int { - userIdAny, exists := ctx.Get("userId") - if !exists { - return nil - } - userIdInt, ok := userIdAny.(int) - if !ok { - return nil - } - return &userIdInt + userIdAny, exists := ctx.Get("userId") + if !exists { + return nil + } + userIdInt, ok := userIdAny.(int) + if !ok { + return nil + } + return &userIdInt } diff --git a/internal/app/server/engagement_handler_v2.go b/internal/app/server/engagement_handler_v2.go new file mode 100644 index 0000000..2fcd806 --- /dev/null +++ b/internal/app/server/engagement_handler_v2.go @@ -0,0 +1,143 @@ +package server + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + domain "github.com/haydenhargreaves/Potion/internal/domain/user" +) + +func (s *Server) EngagementViewRecipeHandlerV2(ctx *gin.Context) { + recipeId, err := strconv.Atoi(ctx.Param("id")) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("[ERROR] Failed to parse ID parameter. %s", err.Error()), + }) + return + } + + userId := getUserId(ctx) + if userId == nil { + if engagement, err := s.deps.EngagementService.ViewRecipe(recipeId); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("[ERROR] Failed to create engagement. %s", err.Error()), + }) + } else { + ctx.JSON(http.StatusOK, gin.H{ + "status": http.StatusOK, + "message": "[OK] Successfully created engagement.", + "engagement": engagement, + }) + } + return + } + if engagement, err := s.deps.EngagementService.UserViewRecipe(*userId, recipeId); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("[ERROR] Failed to create engagement. %s", err.Error()), + }) + } else { + ctx.JSON(http.StatusOK, gin.H{ + "status": http.StatusOK, + "message": "[OK] Successfully created engagement.", + "engagement": engagement, + }) + } +} + +func (s *Server) EngagementShareRecipeHandlerV2(ctx *gin.Context) { + recipeId, err := strconv.Atoi(ctx.Param("id")) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("[ERROR] Failed to parse ID parameter. %s", err.Error()), + }) + return + } + + userId := getUserId(ctx) + if userId == nil { + if engagement, err := s.deps.EngagementService.ShareRecipe(recipeId); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("[ERROR] Failed to create engagement. %s", err.Error()), + }) + } else { + ctx.JSON(http.StatusOK, gin.H{ + "status": http.StatusOK, + "message": "[OK] Successfully created engagement.", + "engagement": engagement, + }) + } + return + } + + if engagement, err := s.deps.EngagementService.UserShareRecipe(*userId, recipeId); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("[ERROR] Failed to create engagement. %s", err.Error()), + }) + } else { + ctx.JSON(http.StatusOK, gin.H{ + "status": http.StatusOK, + "message": "[OK] Successfully created engagement.", + "engagement": engagement, + }) + } +} + +func (s *Server) EngagementFavoriteRecipeHandlerV2(ctx *gin.Context) { + s.withAuthenticatedUser(ctx, func(ctx *gin.Context, user *domain.User) { + recipeId, err := strconv.Atoi(ctx.Param("id")) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("[ERROR] Failed to parse ID parameter. %s", err.Error()), + }) + return + } + + if engagement, err := s.deps.EngagementService.UserFavoriteRecipe(user.Id, recipeId); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("[ERROR] Failed to create engagement. %s", err.Error()), + }) + } else { + ctx.JSON(http.StatusOK, gin.H{ + "status": http.StatusOK, + "message": "[OK] Successfully created engagement.", + "engagement": engagement, + }) + } + }) +} + +func (s *Server) EngagementMakeRecipeHandlerV2(ctx *gin.Context) { + s.withAuthenticatedUser(ctx, func(ctx *gin.Context, user *domain.User) { + recipeId, err := strconv.Atoi(ctx.Param("id")) + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("[ERROR] Failed to parse ID parameter. %s", err.Error()), + }) + return + } + + if engagement, err := s.deps.EngagementService.UserMakeRecipe(user.Id, recipeId); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{ + "status": http.StatusBadRequest, + "message": fmt.Sprintf("[ERROR] Failed to create engagement. %s", err.Error()), + }) + } else { + ctx.JSON(http.StatusOK, gin.H{ + "status": http.StatusOK, + "message": "[OK] Successfully created engagement.", + "engagement": engagement, + }) + } + }) +} diff --git a/internal/app/server/server.go b/internal/app/server/server.go index abbab58..798b16d 100644 --- a/internal/app/server/server.go +++ b/internal/app/server/server.go @@ -220,5 +220,10 @@ func (s *Server) Setup() *Server { ctx.JSON(http.StatusOK, gin.H{"msg": "YAY"}) }) + router_api_v2.POST("/engagement/view/:id", JwtOptionalAuthMiddlewareV2([]byte(cfg.JwtSecret)), s.EngagementViewRecipeHandlerV2) + router_api_v2.POST("/engagement/share/:id", JwtOptionalAuthMiddlewareV2([]byte(cfg.JwtSecret)), s.EngagementShareRecipeHandlerV2) + router_api_v2.POST("/engagement/favorite/:id", JwtAuthMiddlewareV2([]byte(cfg.JwtSecret)), s.EngagementFavoriteRecipeHandlerV2) + router_api_v2.POST("/engagement/make/:id", JwtAuthMiddlewareV2([]byte(cfg.JwtSecret)), s.EngagementMakeRecipeHandlerV2) + return s } diff --git a/internal/app/server/user_handler_v2.go b/internal/app/server/user_handler_v2.go index ae9f11a..9de4a9c 100644 --- a/internal/app/server/user_handler_v2.go +++ b/internal/app/server/user_handler_v2.go @@ -24,7 +24,7 @@ func (s *Server) GetAuthenicatedUserRecipesV2(ctx *gin.Context) { if err != nil { ctx.JSON(http.StatusBadRequest, gin.H{ "status": http.StatusBadRequest, - "message": fmt.Sprintf("[FAILED] Could not fetch authenticated users's recipes. %s", err.Error()), + "message": fmt.Sprintf("[ERROR] Could not fetch authenticated users's recipes. %s", err.Error()), }) return } @@ -43,7 +43,7 @@ func (s *Server) GetAuthenicatedUserFavoritesV2(ctx *gin.Context) { if err != nil { ctx.JSON(http.StatusBadRequest, gin.H{ "status": http.StatusBadRequest, - "message": fmt.Sprintf("[FAILED] Could not fetch authenticated users's favorites. %s", err.Error()), + "message": fmt.Sprintf("[ERROR] Could not fetch authenticated users's favorites. %s", err.Error()), }) return } @@ -62,7 +62,7 @@ func (s *Server) GetAuthenicatedUserEngagementV2(ctx *gin.Context) { if err != nil { ctx.JSON(http.StatusBadRequest, gin.H{ "status": http.StatusBadRequest, - "message": fmt.Sprintf("[FAILED] Failed to get authenticated user engagement. %s", err.Error()), + "message": fmt.Sprintf("[ERROR] Failed to get authenticated user engagement. %s", err.Error()), }) return } @@ -81,7 +81,7 @@ func (s *Server) GetAuthenicatedUserMadeRecipesV2(ctx *gin.Context) { if err != nil { ctx.JSON(http.StatusBadRequest, gin.H{ "status": http.StatusBadRequest, - "message": fmt.Sprintf("[FAILED] Failed to get authenticated user's made recipes. %s", err.Error()), + "message": fmt.Sprintf("[ERROR] Failed to get authenticated user's made recipes. %s", err.Error()), }) return } @@ -100,7 +100,7 @@ func (s *Server) GetAuthenicatedUserViewedRecipesV2(ctx *gin.Context) { if err != nil { ctx.JSON(http.StatusBadRequest, gin.H{ "status": http.StatusBadRequest, - "message": fmt.Sprintf("[FAILED] Failed to get authenticated user's viewed recipes. %s", err.Error()), + "message": fmt.Sprintf("[ERROR] Failed to get authenticated user's viewed recipes. %s", err.Error()), }) return } diff --git a/web/src/components/Spinner.tsx b/web/src/components/Spinner.tsx index 14b509b..e8c536e 100644 --- a/web/src/components/Spinner.tsx +++ b/web/src/components/Spinner.tsx @@ -6,7 +6,7 @@ export default function Spinner({ content }: SpinnerProps) { return ( <>
-