FindMyRecipe / server / handler / user.go
user.go
Raw
package handler

import (
	"fmt"
	db "main/server/database"
	"os"
	"time"

	"github.com/gofiber/fiber/v2"
	jwt "github.com/golang-jwt/jwt/v4"
)

var jwtKey = []byte(os.Getenv("JWT_SECRET"))

type Claims struct {
	Username string `json:"username"`
  UserId int `json:"userid"`
	jwt.RegisteredClaims
}

func SetupUserRoutes(router fiber.Router) {
  router.Post("/login", func (c *fiber.Ctx) error {
    req := struct {
      Username string `json:"username"`
      Password string `json:"password"`
    }{}

    if err := c.BodyParser(&req); err != nil {
      return err
    }
    q, err := db.UserLogin(req.Username, req.Password)
    if err != nil {
      return c.JSON(fiber.Map{"status":"failed", "message":"failed to login"})
    }

    expirationTime := time.Now().Add(time.Hour)

    claims := &Claims{
      Username: req.Username,
      UserId: q.Id,
      RegisteredClaims: jwt.RegisteredClaims{
        ExpiresAt: jwt.NewNumericDate(expirationTime),
      },
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

    tokenString, err := token.SignedString(jwtKey)
    if err != nil {
      return c.JSON(fiber.Map{"status":"failed", "message":"Failed to create JWT"})
    }

    c.Cookie(&fiber.Cookie{
      Name: "token",
      Value: tokenString,
      Expires: expirationTime,
    })

    return c.JSON(fiber.Map{"status":"success", "token":tokenString})
  })

  router.Post("/signup", func (c *fiber.Ctx) error {
    req := struct {
      Username string `json:"username"`
      Password string `json:"password"`
    }{}

    if err := c.BodyParser(&req); err != nil {
      return err
    }
    q := db.UserSignUp(req.Username, req.Password)
    if q != true {
      return c.JSON(fiber.Map{"status":"failed", "message":"username already exists"})
    }
    return c.JSON(fiber.Map{"status":"success"})
  })

  router.Post("/refresh", func (c *fiber.Ctx) error {
    tknStr := c.Cookies("token")
    if tknStr == "" {
      return fiber.ErrUnauthorized
    }
      // Username: req.Username,
      // UserId: q.Id,
      // RegisteredClaims: jwt.RegisteredClaims{
      //   ExpiresAt: jwt.NewNumericDate(expirationTime),
      // },
    req := struct {
      Username string `json:"username"`
      UserId int `json:"userid"`
      Exp int64 `json:"exp"`
    }{}

    if err := c.BodyParser(&req); err != nil {
      return err
    }

    expirationTime := time.Unix(req.Exp, 0)
    claims := &Claims{
      Username: req.Username,
      UserId: req.UserId,
      RegisteredClaims: jwt.RegisteredClaims{
        ExpiresAt: jwt.NewNumericDate(expirationTime),
      },
    }
    tkn, err := jwt.ParseWithClaims(tknStr, claims, func(token *jwt.Token) (interface{}, error) {
      return jwtKey, nil
    })
    if err != nil {
      return fiber.ErrBadRequest
    }
    if !tkn.Valid {
      return fiber.ErrUnauthorized
    }

    if time.Until(claims.ExpiresAt.Time) > 5*time.Minute {
      fmt.Println(tknStr)
      return fiber.ErrBadRequest
    }

    expirationTime = time.Now().Add(time.Hour)
    claims.ExpiresAt = jwt.NewNumericDate(expirationTime)
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    tokenString, err := token.SignedString(jwtKey)
    if err != nil {
      return fiber.ErrInternalServerError
    }

    c.Cookie(&fiber.Cookie{
      Name: "token",
      Value: tokenString,
      Expires: expirationTime,
    })

    return c.JSON(fiber.Map{"token":tokenString})
  })

  router.Post("/logout", func (c *fiber.Ctx) error {
    c.Cookie(&fiber.Cookie{
      Name: "token",
      Expires: time.Now(),
    })
    return c.JSON(fiber.Map{"status":"success"})
  })

  router.Post("/favorite/toggle", func (c *fiber.Ctx) error {// not secure
    tknStr := c.Cookies("token")
    if tknStr == "" {
      return fiber.ErrUnauthorized
    }

    req := struct {
      UserId int `json:"userId"`
      RecipeId int `json:"recipeId"`
    }{}

    if err := c.BodyParser(&req); err != nil {
      return err
    }

    claims := &Claims{}
    tkn, err := jwt.ParseWithClaims(tknStr, claims, func(token *jwt.Token) (interface{}, error) {
      return jwtKey, nil
    })

    if err != nil {
      return fiber.ErrBadRequest
    }
    if !tkn.Valid {
      return fiber.ErrUnauthorized
    }

    q := db.ToggleUserFavorite(req.UserId, req.RecipeId)
    if q != true {
      return c.JSON(fiber.Map{"status":"failed", "message":"failed to toggle favorite"})
    }
    return c.JSON(fiber.Map{"status":"success", "modifiedBy":claims.Username})
  })

  // router.Post("/favorite/test", func (c *fiber.Ctx) error {// not secure
  //   return c.JSON(fiber.Map{"status":"failed", "message":"Removed, only for testing"})
  //   req := struct {
  //     UserId int `json:"userId"`
  //     RecipeIds []int `json:"recipeIds"`
  //   }{}
  //   if err := c.BodyParser(&req); err != nil {
  //     return c.JSON(fiber.Map{"status": "failed", "message": "Failed to load user favorites"})
  //   }
  //
  //   q := db.TestFavorites(req.UserId, req.RecipeIds)
  //   if q != true {
  //     return c.JSON(fiber.Map{"status":"failed", "message":"failed to toggle favorite"})
  //   }
  //   return c.JSON(fiber.Map{"status":"success"})
  // })

  router.Post("/favorite/user", func (c *fiber.Ctx) error {
    tknStr := c.Cookies("token")
    if tknStr == "" {
      return fiber.ErrUnauthorized
    }

    req := struct {
      UserId int `json:"userId"`
    }{}

    claims := &Claims{}

    tkn, err := jwt.ParseWithClaims(tknStr, claims, func(token *jwt.Token) (interface{}, error) {
      return jwtKey, nil
    })

    if err != nil {
      return fiber.ErrBadRequest
    }
    if !tkn.Valid {
      return fiber.ErrUnauthorized
    }

    if err := c.BodyParser(&req); err != nil {
      return c.JSON(fiber.Map{"status": "failed", "message": "Failed to load user favorites"})
    }
    ret := db.GetUserFavorites(req.UserId)
    if (len(ret) == 0) {
      return c.JSON(fiber.Map{"status": "failed", "message": "No user favorites", "data": "[]"})
    }
    return c.JSON(fiber.Map{"status": "success", "message": "User favorite IDs", "data": ret})
  })

}