From 64b1771e2fdbfe758de32fff4f7427dd638edc9c Mon Sep 17 00:00:00 2001 From: ahmadafriadi Date: Fri, 27 Jun 2025 21:58:51 +0700 Subject: [PATCH] Update get User & Refresh Token --- middlewares/jwt.go | 3 +- modules/auth/handler/auth_handler.go | 61 ++++++++++++++++++- modules/auth/repository/user_repo.go | 6 ++ modules/auth/routes.go | 6 ++ modules/auth/service/jwt.go | 10 +++ modules/inventory/handler/product_handler.go | 49 ++++++++++++--- .../handler/production_order_handler.go | 2 +- 7 files changed, 127 insertions(+), 10 deletions(-) diff --git a/middlewares/jwt.go b/middlewares/jwt.go index 4bf2d1b..824981c 100644 --- a/middlewares/jwt.go +++ b/middlewares/jwt.go @@ -35,7 +35,8 @@ func JWTProtected() fiber.Handler { }) } - c.Locals("user", claims) + c.Locals("user_id", uint(claims["user_id"].(float64))) + c.Locals("role", claims["role"].(string)) return c.Next() } } diff --git a/modules/auth/handler/auth_handler.go b/modules/auth/handler/auth_handler.go index 167bb6a..5fc0f96 100644 --- a/modules/auth/handler/auth_handler.go +++ b/modules/auth/handler/auth_handler.go @@ -1,12 +1,14 @@ package handler import ( + "BE-MiniERP/config" "BE-MiniERP/database" "BE-MiniERP/modules/auth/models" "BE-MiniERP/modules/auth/repository" "BE-MiniERP/modules/auth/service" "github.com/gofiber/fiber/v2" + "github.com/golang-jwt/jwt/v4" ) type AuthHandler struct { @@ -68,5 +70,62 @@ func (h *AuthHandler) Login(c *fiber.Ctx) error { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to generate token"}) } - return c.JSON(fiber.Map{"token": token, "role": user.Role}) + refreshToken, err := service.GenerateRefreshToken(user.ID) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to generate refresh token"}) + } + + return c.JSON(fiber.Map{"token": token, "refresh_token": refreshToken, "role": user.Role}) +} + +func (h *AuthHandler) GetUser(c *fiber.Ctx) error { + userID := c.Locals("user_id") // diset dari middleware + if userID == nil { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"}) + } + + user, err := h.Repo.FindByID(userID.(uint)) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to fetch user"}) + } + + return c.JSON(user) +} + +func (h *AuthHandler) RefreshToken(c *fiber.Ctx) error { + var input struct { + RefreshToken string `json:"refresh_token"` + } + + if err := c.BodyParser(&input); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid input"}) + } + + token, err := jwt.Parse(input.RefreshToken, func(token *jwt.Token) (interface{}, error) { + return []byte(config.GetConfig().JWTSecret), nil + }) + + if err != nil || !token.Valid { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "invalid or expired refresh token"}) + } + + claims, ok := token.Claims.(jwt.MapClaims) + if !ok || claims["user_id"] == nil { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "invalid claims"}) + } + + userID := uint(claims["user_id"].(float64)) + user, err := h.Repo.FindByID(userID) + if err != nil { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "user not found"}) + } + + newAccessToken, err := service.GenerateJWT(user.ID, user.Role) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to generate new token"}) + } + + return c.JSON(fiber.Map{ + "token": newAccessToken, + }) } diff --git a/modules/auth/repository/user_repo.go b/modules/auth/repository/user_repo.go index 9176e33..228b1cb 100644 --- a/modules/auth/repository/user_repo.go +++ b/modules/auth/repository/user_repo.go @@ -23,3 +23,9 @@ func (r *UserRepository) FindByUsername(username string) (*models.User, error) { result := r.DB.Where("username = ?", username).First(&user) return &user, result.Error } + +func (r *UserRepository) FindByID(id uint) (*models.User, error) { + var user models.User + result := r.DB.First(&user, id) + return &user, result.Error +} diff --git a/modules/auth/routes.go b/modules/auth/routes.go index 0724e89..0fbb782 100644 --- a/modules/auth/routes.go +++ b/modules/auth/routes.go @@ -1,6 +1,7 @@ package auth import ( + "BE-MiniERP/middlewares" "BE-MiniERP/modules/auth/handler" "github.com/gofiber/fiber/v2" @@ -10,4 +11,9 @@ func RegisterRoutes(r fiber.Router) { h := handler.NewAuthHandler() r.Post("/register", h.Register) r.Post("/login", h.Login) + + protected := r.Group("", middlewares.JWTProtected()) + + protected.Get("/me", middlewares.JWTProtected(), h.GetUser) + protected.Post("/refresh", h.RefreshToken) } diff --git a/modules/auth/service/jwt.go b/modules/auth/service/jwt.go index 3cb74fc..3183fdb 100644 --- a/modules/auth/service/jwt.go +++ b/modules/auth/service/jwt.go @@ -19,3 +19,13 @@ func GenerateJWT(userID uint, role string) (string, error) { // INI PALING PENTING ⬇️ return token.SignedString([]byte(config.GetConfig().JWTSecret)) } + +func GenerateRefreshToken(userID uint) (string, error) { + claims := jwt.MapClaims{ + "user_id": userID, + "exp": time.Now().Add(time.Hour * 24).Unix(), + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString([]byte(config.GetConfig().JWTSecret)) +} diff --git a/modules/inventory/handler/product_handler.go b/modules/inventory/handler/product_handler.go index 6a9049e..30988b2 100644 --- a/modules/inventory/handler/product_handler.go +++ b/modules/inventory/handler/product_handler.go @@ -6,6 +6,7 @@ import ( "BE-MiniERP/modules/inventory/repository" "BE-MiniERP/modules/inventory/service" "strconv" + "strings" "github.com/gofiber/fiber/v2" ) @@ -39,27 +40,61 @@ func (h *ProductHandler) GetAll(c *fiber.Ctx) error { func (h *ProductHandler) Create(c *fiber.Ctx) error { var input models.Product + if err := c.BodyParser(&input); err != nil { - return c.Status(400).JSON(fiber.Map{"error": "Invalid input"}) + return c.Status(400).JSON(fiber.Map{"error": "Invalid input format"}) + } + + if input.Name == "" { + return c.Status(400).JSON(fiber.Map{"error": "Product name is required"}) + } + if input.Price <= 0 { + return c.Status(400).JSON(fiber.Map{"error": "Price must be greater than 0"}) + } + if input.Hpp <= 0 { + return c.Status(400).JSON(fiber.Map{"error": "HPP must be greater than 0"}) + } + if input.CategoryID == 0 { + return c.Status(400).JSON(fiber.Map{"error": "Category ID is required"}) + } + if input.CollectionID == 0 { + return c.Status(400).JSON(fiber.Map{"error": "Collection ID is required"}) + } + if input.ColourID == 0 { + return c.Status(400).JSON(fiber.Map{"error": "Colour ID is required"}) + } + if input.SizeID == 0 { + return c.Status(400).JSON(fiber.Map{"error": "Size ID is required"}) + } + if input.UnitOfMeasure == "" { + return c.Status(400).JSON(fiber.Map{"error": "Unit of measure is required"}) } - // Jika hanya terima ID, lakukan preload relasi if err := h.Repo.PreloadRelations(&input); err != nil { - return c.Status(400).JSON(fiber.Map{"error": "Failed to preload relations"}) + return c.Status(400).JSON(fiber.Map{ + "error": "Failed to preload relations: " + err.Error(), + }) } - // Generate SKU sku, err := service.GenerateSKU(&input) if err != nil { - return c.Status(400).JSON(fiber.Map{"error": "Failed to generate SKU: " + err.Error()}) + return c.Status(400).JSON(fiber.Map{ + "error": "Failed to generate SKU: " + err.Error(), + }) } input.SKU = sku if err := h.Repo.Create(&input); err != nil { - return c.Status(500).JSON(fiber.Map{"error": "Failed to create product"}) + if strings.Contains(err.Error(), "duplicate key value") && strings.Contains(err.Error(), "sku") { + return c.Status(400).JSON(fiber.Map{"error": "SKU already exists"}) + } + + return c.Status(500).JSON(fiber.Map{ + "error": "Failed to create product: " + err.Error(), + }) } - return c.JSON(input) + return c.Status(201).JSON(input) } func (h *ProductHandler) Update(c *fiber.Ctx) error { diff --git a/modules/inventory/handler/production_order_handler.go b/modules/inventory/handler/production_order_handler.go index 9f9b38d..9eeafee 100644 --- a/modules/inventory/handler/production_order_handler.go +++ b/modules/inventory/handler/production_order_handler.go @@ -28,7 +28,7 @@ func (h *ProductionOrderHandler) GetAll(c *fiber.Ctx) error { productID := c.QueryInt("product_id", 0) startDateStr := c.Query("start_date", "") endDateStr := c.Query("end_date", "") - warehouseID := c.QueryInt("origin_id", 0) + warehouseID := c.QueryInt("warehouse_id", 0) var startDate, endDate time.Time var err error