feat: add caching control with redis

This commit is contained in:
pahmiudahgede 2025-01-22 12:20:51 +07:00
parent 674a1e1c50
commit ef95e3bfbe
14 changed files with 382 additions and 53 deletions

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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)

View File

@ -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,
))
}

View File

@ -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(

View File

@ -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()
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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")

View File

@ -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")

View File

@ -18,6 +18,7 @@ func init() {
config.InitConfig()
config.InitDatabase()
config.InitRedis()
}