From 1e6a06e8edd1d76caa83c0f01cb0c9b48bb1cfca Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Thu, 4 Sep 2025 20:25:12 -0700 Subject: [PATCH] (FEAT): Added errors to each of the handlers. I think this is really the only place we need them. For now at least. --- internal/app/handlers/auth_handler.go | 11 +++-------- internal/app/handlers/engagement_handler.go | 5 +++++ internal/app/handlers/recipe_handler.go | 5 +++++ internal/app/handlers/user_handler.go | 7 ++++++- internal/app/service/auth_service.go | 18 +++++++++--------- internal/domain/auth/service.go | 6 +----- 6 files changed, 29 insertions(+), 23 deletions(-) diff --git a/internal/app/handlers/auth_handler.go b/internal/app/handlers/auth_handler.go index 4fa0a66..3c37be0 100644 --- a/internal/app/handlers/auth_handler.go +++ b/internal/app/handlers/auth_handler.go @@ -6,6 +6,7 @@ import ( "github.com/gin-gonic/gin" domain "github.com/haydenhargreaves/Potion/internal/domain/server" + "github.com/haydenhargreaves/Potion/internal/templates/components" ) // GoogleLogin directs the user to Googles select user login page. Once the user has selected an @@ -21,8 +22,6 @@ func GoogleLogin(ctx *gin.Context) { // account. They will be directed here and a JWT is generated. This JWT is stored in the users // cookies and will be used by protected routes to validate their login status. // -// TODO: This route does not do the proper handling, need to work on the redirection or handling. -// // We do not need to return all of this data, it is just for testing. func GoogleCallback(ctx *gin.Context) { deps := ctx.MustGet("deps").(*domain.InjectedDependencies) @@ -32,15 +31,11 @@ func GoogleCallback(ctx *gin.Context) { code string = ctx.Query("code") ) - // TODO: Do something real, not just return data - if jwt, dbUser, googleUserInfo, err := deps.AuthService.GoogleAuthSuccess(state, code); err != nil { + if jwt, err := deps.AuthService.GoogleAuthSuccess(state, code); err != nil { + components.RenderErrorBanner(ctx, err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } else { domain.SetCookie(ctx, "jwt_token", jwt, time.Hour*24*7) - // ctx.JSON(http.StatusOK, gin.H{"jwt": jwt, "googleUserInfo": googleUserInfo, "dbUser": dbUser}) - _ = dbUser - _ = googleUserInfo - ctx.Redirect(http.StatusSeeOther, "/") } } diff --git a/internal/app/handlers/engagement_handler.go b/internal/app/handlers/engagement_handler.go index 1c75b7c..571d6a3 100644 --- a/internal/app/handlers/engagement_handler.go +++ b/internal/app/handlers/engagement_handler.go @@ -24,6 +24,7 @@ func EngagementViewRecipe(ctx *gin.Context) { if !domain.IsLoggedIn(ctx) || user == nil { if _, err := deps.EngagementService.ViewRecipe(recipeId); err != nil { + components.RenderErrorBanner(ctx, err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ "status": http.StatusInternalServerError, "message": err.Error(), @@ -37,6 +38,7 @@ func EngagementViewRecipe(ctx *gin.Context) { // We caught nil already, we can assume the user exists if _, err := deps.EngagementService.UserViewRecipe(user.Id, recipeId); err != nil { + components.RenderErrorBanner(ctx, err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ "status": http.StatusInternalServerError, "message": err.Error(), @@ -61,6 +63,7 @@ func EngagementShareRecipe(ctx *gin.Context) { if !domain.IsLoggedIn(ctx) || user == nil { if _, err := deps.EngagementService.ShareRecipe(recipeId); err != nil { + components.RenderErrorBanner(ctx, err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ "status": http.StatusInternalServerError, "message": err.Error(), @@ -72,6 +75,7 @@ func EngagementShareRecipe(ctx *gin.Context) { } if _, err := deps.EngagementService.UserShareRecipe(user.Id, recipeId); err != nil { + components.RenderErrorBanner(ctx, err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ "status": http.StatusInternalServerError, "message": err.Error(), @@ -133,6 +137,7 @@ func EngagementMakeRecipe(ctx *gin.Context) { recipeId, _ := strconv.Atoi(id) if _, err := deps.EngagementService.UserMakeRecipe(user.Id, recipeId); err != nil { + components.RenderErrorBanner(ctx, err.Error()) ctx.JSON(http.StatusInternalServerError, gin.H{ "status": http.StatusInternalServerError, "message": err.Error(), diff --git a/internal/app/handlers/recipe_handler.go b/internal/app/handlers/recipe_handler.go index 2d24556..770c25c 100644 --- a/internal/app/handlers/recipe_handler.go +++ b/internal/app/handlers/recipe_handler.go @@ -10,6 +10,7 @@ import ( "github.com/gin-gonic/gin" domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe" domain "github.com/haydenhargreaves/Potion/internal/domain/server" + "github.com/haydenhargreaves/Potion/internal/templates/components" templates "github.com/haydenhargreaves/Potion/internal/templates/pages" ) @@ -24,6 +25,7 @@ func CreateRecipe(ctx *gin.Context) { recipe, err := deps.RecipeService.CreateRecipe(ctx) if err != nil { + components.RenderErrorBanner(ctx, err.Error()) ctx.String(http.StatusOK, CREATE_ERROR_HTML, err.Error()) return } @@ -89,6 +91,7 @@ func SearchRecipes(ctx *gin.Context) { // We don't care about favorite status, so use false recipes, err := deps.RecipeService.SearchRecipes(filters, userId, false) if err != nil { + components.RenderErrorBanner(ctx, err.Error()) ctx.JSON(http.StatusOK, gin.H{"error": err.Error()}) } @@ -126,6 +129,7 @@ func SearchRecipesFavorites(ctx *gin.Context) { // TODO: Error here if they're not logged in? // Get user data (they should be logged in) if !domain.IsLoggedIn(ctx) { + components.RenderErrorBanner(ctx, "User is not logged in. User will be nil.") ctx.JSON(http.StatusOK, gin.H{"error": "User is not logged in. User will be nil."}) } @@ -133,6 +137,7 @@ func SearchRecipesFavorites(ctx *gin.Context) { recipes, err := deps.RecipeService.SearchRecipes(filters, &userId, true) if err != nil { + components.RenderErrorBanner(ctx, err.Error()) ctx.JSON(http.StatusOK, gin.H{"error": err.Error()}) } diff --git a/internal/app/handlers/user_handler.go b/internal/app/handlers/user_handler.go index e19cd18..4561f87 100644 --- a/internal/app/handlers/user_handler.go +++ b/internal/app/handlers/user_handler.go @@ -6,6 +6,7 @@ import ( "github.com/gin-gonic/gin" domain "github.com/haydenhargreaves/Potion/internal/domain/server" + "github.com/haydenhargreaves/Potion/internal/templates/components" ) func GetUserRecipes(ctx *gin.Context) { @@ -21,6 +22,7 @@ func GetUserRecipes(ctx *gin.Context) { // Ensure logged in if !domain.IsLoggedIn(ctx) || user == nil { + components.RenderErrorBanner(ctx, "User is not authorized to access this endpoint. Please login to continue.") ctx.JSON(http.StatusUnauthorized, gin.H{ "status": http.StatusUnauthorized, "message": "User is not authorized to access this endpoint. Please login to continue.", @@ -31,6 +33,7 @@ func GetUserRecipes(ctx *gin.Context) { recipes, err := deps.RecipeService.GetUserRecipes(user.Id) if err != nil { + components.RenderErrorBanner(ctx, fmt.Sprintf("Could not get user recipes. %s", err.Error())) ctx.JSON(http.StatusBadRequest, gin.H{ "status": http.StatusBadRequest, "message": fmt.Sprintf("Could not get user recipes. %s", err.Error()), @@ -59,6 +62,7 @@ func GetUserFavoriteRecipes(ctx *gin.Context) { // Ensure logged in if !domain.IsLoggedIn(ctx) || user == nil { + components.RenderErrorBanner(ctx, "User is not authorized to access this endpoint. Please login to continue.") ctx.JSON(http.StatusUnauthorized, gin.H{ "status": http.StatusUnauthorized, "message": "User is not authorized to access this endpoint. Please login to continue.", @@ -69,9 +73,10 @@ func GetUserFavoriteRecipes(ctx *gin.Context) { recipes, err := deps.RecipeService.GetUserFavoriteRecipes(user.Id) if err != nil { + components.RenderErrorBanner(ctx, fmt.Sprintf("Could not get favorite recipes. %s", err.Error())) ctx.JSON(http.StatusBadRequest, gin.H{ "status": http.StatusBadRequest, - "message": fmt.Sprintf("Could not get user recipes. %s", err.Error()), + "message": fmt.Sprintf("Could not get favorite recipes. %s", err.Error()), "recipes": nil, }) return diff --git a/internal/app/service/auth_service.go b/internal/app/service/auth_service.go index bd0bf98..41d77a8 100644 --- a/internal/app/service/auth_service.go +++ b/internal/app/service/auth_service.go @@ -60,48 +60,48 @@ func (s *AuthService) GetGoogleAuthUrl() string { // GoogleAuthSuccess accepts the data from the Google login endpoint and uses it to fetch the users // data. The data is then used to log the user in or create an account. -func (s *AuthService) GoogleAuthSuccess(state, code string) (string, domain.User, domain.GoogleUserInfo, error) { +func (s *AuthService) GoogleAuthSuccess(state, code string) (string, error) { // Ensure the state matches, prevents M.I.T.M. attacks if state != "randomstate" { - return "", domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("States don't match, received %s", state) + return "", fmt.Errorf("States don't match, received %s", state) } // Get access token from Google token, err := auth.GoogleAuthConfig.Exchange(context.Background(), code) if err != nil { - return "", domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("Code exchange failed: %s", err.Error()) + return "", fmt.Errorf("Code exchange failed: %s", err.Error()) } // Use the access token to get user data googleUserInfo, err := auth.GetUserData(token.AccessToken) if err != nil { - return "", domain.User{}, domain.GoogleUserInfo{}, err + return "", err } // Attempt to get the user, user is nil when they don't exit user, err := s.userRepository.GetGoogleUser(googleUserInfo.Id) if err != nil { - return "", domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("Failed to get db user: %s", err) + return "", fmt.Errorf("Failed to get db user: %s", err) } // A user was found if user != nil { jwt, err := generateJwt(user.Id, user.Email, s.jwtSecret) - return jwt, *user, googleUserInfo, err + return jwt, err } // user did not exist, need to create one newUser, err := s.userRepository.CreateGoogleUser(&googleUserInfo, token.RefreshToken) if err != nil { - return "", domain.User{}, domain.GoogleUserInfo{}, fmt.Errorf("Repository failed to create user: %s", err.Error()) + return "", fmt.Errorf("Repository failed to create user: %s", err.Error()) } jwt, err := generateJwt(newUser.Id, newUser.Email, s.jwtSecret) - return jwt, newUser, googleUserInfo, err + return jwt, err } // generateJwt requires user data and returns a JSON web token which can be stored in the browsers -// cookies. This token is used to log a user into the application and allow access to protected +// cookies. This token is used to log a user into the application and allow access to protected // routes. func generateJwt(userId int, email string, jwtSecret []byte) (string, error) { expiration := time.Now().Add(7 * 24 * time.Hour) diff --git a/internal/domain/auth/service.go b/internal/domain/auth/service.go index 936ed7f..81421fe 100644 --- a/internal/domain/auth/service.go +++ b/internal/domain/auth/service.go @@ -1,10 +1,6 @@ package domain -import ( - domain "github.com/haydenhargreaves/Potion/internal/domain/user" -) - type AuthService interface { GetGoogleAuthUrl() string - GoogleAuthSuccess(state, code string) (string, domain.User, domain.GoogleUserInfo, error) + GoogleAuthSuccess(state, code string) (string, error) }