refact: refact code for optimization
This commit is contained in:
parent
dd7fca697d
commit
83d6a09600
|
@ -1,112 +0,0 @@
|
|||
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"
|
||||
)
|
||||
|
||||
var (
|
||||
DB *gorm.DB
|
||||
DBHost string
|
||||
DBPort string
|
||||
DBName string
|
||||
DBUser string
|
||||
DBPassword string
|
||||
|
||||
APIKey string
|
||||
ServerHost string
|
||||
ServerPort string
|
||||
|
||||
RedisClient *redis.Client
|
||||
RedisHost string
|
||||
RedisPort string
|
||||
RedisPassword string
|
||||
RedisDB int
|
||||
)
|
||||
|
||||
func Context() context.Context {
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
func InitConfig() {
|
||||
ServerHost = os.Getenv("SERVER_HOST")
|
||||
ServerPort = os.Getenv("SERVER_PORT")
|
||||
DBHost = os.Getenv("DB_HOST")
|
||||
DBPort = os.Getenv("DB_PORT")
|
||||
DBName = os.Getenv("DB_NAME")
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
func InitDatabase() {
|
||||
InitConfig()
|
||||
|
||||
dsn := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=disable",
|
||||
DBHost, DBPort, DBUser, DBName, DBPassword)
|
||||
|
||||
var err error
|
||||
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatal("gagal terhubung ke database: ", err)
|
||||
}
|
||||
|
||||
err = DB.AutoMigrate(
|
||||
&domain.Point{},
|
||||
&domain.User{},
|
||||
&domain.UserRole{},
|
||||
&domain.UserPin{},
|
||||
&domain.MenuAccess{},
|
||||
&domain.PlatformHandle{},
|
||||
&domain.Address{},
|
||||
&domain.Article{},
|
||||
&domain.TrashCategory{},
|
||||
&domain.TrashDetail{},
|
||||
&domain.Banner{},
|
||||
&domain.CoverageArea{},
|
||||
&domain.CoverageDistric{},
|
||||
&domain.CoverageSubdistrict{},
|
||||
&domain.RequestPickup{},
|
||||
&domain.RequestItem{},
|
||||
&domain.Product{},
|
||||
&domain.ProductImage{},
|
||||
&domain.Store{},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal("Error: Failed to auto migrate domain:", err)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
func InitDatabase() {
|
||||
|
||||
InitConfig()
|
||||
|
||||
dsn := fmt.Sprintf(
|
||||
"host=%s port=%s user=%s dbname=%s password=%s sslmode=disable",
|
||||
DBHost, DBPort, DBUser, DBName, DBPassword,
|
||||
)
|
||||
|
||||
var err error
|
||||
|
||||
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatalf("Error: Gagal terhubung ke database: %v", err)
|
||||
}
|
||||
|
||||
err = DB.AutoMigrate(
|
||||
&domain.Point{},
|
||||
&domain.User{},
|
||||
&domain.UserRole{},
|
||||
&domain.UserPin{},
|
||||
&domain.MenuAccess{},
|
||||
&domain.PlatformHandle{},
|
||||
&domain.Address{},
|
||||
&domain.Article{},
|
||||
&domain.TrashCategory{},
|
||||
&domain.TrashDetail{},
|
||||
&domain.Banner{},
|
||||
&domain.CoverageArea{},
|
||||
&domain.CoverageDistric{},
|
||||
&domain.CoverageSubdistrict{},
|
||||
&domain.RequestPickup{},
|
||||
&domain.RequestItem{},
|
||||
&domain.Product{},
|
||||
&domain.ProductImage{},
|
||||
&domain.Store{},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Error: Gagal melakukan migrasi schema: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Koneksi ke database berhasil dan migrasi schema juga berhasil")
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
DBHost string
|
||||
DBPort string
|
||||
DBName string
|
||||
DBUser string
|
||||
DBPassword string
|
||||
|
||||
RedisHost string
|
||||
RedisPort string
|
||||
RedisPassword string
|
||||
RedisDB int
|
||||
|
||||
ServerHost string
|
||||
ServerPort string
|
||||
APIKey string
|
||||
)
|
||||
|
||||
func InitConfig() {
|
||||
|
||||
ServerHost = os.Getenv("SERVER_HOST")
|
||||
ServerPort = os.Getenv("SERVER_PORT")
|
||||
DBHost = os.Getenv("DB_HOST")
|
||||
DBPort = os.Getenv("DB_PORT")
|
||||
DBName = os.Getenv("DB_NAME")
|
||||
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: Beberapa environment variables tidak ditemukan.")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
var RedisClient *redis.Client
|
||||
|
||||
func Context() context.Context {
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
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.Fatalf("Error: Gagal terhubung ke Redis: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Koneksi ke Redis berhasil.")
|
||||
}
|
||||
|
||||
func GetFromCache(key string) (string, error) {
|
||||
val, err := RedisClient.Get(Context(), key).Result()
|
||||
if err == redis.Nil {
|
||||
|
||||
return "", nil
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func SetToCache(key string, value string, ttl time.Duration) error {
|
||||
err := RedisClient.Set(Context(), key, value, ttl).Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteFromCache(key string) error {
|
||||
err := RedisClient.Del(Context(), key).Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
package dto
|
||||
|
||||
import "github.com/go-playground/validator/v10"
|
||||
|
||||
type PointResponse struct {
|
||||
ID string `json:"id"`
|
||||
CoinName string `json:"coin_name"`
|
||||
|
@ -10,39 +8,12 @@ type PointResponse struct {
|
|||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func NewPointResponse(id, coinName string, valuePerUnit float64, createdAt, updatedAt string) PointResponse {
|
||||
return PointResponse{
|
||||
ID: id,
|
||||
CoinName: coinName,
|
||||
ValuePerUnit: valuePerUnit,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
type PointRequest struct {
|
||||
type PointCreateRequest struct {
|
||||
CoinName string `json:"coin_name" validate:"required"`
|
||||
ValuePerUnit float64 `json:"value_perunit" validate:"required,gt=0"`
|
||||
}
|
||||
|
||||
func NewPointRequest(coinName string, valuePerUnit float64) PointRequest {
|
||||
return PointRequest{
|
||||
CoinName: coinName,
|
||||
ValuePerUnit: valuePerUnit,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PointRequest) Validate() error {
|
||||
validate := validator.New()
|
||||
return validate.Struct(p)
|
||||
}
|
||||
|
||||
type PointUpdateDTO struct {
|
||||
CoinName string `json:"coin_name" validate:"required"`
|
||||
ValuePerUnit float64 `json:"value_perunit" validate:"required,gt=0"`
|
||||
}
|
||||
|
||||
func (p *PointUpdateDTO) Validate() error {
|
||||
validate := validator.New()
|
||||
return validate.Struct(p)
|
||||
type PointUpdateRequest struct {
|
||||
CoinName string `json:"coin_name,omitempty"`
|
||||
ValuePerUnit float64 `json:"value_perunit,omitempty"`
|
||||
}
|
|
@ -4,22 +4,30 @@ import (
|
|||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/controllers"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/middleware"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func AppRouter(app *fiber.App) {
|
||||
// # init
|
||||
pointRepo := repositories.NewPointRepository()
|
||||
pointService := services.NewPointService(pointRepo)
|
||||
pointController := controllers.NewPointController(pointService)
|
||||
|
||||
// # api group domain endpoint #
|
||||
api := app.Group("/apirijikid")
|
||||
|
||||
// # API Secure #
|
||||
api.Use(middleware.APIKeyMiddleware)
|
||||
api.Use(middleware.RateLimitMiddleware)
|
||||
|
||||
// # user initial coint #
|
||||
api.Get("/user/initial-coint", controllers.GetUserInitialCoint)
|
||||
api.Get("/user/initial-coint/:id", controllers.GetUserInitialCointById)
|
||||
api.Post("/user/initial-coint", middleware.RoleRequired(utils.RoleAdministrator), controllers.CreatePoint)
|
||||
api.Put("/user/initial-coint/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.UpdatePoint)
|
||||
api.Delete("/user/initial-coint/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.DeletePoint)
|
||||
api.Get("/user/initial-coint", pointController.GetAllPoints)
|
||||
api.Get("/user/initial-coint/:id", pointController.GetPointByID)
|
||||
api.Post("/user/initial-coint", middleware.RoleRequired(utils.RoleAdministrator), pointController.CreatePoint)
|
||||
api.Put("/user/initial-coint/:id", middleware.RoleRequired(utils.RoleAdministrator), pointController.UpdatePoint)
|
||||
api.Delete("/user/initial-coint/:id", middleware.RoleRequired(utils.RoleAdministrator), pointController.DeletePoint)
|
||||
|
||||
//# coverage area #
|
||||
api.Get("/coverage-areas", controllers.GetCoverageAreas)
|
||||
|
|
|
@ -7,183 +7,116 @@ import (
|
|||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func GetUserInitialCoint(c *fiber.Ctx) error {
|
||||
points, err := services.GetPoints()
|
||||
type PointController struct {
|
||||
service *services.PointService
|
||||
}
|
||||
|
||||
func NewPointController(service *services.PointService) *PointController {
|
||||
return &PointController{service: service}
|
||||
}
|
||||
|
||||
func (pc *PointController) GetAllPoints(c *fiber.Ctx) error {
|
||||
points, err := pc.service.GetAllPoints()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch points",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var pointResponses []dto.PointResponse
|
||||
for _, point := range points {
|
||||
pointResponses = append(pointResponses, dto.PointResponse{
|
||||
ID: point.ID,
|
||||
CoinName: point.CoinName,
|
||||
ValuePerUnit: point.ValuePerUnit,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(point.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(point.UpdatedAt),
|
||||
})
|
||||
}
|
||||
|
||||
if len(pointResponses) == 0 {
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Points successfully displayed but no data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Points fetched successfully",
|
||||
pointResponses,
|
||||
points,
|
||||
))
|
||||
|
||||
}
|
||||
|
||||
func GetUserInitialCointById(c *fiber.Ctx) error {
|
||||
func (pc *PointController) GetPointByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
point, err := services.GetPointByID(id)
|
||||
point, err := pc.service.GetPointByID(id)
|
||||
if err != nil {
|
||||
if err.Error() == "point not found" {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Point not found",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch point",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
pointResponse := dto.PointResponse{
|
||||
ID: point.ID,
|
||||
CoinName: point.CoinName,
|
||||
ValuePerUnit: point.ValuePerUnit,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(point.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(point.UpdatedAt),
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Point fetched successfully",
|
||||
pointResponse,
|
||||
point,
|
||||
))
|
||||
}
|
||||
|
||||
func CreatePoint(c *fiber.Ctx) error {
|
||||
var pointInput dto.PointRequest
|
||||
func (pc *PointController) CreatePoint(c *fiber.Ctx) error {
|
||||
var request dto.PointCreateRequest
|
||||
|
||||
if err := c.BodyParser(&pointInput); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
"Invalid request body",
|
||||
))
|
||||
}
|
||||
|
||||
if err := pointInput.Validate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Validation failed: "+err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
newPoint, err := services.CreatePoint(pointInput.CoinName, pointInput.ValuePerUnit)
|
||||
point, err := pc.service.CreatePoint(&request)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to create point",
|
||||
nil,
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
pointResponse := dto.NewPointResponse(
|
||||
newPoint.ID,
|
||||
newPoint.CoinName,
|
||||
newPoint.ValuePerUnit,
|
||||
utils.FormatDateToIndonesianFormat(newPoint.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(newPoint.UpdatedAt),
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse(
|
||||
fiber.StatusCreated,
|
||||
"Point created successfully",
|
||||
struct {
|
||||
Point dto.PointResponse `json:"point"`
|
||||
}{
|
||||
Point: pointResponse,
|
||||
},
|
||||
point,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdatePoint(c *fiber.Ctx) error {
|
||||
func (pc *PointController) UpdatePoint(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
var request dto.PointUpdateRequest
|
||||
|
||||
var pointInput dto.PointUpdateDTO
|
||||
|
||||
if err := c.BodyParser(&pointInput); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
"Invalid request body",
|
||||
))
|
||||
}
|
||||
|
||||
if err := pointInput.Validate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Validation failed: "+err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
updatedPoint, err := services.UpdatePoint(id, pointInput.CoinName, pointInput.ValuePerUnit)
|
||||
point, err := pc.service.UpdatePoint(id, &request)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to update point",
|
||||
nil,
|
||||
if err.Error() == "point not found" {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Point not found",
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
pointResponse := dto.NewPointResponse(
|
||||
updatedPoint.ID,
|
||||
updatedPoint.CoinName,
|
||||
updatedPoint.ValuePerUnit,
|
||||
utils.FormatDateToIndonesianFormat(updatedPoint.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(updatedPoint.UpdatedAt),
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Point updated successfully",
|
||||
struct {
|
||||
Point dto.PointResponse `json:"point"`
|
||||
}{
|
||||
Point: pointResponse,
|
||||
},
|
||||
point,
|
||||
))
|
||||
}
|
||||
|
||||
func DeletePoint(c *fiber.Ctx) error {
|
||||
func (pc *PointController) DeletePoint(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
err := services.DeletePoint(id)
|
||||
err := pc.service.DeletePoint(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
if err.Error() == "point not found" {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Point not found",
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to delete point",
|
||||
nil,
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,28 +1,54 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func APIKeyMiddleware(c *fiber.Ctx) error {
|
||||
|
||||
apiKey := c.Get("x-api-key")
|
||||
|
||||
expectedAPIKey := os.Getenv("API_KEY")
|
||||
|
||||
if apiKey != expectedAPIKey {
|
||||
|
||||
response := utils.FormatResponse(
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Invalid API Key",
|
||||
nil,
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(response)
|
||||
))
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
func RateLimitMiddleware(c *fiber.Ctx) error {
|
||||
apiKey := c.Get("x-api-key")
|
||||
if apiKey == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"API Key is missing",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
rateLimitKey := fmt.Sprintf("rate_limit:%s", apiKey)
|
||||
|
||||
count, _ := config.RedisClient.Incr(ctx, rateLimitKey).Result()
|
||||
if count > 100 {
|
||||
return c.Status(fiber.StatusTooManyRequests).JSON(utils.FormatResponse(
|
||||
fiber.StatusTooManyRequests,
|
||||
"Rate limit exceeded",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
config.RedisClient.Expire(ctx, rateLimitKey, time.Minute)
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
|
|
|
@ -13,9 +13,18 @@ import (
|
|||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func containsRole(roles []string, role string) bool {
|
||||
for _, r := range roles {
|
||||
if r == role {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func RoleRequired(roles ...string) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
tokenString := c.Get("Authorization")
|
||||
tokenString := strings.TrimPrefix(c.Get("Authorization"), "Bearer ")
|
||||
if tokenString == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
|
@ -24,8 +33,6 @@ 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 == "" {
|
||||
|
@ -63,7 +70,7 @@ func RoleRequired(roles ...string) fiber.Handler {
|
|||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Invalid or missing user ID in token",
|
||||
"Missing or invalid user ID in token",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
@ -72,7 +79,7 @@ func RoleRequired(roles ...string) fiber.Handler {
|
|||
if !ok || role == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Invalid or missing role in token",
|
||||
"Missing or invalid role in token",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
@ -80,59 +87,62 @@ func RoleRequired(roles ...string) fiber.Handler {
|
|||
c.Locals("userID", userID)
|
||||
c.Locals("role", role)
|
||||
|
||||
for _, r := range roles {
|
||||
if r == role {
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
if !containsRole(roles, role) {
|
||||
return c.Status(fiber.StatusForbidden).JSON(utils.FormatResponse(
|
||||
fiber.StatusForbidden,
|
||||
"You do not have permission to access this resource",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func AuthMiddleware(c *fiber.Ctx) error {
|
||||
tokenString := c.Get("Authorization")
|
||||
tokenString = strings.TrimPrefix(tokenString, "Bearer ")
|
||||
|
||||
tokenString := strings.TrimPrefix(c.Get("Authorization"), "Bearer ")
|
||||
if tokenString == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"message": "Missing or invalid token",
|
||||
})
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Missing or invalid token",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
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",
|
||||
})
|
||||
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) {
|
||||
return []byte(os.Getenv("API_KEY")), nil
|
||||
})
|
||||
|
||||
if err != nil || !token.Valid {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"message": "Invalid or expired token",
|
||||
})
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Invalid or expired token",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"message": "Invalid token claims",
|
||||
})
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Invalid token claims",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID := claims["sub"].(string)
|
||||
c.Locals("userID", userID)
|
||||
|
||||
config.RedisClient.Expire(ctx, "auth_token:"+tokenString, time.Hour*24).Err()
|
||||
config.RedisClient.Expire(ctx, "auth_token:"+tokenString, time.Hour*24)
|
||||
|
||||
return c.Next()
|
||||
}
|
|
@ -1,44 +1,44 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
func GetPoints() ([]domain.Point, error) {
|
||||
type PointRepository struct{}
|
||||
|
||||
func NewPointRepository() *PointRepository {
|
||||
return &PointRepository{}
|
||||
}
|
||||
|
||||
func (r *PointRepository) GetAll() ([]domain.Point, error) {
|
||||
var points []domain.Point
|
||||
if err := config.DB.Find(&points).Error; err != nil {
|
||||
return nil, err
|
||||
err := config.DB.Find(&points).Error
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to fetch points from database")
|
||||
}
|
||||
return points, nil
|
||||
}
|
||||
|
||||
func GetPointByID(id string) (domain.Point, error) {
|
||||
func (r *PointRepository) GetByID(id string) (*domain.Point, error) {
|
||||
var point domain.Point
|
||||
if err := config.DB.Where("id = ?", id).First(&point).Error; err != nil {
|
||||
return point, err
|
||||
err := config.DB.First(&point, "id = ?", id).Error
|
||||
if err != nil {
|
||||
return nil, errors.New("point not found")
|
||||
}
|
||||
return point, nil
|
||||
return &point, nil
|
||||
}
|
||||
|
||||
func CreatePoint(point *domain.Point) error {
|
||||
|
||||
if err := config.DB.Create(point).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
func (r *PointRepository) Create(point *domain.Point) error {
|
||||
return config.DB.Create(point).Error
|
||||
}
|
||||
|
||||
func UpdatePoint(point *domain.Point) error {
|
||||
if err := config.DB.Save(point).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
func (r *PointRepository) Update(point *domain.Point) error {
|
||||
return config.DB.Save(point).Error
|
||||
}
|
||||
|
||||
func DeletePoint(id string) error {
|
||||
if err := config.DB.Where("id = ?", id).Delete(&domain.Point{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
func (r *PointRepository) Delete(point *domain.Point) error {
|
||||
return config.DB.Delete(point).Error
|
||||
}
|
|
@ -1,65 +1,170 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func GetPoints() ([]domain.Point, error) {
|
||||
return repositories.GetPoints()
|
||||
type PointService struct {
|
||||
repo *repositories.PointRepository
|
||||
}
|
||||
|
||||
func GetPointByID(id string) (domain.Point, error) {
|
||||
point, err := repositories.GetPointByID(id)
|
||||
func NewPointService(repo *repositories.PointRepository) *PointService {
|
||||
return &PointService{repo: repo}
|
||||
}
|
||||
|
||||
func (s *PointService) GetAllPoints() ([]dto.PointResponse, error) {
|
||||
ctx := config.Context()
|
||||
|
||||
cacheKey := "points:all"
|
||||
cachedData, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil && cachedData != "" {
|
||||
var cachedPoints []dto.PointResponse
|
||||
if err := json.Unmarshal([]byte(cachedData), &cachedPoints); err == nil {
|
||||
return cachedPoints, nil
|
||||
}
|
||||
}
|
||||
|
||||
points, err := s.repo.GetAll()
|
||||
if err != nil {
|
||||
return domain.Point{}, errors.New("point not found")
|
||||
}
|
||||
return point, nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func CreatePoint(coinName string, valuePerUnit float64) (domain.Point, error) {
|
||||
|
||||
newPoint := domain.Point{
|
||||
CoinName: coinName,
|
||||
ValuePerUnit: valuePerUnit,
|
||||
var result []dto.PointResponse
|
||||
for _, point := range points {
|
||||
result = append(result, dto.PointResponse{
|
||||
ID: point.ID,
|
||||
CoinName: point.CoinName,
|
||||
ValuePerUnit: point.ValuePerUnit,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(point.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(point.UpdatedAt),
|
||||
})
|
||||
}
|
||||
|
||||
if err := repositories.CreatePoint(&newPoint); err != nil {
|
||||
return domain.Point{}, err
|
||||
cacheData, _ := json.Marshal(result)
|
||||
config.RedisClient.Set(ctx, cacheKey, cacheData, time.Minute*5)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
return newPoint, nil
|
||||
func (s *PointService) GetPointByID(id string) (*dto.PointResponse, error) {
|
||||
ctx := config.Context()
|
||||
|
||||
cacheKey := "points:" + id
|
||||
cachedData, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil && cachedData != "" {
|
||||
var cachedPoint dto.PointResponse
|
||||
if err := json.Unmarshal([]byte(cachedData), &cachedPoint); err == nil {
|
||||
return &cachedPoint, nil
|
||||
}
|
||||
}
|
||||
|
||||
func UpdatePoint(id, coinName string, valuePerUnit float64) (domain.Point, error) {
|
||||
|
||||
point, err := repositories.GetPointByID(id)
|
||||
point, err := s.repo.GetByID(id)
|
||||
if err != nil {
|
||||
return domain.Point{}, errors.New("point not found")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
point.CoinName = coinName
|
||||
point.ValuePerUnit = valuePerUnit
|
||||
|
||||
if err := repositories.UpdatePoint(&point); err != nil {
|
||||
return domain.Point{}, err
|
||||
result := &dto.PointResponse{
|
||||
ID: point.ID,
|
||||
CoinName: point.CoinName,
|
||||
ValuePerUnit: point.ValuePerUnit,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(point.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(point.UpdatedAt),
|
||||
}
|
||||
|
||||
return point, nil
|
||||
cacheData, _ := json.Marshal(result)
|
||||
config.RedisClient.Set(ctx, cacheKey, cacheData, time.Minute*5)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func DeletePoint(id string) error {
|
||||
func (s *PointService) CreatePoint(request *dto.PointCreateRequest) (*dto.PointResponse, error) {
|
||||
|
||||
_, err := repositories.GetPointByID(id)
|
||||
if request.CoinName == "" || request.ValuePerUnit <= 0 {
|
||||
return nil, errors.New("invalid input data")
|
||||
}
|
||||
|
||||
newPoint := &domain.Point{
|
||||
CoinName: request.CoinName,
|
||||
ValuePerUnit: request.ValuePerUnit,
|
||||
}
|
||||
|
||||
err := s.repo.Create(newPoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := config.Context()
|
||||
config.RedisClient.Del(ctx, "points:all")
|
||||
|
||||
response := &dto.PointResponse{
|
||||
ID: newPoint.ID,
|
||||
CoinName: newPoint.CoinName,
|
||||
ValuePerUnit: newPoint.ValuePerUnit,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(newPoint.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(newPoint.UpdatedAt),
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *PointService) UpdatePoint(id string, request *dto.PointUpdateRequest) (*dto.PointResponse, error) {
|
||||
|
||||
point, err := s.repo.GetByID(id)
|
||||
if err != nil {
|
||||
return nil, errors.New("point not found")
|
||||
}
|
||||
|
||||
if request.CoinName != "" {
|
||||
point.CoinName = request.CoinName
|
||||
}
|
||||
if request.ValuePerUnit > 0 {
|
||||
point.ValuePerUnit = request.ValuePerUnit
|
||||
}
|
||||
point.UpdatedAt = time.Now()
|
||||
|
||||
err = s.repo.Update(point)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to update point")
|
||||
}
|
||||
|
||||
ctx := config.Context()
|
||||
config.RedisClient.Del(ctx, "points:all")
|
||||
config.RedisClient.Del(ctx, "points:"+id)
|
||||
|
||||
response := &dto.PointResponse{
|
||||
ID: point.ID,
|
||||
CoinName: point.CoinName,
|
||||
ValuePerUnit: point.ValuePerUnit,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(point.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(point.UpdatedAt),
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *PointService) DeletePoint(id string) error {
|
||||
|
||||
point, err := s.repo.GetByID(id)
|
||||
if err != nil {
|
||||
return errors.New("point not found")
|
||||
}
|
||||
|
||||
if err := repositories.DeletePoint(id); err != nil {
|
||||
return err
|
||||
err = s.repo.Delete(point)
|
||||
if err != nil {
|
||||
return errors.New("failed to delete point")
|
||||
}
|
||||
|
||||
ctx := config.Context()
|
||||
config.RedisClient.Del(ctx, "points:all")
|
||||
config.RedisClient.Del(ctx, "points:"+id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ type Meta struct {
|
|||
|
||||
type ApiResponse struct {
|
||||
Meta Meta `json:"meta"`
|
||||
Data interface{} `json:"data"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func FormatResponse(statusCode int, message string, data interface{}) ApiResponse {
|
||||
|
@ -19,3 +19,12 @@ func FormatResponse(statusCode int, message string, data interface{}) ApiRespons
|
|||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorResponse(statusCode int, message string) ApiResponse {
|
||||
return ApiResponse{
|
||||
Meta: Meta{
|
||||
StatusCode: statusCode,
|
||||
Message: message,
|
||||
},
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue