feat: add caching control with redis
This commit is contained in:
parent
674a1e1c50
commit
ef95e3bfbe
|
@ -1,10 +1,12 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
|
@ -21,6 +23,12 @@ var (
|
|||
APIKey string
|
||||
ServerHost string
|
||||
ServerPort string
|
||||
|
||||
RedisClient *redis.Client
|
||||
RedisHost string
|
||||
RedisPort string
|
||||
RedisPassword string
|
||||
RedisDB int
|
||||
)
|
||||
|
||||
func InitConfig() {
|
||||
|
@ -32,6 +40,10 @@ func InitConfig() {
|
|||
DBUser = os.Getenv("DB_USER")
|
||||
DBPassword = os.Getenv("DB_PASSWORD")
|
||||
APIKey = os.Getenv("API_KEY")
|
||||
RedisHost = os.Getenv("REDIS_HOST")
|
||||
RedisPort = os.Getenv("REDIS_PORT")
|
||||
RedisPassword = os.Getenv("REDIS_PASSWORD")
|
||||
RedisDB = 0
|
||||
|
||||
if ServerHost == "" || ServerPort == "" || DBHost == "" || DBPort == "" || DBName == "" || DBUser == "" || DBPassword == "" || APIKey == "" {
|
||||
log.Fatal("Error: environment variables yang dibutuhkan tidak ada")
|
||||
|
@ -77,3 +89,20 @@ func InitDatabase() {
|
|||
|
||||
fmt.Println("Koneksi ke database berhasil dan migrasi dilakukan")
|
||||
}
|
||||
|
||||
func InitRedis() {
|
||||
InitConfig()
|
||||
|
||||
RedisClient = redis.NewClient(&redis.Options{
|
||||
Addr: fmt.Sprintf("%s:%s", RedisHost, RedisPort),
|
||||
Password: RedisPassword,
|
||||
DB: RedisDB,
|
||||
})
|
||||
|
||||
_, err := RedisClient.Ping(context.Background()).Result()
|
||||
if err != nil {
|
||||
log.Fatal("Gagal terhubung ke Redis:", err)
|
||||
}
|
||||
|
||||
fmt.Println("Koneksi ke Redis berhasil")
|
||||
}
|
||||
|
|
3
go.mod
3
go.mod
|
@ -4,10 +4,13 @@ go 1.23.3
|
|||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.23.0 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/gofiber/fiber/v2 v2.52.5 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
|
|
6
go.sum
6
go.sum
|
@ -1,6 +1,10 @@
|
|||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
|
@ -9,6 +13,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
|||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
|
||||
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
||||
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
|
|
|
@ -42,6 +42,7 @@ func AppRouter(app *fiber.App) {
|
|||
// # authentication #
|
||||
api.Post("/register", controllers.Register)
|
||||
api.Post("/login", controllers.Login)
|
||||
api.Post("/logout", controllers.Logout)
|
||||
|
||||
// # userinfo #
|
||||
api.Get("/user", middleware.AuthMiddleware, controllers.GetUserInfo)
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
|
@ -86,6 +91,16 @@ func Login(c *fiber.Ctx) error {
|
|||
))
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
err = config.RedisClient.Set(ctx, "auth_token:"+token, credentials.Identifier, time.Hour*24).Err()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to store session",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
user, err := repositories.GetUserByEmailUsernameOrPhone(credentials.Identifier, "")
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
|
@ -248,3 +263,32 @@ func UpdatePassword(c *fiber.Ctx) error {
|
|||
},
|
||||
))
|
||||
}
|
||||
|
||||
func Logout(c *fiber.Ctx) error {
|
||||
tokenString := c.Get("Authorization")
|
||||
tokenString = strings.TrimPrefix(tokenString, "Bearer ")
|
||||
|
||||
if tokenString == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Token is required",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
err := config.RedisClient.Del(ctx, "auth_token:"+tokenString).Err()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to delete session",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Logout successful",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
|
@ -27,8 +31,16 @@ func CreatePin(c *fiber.Ctx) error {
|
|||
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
existingPin, err := services.GetPinByUserID(userID)
|
||||
if err == nil && existingPin.ID != "" {
|
||||
redisPin, err := config.RedisClient.Get(c.Context(), "pin:"+userID).Result()
|
||||
if err != nil && err != redis.Nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to check PIN from Redis",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if redisPin != "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"PIN sudah ada, tidak perlu dibuat lagi",
|
||||
|
@ -45,10 +57,18 @@ func CreatePin(c *fiber.Ctx) error {
|
|||
))
|
||||
}
|
||||
|
||||
err = config.RedisClient.Set(c.Context(), "pin:"+userID, pin.Pin, time.Minute*30).Err()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to save PIN to Redis",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
formattedCreatedAt := utils.FormatDateToIndonesianFormat(pin.CreatedAt)
|
||||
|
||||
pinResponse := dto.PinResponse{
|
||||
|
||||
CreatedAt: formattedCreatedAt,
|
||||
}
|
||||
|
||||
|
@ -62,25 +82,43 @@ func CreatePin(c *fiber.Ctx) error {
|
|||
func GetPinStatus(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
pin, err := services.GetPinByUserID(userID)
|
||||
if err != nil {
|
||||
_, err := config.RedisClient.Get(c.Context(), "pin:"+userID).Result()
|
||||
if err == redis.Nil {
|
||||
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Anda belum membuat PIN",
|
||||
pin, err := services.GetPinByUserID(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Anda belum membuat PIN",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
formattedCreatedAt := utils.FormatDateToIndonesianFormat(pin.CreatedAt)
|
||||
formattedUpdatedAt := utils.FormatDateToIndonesianFormat(pin.UpdatedAt)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"PIN sudah dibuat",
|
||||
map[string]interface{}{
|
||||
"createdAt": formattedCreatedAt,
|
||||
"updatedAt": formattedUpdatedAt,
|
||||
},
|
||||
))
|
||||
} else if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch PIN from Redis",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
formattedCreatedAt := utils.FormatDateToIndonesianFormat(pin.CreatedAt)
|
||||
formattedUpdatedAt := utils.FormatDateToIndonesianFormat(pin.UpdatedAt)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"PIN sudah dibuat",
|
||||
map[string]interface{}{
|
||||
"createdAt": formattedCreatedAt,
|
||||
"updatedAt": formattedUpdatedAt,
|
||||
"createdAt": "PIN ditemukan di Redis",
|
||||
"updatedAt": "PIN ditemukan di Redis",
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -96,19 +134,45 @@ func GetPin(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
userID := c.Locals("userID").(string)
|
||||
pin, err := services.GetPinByUserID(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Sepertinya anda belum membuat pin",
|
||||
|
||||
redisPin, err := config.RedisClient.Get(c.Context(), "pin:"+userID).Result()
|
||||
if err == redis.Nil {
|
||||
|
||||
pin, err := services.GetPinByUserID(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Sepertinya anda belum membuat pin",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
isPinValid := services.CheckPin(pin.Pin, input.Pin)
|
||||
if isPinValid {
|
||||
|
||||
config.RedisClient.Set(c.Context(), "pin:"+userID, pin.Pin, time.Minute*30)
|
||||
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,
|
||||
))
|
||||
} else if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch PIN from Redis",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
isPinValid := services.CheckPin(pin.Pin, input.Pin)
|
||||
|
||||
isPinValid := services.CheckPin(redisPin, input.Pin)
|
||||
if isPinValid {
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"PIN benar",
|
||||
|
@ -145,7 +209,6 @@ func UpdatePin(c *fiber.Ctx) error {
|
|||
|
||||
updatedPin, err := services.UpdatePin(userID, input.OldPin, input.NewPin)
|
||||
if err != nil {
|
||||
|
||||
if err.Error() == "PIN lama salah" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
|
@ -161,6 +224,9 @@ func UpdatePin(c *fiber.Ctx) error {
|
|||
))
|
||||
}
|
||||
|
||||
config.RedisClient.Del(c.Context(), "pin:"+userID)
|
||||
config.RedisClient.Set(c.Context(), "pin:"+userID, updatedPin.Pin, time.Minute*30)
|
||||
|
||||
formattedUpdatedAt := utils.FormatDateToIndonesianFormat(updatedPin.UpdatedAt)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
|
@ -23,6 +26,16 @@ func RoleRequired(roles ...string) fiber.Handler {
|
|||
|
||||
tokenString = strings.TrimPrefix(tokenString, "Bearer ")
|
||||
|
||||
ctx := context.Background()
|
||||
cachedToken, err := config.RedisClient.Get(ctx, "auth_token:"+tokenString).Result()
|
||||
if err != nil || cachedToken == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Invalid or expired token",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, errors.New("unexpected signing method")
|
||||
|
@ -91,6 +104,14 @@ func AuthMiddleware(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
cachedToken, err := config.RedisClient.Get(ctx, "auth_token:"+tokenString).Result()
|
||||
if err != nil || cachedToken == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"message": "Invalid or expired token",
|
||||
})
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(os.Getenv("API_KEY")), nil
|
||||
})
|
||||
|
@ -109,8 +130,9 @@ func AuthMiddleware(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
userID := claims["sub"].(string)
|
||||
|
||||
c.Locals("userID", userID)
|
||||
|
||||
config.RedisClient.Expire(ctx, "auth_token:"+tokenString, time.Hour*24).Err()
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
|
@ -12,15 +16,34 @@ func CreateAddress(address *domain.Address) error {
|
|||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("address:user:%s", address.UserID)
|
||||
config.RedisClient.Del(context.Background(), cacheKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAddressesByUserID(userID string) ([]domain.Address, error) {
|
||||
ctx := context.Background()
|
||||
cacheKey := fmt.Sprintf("address:user:%s", userID)
|
||||
|
||||
cachedAddresses, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil {
|
||||
var addresses []domain.Address
|
||||
if json.Unmarshal([]byte(cachedAddresses), &addresses) == nil {
|
||||
return addresses, nil
|
||||
}
|
||||
}
|
||||
|
||||
var addresses []domain.Address
|
||||
err := config.DB.Where("user_id = ?", userID).Find(&addresses).Error
|
||||
err = config.DB.Where("user_id = ?", userID).Find(&addresses).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addressesJSON, _ := json.Marshal(addresses)
|
||||
config.RedisClient.Set(ctx, cacheKey, addressesJSON, time.Hour).Err()
|
||||
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
|
@ -36,13 +59,25 @@ func UpdateAddress(address domain.Address) (domain.Address, error) {
|
|||
if err := config.DB.Save(&address).Error; err != nil {
|
||||
return address, err
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("address:user:%s", address.UserID)
|
||||
config.RedisClient.Del(context.Background(), cacheKey)
|
||||
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func DeleteAddress(addressID string) error {
|
||||
var address domain.Address
|
||||
if err := config.DB.Where("id = ?", addressID).Delete(&address).Error; err != nil {
|
||||
if err := config.DB.Where("id = ?", addressID).First(&address).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := config.DB.Delete(&address).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("address:user:%s", address.UserID)
|
||||
config.RedisClient.Del(context.Background(), cacheKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,17 +1,31 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
func IsEmailExist(email, roleId string) bool {
|
||||
ctx := context.Background()
|
||||
cacheKey := fmt.Sprintf("email:%s", email)
|
||||
cachedRole, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil && cachedRole == roleId {
|
||||
return true
|
||||
}
|
||||
|
||||
var user domain.User
|
||||
if err := config.DB.Where("email = ?", email).First(&user).Error; err == nil {
|
||||
if user.RoleID == roleId {
|
||||
if err := config.RedisClient.Set(ctx, cacheKey, roleId, 24*time.Hour).Err(); err != nil {
|
||||
log.Printf("Redis Set error: %v", err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +33,19 @@ func IsEmailExist(email, roleId string) bool {
|
|||
}
|
||||
|
||||
func IsUsernameExist(username, roleId string) bool {
|
||||
ctx := context.Background()
|
||||
cacheKey := fmt.Sprintf("username:%s", username)
|
||||
cachedRole, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil && cachedRole == roleId {
|
||||
return true
|
||||
}
|
||||
|
||||
var user domain.User
|
||||
if err := config.DB.Where("username = ?", username).First(&user).Error; err == nil {
|
||||
if user.RoleID == roleId {
|
||||
if err := config.RedisClient.Set(ctx, cacheKey, roleId, 24*time.Hour).Err(); err != nil {
|
||||
log.Printf("Redis Set error: %v", err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -29,9 +53,19 @@ func IsUsernameExist(username, roleId string) bool {
|
|||
}
|
||||
|
||||
func IsPhoneExist(phone, roleId string) bool {
|
||||
ctx := context.Background()
|
||||
cacheKey := fmt.Sprintf("phone:%s", phone)
|
||||
cachedRole, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil && cachedRole == roleId {
|
||||
return true
|
||||
}
|
||||
|
||||
var user domain.User
|
||||
if err := config.DB.Where("phone = ?", phone).First(&user).Error; err == nil {
|
||||
if user.RoleID == roleId {
|
||||
if err := config.RedisClient.Set(ctx, cacheKey, roleId, 24*time.Hour).Err(); err != nil {
|
||||
log.Printf("Redis Set error: %v", err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -56,8 +90,20 @@ func CreateUser(username, name, email, phone, password, roleId string) error {
|
|||
}
|
||||
|
||||
func GetUserByEmailUsernameOrPhone(identifier, roleId string) (domain.User, error) {
|
||||
ctx := context.Background()
|
||||
cacheKey := fmt.Sprintf("user:%s", identifier)
|
||||
var user domain.User
|
||||
err := config.DB.Where("email = ? OR username = ? OR phone = ?", identifier, identifier, identifier).First(&user).Error
|
||||
|
||||
cachedUser, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil {
|
||||
if err := json.Unmarshal([]byte(cachedUser), &user); err == nil {
|
||||
if roleId == "" || user.RoleID == roleId {
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = config.DB.Where("email = ? OR username = ? OR phone = ?", identifier, identifier, identifier).First(&user).Error
|
||||
if err != nil {
|
||||
return user, errors.New("user not found")
|
||||
}
|
||||
|
@ -66,28 +112,45 @@ func GetUserByEmailUsernameOrPhone(identifier, roleId string) (domain.User, erro
|
|||
return user, errors.New("identifier found but role does not match")
|
||||
}
|
||||
|
||||
userJSON, _ := json.Marshal(user)
|
||||
if err := config.RedisClient.Set(ctx, cacheKey, userJSON, 1*time.Hour).Err(); err != nil {
|
||||
log.Printf("Redis Set error: %v", err)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func GetUserByID(userID string) (domain.User, error) {
|
||||
ctx := context.Background()
|
||||
cacheKey := fmt.Sprintf("user:%s", userID)
|
||||
var user domain.User
|
||||
if err := config.DB.
|
||||
Preload("Role").
|
||||
Where("id = ?", userID).
|
||||
First(&user).Error; err != nil {
|
||||
|
||||
cachedUser, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil {
|
||||
if err := json.Unmarshal([]byte(cachedUser), &user); err == nil {
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := config.DB.Preload("Role").Where("id = ?", userID).First(&user).Error; err != nil {
|
||||
return user, errors.New("user not found")
|
||||
}
|
||||
|
||||
fmt.Printf("User ID: %s, Role: %v\n", user.ID, user.Role)
|
||||
userJSON, _ := json.Marshal(user)
|
||||
if err := config.RedisClient.Set(ctx, cacheKey, userJSON, 1*time.Hour).Err(); err != nil {
|
||||
log.Printf("Redis Set error: %v", err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func UpdateUser(user *domain.User) error {
|
||||
|
||||
if err := config.DB.Save(user).Error; err != nil {
|
||||
return errors.New("failed to update user")
|
||||
}
|
||||
cacheKey := fmt.Sprintf("user:%s", user.ID)
|
||||
if err := config.RedisClient.Del(context.Background(), cacheKey).Err(); err != nil {
|
||||
log.Printf("Redis Del error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -104,5 +167,10 @@ func UpdateUserPassword(userID, newPassword string) error {
|
|||
return errors.New("failed to update password")
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("user:%s", userID)
|
||||
if err := config.RedisClient.Del(context.Background(), cacheKey).Err(); err != nil {
|
||||
log.Printf("Redis Del error: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
|
@ -17,12 +20,30 @@ func CreatePin(pin *domain.UserPin) error {
|
|||
}
|
||||
|
||||
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")
|
||||
|
||||
ctx := context.Background()
|
||||
redisClient := config.RedisClient
|
||||
|
||||
redisKey := fmt.Sprintf("user_pin:%s", userID)
|
||||
|
||||
pin, err := redisClient.Get(ctx, redisKey).Result()
|
||||
if err == nil {
|
||||
|
||||
return domain.UserPin{
|
||||
UserID: userID,
|
||||
Pin: pin,
|
||||
}, nil
|
||||
}
|
||||
return pin, nil
|
||||
|
||||
var dbPin domain.UserPin
|
||||
err = config.DB.Where("user_id = ?", userID).First(&dbPin).Error
|
||||
if err != nil {
|
||||
return dbPin, errors.New("PIN tidak ditemukan")
|
||||
}
|
||||
|
||||
redisClient.Set(ctx, redisKey, dbPin.Pin, 5*time.Minute)
|
||||
|
||||
return dbPin, nil
|
||||
}
|
||||
|
||||
func UpdatePin(userID string, newPin string) (domain.UserPin, error) {
|
||||
|
@ -44,5 +65,8 @@ func UpdatePin(userID string, newPin string) (domain.UserPin, error) {
|
|||
return pin, err
|
||||
}
|
||||
|
||||
redisClient := config.RedisClient
|
||||
redisClient.Del(context.Background(), fmt.Sprintf("user_pin:%s", userID))
|
||||
|
||||
return pin, nil
|
||||
}
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
|
@ -25,15 +30,32 @@ func CreateAddress(userID string, input dto.AddressInput) (domain.Address, error
|
|||
return domain.Address{}, err
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("address:user:%s", userID)
|
||||
config.RedisClient.Del(context.Background(), cacheKey)
|
||||
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func GetAllAddressesByUserID(userID string) ([]domain.Address, error) {
|
||||
ctx := context.Background()
|
||||
cacheKey := fmt.Sprintf("address:user:%s", userID)
|
||||
|
||||
cachedAddresses, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil {
|
||||
var addresses []domain.Address
|
||||
if json.Unmarshal([]byte(cachedAddresses), &addresses) == nil {
|
||||
return addresses, nil
|
||||
}
|
||||
}
|
||||
|
||||
addresses, err := repositories.GetAddressesByUserID(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addressesJSON, _ := json.Marshal(addresses)
|
||||
config.RedisClient.Set(ctx, cacheKey, addressesJSON, time.Hour).Err()
|
||||
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
|
@ -46,7 +68,6 @@ func GetAddressByID(addressID string) (domain.Address, error) {
|
|||
}
|
||||
|
||||
func UpdateAddress(addressID string, input dto.AddressInput) (domain.Address, error) {
|
||||
|
||||
address, err := repositories.GetAddressByID(addressID)
|
||||
if err != nil {
|
||||
return address, errors.New("address not found")
|
||||
|
@ -65,13 +86,25 @@ func UpdateAddress(addressID string, input dto.AddressInput) (domain.Address, er
|
|||
return updatedAddress, errors.New("failed to update address")
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("address:user:%s", address.UserID)
|
||||
config.RedisClient.Del(context.Background(), cacheKey)
|
||||
|
||||
return updatedAddress, nil
|
||||
}
|
||||
|
||||
func DeleteAddress(addressID string) error {
|
||||
err := repositories.DeleteAddress(addressID)
|
||||
address, err := repositories.GetAddressByID(addressID)
|
||||
if err != nil {
|
||||
return errors.New("address not found")
|
||||
}
|
||||
|
||||
err = repositories.DeleteAddress(addressID)
|
||||
if err != nil {
|
||||
return errors.New("failed to delete address")
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("address:user:%s", address.UserID)
|
||||
config.RedisClient.Del(context.Background(), cacheKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
)
|
||||
|
||||
func RegisterUser(username, name, email, phone, password, confirmPassword, roleId string) error {
|
||||
|
||||
if password != confirmPassword {
|
||||
return errors.New("password dan confirm password tidak cocok")
|
||||
}
|
||||
|
@ -48,7 +47,6 @@ func LoginUser(identifier, password string) (string, error) {
|
|||
}
|
||||
|
||||
const roleId = ""
|
||||
|
||||
user, err := repositories.GetUserByEmailUsernameOrPhone(identifier, roleId)
|
||||
if err != nil {
|
||||
return "", errors.New("invalid email/username/phone or password")
|
||||
|
@ -71,7 +69,6 @@ func generateJWT(userID, role string) string {
|
|||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
t, err := token.SignedString([]byte(os.Getenv("API_KEY")))
|
||||
if err != nil {
|
||||
return ""
|
||||
|
@ -89,7 +86,6 @@ func GetUserByID(userID string) (domain.User, error) {
|
|||
}
|
||||
|
||||
func UpdateUser(userID, email, username, name, phone string) error {
|
||||
|
||||
user, err := repositories.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return errors.New("user not found")
|
||||
|
@ -129,7 +125,6 @@ func UpdateUser(userID, email, username, name, phone string) error {
|
|||
}
|
||||
|
||||
func UpdatePassword(userID, oldPassword, newPassword string) error {
|
||||
|
||||
user, err := repositories.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return errors.New("user not found")
|
||||
|
|
|
@ -9,14 +9,6 @@ import (
|
|||
"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)
|
||||
|
@ -37,7 +29,17 @@ func CreatePin(userID string, input dto.PinInput) (domain.UserPin, error) {
|
|||
return pin, nil
|
||||
}
|
||||
|
||||
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 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")
|
||||
|
|
|
@ -18,6 +18,7 @@ func init() {
|
|||
|
||||
config.InitConfig()
|
||||
config.InitDatabase()
|
||||
config.InitRedis()
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue