commit
fb6d930ae6
25
.env.example
25
.env.example
|
@ -1,13 +1,22 @@
|
|||
# SERVER SETTINGS
|
||||
SERVER_HOST=localhost
|
||||
SERVER_PORT= # isi listen port anda (bebas)
|
||||
SERVER_HOST=
|
||||
SERVER_PORT=
|
||||
|
||||
# DATABASE SETTINGS
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432 # port default postgres
|
||||
DB_NAME= # nama_database di postgres
|
||||
DB_USER= # username yang digunakan di postgres
|
||||
DB_PASSWORD= # password yang digunakan di postgres
|
||||
DB_HOST=
|
||||
DB_PORT=
|
||||
DB_NAME=
|
||||
DB_USER=
|
||||
DB_PASSWORD=
|
||||
|
||||
# api keyauth
|
||||
# REDIS SETTINGS
|
||||
REDIS_HOST=
|
||||
REDIS_PORT=
|
||||
REDIS_PASSWORD=
|
||||
REDIS_DB=
|
||||
|
||||
# Keyauth
|
||||
API_KEY=
|
||||
|
||||
#SECRET_KEY
|
||||
SECRET_KEY=
|
||||
|
|
|
@ -22,4 +22,11 @@ go.work
|
|||
go.work.sum
|
||||
|
||||
# env file
|
||||
.env
|
||||
.env
|
||||
.env.prod
|
||||
.env.dev
|
||||
|
||||
# Ignore avatar images
|
||||
/public/uploads/avatars/
|
||||
/public/uploads/articles/
|
||||
/public/uploads/banners/
|
|
@ -0,0 +1,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/router"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config.SetupConfig()
|
||||
|
||||
app := fiber.New()
|
||||
|
||||
router.SetupRoutes(app)
|
||||
|
||||
config.StartServer(app)
|
||||
}
|
|
@ -3,54 +3,55 @@ package config
|
|||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
func InitDatabase() {
|
||||
|
||||
InitConfig()
|
||||
func ConnectDatabase() {
|
||||
|
||||
dsn := fmt.Sprintf(
|
||||
"host=%s port=%s user=%s dbname=%s password=%s sslmode=disable",
|
||||
DBHost, DBPort, DBUser, DBName, DBPassword,
|
||||
"host=%s user=%s password=%s dbname=%s port=%s sslmode=disable",
|
||||
os.Getenv("DB_HOST"),
|
||||
os.Getenv("DB_USER"),
|
||||
os.Getenv("DB_PASSWORD"),
|
||||
os.Getenv("DB_NAME"),
|
||||
os.Getenv("DB_PORT"),
|
||||
)
|
||||
|
||||
var err error
|
||||
|
||||
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatalf("Error: Gagal terhubung ke database: %v", err)
|
||||
log.Fatalf("Error connecting to database: %v", err)
|
||||
}
|
||||
log.Println("Database connected successfully!")
|
||||
|
||||
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{},
|
||||
// ==wilayah indonesia==
|
||||
&model.Province{},
|
||||
&model.Regency{},
|
||||
&model.District{},
|
||||
&model.Village{},
|
||||
// ==wilayah indonesia==
|
||||
|
||||
// ==main feature==
|
||||
&model.User{},
|
||||
&model.Role{},
|
||||
&model.UserPin{},
|
||||
&model.Address{},
|
||||
&model.Article{},
|
||||
&model.Banner{},
|
||||
&model.InitialCoint{},
|
||||
&model.TrashCategory{},
|
||||
&model.TrashDetail{},
|
||||
// ==main feature==
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Error: Gagal melakukan migrasi schema: %v", err)
|
||||
log.Fatalf("Error performing auto-migration: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Koneksi ke database berhasil dan migrasi schema juga berhasil")
|
||||
log.Println("Database migrated successfully!")
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
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.")
|
||||
}
|
||||
}
|
|
@ -4,58 +4,24 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
"os"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
var RedisClient *redis.Client
|
||||
var Ctx = context.Background()
|
||||
|
||||
func Context() context.Context {
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
func InitRedis() {
|
||||
|
||||
InitConfig()
|
||||
|
||||
func ConnectRedis() {
|
||||
RedisClient = redis.NewClient(&redis.Options{
|
||||
Addr: fmt.Sprintf("%s:%s", RedisHost, RedisPort),
|
||||
Password: RedisPassword,
|
||||
DB: RedisDB,
|
||||
Addr: fmt.Sprintf("%s:%s", os.Getenv("REDIS_HOST"), os.Getenv("REDIS_PORT")),
|
||||
Password: os.Getenv("REDIS_PASSWORD"),
|
||||
DB: 0,
|
||||
})
|
||||
|
||||
_, err := RedisClient.Ping(context.Background()).Result()
|
||||
_, err := RedisClient.Ping(Ctx).Result()
|
||||
if err != nil {
|
||||
log.Fatalf("Error: Gagal terhubung ke Redis: %v", err)
|
||||
log.Fatalf("Error connecting to 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
|
||||
log.Println("Redis connected successfully!")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func StartServer(app *fiber.App) {
|
||||
host := os.Getenv("SERVER_HOST")
|
||||
port := os.Getenv("SERVER_PORT")
|
||||
|
||||
address := fmt.Sprintf("%s:%s", host, port)
|
||||
|
||||
log.Printf("Server is running on http://%s", address)
|
||||
if err := app.Listen(address); err != nil {
|
||||
log.Fatalf("Error starting server: %v", err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func SetupConfig() {
|
||||
err := godotenv.Load(".env.dev")
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading .env file: %v", err)
|
||||
}
|
||||
|
||||
ConnectDatabase()
|
||||
ConnectRedis()
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package domain
|
||||
|
||||
type Province struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ListRegency []Regency `json:"list_regency,omitempty"`
|
||||
}
|
||||
|
||||
type Regency struct {
|
||||
ID string `json:"id"`
|
||||
ProvinceID string `json:"province_id"`
|
||||
Name string `json:"name"`
|
||||
Province *Province `json:"province,omitempty"`
|
||||
ListDistrict []District `json:"list_district,omitempty"`
|
||||
}
|
||||
|
||||
type District struct {
|
||||
ID string `json:"id"`
|
||||
RegencyID string `json:"regency_id"`
|
||||
Name string `json:"name"`
|
||||
Regency *Regency `json:"regency,omitempty"`
|
||||
ListVillage []Village `json:"list_village,omitempty"`
|
||||
}
|
||||
|
||||
type Village struct {
|
||||
ID string `json:"id"`
|
||||
DistrictID string `json:"district_id"`
|
||||
Name string `json:"name"`
|
||||
District *District `json:"district,omitempty"`
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type Address struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
|
||||
UserID string `gorm:"not null" json:"userId"`
|
||||
User User `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"user"`
|
||||
Province string `gorm:"not null" json:"province"`
|
||||
District string `gorm:"not null" json:"district"`
|
||||
Subdistrict string `gorm:"not null" json:"subdistrict"`
|
||||
PostalCode int `gorm:"not null" json:"postalCode"`
|
||||
Village string `gorm:"not null" json:"village"`
|
||||
Detail string `gorm:"not null" json:"detail"`
|
||||
Geography string `gorm:"not null" json:"geography"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type Article struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
|
||||
Title string `gorm:"not null" json:"title"`
|
||||
CoverImage string `gorm:"not null" json:"coverImage"`
|
||||
Author string `gorm:"not null" json:"author"`
|
||||
Heading string `gorm:"not null" json:"heading"`
|
||||
Content string `gorm:"not null" json:"content"`
|
||||
PublishedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type CoverageArea struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
Province string `gorm:"not null" json:"province"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
|
||||
CoverageDistrics []CoverageDistric `gorm:"foreignKey:CoverageAreaID" json:"coverage_districs"`
|
||||
}
|
||||
|
||||
type CoverageDistric struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
CoverageAreaID string `gorm:"not null;constraint:OnDelete:CASCADE;OnUpdate:CASCADE;" json:"coverage_area_id"`
|
||||
District string `gorm:"not null" json:"district"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
|
||||
CoverageSubdistricts []CoverageSubdistrict `gorm:"foreignKey:CoverageDistrictId" json:"subdistricts"`
|
||||
}
|
||||
|
||||
type CoverageSubdistrict struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
CoverageAreaID string `gorm:"not null" json:"coverage_area_id"`
|
||||
CoverageDistrictId string `gorm:"not null;constraint:OnDelete:CASCADE;OnUpdate:CASCADE;" json:"coverage_district_id"`
|
||||
Subdistrict string `gorm:"not null" json:"subdistrict"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
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"`
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
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,28 +0,0 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type Product struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
UserID string `gorm:"type:uuid;not null" json:"user_id"`
|
||||
StoreID string `gorm:"type:uuid;not null" json:"store_id"`
|
||||
Store Store `gorm:"foreignKey:StoreID" json:"store"`
|
||||
ProductTitle string `gorm:"not null" json:"product_title"`
|
||||
ProductImages []ProductImage `gorm:"foreignKey:ProductID;constraint:OnDelete:CASCADE;" json:"product_images"`
|
||||
TrashDetailID string `gorm:"type:uuid;not null" json:"trash_detail_id"`
|
||||
TrashDetail TrashDetail `gorm:"foreignKey:TrashDetailID" json:"trash_detail"`
|
||||
SalePrice int64 `gorm:"not null" json:"sale_price"`
|
||||
Quantity int `gorm:"not null" json:"quantity"`
|
||||
ProductDescribe string `gorm:"type:text" json:"product_describe"`
|
||||
Sold int `gorm:"default:0" json:"sold"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"`
|
||||
}
|
||||
|
||||
type ProductImage struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
ProductID string `gorm:"type:uuid;not null" json:"product_id"`
|
||||
ImageURL string `gorm:"not null" json:"image_url"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"`
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
|
||||
type RequestPickup struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
UserID string `gorm:"type:uuid;not null" json:"userId"`
|
||||
Request []RequestItem `gorm:"foreignKey:RequestPickupID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"request"`
|
||||
RequestTime string `gorm:"type:text;not null" json:"requestTime"`
|
||||
UserAddressID string `gorm:"type:uuid;not null" json:"userAddressId"`
|
||||
UserAddress Address `gorm:"foreignKey:UserAddressID" json:"userAddress"`
|
||||
StatusRequest string `gorm:"type:text;not null" json:"statusRequest"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
|
||||
}
|
||||
|
||||
type RequestItem struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
RequestPickupID string `gorm:"type:uuid;not null;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"requestPickupId"`
|
||||
TrashCategoryID string `gorm:"type:uuid;not null" json:"trashCategoryId"`
|
||||
TrashCategory TrashCategory `gorm:"foreignKey:TrashCategoryID" json:"trashCategory"`
|
||||
EstimatedAmount string `gorm:"type:text;not null" json:"estimatedAmount"`
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package domain
|
||||
|
||||
type UserRole struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
|
||||
RoleName string `gorm:"unique;not null" json:"roleName"`
|
||||
// Users []User `gorm:"foreignKey:RoleID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"users"`
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type Store struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
UserID string `gorm:"type:uuid;not null" json:"user_id"`
|
||||
User User `gorm:"foreignKey:UserID" json:"user"`
|
||||
StoreName string `gorm:"not null;unique" json:"store_name"`
|
||||
StoreLogo string `json:"store_logo"`
|
||||
StoreBanner string `json:"store_banner"`
|
||||
StoreDesc string `gorm:"type:text" json:"store_desc"`
|
||||
Follower int `gorm:"default:0" json:"follower"`
|
||||
StoreRating float64 `gorm:"default:0" json:"store_rating"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"`
|
||||
}
|
||||
|
||||
// type StoreFinance struct {
|
||||
// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
// StoreID string `gorm:"type:uuid;not null" json:"store_id"`
|
||||
// TotalRevenue int64 `gorm:"default:0" json:"total_revenue"`
|
||||
// TotalExpenses int64 `gorm:"default:0" json:"total_expenses"`
|
||||
// NetProfit int64 `gorm:"default:0" json:"net_profit"`
|
||||
// OrdersCount int `gorm:"default:0" json:"orders_count"`
|
||||
// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"`
|
||||
// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"`
|
||||
// }
|
||||
|
||||
// type Order struct {
|
||||
// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
// StoreID string `gorm:"type:uuid;not null" json:"store_id"`
|
||||
// UserID string `gorm:"type:uuid;not null" json:"user_id"`
|
||||
// TotalPrice int64 `gorm:"not null" json:"total_price"`
|
||||
// OrderStatus string `gorm:"not null" json:"order_status"`
|
||||
// ShippingStatus string `gorm:"not null" json:"shipping_status"`
|
||||
// PaymentStatus string `gorm:"not null" json:"payment_status"`
|
||||
// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"`
|
||||
// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"`
|
||||
// }
|
||||
|
||||
// type OrderDetail struct {
|
||||
// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
// OrderID string `gorm:"type:uuid;not null" json:"order_id"`
|
||||
// ProductID string `gorm:"type:uuid;not null" json:"product_id"`
|
||||
// Quantity int `gorm:"not null" json:"quantity"`
|
||||
// UnitPrice int64 `gorm:"not null" json:"unit_price"`
|
||||
// TotalPrice int64 `gorm:"not null" json:"total_price"`
|
||||
// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"`
|
||||
// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"`
|
||||
// }
|
||||
|
||||
// type Shipping struct {
|
||||
// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
// OrderID string `gorm:"type:uuid;not null" json:"order_id"`
|
||||
// ShippingDate time.Time `gorm:"default:current_timestamp" json:"shipping_date"`
|
||||
// ShippingCost int64 `gorm:"not null" json:"shipping_cost"`
|
||||
// TrackingNo string `gorm:"not null" json:"tracking_no"`
|
||||
// ShippingStatus string `gorm:"not null" json:"shipping_status"`
|
||||
// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"`
|
||||
// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"`
|
||||
// }
|
||||
|
||||
// type Cancellation struct {
|
||||
// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
// OrderID string `gorm:"type:uuid;not null" json:"order_id"`
|
||||
// Reason string `gorm:"type:text" json:"reason"`
|
||||
// CancelledAt time.Time `gorm:"default:current_timestamp" json:"cancelled_at"`
|
||||
// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"`
|
||||
// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"`
|
||||
// }
|
||||
|
||||
// type Return struct {
|
||||
// ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
|
||||
// OrderID string `gorm:"type:uuid;not null" json:"order_id"`
|
||||
// Reason string `gorm:"type:text" json:"reason"`
|
||||
// ReturnedAt time.Time `gorm:"default:current_timestamp" json:"returned_at"`
|
||||
// CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"`
|
||||
// UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"`
|
||||
// }
|
|
@ -1,84 +0,0 @@
|
|||
package dto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type AddressInput struct {
|
||||
Province string `json:"province" validate:"required"`
|
||||
District string `json:"district" validate:"required"`
|
||||
Subdistrict string `json:"subdistrict" validate:"required"`
|
||||
PostalCode int `json:"postalCode" validate:"required,numeric"`
|
||||
Village string `json:"village" validate:"required"`
|
||||
Detail string `json:"detail" validate:"required"`
|
||||
Geography string `json:"geography" validate:"required"`
|
||||
}
|
||||
|
||||
type AddressResponse struct {
|
||||
ID string `json:"id"`
|
||||
Province string `json:"province"`
|
||||
District string `json:"district"`
|
||||
Subdistrict string `json:"subdistrict"`
|
||||
PostalCode int `json:"postalCode"`
|
||||
Village string `json:"village"`
|
||||
Detail string `json:"detail"`
|
||||
Geography string `json:"geography"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (c *AddressInput) ValidatePost() error {
|
||||
err := validate.Struct(c)
|
||||
if err != nil {
|
||||
|
||||
for _, e := range err.(validator.ValidationErrors) {
|
||||
|
||||
switch e.Field() {
|
||||
case "Province":
|
||||
return fmt.Errorf("provinsi harus diisisi")
|
||||
case "District":
|
||||
return fmt.Errorf("kabupaten harus diisi")
|
||||
case "Subdistrict":
|
||||
return fmt.Errorf("kecamatan harus diisi")
|
||||
case "PostalCode":
|
||||
return fmt.Errorf("postal code harus diisi dan berupa angka")
|
||||
case "Village":
|
||||
return fmt.Errorf("desa harus diisi")
|
||||
case "Detail":
|
||||
return fmt.Errorf("detail wajib diisi")
|
||||
case "Geography":
|
||||
return fmt.Errorf("lokasi kordinat harus diisi")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AddressInput) ValidateUpdate() error {
|
||||
err := validate.Struct(c)
|
||||
if err != nil {
|
||||
|
||||
for _, e := range err.(validator.ValidationErrors) {
|
||||
|
||||
switch e.Field() {
|
||||
case "Province":
|
||||
return fmt.Errorf("provinsi harus diisisi")
|
||||
case "District":
|
||||
return fmt.Errorf("kabupaten harus diisi")
|
||||
case "Subdistrict":
|
||||
return fmt.Errorf("kecamatan harus diisi")
|
||||
case "PostalCode":
|
||||
return fmt.Errorf("postal code harus diisi dan berupa angka")
|
||||
case "Village":
|
||||
return fmt.Errorf("desa harus diisi")
|
||||
case "Detail":
|
||||
return fmt.Errorf("detail wajib diisi")
|
||||
case "Geography":
|
||||
return fmt.Errorf("lokasi kordinat harus diisi")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package dto
|
||||
|
||||
import "strings"
|
||||
|
||||
type AddressResponseDTO struct {
|
||||
UserID string `json:"user_id"`
|
||||
ID string `json:"address_id"`
|
||||
Province string `json:"province"`
|
||||
Regency string `json:"regency"`
|
||||
District string `json:"district"`
|
||||
Village string `json:"village"`
|
||||
PostalCode string `json:"postalCode"`
|
||||
Detail string `json:"detail"`
|
||||
Geography string `json:"geography"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type CreateAddressDTO struct {
|
||||
Province string `json:"province_id"`
|
||||
Regency string `json:"regency_id"`
|
||||
District string `json:"district_id"`
|
||||
Village string `json:"village_id"`
|
||||
PostalCode string `json:"postalCode"`
|
||||
Detail string `json:"detail"`
|
||||
Geography string `json:"geography"`
|
||||
}
|
||||
|
||||
func (r *CreateAddressDTO) Validate() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if strings.TrimSpace(r.Province) == "" {
|
||||
errors["province_id"] = append(errors["province_id"], "Province ID is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Regency) == "" {
|
||||
errors["regency_id"] = append(errors["regency_id"], "Regency ID is required")
|
||||
}
|
||||
if strings.TrimSpace(r.District) == "" {
|
||||
errors["district_id"] = append(errors["district_id"], "District ID is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Village) == "" {
|
||||
errors["village_id"] = append(errors["village_id"], "Village ID is required")
|
||||
}
|
||||
if strings.TrimSpace(r.PostalCode) == "" {
|
||||
errors["postalCode"] = append(errors["village_id"], "PostalCode ID is required")
|
||||
} else if len(r.PostalCode) < 5 {
|
||||
errors["postalCode"] = append(errors["postalCode"], "kode pos belum sesuai")
|
||||
}
|
||||
if strings.TrimSpace(r.Detail) == "" {
|
||||
errors["detail"] = append(errors["detail"], "Detail address is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Geography) == "" {
|
||||
errors["geography"] = append(errors["geography"], "Geographic coordinates are required")
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package dto
|
||||
|
||||
type ArticleResponse struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
CoverImage string `json:"coverImage"`
|
||||
Author string `json:"author"`
|
||||
Heading string `json:"heading"`
|
||||
Content string `json:"content"`
|
||||
PublishedAt string `json:"publishedAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type ArticleCreateRequest struct {
|
||||
Title string `json:"title" validate:"required"`
|
||||
CoverImage string `json:"coverImage" validate:"required"`
|
||||
Author string `json:"author" validate:"required"`
|
||||
Heading string `json:"heading" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
||||
type ArticleUpdateRequest struct {
|
||||
Title string `json:"title" validate:"required"`
|
||||
CoverImage string `json:"coverImage" validate:"required"`
|
||||
Author string `json:"author" validate:"required"`
|
||||
Heading string `json:"heading" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
}
|
||||
|
||||
func (p *ArticleCreateRequest) Validate() error {
|
||||
validate := GetValidator()
|
||||
return validate.Struct(p)
|
||||
}
|
||||
|
||||
func (p *ArticleUpdateRequest) Validate() error {
|
||||
validate := GetValidator()
|
||||
return validate.Struct(p)
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package dto
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ArticleResponseDTO struct {
|
||||
ID string `json:"article_id"`
|
||||
Title string `json:"title"`
|
||||
CoverImage string `json:"coverImage"`
|
||||
Author string `json:"author"`
|
||||
Heading string `json:"heading"`
|
||||
Content string `json:"content"`
|
||||
PublishedAt string `json:"publishedAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type RequestArticleDTO struct {
|
||||
Title string `json:"title"`
|
||||
CoverImage string `json:"coverImage"`
|
||||
Author string `json:"author"`
|
||||
Heading string `json:"heading"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
func (r *RequestArticleDTO) Validate() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if strings.TrimSpace(r.Title) == "" {
|
||||
errors["title"] = append(errors["title"], "Title is required")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(r.Author) == "" {
|
||||
errors["author"] = append(errors["author"], "Author is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Heading) == "" {
|
||||
errors["heading"] = append(errors["heading"], "Heading is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Content) == "" {
|
||||
errors["content"] = append(errors["content"], "Content is required")
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
package dto
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LoginDTO struct {
|
||||
RoleID string `json:"roleid"`
|
||||
Identifier string `json:"identifier"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type UserResponseWithToken struct {
|
||||
UserID string `json:"user_id"`
|
||||
RoleName string `json:"role_name"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type RegisterDTO struct {
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
ConfirmPassword string `json:"confirm_password"`
|
||||
RoleID string `json:"roleId,omitempty"`
|
||||
}
|
||||
|
||||
func (l *LoginDTO) Validate() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if strings.TrimSpace(l.RoleID) == "" {
|
||||
errors["roleid"] = append(errors["roleid"], "Role ID is required")
|
||||
}
|
||||
if strings.TrimSpace(l.Identifier) == "" {
|
||||
errors["identifier"] = append(errors["identifier"], "Identifier (username, email, or phone) is required")
|
||||
}
|
||||
if strings.TrimSpace(l.Password) == "" {
|
||||
errors["password"] = append(errors["password"], "Password is required")
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func (r *RegisterDTO) Validate() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
r.validateRequiredFields(errors)
|
||||
|
||||
if r.Phone != "" && !IsValidPhoneNumber(r.Phone) {
|
||||
errors["phone"] = append(errors["phone"], "Invalid phone number format. Use +62 followed by 9-13 digits")
|
||||
}
|
||||
|
||||
if r.Email != "" && !IsValidEmail(r.Email) {
|
||||
errors["email"] = append(errors["email"], "Invalid email format")
|
||||
}
|
||||
|
||||
if r.Password != "" && !IsValidPassword(r.Password) {
|
||||
errors["password"] = append(errors["password"], "Password must be at least 8 characters long and contain at least one number")
|
||||
}
|
||||
|
||||
if r.ConfirmPassword != "" && r.Password != r.ConfirmPassword {
|
||||
errors["confirm_password"] = append(errors["confirm_password"], "Password and confirm password do not match")
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func (r *RegisterDTO) validateRequiredFields(errors map[string][]string) {
|
||||
|
||||
if strings.TrimSpace(r.Username) == "" {
|
||||
errors["username"] = append(errors["username"], "Username is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Name) == "" {
|
||||
errors["name"] = append(errors["name"], "Name is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Phone) == "" {
|
||||
errors["phone"] = append(errors["phone"], "Phone number is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Email) == "" {
|
||||
errors["email"] = append(errors["email"], "Email is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Password) == "" {
|
||||
errors["password"] = append(errors["password"], "Password is required")
|
||||
}
|
||||
if strings.TrimSpace(r.ConfirmPassword) == "" {
|
||||
errors["confirm_password"] = append(errors["confirm_password"], "Confirm password is required")
|
||||
}
|
||||
if strings.TrimSpace(r.RoleID) == "" {
|
||||
errors["roleId"] = append(errors["roleId"], "RoleID is required")
|
||||
}
|
||||
}
|
||||
|
||||
func IsValidPhoneNumber(phone string) bool {
|
||||
|
||||
re := regexp.MustCompile(`^\+62\d{9,13}$`)
|
||||
return re.MatchString(phone)
|
||||
}
|
||||
|
||||
func IsValidEmail(email string) bool {
|
||||
|
||||
re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
||||
return re.MatchString(email)
|
||||
}
|
||||
|
||||
func IsValidPassword(password string) bool {
|
||||
if len(password) < 8 {
|
||||
return false
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`\d`)
|
||||
return re.MatchString(password)
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package dto
|
||||
|
||||
type BannerResponse struct {
|
||||
ID string `json:"id"`
|
||||
BannerName string `json:"bannername"`
|
||||
BannerImage string `json:"bannerimage"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type BannerCreateRequest struct {
|
||||
BannerName string `json:"bannername" validate:"required"`
|
||||
BannerImage string `json:"bannerimage" validate:"required"`
|
||||
}
|
||||
|
||||
type BannerUpdateRequest struct {
|
||||
BannerName *string `json:"bannername,omitempty"`
|
||||
BannerImage *string `json:"bannerimage,omitempty"`
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package dto
|
||||
|
||||
import "strings"
|
||||
|
||||
type ResponseBannerDTO struct {
|
||||
ID string `json:"id"`
|
||||
BannerName string `json:"bannername"`
|
||||
BannerImage string `json:"bannerimage"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type RequestBannerDTO struct {
|
||||
BannerName string `json:"bannername"`
|
||||
BannerImage string `json:"bannerimage"`
|
||||
}
|
||||
|
||||
func (r *RequestBannerDTO) ValidateBannerInput() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if strings.TrimSpace(r.BannerName) == "" {
|
||||
errors["bannername"] = append(errors["bannername"], "nama banner harus diisi")
|
||||
}
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package dto
|
||||
|
||||
type CoverageAreaResponse struct {
|
||||
ID string `json:"id"`
|
||||
Province string `json:"province"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type CoverageAreaWithDistrictsResponse struct {
|
||||
ID string `json:"id"`
|
||||
Province string `json:"province"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CoverageArea []CoverageAreaResponse `json:"coverage_area"`
|
||||
}
|
||||
|
||||
type CoverageAreaDetailWithLocation struct {
|
||||
ID string `json:"id"`
|
||||
Province string `json:"province"`
|
||||
District string `json:"district"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
Subdistrict []SubdistrictResponse `json:"subdistrict"`
|
||||
}
|
||||
|
||||
type SubdistrictResponse struct {
|
||||
ID string `json:"id"`
|
||||
Subdistrict string `json:"subdistrict"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func NewCoverageAreaResponse(id, province, createdAt, updatedAt string) CoverageAreaResponse {
|
||||
return CoverageAreaResponse{
|
||||
ID: id,
|
||||
Province: province,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCoverageAreaWithDistrictsResponse(id, province, createdAt, updatedAt string, coverageArea []CoverageAreaResponse) CoverageAreaWithDistrictsResponse {
|
||||
return CoverageAreaWithDistrictsResponse{
|
||||
ID: id,
|
||||
Province: province,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
CoverageArea: coverageArea,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCoverageAreaDetailWithLocation(id, province, district, createdAt, updatedAt string, subdistricts []SubdistrictResponse) CoverageAreaDetailWithLocation {
|
||||
return CoverageAreaDetailWithLocation{
|
||||
ID: id,
|
||||
Province: province,
|
||||
District: district,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
Subdistrict: subdistricts,
|
||||
}
|
||||
}
|
||||
|
||||
func NewSubdistrictResponse(id, subdistrict, createdAt, updatedAt string) SubdistrictResponse {
|
||||
return SubdistrictResponse{
|
||||
ID: id,
|
||||
Subdistrict: subdistrict,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
type CoverageAreaCreateRequest struct {
|
||||
Province string `json:"province" validate:"required"`
|
||||
}
|
||||
|
||||
func NewCoverageAreaCreateRequest(province string) CoverageAreaCreateRequest {
|
||||
return CoverageAreaCreateRequest{
|
||||
Province: province,
|
||||
}
|
||||
}
|
||||
|
||||
type CoverageDistrictCreateRequest struct {
|
||||
CoverageAreaID string `json:"coverage_area_id" validate:"required"`
|
||||
District string `json:"district" validate:"required"`
|
||||
}
|
||||
|
||||
func NewCoverageDistrictCreateRequest(coverageAreaID, district string) CoverageDistrictCreateRequest {
|
||||
return CoverageDistrictCreateRequest{
|
||||
CoverageAreaID: coverageAreaID,
|
||||
District: district,
|
||||
}
|
||||
}
|
||||
|
||||
type CoverageSubdistrictCreateRequest struct {
|
||||
CoverageAreaID string `json:"coverage_area_id" validate:"required"`
|
||||
CoverageDistrictId string `json:"coverage_district_id" validate:"required"`
|
||||
Subdistrict string `json:"subdistrict" validate:"required"`
|
||||
}
|
||||
|
||||
func NewCoverageSubdistrictCreateRequest(coverageAreaID, coverageDistrictId, subdistrict string) CoverageSubdistrictCreateRequest {
|
||||
return CoverageSubdistrictCreateRequest{
|
||||
CoverageAreaID: coverageAreaID,
|
||||
CoverageDistrictId: coverageDistrictId,
|
||||
Subdistrict: subdistrict,
|
||||
}
|
||||
}
|
||||
|
||||
type CoverageAreaUpdateRequest struct {
|
||||
Province string `json:"province" validate:"required"`
|
||||
}
|
||||
|
||||
type CoverageDistrictUpdateRequest struct {
|
||||
District string `json:"district" validate:"required"`
|
||||
}
|
||||
|
||||
type CoverageSubdistrictUpdateRequest struct {
|
||||
Subdistrict string `json:"subdistrict" validate:"required"`
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package dto
|
||||
|
||||
type PointResponse struct {
|
||||
ID string `json:"id"`
|
||||
CoinName string `json:"coin_name"`
|
||||
ValuePerUnit float64 `json:"value_perunit"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type PointCreateRequest struct {
|
||||
CoinName string `json:"coin_name" validate:"required"`
|
||||
ValuePerUnit float64 `json:"value_perunit" validate:"required,gt=0"`
|
||||
}
|
||||
|
||||
type PointUpdateRequest struct {
|
||||
CoinName string `json:"coin_name" validate:"required"`
|
||||
ValuePerUnit float64 `json:"value_perunit" validate:"required,gt=0"`
|
||||
}
|
||||
|
||||
func (p *PointCreateRequest) Validate() error {
|
||||
validate := GetValidator()
|
||||
return validate.Struct(p)
|
||||
}
|
||||
|
||||
func (p *PointUpdateRequest) Validate() error {
|
||||
validate := GetValidator()
|
||||
return validate.Struct(p)
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package dto
|
||||
|
||||
import "strings"
|
||||
|
||||
type ReponseInitialCointDTO struct {
|
||||
ID string `json:"coin_id"`
|
||||
CoinName string `json:"coin_name"`
|
||||
ValuePerUnit float64 `json:"value_perunit"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type RequestInitialCointDTO struct {
|
||||
CoinName string `json:"coin_name"`
|
||||
ValuePerUnit float64 `json:"value_perunit"`
|
||||
}
|
||||
|
||||
func (r *RequestInitialCointDTO) ValidateCointInput() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if strings.TrimSpace(r.CoinName) == "" {
|
||||
errors["coin_name"] = append(errors["coin_name"], "nama coin harus diisi")
|
||||
}
|
||||
|
||||
if r.ValuePerUnit <= 0 {
|
||||
errors["value_perunit"] = append(errors["value_perunit"], "value per unit harus lebih besar dari 0")
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
package dto
|
||||
|
||||
import "errors"
|
||||
|
||||
type ProductResponseWithSoldDTO struct {
|
||||
ID string `json:"id"`
|
||||
StoreID string `json:"store_id"`
|
||||
ProductTitle string `json:"product_title"`
|
||||
ProductImages []ProductImageDTO `json:"product_images"`
|
||||
TrashDetail TrashDetailResponseDTO `json:"trash_detail"`
|
||||
SalePrice int64 `json:"sale_price"`
|
||||
Quantity int `json:"quantity"`
|
||||
ProductDescribe string `json:"product_describe"`
|
||||
Sold int `json:"sold"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type ProductResponseWithoutSoldDTO struct {
|
||||
ID string `json:"id"`
|
||||
StoreID string `json:"store_id"`
|
||||
ProductTitle string `json:"product_title"`
|
||||
ProductImages []ProductImageDTO `json:"product_images"`
|
||||
TrashDetail TrashDetailResponseDTO `json:"trash_detail"`
|
||||
SalePrice int64 `json:"sale_price"`
|
||||
Quantity int `json:"quantity"`
|
||||
ProductDescribe string `json:"product_describe"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type ProductResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
StoreID string `json:"store_id"`
|
||||
ProductTitle string `json:"product_title"`
|
||||
ProductImages []ProductImageDTO `json:"product_images"`
|
||||
TrashDetail TrashDetailResponseDTO `json:"trash_detail"`
|
||||
SalePrice int64 `json:"sale_price"`
|
||||
Quantity int `json:"quantity"`
|
||||
ProductDescribe string `json:"product_describe"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type ProductImageDTO struct {
|
||||
ImageURL string `json:"image_url"`
|
||||
}
|
||||
|
||||
type TrashDetailResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
Description string `json:"description"`
|
||||
Price int `json:"price"`
|
||||
}
|
||||
|
||||
type CreateProductRequestDTO struct {
|
||||
StoreID string `json:"storeid" validate:"required,uuid"`
|
||||
ProductTitle string `json:"product_title" validate:"required,min=3,max=255"`
|
||||
ProductImages []string `json:"product_images" validate:"required,min=1,dive,url"`
|
||||
TrashDetailID string `json:"trash_detail_id" validate:"required,uuid"`
|
||||
SalePrice int64 `json:"sale_price" validate:"required,gt=0"`
|
||||
Quantity int `json:"quantity" validate:"required,gt=0"`
|
||||
ProductDescribe string `json:"product_describe,omitempty"`
|
||||
}
|
||||
|
||||
type CreateProductResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
StoreID string `json:"store_id"`
|
||||
ProductTitle string `json:"product_title"`
|
||||
ProductImages []string `json:"product_images"`
|
||||
TrashDetail TrashDetailResponseDTO `json:"trash_detail"`
|
||||
SalePrice int64 `json:"sale_price"`
|
||||
Quantity int `json:"quantity"`
|
||||
ProductDescribe string `json:"product_describe,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type UpdateProductRequestDTO struct {
|
||||
ProductTitle string `json:"product_title" validate:"required,min=3,max=255"`
|
||||
ProductImages []string `json:"product_images" validate:"required,min=1,dive,url"`
|
||||
TrashDetailID string `json:"trash_detail_id" validate:"required,uuid"`
|
||||
SalePrice int64 `json:"sale_price" validate:"required,gt=0"`
|
||||
Quantity int `json:"quantity" validate:"required,gt=0"`
|
||||
ProductDescribe string `json:"product_describe,omitempty"`
|
||||
}
|
||||
|
||||
func ValidateSalePrice(marketPrice, salePrice int64) error {
|
||||
|
||||
if salePrice > marketPrice*2 {
|
||||
return errors.New("sale price cannot be more than twice the market price")
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package dto
|
||||
|
||||
type RequestPickupRequest struct {
|
||||
RequestItems []RequestItemDTO `json:"request_items"`
|
||||
RequestTime string `json:"requestTime"`
|
||||
UserAddressID string `json:"userAddressId"`
|
||||
}
|
||||
|
||||
type RequestPickupResponse struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"userId"`
|
||||
Request []RequestItemDTO `json:"request"`
|
||||
RequestTime string `json:"requestTimePickup"`
|
||||
UserAddress UserAddressDTO `json:"userAddress"`
|
||||
StatusRequest string `json:"status"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type RequestItemDTO struct {
|
||||
TrashCategory string `json:"trashCategory"`
|
||||
EstimatedAmount string `json:"estimatedAmount"`
|
||||
}
|
||||
|
||||
type UserAddressDTO struct {
|
||||
Province string `json:"province"`
|
||||
District string `json:"district"`
|
||||
Subdistrict string `json:"subdistrict"`
|
||||
PostalCode int `json:"postalCode"`
|
||||
Village string `json:"village"`
|
||||
Detail string `json:"detail"`
|
||||
Geography string `json:"geography"`
|
||||
}
|
||||
|
||||
func NewRequestPickupResponse(id, userID, requestTime, statusRequest string, request []RequestItemDTO, userAddress UserAddressDTO, createdAt, updatedAt string) RequestPickupResponse {
|
||||
return RequestPickupResponse{
|
||||
ID: id,
|
||||
UserID: userID,
|
||||
Request: request,
|
||||
RequestTime: requestTime,
|
||||
UserAddress: userAddress,
|
||||
StatusRequest: statusRequest,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package dto
|
||||
|
||||
type RoleResponseDTO struct {
|
||||
ID string `json:"role_id"`
|
||||
RoleName string `json:"role_name"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
14
dto/store.go
14
dto/store.go
|
@ -1,14 +0,0 @@
|
|||
package dto
|
||||
|
||||
type StoreResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
StoreName string `json:"store_name"`
|
||||
StoreLogo string `json:"store_logo"`
|
||||
StoreBanner string `json:"store_banner"`
|
||||
StoreDesc string `json:"store_desc"`
|
||||
Follower int `json:"follower"`
|
||||
StoreRating float64 `json:"store_rating"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package dto
|
||||
|
||||
import "strings"
|
||||
|
||||
type RequestTrashCategoryDTO struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type ResponseTrashCategoryDTO struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
Details []ResponseTrashDetailDTO `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
type ResponseTrashDetailDTO struct {
|
||||
ID string `json:"id"`
|
||||
CategoryID string `json:"category_id"`
|
||||
Description string `json:"description"`
|
||||
Price float64 `json:"price"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type RequestTrashDetailDTO struct {
|
||||
CategoryID string `json:"category_id"`
|
||||
Description string `json:"description"`
|
||||
Price float64 `json:"price"`
|
||||
}
|
||||
|
||||
func (r *RequestTrashCategoryDTO) ValidateTrashCategoryInput() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if strings.TrimSpace(r.Name) == "" {
|
||||
errors["name"] = append(errors["name"], "name is required")
|
||||
}
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func (r *RequestTrashDetailDTO) ValidateTrashDetailInput() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if strings.TrimSpace(r.Description) == "" {
|
||||
errors["description"] = append(errors["description"], "description is required")
|
||||
}
|
||||
if r.Price <= 0 {
|
||||
errors["price"] = append(errors["price"], "price must be greater than 0")
|
||||
}
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package dto
|
||||
|
||||
import "github.com/go-playground/validator/v10"
|
||||
|
||||
type TrashCategoryDTO struct {
|
||||
Name string `json:"name" validate:"required,min=3,max=100"`
|
||||
}
|
||||
|
||||
func (t *TrashCategoryDTO) Validate() error {
|
||||
validate := validator.New()
|
||||
return validate.Struct(t)
|
||||
}
|
||||
|
||||
type TrashDetailDTO struct {
|
||||
CategoryID string `json:"category_id" validate:"required,uuid4"`
|
||||
Description string `json:"description" validate:"required,min=3,max=255"`
|
||||
Price int `json:"price" validate:"required,min=0"`
|
||||
}
|
||||
|
||||
func (t *TrashDetailDTO) Validate() error {
|
||||
validate := validator.New()
|
||||
return validate.Struct(t)
|
||||
}
|
||||
|
||||
type TrashCategoryResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func NewTrashCategoryResponse(id, name, createdAt, updatedAt string) TrashCategoryResponse {
|
||||
return TrashCategoryResponse{
|
||||
ID: id,
|
||||
Name: name,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
type TrashDetailResponse struct {
|
||||
ID string `json:"id"`
|
||||
Description string `json:"description"`
|
||||
Price int `json:"price"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func NewTrashDetailResponse(id, description string, price int, createdAt, updatedAt string) TrashDetailResponse {
|
||||
return TrashDetailResponse{
|
||||
ID: id,
|
||||
Description: description,
|
||||
Price: price,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
type UpdateTrashCategoryDTO struct {
|
||||
Name string `json:"name" validate:"required,min=3,max=100"`
|
||||
}
|
||||
|
||||
func (t *UpdateTrashCategoryDTO) Validate() error {
|
||||
validate := validator.New()
|
||||
return validate.Struct(t)
|
||||
}
|
||||
|
||||
type UpdateTrashDetailDTO struct {
|
||||
Description string `json:"description" validate:"required,min=3,max=255"`
|
||||
Price int `json:"price" validate:"required,min=0"`
|
||||
}
|
||||
|
||||
func (t *UpdateTrashDetailDTO) Validate() error {
|
||||
validate := validator.New()
|
||||
return validate.Struct(t)
|
||||
}
|
152
dto/user.go
152
dto/user.go
|
@ -1,152 +0,0 @@
|
|||
package dto
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type UserResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
RoleId string `json:"roleId"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func ValidateEmail(email string) error {
|
||||
if email == "" {
|
||||
return errors.New("email harus diisi")
|
||||
}
|
||||
|
||||
emailRegex := `^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$`
|
||||
re := regexp.MustCompile(emailRegex)
|
||||
if !re.MatchString(email) {
|
||||
return errors.New("format email belum sesuai")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidatePhone(phone string) error {
|
||||
if phone == "" {
|
||||
return errors.New("nomor telepon harus diisi")
|
||||
}
|
||||
|
||||
phoneRegex := `^\+?[0-9]{10,15}$`
|
||||
re := regexp.MustCompile(phoneRegex)
|
||||
if !re.MatchString(phone) {
|
||||
return errors.New("nomor telepon tidak valid")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidatePassword(password string) error {
|
||||
if password == "" {
|
||||
return errors.New("password harus diisi")
|
||||
}
|
||||
|
||||
if len(password) < 8 {
|
||||
return errors.New("password minimal 8 karakter")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RegisterUserInput struct {
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
Password string `json:"password"`
|
||||
ConfirmPassword string `json:"confirm_password"`
|
||||
RoleId string `json:"roleId"`
|
||||
}
|
||||
|
||||
func (input *RegisterUserInput) Validate() error {
|
||||
|
||||
if input.Username == "" {
|
||||
return errors.New("username harus diisi")
|
||||
}
|
||||
|
||||
if input.Name == "" {
|
||||
return errors.New("nama harus diisi")
|
||||
}
|
||||
|
||||
if err := ValidateEmail(input.Email); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ValidatePhone(input.Phone); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ValidatePassword(input.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if input.Password != input.ConfirmPassword {
|
||||
return errors.New("password dan confirm password tidak cocok")
|
||||
}
|
||||
|
||||
if input.RoleId == "" {
|
||||
return errors.New("roleId harus diisi")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdatePasswordInput struct {
|
||||
OldPassword string `json:"old_password"`
|
||||
NewPassword string `json:"new_password"`
|
||||
}
|
||||
|
||||
func (input *UpdatePasswordInput) Validate() error {
|
||||
|
||||
if input.OldPassword == "" {
|
||||
return errors.New("old password must be provided")
|
||||
}
|
||||
|
||||
if input.NewPassword == "" {
|
||||
return errors.New("new password must be provided")
|
||||
}
|
||||
|
||||
if len(input.NewPassword) < 8 {
|
||||
return errors.New("new password must be at least 8 characters long")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateUserInput struct {
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
func (input *UpdateUserInput) Validate() error {
|
||||
|
||||
if input.Email != "" {
|
||||
if err := ValidateEmail(input.Email); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if input.Username == "" {
|
||||
return errors.New("username harus diisi")
|
||||
}
|
||||
|
||||
if input.Name == "" {
|
||||
return errors.New("name harus diisi")
|
||||
}
|
||||
|
||||
if input.Phone != "" {
|
||||
if err := ValidatePhone(input.Phone); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package dto
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UserResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Avatar *string `json:"photoprofile,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"emailVerified"`
|
||||
RoleName string `json:"role"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type UpdateUserDTO struct {
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
func (r *UpdateUserDTO) Validate() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if strings.TrimSpace(r.Name) == "" {
|
||||
errors["name"] = append(errors["name"], "Name is required")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(r.Phone) == "" {
|
||||
errors["phone"] = append(errors["phone"], "Phone number is required")
|
||||
} else if !IsValidPhoneNumber(r.Phone) {
|
||||
errors["phone"] = append(errors["phone"], "Invalid phone number format. Use +62 followed by 9-13 digits")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(r.Email) == "" {
|
||||
errors["email"] = append(errors["email"], "Email is required")
|
||||
} else if !IsValidEmail(r.Email) {
|
||||
errors["email"] = append(errors["email"], "Invalid email format")
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func IsUpdateValidPhoneNumber(phone string) bool {
|
||||
|
||||
re := regexp.MustCompile(`^\+62\d{9,13}$`)
|
||||
return re.MatchString(phone)
|
||||
}
|
||||
|
||||
func IsUPdateValidEmail(email string) bool {
|
||||
|
||||
re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
||||
return re.MatchString(email)
|
||||
}
|
||||
|
||||
type UpdatePasswordDTO struct {
|
||||
OldPassword string `json:"old_password"`
|
||||
NewPassword string `json:"new_password"`
|
||||
ConfirmNewPassword string `json:"confirm_new_password"`
|
||||
}
|
||||
|
||||
func (u *UpdatePasswordDTO) Validate() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if u.OldPassword == "" {
|
||||
errors["old_password"] = append(errors["old_password"], "Old password is required")
|
||||
}
|
||||
|
||||
if u.NewPassword == "" {
|
||||
errors["new_password"] = append(errors["new_password"], "New password is required")
|
||||
} else if len(u.NewPassword) < 8 {
|
||||
errors["new_password"] = append(errors["new_password"], "Password must be at least 8 characters long")
|
||||
}
|
||||
|
||||
if u.ConfirmNewPassword == "" {
|
||||
errors["confirm_new_password"] = append(errors["confirm_new_password"], "Confirm new password is required")
|
||||
} else if u.NewPassword != u.ConfirmNewPassword {
|
||||
errors["confirm_new_password"] = append(errors["confirm_new_password"], "Passwords do not match")
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package dto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type PinResponse struct {
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
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,66 @@
|
|||
package dto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type RequestUserPinDTO struct {
|
||||
Pin string `json:"userpin"`
|
||||
}
|
||||
|
||||
func (r *RequestUserPinDTO) Validate() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if strings.TrimSpace(r.Pin) == "" {
|
||||
errors["pin"] = append(errors["pin"], "Pin is required")
|
||||
}
|
||||
|
||||
if err := validatePin(r.Pin); err != nil {
|
||||
errors["pin"] = append(errors["pin"], err.Error())
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
||||
|
||||
type UpdateUserPinDTO struct {
|
||||
OldPin string `json:"old_pin"`
|
||||
NewPin string `json:"new_pin"`
|
||||
}
|
||||
|
||||
func (u *UpdateUserPinDTO) Validate() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if strings.TrimSpace(u.OldPin) == "" {
|
||||
errors["old_pin"] = append(errors["old_pin"], "Old pin is required")
|
||||
}
|
||||
|
||||
if err := validatePin(u.NewPin); err != nil {
|
||||
errors["new_pin"] = append(errors["new_pin"], err.Error())
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func isNumeric(s string) bool {
|
||||
re := regexp.MustCompile(`^[0-9]+$`)
|
||||
return re.MatchString(s)
|
||||
}
|
||||
|
||||
func validatePin(pin string) error {
|
||||
if len(pin) != 6 {
|
||||
return fmt.Errorf("pin harus terdiri dari 6 digit")
|
||||
} else if !isNumeric(pin) {
|
||||
return fmt.Errorf("pin harus berupa angka")
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package dto
|
||||
|
||||
import "github.com/go-playground/validator/v10"
|
||||
|
||||
var validate = validator.New()
|
||||
|
||||
func GetValidator() *validator.Validate {
|
||||
return validate
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package dto
|
||||
|
||||
type ProvinceResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Regencies []RegencyResponseDTO `json:"regencies,omitempty"`
|
||||
}
|
||||
|
||||
type RegencyResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
ProvinceID string `json:"province_id"`
|
||||
Name string `json:"name"`
|
||||
Districts []DistrictResponseDTO `json:"districts,omitempty"`
|
||||
}
|
||||
|
||||
type DistrictResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
RegencyID string `json:"regency_id"`
|
||||
Name string `json:"name"`
|
||||
Villages []VillageResponseDTO `json:"villages,omitempty"`
|
||||
}
|
||||
|
||||
type VillageResponseDTO struct {
|
||||
ID string `json:"id"`
|
||||
DistrictID string `json:"district_id"`
|
||||
Name string `json:"name"`
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
package api
|
||||
|
||||
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)
|
||||
|
||||
bannerRepo := repositories.NewBannerRepository()
|
||||
bannerService := services.NewBannerService(bannerRepo)
|
||||
bannerController := controllers.NewBannerController(bannerService)
|
||||
|
||||
articleRepo := repositories.NewArticleRepository()
|
||||
articleService := services.NewArticleService(articleRepo)
|
||||
articleController := controllers.NewArticleController(articleService)
|
||||
|
||||
// # 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", 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)
|
||||
api.Get("/coverage-areas-district/:id", controllers.GetCoverageAreaByIDProvince)
|
||||
api.Get("/coverage-areas-subdistrict/:id", controllers.GetCoverageAreaByIDDistrict)
|
||||
api.Post("/coverage-areas", middleware.RoleRequired(utils.RoleAdministrator), controllers.CreateCoverageArea)
|
||||
api.Post("/coverage-areas-district", middleware.RoleRequired(utils.RoleAdministrator), controllers.CreateCoverageDistrict)
|
||||
api.Post("/coverage-areas-subdistrict", middleware.RoleRequired(utils.RoleAdministrator), controllers.CreateCoverageSubdistrict)
|
||||
api.Put("/coverage-areas/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.UpdateCoverageArea)
|
||||
api.Put("/coverage-areas-district/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.UpdateCoverageDistrict)
|
||||
api.Put("/coverage-areas-subdistrict/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.UpdateCoverageSubdistrict)
|
||||
api.Delete("/coverage-areas/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.DeleteCoverageArea)
|
||||
api.Delete("/coverage-areas-district/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.DeleteCoverageDistrict)
|
||||
api.Delete("/coverage-areas-subdistrict/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.DeleteCoverageSubdistrict)
|
||||
|
||||
// # role #
|
||||
api.Get("/roles", middleware.RoleRequired(utils.RoleAdministrator), controllers.GetAllUserRoles)
|
||||
api.Get("/role/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.GetUserRoleByID)
|
||||
|
||||
// # authentication #
|
||||
api.Post("/register", controllers.Register)
|
||||
api.Post("/login", controllers.Login)
|
||||
api.Post("/logout", controllers.Logout)
|
||||
|
||||
// # userinfo #
|
||||
api.Get("/user", middleware.AuthMiddleware, controllers.GetUserInfo)
|
||||
api.Post("/user/update-password", middleware.AuthMiddleware, controllers.UpdatePassword)
|
||||
api.Put("/user/update-user", middleware.AuthMiddleware, controllers.UpdateUser)
|
||||
|
||||
// # view all user (admin)
|
||||
api.Get("/user/listallusers", middleware.RoleRequired(utils.RoleAdministrator), controllers.GetListUsers)
|
||||
api.Get("/user/listalluser/:roleid", middleware.RoleRequired(utils.RoleAdministrator), controllers.GetUsersByRole)
|
||||
api.Get("/user/listuser/:userid", middleware.RoleRequired(utils.RoleAdministrator), controllers.GetUserByUserID)
|
||||
|
||||
// # user set pin #
|
||||
api.Get("/user/verif-pin", middleware.AuthMiddleware, controllers.GetPin)
|
||||
api.Get("/user/cek-pin-status", middleware.AuthMiddleware, controllers.GetPinStatus)
|
||||
api.Post("/user/set-pin", middleware.AuthMiddleware, controllers.CreatePin)
|
||||
api.Put("/user/update-pin", middleware.AuthMiddleware, controllers.UpdatePin)
|
||||
api.Put("/user/update-pin", middleware.AuthMiddleware, controllers.UpdatePin)
|
||||
|
||||
// # address routing #
|
||||
api.Get("/addresses", middleware.AuthMiddleware, controllers.GetListAddress)
|
||||
api.Get("/address/:id", middleware.AuthMiddleware, controllers.GetAddressByID)
|
||||
api.Post("/address/create-address", middleware.AuthMiddleware, controllers.CreateAddress)
|
||||
api.Put("/address/update-address/:id", middleware.AuthMiddleware, controllers.UpdateAddress)
|
||||
api.Delete("/address/delete-address/:id", middleware.AuthMiddleware, controllers.DeleteAddress)
|
||||
|
||||
// # article #
|
||||
api.Get("/articles", articleController.GetAllArticles)
|
||||
api.Get("/article/:id", articleController.GetArticleByID)
|
||||
api.Post("/article/create-article", middleware.RoleRequired(utils.RoleAdministrator), articleController.CreateArticle)
|
||||
api.Put("/article/update-article/:id", middleware.RoleRequired(utils.RoleAdministrator), articleController.UpdateArticle)
|
||||
api.Delete("/article/delete-article/:id", middleware.RoleRequired(utils.RoleAdministrator), articleController.DeleteArticle)
|
||||
|
||||
// # trash type #
|
||||
api.Get("/trash-categorys", controllers.GetTrashCategories)
|
||||
api.Get("/trash-category/:id", controllers.GetTrashCategoryDetail)
|
||||
api.Post("/trash-category/create-trash-category", middleware.RoleRequired(utils.RoleAdministrator), controllers.CreateTrashCategory)
|
||||
api.Post("/trash-category/create-trash-categorydetail", middleware.RoleRequired(utils.RoleAdministrator), controllers.CreateTrashDetail)
|
||||
api.Put("/trash-category/update-trash-category/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.UpdateTrashCategory)
|
||||
api.Put("/trash-category/update-trash-detail/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.UpdateTrashDetail)
|
||||
api.Delete("/trash-category/delete-trash-category/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.DeleteTrashCategory)
|
||||
api.Delete("/trash-category/delete-trash-detail/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.DeleteTrashDetail)
|
||||
|
||||
// # banner #
|
||||
api.Get("/banners", bannerController.GetAllBanners)
|
||||
api.Get("/banner/:id", bannerController.GetBannerByID)
|
||||
api.Post("/banner/create-banner", middleware.RoleRequired(utils.RoleAdministrator), bannerController.CreateBanner)
|
||||
api.Put("/banner/update-banner/:id", middleware.RoleRequired(utils.RoleAdministrator), bannerController.UpdateBanner)
|
||||
api.Delete("/banner/delete-banner/:id", middleware.RoleRequired(utils.RoleAdministrator), bannerController.DeleteBanner)
|
||||
|
||||
// # wilayah di indonesia #
|
||||
api.Get("/wilayah-indonesia/provinces", controllers.GetProvinces)
|
||||
api.Get("/wilayah-indonesia/regencies", controllers.GetRegencies)
|
||||
api.Get("/wilayah-indonesia/subdistricts", controllers.GetDistricts)
|
||||
api.Get("/wilayah-indonesia/villages", controllers.GetVillages)
|
||||
api.Get("/wilayah-indonesia/provinces/:id", controllers.GetProvinceByID)
|
||||
api.Get("/wilayah-indonesia/regencies/:id", controllers.GetRegencyByID)
|
||||
api.Get("/wilayah-indonesia/subdistricts/:id", controllers.GetDistrictByID)
|
||||
api.Get("/wilayah-indonesia/villages/:id", controllers.GetVillageByID)
|
||||
|
||||
// # request pickup by user (masyarakat) #
|
||||
api.Get("/requestpickup", middleware.RoleRequired(utils.RoleMasyarakat, utils.RolePengepul), controllers.GetRequestPickupsByUser)
|
||||
api.Post("/addrequestpickup", middleware.RoleRequired(utils.RoleMasyarakat), controllers.CreateRequestPickup)
|
||||
api.Delete("/deleterequestpickup/:id", middleware.RoleRequired(utils.RoleMasyarakat), controllers.DeleteRequestPickup)
|
||||
|
||||
// # product post by pengepul
|
||||
api.Get("/post/products", middleware.RoleRequired(utils.RolePengepul), controllers.GetAllProducts)
|
||||
api.Get("/post/product/:productid", middleware.RoleRequired(utils.RolePengepul), controllers.GetProductByID)
|
||||
api.Get("/view/product/:storeid", middleware.RoleRequired(utils.RolePengepul), controllers.GetProductsByStore)
|
||||
api.Post("/post/addproduct", middleware.RoleRequired(utils.RolePengepul), controllers.CreateProduct)
|
||||
api.Put("/post/product/:productid", middleware.RoleRequired(utils.RolePengepul), controllers.UpdateProduct)
|
||||
api.Delete("/delete/product/:productid", middleware.RoleRequired(utils.RolePengepul), controllers.DeleteProduct)
|
||||
|
||||
// # marketplace
|
||||
api.Get("/store/marketplace", middleware.RoleRequired(utils.RolePengepul), controllers.GetStoresByUserID)
|
||||
api.Get("/store/marketplace/:storeid", middleware.RoleRequired(utils.RolePengepul), controllers.GetStoreByID)
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func GetProvinces(c *fiber.Ctx) error {
|
||||
provinces, err := services.GetProvinces()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to retrieve provinces",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Provinces retrieved successfully",
|
||||
provinces,
|
||||
))
|
||||
}
|
||||
|
||||
func GetRegencies(c *fiber.Ctx) error {
|
||||
regencies, err := services.GetRegencies()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to retrieve regencies",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Regencies retrieved successfully",
|
||||
regencies,
|
||||
))
|
||||
}
|
||||
|
||||
func GetDistricts(c *fiber.Ctx) error {
|
||||
districts, err := services.GetDistricts()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to retrieve districts",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Districts retrieved successfully",
|
||||
districts,
|
||||
))
|
||||
}
|
||||
|
||||
func GetVillages(c *fiber.Ctx) error {
|
||||
villages, err := services.GetVillages()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to retrieve villages",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Villages retrieved successfully",
|
||||
villages,
|
||||
))
|
||||
}
|
||||
|
||||
func GetProvinceByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
province, err := services.GetProvinceByID(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to retrieve province",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Province by id retrieved successfully",
|
||||
province,
|
||||
))
|
||||
}
|
||||
|
||||
func GetRegencyByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
regency, err := services.GetRegencyByID(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to retrieve regency",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Regency by id retrieved successfully",
|
||||
regency,
|
||||
))
|
||||
}
|
||||
|
||||
func GetDistrictByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
district, err := services.GetDistrictByID(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to retrieve district",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"District by id retrieved successfully",
|
||||
district,
|
||||
))
|
||||
}
|
||||
|
||||
func GetVillageByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
village, err := services.GetVillageByID(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to retrieve village",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Village by id retrieved successfully",
|
||||
village,
|
||||
))
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
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 CreateAddress(c *fiber.Ctx) error {
|
||||
var input dto.AddressInput
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Mohon masukkan alamat dengan benar",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := input.ValidatePost(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID := c.Locals("userID").(string)
|
||||
address, err := services.CreateAddress(userID, input)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to create address",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
createdAtFormatted := utils.FormatDateToIndonesianFormat(address.CreatedAt)
|
||||
updatedAtFormatted := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
|
||||
|
||||
addressResponse := dto.AddressResponse{
|
||||
ID: address.ID,
|
||||
Province: address.Province,
|
||||
District: address.District,
|
||||
Subdistrict: address.Subdistrict,
|
||||
PostalCode: address.PostalCode,
|
||||
Village: address.Village,
|
||||
Detail: address.Detail,
|
||||
Geography: address.Geography,
|
||||
CreatedAt: createdAtFormatted,
|
||||
UpdatedAt: updatedAtFormatted,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Address created successfully",
|
||||
addressResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func GetListAddress(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
addresses, err := services.GetAllAddressesByUserID(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Addresses not found",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var addressResponses []dto.AddressResponse
|
||||
for _, address := range addresses {
|
||||
|
||||
createdAtFormatted := utils.FormatDateToIndonesianFormat(address.CreatedAt)
|
||||
updatedAtFormatted := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
|
||||
|
||||
addressResponse := dto.AddressResponse{
|
||||
ID: address.ID,
|
||||
Province: address.Province,
|
||||
District: address.District,
|
||||
Subdistrict: address.Subdistrict,
|
||||
PostalCode: address.PostalCode,
|
||||
Village: address.Village,
|
||||
Detail: address.Detail,
|
||||
Geography: address.Geography,
|
||||
CreatedAt: createdAtFormatted,
|
||||
UpdatedAt: updatedAtFormatted,
|
||||
}
|
||||
addressResponses = append(addressResponses, addressResponse)
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Addresses fetched successfully",
|
||||
addressResponses,
|
||||
))
|
||||
}
|
||||
|
||||
func GetAddressByID(c *fiber.Ctx) error {
|
||||
addressID := c.Params("id")
|
||||
|
||||
address, err := services.GetAddressByID(addressID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Address not found",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
createdAtFormatted := utils.FormatDateToIndonesianFormat(address.CreatedAt)
|
||||
updatedAtFormatted := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
|
||||
|
||||
addressResponse := dto.AddressResponse{
|
||||
ID: address.ID,
|
||||
Province: address.Province,
|
||||
District: address.District,
|
||||
Subdistrict: address.Subdistrict,
|
||||
PostalCode: address.PostalCode,
|
||||
Village: address.Village,
|
||||
Detail: address.Detail,
|
||||
Geography: address.Geography,
|
||||
CreatedAt: createdAtFormatted,
|
||||
UpdatedAt: updatedAtFormatted,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Address fetched successfully",
|
||||
addressResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdateAddress(c *fiber.Ctx) error {
|
||||
addressID := c.Params("id")
|
||||
|
||||
var input dto.AddressInput
|
||||
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := input.ValidateUpdate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
address, err := services.UpdateAddress(addressID, input)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to update address",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
createdAtFormatted := utils.FormatDateToIndonesianFormat(address.CreatedAt)
|
||||
updatedAtFormatted := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
|
||||
|
||||
addressResponse := dto.AddressResponse{
|
||||
ID: address.ID,
|
||||
Province: address.Province,
|
||||
District: address.District,
|
||||
Subdistrict: address.Subdistrict,
|
||||
PostalCode: address.PostalCode,
|
||||
Village: address.Village,
|
||||
Detail: address.Detail,
|
||||
Geography: address.Geography,
|
||||
CreatedAt: createdAtFormatted,
|
||||
UpdatedAt: updatedAtFormatted,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Address updated successfully",
|
||||
addressResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func DeleteAddress(c *fiber.Ctx) error {
|
||||
addressID := c.Params("id")
|
||||
|
||||
err := services.DeleteAddress(addressID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to delete address",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Address deleted successfully",
|
||||
nil,
|
||||
))
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type ArticleController struct {
|
||||
service *services.ArticleService
|
||||
}
|
||||
|
||||
func NewArticleController(service *services.ArticleService) *ArticleController {
|
||||
return &ArticleController{service: service}
|
||||
}
|
||||
|
||||
func (ac *ArticleController) GetAllArticles(c *fiber.Ctx) error {
|
||||
articles, err := ac.service.GetAllArticles()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch articles",
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Articles fetched successfully",
|
||||
articles,
|
||||
))
|
||||
}
|
||||
|
||||
func (ac *ArticleController) GetArticleByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
article, err := ac.service.GetArticleByID(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Article not found",
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Article fetched successfully",
|
||||
article,
|
||||
))
|
||||
}
|
||||
|
||||
func (ac *ArticleController) CreateArticle(c *fiber.Ctx) error {
|
||||
var request dto.ArticleCreateRequest
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request body",
|
||||
))
|
||||
}
|
||||
|
||||
article, err := ac.service.CreateArticle(&request)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse(
|
||||
fiber.StatusCreated,
|
||||
"Article created successfully",
|
||||
article,
|
||||
))
|
||||
}
|
||||
|
||||
func (ac *ArticleController) UpdateArticle(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
var request dto.ArticleUpdateRequest
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request body",
|
||||
))
|
||||
}
|
||||
|
||||
article, err := ac.service.UpdateArticle(id, &request)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Point updated successfully",
|
||||
article,
|
||||
))
|
||||
}
|
||||
|
||||
func (ac *ArticleController) DeleteArticle(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
err := ac.service.DeleteArticle(id)
|
||||
if err != nil {
|
||||
if err.Error() == "article not found" {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Article not found",
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Article deleted successfully",
|
||||
nil,
|
||||
))
|
||||
}
|
|
@ -1,294 +0,0 @@
|
|||
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"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func Register(c *fiber.Ctx) error {
|
||||
var userInput dto.RegisterUserInput
|
||||
|
||||
if err := c.BodyParser(&userInput); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := userInput.Validate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
err := services.RegisterUser(userInput.Username, userInput.Name, userInput.Email, userInput.Phone, userInput.Password, userInput.ConfirmPassword, userInput.RoleId)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusConflict).JSON(utils.FormatResponse(
|
||||
fiber.StatusConflict,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
user, err := repositories.GetUserByEmailUsernameOrPhone(userInput.Email, userInput.RoleId)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch user after registration",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userResponse := dto.UserResponseDTO{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
Phone: user.Phone,
|
||||
RoleId: user.RoleID,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(user.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(user.UpdatedAt),
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"User registered successfully",
|
||||
userResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func Login(c *fiber.Ctx) error {
|
||||
var credentials struct {
|
||||
Identifier string `json:"identifier"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
if err := c.BodyParser(&credentials); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
token, err := services.LoginUser(credentials.Identifier, credentials.Password)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
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(
|
||||
fiber.StatusUnauthorized,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Login successful",
|
||||
map[string]interface{}{
|
||||
"token": token,
|
||||
"role": user.RoleID,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
func GetUserInfo(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
user, err := services.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"user tidak ditemukan",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userResponse := dto.UserResponseDTO{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Phone: user.Phone,
|
||||
Email: user.Email,
|
||||
RoleId: user.RoleID,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(user.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(user.UpdatedAt),
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Data user berhasil ditampilkan",
|
||||
userResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdateUser(c *fiber.Ctx) error {
|
||||
var userInput dto.UpdateUserInput
|
||||
|
||||
if err := c.BodyParser(&userInput); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := userInput.Validate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Unauthorized access",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
err := services.UpdateUser(userID, userInput.Email, userInput.Username, userInput.Name, userInput.Phone)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusConflict).JSON(utils.FormatResponse(
|
||||
fiber.StatusConflict,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
user, err := repositories.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch user after update",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userResponse := dto.UserResponseDTO{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
Phone: user.Phone,
|
||||
RoleId: user.RoleID,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(user.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(user.UpdatedAt),
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"User updated successfully",
|
||||
userResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdatePassword(c *fiber.Ctx) error {
|
||||
var passwordInput dto.UpdatePasswordInput
|
||||
|
||||
if err := c.BodyParser(&passwordInput); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := passwordInput.Validate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
err := services.UpdatePassword(userID, passwordInput.OldPassword, passwordInput.NewPassword)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
user, err := repositories.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch user after password update",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
updatedAtFormatted := utils.FormatDateToIndonesianFormat(user.UpdatedAt)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Password updated successfully",
|
||||
map[string]string{
|
||||
"updatedAt": updatedAtFormatted,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
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,128 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type BannerController struct {
|
||||
service *services.BannerService
|
||||
}
|
||||
|
||||
func NewBannerController(service *services.BannerService) *BannerController {
|
||||
return &BannerController{service: service}
|
||||
}
|
||||
|
||||
func (bc *BannerController) GetAllBanners(c *fiber.Ctx) error {
|
||||
banners, err := bc.service.GetAllBanners()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch banners",
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Banners fetched successfully",
|
||||
banners,
|
||||
))
|
||||
}
|
||||
|
||||
func (bc *BannerController) GetBannerByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
banner, err := bc.service.GetBannerByID(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Banner not found",
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Banner fetched successfully",
|
||||
banner,
|
||||
))
|
||||
}
|
||||
|
||||
func (bc *BannerController) CreateBanner(c *fiber.Ctx) error {
|
||||
var request dto.BannerCreateRequest
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request body",
|
||||
))
|
||||
}
|
||||
|
||||
banner, err := bc.service.CreateBanner(&request)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse(
|
||||
fiber.StatusCreated,
|
||||
"Banner created successfully",
|
||||
banner,
|
||||
))
|
||||
}
|
||||
|
||||
func (bc *BannerController) UpdateBanner(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
var request dto.BannerUpdateRequest
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request body",
|
||||
))
|
||||
}
|
||||
|
||||
banner, err := bc.service.UpdateBanner(id, &request)
|
||||
if err != nil {
|
||||
if err.Error() == "banner not found" {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Banner not found",
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Banner updated successfully",
|
||||
banner,
|
||||
))
|
||||
}
|
||||
|
||||
func (bc *BannerController) DeleteBanner(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
err := bc.service.DeleteBanner(id)
|
||||
if err != nil {
|
||||
if err.Error() == "banner not found" {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Banner not found",
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.ErrorResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Banner deleted successfully",
|
||||
nil,
|
||||
))
|
||||
}
|
|
@ -1,409 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func GetCoverageAreas(c *fiber.Ctx) error {
|
||||
coverageAreas, err := services.GetCoverageAreas()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch coverage areas",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var coverageAreaResponses []dto.CoverageAreaResponse
|
||||
for _, area := range coverageAreas {
|
||||
coverageAreaResponses = append(coverageAreaResponses, dto.NewCoverageAreaResponse(
|
||||
area.ID,
|
||||
area.Province,
|
||||
utils.FormatDateToIndonesianFormat(area.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(area.UpdatedAt),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage areas has been fetched",
|
||||
coverageAreaResponses,
|
||||
))
|
||||
}
|
||||
|
||||
func GetCoverageAreaByIDProvince(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
coverageArea, err := services.GetCoverageAreaByID(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch coverage area details by province",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var coverageAreaResponse dto.CoverageAreaWithDistrictsResponse
|
||||
coverageAreaResponse.ID = coverageArea.ID
|
||||
coverageAreaResponse.Province = coverageArea.Province
|
||||
coverageAreaResponse.CreatedAt = utils.FormatDateToIndonesianFormat(coverageArea.CreatedAt)
|
||||
coverageAreaResponse.UpdatedAt = utils.FormatDateToIndonesianFormat(coverageArea.UpdatedAt)
|
||||
|
||||
districts, err := services.GetCoverageDistricsByCoverageAreaID(coverageArea.ID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch coverage districts",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var coverageAreas []dto.CoverageAreaResponse
|
||||
for _, district := range districts {
|
||||
coverageAreas = append(coverageAreas, dto.NewCoverageAreaResponse(
|
||||
district.ID,
|
||||
district.District,
|
||||
utils.FormatDateToIndonesianFormat(district.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(district.UpdatedAt),
|
||||
))
|
||||
}
|
||||
|
||||
coverageAreaResponse.CoverageArea = coverageAreas
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage areas detail by province has been fetched",
|
||||
coverageAreaResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func GetCoverageAreaByIDDistrict(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
coverageDetail, err := services.GetCoverageAreaByDistrictID(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch coverage area details by district",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageArea, err := services.GetCoverageAreaByID(coverageDetail.CoverageAreaID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch coverage area details by province",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
subdistricts, err := services.GetSubdistrictsByCoverageDistrictID(coverageDetail.ID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch subdistricts",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var subdistrictResponses []dto.SubdistrictResponse
|
||||
for _, loc := range subdistricts {
|
||||
subdistrictResponses = append(subdistrictResponses, dto.NewSubdistrictResponse(
|
||||
loc.ID,
|
||||
loc.Subdistrict,
|
||||
utils.FormatDateToIndonesianFormat(loc.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(loc.UpdatedAt),
|
||||
))
|
||||
}
|
||||
|
||||
coverageAreaResponse := dto.NewCoverageAreaDetailWithLocation(
|
||||
coverageDetail.ID,
|
||||
coverageArea.Province,
|
||||
coverageDetail.District,
|
||||
utils.FormatDateToIndonesianFormat(coverageDetail.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(coverageDetail.UpdatedAt),
|
||||
subdistrictResponses,
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage areas detail by district has been fetched",
|
||||
coverageAreaResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func CreateCoverageArea(c *fiber.Ctx) error {
|
||||
var request dto.CoverageAreaCreateRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request payload",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageArea, err := services.CreateCoverageArea(request.Province)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to create coverage area",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageAreaResponse := dto.NewCoverageAreaResponse(
|
||||
coverageArea.ID,
|
||||
coverageArea.Province,
|
||||
utils.FormatDateToIndonesianFormat(coverageArea.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(coverageArea.UpdatedAt),
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage area has been created",
|
||||
coverageAreaResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func CreateCoverageDistrict(c *fiber.Ctx) error {
|
||||
var request dto.CoverageDistrictCreateRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request payload",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageDistrict, err := services.CreateCoverageDistrict(request.CoverageAreaID, request.District)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to create coverage district",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageDistrictResponse := dto.NewCoverageAreaResponse(
|
||||
coverageDistrict.ID,
|
||||
coverageDistrict.District,
|
||||
utils.FormatDateToIndonesianFormat(coverageDistrict.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(coverageDistrict.UpdatedAt),
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage district has been created",
|
||||
coverageDistrictResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func CreateCoverageSubdistrict(c *fiber.Ctx) error {
|
||||
|
||||
var request dto.CoverageSubdistrictCreateRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request payload",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageSubdistrict, err := services.CreateCoverageSubdistrict(
|
||||
request.CoverageAreaID,
|
||||
request.CoverageDistrictId,
|
||||
request.Subdistrict,
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to create coverage subdistrict",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageSubdistrictResponse := dto.NewCoverageAreaResponse(
|
||||
coverageSubdistrict.ID,
|
||||
coverageSubdistrict.Subdistrict,
|
||||
utils.FormatDateToIndonesianFormat(coverageSubdistrict.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(coverageSubdistrict.UpdatedAt),
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage subdistrict has been created",
|
||||
coverageSubdistrictResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdateCoverageArea(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
var request dto.CoverageAreaUpdateRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request payload",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageArea, err := services.UpdateCoverageArea(id, domain.CoverageArea{
|
||||
Province: request.Province,
|
||||
})
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to update coverage area",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageAreaResponse := dto.NewCoverageAreaResponse(
|
||||
coverageArea.ID,
|
||||
coverageArea.Province,
|
||||
utils.FormatDateToIndonesianFormat(coverageArea.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(coverageArea.UpdatedAt),
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage area has been updated",
|
||||
coverageAreaResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdateCoverageDistrict(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
var request dto.CoverageDistrictUpdateRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request payload",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageDistrict, err := services.UpdateCoverageDistrict(id, domain.CoverageDistric{
|
||||
District: request.District,
|
||||
})
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to update coverage district",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageDistrictResponse := dto.NewCoverageAreaResponse(
|
||||
coverageDistrict.ID,
|
||||
coverageDistrict.District,
|
||||
utils.FormatDateToIndonesianFormat(coverageDistrict.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(coverageDistrict.UpdatedAt),
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage district has been updated",
|
||||
coverageDistrictResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdateCoverageSubdistrict(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
var request dto.CoverageSubdistrictUpdateRequest
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request payload",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageSubdistrict, err := services.UpdateCoverageSubdistrict(id, domain.CoverageSubdistrict{
|
||||
Subdistrict: request.Subdistrict,
|
||||
})
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to update coverage subdistrict",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
coverageSubdistrictResponse := dto.NewSubdistrictResponse(
|
||||
coverageSubdistrict.ID,
|
||||
coverageSubdistrict.Subdistrict,
|
||||
utils.FormatDateToIndonesianFormat(coverageSubdistrict.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(coverageSubdistrict.UpdatedAt),
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage subdistrict has been updated",
|
||||
coverageSubdistrictResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func DeleteCoverageArea(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
if err := services.DeleteCoverageArea(id); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to delete coverage area",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage area has been deleted",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
func DeleteCoverageDistrict(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
if err := services.DeleteCoverageDistrict(id); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to delete coverage district",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage district has been deleted",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
func DeleteCoverageSubdistrict(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
if err := services.DeleteCoverageSubdistrict(id); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to delete coverage subdistrict",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Coverage subdistrict has been deleted",
|
||||
nil,
|
||||
))
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
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.ErrorResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch points",
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Points fetched successfully",
|
||||
points,
|
||||
))
|
||||
}
|
||||
|
||||
func (pc *PointController) GetPointByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
point, err := pc.service.GetPointByID(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.ErrorResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Point not found",
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Point fetched successfully",
|
||||
point,
|
||||
))
|
||||
}
|
||||
|
||||
func (pc *PointController) CreatePoint(c *fiber.Ctx) error {
|
||||
var request dto.PointCreateRequest
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request body",
|
||||
))
|
||||
}
|
||||
|
||||
point, err := pc.service.CreatePoint(&request)
|
||||
if err != nil {
|
||||
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse(
|
||||
fiber.StatusCreated,
|
||||
"Point created successfully",
|
||||
point,
|
||||
))
|
||||
}
|
||||
|
||||
func (pc *PointController) UpdatePoint(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
var request dto.PointUpdateRequest
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request body",
|
||||
))
|
||||
}
|
||||
|
||||
point, err := pc.service.UpdatePoint(id, &request)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.ErrorResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Point updated successfully",
|
||||
point,
|
||||
))
|
||||
}
|
||||
|
||||
func (pc *PointController) DeletePoint(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
err := pc.service.DeletePoint(id)
|
||||
if err != 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(),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Point deleted successfully",
|
||||
nil,
|
||||
))
|
||||
}
|
|
@ -1,262 +0,0 @@
|
|||
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 GetAllProducts(c *fiber.Ctx) error {
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Unauthorized: user ID is missing",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
storeID := c.Query("storeID", "")
|
||||
|
||||
limit := c.QueryInt("limit", 10)
|
||||
page := c.QueryInt("page", 1)
|
||||
|
||||
if limit <= 0 || page <= 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid pagination parameters",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var products []dto.ProductResponseWithSoldDTO
|
||||
var err error
|
||||
|
||||
if storeID != "" {
|
||||
|
||||
products, err = services.GetProductsByStoreID(storeID, limit, page)
|
||||
} else {
|
||||
|
||||
products, err = services.GetProductsByUserID(userID, limit, page)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch products",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Products fetched successfully",
|
||||
products,
|
||||
))
|
||||
}
|
||||
|
||||
func GetProductByID(c *fiber.Ctx) error {
|
||||
storeID, ok := c.Locals("userID").(string)
|
||||
if !ok || storeID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Unauthorized: store ID is missing",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
productID := c.Params("productid")
|
||||
if productID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Product ID is required",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
product, err := services.GetProductByIDAndStoreID(productID, storeID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Product not found",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Product fetched successfully",
|
||||
product,
|
||||
))
|
||||
}
|
||||
|
||||
func GetProductsByStore(c *fiber.Ctx) error {
|
||||
storeID := c.Params("storeid")
|
||||
if storeID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Store ID is required",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
limit := c.QueryInt("limit", 10)
|
||||
page := c.QueryInt("page", 1)
|
||||
|
||||
if limit <= 0 || page <= 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid pagination parameters",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
products, err := services.GetProductsByStoreID(storeID, limit, page)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Store not found",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Products fetched successfully",
|
||||
products,
|
||||
))
|
||||
}
|
||||
|
||||
func CreateProduct(c *fiber.Ctx) error {
|
||||
var input dto.CreateProductRequestDTO
|
||||
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request payload",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Unauthorized: user ID is missing",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if input.StoreID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Store ID is required in the body",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
product, err := services.CreateProduct(input, userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusUnprocessableEntity).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnprocessableEntity,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse(
|
||||
fiber.StatusCreated,
|
||||
"Product created successfully",
|
||||
product,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdateProduct(c *fiber.Ctx) error {
|
||||
productID := c.Params("productid")
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Unauthorized: user ID is missing",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if productID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Product ID is required",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var input dto.UpdateProductRequestDTO
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request payload",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := dto.GetValidator().Struct(input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid product data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
updatedProduct, err := services.UpdateProduct(productID, input)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusUnprocessableEntity).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnprocessableEntity,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Product updated successfully",
|
||||
updatedProduct,
|
||||
))
|
||||
}
|
||||
|
||||
func DeleteProduct(c *fiber.Ctx) error {
|
||||
productID := c.Params("productid")
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Unauthorized: user ID is missing",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if productID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Product ID is required",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
err := services.DeleteProduct(productID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Product not found or unable to delete",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Product deleted successfully",
|
||||
nil,
|
||||
))
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func GetRequestPickupsByUser(c *fiber.Ctx) error {
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"User not authenticated",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
service := services.NewRequestPickupService(repositories.NewRequestPickupRepository())
|
||||
|
||||
requestPickups, err := service.GetRequestPickupsByUser(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch request pickups",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var requestPickupResponses []dto.RequestPickupResponse
|
||||
for _, requestPickup := range requestPickups {
|
||||
userAddress := dto.UserAddressDTO{
|
||||
Province: requestPickup.UserAddress.Province,
|
||||
District: requestPickup.UserAddress.District,
|
||||
Subdistrict: requestPickup.UserAddress.Subdistrict,
|
||||
PostalCode: requestPickup.UserAddress.PostalCode,
|
||||
Village: requestPickup.UserAddress.Village,
|
||||
Detail: requestPickup.UserAddress.Detail,
|
||||
Geography: requestPickup.UserAddress.Geography,
|
||||
}
|
||||
|
||||
var requestItems []dto.RequestItemDTO
|
||||
for _, item := range requestPickup.Request {
|
||||
requestItems = append(requestItems, dto.RequestItemDTO{
|
||||
TrashCategory: item.TrashCategory.Name,
|
||||
EstimatedAmount: item.EstimatedAmount,
|
||||
})
|
||||
}
|
||||
|
||||
requestPickupResponses = append(requestPickupResponses, dto.NewRequestPickupResponse(
|
||||
requestPickup.ID,
|
||||
requestPickup.UserID,
|
||||
requestPickup.RequestTime,
|
||||
requestPickup.StatusRequest,
|
||||
requestItems,
|
||||
userAddress,
|
||||
utils.FormatDateToIndonesianFormat(requestPickup.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(requestPickup.UpdatedAt),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Request pickup by user has been fetched",
|
||||
requestPickupResponses,
|
||||
))
|
||||
}
|
||||
|
||||
func CreateRequestPickup(c *fiber.Ctx) error {
|
||||
var req dto.RequestPickupRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid request body",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"User not authenticated",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if req.UserAddressID == "" || len(req.RequestItems) == 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Missing required fields",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var requestItems []domain.RequestItem
|
||||
for _, item := range req.RequestItems {
|
||||
requestItems = append(requestItems, domain.RequestItem{
|
||||
TrashCategoryID: item.TrashCategory,
|
||||
EstimatedAmount: item.EstimatedAmount,
|
||||
})
|
||||
}
|
||||
|
||||
requestPickup := &domain.RequestPickup{
|
||||
UserID: userID,
|
||||
Request: requestItems,
|
||||
RequestTime: req.RequestTime,
|
||||
UserAddressID: req.UserAddressID,
|
||||
StatusRequest: "Pending",
|
||||
}
|
||||
|
||||
service := services.NewRequestPickupService(repositories.NewRequestPickupRepository())
|
||||
|
||||
if err := service.CreateRequestPickup(requestPickup); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to create request pickup",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
detail, err := service.GetRequestPickupByID(requestPickup.ID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch created request pickup",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var requestItemsDTO []dto.RequestItemDTO
|
||||
for _, item := range detail.Request {
|
||||
requestItemsDTO = append(requestItemsDTO, dto.RequestItemDTO{
|
||||
TrashCategory: item.TrashCategory.Name,
|
||||
EstimatedAmount: item.EstimatedAmount,
|
||||
})
|
||||
}
|
||||
|
||||
userAddressDTO := dto.UserAddressDTO{
|
||||
Province: detail.UserAddress.Province,
|
||||
District: detail.UserAddress.District,
|
||||
Subdistrict: detail.UserAddress.Subdistrict,
|
||||
PostalCode: detail.UserAddress.PostalCode,
|
||||
Village: detail.UserAddress.Village,
|
||||
Detail: detail.UserAddress.Detail,
|
||||
Geography: detail.UserAddress.Geography,
|
||||
}
|
||||
|
||||
response := dto.NewRequestPickupResponse(
|
||||
detail.ID,
|
||||
detail.UserID,
|
||||
detail.RequestTime,
|
||||
detail.StatusRequest,
|
||||
requestItemsDTO,
|
||||
userAddressDTO,
|
||||
utils.FormatDateToIndonesianFormat(detail.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(detail.UpdatedAt),
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse(
|
||||
fiber.StatusCreated,
|
||||
"Request pickup created successfully",
|
||||
response,
|
||||
))
|
||||
}
|
||||
|
||||
func DeleteRequestPickup(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
if id == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Missing required ID",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
service := services.NewRequestPickupService(repositories.NewRequestPickupRepository())
|
||||
if err := service.DeleteRequestPickupByID(id); err != nil {
|
||||
if err.Error() == fmt.Sprintf("request pickup with id %s not found", id) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Request pickup not found",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to delete request pickup",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Request pickup deleted successfully",
|
||||
nil,
|
||||
))
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func GetUserRoleByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
role, err := services.GetUserRoleByID(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"UserRole tidak ditemukan",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"UserRole ditemukan",
|
||||
role,
|
||||
))
|
||||
}
|
||||
|
||||
func GetAllUserRoles(c *fiber.Ctx) error {
|
||||
roles, err := services.GetAllUserRoles()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Gagal mengambil data UserRole",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Daftar UserRole",
|
||||
roles,
|
||||
))
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func GetStoreByID(c *fiber.Ctx) error {
|
||||
storeID := c.Params("storeid")
|
||||
if storeID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Store ID is required",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
store, err := services.GetStoreByID(storeID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Store not found",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Store fetched successfully",
|
||||
store,
|
||||
))
|
||||
}
|
||||
|
||||
func GetStoresByUserID(c *fiber.Ctx) error {
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Unauthorized: user ID is missing",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
limit := c.QueryInt("limit", 0)
|
||||
page := c.QueryInt("page", 1)
|
||||
|
||||
if limit < 0 || page <= 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid pagination parameters",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
stores, err := services.GetStoresByUserID(userID, limit, page)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch stores",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Stores fetched successfully",
|
||||
stores,
|
||||
))
|
||||
}
|
|
@ -1,293 +0,0 @@
|
|||
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 GetTrashCategories(c *fiber.Ctx) error {
|
||||
trashCategories, err := services.GetTrashCategories()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch trash categories",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
var response []dto.TrashCategoryResponse
|
||||
for _, category := range trashCategories {
|
||||
|
||||
response = append(response, dto.NewTrashCategoryResponse(
|
||||
category.ID,
|
||||
category.Name,
|
||||
utils.FormatDateToIndonesianFormat(category.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(category.UpdatedAt),
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Trash categories fetched successfully",
|
||||
response,
|
||||
))
|
||||
}
|
||||
|
||||
func GetTrashCategoryDetail(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
trashCategoryDetail, err := services.GetTrashCategoryDetail(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch trash category detail",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
detailResponse := dto.NewTrashCategoryResponse(
|
||||
trashCategoryDetail.ID,
|
||||
trashCategoryDetail.Name,
|
||||
utils.FormatDateToIndonesianFormat(trashCategoryDetail.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(trashCategoryDetail.UpdatedAt),
|
||||
)
|
||||
|
||||
var detailResponseList []dto.TrashDetailResponse
|
||||
if trashCategoryDetail.Details != nil {
|
||||
for _, detail := range trashCategoryDetail.Details {
|
||||
detailResponseList = append(detailResponseList, dto.NewTrashDetailResponse(
|
||||
detail.ID,
|
||||
detail.Description,
|
||||
detail.Price,
|
||||
utils.FormatDateToIndonesianFormat(detail.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(detail.UpdatedAt),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Trash category detail fetched successfully",
|
||||
struct {
|
||||
Category dto.TrashCategoryResponse `json:"category"`
|
||||
Details []dto.TrashDetailResponse `json:"details,omitempty"`
|
||||
}{
|
||||
Category: detailResponse,
|
||||
Details: detailResponseList,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
func CreateTrashCategory(c *fiber.Ctx) error {
|
||||
var categoryInput dto.TrashCategoryDTO
|
||||
|
||||
if err := c.BodyParser(&categoryInput); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := categoryInput.Validate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Validation failed: "+err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
newCategory, err := services.CreateTrashCategory(categoryInput.Name)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to create trash category",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
categoryResponse := map[string]interface{}{
|
||||
"id": newCategory.ID,
|
||||
"name": newCategory.Name,
|
||||
"createdAt": newCategory.CreatedAt,
|
||||
"updatedAt": newCategory.UpdatedAt,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse(
|
||||
fiber.StatusCreated,
|
||||
"Trash category created successfully",
|
||||
categoryResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func CreateTrashDetail(c *fiber.Ctx) error {
|
||||
var detailInput dto.TrashDetailDTO
|
||||
|
||||
if err := c.BodyParser(&detailInput); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := detailInput.Validate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Validation failed: "+err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
newDetail, err := services.CreateTrashDetail(detailInput.CategoryID, detailInput.Description, detailInput.Price)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to create trash detail",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
detailResponse := map[string]interface{}{
|
||||
"id": newDetail.ID,
|
||||
"description": newDetail.Description,
|
||||
"price": newDetail.Price,
|
||||
"createdAt": newDetail.CreatedAt,
|
||||
"updatedAt": newDetail.UpdatedAt,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(utils.FormatResponse(
|
||||
fiber.StatusCreated,
|
||||
"Trash detail created successfully",
|
||||
detailResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdateTrashCategory(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
var categoryInput dto.UpdateTrashCategoryDTO
|
||||
if err := c.BodyParser(&categoryInput); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := categoryInput.Validate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Validation failed: "+err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
updatedCategory, err := services.UpdateTrashCategory(id, categoryInput.Name)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to update trash category",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
response := dto.NewTrashCategoryResponse(
|
||||
updatedCategory.ID,
|
||||
updatedCategory.Name,
|
||||
utils.FormatDateToIndonesianFormat(updatedCategory.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(updatedCategory.UpdatedAt),
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Trash category updated successfully",
|
||||
response,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdateTrashDetail(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
var detailInput dto.UpdateTrashDetailDTO
|
||||
if err := c.BodyParser(&detailInput); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := detailInput.Validate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Validation failed: "+err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
updatedDetail, err := services.UpdateTrashDetail(id, detailInput.Description, detailInput.Price)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to update trash detail",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
response := dto.NewTrashDetailResponse(
|
||||
updatedDetail.ID,
|
||||
updatedDetail.Description,
|
||||
updatedDetail.Price,
|
||||
utils.FormatDateToIndonesianFormat(updatedDetail.CreatedAt),
|
||||
utils.FormatDateToIndonesianFormat(updatedDetail.UpdatedAt),
|
||||
)
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Trash detail updated successfully",
|
||||
response,
|
||||
))
|
||||
}
|
||||
|
||||
func DeleteTrashCategory(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
err := services.DeleteTrashCategory(id)
|
||||
if err != nil {
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to delete trash category",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Trash category deleted successfully",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
func DeleteTrashDetail(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
err := services.DeleteTrashDetail(id)
|
||||
if err != nil {
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to delete trash detail",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Trash detail deleted successfully",
|
||||
nil,
|
||||
))
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
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 GetListUsers(c *fiber.Ctx) error {
|
||||
users, err := services.GetUsers()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch users",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Users fetched successfully",
|
||||
users,
|
||||
))
|
||||
}
|
||||
|
||||
func GetUsersByRole(c *fiber.Ctx) error {
|
||||
roleID := c.Params("roleID")
|
||||
|
||||
users, err := services.GetUsersByRole(roleID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch users by role",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if len(users) == 0 {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"No users found for the specified role",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Users fetched successfully",
|
||||
users,
|
||||
))
|
||||
}
|
||||
|
||||
func GetUserByUserID(c *fiber.Ctx) error {
|
||||
userID := c.Params("userID")
|
||||
|
||||
user, err := services.GetUserByUserID(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"User not found",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"User fetched successfully",
|
||||
struct {
|
||||
User dto.UserResponseDTO `json:"user"`
|
||||
}{
|
||||
User: user,
|
||||
},
|
||||
))
|
||||
}
|
|
@ -1,240 +0,0 @@
|
|||
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"
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
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",
|
||||
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,
|
||||
))
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"PIN created successfully",
|
||||
pinResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func GetPinStatus(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
_, 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,
|
||||
"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,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"PIN sudah dibuat",
|
||||
map[string]interface{}{
|
||||
"createdAt": "PIN ditemukan di Redis",
|
||||
"updatedAt": "PIN ditemukan di Redis",
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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(redisPin, 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 {
|
||||
if err.Error() == "PIN lama salah" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"PIN lama salah",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to update PIN",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
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(
|
||||
fiber.StatusOK,
|
||||
"PIN updated successfully",
|
||||
map[string]interface{}{
|
||||
"id": updatedPin.ID,
|
||||
"updatedAt": formattedUpdatedAt,
|
||||
},
|
||||
))
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type AddressHandler struct {
|
||||
AddressService services.AddressService
|
||||
}
|
||||
|
||||
func NewAddressHandler(addressService services.AddressService) *AddressHandler {
|
||||
return &AddressHandler{AddressService: addressService}
|
||||
}
|
||||
|
||||
func (h *AddressHandler) CreateAddress(c *fiber.Ctx) error {
|
||||
var requestAddressDTO dto.CreateAddressDTO
|
||||
if err := c.BodyParser(&requestAddressDTO); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
errors, valid := requestAddressDTO.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
addressResponse, err := h.AddressService.CreateAddress(c.Locals("userID").(string), requestAddressDTO)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
return utils.CreateResponse(c, addressResponse, "user address created successfully")
|
||||
}
|
||||
|
||||
func (h *AddressHandler) GetAddressByUserID(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
addresses, err := h.AddressService.GetAddressByUserID(userID)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, addresses, "User addresses fetched successfully")
|
||||
}
|
||||
|
||||
func (h *AddressHandler) GetAddressByID(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
addressID := c.Params("address_id")
|
||||
|
||||
address, err := h.AddressService.GetAddressByID(userID, addressID)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, address, "Address fetched successfully")
|
||||
}
|
||||
|
||||
func (h *AddressHandler) UpdateAddress(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
addressID := c.Params("address_id")
|
||||
|
||||
var addressDTO dto.CreateAddressDTO
|
||||
if err := c.BodyParser(&addressDTO); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
errors, valid := addressDTO.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
updatedAddress, err := h.AddressService.UpdateAddress(userID, addressID, addressDTO)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, updatedAddress, "User address updated successfully")
|
||||
}
|
||||
|
||||
func (h *AddressHandler) DeleteAddress(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
addressID := c.Params("address_id")
|
||||
|
||||
err := h.AddressService.DeleteAddress(userID, addressID)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusForbidden, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, nil, "Address deleted successfully")
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type ArticleHandler struct {
|
||||
ArticleService services.ArticleService
|
||||
}
|
||||
|
||||
func NewArticleHandler(articleService services.ArticleService) *ArticleHandler {
|
||||
return &ArticleHandler{ArticleService: articleService}
|
||||
}
|
||||
|
||||
func (h *ArticleHandler) CreateArticle(c *fiber.Ctx) error {
|
||||
var request dto.RequestArticleDTO
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
errors, valid := request.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
coverImage, err := c.FormFile("coverImage")
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Cover image is required")
|
||||
}
|
||||
|
||||
articleResponse, err := h.ArticleService.CreateArticle(request, coverImage)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return utils.CreateResponse(c, articleResponse, "Article created successfully")
|
||||
}
|
||||
|
||||
func (h *ArticleHandler) GetAllArticles(c *fiber.Ctx) error {
|
||||
page, err := strconv.Atoi(c.Query("page", "0"))
|
||||
if err != nil || page < 1 {
|
||||
page = 0
|
||||
}
|
||||
|
||||
limit, err := strconv.Atoi(c.Query("limit", "0"))
|
||||
if err != nil || limit < 1 {
|
||||
limit = 0
|
||||
}
|
||||
|
||||
articles, totalArticles, err := h.ArticleService.GetAllArticles(page, limit)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch articles")
|
||||
}
|
||||
|
||||
fmt.Printf("Total Articles: %d\n", totalArticles)
|
||||
|
||||
if page == 0 && limit == 0 {
|
||||
return utils.NonPaginatedResponse(c, articles, totalArticles, "Articles fetched successfully")
|
||||
}
|
||||
|
||||
return utils.PaginatedResponse(c, articles, page, limit, totalArticles, "Articles fetched successfully")
|
||||
}
|
||||
|
||||
func (h *ArticleHandler) GetArticleByID(c *fiber.Ctx) error {
|
||||
id := c.Params("article_id")
|
||||
if id == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Article ID is required")
|
||||
}
|
||||
|
||||
article, err := h.ArticleService.GetArticleByID(id)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, "Article not found")
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, article, "Article fetched successfully")
|
||||
}
|
||||
|
||||
func (h *ArticleHandler) UpdateArticle(c *fiber.Ctx) error {
|
||||
id := c.Params("article_id")
|
||||
if id == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Article ID is required")
|
||||
}
|
||||
|
||||
var request dto.RequestArticleDTO
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
errors, valid := request.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
var coverImage *multipart.FileHeader
|
||||
coverImage, err := c.FormFile("coverImage")
|
||||
if err != nil && err.Error() != "no such file" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Cover image is required")
|
||||
}
|
||||
|
||||
articleResponse, err := h.ArticleService.UpdateArticle(id, request, coverImage)
|
||||
if err != nil {
|
||||
if err.Error() == fmt.Sprintf("article with ID %s not found", id) {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
|
||||
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, articleResponse, "Article updated successfully")
|
||||
}
|
||||
|
||||
func (h *ArticleHandler) DeleteArticle(c *fiber.Ctx) error {
|
||||
id := c.Params("article_id")
|
||||
if id == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Article ID is required")
|
||||
}
|
||||
|
||||
err := h.ArticleService.DeleteArticle(id)
|
||||
if err != nil {
|
||||
|
||||
if err.Error() == fmt.Sprintf("article with ID %s not found", id) {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, "Article deleted successfully")
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type UserHandler struct {
|
||||
UserService services.UserService
|
||||
}
|
||||
|
||||
func NewUserHandler(userService services.UserService) *UserHandler {
|
||||
return &UserHandler{UserService: userService}
|
||||
}
|
||||
|
||||
func (h *UserHandler) Login(c *fiber.Ctx) error {
|
||||
var loginDTO dto.LoginDTO
|
||||
if err := c.BodyParser(&loginDTO); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
validationErrors, valid := loginDTO.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, validationErrors)
|
||||
}
|
||||
|
||||
user, err := h.UserService.Login(loginDTO)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, user, "Login successful")
|
||||
}
|
||||
|
||||
func (h *UserHandler) Register(c *fiber.Ctx) error {
|
||||
|
||||
var registerDTO dto.RegisterDTO
|
||||
if err := c.BodyParser(®isterDTO); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid request body"}})
|
||||
}
|
||||
|
||||
errors, valid := registerDTO.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
userResponse, err := h.UserService.Register(registerDTO)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusConflict, err.Error())
|
||||
}
|
||||
|
||||
return utils.CreateResponse(c, userResponse, "Registration successful")
|
||||
}
|
||||
|
||||
func (h *UserHandler) Logout(c *fiber.Ctx) error {
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
log.Println("Unauthorized access: User ID not found in session")
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
|
||||
}
|
||||
|
||||
err := utils.DeleteSessionData(userID)
|
||||
if err != nil {
|
||||
return utils.InternalServerErrorResponse(c, "Error logging out")
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, nil, "Logout successful")
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type BannerHandler struct {
|
||||
BannerService services.BannerService
|
||||
}
|
||||
|
||||
func NewBannerHandler(bannerService services.BannerService) *BannerHandler {
|
||||
return &BannerHandler{BannerService: bannerService}
|
||||
}
|
||||
|
||||
func (h *BannerHandler) CreateBanner(c *fiber.Ctx) error {
|
||||
var request dto.RequestBannerDTO
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
errors, valid := request.ValidateBannerInput()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
bannerImage, err := c.FormFile("bannerimage")
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Banner image is required")
|
||||
}
|
||||
|
||||
bannerResponse, err := h.BannerService.CreateBanner(request, bannerImage)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return utils.CreateResponse(c, bannerResponse, "Banner created successfully")
|
||||
}
|
||||
|
||||
func (h *BannerHandler) GetAllBanners(c *fiber.Ctx) error {
|
||||
banners, err := h.BannerService.GetAllBanners()
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch banners")
|
||||
}
|
||||
|
||||
return utils.NonPaginatedResponse(c, banners, len(banners), "Banners fetched successfully")
|
||||
}
|
||||
|
||||
func (h *BannerHandler) GetBannerByID(c *fiber.Ctx) error {
|
||||
id := c.Params("banner_id")
|
||||
if id == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Banner ID is required")
|
||||
}
|
||||
|
||||
banner, err := h.BannerService.GetBannerByID(id)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, "invalid banner id")
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, banner, "Banner fetched successfully")
|
||||
}
|
||||
|
||||
func (h *BannerHandler) UpdateBanner(c *fiber.Ctx) error {
|
||||
id := c.Params("banner_id")
|
||||
if id == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Banner ID is required")
|
||||
}
|
||||
|
||||
var request dto.RequestBannerDTO
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
errors, valid := request.ValidateBannerInput()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
bannerImage, err := c.FormFile("bannerimage")
|
||||
if err != nil && err.Error() != "no such file" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Banner image is required")
|
||||
}
|
||||
|
||||
bannerResponse, err := h.BannerService.UpdateBanner(id, request, bannerImage)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, bannerResponse, "Banner updated successfully")
|
||||
}
|
||||
|
||||
func (h *BannerHandler) DeleteBanner(c *fiber.Ctx) error {
|
||||
id := c.Params("banner_id")
|
||||
if id == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Banner ID is required")
|
||||
}
|
||||
|
||||
err := h.BannerService.DeleteBanner(id)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, "Banner deleted successfully")
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type InitialCointHandler struct {
|
||||
InitialCointService services.InitialCointService
|
||||
}
|
||||
|
||||
func NewInitialCointHandler(initialCointService services.InitialCointService) *InitialCointHandler {
|
||||
return &InitialCointHandler{InitialCointService: initialCointService}
|
||||
}
|
||||
|
||||
func (h *InitialCointHandler) CreateInitialCoint(c *fiber.Ctx) error {
|
||||
var request dto.RequestInitialCointDTO
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
errors, valid := request.ValidateCointInput()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
initialCointResponse, err := h.InitialCointService.CreateInitialCoint(request)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return utils.CreateResponse(c, initialCointResponse, "Initial coint created successfully")
|
||||
}
|
||||
|
||||
func (h *InitialCointHandler) GetAllInitialCoints(c *fiber.Ctx) error {
|
||||
initialCoints, err := h.InitialCointService.GetAllInitialCoints()
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch initial coints")
|
||||
}
|
||||
|
||||
return utils.NonPaginatedResponse(c, initialCoints, len(initialCoints), "Initial coints fetched successfully")
|
||||
}
|
||||
|
||||
func (h *InitialCointHandler) GetInitialCointByID(c *fiber.Ctx) error {
|
||||
id := c.Params("coin_id")
|
||||
if id == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Coin ID is required")
|
||||
}
|
||||
|
||||
initialCoint, err := h.InitialCointService.GetInitialCointByID(id)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, "Invalid coin ID")
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, initialCoint, "Initial coint fetched successfully")
|
||||
}
|
||||
|
||||
func (h *InitialCointHandler) UpdateInitialCoint(c *fiber.Ctx) error {
|
||||
id := c.Params("coin_id")
|
||||
if id == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Coin ID is required")
|
||||
}
|
||||
|
||||
var request dto.RequestInitialCointDTO
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
errors, valid := request.ValidateCointInput()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
initialCointResponse, err := h.InitialCointService.UpdateInitialCoint(id, request)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, initialCointResponse, "Initial coint updated successfully")
|
||||
}
|
||||
|
||||
func (h *InitialCointHandler) DeleteInitialCoint(c *fiber.Ctx) error {
|
||||
id := c.Params("coin_id")
|
||||
if id == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Coin ID is required")
|
||||
}
|
||||
|
||||
err := h.InitialCointService.DeleteInitialCoint(id)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, "Initial coint deleted successfully")
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type RoleHandler struct {
|
||||
RoleService services.RoleService
|
||||
}
|
||||
|
||||
func NewRoleHandler(roleService services.RoleService) *RoleHandler {
|
||||
return &RoleHandler{RoleService: roleService}
|
||||
}
|
||||
|
||||
func (h *RoleHandler) GetRoles(c *fiber.Ctx) error {
|
||||
|
||||
roleID, ok := c.Locals("roleID").(string)
|
||||
if !ok || roleID != utils.RoleAdministrator {
|
||||
return utils.GenericResponse(c, fiber.StatusForbidden, "Forbidden: You don't have permission to access this resource")
|
||||
}
|
||||
|
||||
roles, err := h.RoleService.GetRoles()
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, roles, "Roles fetched successfully")
|
||||
}
|
||||
|
||||
func (h *RoleHandler) GetRoleByID(c *fiber.Ctx) error {
|
||||
roleID := c.Params("role_id")
|
||||
|
||||
roleIDFromSession, ok := c.Locals("roleID").(string)
|
||||
if !ok || roleIDFromSession != utils.RoleAdministrator {
|
||||
return utils.GenericResponse(c, fiber.StatusForbidden, "Forbidden: You don't have permission to access this resource")
|
||||
}
|
||||
|
||||
role, err := h.RoleService.GetRoleByID(roleID)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, "role id tidak ditemukan")
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, role, "Role fetched successfully")
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type TrashHandler struct {
|
||||
TrashService services.TrashService
|
||||
}
|
||||
|
||||
func NewTrashHandler(trashService services.TrashService) *TrashHandler {
|
||||
return &TrashHandler{TrashService: trashService}
|
||||
}
|
||||
|
||||
func (h *TrashHandler) CreateCategory(c *fiber.Ctx) error {
|
||||
var request dto.RequestTrashCategoryDTO
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
categoryResponse, err := h.TrashService.CreateCategory(request)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to create category: "+err.Error())
|
||||
}
|
||||
|
||||
return utils.CreateResponse(c, categoryResponse, "Category created successfully")
|
||||
}
|
||||
|
||||
func (h *TrashHandler) AddDetailToCategory(c *fiber.Ctx) error {
|
||||
var request dto.RequestTrashDetailDTO
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
detailResponse, err := h.TrashService.AddDetailToCategory(request)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to add detail to category: "+err.Error())
|
||||
}
|
||||
|
||||
return utils.CreateResponse(c, detailResponse, "Trash detail added successfully")
|
||||
}
|
||||
|
||||
func (h *TrashHandler) GetCategories(c *fiber.Ctx) error {
|
||||
|
||||
categories, err := h.TrashService.GetCategories()
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch categories: "+err.Error())
|
||||
}
|
||||
|
||||
return utils.NonPaginatedResponse(c, categories, len(categories), "Categories retrieved successfully")
|
||||
}
|
||||
|
||||
func (h *TrashHandler) GetCategoryByID(c *fiber.Ctx) error {
|
||||
id := c.Params("category_id")
|
||||
|
||||
category, err := h.TrashService.GetCategoryByID(id)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, "Category not found: "+err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, category, "Category retrieved successfully")
|
||||
}
|
||||
|
||||
func (h *TrashHandler) GetTrashDetailByID(c *fiber.Ctx) error {
|
||||
id := c.Params("detail_id")
|
||||
|
||||
detail, err := h.TrashService.GetTrashDetailByID(id)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, "Trash detail not found: "+err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, detail, "Trash detail retrieved successfully")
|
||||
}
|
||||
|
||||
func (h *TrashHandler) UpdateCategory(c *fiber.Ctx) error {
|
||||
id := c.Params("category_id")
|
||||
|
||||
var request dto.RequestTrashCategoryDTO
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid request body"}})
|
||||
}
|
||||
|
||||
updatedCategory, err := h.TrashService.UpdateCategory(id, request)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Error updating category: "+err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, updatedCategory, "Category updated successfully")
|
||||
}
|
||||
|
||||
func (h *TrashHandler) UpdateDetail(c *fiber.Ctx) error {
|
||||
id := c.Params("detail_id")
|
||||
|
||||
var request dto.RequestTrashDetailDTO
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid request body"}})
|
||||
}
|
||||
|
||||
updatedDetail, err := h.TrashService.UpdateDetail(id, request)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Error updating detail: "+err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, updatedDetail, "Trash detail updated successfully")
|
||||
}
|
||||
|
||||
func (h *TrashHandler) DeleteCategory(c *fiber.Ctx) error {
|
||||
id := c.Params("category_id")
|
||||
|
||||
if err := h.TrashService.DeleteCategory(id); err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Error deleting category: "+err.Error())
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, "Category deleted successfully")
|
||||
}
|
||||
|
||||
func (h *TrashHandler) DeleteDetail(c *fiber.Ctx) error {
|
||||
id := c.Params("detail_id")
|
||||
|
||||
if err := h.TrashService.DeleteDetail(id); err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Error deleting detail: "+err.Error())
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, "Trash detail deleted successfully")
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type UserProfileHandler struct {
|
||||
UserProfileService services.UserProfileService
|
||||
}
|
||||
|
||||
func NewUserProfileHandler(userProfileService services.UserProfileService) *UserProfileHandler {
|
||||
return &UserProfileHandler{UserProfileService: userProfileService}
|
||||
}
|
||||
|
||||
func (h *UserProfileHandler) GetUserProfile(c *fiber.Ctx) error {
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
|
||||
}
|
||||
|
||||
userProfile, err := h.UserProfileService.GetUserProfile(userID)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, userProfile, "User profile retrieved successfully")
|
||||
}
|
||||
|
||||
func (h *UserProfileHandler) UpdateUserProfile(c *fiber.Ctx) error {
|
||||
var updateData dto.UpdateUserDTO
|
||||
if err := c.BodyParser(&updateData); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
|
||||
}
|
||||
|
||||
errors, valid := updateData.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
userResponse, err := h.UserProfileService.UpdateUserProfile(userID, updateData)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusConflict, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, userResponse, "User profile updated successfully")
|
||||
}
|
||||
|
||||
func (h *UserProfileHandler) UpdateUserPassword(c *fiber.Ctx) error {
|
||||
var passwordData dto.UpdatePasswordDTO
|
||||
if err := c.BodyParser(&passwordData); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
|
||||
}
|
||||
|
||||
errors, valid := passwordData.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
message, err := h.UserProfileService.UpdateUserPassword(userID, passwordData)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, message)
|
||||
}
|
||||
func (h *UserProfileHandler) UpdateUserAvatar(c *fiber.Ctx) error {
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
|
||||
}
|
||||
|
||||
file, err := c.FormFile("avatar")
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "No avatar file uploaded")
|
||||
}
|
||||
|
||||
message, err := h.UserProfileService.UpdateUserAvatar(userID, file)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, message)
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type UserPinHandler struct {
|
||||
UserPinService services.UserPinService
|
||||
}
|
||||
|
||||
func NewUserPinHandler(userPinService services.UserPinService) *UserPinHandler {
|
||||
return &UserPinHandler{UserPinService: userPinService}
|
||||
}
|
||||
|
||||
func (h *UserPinHandler) VerifyUserPin(c *fiber.Ctx) error {
|
||||
var requestUserPinDTO dto.RequestUserPinDTO
|
||||
if err := c.BodyParser(&requestUserPinDTO); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
errors, valid := requestUserPinDTO.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
|
||||
}
|
||||
|
||||
message, err := h.UserPinService.VerifyUserPin(userID, requestUserPinDTO.Pin)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, err.Error())
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, message)
|
||||
}
|
||||
|
||||
func (h *UserPinHandler) CheckPinStatus(c *fiber.Ctx) error {
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
|
||||
}
|
||||
|
||||
status, err := h.UserPinService.CheckPinStatus(userID)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if status == "Pin not created" {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, "Pin belum dibuat")
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, "Pin sudah dibuat")
|
||||
}
|
||||
|
||||
func (h *UserPinHandler) CreateUserPin(c *fiber.Ctx) error {
|
||||
var requestUserPinDTO dto.RequestUserPinDTO
|
||||
if err := c.BodyParser(&requestUserPinDTO); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
errors, valid := requestUserPinDTO.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
message, err := h.UserPinService.CreateUserPin(userID, requestUserPinDTO.Pin)
|
||||
if err != nil {
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusConflict, err.Error())
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusCreated, message)
|
||||
}
|
||||
|
||||
func (h *UserPinHandler) UpdateUserPin(c *fiber.Ctx) error {
|
||||
var requestUserPinDTO dto.UpdateUserPinDTO
|
||||
if err := c.BodyParser(&requestUserPinDTO); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
|
||||
errors, valid := requestUserPinDTO.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
message, err := h.UserPinService.UpdateUserPin(userID, requestUserPinDTO.OldPin, requestUserPinDTO.NewPin)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, message)
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type WilayahIndonesiaHandler struct {
|
||||
WilayahService services.WilayahIndonesiaService
|
||||
}
|
||||
|
||||
func NewWilayahImportHandler(wilayahService services.WilayahIndonesiaService) *WilayahIndonesiaHandler {
|
||||
return &WilayahIndonesiaHandler{WilayahService: wilayahService}
|
||||
}
|
||||
|
||||
func (h *WilayahIndonesiaHandler) ImportWilayahData(c *fiber.Ctx) error {
|
||||
|
||||
err := h.WilayahService.ImportDataFromCSV()
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, fiber.StatusCreated, "Data imported successfully")
|
||||
}
|
||||
|
||||
func (h *WilayahIndonesiaHandler) GetProvinces(c *fiber.Ctx) error {
|
||||
|
||||
page, err := strconv.Atoi(c.Query("page", "0"))
|
||||
if err != nil {
|
||||
page = 0
|
||||
}
|
||||
limit, err := strconv.Atoi(c.Query("limit", "0"))
|
||||
if err != nil {
|
||||
limit = 0
|
||||
}
|
||||
|
||||
provinces, totalProvinces, err := h.WilayahService.GetAllProvinces(page, limit)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch provinces")
|
||||
}
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
return utils.PaginatedResponse(c, provinces, page, limit, totalProvinces, "Provinces fetched successfully")
|
||||
}
|
||||
|
||||
return utils.NonPaginatedResponse(c, provinces, totalProvinces, "Provinces fetched successfully")
|
||||
}
|
||||
|
||||
func (h *WilayahIndonesiaHandler) GetProvinceByID(c *fiber.Ctx) error {
|
||||
provinceID := c.Params("provinceid")
|
||||
|
||||
page, err := strconv.Atoi(c.Query("page", "0"))
|
||||
if err != nil {
|
||||
page = 0
|
||||
}
|
||||
limit, err := strconv.Atoi(c.Query("limit", "0"))
|
||||
if err != nil {
|
||||
limit = 0
|
||||
}
|
||||
|
||||
province, totalRegencies, err := h.WilayahService.GetProvinceByID(provinceID, page, limit)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch province")
|
||||
}
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
return utils.PaginatedResponse(c, province, page, limit, totalRegencies, "Province fetched successfully")
|
||||
}
|
||||
|
||||
return utils.NonPaginatedResponse(c, province, totalRegencies, "Province fetched successfully")
|
||||
}
|
||||
|
||||
func (h *WilayahIndonesiaHandler) GetAllRegencies(c *fiber.Ctx) error {
|
||||
page, err := strconv.Atoi(c.Query("page", "0"))
|
||||
if err != nil {
|
||||
page = 0
|
||||
}
|
||||
limit, err := strconv.Atoi(c.Query("limit", "0"))
|
||||
if err != nil {
|
||||
limit = 0
|
||||
}
|
||||
|
||||
regencies, totalRegencies, err := h.WilayahService.GetAllRegencies(page, limit)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch regency")
|
||||
}
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
return utils.PaginatedResponse(c, regencies, page, limit, totalRegencies, "regency fetched successfully")
|
||||
}
|
||||
|
||||
return utils.NonPaginatedResponse(c, regencies, totalRegencies, "Provinces fetched successfully")
|
||||
}
|
||||
|
||||
func (h *WilayahIndonesiaHandler) GetRegencyByID(c *fiber.Ctx) error {
|
||||
regencyId := c.Params("regencyid")
|
||||
|
||||
page, err := strconv.Atoi(c.Query("page", "0"))
|
||||
if err != nil {
|
||||
page = 0
|
||||
}
|
||||
limit, err := strconv.Atoi(c.Query("limit", "0"))
|
||||
if err != nil {
|
||||
limit = 0
|
||||
}
|
||||
|
||||
regency, totalDistrict, err := h.WilayahService.GetRegencyByID(regencyId, page, limit)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch regency")
|
||||
}
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
return utils.PaginatedResponse(c, regency, page, limit, totalDistrict, "regency fetched successfully")
|
||||
}
|
||||
|
||||
return utils.NonPaginatedResponse(c, regency, totalDistrict, "regency fetched successfully")
|
||||
}
|
||||
|
||||
func (h *WilayahIndonesiaHandler) GetAllDistricts(c *fiber.Ctx) error {
|
||||
page, err := strconv.Atoi(c.Query("page", "0"))
|
||||
if err != nil {
|
||||
page = 0
|
||||
}
|
||||
limit, err := strconv.Atoi(c.Query("limit", "0"))
|
||||
if err != nil {
|
||||
limit = 0
|
||||
}
|
||||
|
||||
districts, totalDistricts, err := h.WilayahService.GetAllDistricts(page, limit)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch districts")
|
||||
}
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
return utils.PaginatedResponse(c, districts, page, limit, totalDistricts, "districts fetched successfully")
|
||||
}
|
||||
|
||||
return utils.NonPaginatedResponse(c, districts, totalDistricts, "districts fetched successfully")
|
||||
}
|
||||
|
||||
func (h *WilayahIndonesiaHandler) GetDistrictByID(c *fiber.Ctx) error {
|
||||
districtId := c.Params("districtid")
|
||||
|
||||
page, err := strconv.Atoi(c.Query("page", "0"))
|
||||
if err != nil {
|
||||
page = 0
|
||||
}
|
||||
limit, err := strconv.Atoi(c.Query("limit", "0"))
|
||||
if err != nil {
|
||||
limit = 0
|
||||
}
|
||||
|
||||
district, totalVillages, err := h.WilayahService.GetDistrictByID(districtId, page, limit)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch district")
|
||||
}
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
return utils.PaginatedResponse(c, district, page, limit, totalVillages, "district fetched successfully")
|
||||
}
|
||||
|
||||
return utils.NonPaginatedResponse(c, district, totalVillages, "district fetched successfully")
|
||||
}
|
||||
|
||||
func (h *WilayahIndonesiaHandler) GetAllVillages(c *fiber.Ctx) error {
|
||||
page, err := strconv.Atoi(c.Query("page", "0"))
|
||||
if err != nil {
|
||||
page = 0
|
||||
}
|
||||
limit, err := strconv.Atoi(c.Query("limit", "0"))
|
||||
if err != nil {
|
||||
limit = 0
|
||||
}
|
||||
|
||||
villages, totalVillages, err := h.WilayahService.GetAllVillages(page, limit)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, "Failed to fetch villages")
|
||||
}
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
return utils.PaginatedResponse(c, villages, page, limit, totalVillages, "villages fetched successfully")
|
||||
}
|
||||
|
||||
return utils.NonPaginatedResponse(c, villages, totalVillages, "villages fetched successfully")
|
||||
}
|
||||
|
||||
func (h *WilayahIndonesiaHandler) GetVillageByID(c *fiber.Ctx) error {
|
||||
id := c.Params("villageid")
|
||||
|
||||
village, err := h.WilayahService.GetVillageByID(id)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, village, "Village fetched successfully")
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
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 {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Invalid API Key",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
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"
|
||||
)
|
||||
|
||||
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 := strings.TrimPrefix(c.Get("Authorization"), "Bearer ")
|
||||
if tokenString == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Token is required",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
return []byte(os.Getenv("API_KEY")), nil
|
||||
})
|
||||
if err != nil || !token.Valid {
|
||||
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(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Invalid token claims",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID, ok := claims["sub"].(string)
|
||||
if !ok || userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Missing or invalid user ID in token",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
role, ok := claims["role"].(string)
|
||||
if !ok || role == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Missing or invalid role in token",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
c.Locals("userID", userID)
|
||||
c.Locals("role", role)
|
||||
|
||||
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 := strings.TrimPrefix(c.Get("Authorization"), "Bearer ")
|
||||
if tokenString == "" {
|
||||
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(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(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
"Invalid or expired token",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
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)
|
||||
|
||||
return c.Next()
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func GetProvinces() ([]domain.Province, error) {
|
||||
records, err := utils.ReadCSV("public/document/provinces.csv")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var provinces []domain.Province
|
||||
for _, record := range records {
|
||||
province := domain.Province{
|
||||
ID: record[0],
|
||||
Name: record[1],
|
||||
}
|
||||
provinces = append(provinces, province)
|
||||
}
|
||||
|
||||
return provinces, nil
|
||||
}
|
||||
|
||||
func GetRegencies() ([]domain.Regency, error) {
|
||||
records, err := utils.ReadCSV("public/document/regencies.csv")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var regencies []domain.Regency
|
||||
for _, record := range records {
|
||||
regency := domain.Regency{
|
||||
ID: record[0],
|
||||
ProvinceID: record[1],
|
||||
Name: record[2],
|
||||
}
|
||||
regencies = append(regencies, regency)
|
||||
}
|
||||
|
||||
return regencies, nil
|
||||
}
|
||||
|
||||
func GetDistricts() ([]domain.District, error) {
|
||||
records, err := utils.ReadCSV("public/document/districts.csv")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var districts []domain.District
|
||||
for _, record := range records {
|
||||
district := domain.District{
|
||||
ID: record[0],
|
||||
RegencyID: record[1],
|
||||
Name: record[2],
|
||||
}
|
||||
districts = append(districts, district)
|
||||
}
|
||||
|
||||
return districts, nil
|
||||
}
|
||||
|
||||
func GetVillages() ([]domain.Village, error) {
|
||||
records, err := utils.ReadCSV("public/document/villages.csv")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var villages []domain.Village
|
||||
for _, record := range records {
|
||||
village := domain.Village{
|
||||
ID: record[0],
|
||||
DistrictID: record[1],
|
||||
Name: record[2],
|
||||
}
|
||||
villages = append(villages, village)
|
||||
}
|
||||
|
||||
return villages, nil
|
||||
}
|
||||
|
||||
func GetProvinceByID(id string) (domain.Province, error) {
|
||||
provinces, err := GetProvinces()
|
||||
if err != nil {
|
||||
return domain.Province{}, err
|
||||
}
|
||||
|
||||
for _, province := range provinces {
|
||||
if province.ID == id {
|
||||
|
||||
regencies, err := GetRegenciesByProvinceID(id)
|
||||
if err != nil {
|
||||
return domain.Province{}, err
|
||||
}
|
||||
|
||||
province.ListRegency = regencies
|
||||
return province, nil
|
||||
}
|
||||
}
|
||||
return domain.Province{}, errors.New("province not found")
|
||||
}
|
||||
|
||||
func GetRegencyByID(id string) (domain.Regency, error) {
|
||||
regencies, err := GetRegencies()
|
||||
if err != nil {
|
||||
return domain.Regency{}, err
|
||||
}
|
||||
|
||||
for _, regency := range regencies {
|
||||
if regency.ID == id {
|
||||
|
||||
districts, err := GetDistrictsByRegencyID(id)
|
||||
if err != nil {
|
||||
return domain.Regency{}, err
|
||||
}
|
||||
|
||||
regency.ListDistrict = districts
|
||||
return regency, nil
|
||||
}
|
||||
}
|
||||
return domain.Regency{}, errors.New("regency not found")
|
||||
}
|
||||
|
||||
func GetDistrictByID(id string) (domain.District, error) {
|
||||
districts, err := GetDistricts()
|
||||
if err != nil {
|
||||
return domain.District{}, err
|
||||
}
|
||||
|
||||
for _, district := range districts {
|
||||
if district.ID == id {
|
||||
|
||||
villages, err := GetVillagesByDistrictID(id)
|
||||
if err != nil {
|
||||
return domain.District{}, err
|
||||
}
|
||||
|
||||
district.ListVillage = villages
|
||||
return district, nil
|
||||
}
|
||||
}
|
||||
return domain.District{}, errors.New("district not found")
|
||||
}
|
||||
|
||||
func GetVillageByID(id string) (domain.Village, error) {
|
||||
villages, err := GetVillages()
|
||||
if err != nil {
|
||||
return domain.Village{}, err
|
||||
}
|
||||
|
||||
for _, village := range villages {
|
||||
if village.ID == id {
|
||||
return village, nil
|
||||
}
|
||||
}
|
||||
return domain.Village{}, errors.New("village not found")
|
||||
}
|
||||
|
||||
func GetRegenciesByProvinceID(provinceID string) ([]domain.Regency, error) {
|
||||
regencies, err := GetRegencies()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []domain.Regency
|
||||
for _, regency := range regencies {
|
||||
if regency.ProvinceID == provinceID {
|
||||
result = append(result, regency)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetDistrictsByRegencyID(regencyID string) ([]domain.District, error) {
|
||||
districts, err := GetDistricts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []domain.District
|
||||
for _, district := range districts {
|
||||
if district.RegencyID == regencyID {
|
||||
result = append(result, district)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetVillagesByDistrictID(districtID string) ([]domain.Village, error) {
|
||||
villages, err := GetVillages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []domain.Village
|
||||
for _, village := range villages {
|
||||
if village.DistrictID == districtID {
|
||||
result = append(result, village)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
func CreateAddress(address *domain.Address) error {
|
||||
result := config.DB.Create(address)
|
||||
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
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addressesJSON, _ := json.Marshal(addresses)
|
||||
config.RedisClient.Set(ctx, cacheKey, addressesJSON, time.Hour).Err()
|
||||
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
func GetAddressByID(addressID string) (domain.Address, error) {
|
||||
var address domain.Address
|
||||
if err := config.DB.Where("id = ?", addressID).First(&address).Error; err != nil {
|
||||
return address, errors.New("address not found")
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
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).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
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type AddressRepository interface {
|
||||
CreateAddress(address *model.Address) error
|
||||
FindAddressByUserID(userID string) ([]model.Address, error)
|
||||
FindAddressByID(id string) (*model.Address, error)
|
||||
UpdateAddress(address *model.Address) error
|
||||
DeleteAddress(id string) error
|
||||
}
|
||||
|
||||
type addressRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewAddressRepository(db *gorm.DB) AddressRepository {
|
||||
return &addressRepository{DB: db}
|
||||
}
|
||||
|
||||
func (r *addressRepository) CreateAddress(address *model.Address) error {
|
||||
return r.DB.Create(address).Error
|
||||
}
|
||||
|
||||
|
||||
func (r *addressRepository) FindAddressByUserID(userID string) ([]model.Address, error) {
|
||||
var addresses []model.Address
|
||||
err := r.DB.Where("user_id = ?", userID).Find(&addresses).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
func (r *addressRepository) FindAddressByID(id string) (*model.Address, error) {
|
||||
var address model.Address
|
||||
err := r.DB.Where("id = ?", id).First(&address).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &address, nil
|
||||
}
|
||||
|
||||
func (r *addressRepository) UpdateAddress(address *model.Address) error {
|
||||
err := r.DB.Save(address).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *addressRepository) DeleteAddress(id string) error {
|
||||
err := r.DB.Where("id = ?", id).Delete(&model.Address{}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
type ArticleRepository struct{}
|
||||
|
||||
func NewArticleRepository() *ArticleRepository {
|
||||
return &ArticleRepository{}
|
||||
}
|
||||
|
||||
func (r *ArticleRepository) GetAll() ([]domain.Article, error) {
|
||||
var articles []domain.Article
|
||||
err := config.DB.Find(&articles).Error
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to fetch articles from database")
|
||||
}
|
||||
return articles, nil
|
||||
}
|
||||
|
||||
func (r *ArticleRepository) GetByID(id string) (*domain.Article, error) {
|
||||
var article domain.Article
|
||||
err := config.DB.First(&article, "id = ?", id).Error
|
||||
if err != nil {
|
||||
return nil, errors.New("article not found")
|
||||
}
|
||||
return &article, nil
|
||||
}
|
||||
|
||||
func (r *ArticleRepository) Create(article *domain.Article) error {
|
||||
return config.DB.Create(article).Error
|
||||
}
|
||||
|
||||
func (r *ArticleRepository) Update(article *domain.Article) error {
|
||||
return config.DB.Save(article).Error
|
||||
}
|
||||
|
||||
func (r *ArticleRepository) Delete(article *domain.Article) error {
|
||||
return config.DB.Delete(article).Error
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ArticleRepository interface {
|
||||
CreateArticle(article *model.Article) error
|
||||
FindArticleByID(id string) (*model.Article, error)
|
||||
FindAllArticles(page, limit int) ([]model.Article, int, error)
|
||||
UpdateArticle(id string, article *model.Article) error
|
||||
DeleteArticle(id string) error
|
||||
}
|
||||
|
||||
type articleRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewArticleRepository(db *gorm.DB) ArticleRepository {
|
||||
return &articleRepository{DB: db}
|
||||
}
|
||||
|
||||
func (r *articleRepository) CreateArticle(article *model.Article) error {
|
||||
return r.DB.Create(article).Error
|
||||
}
|
||||
|
||||
func (r *articleRepository) FindArticleByID(id string) (*model.Article, error) {
|
||||
var article model.Article
|
||||
err := r.DB.Where("id = ?", id).First(&article).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("article with ID %s not found", id)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to fetch article: %v", err)
|
||||
}
|
||||
return &article, nil
|
||||
}
|
||||
|
||||
func (r *articleRepository) FindAllArticles(page, limit int) ([]model.Article, int, error) {
|
||||
var articles []model.Article
|
||||
var total int64
|
||||
|
||||
if err := r.DB.Model(&model.Article{}).Count(&total).Error; err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to count articles: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Total Articles Count: %d\n", total)
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
err := r.DB.Offset((page - 1) * limit).Limit(limit).Find(&articles).Error
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to fetch articles: %v", err)
|
||||
}
|
||||
} else {
|
||||
err := r.DB.Find(&articles).Error
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to fetch articles: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return articles, int(total), nil
|
||||
}
|
||||
|
||||
func (r *articleRepository) UpdateArticle(id string, article *model.Article) error {
|
||||
return r.DB.Model(&model.Article{}).Where("id = ?", id).Updates(article).Error
|
||||
}
|
||||
|
||||
func (r *articleRepository) DeleteArticle(id string) error {
|
||||
result := r.DB.Delete(&model.Article{}, "id = ?", id)
|
||||
return result.Error
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CreateUser(username, name, email, phone, password, roleId string) error {
|
||||
user := domain.User{
|
||||
Username: username,
|
||||
Name: name,
|
||||
Email: email,
|
||||
Phone: phone,
|
||||
Password: password,
|
||||
RoleID: roleId,
|
||||
}
|
||||
|
||||
result := config.DB.Create(&user)
|
||||
if result.Error != nil {
|
||||
return errors.New("failed to create user")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUserByEmailUsernameOrPhone(identifier, roleId string) (domain.User, error) {
|
||||
ctx := context.Background()
|
||||
cacheKey := fmt.Sprintf("user:%s", identifier)
|
||||
var user domain.User
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
if roleId != "" && user.RoleID != roleId {
|
||||
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
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func UpdateUserPassword(userID, newPassword string) error {
|
||||
var user domain.User
|
||||
|
||||
if err := config.DB.Where("id = ?", userID).First(&user).Error; err != nil {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
|
||||
user.Password = newPassword
|
||||
|
||||
if err := config.DB.Save(&user).Error; err != nil {
|
||||
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
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserRepository interface {
|
||||
FindByIdentifierAndRole(identifier, roleID string) (*model.User, error)
|
||||
FindByEmailOrUsernameOrPhone(identifier string) (*model.User, error)
|
||||
FindByUsername(username string) (*model.User, error)
|
||||
FindByPhoneAndRole(phone, roleID string) (*model.User, error)
|
||||
FindByEmailAndRole(email, roleID string) (*model.User, error)
|
||||
|
||||
Create(user *model.User) error
|
||||
}
|
||||
|
||||
type userRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewUserRepository(db *gorm.DB) UserRepository {
|
||||
return &userRepository{DB: db}
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByIdentifierAndRole(identifier, roleID string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.DB.Preload("Role").Where("(email = ? OR username = ? OR phone = ?) AND role_id = ?", identifier, identifier, identifier, roleID).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user.Role == nil {
|
||||
return nil, fmt.Errorf("role not found for this user")
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByUsername(username string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.DB.Where("username = ?", username).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByPhoneAndRole(phone, roleID string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.DB.Where("phone = ? AND role_id = ?", phone, roleID).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByEmailAndRole(email, roleID string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.DB.Where("email = ? AND role_id = ?", email, roleID).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByEmailOrUsernameOrPhone(identifier string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.DB.Where("email = ? OR username = ? OR phone = ?", identifier, identifier, identifier).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) Create(user *model.User) error {
|
||||
err := r.DB.Create(user).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
type BannerRepository struct{}
|
||||
|
||||
func NewBannerRepository() *BannerRepository {
|
||||
return &BannerRepository{}
|
||||
}
|
||||
|
||||
func (r *BannerRepository) GetAll() ([]domain.Banner, error) {
|
||||
var banners []domain.Banner
|
||||
err := config.DB.Find(&banners).Error
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to fetch banners from database")
|
||||
}
|
||||
return banners, nil
|
||||
}
|
||||
|
||||
func (r *BannerRepository) GetByID(id string) (*domain.Banner, error) {
|
||||
var banner domain.Banner
|
||||
err := config.DB.First(&banner, "id = ?", id).Error
|
||||
if err != nil {
|
||||
return nil, errors.New("banner not found")
|
||||
}
|
||||
return &banner, nil
|
||||
}
|
||||
|
||||
func (r *BannerRepository) Create(banner *domain.Banner) error {
|
||||
return config.DB.Create(banner).Error
|
||||
}
|
||||
|
||||
func (r *BannerRepository) Update(banner *domain.Banner) error {
|
||||
return config.DB.Save(banner).Error
|
||||
}
|
||||
|
||||
func (r *BannerRepository) Delete(banner *domain.Banner) error {
|
||||
return config.DB.Delete(banner).Error
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type BannerRepository interface {
|
||||
CreateBanner(banner *model.Banner) error
|
||||
FindBannerByID(id string) (*model.Banner, error)
|
||||
FindAllBanners() ([]model.Banner, error)
|
||||
UpdateBanner(id string, banner *model.Banner) error
|
||||
DeleteBanner(id string) error
|
||||
}
|
||||
|
||||
type bannerRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewBannerRepository(db *gorm.DB) BannerRepository {
|
||||
return &bannerRepository{DB: db}
|
||||
}
|
||||
|
||||
func (r *bannerRepository) CreateBanner(banner *model.Banner) error {
|
||||
if err := r.DB.Create(banner).Error; err != nil {
|
||||
return fmt.Errorf("failed to create banner: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *bannerRepository) FindBannerByID(id string) (*model.Banner, error) {
|
||||
var banner model.Banner
|
||||
err := r.DB.Where("id = ?", id).First(&banner).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("banner with ID %s not found", id)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to fetch banner by ID: %v", err)
|
||||
}
|
||||
return &banner, nil
|
||||
}
|
||||
|
||||
func (r *bannerRepository) FindAllBanners() ([]model.Banner, error) {
|
||||
var banners []model.Banner
|
||||
err := r.DB.Find(&banners).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch banners: %v", err)
|
||||
}
|
||||
|
||||
return banners, nil
|
||||
}
|
||||
|
||||
func (r *bannerRepository) UpdateBanner(id string, banner *model.Banner) error {
|
||||
err := r.DB.Model(&model.Banner{}).Where("id = ?", id).Updates(banner).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update banner: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *bannerRepository) DeleteBanner(id string) error {
|
||||
result := r.DB.Delete(&model.Banner{}, "id = ?", id)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("failed to delete banner: %v", result.Error)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
func GetCoverageAreas() ([]domain.CoverageArea, error) {
|
||||
var coverageAreas []domain.CoverageArea
|
||||
if err := config.DB.Find(&coverageAreas).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return coverageAreas, nil
|
||||
}
|
||||
|
||||
func GetCoverageAreaByID(id string) (domain.CoverageArea, error) {
|
||||
var coverageArea domain.CoverageArea
|
||||
if err := config.DB.Where("id = ?", id).First(&coverageArea).Error; err != nil {
|
||||
return coverageArea, err
|
||||
}
|
||||
return coverageArea, nil
|
||||
}
|
||||
|
||||
func GetCoverageAreaByDistrictID(id string) (domain.CoverageDistric, error) {
|
||||
var coverageDistric domain.CoverageDistric
|
||||
if err := config.DB.Where("id = ?", id).First(&coverageDistric).Error; err != nil {
|
||||
return coverageDistric, err
|
||||
}
|
||||
return coverageDistric, nil
|
||||
}
|
||||
|
||||
func GetCoverageDistricsByCoverageAreaID(areaID string) ([]domain.CoverageDistric, error) {
|
||||
var districts []domain.CoverageDistric
|
||||
if err := config.DB.Where("coverage_area_id = ?", areaID).Find(&districts).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return districts, nil
|
||||
}
|
||||
|
||||
func GetSubdistrictsByCoverageDistrictID(districtID string) ([]domain.CoverageSubdistrict, error) {
|
||||
var subdistricts []domain.CoverageSubdistrict
|
||||
if err := config.DB.Where("coverage_district_id = ?", districtID).Find(&subdistricts).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return subdistricts, nil
|
||||
}
|
||||
|
||||
func CreateCoverageArea(coverageArea *domain.CoverageArea) error {
|
||||
if err := config.DB.Create(&coverageArea).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateCoverageDistrict(coverageDistrict *domain.CoverageDistric) error {
|
||||
if err := config.DB.Create(&coverageDistrict).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateCoverageSubdistrict(coverageSubdistrict *domain.CoverageSubdistrict) error {
|
||||
if err := config.DB.Create(&coverageSubdistrict).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateCoverageArea(id string, coverageArea domain.CoverageArea) (domain.CoverageArea, error) {
|
||||
var existingCoverageArea domain.CoverageArea
|
||||
if err := config.DB.Where("id = ?", id).First(&existingCoverageArea).Error; err != nil {
|
||||
return existingCoverageArea, err
|
||||
}
|
||||
|
||||
existingCoverageArea.Province = coverageArea.Province
|
||||
if err := config.DB.Save(&existingCoverageArea).Error; err != nil {
|
||||
return existingCoverageArea, err
|
||||
}
|
||||
|
||||
return existingCoverageArea, nil
|
||||
}
|
||||
|
||||
func UpdateCoverageDistrict(id string, coverageDistrict domain.CoverageDistric) (domain.CoverageDistric, error) {
|
||||
var existingCoverageDistrict domain.CoverageDistric
|
||||
if err := config.DB.Where("id = ?", id).First(&existingCoverageDistrict).Error; err != nil {
|
||||
return existingCoverageDistrict, err
|
||||
}
|
||||
|
||||
existingCoverageDistrict.District = coverageDistrict.District
|
||||
if err := config.DB.Save(&existingCoverageDistrict).Error; err != nil {
|
||||
return existingCoverageDistrict, err
|
||||
}
|
||||
|
||||
return existingCoverageDistrict, nil
|
||||
}
|
||||
|
||||
func UpdateCoverageSubdistrict(id string, coverageSubdistrict domain.CoverageSubdistrict) (domain.CoverageSubdistrict, error) {
|
||||
var existingCoverageSubdistrict domain.CoverageSubdistrict
|
||||
if err := config.DB.Where("id = ?", id).First(&existingCoverageSubdistrict).Error; err != nil {
|
||||
return existingCoverageSubdistrict, err
|
||||
}
|
||||
|
||||
existingCoverageSubdistrict.Subdistrict = coverageSubdistrict.Subdistrict
|
||||
if err := config.DB.Save(&existingCoverageSubdistrict).Error; err != nil {
|
||||
return existingCoverageSubdistrict, err
|
||||
}
|
||||
|
||||
return existingCoverageSubdistrict, nil
|
||||
}
|
||||
|
||||
func DeleteCoverageArea(id string) error {
|
||||
var coverageArea domain.CoverageArea
|
||||
if err := config.DB.Where("id = ?", id).First(&coverageArea).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := config.DB.Delete(&coverageArea).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteCoverageDistrict(id string) error {
|
||||
var coverageDistrict domain.CoverageDistric
|
||||
if err := config.DB.Where("id = ?", id).First(&coverageDistrict).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := config.DB.Delete(&coverageDistrict).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteCoverageSubdistrict(id string) error {
|
||||
var coverageSubdistrict domain.CoverageSubdistrict
|
||||
if err := config.DB.Where("id = ?", id).First(&coverageSubdistrict).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := config.DB.Delete(&coverageSubdistrict).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
type PointRepository struct{}
|
||||
|
||||
func NewPointRepository() *PointRepository {
|
||||
return &PointRepository{}
|
||||
}
|
||||
|
||||
func (r *PointRepository) GetAll() ([]domain.Point, error) {
|
||||
var points []domain.Point
|
||||
err := config.DB.Find(&points).Error
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to fetch points from database")
|
||||
}
|
||||
return points, nil
|
||||
}
|
||||
|
||||
func (r *PointRepository) GetByID(id string) (*domain.Point, error) {
|
||||
var point domain.Point
|
||||
err := config.DB.First(&point, "id = ?", id).Error
|
||||
if err != nil {
|
||||
return nil, errors.New("point not found")
|
||||
}
|
||||
return &point, nil
|
||||
}
|
||||
|
||||
func (r *PointRepository) Create(point *domain.Point) error {
|
||||
return config.DB.Create(point).Error
|
||||
}
|
||||
|
||||
func (r *PointRepository) Update(point *domain.Point) error {
|
||||
return config.DB.Save(point).Error
|
||||
}
|
||||
|
||||
func (r *PointRepository) Delete(point *domain.Point) error {
|
||||
return config.DB.Delete(point).Error
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type InitialCointRepository interface {
|
||||
CreateInitialCoint(coint *model.InitialCoint) error
|
||||
FindInitialCointByID(id string) (*model.InitialCoint, error)
|
||||
FindAllInitialCoints() ([]model.InitialCoint, error)
|
||||
UpdateInitialCoint(id string, coint *model.InitialCoint) error
|
||||
DeleteInitialCoint(id string) error
|
||||
}
|
||||
|
||||
type initialCointRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewInitialCointRepository(db *gorm.DB) InitialCointRepository {
|
||||
return &initialCointRepository{DB: db}
|
||||
}
|
||||
|
||||
func (r *initialCointRepository) CreateInitialCoint(coint *model.InitialCoint) error {
|
||||
if err := r.DB.Create(coint).Error; err != nil {
|
||||
return fmt.Errorf("failed to create initial coint: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *initialCointRepository) FindInitialCointByID(id string) (*model.InitialCoint, error) {
|
||||
var coint model.InitialCoint
|
||||
err := r.DB.Where("id = ?", id).First(&coint).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("initial coint with ID %s not found", id)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to fetch initial coint by ID: %v", err)
|
||||
}
|
||||
return &coint, nil
|
||||
}
|
||||
|
||||
func (r *initialCointRepository) FindAllInitialCoints() ([]model.InitialCoint, error) {
|
||||
var coints []model.InitialCoint
|
||||
err := r.DB.Find(&coints).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch initial coints: %v", err)
|
||||
}
|
||||
return coints, nil
|
||||
}
|
||||
|
||||
func (r *initialCointRepository) UpdateInitialCoint(id string, coint *model.InitialCoint) error {
|
||||
err := r.DB.Model(&model.InitialCoint{}).Where("id = ?", id).Updates(coint).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update initial coint: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *initialCointRepository) DeleteInitialCoint(id string) error {
|
||||
result := r.DB.Delete(&model.InitialCoint{}, "id = ?", id)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("failed to delete initial coint: %v", result.Error)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func GetProductsByStoreID(storeID string, limit, offset int) ([]domain.Product, error) {
|
||||
var products []domain.Product
|
||||
query := config.DB.Preload("ProductImages").Preload("TrashDetail").Where("store_id = ?", storeID)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err := query.Find(&products).Error
|
||||
return products, err
|
||||
}
|
||||
|
||||
func GetProductsByUserID(userID string, limit, offset int) ([]domain.Product, error) {
|
||||
var products []domain.Product
|
||||
query := config.DB.Preload("ProductImages").Preload("TrashDetail").Where("user_id = ?", userID)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err := query.Find(&products).Error
|
||||
return products, err
|
||||
}
|
||||
|
||||
func GetProductByIDAndStoreID(productID, storeID string) (domain.Product, error) {
|
||||
var product domain.Product
|
||||
err := config.DB.Preload("ProductImages").Preload("TrashDetail").
|
||||
Where("id = ? AND store_id = ?", productID, storeID).
|
||||
First(&product).Error
|
||||
|
||||
return product, err
|
||||
}
|
||||
|
||||
func GetProductByID(productID string) (domain.Product, error) {
|
||||
var product domain.Product
|
||||
err := config.DB.Preload("ProductImages").Preload("TrashDetail").
|
||||
Where("id = ?", productID).First(&product).Error
|
||||
return product, err
|
||||
}
|
||||
|
||||
func IsValidStoreID(storeID string) bool {
|
||||
var count int64
|
||||
err := config.DB.Model(&domain.Store{}).Where("id = ?", storeID).Count(&count).Error
|
||||
if err != nil || count == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func CreateProduct(product *domain.Product, images []domain.ProductImage) error {
|
||||
|
||||
return config.DB.Transaction(func(tx *gorm.DB) error {
|
||||
|
||||
if err := tx.Create(product).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(images) > 0 {
|
||||
for i := range images {
|
||||
images[i].ProductID = product.ID
|
||||
}
|
||||
|
||||
if err := tx.Create(&images).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateProduct(product *domain.Product, images []domain.ProductImage) error {
|
||||
return config.DB.Transaction(func(tx *gorm.DB) error {
|
||||
|
||||
if err := tx.Save(product).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(images) > 0 {
|
||||
for i := range images {
|
||||
images[i].ProductID = product.ID
|
||||
}
|
||||
|
||||
if err := tx.Where("product_id = ?", product.ID).Delete(&domain.ProductImage{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Create(&images).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteProduct(productID string) error {
|
||||
|
||||
return config.DB.Where("id = ?", productID).Delete(&domain.Product{}).Error
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
type RequestPickupRepository interface {
|
||||
Create(request *domain.RequestPickup) error
|
||||
GetByID(id string) (*domain.RequestPickup, error)
|
||||
GetByUserID(userID string) ([]domain.RequestPickup, error)
|
||||
DeleteByID(id string) error
|
||||
ExistsByID(id string) (bool, error)
|
||||
}
|
||||
|
||||
type requestPickupRepository struct{}
|
||||
|
||||
func NewRequestPickupRepository() RequestPickupRepository {
|
||||
return &requestPickupRepository{}
|
||||
}
|
||||
|
||||
func (r *requestPickupRepository) Create(request *domain.RequestPickup) error {
|
||||
return config.DB.Create(request).Error
|
||||
}
|
||||
|
||||
func (r *requestPickupRepository) GetByID(id string) (*domain.RequestPickup, error) {
|
||||
var requestPickup domain.RequestPickup
|
||||
if err := config.DB.Preload("Request").
|
||||
Preload("Request.TrashCategory").
|
||||
Preload("UserAddress").
|
||||
Where("id = ?", id).
|
||||
First(&requestPickup).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &requestPickup, nil
|
||||
}
|
||||
|
||||
func (r *requestPickupRepository) GetByUserID(userID string) ([]domain.RequestPickup, error) {
|
||||
var requestPickups []domain.RequestPickup
|
||||
err := config.DB.Preload("Request").
|
||||
Preload("Request.TrashCategory").
|
||||
Preload("UserAddress").
|
||||
Where("user_id = ?", userID).
|
||||
Find(&requestPickups).Error
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return requestPickups, nil
|
||||
}
|
||||
|
||||
func (r *requestPickupRepository) ExistsByID(id string) (bool, error) {
|
||||
var count int64
|
||||
if err := config.DB.Model(&domain.RequestPickup{}).Where("id = ?", id).Count(&count).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
|
||||
func (r *requestPickupRepository) DeleteByID(id string) error {
|
||||
return config.DB.Where("id = ?", id).Delete(&domain.RequestPickup{}).Error
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
func GetUserRoleByID(id string) (domain.UserRole, error) {
|
||||
var role domain.UserRole
|
||||
|
||||
ctx := config.Context()
|
||||
cachedRole, err := config.RedisClient.Get(ctx, "userRole:"+id).Result()
|
||||
|
||||
if err == nil {
|
||||
|
||||
err := json.Unmarshal([]byte(cachedRole), &role)
|
||||
if err != nil {
|
||||
return role, errors.New("gagal mendekode data cache Redis")
|
||||
}
|
||||
return role, nil
|
||||
}
|
||||
|
||||
err = config.DB.Where("id = ?", id).First(&role).Error
|
||||
if err != nil {
|
||||
return role, errors.New("userRole tidak ditemukan")
|
||||
}
|
||||
|
||||
roleJSON, err := json.Marshal(role)
|
||||
if err != nil {
|
||||
return role, errors.New("gagal mendekode data untuk Redis")
|
||||
}
|
||||
|
||||
err = config.RedisClient.Set(ctx, "userRole:"+id, roleJSON, time.Hour*24).Err()
|
||||
if err != nil {
|
||||
return role, errors.New("gagal menyimpan data di Redis")
|
||||
}
|
||||
|
||||
return role, nil
|
||||
}
|
||||
|
||||
func GetAllUserRoles() ([]domain.UserRole, error) {
|
||||
var roles []domain.UserRole
|
||||
|
||||
ctx := config.Context()
|
||||
cachedRoles, err := config.RedisClient.Get(ctx, "allUserRoles").Result()
|
||||
|
||||
if err == nil {
|
||||
|
||||
err := json.Unmarshal([]byte(cachedRoles), &roles)
|
||||
if err != nil {
|
||||
return roles, errors.New("gagal mendekode data cache Redis")
|
||||
}
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
err = config.DB.Find(&roles).Error
|
||||
if err != nil {
|
||||
return nil, errors.New("gagal mengambil data UserRole")
|
||||
}
|
||||
|
||||
rolesJSON, err := json.Marshal(roles)
|
||||
if err != nil {
|
||||
return roles, errors.New("gagal mendekode data untuk Redis")
|
||||
}
|
||||
|
||||
err = config.RedisClient.Set(ctx, "allUserRoles", rolesJSON, time.Hour*24).Err()
|
||||
if err != nil {
|
||||
return roles, errors.New("gagal menyimpan data di Redis")
|
||||
}
|
||||
|
||||
return roles, nil
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type RoleRepository interface {
|
||||
FindByID(id string) (*model.Role, error)
|
||||
FindAll() ([]model.Role, error)
|
||||
}
|
||||
|
||||
type roleRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewRoleRepository(db *gorm.DB) RoleRepository {
|
||||
return &roleRepository{DB: db}
|
||||
}
|
||||
|
||||
func (r *roleRepository) FindByID(id string) (*model.Role, error) {
|
||||
var role model.Role
|
||||
err := r.DB.Where("id = ?", id).First(&role).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
func (r *roleRepository) FindAll() ([]model.Role, error) {
|
||||
var roles []model.Role
|
||||
err := r.DB.Find(&roles).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return roles, nil
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
func GetStoreByID(storeID string) (domain.Store, error) {
|
||||
var store domain.Store
|
||||
err := config.DB.Where("id = ?", storeID).First(&store).Error
|
||||
return store, err
|
||||
}
|
||||
|
||||
func GetStoresByUserID(userID string, limit, offset int) ([]domain.Store, error) {
|
||||
var stores []domain.Store
|
||||
query := config.DB.Where("user_id = ?", userID)
|
||||
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
err := query.Find(&stores).Error
|
||||
return stores, err
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type TrashRepository interface {
|
||||
CreateCategory(category *model.TrashCategory) error
|
||||
AddDetailToCategory(detail *model.TrashDetail) error
|
||||
GetCategories() ([]model.TrashCategory, error)
|
||||
GetCategoryByID(id string) (*model.TrashCategory, error)
|
||||
GetTrashDetailByID(id string) (*model.TrashDetail, error)
|
||||
GetDetailsByCategoryID(categoryID string) ([]model.TrashDetail, error)
|
||||
UpdateCategoryName(id string, newName string) error
|
||||
UpdateTrashDetail(id string, description string, price float64) error
|
||||
DeleteCategory(id string) error
|
||||
DeleteTrashDetail(id string) error
|
||||
}
|
||||
|
||||
type trashRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewTrashRepository(db *gorm.DB) TrashRepository {
|
||||
return &trashRepository{DB: db}
|
||||
}
|
||||
|
||||
func (r *trashRepository) CreateCategory(category *model.TrashCategory) error {
|
||||
if err := r.DB.Create(category).Error; err != nil {
|
||||
return fmt.Errorf("failed to create category: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *trashRepository) AddDetailToCategory(detail *model.TrashDetail) error {
|
||||
if err := r.DB.Create(detail).Error; err != nil {
|
||||
return fmt.Errorf("failed to add detail to category: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *trashRepository) GetCategories() ([]model.TrashCategory, error) {
|
||||
var categories []model.TrashCategory
|
||||
if err := r.DB.Preload("Details").Find(&categories).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch categories: %v", err)
|
||||
}
|
||||
return categories, nil
|
||||
}
|
||||
|
||||
func (r *trashRepository) GetCategoryByID(id string) (*model.TrashCategory, error) {
|
||||
var category model.TrashCategory
|
||||
|
||||
if err := r.DB.Preload("Details").First(&category, "id = ?", id).Error; err != nil {
|
||||
return nil, fmt.Errorf("category not found: %v", err)
|
||||
}
|
||||
return &category, nil
|
||||
}
|
||||
|
||||
func (r *trashRepository) GetTrashDetailByID(id string) (*model.TrashDetail, error) {
|
||||
var detail model.TrashDetail
|
||||
if err := r.DB.First(&detail, "id = ?", id).Error; err != nil {
|
||||
return nil, fmt.Errorf("trash detail not found: %v", err)
|
||||
}
|
||||
return &detail, nil
|
||||
}
|
||||
|
||||
func (r *trashRepository) GetDetailsByCategoryID(categoryID string) ([]model.TrashDetail, error) {
|
||||
var details []model.TrashDetail
|
||||
|
||||
if err := r.DB.Where("category_id = ?", categoryID).Find(&details).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch details for category %s: %v", categoryID, err)
|
||||
}
|
||||
return details, nil
|
||||
}
|
||||
|
||||
func (r *trashRepository) UpdateCategoryName(id string, newName string) error {
|
||||
if err := r.DB.Model(&model.TrashCategory{}).Where("id = ?", id).Update("name", newName).Error; err != nil {
|
||||
return fmt.Errorf("failed to update category name: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *trashRepository) UpdateTrashDetail(id string, description string, price float64) error {
|
||||
if err := r.DB.Model(&model.TrashDetail{}).Where("id = ?", id).Updates(model.TrashDetail{Description: description, Price: price}).Error; err != nil {
|
||||
return fmt.Errorf("failed to update trash detail: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *trashRepository) DeleteCategory(id string) error {
|
||||
if err := r.DB.Delete(&model.TrashCategory{}, "id = ?", id).Error; err != nil {
|
||||
return fmt.Errorf("failed to delete category: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *trashRepository) DeleteTrashDetail(id string) error {
|
||||
if err := r.DB.Delete(&model.TrashDetail{}, "id = ?", id).Error; err != nil {
|
||||
return fmt.Errorf("failed to delete trash detail: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
func GetTrashCategories() ([]domain.TrashCategory, error) {
|
||||
var categories []domain.TrashCategory
|
||||
if err := config.DB.Find(&categories).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return categories, nil
|
||||
}
|
||||
|
||||
func GetTrashCategoryDetail(id string) (domain.TrashCategory, error) {
|
||||
var category domain.TrashCategory
|
||||
if err := config.DB.Preload("Details").Where("id = ?", id).First(&category).Error; err != nil {
|
||||
return category, err
|
||||
}
|
||||
return category, nil
|
||||
}
|
||||
|
||||
func GetTrashDetailByID(id string) (domain.TrashDetail, error) {
|
||||
var detail domain.TrashDetail
|
||||
|
||||
if err := config.DB.Where("id = ?", id).First(&detail).Error; err != nil {
|
||||
return detail, err
|
||||
}
|
||||
return detail, nil
|
||||
}
|
||||
|
||||
func CreateTrashCategory(category *domain.TrashCategory) error {
|
||||
if err := config.DB.Create(category).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateTrashDetail(detail *domain.TrashDetail) error {
|
||||
if err := config.DB.Create(detail).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateTrashCategory(category *domain.TrashCategory) error {
|
||||
if err := config.DB.Save(category).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateTrashDetail(detail *domain.TrashDetail) error {
|
||||
if err := config.DB.Save(detail).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteTrashCategory(id string) error {
|
||||
|
||||
if err := config.DB.Where("category_id = ?", id).Delete(&domain.TrashDetail{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := config.DB.Where("id = ?", id).Delete(&domain.TrashCategory{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteTrashDetail(id string) error {
|
||||
|
||||
if err := config.DB.Where("id = ?", id).Delete(&domain.TrashDetail{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
)
|
||||
|
||||
func GetUsers() ([]domain.User, error) {
|
||||
var users []domain.User
|
||||
|
||||
if err := config.DB.Find(&users).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetUsersByRole(roleID string) ([]domain.User, error) {
|
||||
var users []domain.User
|
||||
|
||||
if err := config.DB.Where("role_id = ?", roleID).Find(&users).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetUserByUserrId(userID string) (domain.User, error) {
|
||||
var user domain.User
|
||||
|
||||
if err := config.DB.Where("id = ?", userID).First(&user).Error; err != nil {
|
||||
return domain.User{}, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserProfileRepository interface {
|
||||
FindByID(userID string) (*model.User, error)
|
||||
Update(user *model.User) error
|
||||
UpdateAvatar(userID, avatarURL string) error
|
||||
}
|
||||
|
||||
type userProfileRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewUserProfileRepository(db *gorm.DB) UserProfileRepository {
|
||||
return &userProfileRepository{DB: db}
|
||||
}
|
||||
|
||||
func (r *userProfileRepository) FindByID(userID string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.DB.Preload("Role").Where("id = ?", userID).First(&user).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, fmt.Errorf("user with ID %s not found", userID)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.Role == nil {
|
||||
return nil, fmt.Errorf("role not found for this user")
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *userProfileRepository) Update(user *model.User) error {
|
||||
err := r.DB.Save(user).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *userProfileRepository) UpdateAvatar(userID, avatarURL string) error {
|
||||
var user model.User
|
||||
err := r.DB.Model(&user).Where("id = ?", userID).Update("avatar", avatarURL).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"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) {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
redisClient := config.RedisClient
|
||||
redisClient.Del(context.Background(), fmt.Sprintf("user_pin:%s", userID))
|
||||
|
||||
return pin, nil
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserPinRepository interface {
|
||||
FindByUserID(userID string) (*model.UserPin, error)
|
||||
FindByPin(userPin string) (*model.UserPin, error)
|
||||
Create(userPin *model.UserPin) error
|
||||
Update(userPin *model.UserPin) error
|
||||
}
|
||||
|
||||
type userPinRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewUserPinRepository(db *gorm.DB) UserPinRepository {
|
||||
return &userPinRepository{DB: db}
|
||||
}
|
||||
|
||||
func (r *userPinRepository) FindByUserID(userID string) (*model.UserPin, error) {
|
||||
var userPin model.UserPin
|
||||
err := r.DB.Where("user_id = ?", userID).First(&userPin).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &userPin, nil
|
||||
}
|
||||
|
||||
func (r *userPinRepository) FindByPin(pin string) (*model.UserPin, error) {
|
||||
var userPin model.UserPin
|
||||
err := r.DB.Where("pin = ?", pin).First(&userPin).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &userPin, nil
|
||||
}
|
||||
|
||||
func (r *userPinRepository) Create(userPin *model.UserPin) error {
|
||||
err := r.DB.Create(userPin).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *userPinRepository) Update(userPin *model.UserPin) error {
|
||||
err := r.DB.Save(userPin).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type WilayahIndonesiaRepository interface {
|
||||
ImportProvinces(provinces []model.Province) error
|
||||
ImportRegencies(regencies []model.Regency) error
|
||||
ImportDistricts(districts []model.District) error
|
||||
ImportVillages(villages []model.Village) error
|
||||
|
||||
FindAllProvinces(page, limit int) ([]model.Province, int, error)
|
||||
FindProvinceByID(id string, page, limit int) (*model.Province, int, error)
|
||||
|
||||
FindAllRegencies(page, limit int) ([]model.Regency, int, error)
|
||||
FindRegencyByID(id string, page, limit int) (*model.Regency, int, error)
|
||||
|
||||
FindAllDistricts(page, limit int) ([]model.District, int, error)
|
||||
FindDistrictByID(id string, page, limit int) (*model.District, int, error)
|
||||
|
||||
FindAllVillages(page, limit int) ([]model.Village, int, error)
|
||||
FindVillageByID(id string) (*model.Village, error)
|
||||
}
|
||||
|
||||
type wilayahIndonesiaRepository struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func NewWilayahIndonesiaRepository(db *gorm.DB) WilayahIndonesiaRepository {
|
||||
return &wilayahIndonesiaRepository{DB: db}
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) ImportProvinces(provinces []model.Province) error {
|
||||
for _, province := range provinces {
|
||||
if err := r.DB.Create(&province).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) ImportRegencies(regencies []model.Regency) error {
|
||||
for _, regency := range regencies {
|
||||
if err := r.DB.Create(®ency).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) ImportDistricts(districts []model.District) error {
|
||||
for _, district := range districts {
|
||||
if err := r.DB.Create(&district).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) ImportVillages(villages []model.Village) error {
|
||||
for _, village := range villages {
|
||||
if err := r.DB.Create(&village).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) FindAllProvinces(page, limit int) ([]model.Province, int, error) {
|
||||
var provinces []model.Province
|
||||
var total int64
|
||||
|
||||
err := r.DB.Model(&model.Province{}).Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
err := r.DB.Offset((page - 1) * limit).Limit(limit).Find(&provinces).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
} else {
|
||||
|
||||
err := r.DB.Find(&provinces).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return provinces, int(total), nil
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) FindProvinceByID(id string, page, limit int) (*model.Province, int, error) {
|
||||
var province model.Province
|
||||
|
||||
err := r.DB.Preload("Regencies", func(db *gorm.DB) *gorm.DB {
|
||||
if page > 0 && limit > 0 {
|
||||
|
||||
return db.Offset((page - 1) * limit).Limit(limit)
|
||||
}
|
||||
|
||||
return db
|
||||
}).Where("id = ?", id).First(&province).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var totalRegencies int64
|
||||
r.DB.Model(&model.Regency{}).Where("province_id = ?", id).Count(&totalRegencies)
|
||||
|
||||
return &province, int(totalRegencies), nil
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) FindAllRegencies(page, limit int) ([]model.Regency, int, error) {
|
||||
var regencies []model.Regency
|
||||
var total int64
|
||||
|
||||
err := r.DB.Model(&model.Regency{}).Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
err := r.DB.Offset((page - 1) * limit).Limit(limit).Find(®encies).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
} else {
|
||||
|
||||
err := r.DB.Find(®encies).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return regencies, int(total), nil
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) FindRegencyByID(id string, page, limit int) (*model.Regency, int, error) {
|
||||
var regency model.Regency
|
||||
|
||||
err := r.DB.Preload("Districts", func(db *gorm.DB) *gorm.DB {
|
||||
if page > 0 && limit > 0 {
|
||||
return db.Offset((page - 1) * limit).Limit(limit)
|
||||
}
|
||||
return db
|
||||
}).Where("id = ?", id).First(®ency).Error
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var totalDistricts int64
|
||||
err = r.DB.Model(&model.District{}).Where("regency_id = ?", id).Count(&totalDistricts).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return ®ency, int(totalDistricts), nil
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) FindAllDistricts(page, limit int) ([]model.District, int, error) {
|
||||
var district []model.District
|
||||
var total int64
|
||||
|
||||
err := r.DB.Model(&model.District{}).Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
err := r.DB.Offset((page - 1) * limit).Limit(limit).Find(&district).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
} else {
|
||||
|
||||
err := r.DB.Find(&district).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return district, int(total), nil
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) FindDistrictByID(id string, page, limit int) (*model.District, int, error) {
|
||||
var district model.District
|
||||
|
||||
err := r.DB.Preload("Villages", func(db *gorm.DB) *gorm.DB {
|
||||
if page > 0 && limit > 0 {
|
||||
|
||||
return db.Offset((page - 1) * limit).Limit(limit)
|
||||
}
|
||||
|
||||
return db
|
||||
}).Where("id = ?", id).First(&district).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var totalVillage int64
|
||||
r.DB.Model(&model.Village{}).Where("district_id = ?", id).Count(&totalVillage)
|
||||
|
||||
return &district, int(totalVillage), nil
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) FindAllVillages(page, limit int) ([]model.Village, int, error) {
|
||||
var villages []model.Village
|
||||
var total int64
|
||||
|
||||
err := r.DB.Model(&model.Village{}).Count(&total).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if page > 0 && limit > 0 {
|
||||
err := r.DB.Offset((page - 1) * limit).Limit(limit).Find(&villages).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
} else {
|
||||
|
||||
err := r.DB.Find(&villages).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return villages, int(total), nil
|
||||
}
|
||||
|
||||
func (r *wilayahIndonesiaRepository) FindVillageByID(id string) (*model.Village, error) {
|
||||
var village model.Village
|
||||
err := r.DB.Where("id = ?", id).First(&village).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &village, nil
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
)
|
||||
|
||||
func GetProvinces() ([]domain.Province, error) {
|
||||
provinces, err := repositories.GetProvinces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return provinces, nil
|
||||
}
|
||||
|
||||
func GetRegencies() ([]domain.Regency, error) {
|
||||
regencies, err := repositories.GetRegencies()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return regencies, nil
|
||||
}
|
||||
|
||||
func GetDistricts() ([]domain.District, error) {
|
||||
districts, err := repositories.GetDistricts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return districts, nil
|
||||
}
|
||||
|
||||
func GetVillages() ([]domain.Village, error) {
|
||||
villages, err := repositories.GetVillages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return villages, nil
|
||||
}
|
||||
|
||||
func GetProvinceByID(id string) (domain.Province, error) {
|
||||
province, err := repositories.GetProvinceByID(id)
|
||||
if err != nil {
|
||||
return domain.Province{}, err
|
||||
}
|
||||
return province, nil
|
||||
}
|
||||
|
||||
func GetRegencyByID(id string) (domain.Regency, error) {
|
||||
regency, err := repositories.GetRegencyByID(id)
|
||||
if err != nil {
|
||||
return domain.Regency{}, err
|
||||
}
|
||||
return regency, nil
|
||||
}
|
||||
|
||||
func GetDistrictByID(id string) (domain.District, error) {
|
||||
district, err := repositories.GetDistrictByID(id)
|
||||
if err != nil {
|
||||
return domain.District{}, err
|
||||
}
|
||||
return district, nil
|
||||
}
|
||||
|
||||
func GetVillageByID(id string) (domain.Village, error) {
|
||||
village, err := repositories.GetVillageByID(id)
|
||||
if err != nil {
|
||||
return domain.Village{}, err
|
||||
}
|
||||
return village, nil
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
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"
|
||||
)
|
||||
|
||||
func CreateAddress(userID string, input dto.AddressInput) (domain.Address, error) {
|
||||
address := domain.Address{
|
||||
UserID: userID,
|
||||
Province: input.Province,
|
||||
District: input.District,
|
||||
Subdistrict: input.Subdistrict,
|
||||
PostalCode: input.PostalCode,
|
||||
Village: input.Village,
|
||||
Detail: input.Detail,
|
||||
Geography: input.Geography,
|
||||
}
|
||||
|
||||
err := repositories.CreateAddress(&address)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func GetAddressByID(addressID string) (domain.Address, error) {
|
||||
address, err := repositories.GetAddressByID(addressID)
|
||||
if err != nil {
|
||||
return address, errors.New("address not found")
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
address.Province = input.Province
|
||||
address.District = input.District
|
||||
address.Subdistrict = input.Subdistrict
|
||||
address.PostalCode = input.PostalCode
|
||||
address.Village = input.Village
|
||||
address.Detail = input.Detail
|
||||
address.Geography = input.Geography
|
||||
|
||||
updatedAddress, err := repositories.UpdateAddress(address)
|
||||
if err != nil {
|
||||
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 {
|
||||
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
|
||||
}
|
|
@ -0,0 +1,402 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type AddressService interface {
|
||||
CreateAddress(userID string, request dto.CreateAddressDTO) (*dto.AddressResponseDTO, error)
|
||||
GetAddressByUserID(userID string) ([]dto.AddressResponseDTO, error)
|
||||
GetAddressByID(userID, id string) (*dto.AddressResponseDTO, error)
|
||||
UpdateAddress(userID, id string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error)
|
||||
DeleteAddress(userID, id string) error
|
||||
}
|
||||
|
||||
type addressService struct {
|
||||
AddressRepo repositories.AddressRepository
|
||||
WilayahRepo repositories.WilayahIndonesiaRepository
|
||||
}
|
||||
|
||||
func NewAddressService(addressRepo repositories.AddressRepository, wilayahRepo repositories.WilayahIndonesiaRepository) AddressService {
|
||||
return &addressService{
|
||||
AddressRepo: addressRepo,
|
||||
WilayahRepo: wilayahRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *addressService) CreateAddress(userID string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) {
|
||||
|
||||
province, _, err := s.WilayahRepo.FindProvinceByID(addressDTO.Province, 0, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid province_id")
|
||||
}
|
||||
|
||||
regency, _, err := s.WilayahRepo.FindRegencyByID(addressDTO.Regency, 0, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid regency_id")
|
||||
}
|
||||
|
||||
district, _, err := s.WilayahRepo.FindDistrictByID(addressDTO.District, 0, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid district_id")
|
||||
}
|
||||
|
||||
village, err := s.WilayahRepo.FindVillageByID(addressDTO.Village)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid village_id")
|
||||
}
|
||||
|
||||
address := model.Address{
|
||||
UserID: userID,
|
||||
Province: province.Name,
|
||||
Regency: regency.Name,
|
||||
District: district.Name,
|
||||
Village: village.Name,
|
||||
PostalCode: addressDTO.PostalCode,
|
||||
Detail: addressDTO.Detail,
|
||||
Geography: addressDTO.Geography,
|
||||
}
|
||||
|
||||
err = s.AddressRepo.CreateAddress(&address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create address: %v", err)
|
||||
}
|
||||
|
||||
userCacheKey := fmt.Sprintf("user:%s:addresses", userID)
|
||||
utils.DeleteData(userCacheKey)
|
||||
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(address.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
|
||||
|
||||
addressResponseDTO := &dto.AddressResponseDTO{
|
||||
UserID: address.UserID,
|
||||
ID: address.ID,
|
||||
Province: address.Province,
|
||||
Regency: address.Regency,
|
||||
District: address.District,
|
||||
Village: address.Village,
|
||||
PostalCode: address.PostalCode,
|
||||
Detail: address.Detail,
|
||||
Geography: address.Geography,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("address:%s", address.ID)
|
||||
cacheData := map[string]interface{}{
|
||||
"data": addressResponseDTO,
|
||||
}
|
||||
err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
|
||||
if err != nil {
|
||||
fmt.Printf("Error caching new address to Redis: %v\n", err)
|
||||
}
|
||||
|
||||
addresses, err := s.AddressRepo.FindAddressByUserID(userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch updated addresses for user: %v", err)
|
||||
}
|
||||
|
||||
var addressDTOs []dto.AddressResponseDTO
|
||||
for _, addr := range addresses {
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(addr.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(addr.UpdatedAt)
|
||||
|
||||
addressDTOs = append(addressDTOs, dto.AddressResponseDTO{
|
||||
UserID: addr.UserID,
|
||||
ID: addr.ID,
|
||||
Province: addr.Province,
|
||||
Regency: addr.Regency,
|
||||
District: addr.District,
|
||||
Village: addr.Village,
|
||||
PostalCode: addr.PostalCode,
|
||||
Detail: addr.Detail,
|
||||
Geography: addr.Geography,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
cacheData = map[string]interface{}{
|
||||
"data": addressDTOs,
|
||||
}
|
||||
err = utils.SetJSONData(userCacheKey, cacheData, time.Hour*24)
|
||||
if err != nil {
|
||||
fmt.Printf("Error caching updated user addresses to Redis: %v\n", err)
|
||||
}
|
||||
|
||||
return addressResponseDTO, nil
|
||||
}
|
||||
|
||||
func (s *addressService) GetAddressByUserID(userID string) ([]dto.AddressResponseDTO, error) {
|
||||
|
||||
cacheKey := fmt.Sprintf("user:%s:addresses", userID)
|
||||
cachedData, err := utils.GetJSONData(cacheKey)
|
||||
if err == nil && cachedData != nil {
|
||||
var addresses []dto.AddressResponseDTO
|
||||
if data, ok := cachedData["data"].([]interface{}); ok {
|
||||
for _, item := range data {
|
||||
addressData, ok := item.(map[string]interface{})
|
||||
if ok {
|
||||
addresses = append(addresses, dto.AddressResponseDTO{
|
||||
UserID: addressData["user_id"].(string),
|
||||
ID: addressData["address_id"].(string),
|
||||
Province: addressData["province"].(string),
|
||||
Regency: addressData["regency"].(string),
|
||||
District: addressData["district"].(string),
|
||||
Village: addressData["village"].(string),
|
||||
PostalCode: addressData["postalCode"].(string),
|
||||
Detail: addressData["detail"].(string),
|
||||
Geography: addressData["geography"].(string),
|
||||
CreatedAt: addressData["createdAt"].(string),
|
||||
UpdatedAt: addressData["updatedAt"].(string),
|
||||
})
|
||||
}
|
||||
}
|
||||
return addresses, nil
|
||||
}
|
||||
}
|
||||
|
||||
addresses, err := s.AddressRepo.FindAddressByUserID(userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch addresses: %v", err)
|
||||
}
|
||||
|
||||
var addressDTOs []dto.AddressResponseDTO
|
||||
for _, address := range addresses {
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(address.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
|
||||
|
||||
addressDTOs = append(addressDTOs, dto.AddressResponseDTO{
|
||||
UserID: address.UserID,
|
||||
ID: address.ID,
|
||||
Province: address.Province,
|
||||
Regency: address.Regency,
|
||||
District: address.District,
|
||||
Village: address.Village,
|
||||
PostalCode: address.PostalCode,
|
||||
Detail: address.Detail,
|
||||
Geography: address.Geography,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
cacheData := map[string]interface{}{
|
||||
"data": addressDTOs,
|
||||
}
|
||||
err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
|
||||
if err != nil {
|
||||
fmt.Printf("Error caching addresses to Redis: %v\n", err)
|
||||
}
|
||||
|
||||
return addressDTOs, nil
|
||||
}
|
||||
|
||||
func (s *addressService) GetAddressByID(userID, id string) (*dto.AddressResponseDTO, error) {
|
||||
address, err := s.AddressRepo.FindAddressByID(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("address not found: %v", err)
|
||||
}
|
||||
|
||||
if address.UserID != userID {
|
||||
return nil, fmt.Errorf("you are not authorized to update this address")
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("address:%s", id)
|
||||
cachedData, err := utils.GetJSONData(cacheKey)
|
||||
if err == nil && cachedData != nil {
|
||||
addressData, ok := cachedData["data"].(map[string]interface{})
|
||||
if ok {
|
||||
address := dto.AddressResponseDTO{
|
||||
UserID: addressData["user_id"].(string),
|
||||
ID: addressData["address_id"].(string),
|
||||
Province: addressData["province"].(string),
|
||||
Regency: addressData["regency"].(string),
|
||||
District: addressData["district"].(string),
|
||||
Village: addressData["village"].(string),
|
||||
PostalCode: addressData["postalCode"].(string),
|
||||
Detail: addressData["detail"].(string),
|
||||
Geography: addressData["geography"].(string),
|
||||
CreatedAt: addressData["createdAt"].(string),
|
||||
UpdatedAt: addressData["updatedAt"].(string),
|
||||
}
|
||||
return &address, nil
|
||||
}
|
||||
}
|
||||
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(address.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
|
||||
|
||||
addressDTO := &dto.AddressResponseDTO{
|
||||
UserID: address.UserID,
|
||||
ID: address.ID,
|
||||
Province: address.Province,
|
||||
Regency: address.Regency,
|
||||
District: address.District,
|
||||
Village: address.Village,
|
||||
PostalCode: address.PostalCode,
|
||||
Detail: address.Detail,
|
||||
Geography: address.Geography,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
|
||||
cacheData := map[string]interface{}{
|
||||
"data": addressDTO,
|
||||
}
|
||||
err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
|
||||
if err != nil {
|
||||
fmt.Printf("Error caching address to Redis: %v\n", err)
|
||||
}
|
||||
|
||||
return addressDTO, nil
|
||||
}
|
||||
|
||||
func (s *addressService) UpdateAddress(userID, id string, addressDTO dto.CreateAddressDTO) (*dto.AddressResponseDTO, error) {
|
||||
|
||||
address, err := s.AddressRepo.FindAddressByID(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("address not found: %v", err)
|
||||
}
|
||||
|
||||
if address.UserID != userID {
|
||||
return nil, fmt.Errorf("you are not authorized to update this address")
|
||||
}
|
||||
|
||||
province, _, err := s.WilayahRepo.FindProvinceByID(addressDTO.Province, 0, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid province_id")
|
||||
}
|
||||
|
||||
regency, _, err := s.WilayahRepo.FindRegencyByID(addressDTO.Regency, 0, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid regency_id")
|
||||
}
|
||||
|
||||
district, _, err := s.WilayahRepo.FindDistrictByID(addressDTO.District, 0, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid district_id")
|
||||
}
|
||||
|
||||
village, err := s.WilayahRepo.FindVillageByID(addressDTO.Village)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid village_id")
|
||||
}
|
||||
|
||||
address.Province = province.Name
|
||||
address.Regency = regency.Name
|
||||
address.District = district.Name
|
||||
address.Village = village.Name
|
||||
address.PostalCode = addressDTO.PostalCode
|
||||
address.Detail = addressDTO.Detail
|
||||
address.Geography = addressDTO.Geography
|
||||
address.UpdatedAt = time.Now()
|
||||
|
||||
err = s.AddressRepo.UpdateAddress(address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update address: %v", err)
|
||||
}
|
||||
|
||||
addressCacheKey := fmt.Sprintf("address:%s", id)
|
||||
utils.DeleteData(addressCacheKey)
|
||||
|
||||
userAddressesCacheKey := fmt.Sprintf("user:%s:addresses", userID)
|
||||
utils.DeleteData(userAddressesCacheKey)
|
||||
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(address.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(address.UpdatedAt)
|
||||
|
||||
addressResponseDTO := &dto.AddressResponseDTO{
|
||||
UserID: address.UserID,
|
||||
ID: address.ID,
|
||||
Province: address.Province,
|
||||
Regency: address.Regency,
|
||||
District: address.District,
|
||||
Village: address.Village,
|
||||
PostalCode: address.PostalCode,
|
||||
Detail: address.Detail,
|
||||
Geography: address.Geography,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
|
||||
cacheData := map[string]interface{}{
|
||||
"data": addressResponseDTO,
|
||||
}
|
||||
err = utils.SetJSONData(addressCacheKey, cacheData, time.Hour*24)
|
||||
if err != nil {
|
||||
fmt.Printf("Error caching updated address to Redis: %v\n", err)
|
||||
}
|
||||
|
||||
addresses, err := s.AddressRepo.FindAddressByUserID(address.UserID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch updated addresses for user: %v", err)
|
||||
}
|
||||
|
||||
var addressDTOs []dto.AddressResponseDTO
|
||||
for _, addr := range addresses {
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(addr.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(addr.UpdatedAt)
|
||||
|
||||
addressDTOs = append(addressDTOs, dto.AddressResponseDTO{
|
||||
UserID: addr.UserID,
|
||||
ID: addr.ID,
|
||||
Province: addr.Province,
|
||||
Regency: addr.Regency,
|
||||
District: addr.District,
|
||||
Village: addr.Village,
|
||||
PostalCode: addr.PostalCode,
|
||||
Detail: addr.Detail,
|
||||
Geography: addr.Geography,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
cacheData = map[string]interface{}{
|
||||
"data": addressDTOs,
|
||||
}
|
||||
err = utils.SetJSONData(userAddressesCacheKey, cacheData, time.Hour*24)
|
||||
if err != nil {
|
||||
fmt.Printf("Error caching updated user addresses to Redis: %v\n", err)
|
||||
}
|
||||
|
||||
return addressResponseDTO, nil
|
||||
}
|
||||
|
||||
func (s *addressService) DeleteAddress(userID, addressID string) error {
|
||||
|
||||
address, err := s.AddressRepo.FindAddressByID(addressID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("address not found: %v", err)
|
||||
}
|
||||
|
||||
if address.UserID != userID {
|
||||
return fmt.Errorf("you are not authorized to delete this address")
|
||||
}
|
||||
|
||||
err = s.AddressRepo.DeleteAddress(addressID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete address: %v", err)
|
||||
}
|
||||
|
||||
addressCacheKey := fmt.Sprintf("address:%s", addressID)
|
||||
err = utils.DeleteData(addressCacheKey)
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting address cache: %v\n", err)
|
||||
}
|
||||
|
||||
userAddressesCacheKey := fmt.Sprintf("user:%s:addresses", address.UserID)
|
||||
err = utils.DeleteData(userAddressesCacheKey)
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting user addresses cache: %v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,192 +0,0 @@
|
|||
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"
|
||||
)
|
||||
|
||||
type ArticleService struct {
|
||||
repo *repositories.ArticleRepository
|
||||
}
|
||||
|
||||
func NewArticleService(repo *repositories.ArticleRepository) *ArticleService {
|
||||
return &ArticleService{repo: repo}
|
||||
}
|
||||
|
||||
func (s *ArticleService) GetAllArticles() ([]dto.ArticleResponse, error) {
|
||||
ctx := config.Context()
|
||||
cacheKey := "articles:all"
|
||||
|
||||
cachedData, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil && cachedData != "" {
|
||||
var cachedArticles []dto.ArticleResponse
|
||||
if err := json.Unmarshal([]byte(cachedData), &cachedArticles); err == nil {
|
||||
return cachedArticles, nil
|
||||
}
|
||||
}
|
||||
|
||||
articles, err := s.repo.GetAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []dto.ArticleResponse
|
||||
for _, article := range articles {
|
||||
result = append(result, dto.ArticleResponse{
|
||||
ID: article.ID,
|
||||
Title: article.Title,
|
||||
CoverImage: article.CoverImage,
|
||||
Author: article.Author,
|
||||
Heading: article.Heading,
|
||||
Content: article.Content,
|
||||
PublishedAt: utils.FormatDateToIndonesianFormat(article.PublishedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(article.UpdatedAt),
|
||||
})
|
||||
}
|
||||
|
||||
cacheData, _ := json.Marshal(result)
|
||||
config.RedisClient.Set(ctx, cacheKey, cacheData, time.Minute*5)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *ArticleService) GetArticleByID(id string) (*dto.ArticleResponse, error) {
|
||||
ctx := config.Context()
|
||||
cacheKey := "articles:" + id
|
||||
|
||||
cachedData, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil && cachedData != "" {
|
||||
var cachedArticle dto.ArticleResponse
|
||||
if err := json.Unmarshal([]byte(cachedData), &cachedArticle); err == nil {
|
||||
return &cachedArticle, nil
|
||||
}
|
||||
}
|
||||
|
||||
article, err := s.repo.GetByID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &dto.ArticleResponse{
|
||||
ID: article.ID,
|
||||
Title: article.Title,
|
||||
CoverImage: article.CoverImage,
|
||||
Author: article.Author,
|
||||
Heading: article.Heading,
|
||||
Content: article.Content,
|
||||
PublishedAt: utils.FormatDateToIndonesianFormat(article.PublishedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(article.UpdatedAt),
|
||||
}
|
||||
|
||||
cacheData, _ := json.Marshal(result)
|
||||
config.RedisClient.Set(ctx, cacheKey, cacheData, time.Minute*5)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *ArticleService) CreateArticle(request *dto.ArticleCreateRequest) (*dto.ArticleResponse, error) {
|
||||
|
||||
if err := request.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newArticle := &domain.Article{
|
||||
Title: request.Title,
|
||||
CoverImage: request.CoverImage,
|
||||
Author: request.Author,
|
||||
Heading: request.Heading,
|
||||
Content: request.Content,
|
||||
}
|
||||
|
||||
err := s.repo.Create(newArticle)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to create article")
|
||||
}
|
||||
|
||||
ctx := config.Context()
|
||||
config.RedisClient.Del(ctx, "articles:all")
|
||||
|
||||
response := &dto.ArticleResponse{
|
||||
ID: newArticle.ID,
|
||||
Title: newArticle.Title,
|
||||
CoverImage: newArticle.CoverImage,
|
||||
Author: newArticle.Author,
|
||||
Heading: newArticle.Heading,
|
||||
Content: newArticle.Content,
|
||||
PublishedAt: utils.FormatDateToIndonesianFormat(newArticle.PublishedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(newArticle.UpdatedAt),
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *ArticleService) UpdateArticle(id string, request *dto.ArticleUpdateRequest) (*dto.ArticleResponse, error) {
|
||||
|
||||
// if err := dto.GetValidator().Struct(request); err != nil {
|
||||
// return nil, errors.New("invalid input data")
|
||||
// }
|
||||
|
||||
if err := request.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
article, err := s.repo.GetByID(id)
|
||||
if err != nil {
|
||||
return nil, errors.New("article not found")
|
||||
}
|
||||
|
||||
article.Title = request.Title
|
||||
article.CoverImage = request.CoverImage
|
||||
article.Author = request.Author
|
||||
article.Heading = request.Heading
|
||||
article.Content = request.Content
|
||||
article.UpdatedAt = time.Now()
|
||||
|
||||
err = s.repo.Update(article)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to update article")
|
||||
}
|
||||
|
||||
ctx := config.Context()
|
||||
config.RedisClient.Del(ctx, "articles:all")
|
||||
config.RedisClient.Del(ctx, "articles:"+id)
|
||||
|
||||
response := &dto.ArticleResponse{
|
||||
ID: article.ID,
|
||||
Title: article.Title,
|
||||
CoverImage: article.CoverImage,
|
||||
Author: article.Author,
|
||||
Heading: article.Heading,
|
||||
Content: article.Content,
|
||||
PublishedAt: utils.FormatDateToIndonesianFormat(article.PublishedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(article.UpdatedAt),
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *ArticleService) DeleteArticle(id string) error {
|
||||
|
||||
article, err := s.repo.GetByID(id)
|
||||
if err != nil {
|
||||
return errors.New("article not found")
|
||||
}
|
||||
|
||||
err = s.repo.Delete(article)
|
||||
if err != nil {
|
||||
return errors.New("failed to delete article")
|
||||
}
|
||||
|
||||
ctx := config.Context()
|
||||
config.RedisClient.Del(ctx, "articles:all")
|
||||
config.RedisClient.Del(ctx, "articles:"+id)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,418 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type ArticleService interface {
|
||||
CreateArticle(request dto.RequestArticleDTO, coverImage *multipart.FileHeader) (*dto.ArticleResponseDTO, error)
|
||||
GetAllArticles(page, limit int) ([]dto.ArticleResponseDTO, int, error)
|
||||
GetArticleByID(id string) (*dto.ArticleResponseDTO, error)
|
||||
UpdateArticle(id string, request dto.RequestArticleDTO, coverImage *multipart.FileHeader) (*dto.ArticleResponseDTO, error)
|
||||
DeleteArticle(id string) error
|
||||
}
|
||||
|
||||
type articleService struct {
|
||||
ArticleRepo repositories.ArticleRepository
|
||||
}
|
||||
|
||||
func NewArticleService(articleRepo repositories.ArticleRepository) ArticleService {
|
||||
return &articleService{ArticleRepo: articleRepo}
|
||||
}
|
||||
|
||||
func (s *articleService) CreateArticle(request dto.RequestArticleDTO, coverImage *multipart.FileHeader) (*dto.ArticleResponseDTO, error) {
|
||||
|
||||
coverImageDir := "./public/uploads/articles"
|
||||
if err := os.MkdirAll(coverImageDir, os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("failed to create directory for cover image: %v", err)
|
||||
}
|
||||
|
||||
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true}
|
||||
extension := filepath.Ext(coverImage.Filename)
|
||||
if !allowedExtensions[extension] {
|
||||
return nil, fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
|
||||
}
|
||||
|
||||
coverImageFileName := fmt.Sprintf("%s_cover%s", uuid.New().String(), extension)
|
||||
coverImagePath := filepath.Join(coverImageDir, coverImageFileName)
|
||||
|
||||
src, err := coverImage.Open()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open uploaded file: %v", err)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
dst, err := os.Create(coverImagePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create cover image file: %v", err)
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
if _, err := dst.ReadFrom(src); err != nil {
|
||||
return nil, fmt.Errorf("failed to save cover image: %v", err)
|
||||
}
|
||||
|
||||
article := model.Article{
|
||||
Title: request.Title,
|
||||
CoverImage: coverImagePath,
|
||||
Author: request.Author,
|
||||
Heading: request.Heading,
|
||||
Content: request.Content,
|
||||
}
|
||||
|
||||
if err := s.ArticleRepo.CreateArticle(&article); err != nil {
|
||||
return nil, fmt.Errorf("failed to create article: %v", err)
|
||||
}
|
||||
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(article.PublishedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(article.UpdatedAt)
|
||||
|
||||
articleResponseDTO := &dto.ArticleResponseDTO{
|
||||
ID: article.ID,
|
||||
Title: article.Title,
|
||||
CoverImage: article.CoverImage,
|
||||
Author: article.Author,
|
||||
Heading: article.Heading,
|
||||
Content: article.Content,
|
||||
PublishedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("article:%s", article.ID)
|
||||
cacheData := map[string]interface{}{
|
||||
"data": articleResponseDTO,
|
||||
}
|
||||
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
|
||||
fmt.Printf("Error caching article to Redis: %v\n", err)
|
||||
}
|
||||
|
||||
articles, total, err := s.ArticleRepo.FindAllArticles(0, 0)
|
||||
if err != nil {
|
||||
fmt.Printf("Error fetching all articles: %v\n", err)
|
||||
}
|
||||
|
||||
var articleDTOs []dto.ArticleResponseDTO
|
||||
for _, a := range articles {
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(a.PublishedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(a.UpdatedAt)
|
||||
|
||||
articleDTOs = append(articleDTOs, dto.ArticleResponseDTO{
|
||||
ID: a.ID,
|
||||
Title: a.Title,
|
||||
CoverImage: a.CoverImage,
|
||||
Author: a.Author,
|
||||
Heading: a.Heading,
|
||||
Content: a.Content,
|
||||
PublishedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
articlesCacheKey := "articles:all"
|
||||
cacheData = map[string]interface{}{
|
||||
"data": articleDTOs,
|
||||
"total": total,
|
||||
}
|
||||
if err := utils.SetJSONData(articlesCacheKey, cacheData, time.Hour*24); err != nil {
|
||||
fmt.Printf("Error caching all articles to Redis: %v\n", err)
|
||||
}
|
||||
|
||||
return articleResponseDTO, nil
|
||||
}
|
||||
|
||||
func (s *articleService) GetAllArticles(page, limit int) ([]dto.ArticleResponseDTO, int, error) {
|
||||
var cacheKey string
|
||||
|
||||
if page == 0 && limit == 0 {
|
||||
cacheKey = "articles:all"
|
||||
cachedData, err := utils.GetJSONData(cacheKey)
|
||||
if err == nil && cachedData != nil {
|
||||
if data, ok := cachedData["data"].([]interface{}); ok {
|
||||
var articles []dto.ArticleResponseDTO
|
||||
for _, item := range data {
|
||||
articleData, ok := item.(map[string]interface{})
|
||||
if ok {
|
||||
articles = append(articles, dto.ArticleResponseDTO{
|
||||
ID: articleData["article_id"].(string),
|
||||
Title: articleData["title"].(string),
|
||||
CoverImage: articleData["coverImage"].(string),
|
||||
Author: articleData["author"].(string),
|
||||
Heading: articleData["heading"].(string),
|
||||
Content: articleData["content"].(string),
|
||||
PublishedAt: articleData["publishedAt"].(string),
|
||||
UpdatedAt: articleData["updatedAt"].(string),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if total, ok := cachedData["total"].(float64); ok {
|
||||
fmt.Printf("Cached Total Articles: %f\n", total)
|
||||
return articles, int(total), nil
|
||||
} else {
|
||||
fmt.Println("Total articles not found in cache, using 0 as fallback.")
|
||||
return articles, 0, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
articles, total, err := s.ArticleRepo.FindAllArticles(page, limit)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to fetch articles: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Total Articles from Database: %d\n", total)
|
||||
|
||||
var articleDTOs []dto.ArticleResponseDTO
|
||||
for _, article := range articles {
|
||||
publishedAt, _ := utils.FormatDateToIndonesianFormat(article.PublishedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(article.UpdatedAt)
|
||||
|
||||
articleDTOs = append(articleDTOs, dto.ArticleResponseDTO{
|
||||
ID: article.ID,
|
||||
Title: article.Title,
|
||||
CoverImage: article.CoverImage,
|
||||
Author: article.Author,
|
||||
Heading: article.Heading,
|
||||
Content: article.Content,
|
||||
PublishedAt: publishedAt,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
cacheKey = fmt.Sprintf("articles_page:%d_limit:%d", page, limit)
|
||||
cacheData := map[string]interface{}{
|
||||
"data": articleDTOs,
|
||||
"total": total,
|
||||
}
|
||||
|
||||
fmt.Printf("Setting cache with total: %d\n", total)
|
||||
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
|
||||
fmt.Printf("Error caching articles to Redis: %v\n", err)
|
||||
}
|
||||
|
||||
return articleDTOs, total, nil
|
||||
}
|
||||
|
||||
func (s *articleService) GetArticleByID(id string) (*dto.ArticleResponseDTO, error) {
|
||||
|
||||
cacheKey := fmt.Sprintf("article:%s", id)
|
||||
cachedData, err := utils.GetJSONData(cacheKey)
|
||||
if err == nil && cachedData != nil {
|
||||
articleResponse := &dto.ArticleResponseDTO{}
|
||||
if data, ok := cachedData["data"].(string); ok {
|
||||
if err := json.Unmarshal([]byte(data), articleResponse); err == nil {
|
||||
return articleResponse, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
article, err := s.ArticleRepo.FindArticleByID(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch article by ID: %v", err)
|
||||
}
|
||||
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(article.PublishedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(article.UpdatedAt)
|
||||
|
||||
articleResponseDTO := &dto.ArticleResponseDTO{
|
||||
ID: article.ID,
|
||||
Title: article.Title,
|
||||
CoverImage: article.CoverImage,
|
||||
Author: article.Author,
|
||||
Heading: article.Heading,
|
||||
Content: article.Content,
|
||||
PublishedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
|
||||
cacheData := map[string]interface{}{
|
||||
"data": articleResponseDTO,
|
||||
}
|
||||
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
|
||||
fmt.Printf("Error caching article to Redis: %v\n", err)
|
||||
}
|
||||
|
||||
return articleResponseDTO, nil
|
||||
}
|
||||
|
||||
func (s *articleService) UpdateArticle(id string, request dto.RequestArticleDTO, coverImage *multipart.FileHeader) (*dto.ArticleResponseDTO, error) {
|
||||
article, err := s.ArticleRepo.FindArticleByID(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("article not found: %v", id)
|
||||
}
|
||||
|
||||
article.Title = request.Title
|
||||
article.Heading = request.Heading
|
||||
article.Content = request.Content
|
||||
article.Author = request.Author
|
||||
|
||||
var coverImagePath string
|
||||
if coverImage != nil {
|
||||
|
||||
coverImagePath, err = s.saveCoverImage(coverImage, article.CoverImage)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save cover image: %v", err)
|
||||
}
|
||||
article.CoverImage = coverImagePath
|
||||
}
|
||||
|
||||
err = s.ArticleRepo.UpdateArticle(id, article)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update article: %v", err)
|
||||
}
|
||||
|
||||
updatedArticle, err := s.ArticleRepo.FindArticleByID(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch updated article: %v", err)
|
||||
}
|
||||
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(updatedArticle.PublishedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(updatedArticle.UpdatedAt)
|
||||
|
||||
articleResponseDTO := &dto.ArticleResponseDTO{
|
||||
ID: updatedArticle.ID,
|
||||
Title: updatedArticle.Title,
|
||||
CoverImage: updatedArticle.CoverImage,
|
||||
Author: updatedArticle.Author,
|
||||
Heading: updatedArticle.Heading,
|
||||
Content: updatedArticle.Content,
|
||||
PublishedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
|
||||
articleCacheKey := fmt.Sprintf("article:%s", updatedArticle.ID)
|
||||
err = utils.SetJSONData(articleCacheKey, map[string]interface{}{"data": articleResponseDTO}, time.Hour*24)
|
||||
if err != nil {
|
||||
fmt.Printf("Error caching updated article to Redis: %v\n", err)
|
||||
}
|
||||
|
||||
articlesCacheKey := "articles:all"
|
||||
err = utils.DeleteData(articlesCacheKey)
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting articles cache: %v\n", err)
|
||||
}
|
||||
|
||||
articles, _, err := s.ArticleRepo.FindAllArticles(0, 0)
|
||||
if err != nil {
|
||||
fmt.Printf("Error fetching all articles: %v\n", err)
|
||||
} else {
|
||||
var articleDTOs []dto.ArticleResponseDTO
|
||||
for _, a := range articles {
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(a.PublishedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(a.UpdatedAt)
|
||||
|
||||
articleDTOs = append(articleDTOs, dto.ArticleResponseDTO{
|
||||
ID: a.ID,
|
||||
Title: a.Title,
|
||||
CoverImage: a.CoverImage,
|
||||
Author: a.Author,
|
||||
Heading: a.Heading,
|
||||
Content: a.Content,
|
||||
PublishedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
cacheData := map[string]interface{}{
|
||||
"data": articleDTOs,
|
||||
}
|
||||
err = utils.SetJSONData(articlesCacheKey, cacheData, time.Hour*24)
|
||||
if err != nil {
|
||||
fmt.Printf("Error caching updated articles to Redis: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
return articleResponseDTO, nil
|
||||
}
|
||||
|
||||
func (s *articleService) saveCoverImage(coverImage *multipart.FileHeader, oldImagePath string) (string, error) {
|
||||
coverImageDir := "./public/uploads/articles"
|
||||
if _, err := os.Stat(coverImageDir); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(coverImageDir, os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("failed to create directory for cover image: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
extension := filepath.Ext(coverImage.Filename)
|
||||
if extension != ".jpg" && extension != ".jpeg" && extension != ".png" {
|
||||
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
|
||||
}
|
||||
|
||||
coverImageFileName := fmt.Sprintf("%s_cover%s", uuid.New().String(), extension)
|
||||
coverImagePath := filepath.Join(coverImageDir, coverImageFileName)
|
||||
|
||||
if oldImagePath != "" {
|
||||
err := os.Remove(oldImagePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to delete old cover image: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Successfully deleted old cover image: %s\n", oldImagePath)
|
||||
}
|
||||
}
|
||||
|
||||
src, err := coverImage.Open()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to open uploaded file: %v", err)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
dst, err := os.Create(coverImagePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create cover image file: %v", err)
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
_, err = dst.ReadFrom(src)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to save cover image: %v", err)
|
||||
}
|
||||
|
||||
return coverImagePath, nil
|
||||
}
|
||||
|
||||
func (s *articleService) DeleteArticle(id string) error {
|
||||
article, err := s.ArticleRepo.FindArticleByID(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find article: %v", id)
|
||||
}
|
||||
|
||||
if article.CoverImage != "" {
|
||||
err := os.Remove(article.CoverImage)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to delete cover image: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Successfully deleted cover image: %s\n", article.CoverImage)
|
||||
}
|
||||
}
|
||||
|
||||
err = s.ArticleRepo.DeleteArticle(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete article: %v", err)
|
||||
}
|
||||
|
||||
articleCacheKey := fmt.Sprintf("article:%s", id)
|
||||
err = utils.DeleteData(articleCacheKey)
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting cache for article: %v\n", err)
|
||||
}
|
||||
|
||||
articlesCacheKey := "articles:all"
|
||||
err = utils.DeleteData(articlesCacheKey)
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting cache for all articles: %v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func RegisterUser(username, name, email, phone, password, confirmPassword, roleId string) error {
|
||||
if password != confirmPassword {
|
||||
return errors.New("password dan confirm password tidak cocok")
|
||||
}
|
||||
|
||||
if repositories.IsEmailExist(email, roleId) {
|
||||
return errors.New("email is already registered with the same role")
|
||||
}
|
||||
|
||||
if repositories.IsUsernameExist(username, roleId) {
|
||||
return errors.New("username is already registered with the same role")
|
||||
}
|
||||
|
||||
if repositories.IsPhoneExist(phone, roleId) {
|
||||
return errors.New("phone number is already registered with the same role")
|
||||
}
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return errors.New("failed to hash password")
|
||||
}
|
||||
|
||||
err = repositories.CreateUser(username, name, email, phone, string(hashedPassword), roleId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoginUser(identifier, password string) (string, error) {
|
||||
if identifier == "" || password == "" {
|
||||
return "", errors.New("email/username/phone and password must be provided")
|
||||
}
|
||||
|
||||
const roleId = ""
|
||||
user, err := repositories.GetUserByEmailUsernameOrPhone(identifier, roleId)
|
||||
if err != nil {
|
||||
return "", errors.New("invalid email/username/phone or password")
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
|
||||
if err != nil {
|
||||
return "", errors.New("invalid email/username/phone or password")
|
||||
}
|
||||
|
||||
token := generateJWT(user.ID, user.RoleID)
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func generateJWT(userID, role string) string {
|
||||
claims := jwt.MapClaims{
|
||||
"sub": userID,
|
||||
"role": role,
|
||||
"exp": time.Now().Add(time.Hour * 24 * 7).Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
t, err := token.SignedString([]byte(os.Getenv("API_KEY")))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func GetUserByID(userID string) (domain.User, error) {
|
||||
user, err := repositories.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return user, errors.New("user not found")
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func UpdateUser(userID, email, username, name, phone string) error {
|
||||
user, err := repositories.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
|
||||
if email != "" && email != user.Email {
|
||||
if repositories.IsEmailExist(email, user.RoleID) {
|
||||
return errors.New("email is already registered with the same role")
|
||||
}
|
||||
user.Email = email
|
||||
}
|
||||
|
||||
if username != "" && username != user.Username {
|
||||
if repositories.IsUsernameExist(username, user.RoleID) {
|
||||
return errors.New("username is already registered with the same role")
|
||||
}
|
||||
user.Username = username
|
||||
}
|
||||
|
||||
if phone != "" && phone != user.Phone {
|
||||
if repositories.IsPhoneExist(phone, user.RoleID) {
|
||||
return errors.New("phone number is already registered with the same role")
|
||||
}
|
||||
user.Phone = phone
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
user.Name = name
|
||||
}
|
||||
|
||||
err = repositories.UpdateUser(&user)
|
||||
if err != nil {
|
||||
return errors.New("failed to update user")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdatePassword(userID, oldPassword, newPassword string) error {
|
||||
user, err := repositories.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(oldPassword))
|
||||
if err != nil {
|
||||
return errors.New("old password is incorrect")
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(newPassword))
|
||||
if err == nil {
|
||||
return errors.New("new password cannot be the same as the old password")
|
||||
}
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return errors.New("failed to hash new password")
|
||||
}
|
||||
|
||||
err = repositories.UpdateUserPassword(userID, string(hashedPassword))
|
||||
if err != nil {
|
||||
return errors.New("failed to update password")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
const (
|
||||
ErrUsernameTaken = "username is already taken"
|
||||
ErrPhoneTaken = "phone number is already used for this role"
|
||||
ErrEmailTaken = "email is already used for this role"
|
||||
ErrInvalidRoleID = "invalid roleId"
|
||||
ErrPasswordMismatch = "password and confirm password do not match"
|
||||
ErrRoleIDRequired = "roleId is required"
|
||||
ErrFailedToHashPassword = "failed to hash password"
|
||||
ErrFailedToCreateUser = "failed to create user"
|
||||
ErrIncorrectPassword = "incorrect password"
|
||||
ErrAccountNotFound = "account not found"
|
||||
)
|
||||
|
||||
type UserService interface {
|
||||
Login(credentials dto.LoginDTO) (*dto.UserResponseWithToken, error)
|
||||
Register(user dto.RegisterDTO) (*dto.UserResponseDTO, error)
|
||||
}
|
||||
|
||||
type userService struct {
|
||||
UserRepo repositories.UserRepository
|
||||
RoleRepo repositories.RoleRepository
|
||||
SecretKey string
|
||||
}
|
||||
|
||||
func NewUserService(userRepo repositories.UserRepository, roleRepo repositories.RoleRepository, secretKey string) UserService {
|
||||
return &userService{UserRepo: userRepo, RoleRepo: roleRepo, SecretKey: secretKey}
|
||||
}
|
||||
|
||||
func (s *userService) Login(credentials dto.LoginDTO) (*dto.UserResponseWithToken, error) {
|
||||
if credentials.RoleID == "" {
|
||||
return nil, errors.New(ErrRoleIDRequired)
|
||||
}
|
||||
|
||||
user, err := s.UserRepo.FindByIdentifierAndRole(credentials.Identifier, credentials.RoleID)
|
||||
if err != nil {
|
||||
return nil, errors.New(ErrAccountNotFound)
|
||||
}
|
||||
|
||||
if !CheckPasswordHash(credentials.Password, user.Password) {
|
||||
return nil, errors.New(ErrIncorrectPassword)
|
||||
}
|
||||
|
||||
token, err := s.generateJWT(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sessionKey := fmt.Sprintf("session:%s", user.ID)
|
||||
sessionData := map[string]interface{}{
|
||||
"userID": user.ID,
|
||||
"roleID": user.RoleID,
|
||||
"roleName": user.Role.RoleName,
|
||||
}
|
||||
|
||||
err = utils.SetJSONData(sessionKey, sessionData, time.Hour*24)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.UserResponseWithToken{
|
||||
RoleName: user.Role.RoleName,
|
||||
UserID: user.ID,
|
||||
Token: token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *userService) generateJWT(user *model.User) (string, error) {
|
||||
claims := jwt.MapClaims{
|
||||
"sub": user.ID,
|
||||
"iat": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
tokenString, err := token.SignedString([]byte(s.SecretKey))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
func CheckPasswordHash(password, hashedPassword string) bool {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (s *userService) Register(user dto.RegisterDTO) (*dto.UserResponseDTO, error) {
|
||||
|
||||
if user.Password != user.ConfirmPassword {
|
||||
return nil, fmt.Errorf("%s", ErrPasswordMismatch)
|
||||
}
|
||||
|
||||
if user.RoleID == "" {
|
||||
return nil, fmt.Errorf("%s", ErrRoleIDRequired)
|
||||
}
|
||||
|
||||
role, err := s.RoleRepo.FindByID(user.RoleID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %v", ErrInvalidRoleID, err)
|
||||
}
|
||||
|
||||
if existingUser, _ := s.UserRepo.FindByUsername(user.Username); existingUser != nil {
|
||||
return nil, fmt.Errorf("%s", ErrUsernameTaken)
|
||||
}
|
||||
|
||||
if existingPhone, _ := s.UserRepo.FindByPhoneAndRole(user.Phone, user.RoleID); existingPhone != nil {
|
||||
return nil, fmt.Errorf("%s", ErrPhoneTaken)
|
||||
}
|
||||
|
||||
if existingEmail, _ := s.UserRepo.FindByEmailAndRole(user.Email, user.RoleID); existingEmail != nil {
|
||||
return nil, fmt.Errorf("%s", ErrEmailTaken)
|
||||
}
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %v", ErrFailedToHashPassword, err)
|
||||
}
|
||||
|
||||
newUser := model.User{
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Phone: user.Phone,
|
||||
Email: user.Email,
|
||||
Password: string(hashedPassword),
|
||||
RoleID: user.RoleID,
|
||||
}
|
||||
|
||||
err = s.UserRepo.Create(&newUser)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %v", ErrFailedToCreateUser, err)
|
||||
}
|
||||
|
||||
userResponse := s.prepareUserResponse(newUser, role)
|
||||
|
||||
return userResponse, nil
|
||||
}
|
||||
|
||||
func (s *userService) prepareUserResponse(user model.User, role *model.Role) *dto.UserResponseDTO {
|
||||
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(user.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(user.UpdatedAt)
|
||||
|
||||
return &dto.UserResponseDTO{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Phone: user.Phone,
|
||||
Email: user.Email,
|
||||
EmailVerified: user.EmailVerified,
|
||||
RoleName: role.RoleName,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
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"
|
||||
)
|
||||
|
||||
type BannerService struct {
|
||||
repo *repositories.BannerRepository
|
||||
}
|
||||
|
||||
func NewBannerService(repo *repositories.BannerRepository) *BannerService {
|
||||
return &BannerService{repo: repo}
|
||||
}
|
||||
|
||||
func (s *BannerService) GetAllBanners() ([]dto.BannerResponse, error) {
|
||||
ctx := config.Context()
|
||||
cacheKey := "banners:all"
|
||||
|
||||
cachedData, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil && cachedData != "" {
|
||||
var cachedBanners []dto.BannerResponse
|
||||
if err := json.Unmarshal([]byte(cachedData), &cachedBanners); err == nil {
|
||||
return cachedBanners, nil
|
||||
}
|
||||
}
|
||||
|
||||
banners, err := s.repo.GetAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []dto.BannerResponse
|
||||
for _, banner := range banners {
|
||||
result = append(result, dto.BannerResponse{
|
||||
ID: banner.ID,
|
||||
BannerName: banner.BannerName,
|
||||
BannerImage: banner.BannerImage,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(banner.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(banner.UpdatedAt),
|
||||
})
|
||||
}
|
||||
|
||||
cacheData, _ := json.Marshal(result)
|
||||
config.RedisClient.Set(ctx, cacheKey, cacheData, time.Minute*5)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *BannerService) GetBannerByID(id string) (*dto.BannerResponse, error) {
|
||||
ctx := config.Context()
|
||||
cacheKey := "banners:" + id
|
||||
|
||||
cachedData, err := config.RedisClient.Get(ctx, cacheKey).Result()
|
||||
if err == nil && cachedData != "" {
|
||||
var cachedBanner dto.BannerResponse
|
||||
if err := json.Unmarshal([]byte(cachedData), &cachedBanner); err == nil {
|
||||
return &cachedBanner, nil
|
||||
}
|
||||
}
|
||||
|
||||
banner, err := s.repo.GetByID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &dto.BannerResponse{
|
||||
ID: banner.ID,
|
||||
BannerName: banner.BannerName,
|
||||
BannerImage: banner.BannerImage,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(banner.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(banner.UpdatedAt),
|
||||
}
|
||||
|
||||
cacheData, _ := json.Marshal(result)
|
||||
config.RedisClient.Set(ctx, cacheKey, cacheData, time.Minute*5)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *BannerService) CreateBanner(request *dto.BannerCreateRequest) (*dto.BannerResponse, error) {
|
||||
|
||||
if request.BannerName == "" || request.BannerImage == "" {
|
||||
return nil, errors.New("invalid input data")
|
||||
}
|
||||
|
||||
newBanner := &domain.Banner{
|
||||
BannerName: request.BannerName,
|
||||
BannerImage: request.BannerImage,
|
||||
}
|
||||
|
||||
err := s.repo.Create(newBanner)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to create banner")
|
||||
}
|
||||
|
||||
ctx := config.Context()
|
||||
config.RedisClient.Del(ctx, "banners:all")
|
||||
|
||||
response := &dto.BannerResponse{
|
||||
ID: newBanner.ID,
|
||||
BannerName: newBanner.BannerName,
|
||||
BannerImage: newBanner.BannerImage,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(newBanner.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(newBanner.UpdatedAt),
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *BannerService) UpdateBanner(id string, request *dto.BannerUpdateRequest) (*dto.BannerResponse, error) {
|
||||
|
||||
banner, err := s.repo.GetByID(id)
|
||||
if err != nil {
|
||||
return nil, errors.New("banner not found")
|
||||
}
|
||||
|
||||
if request.BannerName != nil && *request.BannerName != "" {
|
||||
banner.BannerName = *request.BannerName
|
||||
}
|
||||
if request.BannerImage != nil && *request.BannerImage != "" {
|
||||
banner.BannerImage = *request.BannerImage
|
||||
}
|
||||
banner.UpdatedAt = time.Now()
|
||||
|
||||
err = s.repo.Update(banner)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to update banner")
|
||||
}
|
||||
|
||||
ctx := config.Context()
|
||||
config.RedisClient.Del(ctx, "banners:all")
|
||||
config.RedisClient.Del(ctx, "banners:"+id)
|
||||
|
||||
response := &dto.BannerResponse{
|
||||
ID: banner.ID,
|
||||
BannerName: banner.BannerName,
|
||||
BannerImage: banner.BannerImage,
|
||||
CreatedAt: utils.FormatDateToIndonesianFormat(banner.CreatedAt),
|
||||
UpdatedAt: utils.FormatDateToIndonesianFormat(banner.UpdatedAt),
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *BannerService) DeleteBanner(id string) error {
|
||||
|
||||
banner, err := s.repo.GetByID(id)
|
||||
if err != nil {
|
||||
return errors.New("banner not found")
|
||||
}
|
||||
|
||||
err = s.repo.Delete(banner)
|
||||
if err != nil {
|
||||
return errors.New("failed to delete banner")
|
||||
}
|
||||
|
||||
ctx := config.Context()
|
||||
config.RedisClient.Del(ctx, "banners:all")
|
||||
config.RedisClient.Del(ctx, "banners:"+id)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,365 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type BannerService interface {
|
||||
CreateBanner(request dto.RequestBannerDTO, bannerImage *multipart.FileHeader) (*dto.ResponseBannerDTO, error)
|
||||
GetAllBanners() ([]dto.ResponseBannerDTO, error)
|
||||
GetBannerByID(id string) (*dto.ResponseBannerDTO, error)
|
||||
UpdateBanner(id string, request dto.RequestBannerDTO, bannerImage *multipart.FileHeader) (*dto.ResponseBannerDTO, error)
|
||||
DeleteBanner(id string) error
|
||||
}
|
||||
|
||||
type bannerService struct {
|
||||
BannerRepo repositories.BannerRepository
|
||||
}
|
||||
|
||||
func NewBannerService(bannerRepo repositories.BannerRepository) BannerService {
|
||||
return &bannerService{BannerRepo: bannerRepo}
|
||||
}
|
||||
|
||||
func (s *bannerService) saveBannerImage(bannerImage *multipart.FileHeader) (string, error) {
|
||||
bannerImageDir := "./public/uploads/banners"
|
||||
if _, err := os.Stat(bannerImageDir); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(bannerImageDir, os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("failed to create directory for banner image: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true}
|
||||
extension := filepath.Ext(bannerImage.Filename)
|
||||
if !allowedExtensions[extension] {
|
||||
return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed")
|
||||
}
|
||||
|
||||
bannerImageFileName := fmt.Sprintf("%s_banner%s", uuid.New().String(), extension)
|
||||
bannerImagePath := filepath.Join(bannerImageDir, bannerImageFileName)
|
||||
|
||||
src, err := bannerImage.Open()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to open uploaded file: %v", err)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
dst, err := os.Create(bannerImagePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create banner image file: %v", err)
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
if _, err := dst.ReadFrom(src); err != nil {
|
||||
return "", fmt.Errorf("failed to save banner image: %v", err)
|
||||
}
|
||||
|
||||
return bannerImagePath, nil
|
||||
}
|
||||
|
||||
func (s *bannerService) CreateBanner(request dto.RequestBannerDTO, bannerImage *multipart.FileHeader) (*dto.ResponseBannerDTO, error) {
|
||||
|
||||
errors, valid := request.ValidateBannerInput()
|
||||
if !valid {
|
||||
return nil, fmt.Errorf("validation error: %v", errors)
|
||||
}
|
||||
|
||||
bannerImagePath, err := s.saveBannerImage(bannerImage)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save banner image: %v", err)
|
||||
}
|
||||
|
||||
banner := model.Banner{
|
||||
BannerName: request.BannerName,
|
||||
BannerImage: bannerImagePath,
|
||||
}
|
||||
|
||||
if err := s.BannerRepo.CreateBanner(&banner); err != nil {
|
||||
return nil, fmt.Errorf("failed to create banner: %v", err)
|
||||
}
|
||||
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(banner.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(banner.UpdatedAt)
|
||||
|
||||
bannerResponseDTO := &dto.ResponseBannerDTO{
|
||||
ID: banner.ID,
|
||||
BannerName: banner.BannerName,
|
||||
BannerImage: banner.BannerImage,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
|
||||
articlesCacheKey := "banners:all"
|
||||
err = utils.DeleteData(articlesCacheKey)
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting cache for all banners: %v\n", err)
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("banner:%s", banner.ID)
|
||||
cacheData := map[string]interface{}{
|
||||
"data": bannerResponseDTO,
|
||||
}
|
||||
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
|
||||
fmt.Printf("Error caching banner: %v\n", err)
|
||||
}
|
||||
|
||||
banners, err := s.BannerRepo.FindAllBanners()
|
||||
if err == nil {
|
||||
var bannersDTO []dto.ResponseBannerDTO
|
||||
for _, b := range banners {
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(b.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(b.UpdatedAt)
|
||||
|
||||
bannersDTO = append(bannersDTO, dto.ResponseBannerDTO{
|
||||
ID: b.ID,
|
||||
BannerName: b.BannerName,
|
||||
BannerImage: b.BannerImage,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
cacheData = map[string]interface{}{
|
||||
"data": bannersDTO,
|
||||
}
|
||||
if err := utils.SetJSONData(articlesCacheKey, cacheData, time.Hour*24); err != nil {
|
||||
fmt.Printf("Error caching updated banners to Redis: %v\n", err)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Error fetching all banners: %v\n", err)
|
||||
}
|
||||
|
||||
return bannerResponseDTO, nil
|
||||
}
|
||||
|
||||
func (s *bannerService) GetAllBanners() ([]dto.ResponseBannerDTO, error) {
|
||||
var banners []dto.ResponseBannerDTO
|
||||
|
||||
cacheKey := "banners:all"
|
||||
cachedData, err := utils.GetJSONData(cacheKey)
|
||||
if err == nil && cachedData != nil {
|
||||
|
||||
if data, ok := cachedData["data"].([]interface{}); ok {
|
||||
for _, item := range data {
|
||||
if bannerData, ok := item.(map[string]interface{}); ok {
|
||||
banners = append(banners, dto.ResponseBannerDTO{
|
||||
ID: bannerData["id"].(string),
|
||||
BannerName: bannerData["bannername"].(string),
|
||||
BannerImage: bannerData["bannerimage"].(string),
|
||||
CreatedAt: bannerData["createdAt"].(string),
|
||||
UpdatedAt: bannerData["updatedAt"].(string),
|
||||
})
|
||||
}
|
||||
}
|
||||
return banners, nil
|
||||
}
|
||||
}
|
||||
|
||||
records, err := s.BannerRepo.FindAllBanners()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch banners: %v", err)
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(record.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(record.UpdatedAt)
|
||||
|
||||
banners = append(banners, dto.ResponseBannerDTO{
|
||||
ID: record.ID,
|
||||
BannerName: record.BannerName,
|
||||
BannerImage: record.BannerImage,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
cacheData := map[string]interface{}{
|
||||
"data": banners,
|
||||
}
|
||||
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
|
||||
fmt.Printf("Error caching banners: %v\n", err)
|
||||
}
|
||||
|
||||
return banners, nil
|
||||
}
|
||||
|
||||
func (s *bannerService) GetBannerByID(id string) (*dto.ResponseBannerDTO, error) {
|
||||
|
||||
cacheKey := fmt.Sprintf("banner:%s", id)
|
||||
cachedData, err := utils.GetJSONData(cacheKey)
|
||||
if err == nil && cachedData != nil {
|
||||
if data, ok := cachedData["data"].(map[string]interface{}); ok {
|
||||
return &dto.ResponseBannerDTO{
|
||||
ID: data["id"].(string),
|
||||
BannerName: data["bannername"].(string),
|
||||
BannerImage: data["bannerimage"].(string),
|
||||
CreatedAt: data["createdAt"].(string),
|
||||
UpdatedAt: data["updatedAt"].(string),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
banner, err := s.BannerRepo.FindBannerByID(id)
|
||||
if err != nil {
|
||||
|
||||
return nil, fmt.Errorf("banner with ID %s not found", id)
|
||||
}
|
||||
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(banner.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(banner.UpdatedAt)
|
||||
|
||||
bannerResponseDTO := &dto.ResponseBannerDTO{
|
||||
ID: banner.ID,
|
||||
BannerName: banner.BannerName,
|
||||
BannerImage: banner.BannerImage,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
|
||||
cacheData := map[string]interface{}{
|
||||
"data": bannerResponseDTO,
|
||||
}
|
||||
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
|
||||
fmt.Printf("Error caching banner: %v\n", err)
|
||||
}
|
||||
|
||||
return bannerResponseDTO, nil
|
||||
}
|
||||
|
||||
func (s *bannerService) UpdateBanner(id string, request dto.RequestBannerDTO, bannerImage *multipart.FileHeader) (*dto.ResponseBannerDTO, error) {
|
||||
|
||||
banner, err := s.BannerRepo.FindBannerByID(id)
|
||||
if err != nil {
|
||||
|
||||
return nil, fmt.Errorf("banner with ID %s not found", id)
|
||||
}
|
||||
|
||||
var oldImagePath string
|
||||
if bannerImage != nil {
|
||||
|
||||
bannerImagePath, err := s.saveBannerImage(bannerImage)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save banner image: %v", err)
|
||||
}
|
||||
|
||||
oldImagePath = banner.BannerImage
|
||||
banner.BannerImage = bannerImagePath
|
||||
}
|
||||
|
||||
banner.BannerName = request.BannerName
|
||||
|
||||
if err := s.BannerRepo.UpdateBanner(id, banner); err != nil {
|
||||
return nil, fmt.Errorf("failed to update banner: %v", err)
|
||||
}
|
||||
|
||||
if oldImagePath != "" {
|
||||
err := os.Remove(oldImagePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to delete old banner image: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(banner.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(banner.UpdatedAt)
|
||||
|
||||
bannerResponseDTO := &dto.ResponseBannerDTO{
|
||||
ID: banner.ID,
|
||||
BannerName: banner.BannerName,
|
||||
BannerImage: banner.BannerImage,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("banner:%s", id)
|
||||
err = utils.DeleteData(cacheKey)
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting cache for banner: %v\n", err)
|
||||
}
|
||||
|
||||
cacheData := map[string]interface{}{
|
||||
"data": bannerResponseDTO,
|
||||
}
|
||||
if err := utils.SetJSONData(cacheKey, cacheData, time.Hour*24); err != nil {
|
||||
fmt.Printf("Error caching updated banner: %v\n", err)
|
||||
}
|
||||
|
||||
articlesCacheKey := "banners:all"
|
||||
err = utils.DeleteData(articlesCacheKey)
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting cache for all banners: %v\n", err)
|
||||
}
|
||||
|
||||
banners, err := s.BannerRepo.FindAllBanners()
|
||||
if err == nil {
|
||||
var bannersDTO []dto.ResponseBannerDTO
|
||||
for _, b := range banners {
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(b.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(b.UpdatedAt)
|
||||
|
||||
bannersDTO = append(bannersDTO, dto.ResponseBannerDTO{
|
||||
ID: b.ID,
|
||||
BannerName: b.BannerName,
|
||||
BannerImage: b.BannerImage,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
cacheData = map[string]interface{}{
|
||||
"data": bannersDTO,
|
||||
}
|
||||
if err := utils.SetJSONData(articlesCacheKey, cacheData, time.Hour*24); err != nil {
|
||||
fmt.Printf("Error caching updated banners to Redis: %v\n", err)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Error fetching all banners: %v\n", err)
|
||||
}
|
||||
|
||||
return bannerResponseDTO, nil
|
||||
}
|
||||
|
||||
func (s *bannerService) DeleteBanner(id string) error {
|
||||
|
||||
banner, err := s.BannerRepo.FindBannerByID(id)
|
||||
if err != nil {
|
||||
|
||||
return fmt.Errorf("banner with ID %s not found", id)
|
||||
}
|
||||
|
||||
if banner.BannerImage != "" {
|
||||
err := os.Remove(banner.BannerImage)
|
||||
if err != nil {
|
||||
|
||||
fmt.Printf("Failed to delete banner image: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Successfully deleted banner image: %s\n", banner.BannerImage)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.BannerRepo.DeleteBanner(id); err != nil {
|
||||
return fmt.Errorf("failed to delete banner from database: %v", err)
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("banner:%s", banner.ID)
|
||||
err = utils.DeleteData(cacheKey)
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting cache for banner: %v\n", err)
|
||||
}
|
||||
|
||||
articlesCacheKey := "banners:all"
|
||||
err = utils.DeleteData(articlesCacheKey)
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting cache for all banners: %v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue