feat: add feature set pin for user
This commit is contained in:
parent
add86d7d83
commit
5636d99dc9
|
@ -53,6 +53,9 @@ func InitDatabase() {
|
|||
err = DB.AutoMigrate(
|
||||
&domain.User{},
|
||||
&domain.UserRole{},
|
||||
&domain.UserPin{},
|
||||
&domain.MenuAccess{},
|
||||
&domain.PlatformHandle{},
|
||||
&domain.Address{},
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
import "time"
|
||||
|
||||
type Address struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type MenuAccess struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
|
||||
RoleID string `gorm:"not null" json:"roleId"`
|
||||
Role UserRole `gorm:"foreignKey:RoleID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"role"`
|
||||
MenuName string `gorm:"not null" json:"menuName"`
|
||||
Path string `gorm:"not null" json:"path"`
|
||||
IconURL string `gorm:"not null" json:"iconUrl"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package domain
|
||||
|
||||
type PlatformHandle struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
|
||||
Platform string `gorm:"not null" json:"platform"`
|
||||
Description string `gorm:"not null" json:"description"`
|
||||
}
|
|
@ -1,18 +1,17 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
import "time"
|
||||
|
||||
type User struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
|
||||
Avatar *string `json:"avatar,omitempty"`
|
||||
Username string `gorm:"unique;not null" json:"username"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Phone string `gorm:"not null" json:"phone"`
|
||||
Email string `gorm:"unique;not null" json:"email"`
|
||||
EmailVerified bool `gorm:"default:false" json:"emailVerified"`
|
||||
Password string `gorm:"not null" json:"password"`
|
||||
Pin UserPin `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"pin"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
|
||||
RoleID string `gorm:"not null" json:"roleId"`
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type UserPin struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
|
||||
UserID string `gorm:"not null" json:"userId"`
|
||||
Pin string `gorm:"not null" json:"pin"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
|
||||
}
|
|
@ -16,8 +16,6 @@ type AddressInput struct {
|
|||
Geography string `json:"geography" validate:"required"`
|
||||
}
|
||||
|
||||
var validate = validator.New()
|
||||
|
||||
func (c *AddressInput) ValidatePost() error {
|
||||
err := validate.Struct(c)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package dto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type PinInput struct {
|
||||
Pin string `json:"pin" validate:"required,len=6,numeric"`
|
||||
}
|
||||
|
||||
func (p *PinInput) ValidateCreate() error {
|
||||
err := validate.Struct(p)
|
||||
if err != nil {
|
||||
for _, e := range err.(validator.ValidationErrors) {
|
||||
switch e.Field() {
|
||||
case "Pin":
|
||||
return fmt.Errorf("PIN harus terdiri dari 6 digit angka")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PinUpdateInput struct {
|
||||
OldPin string `json:"old_pin" validate:"required,len=6,numeric"`
|
||||
NewPin string `json:"new_pin" validate:"required,len=6,numeric"`
|
||||
}
|
||||
|
||||
func (p *PinUpdateInput) ValidateUpdate() error {
|
||||
err := validate.Struct(p)
|
||||
if err != nil {
|
||||
for _, e := range err.(validator.ValidationErrors) {
|
||||
switch e.Field() {
|
||||
case "OldPin":
|
||||
return fmt.Errorf("PIN lama harus terdiri dari 6 digit angka")
|
||||
case "NewPin":
|
||||
return fmt.Errorf("PIN baru harus terdiri dari 6 digit angka")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package dto
|
||||
|
||||
import "github.com/go-playground/validator/v10"
|
||||
|
||||
var validate = validator.New()
|
|
@ -7,16 +7,24 @@ import (
|
|||
)
|
||||
|
||||
func AppRouter(app *fiber.App) {
|
||||
// # authentication
|
||||
app.Post("/register", controllers.Register)
|
||||
app.Post("/login", controllers.Login)
|
||||
|
||||
// # userinfo
|
||||
app.Get("/user", middleware.AuthMiddleware, controllers.GetUserInfo)
|
||||
app.Put("/update-user", middleware.AuthMiddleware, controllers.UpdateUser)
|
||||
app.Post("/user/update-password", middleware.AuthMiddleware, controllers.UpdatePassword)
|
||||
|
||||
// # user set pin
|
||||
app.Post("/user/set-pin", middleware.AuthMiddleware, controllers.CreatePin)
|
||||
app.Get("/user/get-pin", middleware.AuthMiddleware, controllers.GetPin)
|
||||
app.Put("/user/update-pin", middleware.AuthMiddleware, controllers.UpdatePin)
|
||||
|
||||
// # address routing
|
||||
app.Get("/list-address", middleware.AuthMiddleware, controllers.GetListAddress)
|
||||
app.Get("/address/:id", middleware.AuthMiddleware, controllers.GetAddressByID)
|
||||
app.Post("/create-address", middleware.AuthMiddleware, controllers.CreateAddress)
|
||||
app.Put("/address/:id", middleware.AuthMiddleware, controllers.UpdateAddress)
|
||||
app.Delete("/address/:id", middleware.AuthMiddleware, controllers.DeleteAddress)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func CreatePin(c *fiber.Ctx) error {
|
||||
var input dto.PinInput
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Data input tidak valid",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := input.ValidateCreate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
existingPin, err := services.GetPinByUserID(userID)
|
||||
if err == nil && existingPin.ID != "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"PIN sudah ada, tidak perlu dibuat lagi",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
pin, err := services.CreatePin(userID, input)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to create PIN",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
pinResponse := map[string]interface{}{
|
||||
"id": pin.ID,
|
||||
"createdAt": pin.CreatedAt,
|
||||
"updatedAt": pin.UpdatedAt,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"PIN created successfully",
|
||||
pinResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func GetPin(c *fiber.Ctx) error {
|
||||
var input dto.PinInput
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Data input tidak valid",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID := c.Locals("userID").(string)
|
||||
pin, err := services.GetPinByUserID(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"PIN tidak ditemukan",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
isPinValid := services.CheckPin(pin.Pin, input.Pin)
|
||||
|
||||
if isPinValid {
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"PIN benar",
|
||||
true,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"PIN salah",
|
||||
false,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdatePin(c *fiber.Ctx) error {
|
||||
var input dto.PinUpdateInput
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Data input tidak valid",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := input.ValidateUpdate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
updatedPin, err := services.UpdatePin(userID, input.OldPin, input.NewPin)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to update PIN",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"PIN updated successfully",
|
||||
updatedPin,
|
||||
))
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func CreatePin(pin *domain.UserPin) error {
|
||||
result := config.DB.Create(pin)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetPinByUserID(userID string) (domain.UserPin, error) {
|
||||
var pin domain.UserPin
|
||||
err := config.DB.Where("user_id = ?", userID).First(&pin).Error
|
||||
if err != nil {
|
||||
return pin, errors.New("PIN tidak ditemukan")
|
||||
}
|
||||
return pin, nil
|
||||
}
|
||||
|
||||
func UpdatePin(userID string, newPin string) (domain.UserPin, error) {
|
||||
var pin domain.UserPin
|
||||
|
||||
err := config.DB.Where("user_id = ?", userID).First(&pin).Error
|
||||
if err != nil {
|
||||
return pin, errors.New("PIN tidak ditemukan")
|
||||
}
|
||||
|
||||
hashedPin, err := bcrypt.GenerateFromPassword([]byte(newPin), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return pin, err
|
||||
}
|
||||
|
||||
pin.Pin = string(hashedPin)
|
||||
|
||||
if err := config.DB.Save(&pin).Error; err != nil {
|
||||
return pin, err
|
||||
}
|
||||
|
||||
return pin, nil
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func GetPinByUserID(userID string) (domain.UserPin, error) {
|
||||
pin, err := repositories.GetPinByUserID(userID)
|
||||
if err != nil {
|
||||
return pin, errors.New("PIN tidak ditemukan")
|
||||
}
|
||||
return pin, nil
|
||||
}
|
||||
|
||||
func CreatePin(userID string, input dto.PinInput) (domain.UserPin, error) {
|
||||
|
||||
hashedPin, err := bcrypt.GenerateFromPassword([]byte(input.Pin), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return domain.UserPin{}, err
|
||||
}
|
||||
|
||||
pin := domain.UserPin{
|
||||
UserID: userID,
|
||||
Pin: string(hashedPin),
|
||||
}
|
||||
|
||||
err = repositories.CreatePin(&pin)
|
||||
if err != nil {
|
||||
return domain.UserPin{}, err
|
||||
}
|
||||
|
||||
return pin, nil
|
||||
}
|
||||
|
||||
func UpdatePin(userID string, oldPin string, newPin string) (domain.UserPin, error) {
|
||||
|
||||
pin, err := repositories.GetPinByUserID(userID)
|
||||
if err != nil {
|
||||
return pin, errors.New("PIN tidak ditemukan")
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(pin.Pin), []byte(oldPin)); err != nil {
|
||||
return pin, errors.New("PIN lama tidak cocok")
|
||||
}
|
||||
|
||||
updatedPin, err := repositories.UpdatePin(userID, newPin)
|
||||
if err != nil {
|
||||
return updatedPin, err
|
||||
}
|
||||
|
||||
return updatedPin, nil
|
||||
}
|
||||
|
||||
func CheckPin(storedPinHash string, inputPin string) bool {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(storedPinHash), []byte(inputPin))
|
||||
return err == nil
|
||||
}
|
Loading…
Reference in New Issue