fix and refact: architecture updated and refactored

This commit is contained in:
pahmiudahgede 2025-01-28 23:16:33 +07:00
parent aa786d646b
commit 822caa8121
91 changed files with 455 additions and 6871 deletions

View File

@ -1,13 +1,19 @@
# SERVER SETTINGS # SERVER SETTINGS
SERVER_HOST=localhost SERVER_HOST=
SERVER_PORT= # isi listen port anda (bebas) SERVER_PORT=
# DATABASE SETTINGS # DATABASE SETTINGS
DB_HOST=localhost DB_HOST=
DB_PORT=5432 # port default postgres DB_PORT=
DB_NAME= # nama_database di postgres DB_NAME=
DB_USER= # username yang digunakan di postgres DB_USER=
DB_PASSWORD= # password yang digunakan di postgres DB_PASSWORD=
# api keyauth # REDIS SETTINGS
REDIS_HOST=
REDIS_PORT=
REDIS_PASSWORD=
REDIS_DB=
# Keyauth
API_KEY= API_KEY=

2
.gitignore vendored
View File

@ -23,3 +23,5 @@ go.work.sum
# env file # env file
.env .env
.env.prod
.env.dev

17
cmd/main.go Normal file
View File

@ -0,0 +1,17 @@
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/pahmiudahgede/senggoldong/config"
"github.com/pahmiudahgede/senggoldong/middleware"
"github.com/pahmiudahgede/senggoldong/presentation"
)
func main() {
config.SetupConfig()
app := fiber.New()
app.Use(middleware.APIKeyMiddleware)
presentation.AuthRouter(app)
config.StartServer(app)
}

View File

@ -3,54 +3,36 @@ package config
import ( import (
"fmt" "fmt"
"log" "log"
"os"
"github.com/pahmiudahgede/senggoldong/domain" "github.com/pahmiudahgede/senggoldong/model"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/gorm" "gorm.io/gorm"
) )
var DB *gorm.DB var DB *gorm.DB
func InitDatabase() { func ConnectDatabase() {
InitConfig()
dsn := fmt.Sprintf( dsn := fmt.Sprintf(
"host=%s port=%s user=%s dbname=%s password=%s sslmode=disable", "host=%s user=%s password=%s dbname=%s port=%s sslmode=disable",
DBHost, DBPort, DBUser, DBName, DBPassword, os.Getenv("DB_HOST"),
os.Getenv("DB_USER"),
os.Getenv("DB_PASSWORD"),
os.Getenv("DB_NAME"),
os.Getenv("DB_PORT"),
) )
var err error var err error
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil { 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( err = DB.AutoMigrate(&model.User{})
&domain.Point{},
&domain.User{},
&domain.UserRole{},
&domain.UserPin{},
&domain.MenuAccess{},
&domain.PlatformHandle{},
&domain.Address{},
&domain.Article{},
&domain.TrashCategory{},
&domain.TrashDetail{},
&domain.Banner{},
&domain.CoverageArea{},
&domain.CoverageDistric{},
&domain.CoverageSubdistrict{},
&domain.RequestPickup{},
&domain.RequestItem{},
&domain.Product{},
&domain.ProductImage{},
&domain.Store{},
)
if err != nil { if err != nil {
log.Fatalf("Error: Gagal melakukan migrasi schema: %v", err) log.Fatalf("Error performing auto-migration: %v", err)
} }
log.Println("Database migrated successfully!")
log.Println("Koneksi ke database berhasil dan migrasi schema juga berhasil")
} }

View File

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

View File

@ -4,58 +4,24 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"time" "os"
"github.com/go-redis/redis/v8" "github.com/go-redis/redis/v8"
) )
var RedisClient *redis.Client var RedisClient *redis.Client
var Ctx = context.Background()
func Context() context.Context { func ConnectRedis() {
return context.Background()
}
func InitRedis() {
InitConfig()
RedisClient = redis.NewClient(&redis.Options{ RedisClient = redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%s", RedisHost, RedisPort), Addr: fmt.Sprintf("%s:%s", os.Getenv("REDIS_HOST"), os.Getenv("REDIS_PORT")),
Password: RedisPassword, Password: os.Getenv("REDIS_PASSWORD"),
DB: RedisDB, DB: 0,
}) })
_, err := RedisClient.Ping(context.Background()).Result() _, err := RedisClient.Ping(Ctx).Result()
if err != nil { if err != nil {
log.Fatalf("Error: Gagal terhubung ke Redis: %v", err) log.Fatalf("Error connecting to Redis: %v", err)
} }
log.Println("Redis connected successfully!")
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
} }

21
config/server.go Normal file
View File

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

17
config/setup.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +0,0 @@
package domain
import "time"
type Banner struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
BannerName string `gorm:"not null" json:"bannername"`
BannerImage string `gorm:"not null" json:"bannerimage"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
}

View File

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

View File

@ -1,11 +0,0 @@
package domain
import "time"
type Point struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
CoinName string `gorm:"not null" json:"coin_name"`
ValuePerUnit float64 `gorm:"not null" json:"value_perunit"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"`
// }

View File

@ -1,20 +0,0 @@
package domain
import "time"
type TrashCategory struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
Name string `gorm:"not null" json:"name"`
Details []TrashDetail `gorm:"foreignKey:CategoryID" json:"details"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
}
type TrashDetail struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"`
CategoryID string `gorm:"type:uuid;not null" json:"category_id"`
Description string `gorm:"not null" json:"description"`
Price int `gorm:"not null" json:"price"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
}

View File

@ -1,11 +0,0 @@
package domain
import "time"
type UserPin struct {
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
UserID string `gorm:"not null" json:"userId"`
Pin string `gorm:"not null" json:"pin"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
}

View File

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

View File

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

11
dto/auth_dto.go Normal file
View File

@ -0,0 +1,11 @@
package dto
type LoginDTO struct {
Identifier string `json:"identifier" validate:"required"`
Password string `json:"password" validate:"required,min=6"`
}
type UserResponseWithToken struct {
UserID string `json:"user_id"`
Token string `json:"token"`
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +0,0 @@
package dto
import "github.com/go-playground/validator/v10"
var validate = validator.New()
func GetValidator() *validator.Validate {
return validate
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,40 @@
package handler
import (
"github.com/gofiber/fiber/v2"
"github.com/pahmiudahgede/senggoldong/dto"
"github.com/pahmiudahgede/senggoldong/internal/services"
"github.com/pahmiudahgede/senggoldong/utils"
"golang.org/x/crypto/bcrypt"
)
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"}})
}
user, err := h.UserService.Login(loginDTO)
if err != nil {
if err.Error() == "user not found" {
return utils.ErrorResponse(c, "User not found")
}
if err == bcrypt.ErrMismatchedHashAndPassword {
return utils.ErrorResponse(c, "Invalid password")
}
return utils.InternalServerErrorResponse(c, "Error logging in")
}
return utils.LogResponse(c, user, "Login successful")
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,27 @@
package repositories
import (
"github.com/pahmiudahgede/senggoldong/model"
"gorm.io/gorm"
)
type UserRepository interface {
FindByEmailOrUsernameOrPhone(identifier string) (*model.User, error)
}
type userRepository struct {
DB *gorm.DB
}
func NewUserRepository(db *gorm.DB) UserRepository {
return &userRepository{DB: db}
}
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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,76 @@
package services
import (
"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"
)
type UserService interface {
Login(credentials dto.LoginDTO) (*dto.UserResponseWithToken, error)
}
type userService struct {
UserRepo repositories.UserRepository
SecretKey string
}
func NewUserService(userRepo repositories.UserRepository, secretKey string) UserService {
return &userService{UserRepo: userRepo, SecretKey: secretKey}
}
func (s *userService) Login(credentials dto.LoginDTO) (*dto.UserResponseWithToken, error) {
user, err := s.UserRepo.FindByEmailOrUsernameOrPhone(credentials.Identifier)
if err != nil {
return nil, fmt.Errorf("user not found")
}
if !CheckPasswordHash(credentials.Password, user.Password) {
return nil, bcrypt.ErrMismatchedHashAndPassword
}
token, err := s.generateJWT(user)
if err != nil {
return nil, err
}
err = utils.SetData(credentials.Identifier, token, time.Hour*24)
if err != nil {
return nil, err
}
return &dto.UserResponseWithToken{
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
}

View File

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

View File

@ -1,89 +0,0 @@
package services
import (
"github.com/pahmiudahgede/senggoldong/domain"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
)
func GetCoverageAreas() ([]domain.CoverageArea, error) {
return repositories.GetCoverageAreas()
}
func GetCoverageAreaByID(id string) (domain.CoverageArea, error) {
return repositories.GetCoverageAreaByID(id)
}
func GetCoverageAreaByDistrictID(id string) (domain.CoverageDistric, error) {
return repositories.GetCoverageAreaByDistrictID(id)
}
func GetCoverageDistricsByCoverageAreaID(areaID string) ([]domain.CoverageDistric, error) {
return repositories.GetCoverageDistricsByCoverageAreaID(areaID)
}
func GetSubdistrictsByCoverageDistrictID(districtID string) ([]domain.CoverageSubdistrict, error) {
return repositories.GetSubdistrictsByCoverageDistrictID(districtID)
}
func CreateCoverageArea(province string) (*domain.CoverageArea, error) {
coverageArea := &domain.CoverageArea{
Province: province,
}
if err := repositories.CreateCoverageArea(coverageArea); err != nil {
return nil, err
}
return coverageArea, nil
}
func CreateCoverageDistrict(coverageAreaID, district string) (*domain.CoverageDistric, error) {
coverageDistrict := &domain.CoverageDistric{
CoverageAreaID: coverageAreaID,
District: district,
}
if err := repositories.CreateCoverageDistrict(coverageDistrict); err != nil {
return nil, err
}
return coverageDistrict, nil
}
func CreateCoverageSubdistrict(coverageAreaID, coverageDistrictId, subdistrict string) (*domain.CoverageSubdistrict, error) {
coverageSubdistrict := &domain.CoverageSubdistrict{
CoverageAreaID: coverageAreaID,
CoverageDistrictId: coverageDistrictId,
Subdistrict: subdistrict,
}
if err := repositories.CreateCoverageSubdistrict(coverageSubdistrict); err != nil {
return nil, err
}
return coverageSubdistrict, nil
}
func UpdateCoverageArea(id string, request domain.CoverageArea) (domain.CoverageArea, error) {
return repositories.UpdateCoverageArea(id, request)
}
func UpdateCoverageDistrict(id string, request domain.CoverageDistric) (domain.CoverageDistric, error) {
return repositories.UpdateCoverageDistrict(id, request)
}
func UpdateCoverageSubdistrict(id string, request domain.CoverageSubdistrict) (domain.CoverageSubdistrict, error) {
return repositories.UpdateCoverageSubdistrict(id, request)
}
func DeleteCoverageArea(id string) error {
return repositories.DeleteCoverageArea(id)
}
func DeleteCoverageDistrict(id string) error {
return repositories.DeleteCoverageDistrict(id)
}
func DeleteCoverageSubdistrict(id string) error {
return repositories.DeleteCoverageSubdistrict(id)
}

View File

@ -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 PointService struct {
repo *repositories.PointRepository
}
func NewPointService(repo *repositories.PointRepository) *PointService {
return &PointService{repo: repo}
}
func (s *PointService) GetAllPoints() ([]dto.PointResponse, error) {
ctx := config.Context()
cacheKey := "points:all"
cachedData, err := config.RedisClient.Get(ctx, cacheKey).Result()
if err == nil && cachedData != "" {
var cachedPoints []dto.PointResponse
if err := json.Unmarshal([]byte(cachedData), &cachedPoints); err == nil {
return cachedPoints, nil
}
}
points, err := s.repo.GetAll()
if err != nil {
return nil, err
}
var result []dto.PointResponse
for _, point := range points {
result = append(result, dto.PointResponse{
ID: point.ID,
CoinName: point.CoinName,
ValuePerUnit: point.ValuePerUnit,
CreatedAt: utils.FormatDateToIndonesianFormat(point.CreatedAt),
UpdatedAt: utils.FormatDateToIndonesianFormat(point.UpdatedAt),
})
}
cacheData, _ := json.Marshal(result)
config.RedisClient.Set(ctx, cacheKey, cacheData, time.Minute*5)
return result, nil
}
func (s *PointService) GetPointByID(id string) (*dto.PointResponse, error) {
ctx := config.Context()
cacheKey := "points:" + id
cachedData, err := config.RedisClient.Get(ctx, cacheKey).Result()
if err == nil && cachedData != "" {
var cachedPoint dto.PointResponse
if err := json.Unmarshal([]byte(cachedData), &cachedPoint); err == nil {
return &cachedPoint, nil
}
}
point, err := s.repo.GetByID(id)
if err != nil {
return nil, err
}
result := &dto.PointResponse{
ID: point.ID,
CoinName: point.CoinName,
ValuePerUnit: point.ValuePerUnit,
CreatedAt: utils.FormatDateToIndonesianFormat(point.CreatedAt),
UpdatedAt: utils.FormatDateToIndonesianFormat(point.UpdatedAt),
}
cacheData, _ := json.Marshal(result)
config.RedisClient.Set(ctx, cacheKey, cacheData, time.Minute*5)
return result, nil
}
func (s *PointService) CreatePoint(request *dto.PointCreateRequest) (*dto.PointResponse, error) {
if err := request.Validate(); err != nil {
return nil, err
}
newPoint := &domain.Point{
CoinName: request.CoinName,
ValuePerUnit: request.ValuePerUnit,
}
err := s.repo.Create(newPoint)
if err != nil {
return nil, err
}
ctx := config.Context()
config.RedisClient.Del(ctx, "points:all")
response := &dto.PointResponse{
ID: newPoint.ID,
CoinName: newPoint.CoinName,
ValuePerUnit: newPoint.ValuePerUnit,
CreatedAt: utils.FormatDateToIndonesianFormat(newPoint.CreatedAt),
UpdatedAt: utils.FormatDateToIndonesianFormat(newPoint.UpdatedAt),
}
return response, nil
}
func (s *PointService) UpdatePoint(id string, request *dto.PointUpdateRequest) (*dto.PointResponse, error) {
if err := request.Validate(); err != nil {
return nil, err
}
point, err := s.repo.GetByID(id)
if err != nil {
return nil, errors.New("point not found")
}
point.CoinName = request.CoinName
point.ValuePerUnit = request.ValuePerUnit
point.UpdatedAt = time.Now()
err = s.repo.Update(point)
if err != nil {
return nil, errors.New("failed to update point")
}
ctx := config.Context()
config.RedisClient.Del(ctx, "points:all")
config.RedisClient.Del(ctx, "points:"+id)
response := &dto.PointResponse{
ID: point.ID,
CoinName: point.CoinName,
ValuePerUnit: point.ValuePerUnit,
CreatedAt: utils.FormatDateToIndonesianFormat(point.CreatedAt),
UpdatedAt: utils.FormatDateToIndonesianFormat(point.UpdatedAt),
}
return response, nil
}
func (s *PointService) DeletePoint(id string) error {
point, err := s.repo.GetByID(id)
if err != nil {
return errors.New("point not found")
}
err = s.repo.Delete(point)
if err != nil {
return errors.New("failed to delete point")
}
ctx := config.Context()
config.RedisClient.Del(ctx, "points:all")
config.RedisClient.Del(ctx, "points:"+id)
return nil
}

View File

@ -1,208 +0,0 @@
package services
import (
"errors"
"github.com/pahmiudahgede/senggoldong/domain"
"github.com/pahmiudahgede/senggoldong/dto"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
"github.com/pahmiudahgede/senggoldong/utils"
)
func GetProductsByStoreID(storeID string, limit, page int) ([]dto.ProductResponseWithSoldDTO, error) {
offset := (page - 1) * limit
products, err := repositories.GetProductsByStoreID(storeID, limit, offset)
if err != nil {
return nil, err
}
return mapProductsToDTO(products), nil
}
func GetProductsByUserID(userID string, limit, page int) ([]dto.ProductResponseWithSoldDTO, error) {
offset := (page - 1) * limit
products, err := repositories.GetProductsByUserID(userID, limit, offset)
if err != nil {
return nil, err
}
return mapProductsToDTO(products), nil
}
func mapProductsToDTO(products []domain.Product) []dto.ProductResponseWithSoldDTO {
var productResponses []dto.ProductResponseWithSoldDTO
for _, product := range products {
var images []dto.ProductImageDTO
for _, img := range product.ProductImages {
images = append(images, dto.ProductImageDTO{ImageURL: img.ImageURL})
}
productResponses = append(productResponses, dto.ProductResponseWithSoldDTO{
ID: product.ID,
StoreID: product.StoreID,
ProductTitle: product.ProductTitle,
ProductImages: images,
TrashDetail: dto.TrashDetailResponseDTO{
ID: product.TrashDetail.ID,
Description: product.TrashDetail.Description,
Price: product.TrashDetail.Price,
},
SalePrice: product.SalePrice,
Quantity: product.Quantity,
ProductDescribe: product.ProductDescribe,
Sold: product.Sold,
CreatedAt: utils.FormatDateToIndonesianFormat(product.CreatedAt),
UpdatedAt: utils.FormatDateToIndonesianFormat(product.UpdatedAt),
})
}
return productResponses
}
func GetProductByIDAndStoreID(productID, storeID string) (dto.ProductResponseWithSoldDTO, error) {
product, err := repositories.GetProductByIDAndStoreID(productID, storeID)
if err != nil {
return dto.ProductResponseWithSoldDTO{}, err
}
var images []dto.ProductImageDTO
for _, img := range product.ProductImages {
images = append(images, dto.ProductImageDTO{ImageURL: img.ImageURL})
}
return dto.ProductResponseWithSoldDTO{
ID: product.ID,
StoreID: product.StoreID,
ProductTitle: product.ProductTitle,
ProductImages: images,
TrashDetail: dto.TrashDetailResponseDTO{
ID: product.TrashDetail.ID,
Description: product.TrashDetail.Description,
Price: product.TrashDetail.Price,
},
SalePrice: product.SalePrice,
Quantity: product.Quantity,
ProductDescribe: product.ProductDescribe,
Sold: product.Sold,
CreatedAt: utils.FormatDateToIndonesianFormat(product.CreatedAt),
UpdatedAt: utils.FormatDateToIndonesianFormat(product.UpdatedAt),
}, nil
}
func CreateProduct(input dto.CreateProductRequestDTO, userID string) (dto.CreateProductResponseDTO, error) {
if err := dto.GetValidator().Struct(input); err != nil {
return dto.CreateProductResponseDTO{}, err
}
trashDetail, err := repositories.GetTrashDetailByID(input.TrashDetailID)
if err != nil {
return dto.CreateProductResponseDTO{}, err
}
marketPrice := int64(trashDetail.Price)
if err := dto.ValidateSalePrice(marketPrice, input.SalePrice); err != nil {
return dto.CreateProductResponseDTO{}, err
}
product := &domain.Product{
UserID: userID,
StoreID: input.StoreID,
ProductTitle: input.ProductTitle,
TrashDetailID: input.TrashDetailID,
SalePrice: input.SalePrice,
Quantity: input.Quantity,
ProductDescribe: input.ProductDescribe,
}
var images []domain.ProductImage
for _, imageURL := range input.ProductImages {
images = append(images, domain.ProductImage{ImageURL: imageURL})
}
if err := repositories.CreateProduct(product, images); err != nil {
return dto.CreateProductResponseDTO{}, err
}
trashDetail, err = repositories.GetTrashDetailByID(product.TrashDetailID)
if err != nil {
return dto.CreateProductResponseDTO{}, err
}
return dto.CreateProductResponseDTO{
ID: product.ID,
StoreID: product.StoreID,
ProductTitle: product.ProductTitle,
ProductImages: input.ProductImages,
TrashDetail: dto.TrashDetailResponseDTO{
ID: trashDetail.ID,
Description: trashDetail.Description,
Price: trashDetail.Price,
},
SalePrice: product.SalePrice,
Quantity: product.Quantity,
ProductDescribe: product.ProductDescribe,
CreatedAt: utils.FormatDateToIndonesianFormat(product.CreatedAt),
UpdatedAt: utils.FormatDateToIndonesianFormat(product.UpdatedAt),
}, nil
}
func UpdateProduct(productID string, input dto.UpdateProductRequestDTO) (dto.CreateProductResponseDTO, error) {
product, err := repositories.GetProductByID(productID)
if err != nil {
return dto.CreateProductResponseDTO{}, errors.New("product not found")
}
product.ProductTitle = input.ProductTitle
product.TrashDetailID = input.TrashDetailID
product.SalePrice = input.SalePrice
product.Quantity = input.Quantity
product.ProductDescribe = input.ProductDescribe
var images []domain.ProductImage
for _, imageURL := range input.ProductImages {
images = append(images, domain.ProductImage{ImageURL: imageURL})
}
if err := repositories.UpdateProduct(&product, images); err != nil {
return dto.CreateProductResponseDTO{}, err
}
trashDetail, err := repositories.GetTrashDetailByID(product.TrashDetailID)
if err != nil {
return dto.CreateProductResponseDTO{}, err
}
return dto.CreateProductResponseDTO{
ID: product.ID,
StoreID: product.StoreID,
ProductTitle: product.ProductTitle,
ProductImages: input.ProductImages,
TrashDetail: dto.TrashDetailResponseDTO{
ID: trashDetail.ID,
Description: trashDetail.Description,
Price: trashDetail.Price,
},
SalePrice: product.SalePrice,
Quantity: product.Quantity,
ProductDescribe: product.ProductDescribe,
CreatedAt: utils.FormatDateToIndonesianFormat(product.CreatedAt),
UpdatedAt: utils.FormatDateToIndonesianFormat(product.UpdatedAt),
}, nil
}
func DeleteProduct(productID string) error {
_, err := repositories.GetProductByID(productID)
if err != nil {
return errors.New("product not found")
}
if err := repositories.DeleteProduct(productID); err != nil {
return err
}
return nil
}

View File

@ -1,42 +0,0 @@
package services
import (
"fmt"
"github.com/pahmiudahgede/senggoldong/domain"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
)
type RequestPickupService struct {
repository repositories.RequestPickupRepository
}
func NewRequestPickupService(repository repositories.RequestPickupRepository) *RequestPickupService {
return &RequestPickupService{repository: repository}
}
func (s *RequestPickupService) CreateRequestPickup(request *domain.RequestPickup) error {
return s.repository.Create(request)
}
func (s *RequestPickupService) GetRequestPickupByID(id string) (*domain.RequestPickup, error) {
return s.repository.GetByID(id)
}
func (s *RequestPickupService) GetRequestPickupsByUser(userID string) ([]domain.RequestPickup, error) {
return s.repository.GetByUserID(userID)
}
func (s *RequestPickupService) DeleteRequestPickupByID(id string) error {
exists, err := s.repository.ExistsByID(id)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("request pickup with id %s not found", id)
}
return s.repository.DeleteByID(id)
}

View File

@ -1,24 +0,0 @@
package services
import (
"errors"
"github.com/pahmiudahgede/senggoldong/domain"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
)
func GetUserRoleByID(id string) (domain.UserRole, error) {
role, err := repositories.GetUserRoleByID(id)
if err != nil {
return role, errors.New("userRole tidak ditemukan")
}
return role, nil
}
func GetAllUserRoles() ([]domain.UserRole, error) {
roles, err := repositories.GetAllUserRoles()
if err != nil {
return nil, errors.New("gagal mengambil data UserRole")
}
return roles, nil
}

View File

@ -1,53 +0,0 @@
package services
import (
"github.com/pahmiudahgede/senggoldong/dto"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
"github.com/pahmiudahgede/senggoldong/utils"
)
func GetStoreByID(storeID string) (dto.StoreResponseDTO, error) {
store, err := repositories.GetStoreByID(storeID)
if err != nil {
return dto.StoreResponseDTO{}, err
}
return dto.StoreResponseDTO{
ID: store.ID,
UserID: store.UserID,
StoreName: store.StoreName,
StoreLogo: store.StoreLogo,
StoreBanner: store.StoreBanner,
StoreDesc: store.StoreDesc,
Follower: store.Follower,
StoreRating: store.StoreRating,
CreatedAt: utils.FormatDateToIndonesianFormat(store.CreatedAt),
UpdatedAt: utils.FormatDateToIndonesianFormat(store.UpdatedAt),
}, nil
}
func GetStoresByUserID(userID string, limit, page int) ([]dto.StoreResponseDTO, error) {
offset := (page - 1) * limit
stores, err := repositories.GetStoresByUserID(userID, limit, offset)
if err != nil {
return nil, err
}
var storeResponses []dto.StoreResponseDTO
for _, store := range stores {
storeResponses = append(storeResponses, dto.StoreResponseDTO{
ID: store.ID,
UserID: store.UserID,
StoreName: store.StoreName,
StoreLogo: store.StoreLogo,
StoreBanner: store.StoreBanner,
StoreDesc: store.StoreDesc,
Follower: store.Follower,
StoreRating: store.StoreRating,
CreatedAt: utils.FormatDateToIndonesianFormat(store.CreatedAt),
UpdatedAt: utils.FormatDateToIndonesianFormat(store.UpdatedAt),
})
}
return storeResponses, nil
}

View File

@ -1,85 +0,0 @@
package services
import (
"github.com/pahmiudahgede/senggoldong/domain"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
)
func GetTrashCategories() ([]domain.TrashCategory, error) {
return repositories.GetTrashCategories()
}
func GetTrashCategoryDetail(id string) (domain.TrashCategory, error) {
return repositories.GetTrashCategoryDetail(id)
}
func CreateTrashCategory(name string) (domain.TrashCategory, error) {
category := domain.TrashCategory{Name: name}
err := repositories.CreateTrashCategory(&category)
if err != nil {
return category, err
}
return category, nil
}
func CreateTrashDetail(categoryID, description string, price int) (domain.TrashDetail, error) {
detail := domain.TrashDetail{
CategoryID: categoryID,
Description: description,
Price: price,
}
err := repositories.CreateTrashDetail(&detail)
if err != nil {
return detail, err
}
return detail, nil
}
func UpdateTrashCategory(id, name string) (domain.TrashCategory, error) {
category, err := repositories.GetTrashCategoryDetail(id)
if err != nil {
return domain.TrashCategory{}, err
}
category.Name = name
if err := repositories.UpdateTrashCategory(&category); err != nil {
return domain.TrashCategory{}, err
}
return category, nil
}
func UpdateTrashDetail(id, description string, price int) (domain.TrashDetail, error) {
detail, err := repositories.GetTrashDetailByID(id)
if err != nil {
return domain.TrashDetail{}, err
}
detail.Description = description
detail.Price = price
if err := repositories.UpdateTrashDetail(&detail); err != nil {
return domain.TrashDetail{}, err
}
return detail, nil
}
func DeleteTrashCategory(id string) error {
err := repositories.DeleteTrashCategory(id)
if err != nil {
return err
}
return nil
}
func DeleteTrashDetail(id string) error {
err := repositories.DeleteTrashDetail(id)
if err != nil {
return err
}
return nil
}

View File

@ -1,71 +0,0 @@
package services
import (
"github.com/pahmiudahgede/senggoldong/dto"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
"github.com/pahmiudahgede/senggoldong/utils"
)
func GetUsers() ([]dto.UserResponseDTO, error) {
users, err := repositories.GetUsers()
if err != nil {
return nil, err
}
var userResponses []dto.UserResponseDTO
for _, user := range users {
userResponses = append(userResponses, 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 userResponses, nil
}
func GetUsersByRole(roleID string) ([]dto.UserResponseDTO, error) {
users, err := repositories.GetUsersByRole(roleID)
if err != nil {
return nil, err
}
var userResponses []dto.UserResponseDTO
for _, user := range users {
userResponses = append(userResponses, 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 userResponses, nil
}
func GetUserByUserID(userID string) (dto.UserResponseDTO, error) {
user, err := repositories.GetUserByID(userID)
if err != nil {
return dto.UserResponseDTO{}, err
}
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 userResponse, nil
}

View File

@ -1,63 +0,0 @@
package services
import (
"errors"
"github.com/pahmiudahgede/senggoldong/domain"
"github.com/pahmiudahgede/senggoldong/dto"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
"golang.org/x/crypto/bcrypt"
)
func CreatePin(userID string, input dto.PinInput) (domain.UserPin, error) {
hashedPin, err := bcrypt.GenerateFromPassword([]byte(input.Pin), bcrypt.DefaultCost)
if err != nil {
return domain.UserPin{}, err
}
pin := domain.UserPin{
UserID: userID,
Pin: string(hashedPin),
}
err = repositories.CreatePin(&pin)
if err != nil {
return domain.UserPin{}, err
}
return pin, nil
}
func GetPinByUserID(userID string) (domain.UserPin, error) {
pin, err := repositories.GetPinByUserID(userID)
if err != nil {
return pin, errors.New("PIN tidak ditemukan")
}
return pin, nil
}
func UpdatePin(userID string, oldPin string, newPin string) (domain.UserPin, error) {
pin, err := repositories.GetPinByUserID(userID)
if err != nil {
return pin, errors.New("PIN tidak ditemukan")
}
if err := bcrypt.CompareHashAndPassword([]byte(pin.Pin), []byte(oldPin)); err != nil {
return pin, errors.New("PIN lama salah")
}
updatedPin, err := repositories.UpdatePin(userID, newPin)
if err != nil {
return updatedPin, err
}
return updatedPin, nil
}
func CheckPin(storedPinHash string, inputPin string) bool {
err := bcrypt.CompareHashAndPassword([]byte(storedPinHash), []byte(inputPin))
return err == nil
}

24
middleware/api_key.go Normal file
View File

@ -0,0 +1,24 @@
package middleware
import (
"log"
"os"
"github.com/gofiber/fiber/v2"
"github.com/pahmiudahgede/senggoldong/utils"
)
func APIKeyMiddleware(c *fiber.Ctx) error {
apiKey := c.Get("x-api-key")
validAPIKey := os.Getenv("API_KEY")
if apiKey != validAPIKey {
log.Printf("Invalid API Key: %s", apiKey)
return utils.GenericErrorResponse(c, fiber.StatusUnauthorized, "Unauthorized: api key yang anda masukkan tidak valid")
}
return c.Next()
}

View File

@ -1,4 +1,4 @@
package domain package model
import "time" import "time"
@ -11,11 +11,6 @@ type User struct {
Email string `gorm:"not null" json:"email"` Email string `gorm:"not null" json:"email"`
EmailVerified bool `gorm:"default:false" json:"emailVerified"` EmailVerified bool `gorm:"default:false" json:"emailVerified"`
Password string `gorm:"not null" json:"password"` Password string `gorm:"not null" json:"password"`
Pin UserPin `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"pin"`
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"` CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"` UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
RoleID string `gorm:"not null" json:"roleId"`
Role UserRole `gorm:"foreignKey:RoleID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"role"`
AddressId *string `gorm:"default:null" json:"addressId"`
Addresses []Address `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"addresses"`
} }

View File

@ -0,0 +1,26 @@
package presentation
import (
"os"
"github.com/gofiber/fiber/v2"
"github.com/pahmiudahgede/senggoldong/config"
"github.com/pahmiudahgede/senggoldong/internal/handler"
"github.com/pahmiudahgede/senggoldong/internal/repositories"
"github.com/pahmiudahgede/senggoldong/internal/services"
)
func AuthRouter(app *fiber.App) {
api := app.Group("/apirijikid")
secretKey := os.Getenv("SECRET_KEY")
if secretKey == "" {
panic("SECRET_KEY is not set in the environment variables")
}
userRepo := repositories.NewUserRepository(config.DB)
userService := services.NewUserService(userRepo, secretKey)
userHandler := handler.NewUserHandler(userService)
api.Post("/login", userHandler.Login)
}

View File

@ -1,31 +0,0 @@
package main
import (
"log"
"os"
"github.com/gofiber/fiber/v2"
"github.com/joho/godotenv"
"github.com/pahmiudahgede/senggoldong/config"
"github.com/pahmiudahgede/senggoldong/internal/api"
)
func init() {
err := godotenv.Load()
if err != nil {
log.Fatal("error saat memuat file .env")
}
config.InitConfig()
config.InitDatabase()
config.InitRedis()
}
func main() {
app := fiber.New()
api.AppRouter(app)
log.Fatal(app.Listen(":" + os.Getenv("SERVER_PORT")))
}

1
test/go.txt Normal file
View File

@ -0,0 +1 @@
setup

View File

@ -1,21 +1,22 @@
package utils package utils
import ( import (
"fmt"
"log" "log"
"time" "time"
) )
func FormatDateToIndonesianFormat(t time.Time) string { func FormatDateToIndonesianFormat(t time.Time) (string, error) {
utcTime := t.UTC() utcTime := t.UTC()
loc, err := time.LoadLocation("Asia/Jakarta") loc, err := time.LoadLocation("Asia/Jakarta")
if err != nil { if err != nil {
log.Printf("Error loading timezone: %v", err) log.Printf("Error loading timezone: %v", err)
return "" return "", fmt.Errorf("could not load location 'Asia/Jakarta'")
} }
indonesianTime := utcTime.In(loc) indonesianTime := utcTime.In(loc)
return indonesianTime.Format("02-01-2006 15:04") return indonesianTime.Format("02-01-2006 15:04"), nil
} }

67
utils/redis_caching.go Normal file
View File

@ -0,0 +1,67 @@
package utils
import (
"context"
"log"
"time"
"github.com/go-redis/redis/v8"
"github.com/pahmiudahgede/senggoldong/config"
)
func SetData(key string, value interface{}, expiration time.Duration) error {
err := config.RedisClient.Set(context.Background(), key, value, expiration).Err()
if err != nil {
log.Printf("Error setting data to Redis: %v", err)
return err
}
log.Printf("Data stored in Redis with key: %s", key)
return nil
}
func GetData(key string) (string, error) {
val, err := config.RedisClient.Get(context.Background(), key).Result()
if err == redis.Nil {
log.Printf("No data found for key: %s", key)
return "", nil
} else if err != nil {
log.Printf("Error getting data from Redis: %v", err)
return "", err
}
log.Printf("Data retrieved from Redis for key: %s", key)
return val, nil
}
func DeleteData(key string) error {
err := config.RedisClient.Del(context.Background(), key).Err()
if err != nil {
log.Printf("Error deleting data from Redis: %v", err)
return err
}
log.Printf("Data deleted from Redis with key: %s", key)
return nil
}
func SetDataWithExpire(key string, value interface{}, expiration time.Duration) error {
err := config.RedisClient.Set(context.Background(), key, value, expiration).Err()
if err != nil {
log.Printf("Error setting data with expiration to Redis: %v", err)
return err
}
log.Printf("Data stored in Redis with key: %s and expiration: %v", key, expiration)
return nil
}
func CheckKeyExists(key string) (bool, error) {
val, err := config.RedisClient.Exists(context.Background(), key).Result()
if err != nil {
log.Printf("Error checking if key exists in Redis: %v", err)
return false, err
}
return val > 0, nil
}

View File

@ -1,30 +1,96 @@
package utils package utils
type Meta struct { import (
StatusCode int `json:"statusCode"` "github.com/gofiber/fiber/v2"
Message string `json:"message"` )
type MetaData struct {
Status int `json:"status"`
Page int `json:"page,omitempty"`
Limit int `json:"limit,omitempty"`
Total int `json:"total,omitempty"`
Message string `json:"message"`
} }
type ApiResponse struct { type APIResponse struct {
Meta Meta `json:"meta"` Meta MetaData `json:"meta"`
Data interface{} `json:"data,omitempty"` Data interface{} `json:"data,omitempty"`
} }
func FormatResponse(statusCode int, message string, data interface{}) ApiResponse { func PaginatedResponse(c *fiber.Ctx, data interface{}, page, limit, total int, message string) error {
return ApiResponse{ response := APIResponse{
Meta: Meta{ Meta: MetaData{
StatusCode: statusCode, Status: fiber.StatusOK,
Message: message, Page: page,
Limit: limit,
Total: total,
Message: message,
}, },
Data: data, Data: data,
} }
return c.Status(fiber.StatusOK).JSON(response)
} }
func ErrorResponse(statusCode int, message string) ApiResponse { func NonPaginatedResponse(c *fiber.Ctx, data interface{}, total int, message string) error {
return ApiResponse{ response := APIResponse{
Meta: Meta{ Meta: MetaData{
StatusCode: statusCode, Status: fiber.StatusOK,
Message: message, Total: total,
Message: message,
},
Data: data,
}
return c.Status(fiber.StatusOK).JSON(response)
}
func ErrorResponse(c *fiber.Ctx, message string) error {
response := APIResponse{
Meta: MetaData{
Status: fiber.StatusNotFound,
Message: message,
}, },
} }
return c.Status(fiber.StatusNotFound).JSON(response)
}
func ValidationErrorResponse(c *fiber.Ctx, errors map[string][]string) error {
response := APIResponse{
Meta: MetaData{
Status: fiber.StatusBadRequest,
Message: "invalid user request",
},
Data: errors,
}
return c.Status(fiber.StatusBadRequest).JSON(response)
}
func InternalServerErrorResponse(c *fiber.Ctx, message string) error {
response := APIResponse{
Meta: MetaData{
Status: fiber.StatusInternalServerError,
Message: message,
},
}
return c.Status(fiber.StatusInternalServerError).JSON(response)
}
func GenericErrorResponse(c *fiber.Ctx, status int, message string) error {
response := APIResponse{
Meta: MetaData{
Status: status,
Message: message,
},
}
return c.Status(status).JSON(response)
}
func LogResponse(c *fiber.Ctx, data interface{}, message string) error {
response := APIResponse{
Meta: MetaData{
Status: fiber.StatusOK,
Message: message,
},
Data: data,
}
return c.Status(fiber.StatusOK).JSON(response)
} }