diff --git a/%USERPROFILE%/go/bin/pkg/sumdb/sum.golang.org/latest b/%USERPROFILE%/go/bin/pkg/sumdb/sum.golang.org/latest new file mode 100644 index 0000000..8eabf94 --- /dev/null +++ b/%USERPROFILE%/go/bin/pkg/sumdb/sum.golang.org/latest @@ -0,0 +1,5 @@ +go.sum database tree +20857462 +Kn0F1e8O8fQOl6YvfyegETTq1tmu1qpkfDZj5AtvhWI= + +— sum.golang.org Az3gru8zyaj7EuaczqV+hDOTF+63AeV8mvhZHqRv6bbiV3gaYIvN9DnNHoSzVwNBCGSXS7qp50Ui6l9wNpxiJmNpwAc= diff --git a/.env b/.env index 3c14277..79493ba 100644 --- a/.env +++ b/.env @@ -1,7 +1,12 @@ -PORT=8080 -POSTGRES_HOST=localhost -POSTGRES_PORT=5432 -POSTGRES_USER=postgres -POSTGRES_PASSWORD=mysecretpassword -POSTGRES_DATABASE=weather-app -JWT_SECRET_KEY="akusayangmantanku" \ No newline at end of file +PORT=8080 +ENV="development" +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +POSTGRES_USER=postgres +POSTGRES_PASSWORD=mysecretpassword +POSTGRES_DATABASE=Tiketing +JWT_SECRET_KEY=secret +MIDTRANS_CLIENT_KEY="" +MIDTRANS_SERVER_KEY="" + + diff --git a/Materi/erd.jpg b/Materi/erd.jpg new file mode 100644 index 0000000..ea7d5bd Binary files /dev/null and b/Materi/erd.jpg differ diff --git a/README.md b/README.md index db92655..c0a8892 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ # Capstone-3: Aplikasi Backend Pemesanan Tiket -By : Reza +By : Golang - Kelompok 3 +Anggota : +- [x] [Rio] +- [x] [Reza] +- [x] [Ravita] +- [x] [Rahma] +- [x] [Sandhya] Dalam proyek capstone ini, tujuan utamanya adalah untuk memberikan pengalaman baru kepada peserta, serta mengimplementasikan Metode Scrum dalam pengembangan produk digital. Proyek ini akan dilakukan dalam bentuk kelompok dan fokus pada pengembangan bagian Back End. @@ -52,9 +58,12 @@ Proyek ini akan berlangsung selama 2 sprint (2 minggu) dengan penilaian secara b ![Figma](https://raw.githubusercontent.com/Capstone-Mikti/Capstone-3/main/Materi/flow%201.png) ![Figma](https://raw.githubusercontent.com/Capstone-Mikti/Capstone-3/main/Materi/flow%202.png) +![ERD](https://raw.githubusercontent.com/Capstone-Mikti/Capstone-3/main/Materi/erd.jpg) - [x] [Figma](https://www.figma.com/file/BdDlFZ7u3E6upQlSwUcOax/Code-Challenge-Project-Based?type=whiteboard&node-id=0-1&t=Le6Pdip9Patyl5DV-0) - [x] [PPT Document](https://docs.google.com/presentation/d/1vvOwCKjysLxORL7GOtOJbgiW3XAVuYsRK0ccUa8VOzA/edit#slide=id.g21e0640a876_0_25) -- [x] [Desain Aplikasi](https://www.figma.com/file/1MtWn0HIt9SidftaQIlrXG/Project-Coding-Challenge-References?type=design&node-id=0-4&mode=design&t=dC1tZcf7zlIMfZBL-0) \ No newline at end of file +- [x] [Desain Aplikasi](https://www.figma.com/file/1MtWn0HIt9SidftaQIlrXG/Project-Coding-Challenge-References?type=design&node-id=0-4&mode=design&t=dC1tZcf7zlIMfZBL-0) +- [x] [Notion](https://pouncing-language-444.notion.site/15cb9b14d1cd47f4a355e8b7583b0857?v=e7c8616df95346d7af872d52aba69259&pvs=4) +- [x] [Postman](https://trello.com/b/6Z6ZQZ6Z/capstone-3) \ No newline at end of file diff --git a/cmd/server/main.go b/cmd/server/main.go index fa73f0e..75c0115 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -1,100 +1,122 @@ -package main - -import ( - "context" - "fmt" - "log" - "os" - "os/signal" - "strings" - "time" - - "github.com/labstack/echo/v4" - "github.com/zhikariz/weather-app/internal/builder" - "github.com/zhikariz/weather-app/internal/config" - "github.com/zhikariz/weather-app/internal/http/binder" - "github.com/zhikariz/weather-app/internal/http/server" - "github.com/zhikariz/weather-app/internal/http/validator" - "gorm.io/driver/postgres" - "gorm.io/gorm" - "gorm.io/gorm/logger" -) - -func main() { - cfg, err := config.NewConfig(".env") - checkError(err) - - splash() - - db, err := buildGormDB(cfg.Postgres) - checkError(err) - - publicRoutes := builder.BuildPublicRoutes(cfg, db) - privateRoutes := builder.BuildPrivateRoutes(cfg, db) - - echoBinder := &echo.DefaultBinder{} - formValidator := validator.NewFormValidator() - customBinder := binder.NewBinder(echoBinder, formValidator) - - srv := server.NewServer( - cfg, - customBinder, - publicRoutes, - privateRoutes, - ) - - runServer(srv, cfg.Port) - - waitForShutdown(srv) -} - -func runServer(srv *server.Server, port string) { - go func() { - err := srv.Start(fmt.Sprintf(":%s", port)) - log.Fatal(err) - }() -} - -func waitForShutdown(srv *server.Server) { - quit := make(chan os.Signal, 1) - signal.Notify(quit, os.Interrupt) - - <-quit - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - go func() { - if err := srv.Shutdown(ctx); err != nil { - srv.Logger.Fatal(err) - } - }() -} - -func buildGormDB(cfg config.PostgresConfig) (*gorm.DB, error) { - dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Jakarta", cfg.Host, cfg.User, cfg.Password, cfg.Database, cfg.Port) - return gorm.Open(postgres.Open(dsn), &gorm.Config{ - Logger: logger.Default.LogMode(logger.Info), - }) -} - -func splash() { - colorReset := "\033[0m" - - splashText := ` - - __ __ __ .__ _____ -/ \ / \ ____ _____ _/ |_| |__ ___________ / _ \ ______ ______ -\ \/\/ // __ \\__ \\ __\ | \_/ __ \_ __ \/ /_\ \\____ \\____ \ - \ /\ ___/ / __ \| | | Y \ ___/| | \/ | \ |_> > |_> > - \__/\ / \___ >____ /__| |___| /\___ >__| \____|__ / __/| __/ - \/ \/ \/ \/ \/ \/|__| |__| -` - fmt.Println(colorReset, strings.TrimSpace(splashText)) -} - -func checkError(err error) { - if err != nil { - panic(err) - } -} +package main + +import ( + "Ticketing/internal/builder" + "Ticketing/internal/config" + "Ticketing/internal/http/binder" + "Ticketing/internal/http/server" + "Ticketing/internal/http/validator" + "context" + "fmt" + "log" + "os" + "os/signal" + "strings" + "time" + + "github.com/labstack/echo/v4" + "github.com/midtrans/midtrans-go" + "github.com/midtrans/midtrans-go/snap" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +func main() { + //menghubungkan ke postgresql atau database + cfg, err := config.NewConfig(".env") + checkError(err) + + splash() + + db, err := buildGormDB(cfg.Postgres) + checkError(err) + + midtransClient := initMidtrans(cfg) + + publicRoutes := builder.BuildPublicRoutes(cfg, db, midtransClient) + privateRoutes := builder.BuildPrivateRoutes(cfg, db, midtransClient) + + echoBinder := &echo.DefaultBinder{} + formValidator := validator.NewFormValidator() + customBinder := binder.NewBinder(echoBinder, formValidator) + + srv := server.NewServer( + cfg, + customBinder, + publicRoutes, + privateRoutes, + ) + + runServer(srv, cfg.Port) + + waitForShutdown(srv) +} + +func initMidtrans(cfg *config.Config) snap.Client { + snapClient := snap.Client{} + + if cfg.Env == "development" { + snapClient.New(cfg.MidtransConfig.ServerKey, midtrans.Sandbox) + } else { + snapClient.New(cfg.MidtransConfig.ServerKey, midtrans.Production) + } + + return snapClient +} + +func runServer(srv *server.Server, port string) { + go func() { + err := srv.Start(fmt.Sprintf(":%s", port)) + log.Fatal(err) + }() +} + +// berfungsi ketika API mati akan hidup sendiri lagi. ini untuk menghindari error ketika API mati +func waitForShutdown(srv *server.Server) { + quit := make(chan os.Signal, 1) + signal.Notify(quit, os.Interrupt) + + <-quit + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + go func() { + if err := srv.Shutdown(ctx); err != nil { + srv.Logger.Fatal(err) + } + }() +} + +// func untuk koneksi ke postgresql +func buildGormDB(cfg config.PostgresConfig) (*gorm.DB, error) { + dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Jakarta", cfg.Host, cfg.User, cfg.Password, cfg.Database, cfg.Port) + return gorm.Open(postgres.Open(dsn), &gorm.Config{ + Logger: logger.Default.LogMode(logger.Info), + }) +} + +// untuk membuat spalsh screen ini bisa menggunakan website +// ascii text style generator seperti patorjk.com +func splash() { + colorReset := "\033[0m" + + splashText := ` + + ___________.__ __ __ .__ + \__ ___/|__| ____ | | __ ____ _/ |_ |__| ____ ____ + | | | |_/ ___\ | |/ /_/ __ \ \ __\| | / \ / ___\ + | | | |\ \___ | < \ ___/ | | | || | \ / /_/ > + |____| |__| \___ >|__|_ \ \___ > |__| |__||___| / \___ / + \/ \/ \/ \/ /_____/ +` + fmt.Println(colorReset, strings.TrimSpace(splashText)) +} + +// func untuk cek error +func checkError(err error) { + if err != nil { + panic(err) + } +} diff --git a/common/jwt.go b/common/jwt.go index b5c1f60..bb54e55 100644 --- a/common/jwt.go +++ b/common/jwt.go @@ -1,12 +1,17 @@ -package common - -import ( - "github.com/golang-jwt/jwt/v5" -) - -type JwtCustomClaims struct { - ID int64 `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - jwt.RegisteredClaims -} +package common + +//note : file ini untuk buat claims JWT +// claims JWT adalah data yang akan dienkripsi dan ditandatangani oleh JWT +// claims JWT ini nantinya akan disimpan di dalam payload JWT + +import ( + "github.com/golang-jwt/jwt/v5" +) + +type JwtCustomClaims struct { + ID int64 `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + Role string `json:"role"` + jwt.RegisteredClaims // ini untuk exp at +} diff --git a/db/migration-golang/20231123141605_add_several_field_in_users_table.down.sql b/db/migration-golang/20231123141605_add_several_field_in_users_table.down.sql deleted file mode 100644 index 4c001ce..0000000 --- a/db/migration-golang/20231123141605_add_several_field_in_users_table.down.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN; - - -ALTER TABLE "public"."users" -DROP COLUMN IF EXISTS email, -DROP COLUMN IF EXISTS password; - -COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231123141605_add_several_field_in_users_table.up.sql b/db/migration-golang/20231123141605_add_several_field_in_users_table.up.sql deleted file mode 100644 index 4c7a81a..0000000 --- a/db/migration-golang/20231123141605_add_several_field_in_users_table.up.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -ALTER TABLE "public"."users" -ADD COLUMN email VARCHAR(255), -ADD COLUMN password VARCHAR(255); - -COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231127065301_create_topup_tabel.down.sql b/db/migration-golang/20231127065301_create_topup_tabel.down.sql new file mode 100644 index 0000000..6222119 --- /dev/null +++ b/db/migration-golang/20231127065301_create_topup_tabel.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS "public"."topup"; + +COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231127065301_create_topup_tabel.up.sql b/db/migration-golang/20231127065301_create_topup_tabel.up.sql new file mode 100644 index 0000000..0a67574 --- /dev/null +++ b/db/migration-golang/20231127065301_create_topup_tabel.up.sql @@ -0,0 +1,11 @@ +BEGIN; +CREATE TABLE IF NOT EXISTS "public"."topup" ( + "id" varchar(255) NOT NULL PRIMARY KEY, + "user_id" INT, + "amount" INT, + "status" INT, + "snap_url" varchar(255), + "created_at" TIMESTAMP, + "updated_at" TIMESTAMP +); +COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231120141232_create_users_table.down.sql b/db/migration-golang/20231127065801_create_users_tabel.down.sql similarity index 93% rename from db/migration-golang/20231120141232_create_users_table.down.sql rename to db/migration-golang/20231127065801_create_users_tabel.down.sql index 07f389a..1bbd511 100644 --- a/db/migration-golang/20231120141232_create_users_table.down.sql +++ b/db/migration-golang/20231127065801_create_users_tabel.down.sql @@ -1,5 +1,5 @@ -BEGIN; - -DROP TABLE IF EXISTS "public"."users"; - +BEGIN; + +DROP TABLE IF EXISTS "public"."users"; + COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231120141232_create_users_table.up.sql b/db/migration-golang/20231127065801_create_users_tabel.up.sql similarity index 61% rename from db/migration-golang/20231120141232_create_users_table.up.sql rename to db/migration-golang/20231127065801_create_users_tabel.up.sql index f9d3962..357e020 100644 --- a/db/migration-golang/20231120141232_create_users_table.up.sql +++ b/db/migration-golang/20231127065801_create_users_tabel.up.sql @@ -1,9 +1,14 @@ -BEGIN; -CREATE TABLE IF NOT EXISTS "public"."users" ( - "id" SERIAL NOT NULL PRIMARY KEY, - "name" varchar(255) COLLATE "pg_catalog"."default" NOT NULL, - "created_at" timestamptz (6) NOT NULL, - "updated_at" timestamptz (6) NOT NULL, - "deleted_at" timestamptz (6) -); +BEGIN; +CREATE TABLE IF NOT EXISTS "public"."users" ( + "id" SERIAL NOT NULL PRIMARY KEY, + "name" varchar(255) COLLATE "pg_catalog"."default" NOT NULL, + email VARCHAR(255) NOT NULL, + number VARCHAR(255) NOT NULL, + roles VARCHAR(255) NOT NULL, + password VARCHAR(255) NOT NULL, + saldo INTEGER NOT NULL, + "created_at" timestamptz (6) NOT NULL, + "updated_at" timestamptz (6) NOT NULL, + "deleted_at" timestamptz (6) +); COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231127065901_create_notification_tabel.down.sql b/db/migration-golang/20231127065901_create_notification_tabel.down.sql new file mode 100644 index 0000000..2734c73 --- /dev/null +++ b/db/migration-golang/20231127065901_create_notification_tabel.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS "public"."notification"; + +COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231127065901_create_notification_tabel.up.sql b/db/migration-golang/20231127065901_create_notification_tabel.up.sql new file mode 100644 index 0000000..a2067dc --- /dev/null +++ b/db/migration-golang/20231127065901_create_notification_tabel.up.sql @@ -0,0 +1,11 @@ +BEGIN; +CREATE TABLE IF NOT EXISTS "public"."notifications" ( + ID SERIAL NOT NULL PRIMARY KEY, + Type TEXT, + Message TEXT, + Is_Read BOOLEAN DEFAULT FALSE, + Created_At TIMESTAMP, + Updated_At TIMESTAMP, + Deleted_At TIMESTAMP +); +COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231127065951_create_ticket_tabel.down.sql b/db/migration-golang/20231127065951_create_ticket_tabel.down.sql new file mode 100644 index 0000000..3f0a1c2 --- /dev/null +++ b/db/migration-golang/20231127065951_create_ticket_tabel.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS "public"."tickets"; + +COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231127065951_create_ticket_tabel.up.sql b/db/migration-golang/20231127065951_create_ticket_tabel.up.sql new file mode 100644 index 0000000..748ab4b --- /dev/null +++ b/db/migration-golang/20231127065951_create_ticket_tabel.up.sql @@ -0,0 +1,18 @@ +BEGIN; +CREATE TABLE IF NOT EXISTS "public"."tickets" ( + ID SERIAL NOT NULL PRIMARY KEY, + Image TEXT, + Location TEXT, + Date DATE, + Title TEXT, + Description TEXT, + Price INT, + Status TEXT DEFAULT 'available', + Quota INT, + Category TEXT, + Terjual INT, + Created_At TIMESTAMP, + Updated_At TIMESTAMP, + Deleted_At TIMESTAMP +); +COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231127070155_create_blog_tabel.down.sql b/db/migration-golang/20231127070155_create_blog_tabel.down.sql new file mode 100644 index 0000000..3bcb906 --- /dev/null +++ b/db/migration-golang/20231127070155_create_blog_tabel.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS "public"."blogs"; + +COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231127070155_create_blog_tabel.up.sql b/db/migration-golang/20231127070155_create_blog_tabel.up.sql new file mode 100644 index 0000000..44d74e2 --- /dev/null +++ b/db/migration-golang/20231127070155_create_blog_tabel.up.sql @@ -0,0 +1,12 @@ +BEGIN; +CREATE TABLE IF NOT EXISTS "public". "blogs" ( + ID SERIAL PRIMARY KEY, + Image TEXT, + Date DATE, + Title TEXT, + Description TEXT, + Created_At TIMESTAMP, + Updated_At TIMESTAMP, + Deleted_At TIMESTAMP +); +COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231127160034_create_order_tabel.down.sql b/db/migration-golang/20231127160034_create_order_tabel.down.sql new file mode 100644 index 0000000..6119cf0 --- /dev/null +++ b/db/migration-golang/20231127160034_create_order_tabel.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS "public"."orders"; + +COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231127160034_create_order_tabel.up.sql b/db/migration-golang/20231127160034_create_order_tabel.up.sql new file mode 100644 index 0000000..56d4601 --- /dev/null +++ b/db/migration-golang/20231127160034_create_order_tabel.up.sql @@ -0,0 +1,21 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS "public". "orders" ( + ID SERIAL PRIMARY KEY, + ticket_id INT, + user_id INT, + quantity INT, + total INT, + status VARCHAR(255), + order_at TIMESTAMP, + updated_at TIMESTAMP, + deleted_at TIMESTAMP, + order_by VARCHAR(255), + update_by VARCHAR(255), + delete_by VARCHAR(255), + FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE +); + +COMMIT; + diff --git a/db/migration-golang/20231205131102_create_table_transaction.down.sql b/db/migration-golang/20231205131102_create_table_transaction.down.sql new file mode 100644 index 0000000..18ab5f2 --- /dev/null +++ b/db/migration-golang/20231205131102_create_table_transaction.down.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP TABLE IF EXISTS "public"."transactions"; + +COMMIT; \ No newline at end of file diff --git a/db/migration-golang/20231205131102_create_table_transaction.up.sql b/db/migration-golang/20231205131102_create_table_transaction.up.sql new file mode 100644 index 0000000..e2cf32f --- /dev/null +++ b/db/migration-golang/20231205131102_create_table_transaction.up.sql @@ -0,0 +1,18 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS "public"."transactions" ( + "id" SERIAL NOT NULL PRIMARY KEY, + "order_id" varchar(255) NOT NULL, + "user_id" INT NOT NULL, + "amount" INT NULL, + "status" varchar(255) NOT NULL + -- "id" varchar(255) NOT NULL PRIMARY KEY, + -- "user_id" INT, + -- "amount" INT, + -- "status" INT, + -- "snap_url" varchar(255), + -- "created_at" TIMESTAMP, + -- "updated_at" TIMESTAMP +); + +COMMIT; \ No newline at end of file diff --git a/db/migrations/20231117204623_create_users_table.sql b/db/migrations/20231117204623_create_users_table.sql deleted file mode 100644 index 7f4b766..0000000 --- a/db/migrations/20231117204623_create_users_table.sql +++ /dev/null @@ -1,15 +0,0 @@ --- +goose Up --- +goose StatementBegin -CREATE TABLE IF NOT EXISTS "public"."users" ( - "id" SERIAL NOT NULL PRIMARY KEY, - "name" varchar(255) COLLATE "pg_catalog"."default" NOT NULL, - "created_at" timestamptz (6) NOT NULL, - "updated_at" timestamptz (6) NOT NULL, - "deleted_at" timestamptz (6) -); --- +goose StatementEnd - --- +goose Down --- +goose StatementBegin -DROP TABLE IF EXISTS "public"."users"; --- +goose StatementEnd \ No newline at end of file diff --git a/db/users.sql b/db/users.sql deleted file mode 100644 index fe81376..0000000 --- a/db/users.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE - "public"."users" ( - "id" SERIAL NOT NULL, - "name" varchar(255) COLLATE "pg_catalog"."default" NOT NULL, - "created_at" timestamptz (6) NOT NULL, - "updated_at" timestamptz (6) NOT NULL, - "deleted_at" timestamptz (6) - ); - -ALTER TABLE "public"."users" ADD CONSTRAINT "users_pkey" PRIMARY KEY ("id"); \ No newline at end of file diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..9c2cbcb --- /dev/null +++ b/dockerfile @@ -0,0 +1,13 @@ +FROM golang:1.21-alpine + +COPY . /app + +WORKDIR /app + +RUN go mod tidy + +RUN migrate -database "postgres://postgres:mysecretpassword@localhost:5432/Tiketing?sslmode=disable" -path db/migration-golang up + +RUN go build -o app . + +CMD ["/app/app"] \ No newline at end of file diff --git a/entity/blog.go b/entity/blog.go new file mode 100644 index 0000000..01a96b9 --- /dev/null +++ b/entity/blog.go @@ -0,0 +1,37 @@ +package entity + +import ( + "time" +) + +type Blog struct { + ID int64 + Image string + Date string // Format: YYYY-MM-DD + Title string + Description string + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt time.Time +} + +func NewBlog(image, date, title, description string) *Blog { + return &Blog{ + Image: image, + Date: date, + Title: title, + Description: description, + CreatedAt: time.Now(), + } +} + +func UpdateBlog(id int64, image, date, title, description string) *Blog { + return &Blog{ + ID: id, + Image: image, + Date: date, + Title: title, + Description: description, + UpdatedAt: time.Now(), + } +} diff --git a/entity/midtrans.go b/entity/midtrans.go new file mode 100644 index 0000000..8797dba --- /dev/null +++ b/entity/midtrans.go @@ -0,0 +1,23 @@ +package entity + +type MidtransRequest struct { + VaNumbers []struct { + VaNumber string `json:"va_number"` + Bank string `json:"bank"` + } `json:"va_numbers"` + TransactionTime string `json:"transaction_time"` + TransactionStatus string `json:"transaction_status"` + TransactionID string `json:"transaction_id"` + StatusMessage string `json:"status_message"` + StatusCode string `json:"status_code"` + SignatureKey string `json:"signature_key"` + SettlementTime string `json:"settlement_time"` + PaymentType string `json:"payment_type"` + PaymentAmounts []interface{} `json:"payment_amounts"` + OrderID string `json:"order_id"` + MerchantID string `json:"merchant_id"` + GrossAmount string `json:"gross_amount"` + FraudStatus string `json:"fraud_status"` + ExpiryTime string `json:"expiry_time"` + Currency string `json:"currency"` +} diff --git a/entity/notification.go b/entity/notification.go new file mode 100644 index 0000000..1d1ffad --- /dev/null +++ b/entity/notification.go @@ -0,0 +1,27 @@ +package entity + +import ( + "time" +) + +type Notification struct { + ID int `json:"id"` + Type string `json:"type"` + Message string `json:"message"` + IsRead bool `json:"is_read"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt time.Time `json:"deleted_at"` +} + +func NewNotification(id int, typ, message string, isRead bool, createdAt, updatedAt, deletedAt time.Time) *Notification { + return &Notification{ + ID: id, + Type: typ, + Message: message, + IsRead: isRead, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + DeletedAt: deletedAt, + } +} \ No newline at end of file diff --git a/entity/order.go b/entity/order.go new file mode 100644 index 0000000..e4f6f0e --- /dev/null +++ b/entity/order.go @@ -0,0 +1,43 @@ +package entity + +import ( + "time" +) + +type Order struct { + Id int64 `json:"id"` + TicketID int64 `json:"ticket_id"` + Ticket Ticket `json:"ticket"` + UserID int64 `json:"user_id"` + User User `json:"user"` + Quantity int64 `json:"quantity"` + Total int64 `json:"total"` + Status string `json:"status"` + OrderAt time.Time `json:"order_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt *time.Time `json:"deleted_at,omitempty"` + OrderBy string `json:"order_by"` + UpdateBy string `json:"-"` + DeleteBy string `json:"-"` +} + +// membuat func NewOrder dengan memanggil tiketID, quantity, total, dan OrderAt +func NewOrder(ticketID, quantity, userID int64, status string) *Order { + return &Order{ + TicketID: ticketID, + Quantity: quantity, + UserID: userID, + Status: status, + OrderAt: time.Now(), + } +} + +type OrderDetail struct { + UserID int64 `json:"user_id"` + Quantity int64 `json:"quantity"` + Total int `json:"total"` + Status string `json:"status"` + OrderAt time.Time `json:"order_at"` + UpdatedAt time.Time `json:"updated_at"` + Ticket TicketDetail `json:"ticket"` +} diff --git a/entity/payment.go b/entity/payment.go new file mode 100644 index 0000000..012a6be --- /dev/null +++ b/entity/payment.go @@ -0,0 +1,19 @@ +package entity + +type PaymentRequest struct { + OrderID string + Amount int64 + FirstName string + LastName string + Email string +} + +func NewPaymentRequest(orderID string, amount int64, firstName, lastName, email string) *PaymentRequest { + return &PaymentRequest{ + OrderID: orderID, + Amount: amount, + FirstName: firstName, + LastName: lastName, + Email: email, + } +} diff --git a/entity/tiket.go b/entity/tiket.go new file mode 100644 index 0000000..d2d4f7c --- /dev/null +++ b/entity/tiket.go @@ -0,0 +1,59 @@ +package entity + +import ( + "time" +) + +type Ticket struct { + ID int64 `json:"id"` + Image string `json:"image"` + Location string `json:"location"` + Date string // Format: YYYY-MM-DD + Title string `json:"title"` + Description string `json:"description"` + Price int64 `json:"price"` + Status string `json:"Status"` // e.g., 'available', 'sold' + Quota int64 `json:"Quota"` + Category string `json:"category"` + Terjual int64 `json:"Terjual"` // e.g., 1000, 5000, 10000 + CreatedAt time.Time `json:"-"` + UpdatedAt time.Time `json:"-"` + DeletedAt time.Time `json:"-"` +} + +func NewTicket(image, location, date, title, description, category string, price, quota, terjual int64) *Ticket { + return &Ticket{ + Image: image, + Location: location, + Date: date, + Title: title, + Description: description, + Category: category, + Price: price, + Quota: quota, + Terjual: terjual, + } +} + +func UpdateTicket(id int64, image, location, date, title, description, category string, price, quota, terjual int64) *Ticket { + return &Ticket{ + ID: id, + Image: image, + Location: location, + Date: date, + Title: title, + Description: description, + Category: category, + Price: price, + Quota: quota, + Terjual: terjual, + } +} + +type TicketDetail struct { + Image string `json:"image"` + Location string `json:"location"` + Date time.Time `json:"date"` + Title string `json:"title"` + Description string `json:"description"` +} diff --git a/entity/topup.go b/entity/topup.go new file mode 100644 index 0000000..57cf022 --- /dev/null +++ b/entity/topup.go @@ -0,0 +1,27 @@ +package entity + +import ( + "time" +) + +// Topup struct menyimpan data top-up +type Topup struct { + ID string `json:"id" db:"id"` + UserID int `json:"user_id" db:"user_id"` + Amount int `json:"amount" db:"amount"` + Status int `json:"status" db:"status"` + SnapURL string `json:"snap_url" db:"snap_url"` + CreatedAt time.Time `json:"created_at" db:"created_at"` + UpdatedAt time.Time `json:"updated_at" db:"updated_at"` +} + +// TopupStatus struct menyimpan data status top-up +type NewTopup struct { + ID string `json:"id" db:"id"` + UserID int `json:"user_id" db:"user_id"` + Amount int `json:"amount" db:"amount"` + Status int `json:"status" db:"status"` + SnapURL string `json:"snap_url" db:"snap_url"` + CreatedAt time.Time `json:"created_at" db:"created_at"` + UpdatedAt time.Time `json:"updated_at" db:"updated_at"` +} \ No newline at end of file diff --git a/entity/transaction.go b/entity/transaction.go new file mode 100644 index 0000000..a264125 --- /dev/null +++ b/entity/transaction.go @@ -0,0 +1,18 @@ +package entity + +type Transaction struct { + ID int64 + OrderID string + UserID int64 + Amount int64 + Status string +} + +func NewTransaction(orderID string, userID int64, amount int64, status string) *Transaction { + return &Transaction{ + OrderID: orderID, + UserID: userID, + Amount: amount, + Status: status, + } +} diff --git a/entity/user.go b/entity/user.go index 86c5e20..f70fc99 100644 --- a/entity/user.go +++ b/entity/user.go @@ -1,37 +1,97 @@ -package entity - -import ( - "time" - - "gorm.io/gorm" -) - -type User struct { - ID int64 `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - Password string `json:"-"` - CreatedAt time.Time `json:"-"` - UpdatedAt time.Time `json:"-"` - DeletedAt gorm.DeletedAt `json:"-"` -} - -func NewUser(name, email, password string) *User { - return &User{ - Name: name, - Email: email, - Password: password, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - } -} - -func UpdateUser(id int64, name, email, password string) *User { - return &User{ - ID: id, - Name: name, - Email: email, - Password: password, - UpdatedAt: time.Now(), - } -} +package entity + +import ( + "time" +) + +type User struct { + ID int64 `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + Number string `json:"number"` + Roles string `json:"roles"` + Saldo int64 `json:"saldo"` + Password string `json:"-"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt *time.Time `json:"deleted_at,omitempty"` +} + +// Admin New User +func NewUser(name, email, number, password, roles string, saldo int64) *User { + return &User{ + Name: name, + Email: email, + Number: number, + Roles: roles, + Saldo: saldo, + Password: password, + CreatedAt: time.Now(), + } +} + +// Admin Update User +func UpdateUser(id int64, name, email, number, roles, password string, saldo int64) *User { + return &User{ + ID: id, + Name: name, + Email: email, + Number: number, + Roles: roles, + Password: password, + Saldo: saldo, + UpdatedAt: time.Now(), + } +} + +// Public Register +func Register(email, password, roles, number string) *User { + return &User{ + Email: email, + Password: password, + Roles: roles, + Number: number, + } +} + +// user update by self +func UpdateProfile(id int64, name, email, number, password string) *User { + return &User{ + ID: id, + Name: name, + Email: email, + Number: number, + Password: password, + UpdatedAt: time.Now(), + } +} + +// Update the return type to be *User +func DeleteUserSelfByEmail(email string) *User { + return &User{ + Email: email, + DeletedAt: nil, + } +} + +func UpgradeSaldo(id int64, saldo int64) *User { + return &User{ + ID: id, + Saldo: saldo, + } +} + +// user logout +func UserLogout(id int64) *User { + return &User{ + ID: id, + } +} + +// updatesaldo +func UpdateSaldo(id int64, saldo int64) *User { + return &User{ + ID: id, + Saldo: saldo, + } +} diff --git a/entity/weather.go b/entity/weather.go deleted file mode 100644 index d634b6b..0000000 --- a/entity/weather.go +++ /dev/null @@ -1,4 +0,0 @@ -package entity - -type Weather struct { -} diff --git a/go.mod b/go.mod index 22e6882..ac66a72 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/zhikariz/weather-app +module Ticketing go 1.20 @@ -6,9 +6,13 @@ require ( github.com/caarlos0/env/v6 v6.10.1 github.com/creasty/defaults v1.7.0 github.com/go-playground/validator/v10 v10.16.0 + github.com/golang-jwt/jwt/v5 v5.0.0 github.com/joho/godotenv v1.5.1 github.com/labstack/echo-jwt/v4 v4.2.0 - github.com/labstack/echo/v4 v4.11.2 + github.com/labstack/echo/v4 v4.11.3 + github.com/midtrans/midtrans-go v1.3.7 + github.com/zhikariz/weather-app v0.0.0-20231205134708-aadb44694e44 + golang.org/x/crypto v0.16.0 gorm.io/driver/postgres v1.5.4 gorm.io/gorm v1.25.5 ) @@ -18,7 +22,6 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.4.3 // indirect @@ -30,9 +33,8 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect ) diff --git a/go.sum b/go.sum index 28f5603..e64217e 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/labstack/echo-jwt/v4 v4.2.0 h1:odSISV9JgcSCuhgQSV/6Io3i7nUmfM/QkBeR5GVJj5c= github.com/labstack/echo-jwt/v4 v4.2.0/go.mod h1:MA2RqdXdEn4/uEglx0HcUOgQSyBaTh5JcaHIan3biwU= -github.com/labstack/echo/v4 v4.11.2 h1:T+cTLQxWCDfqDEoydYm5kCobjmHwOwcv4OJAPHilmdE= -github.com/labstack/echo/v4 v4.11.2/go.mod h1:UcGuQ8V6ZNRmSweBIJkPvGfwCMIlFmiqrPqiEBfPYws= +github.com/labstack/echo/v4 v4.11.3 h1:Upyu3olaqSHkCjs1EJJwQ3WId8b8b1hxbogyommKktM= +github.com/labstack/echo/v4 v4.11.3/go.mod h1:UcGuQ8V6ZNRmSweBIJkPvGfwCMIlFmiqrPqiEBfPYws= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= @@ -45,6 +45,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/midtrans/midtrans-go v1.3.7 h1:3vL9ydlVqp9VfRHDzOG17w1D6X9241jj6LQdPTxVE/g= +github.com/midtrans/midtrans-go v1.3.7/go.mod h1:5hN2oiZDP3/SwSBxHPTg8eC/RVoRE9DXQOY1Ah9au10= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -61,19 +63,21 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +github.com/zhikariz/weather-app v0.0.0-20231205134708-aadb44694e44 h1:CJuyXVSmaBFrmRObMJ3umd47aLM3DhMBGv78/A0kZEs= +github.com/zhikariz/weather-app v0.0.0-20231205134708-aadb44694e44/go.mod h1:+dVsK3JyKP6D25PJHuD09eg3NA5/pSAYmiCrU0nOH1g= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/builder/builder.go b/internal/builder/builder.go index be59d09..0d9eb55 100644 --- a/internal/builder/builder.go +++ b/internal/builder/builder.go @@ -1,25 +1,84 @@ -package builder - -import ( - "github.com/zhikariz/weather-app/internal/config" - "github.com/zhikariz/weather-app/internal/http/handler" - "github.com/zhikariz/weather-app/internal/http/router" - "github.com/zhikariz/weather-app/internal/repository" - "github.com/zhikariz/weather-app/internal/service" - "gorm.io/gorm" -) - -func BuildPublicRoutes(cfg *config.Config, db *gorm.DB) []*router.Route { - userRepository := repository.NewUserRepository(db) - loginService := service.NewLoginService(userRepository) - tokenService := service.NewTokenService(cfg) - authHandler := handler.NewAuthHandler(loginService, tokenService) - return router.PublicRoutes(authHandler) -} - -func BuildPrivateRoutes(cfg *config.Config, db *gorm.DB) []*router.Route { - userRepository := repository.NewUserRepository(db) - userService := service.NewUserService(userRepository) - userHandler := handler.NewUserHandler(userService) - return router.PrivateRoutes(userHandler) -} +package builder + +import ( + "Ticketing/internal/config" + "Ticketing/internal/http/handler" + "Ticketing/internal/http/router" + "Ticketing/internal/repository" + "Ticketing/internal/service" + + "github.com/midtrans/midtrans-go/snap" + "gorm.io/gorm" +) + +func BuildPublicRoutes(cfg *config.Config, db *gorm.DB, midtransClient snap.Client) []*router.Route { + registrationRepository := repository.NewRegistrationRepository(db) + registrationService := service.NewRegistrationService(registrationRepository) + transactionRepository := repository.NewTransactionRepository(db) + + userRepository := repository.NewUserRepository(db) + loginService := service.NewLoginService(userRepository) + tokenService := service.NewTokenService(cfg) + transactionService := service.NewTransactionService(transactionRepository) + paymentService := service.NewPaymentService(midtransClient) + + // Create and initialize userService + userService := service.NewUserService(userRepository) + + transactionHandler := handler.NewTransactionHandler(transactionService, paymentService, userService) + + BlogRepository := repository.NewBlogRepository(db) + BlogService := service.NewBlogService(BlogRepository) + BlogHandler := handler.NewBlogHandler(BlogService) + + ticketRepository := repository.NewTicketRepository(db) + ticketService := service.NewTicketService(ticketRepository) + ticketHandler := handler.NewTicketHandler(ticketService) + + authHandler := handler.NewAuthHandler(registrationService, loginService, tokenService) + + // Update the line below with the additional TicketHandler argument + return router.PublicRoutes(authHandler, ticketHandler, BlogHandler, transactionHandler) +} + +func BuildPrivateRoutes(cfg *config.Config, db *gorm.DB, midtransClient snap.Client) []*router.Route { + // Create a user handler + userRepository := repository.NewUserRepository(db) + userService := service.NewUserService(userRepository) + userHandler := handler.NewUserHandler(userService) + + transactionRepository := repository.NewTransactionRepository(db) + paymentService := service.NewPaymentService(midtransClient) + transactionService := service.NewTransactionService(transactionRepository) + + // Create and initialize transactionHandler with userService + transactionHandler := handler.NewTransactionHandler(transactionService, paymentService, userService) + + // Create a ticket handler + ticketRepository := repository.NewTicketRepository(db) + ticketService := service.NewTicketService(ticketRepository) + ticketHandler := handler.NewTicketHandler(ticketService) + + // Create a Blog handler + BlogRepository := repository.NewBlogRepository(db) + BlogService := service.NewBlogService(BlogRepository) + BlogHandler := handler.NewBlogHandler(BlogService) + + // Create an order handler + OrderRepository := repository.NewOrderRepository(db) + OrderService := service.NewOrderService(OrderRepository) + OrderHandler := handler.NewOrderHandler(OrderService) + + NotificationRepository := repository.NewNotificationRepository(db) + NotificationService := service.NewNotificationService(NotificationRepository) + NotificationHandler := handler.NewNotificationHandler(NotificationService) + + TopupRepository := repository.NewTopupRepository(db) + TopupService := service.NewTopupService(TopupRepository) + + // Create and initialize TopupHandler with TopupService + TopupHandler := handler.NewTopupHandler(TopupService) + + // Menggunakan PrivateRoutes dengan kedua handler + return router.PrivateRoutes(userHandler, ticketHandler, BlogHandler, OrderHandler, NotificationHandler, transactionHandler, TopupHandler) +} diff --git a/internal/config/config.go b/internal/config/config.go index 4f76170..3a9fa10 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,59 +1,70 @@ -package config - -import ( - "errors" - - "github.com/caarlos0/env/v6" - "github.com/joho/godotenv" -) - -// Config is a config -type Config struct { - Port string `env:"PORT" envDefault:"8080"` - Postgres PostgresConfig `envPrefix:"POSTGRES_"` - JWT JwtConfig `envPrefix:"JWT_"` -} - -// JwtConfig is a config for jwt -type JwtConfig struct { - SecretKey string `env:"SECRET_KEY"` -} - -// PostgresConfig is a config for postgres -type PostgresConfig struct { - Host string `env:"HOST" envDefault:"localhost"` - Port string `env:"PORT" envDefault:"5432"` - User string `env:"USER" envDefault:"postgres"` - Password string `env:"PASSWORD" envDefault:"postgres"` - Database string `env:"DATABASE" envDefault:"postgres"` -} - -// NewConfig creates a new config -func NewConfig(envPath string) (*Config, error) { - cfg, err := parseConfig(envPath) - if err != nil { - return nil, err - } - return cfg, nil -} - -// parseConfig parses the configuration file located at envPath and returns a -// Config struct and an error if any. It uses the godotenv package to load the -// environment variables from the file and the env package to parse them into -// the Config struct. -// -// envPath: The path to the environment file. -// Returns: A pointer to the Config struct and an error. -func parseConfig(envPath string) (*Config, error) { - err := godotenv.Load(envPath) - if err != nil { - return nil, errors.New("failed to load env") - } - - cfg := &Config{} - err = env.Parse(cfg) - if err != nil { - return nil, errors.New("failed to parse config") - } - return cfg, nil -} +package config + +//NOTE : +// direktory ini berfungsi untuk konfigurasi database, port, dan lain-lain +import ( + "errors" + + "github.com/caarlos0/env/v6" + "github.com/joho/godotenv" +) + +// Config adalah konfigurasi untuk aplikasi +type Config struct { + Env string `env:"ENV" envDefault:"development"` + Port string `env:"PORT" envDefault:"8080"` + Postgres PostgresConfig `envPrefix:"POSTGRES_"` + JWT JwtConfig `envPrefix:"JWT_"` + MidtransConfig MidtransConfig `envPrefix:"MIDTRANS_"` +} + +type MidtransConfig struct { + BaseURL string `env:"BASE_URL"` + ClientKey string `env:"CLIENT_KEY"` + ServerKey string `env:"SERVER_KEY"` +} + +// JwtConfig adalah konfigurasi untuk JWT +type JwtConfig struct { + SecretKey string `env:"SECRET_KEY"` +} + +// PostgresConfig adalah konfigurasi untuk koneksi ke database postgres +type PostgresConfig struct { + Host string `env:"HOST" envDefault:"localhost"` + Port string `env:"PORT" envDefault:"5432"` + User string `env:"USER" envDefault:"postgres"` + Password string `env:"PASSWORD" envDefault:"postgres"` + Database string `env:"DATABASE" envDefault:"postgres"` +} + +// untuk membuat new config +func NewConfig(envPath string) (*Config, error) { + cfg, err := parseConfig(envPath) + if err != nil { + return nil, err + } + return cfg, nil +} + +// untuk parse config +func parseConfig(envPath string) (*Config, error) { + err := godotenv.Load(envPath) + if err != nil { + return nil, errors.New("failed to load env") + } + + cfg := &Config{} + err = env.Parse(cfg) + if err != nil { + return nil, errors.New("failed to parse config") + } + return cfg, nil +} + +// func init() { +// // Isi konfigurasi Midtrans Anda di sini +// MidtransConfig.ClientKey = "" +// MidtransConfig.ServerKey = "" +// MidtransConfig.IsProduction = false // Ganti menjadi true jika sudah di production +// } diff --git a/internal/http/binder/binder.go b/internal/http/binder/binder.go index 318275d..cb777ee 100644 --- a/internal/http/binder/binder.go +++ b/internal/http/binder/binder.go @@ -1,36 +1,41 @@ -package binder - -import ( - "github.com/creasty/defaults" - "github.com/go-playground/validator/v10" - "github.com/labstack/echo/v4" - internalValidator "github.com/zhikariz/weather-app/internal/http/validator" -) - -type Binder struct { - defaultBinder *echo.DefaultBinder - *internalValidator.FormValidator -} - -func NewBinder( - dbr *echo.DefaultBinder, - vdr *internalValidator.FormValidator) *Binder { - return &Binder{dbr, vdr} -} - -func (b *Binder) Bind(i interface{}, c echo.Context) error { - if err := b.defaultBinder.Bind(i, c); err != nil { - return err - } - - if err := defaults.Set(i); err != nil { - return err - } - - if err := b.Validate(i); err != nil { - errs := err.(validator.ValidationErrors) - return errs - } - - return nil -} +package binder +// folder ini digunakan untuk mengcombine data yang diinputkan dengan data yang diinginkan +import ( + internalValidator "Ticketing/internal/http/validator" + + "github.com/creasty/defaults" + "github.com/go-playground/validator/v10" + "github.com/labstack/echo/v4" +) + +// untuk override echo.Binder , karena untuk mapping apa saja yang perlu di binding +type Binder struct { + defaultBinder *echo.DefaultBinder + *internalValidator.FormValidator +} + +//untuk mereturn struct binder diatas +func NewBinder( + dbr *echo.DefaultBinder, + vdr *internalValidator.FormValidator) *Binder { + return &Binder{dbr, vdr} +} + +// untuk melakukan binding +func (b *Binder) Bind(i interface{}, c echo.Context) error { + if err := b.defaultBinder.Bind(i, c); err != nil { + return err + } + + if err := defaults.Set(i); err != nil { + return err + } + + // untuk melakukan validasi + if err := b.Validate(i); err != nil { + errs := err.(validator.ValidationErrors) + return errs + } + + return nil +} diff --git a/internal/http/handler/auth.handler.go b/internal/http/handler/auth.handler.go deleted file mode 100644 index 6b344a1..0000000 --- a/internal/http/handler/auth.handler.go +++ /dev/null @@ -1,53 +0,0 @@ -package handler - -import ( - "net/http" - - "github.com/labstack/echo/v4" - "github.com/zhikariz/weather-app/internal/http/validator" - "github.com/zhikariz/weather-app/internal/service" -) - -type AuthHandler struct { - loginService service.LoginUseCase - tokenService service.TokenUseCase -} - -func NewAuthHandler( - loginService service.LoginUseCase, - tokenService service.TokenUseCase, -) *AuthHandler { - return &AuthHandler{ - loginService: loginService, - tokenService: tokenService, - } -} - -func (h *AuthHandler) Login(ctx echo.Context) error { - var input struct { - Email string `json:"email" validate:"required"` - Password string `json:"password" validate:"required"` - } - - if err := ctx.Bind(&input); err != nil { - return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) - } - - user, err := h.loginService.Login(ctx.Request().Context(), input.Email, input.Password) - - if err != nil { - return ctx.JSON(http.StatusUnprocessableEntity, err) - } - - accessToken, err := h.tokenService.GenerateAccessToken(ctx.Request().Context(), user) - - if err != nil { - return ctx.JSON(http.StatusUnprocessableEntity, err) - } - - data := map[string]string{ - "access_token": accessToken, - } - - return ctx.JSON(http.StatusOK, data) -} diff --git a/internal/http/handler/auth_hendler.go b/internal/http/handler/auth_hendler.go new file mode 100644 index 0000000..3978a2a --- /dev/null +++ b/internal/http/handler/auth_hendler.go @@ -0,0 +1,129 @@ +package handler + +import ( + "Ticketing/entity" + "Ticketing/internal/http/validator" + "Ticketing/internal/service" + "net/http" + + "github.com/labstack/echo/v4" +) + +type AuthHandler struct { + registrationService service.RegistrationUseCase // untuk regist + loginService service.LoginUseCase //untuk memanggil service yang ngelakuin pengecekan user. + tokenService service.TokenUsecase //untuk memanggil func akses token + // buyercreateaccountService service.BuyerCreateAccountUseCase +} + +// ini func untuk type AuthHandler +func NewAuthHandler( + registartionService service.RegistrationUseCase, + loginService service.LoginUseCase, + tokenService service.TokenUsecase, + // buyercreateaccountService service.BuyerCreateAccountUseCase, +) *AuthHandler { + return &AuthHandler{ + registrationService: registartionService, + loginService: loginService, + tokenService: tokenService, + // buyercreateaccountService: buyercreateaccountService, + } +} + +// func ini untuk login +func (h *AuthHandler) Login(ctx echo.Context) error { + //pengecekan request + var input struct { + Email string `json:"email" validate:"required,email"` + Password string `json:"password" validate:"required,min=8"` + } + + if err := ctx.Bind(&input); err != nil { // di cek pake validate buat masukin input + return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + //untuk manggil login service di folder service + user, err := h.loginService.Login(ctx.Request().Context(), input.Email, input.Password) + + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + //untuk manggil token service di folder service + accessToken, err := h.tokenService.GenerateAccessToken(ctx.Request().Context(), user) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + data := map[string]interface{}{ + "access_token": accessToken, + } + return ctx.JSON(http.StatusOK, data) +} + +// Public Register +func (h *AuthHandler) Registration(ctx echo.Context) error { + //pengecekan request + var input struct { + Email string `json:"email" validate:"required,email"` + Password string `json:"password" validate:"required,min=8"` + Roles string `json:"roles" default:"Buyer"` + Number string `json:"number" validate:"required,min=11,max=13"` + } + + if err := ctx.Bind(&input); err != nil { // di cek pake validate buat masukin input + return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + //untuk manggil registration service di folder service + user := entity.Register(input.Email, input.Password, input.Roles, input.Number) + err := h.registrationService.Registration(ctx.Request().Context(), user) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + accessToken, err := h.tokenService.GenerateAccessToken(ctx.Request().Context(), user) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + return ctx.JSON(http.StatusOK, map[string]interface{}{ + "message": "User registration successfully", + "access_token": accessToken, + }) + +} + +// BuyerCreateAccount +// func (h *AuthHandler) BuyerCreateAccount(ctx echo.Context) error { +// // Pengecekan request +// var input struct { +// Name string `json:"name" validate:"required"` +// Email string `json:"email" validate:"required,email"` +// Number string `json:"number" validate:"required,min=11,max=13"` +// Password string `json:"password" validate:"required,min=8"` +// } + +// if err := ctx.Bind(&input); err != nil { // Di cek pake validate buat masukin input +// return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) +// } + +// // Untuk manggil BuyerCreateAccount service di folder service +// user := entity.Register(input.Email, input.Password, "Buyer", input.Number) +// err := h.buyercreateaccountService.BuyerCreateAccount(ctx.Request().Context(), user) +// if err != nil { +// return ctx.JSON(http.StatusUnprocessableEntity, err) +// } + +// accessToken, err := h.tokenService.GenerateAccessToken(ctx.Request().Context(), user) +// if err != nil { +// return ctx.JSON(http.StatusUnprocessableEntity, err) +// } + +// return ctx.JSON(http.StatusOK, map[string]interface{}{ +// "message": "Buyer account created successfully", +// "access_token": accessToken, +// }) +// } + + diff --git a/internal/http/handler/blog_hendler.go b/internal/http/handler/blog_hendler.go new file mode 100644 index 0000000..5de4650 --- /dev/null +++ b/internal/http/handler/blog_hendler.go @@ -0,0 +1,181 @@ +package handler + +import ( + "net/http" + "Ticketing/entity" + "Ticketing/internal/service" + + "github.com/labstack/echo/v4" + "strconv" + "time" + "Ticketing/internal/http/validator" +) + +// BlogHandler handles HTTP requests related to Blogs. +type BlogHandler struct { + blogService service.BlogUseCase +} + +// NewBlogHandler creates a new instance of BlogHandler. +func NewBlogHandler(blogService service.BlogUseCase) *BlogHandler { + return &BlogHandler{blogService} +} + +// GetAllBlog +func (h *BlogHandler) GetAllBlogs(c echo.Context) error { + Blogs, err := h.blogService.GetAllBlogs(c.Request().Context()) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": Blogs, + }) +} + + +// CreateBlog +func (h *BlogHandler) CreateBlog(c echo.Context) error { + var input struct { + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + Date time.Time `json:"date"` + Image string `json:"image"` + } + + // Input validation + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + // Convert input.Date to a string with the desired format + + + // Create a Blog object + Blog := entity.Blog{ + Title: input.Title, + Description: input.Description, + Image: input.Image, + Date: time.Now().Format("2006-01-02T15:04:05Z"), + } + + // Call the blogService to create the Blog + err := h.blogService.CreateBlog(c.Request().Context(), &Blog) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + // Return a success message + return c.JSON(http.StatusCreated, "Blog created successfully") +} + + +// GetBlog handles the retrieval of a Blog by ID. +func (h *BlogHandler) GetBlog(c echo.Context) error { + idStr := c.Param("id") // assuming the ID is passed as a URL parameter as a string + id, err := strconv.ParseInt(idStr, 10, 64) // Convert the string to int64 + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]interface{}{ + "error": "Invalid ID", + }) + } + + Blog, err := h.blogService.GetBlog(c.Request().Context(), id) + if err != nil { + return c.JSON(http.StatusNotFound, map[string]interface{}{ + "error": err.Error(), + }) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": map[string]interface{}{ + "id": Blog.ID, + "title": Blog.Title, + "description": Blog.Description, + "image": Blog.Image, + "date": Blog.Date, + "created": Blog.CreatedAt, + }, + }) +} + + +// UpdateBlog handles the update of an existing Blog. +func (h *BlogHandler) UpdateBlog(c echo.Context) error { + var input struct { + ID int64 `param:"id" validate:"required"` + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + Image string `json:"image"` + Date time.Time `json:"date"` + } + + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + + // Convert input.Date to a formatted string + dateStr := input.Date.Format("2006-01-02T15:04:05Z") + + // Create a Blog object + Blog := entity.Blog{ + ID: input.ID, // Assuming ID is already of type int64 + Title: input.Title, + Description: input.Description, + Image: input.Image, + Date: dateStr, // Assign the formatted date string + } + + + + err := h.blogService.UpdateBlog(c.Request().Context(), &Blog) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "message": "Blog updated successfully", + "Blog": Blog, + }) +} + + +// DeleteBlog handles the deletion of a Blog by ID. +func (h *BlogHandler) DeleteBlog(c echo.Context) error { + var input struct { + ID int64 `param:"id" validate:"required"` + } + + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + err := h.blogService.DeleteBlog(c.Request().Context(), input.ID) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "message": "Blog deleted successfully", + }) +} + +// SearchBlog handles the search of a Blog by title. +func (h *BlogHandler) SearchBlog(c echo.Context) error { + var input struct { + Search string `param:"search" validate:"required"` //harus pramater search + } + + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + Blogs, err := h.blogService.SearchBlog(c.Request().Context(), input.Search) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": Blogs, + }) +} diff --git a/internal/http/handler/notification_hendler.go b/internal/http/handler/notification_hendler.go new file mode 100644 index 0000000..092c5ca --- /dev/null +++ b/internal/http/handler/notification_hendler.go @@ -0,0 +1,72 @@ +package handler + +import ( + "Ticketing/entity" + "Ticketing/internal/service" + "net/http" + + "Ticketing/internal/http/validator" + "time" + + "github.com/labstack/echo/v4" +) + +type NotificationHandler struct { + notificationService service.NotificationUsecase +} + +func NewNotificationHandler(notificationService service.NotificationUsecase) *NotificationHandler { + return &NotificationHandler{notificationService} +} + +// GetAllNotification +func (h *NotificationHandler) GetAllNotification(c echo.Context) error { + Notifications, err := h.notificationService.GetAllNotification(c.Request().Context()) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": Notifications, + }) +} + +// func untuk create notification +func (h *NotificationHandler) CreateNotification(c echo.Context) error { + var input struct { + Type string `json:"type" validate:"required"` + Message string `json:"message" validate:"required"` + Is_Read bool `json:"is_read"` + Create_at time.Time `json:"create_at"` + } + + // Input validation + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + // Create a Notification object + Notification := entity.Notification{ + Type: input.Type, + Message: input.Message, + IsRead: input.Is_Read, + CreatedAt: time.Now(), + } + + err := h.notificationService.CreateNotification(c.Request().Context(), &Notification) + if err != nil { + return c.JSON(http.StatusInternalServerError, validator.ValidatorErrors(err)) + } + + return c.JSON(http.StatusCreated, Notification) +} + +// get notification after get chage value isRead to true and only get notification if isread false +func (h *NotificationHandler) UserGetNotification(c echo.Context) error { + Notifications, err := h.notificationService.UserGetNotification(c.Request().Context()) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": Notifications, + }) +} diff --git a/internal/http/handler/order_handler.go b/internal/http/handler/order_handler.go new file mode 100644 index 0000000..767e86a --- /dev/null +++ b/internal/http/handler/order_handler.go @@ -0,0 +1,225 @@ +package handler + +import ( + "Ticketing/common" + "Ticketing/entity" + "Ticketing/internal/http/validator" + "Ticketing/internal/service" + "errors" + "net/http" + + "github.com/golang-jwt/jwt/v5" + "github.com/labstack/echo/v4" +) + +type OrderHandler struct { + OrderService service.OrderUsecase +} + +func NewOrderHandler(OrderService service.OrderUsecase) *OrderHandler { + return &OrderHandler{OrderService} +} + +// func untuk create order +func (h *OrderHandler) CreateOrder(ctx echo.Context) error { + var input struct { + TicketID int64 `json:"ticket_id" validate:"required"` + Quantity int64 `json:"quantity" validate:"required"` + UserID int64 `json:"user_id" validate:"required"` + Status string `json:"status" validate:"required"` + } + + if err := ctx.Bind(&input); err != nil { + return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + // Mendapatkan informasi saldo pengguna sebelum membuat pesanan + userBalance, err := h.OrderService.GetUserBalance(ctx.Request().Context(), input.UserID) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + // Mengambil informasi tiket dari TicketService untuk mendapatkan harga tiket + ticketPrice, err := h.OrderService.GetTicketPrice(ctx.Request().Context(), input.TicketID) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + // Memeriksa apakah saldo cukup untuk membuat pesanan + if userBalance < (input.Quantity * ticketPrice) { + return ctx.JSON(http.StatusUnprocessableEntity, errors.New("insufficient balance")) + } + + order := entity.NewOrder(input.TicketID, input.Quantity, input.UserID, input.Status) + err = h.OrderService.CreateOrder(ctx.Request().Context(), order) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + // Mengurangkan saldo pengguna setelah membuat pesanan + err = h.OrderService.UpdateUserBalance(ctx.Request().Context(), input.UserID, input.Quantity*ticketPrice) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + return ctx.JSON(http.StatusCreated, "Order created successfully") +} + +// Get All Order +func (h *OrderHandler) GetAllOrders(ctx echo.Context) error { + // Implementasi untuk mendapatkan semua pesanan dari usecase + orders, err := h.OrderService.GetOrders(ctx.Request().Context()) + if err != nil { + return ctx.JSON(http.StatusBadRequest, echo.NewHTTPError(http.StatusBadRequest, err.Error())) + } + + var orderDetails []map[string]interface{} + for _, order := range orders { + ticket, err := h.OrderService.GetTicketByID(ctx.Request().Context(), order.TicketID) + if err != nil { + return ctx.JSON(http.StatusInternalServerError, echo.NewHTTPError(http.StatusInternalServerError, err.Error())) + } + + orderDetail := map[string]interface{}{ + "user_id": order.UserID, + "ticket": ticket, + } + orderDetails = append(orderDetails, orderDetail) + } + + return ctx.JSON(http.StatusOK, map[string]interface{}{ + "message": "Get all orders success", + "order_details": orderDetails, + }) +} + +// get order by user_id +func (h *OrderHandler) GetOrderByUserID(ctx echo.Context) error { + // Implementasi untuk mendapatkan semua pesanan dari usecase + orders, err := h.OrderService.GetOrders(ctx.Request().Context()) + if err != nil { + return ctx.JSON(http.StatusBadRequest, echo.NewHTTPError(http.StatusBadRequest, err.Error())) + } + + var orderDetails []map[string]interface{} + for _, order := range orders { + ticket, err := h.OrderService.GetTicketByID(ctx.Request().Context(), order.TicketID) + if err != nil { + return ctx.JSON(http.StatusInternalServerError, echo.NewHTTPError(http.StatusInternalServerError, err.Error())) + } + + orderDetail := map[string]interface{}{ + "user_id": order.UserID, + "ticket": ticket, + } + orderDetails = append(orderDetails, orderDetail) + } + + return ctx.JSON(http.StatusOK, map[string]interface{}{ + "message": "Get all orders success", + "order_details": orderDetails, + }) +} + +// user create order +func (h *OrderHandler) UserCreateOrder(ctx echo.Context) error { + var input struct { + UserID int64 + TicketID int64 `json:"ticket_id" validate:"required"` + Quantity int64 `json:"quantity" validate:"required"` + Status string `json:"status" default:"success"` + } + + // Get JWT token from the context + token, ok := ctx.Get("user").(*jwt.Token) + if !ok { + return ctx.JSON(http.StatusUnauthorized, errors.New("missing or invalid token")) + } + + // Extract claims from the JWT token + claims, ok := token.Claims.(*common.JwtCustomClaims) + if !ok { + return ctx.JSON(http.StatusUnauthorized, errors.New("invalid token claims")) + } + + // Assign UserID from the JWT claims to the input struct + input.UserID = claims.ID + + if err := ctx.Bind(&input); err != nil { + return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + // Remaining code remains the same... + + // Mendapatkan informasi saldo pengguna sebelum membuat pesanan + userBalance, err := h.OrderService.GetUserBalance(ctx.Request().Context(), input.UserID) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + // Mengambil informasi tiket dari TicketService untuk mendapatkan harga tiket + ticketPrice, err := h.OrderService.GetTicketPrice(ctx.Request().Context(), input.TicketID) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + // Memeriksa apakah saldo cukup untuk membuat pesanan + if userBalance < (input.Quantity * ticketPrice) { + return ctx.JSON(http.StatusUnprocessableEntity, errors.New("insufficient balance")) + } + + order := entity.NewOrder(input.TicketID, input.Quantity, input.UserID, input.Status) + err = h.OrderService.CreateOrder(ctx.Request().Context(), order) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + // Mengurangkan saldo pengguna setelah membuat pesanan + err = h.OrderService.UpdateUserBalance(ctx.Request().Context(), input.UserID, input.Quantity*ticketPrice) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + // Remaining code remains the same... + return ctx.JSON(http.StatusCreated, "Order created successfully") +} + +// Get order History by jwt +func (h *OrderHandler) GetOrderHistory(ctx echo.Context) error { + // Get JWT token from the context + token, ok := ctx.Get("user").(*jwt.Token) + if !ok { + return ctx.JSON(http.StatusUnauthorized, errors.New("missing or invalid token")) + } + + // Extract claims from the JWT token + claims, ok := token.Claims.(*common.JwtCustomClaims) + if !ok { + return ctx.JSON(http.StatusUnauthorized, errors.New("invalid token claims")) + } + + // Get all orders by user ID + orders, err := h.OrderService.GetOrderByUserID(ctx.Request().Context(), claims.ID) + if err != nil { + return ctx.JSON(http.StatusBadRequest, echo.NewHTTPError(http.StatusBadRequest, err.Error())) + } + + var orderDetails []map[string]interface{} + for _, order := range orders { + ticket, err := h.OrderService.GetTicketByID(ctx.Request().Context(), order.TicketID) + if err != nil { + return ctx.JSON(http.StatusInternalServerError, echo.NewHTTPError(http.StatusInternalServerError, err.Error())) + } + + orderDetail := map[string]interface{}{ + "user_id": order.UserID, + "ticket": ticket, + } + orderDetails = append(orderDetails, orderDetail) + } + + return ctx.JSON(http.StatusOK, map[string]interface{}{ + "message": "Get all orders success", + "order_details": orderDetails, + }) +} diff --git a/internal/http/handler/ticket_handler.go b/internal/http/handler/ticket_handler.go new file mode 100644 index 0000000..32ff4ae --- /dev/null +++ b/internal/http/handler/ticket_handler.go @@ -0,0 +1,396 @@ +package handler + +import ( + "Ticketing/entity" + "Ticketing/internal/service" + "net/http" + + "Ticketing/internal/http/validator" + "strconv" + "time" + + "github.com/labstack/echo/v4" +) + +// TicketHandler handles HTTP requests related to tickets. +type TicketHandler struct { + ticketService service.TicketUseCase +} + +// NewTicketHandler creates a new instance of TicketHandler. +func NewTicketHandler(ticketService service.TicketUseCase) *TicketHandler { + return &TicketHandler{ticketService} +} + +// GetAllTicket +func (h *TicketHandler) GetAllTickets(c echo.Context) error { + tickets, err := h.ticketService.GetAllTickets(c.Request().Context()) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": tickets, + }) +} + +// CreateTicket +func (h *TicketHandler) CreateTicket(c echo.Context) error { + var input struct { + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + Image string `json:"image"` + Location string `json:"location"` + Date time.Time `json:"date"` + Status string `json:"status"` + Price float64 `json:"price"` + Quota int `json:"quota"` + Terjual int `json:"terjual"` + Category string `json:"category"` + } + + // Input validation + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + // Convert input.Date to a string with the desired format + dateStr := input.Date.Format("2006-01-02T15:04:05Z") + + // Create a Ticket object + ticket := entity.Ticket{ + Title: input.Title, + Description: input.Description, + Image: input.Image, + Location: input.Location, + Date: dateStr, // Assign the formatted date string + Status: input.Status, + Price: int64(input.Price), + Quota: int64(input.Quota), + Terjual: int64(input.Terjual), + Category: input.Category, + CreatedAt: time.Now(), + } + + // Call the ticketService to create the ticket + err := h.ticketService.CreateTicket(c.Request().Context(), &ticket) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + // Return a success message + return c.JSON(http.StatusCreated, "Ticket created successfully") +} + +// GetTicket handles the retrieval of a ticket by ID. +// untuk sesudah login +func (h *TicketHandler) GetTicket(c echo.Context) error { + idStr := c.Param("id") // assuming the ID is passed as a URL parameter as a string + id, err := strconv.ParseInt(idStr, 10, 64) // Convert the string to int64 + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]interface{}{ + "error": "Invalid ID", + }) + } + + ticket, err := h.ticketService.GetTicket(c.Request().Context(), id) + if err != nil { + return c.JSON(http.StatusNotFound, map[string]interface{}{ + "error": err.Error(), + }) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": map[string]interface{}{ + "id": ticket.ID, + "title": ticket.Title, + "description": ticket.Description, + "image": ticket.Image, + "location": ticket.Location, + "date": ticket.Date, + "price": ticket.Price, + "quota": ticket.Quota, + "category": ticket.Category, + "created": ticket.CreatedAt, + }, + }) +} + +// UpdateTicket handles the update of an existing ticket. +func (h *TicketHandler) UpdateTicket(c echo.Context) error { + var input struct { + ID int64 `param:"id" validate:"required"` + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + Image string `json:"image"` + Location string `json:"location"` + Date time.Time `json:"date"` + Price float64 `json:"price"` + Quota int `json:"quota"` + Category string `json:"category"` + } + + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + // Convert input.Date to a formatted string + dateStr := input.Date.Format("2006-01-02T15:04:05Z") + + // Create a Ticket object + ticket := entity.Ticket{ + ID: input.ID, // Assuming ID is already of type int64 + Title: input.Title, + Description: input.Description, + Image: input.Image, + Location: input.Location, + Date: dateStr, // Assign the formatted date string + Price: int64(input.Price), // Convert Price to int64 if needed + Quota: int64(input.Quota), // Convert Quota to int64 if needed + Category: input.Category, + } + + err := h.ticketService.UpdateTicket(c.Request().Context(), &ticket) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "message": "Ticket updated successfully", + "data": map[string]interface{}{ + "id": ticket.ID, + "title": ticket.Title, + "description": ticket.Description, + "image": ticket.Image, + "location": ticket.Location, + "date": ticket.Date, + "price": ticket.Price, + "quota": ticket.Quota, + "category": ticket.Category, + "update": ticket.UpdatedAt, + }, + }) +} + +// DeleteTicket handles the deletion of a ticket by ID. +func (h *TicketHandler) DeleteTicket(c echo.Context) error { + var input struct { + ID int64 `param:"id" validate:"required"` + } + + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + err := h.ticketService.DeleteTicket(c.Request().Context(), input.ID) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "message": "Ticket deleted successfully", + }) +} + +// SearchTicket handles the search of a ticket by title. +func (h *TicketHandler) SearchTicket(c echo.Context) error { + var input struct { + Search string `param:"search" validate:"required"` //harus pramater search + } + + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + tickets, err := h.ticketService.SearchTicket(c.Request().Context(), input.Search) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": tickets, + }) +} + +// filter ticket by location +func (h *TicketHandler) FilterTicket(c echo.Context) error { + var input struct { + Location string `param:"location" validate:"required"` //harus pramater search + } + + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + tickets, err := h.ticketService.FilterTicket(c.Request().Context(), input.Location) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": tickets, + }) +} + +// filter ticket by category +func (h *TicketHandler) FilterTicketByCategory(c echo.Context) error { + var input struct { + Category string `param:"category" validate:"required"` //harus pramater search + } + + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + tickets, err := h.ticketService.FilterTicketByCategory(c.Request().Context(), input.Category) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": tickets, + }) +} + +// filter ticket by range time (start - end) +func (h *TicketHandler) FilterTicketByRangeTime(c echo.Context) error { + var input struct { + Start string `param:"start" validate:"required"` //harus pramater search + End string `param:"end" validate:"required"` //harus pramater search + } + + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + tickets, err := h.ticketService.FilterTicketByRangeTime(c.Request().Context(), input.Start, input.End) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": tickets, + }) +} + +// filter ticket by price (min - max) +func (h *TicketHandler) FilterTicketByPrice(c echo.Context) error { + var input struct { + Min string `param:"min" validate:"required"` //harus pramater search + Max string `param:"max" validate:"required"` //harus pramater search + } + + if err := c.Bind(&input); err != nil { + return c.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + tickets, err := h.ticketService.FilterTicketByPrice(c.Request().Context(), input.Min, input.Max) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": tickets, + }) +} + +// sortir tiket dari yang terbaru +func (h *TicketHandler) SortTicketByNewest(c echo.Context) error { + // Membaca parameter 'sort' dari URL + sortParam := c.QueryParam("sort") + + // Memastikan bahwa parameter sort adalah 'terbaru' + if sortParam != "terbaru" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid sort parameter"}) + } + + // Memanggil service untuk mengurutkan tiket + tickets, err := h.ticketService.SortTicketByNewest(c.Request().Context()) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": tickets, + }) +} + +// sortir dari yang termahal +func (h *TicketHandler) SortTicketByMostExpensive(c echo.Context) error { + // Membaca parameter 'sort' dari URL + sortParam := c.QueryParam("sort") + + // Memastikan bahwa parameter sort adalah 'termurah' + if sortParam != "termahal" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid sort parameter"}) + } + + // Memanggil service untuk mengurutkan tiket dari yang termurah + tickets, err := h.ticketService.SortTicketByMostExpensive(c.Request().Context()) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": tickets, + }) +} + +// ticket yang paling banyak dibeli +func (h *TicketHandler) SortTicketByCheapest(c echo.Context) error { + sortParam := c.QueryParam("sort") + + // Memastikan bahwa parameter sort adalah 'termurah' + if sortParam != "termurah" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid sort parameter"}) + } + + // Memanggil service untuk mengurutkan tiket dari yang termurah + tickets, err := h.ticketService.SortTicketByCheapest(c.Request().Context()) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": tickets, + }) +} + +// ticket yang paling banyak dibeli +func (h *TicketHandler) SortTicketByMostBought(c echo.Context) error { + sortParam := c.QueryParam("sort") + + // Memastikan bahwa parameter sort adalah 'terbanyak' + if sortParam != "terbanyak" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid sort parameter"}) + } + + // Memanggil service untuk mengurutkan tiket dari yang terbanyak + tickets, err := h.ticketService.SortTicketByMostBought(c.Request().Context()) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": tickets, + }) +} + +// ticket yang masih tersedia +func (h *TicketHandler) SortTicketByAvailable(c echo.Context) error { + sortParam := c.QueryParam("sort") + + // Memastikan bahwa parameter sort adalah 'tersedia' + if sortParam != "tersedia" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid sort parameter"}) + } + + // Memanggil service untuk mengurutkan tiket dari yang tersedia + tickets, err := h.ticketService.SortTicketByAvailable(c.Request().Context()) + if err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": tickets, + }) +} \ No newline at end of file diff --git a/internal/http/handler/topup_handler.go b/internal/http/handler/topup_handler.go new file mode 100644 index 0000000..49b3bc9 --- /dev/null +++ b/internal/http/handler/topup_handler.go @@ -0,0 +1,80 @@ +package handler + +import ( + "Ticketing/entity" + "Ticketing/internal/service" + "net/http" + + "github.com/golang-jwt/jwt/v5" + "github.com/labstack/echo/v4" +) + +type TopupHandler struct { + topupService service.TopupService +} + +func NewTopupHandler(topupService service.TopupService) *TopupHandler { + return &TopupHandler{topupService} +} + +func (h *TopupHandler) CreateTopup(c echo.Context) error { + var topup entity.Topup + if err := c.Bind(&topup); err != nil { + return c.JSON(http.StatusBadRequest, echo.Map{"error": err.Error()}) + } + + chargeResp, err := h.topupService.CreateMidtransCharge(topup.ID, int64(topup.Amount)) + if err != nil { + return c.JSON(http.StatusInternalServerError, echo.Map{"error": err.Error()}) + } + + topup.SnapURL = chargeResp.RedirectURL + + // Perhatikan penambahan c.Request().Context() di sini + newTopup, err := h.topupService.CreateTopup(c.Request().Context(), topup) + if err != nil { + return c.JSON(http.StatusInternalServerError, echo.Map{"error": err.Error()}) + } + + return c.JSON(http.StatusCreated, newTopup) +} + +// topup saldo by jwt token +func (h *TopupHandler) UserTopup(c echo.Context) error { + // Get JWT token from the context + token, ok := c.Get("user").(*jwt.Token) + if !ok { + return c.JSON(http.StatusUnauthorized, echo.Map{"error": "missing or invalid token"}) + } + + // Extract claims from the JWT token + claims, ok := token.Claims.(jwt.MapClaims) + if !ok { + return c.JSON(http.StatusUnauthorized, echo.Map{"error": "invalid token claims"}) + } + + // Get user ID from the JWT claims + userID := int(claims["user_id"].(float64)) + + var topup entity.Topup + if err := c.Bind(&topup); err != nil { + return c.JSON(http.StatusBadRequest, echo.Map{"error": err.Error()}) + } + + // Update user saldo + userSaldo, err := h.topupService.UpdateUserSaldo(c.Request().Context(), userID, int64(topup.Amount)) + if err != nil { + return c.JSON(http.StatusInternalServerError, echo.Map{"error": err.Error()}) + } + + // Create topup record + newTopup, err := h.topupService.UserTopup(c.Request().Context(), userID, topup) + if err != nil { + return c.JSON(http.StatusInternalServerError, echo.Map{"error": err.Error()}) + } + + return c.JSON(http.StatusCreated, echo.Map{ + "user_saldo": userSaldo, + "topup_data": newTopup, + }) +} diff --git a/internal/http/handler/transaction.handler.go b/internal/http/handler/transaction.handler.go new file mode 100644 index 0000000..4de668f --- /dev/null +++ b/internal/http/handler/transaction.handler.go @@ -0,0 +1,118 @@ +package handler + +import ( + "net/http" + + "Ticketing/entity" + + "Ticketing/common" + "Ticketing/internal/http/validator" + "Ticketing/internal/service" + + "github.com/golang-jwt/jwt/v5" + "github.com/labstack/echo/v4" +) + +type TransactionHandler struct { + transactionService service.TransactionUseCase + paymentService service.PaymentUseCase + userService service.UserUsecase +} + +func NewTransactionHandler(transactionService service.TransactionUseCase, paymentService service.PaymentUseCase, userService service.UserUsecase) *TransactionHandler { + return &TransactionHandler{ + transactionService: transactionService, + paymentService: paymentService, + userService: userService, + } +} + +func (h *TransactionHandler) CreateOrder(ctx echo.Context) error { + var input struct { + OrderID string `json:"order_id" validate:"required"` + Amount int64 `json:"amount" validate:"required"` + } + + if err := ctx.Bind(&input); err != nil { + return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + dataUser, _ := ctx.Get("user").(*jwt.Token) + claims := dataUser.Claims.(*common.JwtCustomClaims) + + transaction := entity.NewTransaction(input.OrderID, claims.ID, input.Amount, "unpaid") + + err := h.transactionService.Create(ctx.Request().Context(), transaction) + + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + + paymentRequest := entity.NewPaymentRequest(transaction.OrderID, transaction.Amount, claims.Name, "", claims.Email) + + payment, err := h.paymentService.CreateTransaction(ctx.Request().Context(), paymentRequest) + + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + + return ctx.JSON(http.StatusCreated, map[string]string{"url_pembayaran": payment}) +} + +func (h *TransactionHandler) WebHookTransaction(ctx echo.Context) error { + var input entity.MidtransRequest + + if err := ctx.Bind(&input); err != nil { + return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + // Cari transaksi berdasarkan order ID untuk mendapatkan ID pengguna (user ID) + transaction, err := h.transactionService.FindByOrderID(ctx.Request().Context(), input.OrderID) + + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + + // Tentukan status default + status := "unpaid" + + // Jika status transaksi adalah "settlement", ubah status menjadi "paid" + if input.TransactionStatus == "settlement" { + status = "paid" + + // Update status transaksi di database + err = h.transactionService.UpdateStatus(ctx.Request().Context(), transaction.OrderID, status) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + + // Tambahkan saldo ke user jika status transaksi adalah "paid" + user, err := h.userService.FindByID(ctx.Request().Context(), transaction.UserID) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + + // Tambahkan saldo ke user + updatedSaldo := user.Saldo + transaction.Amount + err = h.userService.UpdateSaldo(ctx.Request().Context(), user.ID, updatedSaldo) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + } + + return ctx.JSON(http.StatusOK, map[string]string{"message": "success"}) +} + +// history transaction +func (h *TransactionHandler) HistoryTransaction(ctx echo.Context) error { + dataUser, _ := ctx.Get("user").(*jwt.Token) + claims := dataUser.Claims.(*common.JwtCustomClaims) + + transactions, err := h.transactionService.FindByUserID(ctx.Request().Context(), claims.ID) + + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + + return ctx.JSON(http.StatusOK, transactions) +} diff --git a/internal/http/handler/user.handler.go b/internal/http/handler/user.handler.go deleted file mode 100644 index 4b90f76..0000000 --- a/internal/http/handler/user.handler.go +++ /dev/null @@ -1,116 +0,0 @@ -package handler - -import ( - "errors" - "fmt" - "net/http" - - "github.com/golang-jwt/jwt/v5" - "github.com/labstack/echo/v4" - "github.com/zhikariz/weather-app/common" - "github.com/zhikariz/weather-app/entity" - "github.com/zhikariz/weather-app/internal/http/validator" - "github.com/zhikariz/weather-app/internal/service" -) - -type UserHandler struct { - userService service.UserUseCase -} - -func NewUserHandler(userService service.UserUseCase) *UserHandler { - return &UserHandler{userService} -} - -func (h *UserHandler) GetAllUsers(ctx echo.Context) error { - // Parse the token - userClaim := ctx.Get("user").(*jwt.Token) - claims := userClaim.Claims.(*common.JwtCustomClaims) - - if claims.Email == "helmi@ganteng.com" { - return ctx.JSON(http.StatusForbidden, errors.New("you don't have permission to access this resource")) - } - - fmt.Println(claims.Email) - - users, err := h.userService.FindAll(ctx.Request().Context()) - if err != nil { - return ctx.JSON(http.StatusUnprocessableEntity, err) - } - return ctx.JSON(http.StatusOK, users) -} - -func (h *UserHandler) CreateUser(ctx echo.Context) error { - var input struct { - Name string `json:"name" validate:"required"` - Email string `json:"email" validate:"required"` - Password string `json:"password" validate:"required"` - } - - if err := ctx.Bind(&input); err != nil { - return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) - } - - user := entity.NewUser(input.Name, input.Email, input.Password) - err := h.userService.Create(ctx.Request().Context(), user) - if err != nil { - return ctx.JSON(http.StatusUnprocessableEntity, err) - } - - return ctx.JSON(http.StatusCreated, user) -} - -func (h *UserHandler) UpdateUser(ctx echo.Context) error { - var input struct { - ID int64 `param:"id" validate:"required"` - Name string `json:"name" validate:"required"` - Email string `json:"email" validate:"required"` - Password string `json:"password" validate:"required"` - } - - if err := ctx.Bind(&input); err != nil { - return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) - } - - user := entity.UpdateUser(input.ID, input.Name, input.Email, input.Password) - - err := h.userService.Update(ctx.Request().Context(), user) - if err != nil { - return ctx.JSON(http.StatusUnprocessableEntity, err) - } - - return ctx.JSON(http.StatusOK, user) -} - -func (h *UserHandler) DeleteUser(ctx echo.Context) error { - var input struct { - ID int64 `param:"id" validate:"required"` - } - - if err := ctx.Bind(&input); err != nil { - return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) - } - - err := h.userService.Delete(ctx.Request().Context(), input.ID) - if err != nil { - return ctx.JSON(http.StatusUnprocessableEntity, err) - } - - return ctx.NoContent(http.StatusNoContent) -} - -func (h *UserHandler) GetUserByID(ctx echo.Context) error { - var input struct { - ID int64 `param:"id" validate:"required"` - } - - if err := ctx.Bind(&input); err != nil { - return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) - } - - user, err := h.userService.FindByID(ctx.Request().Context(), input.ID) - if err != nil { - return ctx.JSON(http.StatusUnprocessableEntity, err) - } - - return ctx.JSON(http.StatusOK, user) -} diff --git a/internal/http/handler/user_hendler.go b/internal/http/handler/user_hendler.go new file mode 100644 index 0000000..a05e33f --- /dev/null +++ b/internal/http/handler/user_hendler.go @@ -0,0 +1,404 @@ +package handler + +//NOTE : +// FOLDER INI UNTUK MEMANGGIL SERVICE DAN REPOSITORY +import ( + "Ticketing/common" + "Ticketing/entity" + "Ticketing/internal/http/validator" + "Ticketing/internal/service" + "net/http" + "strconv" + + "github.com/golang-jwt/jwt/v5" + "github.com/labstack/echo/v4" +) + +type UserHandler struct { + userService service.UserUsecase +} + +// melakukan instace dari user handler +func NewUserHandler(userService service.UserUsecase) *UserHandler { + return &UserHandler{userService} +} + +// func untuk melakukan getAll User +func (h *UserHandler) GetAllUser(ctx echo.Context) error { + users, err := h.userService.GetAll(ctx.Request().Context()) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + return ctx.JSON(http.StatusOK, map[string]interface{}{ + "data": users, + }) +} + +// func untuk melakukan createUser update versi reza v5 halo +func (h *UserHandler) CreateUser(ctx echo.Context) error { + var input struct { + Name string `json:"name" validate:"required"` + Email string `json:"email" validate:"email"` + Number string `json:"number" validate:"min=11,max=13"` + Roles string `json:"roles" validate:"oneof=Admin Buyer"` + Password string `json:"password"` + Saldo int64 `json:"saldo"` + } + //ini func untuk error checking + if err := ctx.Bind(&input); err != nil { + return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + user := entity.NewUser(input.Name, input.Email, input.Number, input.Roles, input.Password, input.Saldo) + err := h.userService.CreateUser(ctx.Request().Context(), user) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + //kalau retrun nya kaya gini akan tampil pesan "User created successfully" + return ctx.JSON(http.StatusCreated, "User created successfully") +} + +// func untuk melakukan updateUser by id +func (h *UserHandler) UpdateUser(ctx echo.Context) error { + var input struct { + ID int64 `param:"id" validate:"required"` + Name string `json:"name"` + Email string `json:"email" validate:"email"` + Number string `json:"number" validate:"min=11,max=13"` + Roles string `json:"roles" validate:"oneof=Admin Buyer"` + Password string `json:"password"` + Saldo int64 `json:"saldo"` + } + + if err := ctx.Bind(&input); err != nil { + return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + user := entity.UpdateUser(input.ID, input.Name, input.Email, input.Number, input.Roles, input.Password, input.Saldo) + + err := h.userService.UpdateUser(ctx.Request().Context(), user) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + + return ctx.JSON(http.StatusOK, map[string]string{"success": "succesfully update user"}) +} + +// func untuk melakukan getUser by id +func (h *UserHandler) GetUserByID(ctx echo.Context) error { + idStr := ctx.Param("id") + id, err := strconv.ParseInt(idStr, 10, 64) + if err != nil { + // Jika tidak dapat mengonversi ID menjadi int64, kembalikan respons error + return ctx.JSON(http.StatusBadRequest, map[string]interface{}{ + "error": "Invalid ID", + }) + } + user, err := h.userService.GetUserByID(ctx.Request().Context(), id) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ + "error": err.Error(), + }) + } + return ctx.JSON(http.StatusOK, map[string]interface{}{ + "data": map[string]interface{}{ + "id": user.ID, + "name": user.Name, + "email": user.Email, + "number": user.Number, + "password": user.Password, + "created": user.CreatedAt, + "updated": user.UpdatedAt, + }, + }) +} + +// DeleteUser func untuk melakukan delete user by id +func (h *UserHandler) DeleteUser(ctx echo.Context) error { + var input struct { + ID int64 `param:"id" validate:"required"` + } + + if err := ctx.Bind(&input); err != nil { + return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + err := h.userService.Delete(ctx.Request().Context(), input.ID) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + return ctx.JSON(http.StatusOK, map[string]interface{}{ + "message": "User deleted successfully", + }) +} + +// Update User Self +func (h *UserHandler) UpdateProfile(ctx echo.Context) error { + var input struct { + ID int64 + Name string `json:"name"` + Email string `json:"email" validate:"email"` + Number string `json:"number" validate:"min=11,max=13"` + Password string `json:"password"` + Saldo int64 `json:"saldo"` + } + + // Mengambil nilai 'claims' dari JWT token + claims, ok := ctx.Get("user").(*jwt.Token) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user claims") + } + + // Extract user information from claims + claimsData, ok := claims.Claims.(*common.JwtCustomClaims) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user information from claims") + } + + // Mengisi ID dari klaim ke input + input.ID = claimsData.ID + + // Update user + user := entity.UpdateProfile(input.ID, input.Name, input.Email, input.Number, input.Password) + + // Memanggil service untuk update user + err := h.userService.UpdateProfile(ctx.Request().Context(), user) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + + return ctx.JSON(http.StatusOK, map[string]string{"success": "successfully update user"}) +} + +// get profile +func (h *UserHandler) GetProfile(ctx echo.Context) error { + // Retrieve user claims from the JWT token + claims, ok := ctx.Get("user").(*jwt.Token) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user claims") + } + + // Extract user information from claims + claimsData, ok := claims.Claims.(*common.JwtCustomClaims) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user information from claims") + } + + // Fetch user profile using the user ID + user, err := h.userService.GetProfile(ctx.Request().Context(), claimsData.ID) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + + // Return the user profile + return ctx.JSON(http.StatusOK, user) +} + +// Get user balance +func (h *UserHandler) GetUserBalance(ctx echo.Context) error { + // Retrieve user claims from the JWT token + claims, ok := ctx.Get("user").(*jwt.Token) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user claims") + } + + // Extract user information from claims + claimsData, ok := claims.Claims.(*common.JwtCustomClaims) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user information from claims") + } + + // Fetch user balance using the user ID + balance, err := h.userService.GetUserBalance(ctx.Request().Context(), claimsData.ID) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + + // Return the user balance + return ctx.JSON(http.StatusOK, balance.Saldo) +} + +// delete account +func (h *UserHandler) DeleteAccount(ctx echo.Context) error { + claims, ok := ctx.Get("user").(*jwt.Token) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user claims") + } + + // Extract user information from claims + claimsData, ok := claims.Claims.(*common.JwtCustomClaims) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user information from claims") + } + + // Menggunakan ID dari klaim JWT + idToDelete := claimsData.ID + + err := h.userService.Delete(ctx.Request().Context(), idToDelete) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err) + } + + return ctx.JSON(http.StatusOK, map[string]interface{}{ + "message": "User deleted successfully", + }) +} + +// upgrade saldo +func (h *UserHandler) UpgradeSaldo(ctx echo.Context) error { + // Retrieve user ID from JWT claims + claims, ok := ctx.Get("user").(*jwt.Token) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user claims") + } + + // Extract user information from claims + claimsData, ok := claims.Claims.(*common.JwtCustomClaims) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user information from claims") + } + + userID := claimsData.ID + + // Fetch current saldo for the user + currentUser, err := h.userService.GetUserByID(ctx.Request().Context(), userID) + if err != nil { + return ctx.JSON(http.StatusInternalServerError, "unable to fetch user information") + } + + // Extract input data + var input struct { + Saldo int64 `json:"saldo"` + } + + if err := ctx.Bind(&input); err != nil { + return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) + } + + // Add the new saldo to the current saldo + newSaldo := currentUser.Saldo + input.Saldo + + // Update user saldo + currentUser.Saldo = newSaldo + err = h.userService.UpgradeSaldo(ctx.Request().Context(), currentUser) + if err != nil { + return ctx.JSON(http.StatusUnprocessableEntity, err.Error()) + } + + return ctx.JSON(http.StatusOK, map[string]string{"success": "successfully updated user saldo"}) +} + +// logout +func (h *UserHandler) UserLogout(ctx echo.Context) error { + // Retrieve user claims from the JWT token + claims, ok := ctx.Get("user").(*jwt.Token) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user claims") + } + + // Extract user information from claims + claimsData, ok := claims.Claims.(*common.JwtCustomClaims) + if !ok { + return ctx.JSON(http.StatusInternalServerError, "unable to get user information from claims") + } + + userID := claimsData.ID + + // Create a *entity.User instance with the userID + user := &entity.User{ID: userID} + + // Invalidate the JWT token + err := h.userService.UserLogout(ctx.Request().Context(), user) + if err != nil { + return ctx.JSON(http.StatusInternalServerError, "unable to invalidate JWT token") + } + + return ctx.JSON(http.StatusOK, map[string]string{"success": "successfully logged out"}) +} + +// func DiagnosticAI(userInput, openAIKey string) (string, error) { +// ctx := context.Background() +// client := openai.NewClient(openAIKey) +// model := openai.GPT3Dot5Turbo +// messages := []openai.ChatCompletionMessage{ +// { +// Role: openai.ChatMessageRoleSystem, +// Content: "Siswa menanyakan soal. Jawablah dengan materi tentang soal dan jangan berikan jawaban secara langsung", +// }, +// { +// Role: openai.ChatMessageRoleUser, +// Content: userInput, +// }, +// } + +// resp, err := getCompletionFromMessages(ctx, client, messages, model) +// if err != nil { +// return "", err +// } +// answer := resp.Choices[0].Message.Content +// return answer, nil +// } + +// func getCompletionFromMessages( +// ctx context.Context, +// client *openai.Client, +// messages []openai.ChatCompletionMessage, +// model string, +// ) (openai.ChatCompletionResponse, error) { +// if model == "" { +// model = openai.GPT3Dot5Turbo +// } + +// resp, err := client.CreateChatCompletion( +// ctx, +// openai.ChatCompletionRequest{ +// Model: model, +// Messages: messages, +// }, +// ) +// return resp, err +// } + +// TanyaAI +// func (h *UserHandler) TanyaAI(c echo.Context) error { +// // Baca data masukan dari body request +// var requestBody struct { +// Pertanyaan string `json:"pertanyaan"` +// } + +// if err := c.Bind(&requestBody); err != nil { +// return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()}) +// } + +// // Panggil fungsi AI untuk menjawab pertanyaan +// jawaban, err := h.aiService.TanyaAI(context.Background(), requestBody.Pertanyaan) +// if err != nil { +// return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) +// } + +// // Kirim jawaban ke client +// return c.JSON(http.StatusOK, map[string]string{"jawaban": jawaban}) +// } + +// buyer create account +// func (h *UserHandler) BuyerCreateAccount(ctx echo.Context) error { +// var input struct { +// Name string `json:"name" validate:"required"` +// Email string `json:"email" validate:"email"` +// Number string `json:"number" validate:"min=11,max=13"` +// Roles string `json:"roles" default:"Buyer"` +// Password string `json:"password"` +// Saldo int64 `json:"saldo" default:"0"` +// } +// //ini func untuk error checking +// if err := ctx.Bind(&input); err != nil { +// return ctx.JSON(http.StatusBadRequest, validator.ValidatorErrors(err)) +// } +// user := entity.NewUser(input.Name, input.Email, input.Number, input.Roles, input.Password, input.Saldo) +// err := h.userService.CreateUser(ctx.Request().Context(), user) +// if err != nil { +// return ctx.JSON(http.StatusUnprocessableEntity, err) +// } +// //kalau retrun nya kaya gini akan tampil pesan "User created successfully" +// return ctx.JSON(http.StatusCreated, "User created successfully") +// } diff --git a/internal/http/router/routes.go b/internal/http/router/routes.go index 1281abf..ce018b5 100644 --- a/internal/http/router/routes.go +++ b/internal/http/router/routes.go @@ -1,52 +1,395 @@ -package router - -import ( - "github.com/labstack/echo/v4" - "github.com/zhikariz/weather-app/internal/http/handler" -) - -type Route struct { - Method string - Path string - Handler echo.HandlerFunc -} - -func PublicRoutes(authHandler *handler.AuthHandler) []*Route { - return []*Route{ - { - Method: echo.POST, - Path: "/login", - Handler: authHandler.Login, - }, - } -} - -func PrivateRoutes(userHandler *handler.UserHandler) []*Route { - return []*Route{ - { - Method: echo.GET, - Path: "/users", - Handler: userHandler.GetAllUsers, - }, - { - Method: echo.GET, - Path: "/users/:id", - Handler: userHandler.GetUserByID, - }, - { - Method: echo.POST, - Path: "/users", - Handler: userHandler.CreateUser, - }, - { - Method: echo.PUT, - Path: "/users/:id", - Handler: userHandler.UpdateUser, - }, - { - Method: echo.DELETE, - Path: "/users/:id", - Handler: userHandler.DeleteUser, - }, - } -} +package router + +import ( + "Ticketing/internal/http/handler" + + "github.com/labstack/echo/v4" +) + +const ( + Admin = "Admin" + Buyer = "Buyer" +) + +var ( + allRoles = []string{Admin, Buyer} + onlyAdmin = []string{Admin} + onlyBuyer = []string{Buyer} +) + +// membuat struct route +type Route struct { + Method string + Path string + Handler echo.HandlerFunc + Role []string +} + +// membuat fungsi untuk mengembalikan route +// pada func ini perlu login krna private +func PublicRoutes( + authHandler *handler.AuthHandler, + TicketHandler *handler.TicketHandler, + BlogHandler *handler.BlogHandler, + transactionHandler *handler.TransactionHandler) []*Route { + return []*Route{ + { + Method: echo.POST, + Path: "/login", + Handler: authHandler.Login, + }, + { + Method: echo.POST, + Path: "/register", + Handler: authHandler.Registration, + }, + { + Method: echo.GET, + Path: "/public/blog", + Handler: BlogHandler.GetAllBlogs, + }, + { + Method: echo.GET, + Path: "/public/ticket", + Handler: TicketHandler.GetAllTickets, + }, + { + Method: echo.GET, + Path: "/blog", + Handler: BlogHandler.GetAllBlogs, + }, + { + Method: echo.GET, + Path: "/blog/:id", + Handler: BlogHandler.GetBlog, + }, + { + Method: echo.GET, + Path: "/blog/search/:search", + Handler: BlogHandler.SearchBlog, + }, + { + Method: echo.GET, + Path: "/ticket/:id", + Handler: TicketHandler.GetTicket, + }, + { + Method: echo.GET, + Path: "/ticket", + Handler: TicketHandler.GetAllTickets, + }, + //filter ticket by location + { + Method: echo.GET, + Path: "/ticket/location/:location", + Handler: TicketHandler.FilterTicket, + }, + // filter ticket by category + { + Method: echo.GET, + Path: "/ticket/category/:category", + Handler: TicketHandler.FilterTicketByCategory, + }, + // filter ticket by range time (start - end) + { + Method: echo.GET, + Path: "/ticket/range/:start/:end", + Handler: TicketHandler.FilterTicketByRangeTime, + }, + // filter ticket by price (min - max) + { + Method: echo.GET, + Path: "/ticket/price/:min/:max", + Handler: TicketHandler.FilterTicketByPrice, + }, + //sortir tiket dari yang terbaru + { + Method: echo.GET, + Path: "/ticket/terbaru", + Handler: TicketHandler.SortTicketByNewest, + }, + //sortir tiket dari yang termahal + { + Method: echo.GET, + Path: "/ticket/most-expensive", + Handler: TicketHandler.SortTicketByMostExpensive, + }, + //sortir tiket dari yang termurah + { + Method: echo.GET, + Path: "/ticket/cheapest", + Handler: TicketHandler.SortTicketByCheapest, + }, + // filter ticket by most bought + { + Method: echo.GET, + Path: "/ticket/most-bought", + Handler: TicketHandler.SortTicketByMostBought, + }, + // ticket yang masih tersedia + { + Method: echo.GET, + Path: "/ticket/available", + Handler: TicketHandler.SortTicketByAvailable, + }, + { + Method: echo.GET, + Path: "/ticket/search/:search", + Handler: TicketHandler.SearchTicket, + }, + { + Method: echo.POST, + Path: "/transactions/webhook", + Handler: transactionHandler.WebHookTransaction, + }, + // { + // Method: echo.POST, + // Path: "/users/register/buyer", + // Handler: authHandler.BuyerCreateAccount, + // }, + } +} + +// membuat fungsi untuk mengembalikan route +// pada func ini tdk perlu login krna public +func PrivateRoutes( + UserHandler *handler.UserHandler, + TicketHandler *handler.TicketHandler, + BlogHandler *handler.BlogHandler, + OrderHandler *handler.OrderHandler, + NotificationHandler *handler.NotificationHandler, + transactionHandler *handler.TransactionHandler, + TopupHandler *handler.TopupHandler) []*Route { + return []*Route{ + { + Method: echo.POST, + Path: "/users", + Handler: UserHandler.CreateUser, + Role: allRoles, + }, + + { + Method: echo.GET, + Path: "/users", + Handler: UserHandler.GetAllUser, + Role: onlyAdmin, + }, + + { + Method: echo.PUT, + Path: "/users/:id", + Handler: UserHandler.UpdateUser, + Role: allRoles, + }, + + { + Method: echo.GET, + Path: "/users/:id", + Handler: UserHandler.GetUserByID, + Role: allRoles, + }, + + { + Method: echo.DELETE, + Path: "/users/:id", + Handler: UserHandler.DeleteUser, + Role: onlyAdmin, + }, + + { + Method: echo.POST, + Path: "/ticket", + Handler: TicketHandler.CreateTicket, + Role: onlyAdmin, + }, + + { + Method: echo.GET, + Path: "/ticketa", + Handler: TicketHandler.GetAllTickets, + Role: onlyAdmin, + }, + + { + Method: echo.PUT, + Path: "/ticket/:id", + Handler: TicketHandler.UpdateTicket, + Role: onlyAdmin, + }, + + { + Method: echo.DELETE, + Path: "/ticket/:id", + Handler: TicketHandler.DeleteTicket, + Role: onlyAdmin, + }, + + { + Method: echo.POST, + Path: "/blog", + Handler: BlogHandler.CreateBlog, + Role: onlyAdmin, + }, + + { + Method: echo.PUT, + Path: "/blog/:id", + Handler: BlogHandler.UpdateBlog, + Role: onlyAdmin, + }, + + { + Method: echo.DELETE, + Path: "/blog/:id", + Handler: BlogHandler.DeleteBlog, + Role: onlyAdmin, + }, + + { + Method: echo.POST, + Path: "/order", + Handler: OrderHandler.CreateOrder, + Role: allRoles, + }, + + { + Method: echo.GET, + Path: "/order", + Handler: OrderHandler.GetAllOrders, + Role: onlyAdmin, + }, + + { + Method: echo.GET, + Path: "/order/:id", + Handler: OrderHandler.GetOrderByUserID, + Role: allRoles, + }, + + // create notification + { + Method: echo.POST, + Path: "/notification", + Handler: NotificationHandler.CreateNotification, + Role: onlyAdmin, + }, + + // get all notification + { + Method: echo.GET, + Path: "/notifications", + Handler: NotificationHandler.GetAllNotification, + Role: allRoles, + }, + + // topup + { + Method: echo.POST, + Path: "/topup", + Handler: TopupHandler.CreateTopup, + Role: allRoles, + }, + + // delete user self + // { + // Method: echo.DELETE, + // Path: "/users/self/:id", + // Handler: UserHandler.DeleteUserSelf, + // Role: allRoles, + // }, + + // getprofile + { + Method: echo.GET, + Path: "/users/profile", + Handler: UserHandler.GetProfile, + Role: allRoles, + }, + + // update profile + { + Method: echo.PUT, + Path: "/users/profile", + Handler: UserHandler.UpdateProfile, + Role: allRoles, + }, + { + Method: echo.GET, + Path: "/users/balance", + Handler: UserHandler.GetUserBalance, + Role: onlyBuyer, + }, + { + Method: echo.DELETE, + Path: "/users/deleteprofile", + Handler: UserHandler.DeleteAccount, + Role: allRoles, + }, + //UserCreateOrder + { + Method: echo.POST, + Path: "user/order", + Handler: OrderHandler.UserCreateOrder, + Role: onlyBuyer, + }, + //GetOrderHistory + { + Method: echo.GET, + Path: "user/order", + Handler: OrderHandler.GetOrderHistory, + Role: onlyBuyer, + }, + //UserGetNotification + { + Method: echo.GET, + Path: "user/notification", + Handler: NotificationHandler.UserGetNotification, + Role: allRoles, + }, + { + Method: echo.POST, + Path: "/user/topup", + Handler: TopupHandler.UserTopup, + Role: onlyBuyer, + }, + { + Method: echo.POST, + Path: "/user/upgrade", + Handler: UserHandler.UpgradeSaldo, + Role: onlyBuyer, + }, + { + Method: echo.POST, + Path: "/user/logout", + Handler: UserHandler.UserLogout, + Role: allRoles, + }, + { + Method: echo.POST, + Path: "/transactions", + Handler: transactionHandler.CreateOrder, + Role: allRoles, + }, + { + Method: echo.GET, + Path: "/transactions/history", + Handler: transactionHandler.HistoryTransaction, + Role: allRoles, + }, + // { + // Method: http.MethodPost, + // Path: "/diagnostic-ai", + // Handler: UserHandler.DiagnosticAI, + // Role: allRoles, + // }, + } +} + +//NOTE : +//MENGAPA TERDAPAT 2 FUNC DIATAS? YAITU PUBLIC DAN PRIVATE +//KAREN DI SERVER.GO KITA BUAT GROUP API, DAN KITA MEMBAGI ROUTE YANG PERLU LOGIN DAN TIDAK PERLU LOGIN +// YAITU PUBLIC DAN PRIVATE + +//note ; +//untuk menjalankan nya setelah port 8080 ditambahin /api/v1 +// karna di server.go kita membuat group API diff --git a/internal/http/server/echo.go b/internal/http/server/echo.go deleted file mode 100644 index e328be6..0000000 --- a/internal/http/server/echo.go +++ /dev/null @@ -1,56 +0,0 @@ -package server - -import ( - "github.com/golang-jwt/jwt/v5" - echojwt "github.com/labstack/echo-jwt/v4" - "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" - "github.com/zhikariz/weather-app/common" - "github.com/zhikariz/weather-app/internal/config" - "github.com/zhikariz/weather-app/internal/http/binder" - "github.com/zhikariz/weather-app/internal/http/router" -) - -type Server struct { - *echo.Echo -} - -func NewServer( - cfg *config.Config, - binder *binder.Binder, - publicRoutes, privateRoutes []*router.Route) *Server { - e := echo.New() - e.HideBanner = true - e.Binder = binder - - e.Use( - middleware.Logger(), - middleware.Recover(), - middleware.CORS(), - ) - - v1 := e.Group("/api/v1") - - for _, public := range publicRoutes { - v1.Add(public.Method, public.Path, public.Handler) - } - - for _, private := range privateRoutes { - v1.Add(private.Method, private.Path, private.Handler, JWTProtected(cfg.JWT.SecretKey)) - } - - e.GET("/ping", func(c echo.Context) error { - return c.String(200, "pong") - }) - - return &Server{e} -} - -func JWTProtected(secretKey string) echo.MiddlewareFunc { - return echojwt.WithConfig(echojwt.Config{ - NewClaimsFunc: func(c echo.Context) jwt.Claims { - return new(common.JwtCustomClaims) - }, - SigningKey: []byte(secretKey), - }) -} diff --git a/internal/http/server/server.go b/internal/http/server/server.go new file mode 100644 index 0000000..ed231cc --- /dev/null +++ b/internal/http/server/server.go @@ -0,0 +1,105 @@ +package server + +import ( + "Ticketing/common" + "Ticketing/internal/config" + "Ticketing/internal/http/binder" + "Ticketing/internal/http/router" + "net/http" + + "github.com/golang-jwt/jwt/v5" + echojwt "github.com/labstack/echo-jwt/v4" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + "golang.org/x/crypto/bcrypt" +) + +// merupakan struct dari eco +type Server struct { + *echo.Echo +} + +// untuk membuat server +func NewServer( + cfg *config.Config, + binder *binder.Binder, + publicRoutes, privateRoutes []*router.Route) *Server { + e := echo.New() + e.HideBanner = true // untuk menghilangkan banner echo, karena sudah membuat banner sendiri di splash + e.Binder = binder + + e.Use( + middleware.Logger(), + middleware.Recover(), + middleware.CORS(), + ) + + //membuat group API + v1 := e.Group("/api/v1") + + for _, public := range publicRoutes { + //e.add = untuk menambahkan route baru. + v1.Add(public.Method, public.Path, public.Handler) + } + + //ketika sudah ingin menggunakan middleware, maka menambahkan private.Middleware. + for _, private := range privateRoutes { + v1.Add(private.Method, private.Path, private.Handler, JWTProtected(cfg.JWT.SecretKey), RBACMiddleware(private.Role...)) + } + + //hedler untuk mengecek kesehatan server + e.GET("/ping", func(c echo.Context) error { + return c.String(200, "pong") + }) + //handler untuk generate password secara manual + e.GET("/generate-password/:password", func(c echo.Context) error { + password := c.Param("password") + hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + return c.String(200, string(hashedPassword)) + }) + + return &Server{e} + +} + +// func untuk pendeklarasian JWT Middleware +func JWTProtected(secretKey string) echo.MiddlewareFunc { + return echojwt.WithConfig(echojwt.Config{ + NewClaimsFunc: func(c echo.Context) jwt.Claims { + return new(common.JwtCustomClaims) + }, + SigningKey: []byte(secretKey), + }) +} + +// func untuk pendeklarasian RBAC Middleware +func RBACMiddleware(role ...string) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + user, ok := c.Get("user").(*jwt.Token) + if !ok { + return c.JSON(http.StatusUnauthorized, map[string]string{"error": "silahkan login terlebih dahulu"}) + } + + claims := user.Claims.(*common.JwtCustomClaims) + + // Check if the user has the required role + if !contains(role, claims.Role) { + return c.JSON(http.StatusForbidden, map[string]string{"error": "anda tidak diperbolehkan untuk mengakses resource ini"}) + } + + return next(c) + } + } +} + +// Helper function to check if a string is in a slice of strings +func contains(slice []string, s string) bool { + for _, value := range slice { + if value == s { + return true + } + } + return false +} + diff --git a/internal/http/validator/validator.go b/internal/http/validator/validator.go index d82963b..43610e5 100644 --- a/internal/http/validator/validator.go +++ b/internal/http/validator/validator.go @@ -1,48 +1,59 @@ -package validator - -import ( - "fmt" - "reflect" - "strings" - - "github.com/go-playground/validator/v10" -) - -type FormValidator struct { - validator *validator.Validate -} - -func (fv *FormValidator) Validate(i interface{}) error { - return fv.validator.Struct(i) -} - -func NewFormValidator() *FormValidator { - validate := validator.New(validator.WithRequiredStructEnabled()) - - validate.RegisterTagNameFunc(func(fld reflect.StructField) string { - name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] - if name == "-" { - return "" - } - return name - }) - - return &FormValidator{validate} -} - -func ValidatorErrors(err error) map[string]string { - fields := map[string]string{} - - if castedObject, ok := err.(validator.ValidationErrors); ok { - for _, err := range castedObject { - switch err.Tag() { - case "required": - fields[err.Field()] = fmt.Sprintf("field %s harus di isi", err.Field()) - default: - fields[err.Field()] = fmt.Sprintf("%s error with tag %s should be %s", err.Field(), err.Tag(), err.Param()) - } - } - } - - return fields -} +package validator + +import ( + "fmt" + "reflect" + "strings" + + "github.com/go-playground/validator/v10" +) + +// untuk membuat validator +type FormValidator struct { + validator *validator.Validate +} + +// untuk memvalidasi struct yang diinputkan +func (fv *FormValidator) Validate(i interface{}) error { + // untuk melakukan validasi + return fv.validator.Struct(i) +} + +// func ini digunakan ketika ingin mereturn data json yang diinputkan +func NewFormValidator() *FormValidator { + // diguanakn ketik ingin melakuka validate ke struct akan di enable + validate := validator.New(validator.WithRequiredStructEnabled()) + + //untk memvalidate type data json yang diinputkan. misal ketika ingin menginputkan nama harus huruf kecil semua. gabisa satu kecil satu gede. + validate.RegisterTagNameFunc(func(fld reflect.StructField) string { + name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] + if name == "-" { + return "" + } + return name + }) + + return &FormValidator{validate} +} + +// untuk menampilkan error yang terjadi / maping error validator pada field atau json yang required. +func ValidatorErrors(err error) map[string]string { + // untuk membuat map + fields := map[string]string{} + + if castedObject, ok := err.(validator.ValidationErrors); ok { + for _, err := range castedObject { + switch err.Tag() { + case "required": + fields[err.Field()] = fmt.Sprintf("field %s harus di isi", err.Field()) + case "oneof": + fields[err.Field()] = fmt.Sprintf("field %s harus memiliki salah satu dari nilai: %s", err.Field(), err.Param()) + default: + fields[err.Field()] = fmt.Sprintf("%s error with tag %s should be %s", err.Field(), err.Tag(), err.Param()) + } + } + } + + return fields +} + diff --git a/internal/repository/blog_repository.go b/internal/repository/blog_repository.go new file mode 100644 index 0000000..6b292dd --- /dev/null +++ b/internal/repository/blog_repository.go @@ -0,0 +1,78 @@ +package repository +// NOTE : +// FOLDER INI UNTUK MENANGANI KE BAGIAN DATABASE DAN QUERY +import ( + "context" + + "Ticketing/entity" + + "gorm.io/gorm" +) + +// Blog repository +type BlogRepository struct { + db *gorm.DB +} + +func NewBlogRepository(db *gorm.DB) *BlogRepository { + return &BlogRepository{ + db: db, + } +} + +// GetAllBlogs retrieves all blogs from the database. +func (r *BlogRepository) GetAllBlogs(ctx context.Context) ([]*entity.Blog, error) { + blogs := make([]*entity.Blog, 0) + result := r.db.WithContext(ctx).Find(&blogs) + if result.Error != nil { + return nil, result.Error + } + return blogs, nil +} + +// CreateBlog saves a new Blog to the database. +func (r *BlogRepository) CreateBlog(ctx context.Context, Blog *entity.Blog) error { + result := r.db.WithContext(ctx).Create(&Blog) + if result.Error != nil { + return result.Error + } + return nil +} + +// UpdateBlog updates a Blog in the database. +func (r *BlogRepository) UpdateBlog(ctx context.Context, Blog *entity.Blog) error { + result := r.db.WithContext(ctx).Model(&entity.Blog{}).Where("id = ?", Blog.ID).Updates(&Blog) + if result.Error != nil { + return result.Error + } + return nil +} + +// GetBlog retrieves a Blog by its ID from the database. +func (r *BlogRepository) GetBlog(ctx context.Context, id int64) (*entity.Blog, error) { + Blog := new(entity.Blog) + result := r.db.WithContext(ctx).First(&Blog, id) + if result.Error != nil { + return nil, result.Error + } + return Blog, nil +} + +// DeleteBlog deletes a Blog from the database. +func (r *BlogRepository) DeleteBlog(ctx context.Context, id int64) error { + result := r.db.WithContext(ctx).Delete(&entity.Blog{}, id) + if result.Error != nil { + return result.Error + } + return nil +} + +// SearchBlog search Blog +func (r *BlogRepository) SearchBlog(ctx context.Context, search string) ([]*entity.Blog, error) { + blogs := make([]*entity.Blog, 0) + result := r.db.WithContext(ctx).Where("title LIKE ?", "%"+search+"%").Find(&blogs) + if result.Error != nil { + return nil, result.Error + } + return blogs, nil +} diff --git a/internal/repository/notification_repository.go b/internal/repository/notification_repository.go new file mode 100644 index 0000000..e23e4fb --- /dev/null +++ b/internal/repository/notification_repository.go @@ -0,0 +1,67 @@ +package repository + +import ( + "Ticketing/entity" + "context" + + "gorm.io/gorm" +) + +type NotificationRepository struct { + db *gorm.DB +} + +func NewNotificationRepository(db *gorm.DB) *NotificationRepository { + return &NotificationRepository{ + db: db, + } +} + +// get all notification +func (r *NotificationRepository) GetAllNotification(ctx context.Context) ([]*entity.Notification, error) { + Notifications := make([]*entity.Notification, 0) + result := r.db.WithContext(ctx).Find(&Notifications) + if result.Error != nil { + return nil, result.Error + } + return Notifications, nil +} + +// create notification +func (r *NotificationRepository) CreateNotification(ctx context.Context, Notification *entity.Notification) error { + result := r.db.WithContext(ctx).Create(&Notification) + if result.Error != nil { + return result.Error + } + return nil +} + +// get notification after get change value isRead to true and only get notification if is_read false UserGetNotification +func (r *NotificationRepository) UserGetNotification(ctx context.Context) ([]*entity.Notification, error) { + Notifications := make([]*entity.Notification, 0) + + // Retrieve notifications with is_read = false + result := r.db.WithContext(ctx).Where("is_read = ?", false).Find(&Notifications) + if result.Error != nil { + return nil, result.Error + } + + // Mark retrieved notifications as read + for _, notification := range Notifications { + // Assuming you have a method to update the is_read field + err := r.MarkNotificationAsRead(ctx, notification.ID) + if err != nil { + return nil, err + } + } + + return Notifications, nil +} + +func (r *NotificationRepository) MarkNotificationAsRead(ctx context.Context, notificationID int) error { + result := r.db.WithContext(ctx).Model(&entity.Notification{}).Where("id = ?", notificationID).Update("is_read", true) + if result.Error != nil { + return result.Error + } + return nil +} diff --git a/internal/repository/order_repository.go b/internal/repository/order_repository.go new file mode 100644 index 0000000..72f8bc3 --- /dev/null +++ b/internal/repository/order_repository.go @@ -0,0 +1,133 @@ +package repository + +import ( + "Ticketing/entity" + "context" + "errors" + + "gorm.io/gorm" +) + +type OrderRepository struct { + db *gorm.DB +} + +func NewOrderRepository(db *gorm.DB) *OrderRepository { + return &OrderRepository{ + db: db, + } +} + +func (r *OrderRepository) CreateOrder(ctx context.Context, order *entity.Order) error { + err := r.db.WithContext(ctx).Create(&order).Error + if err != nil { + return err + } + return nil +} + +func (r *OrderRepository) GetTicket(ctx context.Context, ticketID int64) (*entity.Ticket, error) { + ticket := new(entity.Ticket) + if err := r.db.WithContext(ctx).Where("id = ?", ticketID).First(&ticket).Error; err != nil { + return nil, err + } + return ticket, nil +} + +func (r *OrderRepository) UpdateTicket(ctx context.Context, ticket *entity.Ticket) error { + if err := r.db.WithContext(ctx). + Model(&entity.Ticket{}). + Where("id = ?", ticket.ID). + Updates(&ticket).Error; err != nil { + return err + } + return nil +} + +// Add the following method to implement the missing GetTicketByID +func (r *OrderRepository) GetTicketByID(ctx context.Context, id int64) (*entity.Ticket, error) { + ticket := new(entity.Ticket) + result := r.db.WithContext(ctx).First(&ticket, id) + if result.Error != nil { + return nil, result.Error + } + return ticket, nil +} + +// repository order.go +func (r *OrderRepository) GetOrders(ctx context.Context) ([]*entity.Order, error) { + orders := make([]*entity.Order, 0) + err := r.db.WithContext(ctx).Preload("Ticket").Find(&orders).Error + if err != nil { + return nil, err + } + return orders, nil +} + +// get order by user_id +func (r *OrderRepository) GetOrderByUserID(ctx context.Context, userID int64) ([]*entity.Order, error) { + orders := make([]*entity.Order, 0) + err := r.db.WithContext(ctx).Preload("Ticket").Where("user_id = ?", userID).Find(&orders).Error + if err != nil { + return nil, err + } + return orders, nil +} + +// UpdateUserBalance +func (r *OrderRepository) UpdateUserBalance(ctx context.Context, userID int64, total int64) error { + user := new(entity.User) + if err := r.db.WithContext(ctx).Where("id = ?", userID).First(user).Error; err != nil { + return err + } + + if user.Saldo < total { + return errors.New("insufficient balance") + } + + user.Saldo -= total + if err := r.db.WithContext(ctx).Model(&entity.User{}).Where("id = ?", userID).Updates(user).Error; err != nil { + return err + } + + return nil +} + +// get user balance +func (r *OrderRepository) GetUserBalance(ctx context.Context, userID int64) (int64, error) { + user := new(entity.User) + if err := r.db.WithContext(ctx).Where("id = ?", userID).First(user).Error; err != nil { + return 0, err + } + + return user.Saldo, nil +} + +// GetTicketPrice +func (r *OrderRepository) GetTicketPrice(ctx context.Context, ticketID int64) (int64, error) { + ticket := new(entity.Ticket) + if err := r.db.WithContext(ctx).Where("id = ?", ticketID).First(ticket).Error; err != nil { + return 0, err + } + + return int64(ticket.Price), nil +} + +// UserCreateOrder +func (r *OrderRepository) UserCreateOrder(ctx context.Context, order *entity.Order) error { + err := r.db.WithContext(ctx).Create(&order).Error + if err != nil { + return err + } + return nil +} + +// GetOrderHistory +func (r *OrderRepository) GetOrderHistory(ctx context.Context, userID int64) ([]*entity.Order, error) { + orders := make([]*entity.Order, 0) + err := r.db.WithContext(ctx).Preload("Ticket").Where("user_id = ?", userID).Find(&orders).Error + if err != nil { + return nil, err + } + return orders, nil +} diff --git a/internal/repository/regist_repository.go b/internal/repository/regist_repository.go new file mode 100644 index 0000000..71194c4 --- /dev/null +++ b/internal/repository/regist_repository.go @@ -0,0 +1,27 @@ +package repository + +import ( + "Ticketing/entity" + "context" + + "gorm.io/gorm" +) + +type RegistrationRepository struct { + db *gorm.DB +} + +func NewRegistrationRepository(db *gorm.DB) *RegistrationRepository { + return &RegistrationRepository{ + db: db, + } +} + +func (r *RegistrationRepository) Registration(ctx context.Context, user *entity.User) error { + //menggunakan db untuk melakukan query ke database + err := r.db.WithContext(ctx).Create(&user).Error // pada line ini akan melakukan query "INSERT INTO users" + if err != nil { + return err + } + return nil +} diff --git a/internal/repository/ticket_repository.go b/internal/repository/ticket_repository.go new file mode 100644 index 0000000..80ef1cc --- /dev/null +++ b/internal/repository/ticket_repository.go @@ -0,0 +1,174 @@ +package repository + +// NOTE : +// FOLDER INI UNTUK MENANGANI KE BAGIAN DATABASE DAN QUERY +import ( + "context" + + "Ticketing/entity" + + "gorm.io/gorm" + "fmt" +) + +// ticket repository +type TicketRepository struct { + db *gorm.DB +} + +func NewTicketRepository(db *gorm.DB) *TicketRepository { + return &TicketRepository{ + db: db, + } +} + +// GetAllTickets retrieves all tickets from the database. +func (r *TicketRepository) GetAllTickets(ctx context.Context) ([]*entity.Ticket, error) { + tickets := make([]*entity.Ticket, 0) + result := r.db.WithContext(ctx).Find(&tickets) + if result.Error != nil { + return nil, result.Error + } + + // Log untuk memeriksa data sebelum dikembalikan + fmt.Printf("Tickets: %+v\n", tickets) + + return tickets, nil +} + +// CreateTicket saves a new ticket to the database. +func (r *TicketRepository) CreateTicket(ctx context.Context, ticket *entity.Ticket) error { + result := r.db.WithContext(ctx).Create(&ticket) + if result.Error != nil { + return result.Error + } + return nil +} + +// UpdateTicket updates a ticket in the database. +func (r *TicketRepository) UpdateTicket(ctx context.Context, ticket *entity.Ticket) error { + result := r.db.WithContext(ctx).Model(&entity.Ticket{}).Where("id = ?", ticket.ID).Updates(&ticket) + if result.Error != nil { + return result.Error + } + return nil +} + +// GetTicket retrieves a ticket by its ID from the database. +func (r *TicketRepository) GetTicket(ctx context.Context, id int64) (*entity.Ticket, error) { + ticket := new(entity.Ticket) + result := r.db.WithContext(ctx).First(&ticket, id) + if result.Error != nil { + return nil, result.Error + } + return ticket, nil +} + +// DeleteTicket deletes a ticket from the database. +func (r *TicketRepository) DeleteTicket(ctx context.Context, id int64) error { + result := r.db.WithContext(ctx).Delete(&entity.Ticket{}, id) + if result.Error != nil { + return result.Error + } + return nil +} + +// SearchTicket search ticket +func (r *TicketRepository) SearchTicket(ctx context.Context, search string) ([]*entity.Ticket, error) { + tickets := make([]*entity.Ticket, 0) + result := r.db.WithContext(ctx).Where("title LIKE ?", "%"+search+"%").Find(&tickets) + if result.Error != nil { + return nil, result.Error + } + return tickets, nil +} + +// filter ticket by location +func (r *TicketRepository) FilterTicket(ctx context.Context, location string) ([]*entity.Ticket, error) { + tickets := make([]*entity.Ticket, 0) + result := r.db.WithContext(ctx).Where("location LIKE ?", "%"+location+"%").Find(&tickets) + if result.Error != nil { + return nil, result.Error + } + return tickets, nil +} + +// filter ticket by category +func (r *TicketRepository) FilterTicketByCategory(ctx context.Context, category string) ([]*entity.Ticket, error) { + tickets := make([]*entity.Ticket, 0) + result := r.db.WithContext(ctx).Where("category LIKE ?", "%"+category+"%").Find(&tickets) + if result.Error != nil { + return nil, result.Error + } + return tickets, nil +} + +// filter ticket by range time (start - end) +func (r *TicketRepository) FilterTicketByRangeTime(ctx context.Context, start string, end string) ([]*entity.Ticket, error) { + tickets := make([]*entity.Ticket, 0) + result := r.db.WithContext(ctx).Where("Date >= ? AND Date <= ?", start, end).Find(&tickets) + if result.Error != nil { + return nil, result.Error + } + return tickets, nil +} + +// filter ticket by price (min - max) +func (r *TicketRepository) FilterTicketByPrice(ctx context.Context, min string, max string) ([]*entity.Ticket, error) { + tickets := make([]*entity.Ticket, 0) + result := r.db.WithContext(ctx).Where("price >= ? AND price <= ?", min, max).Find(&tickets) + if result.Error != nil { + return nil, result.Error + } + return tickets, nil +} + +// sortir tiket dari yang terbaru +func (r *TicketRepository) SortTicketByNewest(ctx context.Context) ([]*entity.Ticket, error) { + tickets := make([]*entity.Ticket, 0) + result := r.db.WithContext(ctx).Order("created_at DESC").Find(&tickets) + if result.Error != nil { + return nil, result.Error + } + return tickets, nil +} + +// sortir dari yang termahal +func (r *TicketRepository) SortTicketByMostExpensive(ctx context.Context) ([]*entity.Ticket, error) { + tickets := make([]*entity.Ticket, 0) + result := r.db.WithContext(ctx).Order("price DESC").Find(&tickets) + if result.Error != nil { + return nil, result.Error + } + return tickets, nil +} + +// sortir tiket dari yang termurah +func (r *TicketRepository) SortTicketByCheapest(ctx context.Context) ([]*entity.Ticket, error) { + tickets := make([]*entity.Ticket, 0) + result := r.db.WithContext(ctx).Order("price DESC").Find(&tickets) + if result.Error != nil { + return nil, result.Error + } + return tickets, nil +} + +// filter ticket dari yang paling banyak dibeli +func (r *TicketRepository) SortTicketByMostBought(ctx context.Context) ([]*entity.Ticket, error) { + tickets := make([]*entity.Ticket, 0) + result := r.db.WithContext(ctx).Order("Terjual DESC").Find(&tickets) + if result.Error != nil { + return nil, result.Error + } + return tickets, nil +} + +// ticket yang masih tersedia +func (r *TicketRepository) SortTicketByAvailable(ctx context.Context) ([]*entity.Ticket, error) { + tickets := make([]*entity.Ticket, 0) + result := r.db.WithContext(ctx).Where("status = ?", "available").Find(&tickets) + if result.Error != nil { + return nil, result.Error + } + return tickets, nil +} \ No newline at end of file diff --git a/internal/repository/topup_repository.go b/internal/repository/topup_repository.go new file mode 100644 index 0000000..2432d89 --- /dev/null +++ b/internal/repository/topup_repository.go @@ -0,0 +1,61 @@ +package repository + +import ( + "Ticketing/entity" + "context" + + "gorm.io/gorm" +) + +type TopupRepository interface { + InsertTopup(ctx context.Context, topup entity.Topup) (entity.Topup, error) + UserTopup(ctx context.Context, topup entity.Topup) (entity.Topup, error) + GetUserByID(ctx context.Context, id int) (*entity.User, error) + UpdateUser(ctx context.Context, user *entity.User) error +} + +type topupRepository struct { + db *gorm.DB +} + +func NewTopupRepository(db *gorm.DB) *topupRepository { + return &topupRepository{db} +} + +func (r *topupRepository) InsertTopup(ctx context.Context, topup entity.Topup) (entity.Topup, error) { + result := r.db.WithContext(ctx).Create(&topup) + if result.Error != nil { + return entity.Topup{}, result.Error + } + return topup, nil +} + +// UserTopup +func (r *topupRepository) UserTopup(ctx context.Context, topup entity.Topup) (entity.Topup, error) { + result := r.db.WithContext(ctx).Create(&topup) + if result.Error != nil { + return entity.Topup{}, result.Error + } + return topup, nil +} + +// GetUserByID +func (r *topupRepository) GetUserByID(ctx context.Context, id int) (*entity.User, error) { + user := new(entity.User) + result := r.db.WithContext(ctx).First(&user, id) + if result.Error != nil { + return nil, result.Error + } + return user, nil +} + +// UpdateUser updates the user information +func (r *topupRepository) UpdateUser(ctx context.Context, user *entity.User) error { + if err := r.db.WithContext(ctx). + Model(&entity.User{}). + Where("id = ?", user.ID). + Updates(user).Error; err != nil { + return err + } + return nil +} diff --git a/internal/repository/transaction.repository.go b/internal/repository/transaction.repository.go new file mode 100644 index 0000000..9e97ce0 --- /dev/null +++ b/internal/repository/transaction.repository.go @@ -0,0 +1,56 @@ +package repository + +import ( + "context" + + "Ticketing/entity" + + "gorm.io/gorm" +) + +type TransactionRepository struct { + db *gorm.DB +} + +func NewTransactionRepository(db *gorm.DB) *TransactionRepository { + return &TransactionRepository{db: db} +} + +func (r *TransactionRepository) Create(ctx context.Context, transaction *entity.Transaction) error { + if err := r.db.WithContext(ctx).Create(&transaction).Error; err != nil { + return err + } + return nil +} + +func (r *TransactionRepository) FindByOrderID(ctx context.Context, orderID string) (*entity.Transaction, error) { + transaction := new(entity.Transaction) + if err := r.db.WithContext(ctx).Where("order_id = ?", orderID).First(&transaction).Error; err != nil { + return nil, err + } + return transaction, nil +} + +func (r *TransactionRepository) FindByUserID(ctx context.Context, userID int64) ([]*entity.Transaction, error) { + transactions := make([]*entity.Transaction, 0) + if err := r.db.WithContext(ctx).Where("user_id = ?", userID).Find(&transactions).Error; err != nil { + return nil, err + } + return transactions, nil +} + +func (r *TransactionRepository) UpdateStatus(ctx context.Context, orderID string, status string) error { + if err := r.db.WithContext(ctx).Model(&entity.Transaction{}).Where("order_id = ?", orderID).Update("status", status).Error; err != nil { + return err + } + return nil +} + +// HistoryTransaction +func (r *TransactionRepository) HistoryTransaction(ctx context.Context, userID int64) ([]*entity.Transaction, error) { + transactions := make([]*entity.Transaction, 0) + if err := r.db.WithContext(ctx).Where("user_id = ?", userID).Find(&transactions).Error; err != nil { + return nil, err + } + return transactions, nil +} diff --git a/internal/repository/user.repository.go b/internal/repository/user.repository.go deleted file mode 100644 index 381d218..0000000 --- a/internal/repository/user.repository.go +++ /dev/null @@ -1,68 +0,0 @@ -package repository - -import ( - "context" - "errors" - - "github.com/zhikariz/weather-app/entity" - "gorm.io/gorm" -) - -type UserRepository struct { - db *gorm.DB -} - -func NewUserRepository(db *gorm.DB) *UserRepository { - return &UserRepository{ - db: db, - } -} - -func (r *UserRepository) FindAll(ctx context.Context) ([]*entity.User, error) { - users := make([]*entity.User, 0) - err := r.db.WithContext(ctx).Find(&users).Error // SELECT * FROM users - if err != nil { - return nil, err - } - return users, nil -} - -func (r *UserRepository) Create(ctx context.Context, user *entity.User) error { - if err := r.db.WithContext(ctx).Create(&user).Error; err != nil { - return err - } - return nil -} - -func (r *UserRepository) Update(ctx context.Context, user *entity.User) error { - if err := r.db.WithContext(ctx). - Model(&entity.User{}). - Where("id = ?", user.ID). - Updates(&user).Error; err != nil { - return err - } - return nil -} - -func (r *UserRepository) Delete(ctx context.Context, id int64) error { - if err := r.db.WithContext(ctx).Delete(&entity.User{}, id).Error; err != nil { - return err - } - return nil -} - -func (r *UserRepository) FindByID(ctx context.Context, id int64) (*entity.User, error) { - user := new(entity.User) - if err := r.db.WithContext(ctx).First(&user, id).Error; err != nil { - return nil, err - } - return user, nil -} - -func (r *UserRepository) FindByEmail(ctx context.Context, email string) (*entity.User, error) { - user := new(entity.User) - if err := r.db.WithContext(ctx).Where("email = ?", email).First(&user).Error; err != nil { - return nil, errors.New("user with that email not found") - } - return user, nil -} diff --git a/internal/repository/user_repository.go b/internal/repository/user_repository.go new file mode 100644 index 0000000..40ec63d --- /dev/null +++ b/internal/repository/user_repository.go @@ -0,0 +1,191 @@ +package repository + +// NOTE : +// FOLDER INI UNTUK MENANGANI KE BAGIAN DATABASE DAN QUERY +import ( + "context" + "errors" + + "Ticketing/entity" + + "gorm.io/gorm" +) + +// membuat struct untuk dependency +type UserRepository struct { + db *gorm.DB +} + +// membuat constructor untuk dependency +func NewUserRepository(db *gorm.DB) *UserRepository { + return &UserRepository{ + db: db} +} + +// menampilkan get all user +// menggunakan []*entity.User = karena akan membutuhkan data yg banyak dengan array slice of user. +func (r *UserRepository) GetAll(ctx context.Context) ([]*entity.User, error) { + //melakukan returtn dari data user itu sendir, sehingga disimpan di variabel users + users := make([]*entity.User, 0) + //menggunakan db untuk melakukan query ke database + err := r.db.WithContext(ctx).Find(&users).Error // pada line ini akan melakukan query "SELECT * FROM users" + if err != nil { + return nil, err + } + return users, nil + +} + +// membuat create user +func (r *UserRepository) CreateUser(ctx context.Context, user *entity.User) error { + //menggunakan db untuk melakukan query ke database + err := r.db.WithContext(ctx).Create(&user).Error // pada line ini akan melakukan query "INSERT INTO users" + if err != nil { + return err + } + return nil +} + +// update data user byID +func (r *UserRepository) UpdateUser(ctx context.Context, user *entity.User) error { + if err := r.db.WithContext(ctx). + Model(&entity.User{}). + Where("id = ?", user.ID). + Updates(&user).Error; err != nil { + return err + } + return nil +} + +// get user by id +func (r *UserRepository) GetUserByID(ctx context.Context, id int64) (*entity.User, error) { + user := new(entity.User) + if err := r.db.WithContext(ctx).Where("id = ?", id).First(&user).Error; err != nil { + return nil, err + } + return user, nil +} + +// detele user by id +func (r *UserRepository) Delete(ctx context.Context, id int64) error { + if err := r.db.WithContext(ctx).Delete(&entity.User{}, id).Error; err != nil { + return err + } + return nil +} + +// get by email +func (r *UserRepository) GetByEmail(ctx context.Context, email string) (*entity.User, error) { + user := new(entity.User) + if err := r.db.WithContext(ctx).Where("email = ?", email).First(&user).Error; err != nil { + return nil, errors.New("user with that email not found") + } + return user, nil +} + +// Update User Self +func (r *UserRepository) UpdateProfile(ctx context.Context, user *entity.User) error { + if err := r.db.WithContext(ctx). + Model(&entity.User{}). + Where("id = ?", user.ID). + Updates(&user).Error; err != nil { + return err + } + return nil +} + +// update user balance by id +func (r *UserRepository) UpdateUserBalance(ctx context.Context, user *entity.User) error { + if err := r.db.WithContext(ctx). + Model(&entity.User{}). + Where("id = ?", user.ID). + Updates(&user).Error; err != nil { + return err + } + return nil +} + +// GetProfile retrieves the user profile by ID from the database +func (r *UserRepository) GetProfile(ctx context.Context, userID int64) (*entity.User, error) { + var user entity.User + err := r.db.WithContext(ctx).First(&user, userID).Error + if err != nil { + return nil, err + } + return &user, nil +} + +// GetUserBalance +func (r *UserRepository) GetUserBalance(ctx context.Context, userID int64) (*entity.User, error) { + var user entity.User + err := r.db.WithContext(ctx).First(&user, userID).Error + if err != nil { + return nil, err + } + return &user, nil +} + +// DeleteAccount +func (r *UserRepository) DeleteAccount(ctx context.Context, email string) error { + if err := r.db.WithContext(ctx).Delete(&entity.User{}, email).Error; err != nil { + return err + } + return nil +} + +// upgrade saldo +func (r *UserRepository) UpgradeSaldo(ctx context.Context, user *entity.User) error { + if err := r.db.WithContext(ctx). + Model(&entity.User{}). + Where("id = ?", user.ID). + Updates(&user).Error; err != nil { + return err + } + return nil +} + +// logout +func (r *UserRepository) UserLogout(ctx context.Context, user *entity.User) error { + if err := r.db.WithContext(ctx). + Model(&entity.User{}). + Where("id = ?", user.ID). + Updates(&user).Error; err != nil { + return err + } + return nil +} + +// UpdateSaldo +// UpdateSaldo updates the saldo of a user by ID +func (r *UserRepository) UpdateSaldo(ctx context.Context, userID int64, updatedSaldo int64) error { + user := &entity.User{ID: userID, Saldo: updatedSaldo} + + if err := r.db.WithContext(ctx).Model(&entity.User{}).Where("id = ?", userID).Updates(&user).Error; err != nil { + return err + } + return nil +} + +// FindByID +func (r *UserRepository) FindByID(ctx context.Context, id int64) (*entity.User, error) { + user := new(entity.User) + if err := r.db.WithContext(ctx).Where("id = ?", id).First(&user).Error; err != nil { + return nil, err + } + return user, nil +} + +// TanyaAI +// func (r *UserRepository) TanyaAI(ctx context.Context, user *entity.User) error { +// return nil +// } + +//BuyerCreateAccount +// func (r *UserRepository) BuyerCreateAccount(ctx context.Context, user *entity.User) error { +// //menggunakan db untuk melakukan query ke database +// err := r.db.WithContext(ctx).Create(&user).Error // pada line ini akan melakukan query "INSERT INTO users" +// if err != nil { +// return err +// } +// return nil +// } diff --git a/internal/service/auth_service.go b/internal/service/auth_service.go new file mode 100644 index 0000000..aaff9f4 --- /dev/null +++ b/internal/service/auth_service.go @@ -0,0 +1,109 @@ +package service + +import ( + "Ticketing/entity" + "context" + "errors" + + "golang.org/x/crypto/bcrypt" +) + +// login +type LoginUseCase interface { + Login(ctx context.Context, email string, password string) (*entity.User, error) +} + +type LoginRepository interface { + GetByEmail(ctx context.Context, email string) (*entity.User, error) +} + +type loginService struct { + repository LoginRepository +} + +func NewLoginService(repository LoginRepository) *loginService { + return &loginService{ + repository: repository, + } +} + +// func untuk melakikan pengecekan apakah semua data nya sama mulai dari email, password +func (s *loginService) Login(ctx context.Context, email string, password string) (*entity.User, error) { + user, err := s.repository.GetByEmail(ctx, email) + if err != nil { + return nil, err + } + + //untuk pengecakan apakah email ada di database? + if user == nil { + return nil, errors.New("user with that email not found") + } + + //untuk pengecekan apakah password nya ada atau gaa di databse? + err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) + if err != nil { + return nil, errors.New("incorrect login credentials") + } + //ketika email dan passwerd sama maka akan mengembalikan nil + return user, nil + +} + +// register +type RegistrationUseCase interface { + Registration(ctx context.Context, user *entity.User) error +} + +type RegistrationRepository interface { + Registration(ctx context.Context, user *entity.User) error + // GetByEmail(ctx context.Context, email string) (*entity.User, error) +} + +type registrationService struct { + repository RegistrationRepository +} + +func NewRegistrationService(repository RegistrationRepository) *registrationService { + return ®istrationService{ + repository: repository, + } +} + +func (s *registrationService) Registration(ctx context.Context, user *entity.User) error { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost) + if err != nil { + return err + } + + user.Password = string(hashedPassword) + return s.repository.Registration(ctx, user) +} + +// BuyerCreateAccount +type BuyerCreateAccountUseCase interface { + BuyerCreateAccount(ctx context.Context, user *entity.User) error +} + +type BuyerCreateAccountRepository interface { + BuyerCreateAccount(ctx context.Context, user *entity.User) error +} + +type buyercreateaccountService struct { + repository BuyerCreateAccountRepository +} + +func NewBuyerCreateAccountService(repository BuyerCreateAccountRepository) *buyercreateaccountService { + return &buyercreateaccountService{ + repository: repository, + } +} + +// func (s *buyercreateaccountService) BuyerCreateAccount(ctx context.Context, user *entity.User) error { +// hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost) +// if err != nil { +// return err +// } + +// user.Password = string(hashedPassword) +// return s.repository.BuyerCreateAccount(ctx, user) +// } \ No newline at end of file diff --git a/internal/service/blog_services.go b/internal/service/blog_services.go new file mode 100644 index 0000000..8202aee --- /dev/null +++ b/internal/service/blog_services.go @@ -0,0 +1,61 @@ +package service + +import ( + "context" + + "Ticketing/entity" +) + +// BlogUseCase is an interface for Blog-related use cases. +type BlogUseCase interface { + GetAllBlogs(ctx context.Context) ([]*entity.Blog, error) + CreateBlog(ctx context.Context, Blog *entity.Blog) error + GetBlog(ctx context.Context, id int64) (*entity.Blog, error) + UpdateBlog(ctx context.Context, Blog *entity.Blog) error + SearchBlog(ctx context.Context, search string) ([]*entity.Blog, error) + DeleteBlog(ctx context.Context, id int64) error +} + +type BlogRepository interface { + GetAllBlogs(ctx context.Context) ([]*entity.Blog, error) + CreateBlog(ctx context.Context, Blog *entity.Blog) error + GetBlog(ctx context.Context, id int64) (*entity.Blog, error) + UpdateBlog(ctx context.Context, Blog *entity.Blog) error + SearchBlog(ctx context.Context, search string) ([]*entity.Blog, error) + DeleteBlog(ctx context.Context, id int64) error +} + +// BlogService is responsible for Blog-related business logic. +type BlogService struct { + Repository BlogRepository +} + +// NewBlogService creates a new instance of BlogService. +func NewBlogService(Repository BlogRepository) *BlogService { + return &BlogService{Repository: Repository} +} + +func (s *BlogService) GetAllBlogs(ctx context.Context) ([]*entity.Blog, error) { + return s.Repository.GetAllBlogs(ctx) +} + +func (s *BlogService) CreateBlog(ctx context.Context, Blog *entity.Blog) error { + return s.Repository.CreateBlog(ctx, Blog) +} + +func (s *BlogService) UpdateBlog(ctx context.Context, Blog *entity.Blog) error { + return s.Repository.UpdateBlog(ctx, Blog) +} + +func (s *BlogService) GetBlog(ctx context.Context, id int64) (*entity.Blog, error) { + return s.Repository.GetBlog(ctx, id) +} + +func (s *BlogService) DeleteBlog(ctx context.Context, id int64) error { + return s.Repository.DeleteBlog(ctx, id) +} + +// search Blog +func (s *BlogService) SearchBlog(ctx context.Context, search string) ([]*entity.Blog, error) { + return s.Repository.SearchBlog(ctx, search) +} \ No newline at end of file diff --git a/internal/service/login.service.go b/internal/service/login.service.go deleted file mode 100644 index a05eb76..0000000 --- a/internal/service/login.service.go +++ /dev/null @@ -1,44 +0,0 @@ -package service - -import ( - "context" - "errors" - - "github.com/zhikariz/weather-app/entity" -) - -type LoginUseCase interface { - Login(ctx context.Context, email, password string) (*entity.User, error) -} - -type LoginRepository interface { - FindByEmail(ctx context.Context, email string) (*entity.User, error) -} - -type LoginService struct { - repo LoginRepository -} - -func NewLoginService(repo LoginRepository) *LoginService { - return &LoginService{ - repo: repo, - } -} - -func (s *LoginService) Login(ctx context.Context, email, password string) (*entity.User, error) { - user, err := s.repo.FindByEmail(ctx, email) - - if err != nil { - return nil, err - } - - if user == nil { - return nil, errors.New("user with that email not found") - } - - if user.Password != password { - return nil, errors.New("incorrect login credentials") - } - - return user, nil -} diff --git a/internal/service/notification_services.go b/internal/service/notification_services.go new file mode 100644 index 0000000..363a2ba --- /dev/null +++ b/internal/service/notification_services.go @@ -0,0 +1,41 @@ +package service + +import ( + "Ticketing/entity" + "context" +) + +type NotificationUsecase interface { + GetAllNotification(ctx context.Context) ([]*entity.Notification, error) + CreateNotification(ctx context.Context, Notification *entity.Notification) error + UserGetNotification(ctx context.Context) ([]*entity.Notification, error) +} + +type NotificationRepository interface { + GetAllNotification(ctx context.Context) ([]*entity.Notification, error) + CreateNotification(ctx context.Context, Notification *entity.Notification) error + UserGetNotification(ctx context.Context) ([]*entity.Notification, error) +} + +type NotificationService struct { + Repository NotificationRepository +} + +func NewNotificationService(Repository NotificationRepository) *NotificationService { + return &NotificationService{Repository: Repository} +} + +// Get All Notification ketika di get maka status notifikasi akan berubah menjadi true +func (s *NotificationService) GetAllNotification(ctx context.Context) ([]*entity.Notification, error) { + return s.Repository.GetAllNotification(ctx) +} + +// func untuk create notification +func (s *NotificationService) CreateNotification(ctx context.Context, Notification *entity.Notification) error { + return s.Repository.CreateNotification(ctx, Notification) +} + +// get notification after get chage value isRead to true and only get notification if isread false UserGetNotification +func (s *NotificationService) UserGetNotification(ctx context.Context) ([]*entity.Notification, error) { + return s.Repository.UserGetNotification(ctx) +} diff --git a/internal/service/order_service.go b/internal/service/order_service.go new file mode 100644 index 0000000..5116d58 --- /dev/null +++ b/internal/service/order_service.go @@ -0,0 +1,160 @@ +package service + +import ( + "Ticketing/entity" + "context" + "errors" +) + +type OrderUsecase interface { + CreateOrder(ctx context.Context, order *entity.Order) error + GetTicket(ctx context.Context, ticketID int64) (*entity.Ticket, error) + UpdateTicket(ctx context.Context, ticket *entity.Ticket) error + GetOrders(ctx context.Context) ([]*entity.Order, error) + GetTicketByID(ctx context.Context, id int64) (*entity.Ticket, error) + GetOrderByUserID(ctx context.Context, userID int64) ([]*entity.Order, error) + UpdateUserBalance(ctx context.Context, userID int64, amount int64) error + GetUserBalance(ctx context.Context, userID int64) (int64, error) + GetTicketPrice(ctx context.Context, ticketID int64) (int64, error) + UserCreateOrder(ctx context.Context, order *entity.Order) error + GetOrderHistory(ctx context.Context, userID int64) ([]*entity.Order, error) +} + +type OrderRepository interface { + CreateOrder(ctx context.Context, order *entity.Order) error + GetTicket(ctx context.Context, ticketID int64) (*entity.Ticket, error) + UpdateTicket(ctx context.Context, ticket *entity.Ticket) error + GetOrders(ctx context.Context) ([]*entity.Order, error) + GetTicketByID(ctx context.Context, id int64) (*entity.Ticket, error) + GetOrderByUserID(ctx context.Context, userID int64) ([]*entity.Order, error) + UpdateUserBalance(ctx context.Context, userID int64, amount int64) error + GetUserBalance(ctx context.Context, userID int64) (int64, error) + GetTicketPrice(ctx context.Context, ticketID int64) (int64, error) + UserCreateOrder(ctx context.Context, order *entity.Order) error + GetOrderHistory(ctx context.Context, userID int64) ([]*entity.Order, error) +} + +type OrderService struct { + repository OrderRepository +} + +func NewOrderService(repository OrderRepository) *OrderService { + return &OrderService{repository} +} + +// Updated CreateOrder method in OrderService to receive TicketService +func (s *OrderService) CreateOrder(ctx context.Context, order *entity.Order) error { + // Mendapatkan informasi tiket berdasarkan ID tiket dalam pesanan + ticket, err := s.repository.GetTicket(ctx, order.TicketID) + if err != nil { + return err + } + + // Memeriksa ketersediaan tiket + if int64(ticket.Quota) < order.Quantity { + return errors.New("ticket is not available") + } + + // Melakukan perhitungan total harga pesanan + order.Total = ticket.Price * int64(order.Quantity) + + // Membuat pesanan + if err := s.repository.CreateOrder(ctx, order); err != nil { + return err + } + + // Mengurangi ketersediaan tiket + ticket.Quota -= order.Quantity + if err := s.repository.UpdateTicket(ctx, ticket); err != nil { + return err + } + + // Mengurangi saldo pengguna + if err := s.repository.UpdateUserBalance(ctx, order.UserID, order.Total); err != nil { + return err + } + + return nil +} + +// Implementasi fungsi GetTicket +func (s *OrderService) GetTicket(ctx context.Context, ticketID int64) (*entity.Ticket, error) { + return s.repository.GetTicket(ctx, ticketID) +} + +// Implementasi fungsi UpdateTicket +func (s *OrderService) UpdateTicket(ctx context.Context, ticket *entity.Ticket) error { + return s.repository.UpdateTicket(ctx, ticket) +} + +// implementasi fungsi update user balance +func (s *OrderService) UpdateUserBalance(ctx context.Context, userID int64, saldo int64) error { + return s.repository.UpdateUserBalance(ctx, userID, saldo) +} + +func (s *OrderService) GetOrders(ctx context.Context) ([]*entity.Order, error) { + return s.repository.GetOrders(ctx) +} + +func (s *OrderService) GetTicketByID(ctx context.Context, id int64) (*entity.Ticket, error) { + return s.repository.GetTicketByID(ctx, id) +} + +// get order by user_id +func (s *OrderService) GetOrderByUserID(ctx context.Context, userID int64) ([]*entity.Order, error) { + return s.repository.GetOrderByUserID(ctx, userID) +} + +// get user balance +func (s *OrderService) GetUserBalance(ctx context.Context, userID int64) (int64, error) { + return s.repository.GetUserBalance(ctx, userID) +} + +// GetTicketPrice +func (s *OrderService) GetTicketPrice(ctx context.Context, ticketID int64) (int64, error) { + ticket, err := s.repository.GetTicket(ctx, ticketID) + if err != nil { + return 0, err + } + return int64(ticket.Price), nil +} + +// UserCreateOrder +func (s *OrderService) UserCreateOrder(ctx context.Context, order *entity.Order) error { + // Mendapatkan informasi tiket berdasarkan ID tiket dalam pesanan + ticket, err := s.repository.GetTicket(ctx, order.TicketID) + if err != nil { + return err + } + + // Memeriksa ketersediaan tiket + if int64(ticket.Quota) < order.Quantity { + return errors.New("ticket is not available") + } + + // Melakukan perhitungan total harga pesanan + order.Total = ticket.Price * int64(order.Quantity) + + // Membuat pesanan + if err := s.repository.CreateOrder(ctx, order); err != nil { + return err + } + + // Mengurangi ketersediaan tiket + ticket.Quota -= order.Quantity + if err := s.repository.UpdateTicket(ctx, ticket); err != nil { + return err + } + + // Mengurangi saldo pengguna + if err := s.repository.UpdateUserBalance(ctx, order.UserID, order.Total); err != nil { + return err + } + + return nil +} + +// GetOrderHistory +func (s *OrderService) GetOrderHistory(ctx context.Context, userID int64) ([]*entity.Order, error) { + return s.repository.GetOrderByUserID(ctx, userID) +} diff --git a/internal/service/payment.service.go b/internal/service/payment.service.go new file mode 100644 index 0000000..d90ee3b --- /dev/null +++ b/internal/service/payment.service.go @@ -0,0 +1,43 @@ +package service + +import ( + "context" + + "Ticketing/entity" + + "github.com/midtrans/midtrans-go" + "github.com/midtrans/midtrans-go/snap" +) + +type PaymentUseCase interface { + CreateTransaction(ctx context.Context, paymentRequest *entity.PaymentRequest) (string, error) +} + +type PaymentService struct { + client snap.Client +} + +func NewPaymentService(client snap.Client) *PaymentService { + return &PaymentService{client: client} +} + +func (s *PaymentService) CreateTransaction(ctx context.Context, paymentRequest *entity.PaymentRequest) (string, error) { + request := &snap.Request{ + TransactionDetails: midtrans.TransactionDetails{ + OrderID: paymentRequest.OrderID, + GrossAmt: paymentRequest.Amount, + }, + CustomerDetail: &midtrans.CustomerDetails{ + FName: paymentRequest.FirstName, + LName: paymentRequest.LastName, + Email: paymentRequest.Email, + }, + } + + snapResponse, err := s.client.CreateTransaction(request) + if err != nil { + return "", err + } + + return snapResponse.RedirectURL, nil +} diff --git a/internal/service/ticket_services.go b/internal/service/ticket_services.go new file mode 100644 index 0000000..a767397 --- /dev/null +++ b/internal/service/ticket_services.go @@ -0,0 +1,125 @@ +package service + +import ( + "context" + + "Ticketing/entity" +) + +// TicketUseCase is an interface for ticket-related use cases. +type TicketUseCase interface { + GetAllTickets(ctx context.Context) ([]*entity.Ticket, error) + CreateTicket(ctx context.Context, ticket *entity.Ticket) error + GetTicket(ctx context.Context, id int64) (*entity.Ticket, error) + UpdateTicket(ctx context.Context, ticket *entity.Ticket) error + DeleteTicket(ctx context.Context, id int64) error + SearchTicket(ctx context.Context, search string) ([]*entity.Ticket, error) + FilterTicket(ctx context.Context, location string) ([]*entity.Ticket, error) + FilterTicketByCategory(ctx context.Context, category string) ([]*entity.Ticket, error) + FilterTicketByRangeTime(ctx context.Context, start string, end string) ([]*entity.Ticket, error) + FilterTicketByPrice(ctx context.Context, min string, max string) ([]*entity.Ticket, error) + SortTicketByNewest(ctx context.Context) ([]*entity.Ticket, error) + SortTicketByMostExpensive(ctx context.Context) ([]*entity.Ticket, error) + SortTicketByCheapest(ctx context.Context) ([]*entity.Ticket, error) + SortTicketByMostBought(ctx context.Context) ([]*entity.Ticket, error) + SortTicketByAvailable(ctx context.Context) ([]*entity.Ticket, error) + +} + +type TicketRepository interface { + GetAllTickets(ctx context.Context) ([]*entity.Ticket, error) + CreateTicket(ctx context.Context, ticket *entity.Ticket) error + GetTicket(ctx context.Context, id int64) (*entity.Ticket, error) + UpdateTicket(ctx context.Context, ticket *entity.Ticket) error + SearchTicket(ctx context.Context, search string) ([]*entity.Ticket, error) + DeleteTicket(ctx context.Context, id int64) error + FilterTicket(ctx context.Context, location string) ([]*entity.Ticket, error) + FilterTicketByCategory(ctx context.Context, category string) ([]*entity.Ticket, error) + FilterTicketByRangeTime(ctx context.Context, start string, end string) ([]*entity.Ticket, error) + FilterTicketByPrice(ctx context.Context, min string, max string) ([]*entity.Ticket, error) + SortTicketByNewest(ctx context.Context) ([]*entity.Ticket, error) + SortTicketByMostExpensive(ctx context.Context) ([]*entity.Ticket, error) + SortTicketByCheapest(ctx context.Context) ([]*entity.Ticket, error) + SortTicketByMostBought(ctx context.Context) ([]*entity.Ticket, error) + SortTicketByAvailable(ctx context.Context) ([]*entity.Ticket, error) +} + +// TicketService is responsible for ticket-related business logic. +type TicketService struct { + Repository TicketRepository +} + +// NewTicketService creates a new instance of TicketService. +func NewTicketService(Repository TicketRepository) *TicketService { + return &TicketService{Repository: Repository} +} + +func (s *TicketService) GetAllTickets(ctx context.Context) ([]*entity.Ticket, error) { + return s.Repository.GetAllTickets(ctx) +} + +func (s *TicketService) CreateTicket(ctx context.Context, ticket *entity.Ticket) error { + return s.Repository.CreateTicket(ctx, ticket) +} + +func (s *TicketService) UpdateTicket(ctx context.Context, ticket *entity.Ticket) error { + return s.Repository.UpdateTicket(ctx, ticket) +} + +func (s *TicketService) GetTicket(ctx context.Context, id int64) (*entity.Ticket, error) { + return s.Repository.GetTicket(ctx, id) +} + +func (s *TicketService) DeleteTicket(ctx context.Context, id int64) error { + return s.Repository.DeleteTicket(ctx, id) +} + +// search ticket +func (s *TicketService) SearchTicket(ctx context.Context, search string) ([]*entity.Ticket, error) { + return s.Repository.SearchTicket(ctx, search) +} + +// filter ticket by location +func (s *TicketService) FilterTicket(ctx context.Context, location string) ([]*entity.Ticket, error) { + return s.Repository.FilterTicket(ctx, location) +} + +// filter ticket by category +func (s *TicketService) FilterTicketByCategory(ctx context.Context, category string) ([]*entity.Ticket, error) { + return s.Repository.FilterTicketByCategory(ctx, category) +} + +// filter ticket by range time (start - end) +func (s *TicketService) FilterTicketByRangeTime(ctx context.Context, start string, end string) ([]*entity.Ticket, error) { + return s.Repository.FilterTicketByRangeTime(ctx, start, end) +} + +// filter ticket by price (min - max) +func (s *TicketService) FilterTicketByPrice(ctx context.Context, min string, max string) ([]*entity.Ticket, error) { + return s.Repository.FilterTicketByPrice(ctx, min, max) +} + +// sortir tiket dari yang terbaru +func (s *TicketService) SortTicketByNewest(ctx context.Context) ([]*entity.Ticket, error) { + return s.Repository.SortTicketByNewest(ctx) +} + +// sortir dari yang termahal +func (s *TicketService) SortTicketByMostExpensive(ctx context.Context) ([]*entity.Ticket, error) { + return s.Repository.SortTicketByMostExpensive(ctx) +} + +// sortir tiket dari yang termurah +func (s *TicketService) SortTicketByCheapest(ctx context.Context) ([]*entity.Ticket, error) { + return s.Repository.SortTicketByCheapest(ctx) +} + +// filter ticket by most bought +func (s *TicketService) SortTicketByMostBought(ctx context.Context) ([]*entity.Ticket, error) { + return s.Repository.SortTicketByMostBought(ctx) +} + +// ticket yang masih tersedia +func (s *TicketService) SortTicketByAvailable(ctx context.Context) ([]*entity.Ticket, error) { + return s.Repository.SortTicketByAvailable(ctx) +} \ No newline at end of file diff --git a/internal/service/token.service.go b/internal/service/token_service.go similarity index 58% rename from internal/service/token.service.go rename to internal/service/token_service.go index 1e450b5..a1e3805 100644 --- a/internal/service/token.service.go +++ b/internal/service/token_service.go @@ -1,47 +1,47 @@ -package service - -import ( - "context" - "time" - - "github.com/golang-jwt/jwt/v5" - "github.com/zhikariz/weather-app/common" - "github.com/zhikariz/weather-app/entity" - "github.com/zhikariz/weather-app/internal/config" -) - -type TokenUseCase interface { - GenerateAccessToken(ctx context.Context, user *entity.User) (string, error) -} - -type TokenService struct { - cfg *config.Config -} - -func NewTokenService(cfg *config.Config) *TokenService { - return &TokenService{ - cfg: cfg, - } -} - -func (s *TokenService) GenerateAccessToken(ctx context.Context, user *entity.User) (string, error) { - expiredTime := time.Now().Local().Add(10 * time.Minute) - claims := common.JwtCustomClaims{ - ID: user.ID, - Name: user.Name, - Email: user.Email, - RegisteredClaims: jwt.RegisteredClaims{ - ExpiresAt: jwt.NewNumericDate(expiredTime), - }, - } - - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - - encodedToken, err := token.SignedString([]byte(s.cfg.JWT.SecretKey)) - - if err != nil { - return "", err - } - - return encodedToken, nil -} +package service + +import ( + "Ticketing/common" + "Ticketing/entity" + "Ticketing/internal/config" + "context" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +type TokenUsecase interface { + GenerateAccessToken(ctx context.Context, user *entity.User) (string, error) +} + +type TokenService struct { + cfg *config.Config //ini dipake krna secret key nya diambil dari config +} + +func NewTokenService(cfg *config.Config) *TokenService { + return &TokenService{ + cfg: cfg, + } +} + +// untuk generate token +func (s *TokenService) GenerateAccessToken(ctx context.Context, user *entity.User) (string, error) { + expiredTime := time.Now().Local().Add(10 * time.Minute) //ini untuk mengatur waktu kadaluarsa token + claims := common.JwtCustomClaims{ + ID: user.ID, + Name: user.Name, + Email: user.Email, + Role: user.Roles, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(expiredTime), + }, + } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) //ini untuk membuat token + encodedToken, err := token.SignedString([]byte(s.cfg.JWT.SecretKey)) //ini untuk mengenkripsi token + + if err != nil { + return "", err + } + return encodedToken, nil + +} diff --git a/internal/service/topup_services.go b/internal/service/topup_services.go new file mode 100644 index 0000000..2bd8bd5 --- /dev/null +++ b/internal/service/topup_services.go @@ -0,0 +1,78 @@ +package service + +import ( + "Ticketing/entity" + "Ticketing/internal/repository" + "context" + + "github.com/midtrans/midtrans-go" + "github.com/midtrans/midtrans-go/coreapi" +) + +type TopupService interface { + CreateTopup(ctx context.Context, topup entity.Topup) (entity.Topup, error) + CreateMidtransCharge(orderID string, amount int64) (*coreapi.ChargeResponse, error) + UpdateUserSaldo(ctx context.Context, userID int, amount int64) (int64, error) + UserTopup(ctx context.Context, userID int, topup entity.Topup) (entity.Topup, error) + + // TopupSaldo(ctx context.Context, topup entity.Topup) (entity.Topup, error) +} + +type topupService struct { + topupRepository repository.TopupRepository +} + +type TopupRepository interface { + UserTopup(ctx context.Context, topup entity.Topup) (entity.Topup, error) + UpdateUserSaldo(ctx context.Context, userID int, amount int64) (int64, error) +} +type TopupUsecase interface { + UserTopup(ctx context.Context, topup entity.Topup) (entity.Topup, error) + UpdateUserSaldo(ctx context.Context, userID int, amount int64) (int64, error) +} + +func NewTopupService(topupRepository repository.TopupRepository) *topupService { + return &topupService{topupRepository} +} +func (s *topupService) CreateTopup(ctx context.Context, topup entity.Topup) (entity.Topup, error) { + return s.topupRepository.InsertTopup(ctx, topup) +} + +func (s *topupService) CreateMidtransCharge(orderID string, amount int64) (*coreapi.ChargeResponse, error) { + c := coreapi.Client{} + c.New("SB-Mid-server-RGIeiP4-kdQSADSfhbpG42B-", midtrans.Sandbox) // Ganti dengan server key Anda + + chargeReq := &coreapi.ChargeReq{ + PaymentType: coreapi.PaymentTypeBankTransfer, // Sesuaikan dengan jenis pembayaran + TransactionDetails: midtrans.TransactionDetails{ + OrderID: orderID, + GrossAmt: amount, + }, + // Tambahkan detail lainnya sesuai kebutuhan + } + + return c.ChargeTransaction(chargeReq) +} + +// UserTopup +func (s *topupService) UserTopup(ctx context.Context, userID int, topup entity.Topup) (entity.Topup, error) { + return s.topupRepository.UserTopup(ctx, topup) +} + +// TopupService method for updating user saldo +func (s *topupService) UpdateUserSaldo(ctx context.Context, userID int, amount int64) (int64, error) { + user, err := s.topupRepository.GetUserByID(ctx, userID) + if err != nil { + return 0, err + } + + // Update user saldo + user.Saldo += amount + + // Save the updated user information + if err := s.topupRepository.UpdateUser(ctx, user); err != nil { + return 0, err + } + + return user.Saldo, nil +} diff --git a/internal/service/transaction.service.go b/internal/service/transaction.service.go new file mode 100644 index 0000000..0c512b1 --- /dev/null +++ b/internal/service/transaction.service.go @@ -0,0 +1,52 @@ +package service + +import ( + "context" + + "Ticketing/entity" +) + +type TransactionUseCase interface { + Create(ctx context.Context, transaction *entity.Transaction) error + FindByOrderID(ctx context.Context, orderID string) (*entity.Transaction, error) + FindByUserID(ctx context.Context, userID int64) ([]*entity.Transaction, error) + UpdateStatus(ctx context.Context, orderID string, status string) error + HistoryTransaction(ctx context.Context, userID int64) ([]*entity.Transaction, error) +} + +type TransactionRepository interface { + Create(ctx context.Context, transaction *entity.Transaction) error + FindByOrderID(ctx context.Context, orderID string) (*entity.Transaction, error) + FindByUserID(ctx context.Context, userID int64) ([]*entity.Transaction, error) + UpdateStatus(ctx context.Context, orderID string, status string) error + HistoryTransaction(ctx context.Context, userID int64) ([]*entity.Transaction, error) +} + +type TransactionService struct { + repo TransactionRepository +} + +func NewTransactionService(repo TransactionRepository) *TransactionService { + return &TransactionService{repo: repo} +} + +func (s *TransactionService) Create(ctx context.Context, transaction *entity.Transaction) error { + return s.repo.Create(ctx, transaction) +} + +func (s *TransactionService) FindByOrderID(ctx context.Context, orderID string) (*entity.Transaction, error) { + return s.repo.FindByOrderID(ctx, orderID) +} + +func (s *TransactionService) FindByUserID(ctx context.Context, userID int64) ([]*entity.Transaction, error) { + return s.repo.FindByUserID(ctx, userID) +} + +func (s *TransactionService) UpdateStatus(ctx context.Context, orderID string, status string) error { + return s.repo.UpdateStatus(ctx, orderID, status) +} + +// HistoryTransaction +func (s *TransactionService) HistoryTransaction(ctx context.Context, userID int64) ([]*entity.Transaction, error) { + return s.repo.FindByUserID(ctx, userID) +} diff --git a/internal/service/user.service.go b/internal/service/user.service.go deleted file mode 100644 index b5a056d..0000000 --- a/internal/service/user.service.go +++ /dev/null @@ -1,51 +0,0 @@ -package service - -import ( - "context" - - "github.com/zhikariz/weather-app/entity" -) - -type UserUseCase interface { - FindAll(ctx context.Context) ([]*entity.User, error) - Create(ctx context.Context, user *entity.User) error - Update(ctx context.Context, user *entity.User) error - Delete(ctx context.Context, id int64) error - FindByID(ctx context.Context, id int64) (*entity.User, error) -} - -type UserRepository interface { - FindAll(ctx context.Context) ([]*entity.User, error) - Create(ctx context.Context, user *entity.User) error - Update(ctx context.Context, user *entity.User) error - Delete(ctx context.Context, id int64) error - FindByID(ctx context.Context, id int64) (*entity.User, error) -} - -type UserService struct { - repository UserRepository -} - -func NewUserService(repository UserRepository) *UserService { - return &UserService{repository} -} - -func (s *UserService) FindAll(ctx context.Context) ([]*entity.User, error) { - return s.repository.FindAll(ctx) -} - -func (s *UserService) Create(ctx context.Context, user *entity.User) error { - return s.repository.Create(ctx, user) -} - -func (s *UserService) Update(ctx context.Context, user *entity.User) error { - return s.repository.Update(ctx, user) -} - -func (s *UserService) Delete(ctx context.Context, id int64) error { - return s.repository.Delete(ctx, id) -} - -func (s *UserService) FindByID(ctx context.Context, id int64) (*entity.User, error) { - return s.repository.FindByID(ctx, id) -} diff --git a/internal/service/user_service.go b/internal/service/user_service.go new file mode 100644 index 0000000..0bd0145 --- /dev/null +++ b/internal/service/user_service.go @@ -0,0 +1,149 @@ +package service + +//NOTE : +// FOLDER INI UNTUK MENANGANI LOGIC DAN MEMANGGIL REPOSITORY +import ( + "context" + + "Ticketing/entity" +) + +// interface untuk service +// untuk memanngil repository +type UserUsecase interface { + GetAll(ctx context.Context) ([]*entity.User, error) + CreateUser(ctx context.Context, user *entity.User) error + UpdateUser(ctx context.Context, user *entity.User) error + GetUserByID(ctx context.Context, id int64) (*entity.User, error) + Delete(ctx context.Context, id int64) error + UpdateProfile(ctx context.Context, user *entity.User) error + UpdateUserBalance(ctx context.Context, user *entity.User) error + GetProfile(ctx context.Context, userID int64) (*entity.User, error) + GetUserBalance(ctx context.Context, userID int64) (*entity.User, error) + DeleteAccount(ctx context.Context, email string) error + UpgradeSaldo(ctx context.Context, user *entity.User) error + UserLogout(ctx context.Context, user *entity.User) error + UpdateSaldo(ctx context.Context, userID int64, updatedSaldo int64) error + FindByID(ctx context.Context, id int64) (*entity.User, error) + // BuyerCreateAccount(ctx context.Context, user *entity.User) error +} + +// interface untuk repository +// untuk memanggil repository +// GetAll = untuk menampilkan semua data user, dan itu harus sama dengan yang ada di repository +type UserRepository interface { + GetAll(ctx context.Context) ([]*entity.User, error) + CreateUser(ctx context.Context, user *entity.User) error + UpdateUser(ctx context.Context, user *entity.User) error + GetUserByID(ctx context.Context, id int64) (*entity.User, error) + Delete(ctx context.Context, id int64) error + UpdateProfile(ctx context.Context, user *entity.User) error + UpdateUserBalance(ctx context.Context, user *entity.User) error + GetProfile(ctx context.Context, userID int64) (*entity.User, error) + GetUserBalance(ctx context.Context, userID int64) (*entity.User, error) + DeleteAccount(ctx context.Context, email string) error + UpgradeSaldo(ctx context.Context, user *entity.User) error + UserLogout(ctx context.Context, user *entity.User) error + UpdateSaldo(ctx context.Context, userID int64, updatedSaldo int64) error + FindByID(ctx context.Context, id int64) (*entity.User, error) + // BuyerCreateAccount(ctx context.Context, user *entity.User) error +} + +// code di line 23 merupakan dependency injection, karena repository tidak langsung di panggil. +// karena repository dipanggil melalui code pada line 18 +type UserService struct { + repository UserRepository +} + +// func untuk UserRepository +func NewUserService(repository UserRepository) *UserService { + return &UserService{repository} +} + +// func dibawah ini untuk type user usecase +// ini untuk menampilkan data user +// untuk memanggil repository +func (s *UserService) GetAll(ctx context.Context) ([]*entity.User, error) { + return s.repository.GetAll(ctx) +} + +// func dibawah ini untuk type user usecase +// ini untuk membuat data user +func (s *UserService) CreateUser(ctx context.Context, user *entity.User) error { + return s.repository.CreateUser(ctx, user) +} + +// untuk update data user +func (s *UserService) UpdateUser(ctx context.Context, user *entity.User) error { + return s.repository.UpdateUser(ctx, user) +} + +// untuk get user by id +func (s *UserService) GetUserByID(ctx context.Context, id int64) (*entity.User, error) { + return s.repository.GetUserByID(ctx, id) +} + +// untuk delete by id +func (s *UserService) Delete(ctx context.Context, id int64) error { + return s.repository.Delete(ctx, id) +} + +// func update saldo user by id +func (s *UserService) UpdateUserBalance(ctx context.Context, user *entity.User) error { + return s.repository.UpdateUserBalance(ctx, user) +} + +// Update User Self +func (s *UserService) UpdateProfile(ctx context.Context, user *entity.User) error { + return s.repository.UpdateProfile(ctx, user) +} + +// GetProfile retrieves the user profile by ID +func (s *UserService) GetProfile(ctx context.Context, userID int64) (*entity.User, error) { + return s.repository.GetProfile(ctx, userID) +} + +// GetUserBalance +func (s *UserService) GetUserBalance(ctx context.Context, userID int64) (*entity.User, error) { + return s.repository.GetUserBalance(ctx, userID) +} + +// DeleteAccount +func (s *UserService) DeleteAccount(ctx context.Context, email string) error { + return s.repository.DeleteAccount(ctx, email) +} + +// upgrade saldo +func (s *UserService) UpgradeSaldo(ctx context.Context, user *entity.User) error { + return s.repository.UpgradeSaldo(ctx, user) +} + +// logout +func (s *UserService) UserLogout(ctx context.Context, user *entity.User) error { + return s.repository.UserLogout(ctx, user) +} + +// UpdateSaldo updates the saldo of a user by ID +func (s *UserService) UpdateSaldo(ctx context.Context, userID int64, updatedSaldo int64) error { + return s.repository.UpdateSaldo(ctx, userID, updatedSaldo) +} + +// FindByID +func (s *UserService) FindByID(ctx context.Context, id int64) (*entity.User, error) { + return s.repository.FindByID(ctx, id) +} + +// TanyaAI +// func (s *UserService) TanyaAI(ctx context.Context, pertanyaan string) (string, error) { +// // Implementasi logika untuk bertanya ke AI di sini +// // Misalnya, menggunakan paket go-openai atau alat pemrosesan bahasa alami lainnya +// // ... + +// // Sebagai contoh, kita akan menggunakan jawaban statis +// return "Jawaban dari AI untuk pertanyaan: " + pertanyaan, nil +// } + +//BuyerCreateAccount +// func (s *UserService) BuyerCreateAccount(ctx context.Context, user *entity.User) error { +// return s.repository.CreateUser(ctx, user) +// } diff --git a/output/Backend Ticketing.postman_collection.json b/output/Backend Ticketing.postman_collection.json new file mode 100644 index 0000000..582626f --- /dev/null +++ b/output/Backend Ticketing.postman_collection.json @@ -0,0 +1,5314 @@ +{ + "info": { + "_postman_id": "5d86b314-1544-438b-a2a6-5a7179ccb076", + "name": "Backend Ticketing", + "description": "Documentation API untuk program aplikasi pembayaran tiket menggunakan bahasa Go", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "26142588", + "_collection_link": "https://ticketing-mikti.postman.co/workspace/Ticketing-Workspace~1c7b0ec0-f35f-4273-b1d6-67507ad4c237/collection/30169354-5d86b314-1544-438b-a2a6-5a7179ccb076?action=share&source=collection_link&creator=26142588" + }, + "item": [ + { + "name": "Admin", + "item": [ + { + "name": "User", + "item": [ + { + "name": "Create User", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\" : \"Reza\",\r\n \"email\" : \"RezaHidayat@Gmail.com\",\r\n \"number\": \"082210811378\",\r\n \"roles\" : \"Admin\",\r\n \"password\" : \"Djarum76\",\r\n \"saldo\": 1000\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/users", + "host": [ + "{{URL}}" + ], + "path": [ + "users" + ] + }, + "description": "berikut merupakan endpoint untuk membuat data atau akun user." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\" : \"Reza\",\r\n \"email\" : \"RezaHidayat@Gmail.com\",\r\n \"number\": \"082210811378\",\r\n \"roles\" : \"Admin\",\r\n \"password\" : \"Djarum76\",\r\n \"saldo\": 1000\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{url}}/users", + "host": [ + "{{url}}" + ], + "path": [ + "users" + ] + } + }, + "status": "Created", + "code": 201, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 22:51:08 GMT" + }, + { + "key": "Content-Length", + "value": "28" + } + ], + "cookie": [], + "body": "\"User created successfully\"" + } + ] + }, + { + "name": "Get All User", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/users", + "host": [ + "{{URL}}" + ], + "path": [ + "users" + ] + }, + "description": "berikut merupakan endpoint untuk menampilkan seluruh data user" + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/users", + "host": [ + "{{url}}" + ], + "path": [ + "users" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 22:51:40 GMT" + }, + { + "key": "Content-Length", + "value": "980" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 2,\n \"name\": \"\",\n \"email\": \"Admin@gmail.com\",\n \"number\": \"00000000000\",\n \"roles\": \"Admin\",\n \"saldo\": 0,\n \"created_at\": \"2023-12-01T23:38:43.332476+07:00\",\n \"updated_at\": \"2023-12-01T23:38:43.332476+07:00\"\n },\n {\n \"id\": 1,\n \"name\": \"\",\n \"email\": \"Admin@gmail.com\",\n \"number\": \"00000000000\",\n \"roles\": \"Admin\",\n \"saldo\": 9999800,\n \"created_at\": \"2023-12-01T22:19:22.698594+07:00\",\n \"updated_at\": \"2023-12-01T23:40:34.288419+07:00\"\n },\n {\n \"id\": 3,\n \"name\": \"\",\n \"email\": \"Admin@gmail.com\",\n \"number\": \"00000000000\",\n \"roles\": \"Admin\",\n \"saldo\": 0,\n \"created_at\": \"2023-12-02T04:00:08.15209+07:00\",\n \"updated_at\": \"2023-12-02T04:00:08.15209+07:00\"\n },\n {\n \"id\": 4,\n \"name\": \"\",\n \"email\": \"Buyer@gmail.com\",\n \"number\": \"00000000000\",\n \"roles\": \"Buyer\",\n \"saldo\": 0,\n \"created_at\": \"2023-12-02T04:00:49.67267+07:00\",\n \"updated_at\": \"2023-12-02T04:00:49.67267+07:00\"\n },\n {\n \"id\": 5,\n \"name\": \"Reza\",\n \"email\": \"RezaHidayat@Gmail.com\",\n \"number\": \"082210811378\",\n \"roles\": \"Djarum76\",\n \"saldo\": 1000,\n \"created_at\": \"2023-12-02T05:51:08.376309+07:00\",\n \"updated_at\": \"2023-12-02T05:51:08.390398+07:00\"\n }\n ]\n}" + } + ] + }, + { + "name": "Get User By ID", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/users/1", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "1" + ] + }, + "description": "berikut merupakan endpoint untuk menampilkan data user berdasarkan ID yang ingin diinginkan." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/users/1", + "host": [ + "{{url}}" + ], + "path": [ + "users", + "1" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 22:52:21 GMT" + }, + { + "key": "Content-Length", + "value": "241" + } + ], + "cookie": [], + "body": "{\n \"data\": {\n \"created\": \"2023-12-01T22:19:22.698594+07:00\",\n \"email\": \"Admin@gmail.com\",\n \"id\": 1,\n \"name\": \"\",\n \"number\": \"00000000000\",\n \"password\": \"$2a$10$c8VMh9ruzUM.rEr5PJk1GOY75G0OXXtOo2VkLIfHgiM6FDLb96JEO\",\n \"updated\": \"2023-12-01T23:40:34.288419+07:00\"\n }\n}" + } + ] + }, + { + "name": "Edit User", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\": \"Ravita\",\r\n \"email\": \"Ravita@gmail.com\",\r\n \"number\": \"082210811377\",\r\n \"roles\": \"Buyer\",\r\n \"saldo\": 100\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/users/1", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "1" + ] + }, + "description": "berikut merupakan endpoint untuk mengupdate atau memperbarui data user berdasarkan ID user." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\": \"Ravita\",\r\n \"email\": \"Ravita@gmail.com\",\r\n \"number\": \"082210811377\",\r\n \"roles\": \"Buyer\",\r\n \"saldo\": 100\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{url}}/users/1", + "host": [ + "{{url}}" + ], + "path": [ + "users", + "1" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 22:54:02 GMT" + }, + { + "key": "Content-Length", + "value": "38" + } + ], + "cookie": [], + "body": "{\n \"success\": \"succesfully update user\"\n}" + } + ] + }, + { + "name": "Delete User", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "url": { + "raw": "{{URL}}/users/2", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "2" + ] + }, + "description": "berikut merupakan endpoint untuk menghapus secara permanen data user berdasarkan ID, ketika data atau akun tersebut sudah tidak terpakai." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "DELETE", + "header": [], + "url": { + "raw": "http://localhost:8080/api/v1/users/2", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "api", + "v1", + "users", + "2" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 22:54:27 GMT" + }, + { + "key": "Content-Length", + "value": "40" + } + ], + "cookie": [], + "body": "{\n \"message\": \"User deleted successfully\"\n}" + } + ] + }, + { + "name": "Logout Copy", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "url": { + "raw": "{{URL}}/user/logout", + "host": [ + "{{URL}}" + ], + "path": [ + "user", + "logout" + ] + } + }, + "response": [ + { + "name": "Logout", + "originalRequest": { + "method": "POST", + "header": [], + "url": { + "raw": "{{URL}}/user/logout", + "host": [ + "{{URL}}" + ], + "path": [ + "user", + "logout" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Mon, 04 Dec 2023 20:08:02 GMT" + }, + { + "key": "Content-Length", + "value": "38" + } + ], + "cookie": [], + "body": "{\n \"success\": \"successfully logged out\"\n}" + } + ] + } + ], + "description": "Folder ini berfungsi untuk membuat akun user, update data user, get all data user, get user by ID, dan delete user pada bagian atau sisi admin." + }, + { + "name": "Ticket", + "item": [ + { + "name": "Create Ticket", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"title\": \"coldplay\",\r\n \"description\": \"penyanyi luar negri\",\r\n \"image\": \"https/image\",\r\n \"location\": \"GBK\",\r\n \"date\": \"2023-12-31T18:00:01Z\", // Format tanggal dan waktu sesuai dengan RFC3339\r\n \"status\": \"available\",\r\n \"price\": 50.0,\r\n \"quota\": 100,\r\n \"terjual\": 5,\r\n \"category\": \"music\"\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/ticket", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket" + ] + }, + "description": "berikut merupakan endpoint untuk membuat data atau daftar tiket yang akan dijual oleh seorang admin." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"title\": \"coldplay\",\r\n \"description\": \"penyanyi luar negri\",\r\n \"image\": \"https/image\",\r\n \"location\": \"GBK\",\r\n \"date\": \"2023-12-31T18:00:01Z\", // Format tanggal dan waktu sesuai dengan RFC3339\r\n \"status\": \"available\",\r\n \"price\": 50.0,\r\n \"quota\": 100,\r\n \"terjual\": 5,\r\n \"category\": \"music\"\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/ticket", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket" + ] + } + }, + "status": "Created", + "code": 201, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 22:58:11 GMT" + }, + { + "key": "Content-Length", + "value": "30" + } + ], + "cookie": [], + "body": "\"Ticket created successfully\"" + } + ] + }, + { + "name": "Get All Ticket", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticketa", + "host": [ + "{{URL}}" + ], + "path": [ + "ticketa" + ] + }, + "description": "berikut merupakan endpoint untuk melihat secara keseluruhan daftar tiket yang ada pada program ini." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticketa", + "host": [ + "{{URL}}" + ], + "path": [ + "ticketa" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:01:27 GMT" + }, + { + "key": "Content-Length", + "value": "636" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay3\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 96,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 2,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay3\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Get Ticket By ID", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/1", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "1" + ] + }, + "description": "berikut merupakan endpoint untuk melihat daftar tiket yang ada berdasarkan ID tiket." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/1", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "1" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:01:43 GMT" + }, + { + "key": "Content-Length", + "value": "224" + } + ], + "cookie": [], + "body": "{\n \"data\": {\n \"category\": \"music\",\n \"created\": \"2023-12-01T22:20:09.060639Z\",\n \"date\": \"2023-12-31T00:00:00Z\",\n \"description\": \"penyanyi luar negri\",\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"price\": 50,\n \"quota\": 96,\n \"title\": \"coldplay3\"\n }\n}" + } + ] + }, + { + "name": "Update Ticket", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"title\": \"Dragon\",\r\n \"description\": \"konser lagu edm terbaik\",\r\n \"image\": \"https/image\",\r\n \"location\": \"bekasi\",\r\n \"date\": \"2023-12-31T18:00:00Z\", // Format tanggal dan waktu sesuai dengan RFC3339\r\n \"category\": \"music\",\r\n \"price\": 50.0,\r\n \"quota\": 200\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/ticket/1", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "1" + ] + }, + "description": "berikut merupakan endpoint untuk memperbarui data tiket yang ada berdasarkan ID tiket yang sudah terdaftar sebelumnya." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"title\": \"Dragon\",\r\n \"description\": \"konser lagu edm terbaik\",\r\n \"image\": \"https/image\",\r\n \"location\": \"bekasi\",\r\n \"date\": \"2023-12-31T18:00:00Z\", // Format tanggal dan waktu sesuai dengan RFC3339\r\n \"category\": \"music\",\r\n \"price\": 50.0,\r\n \"quota\": 200\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/ticket/1", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "1" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:03:34 GMT" + }, + { + "key": "Content-Length", + "value": "261" + } + ], + "cookie": [], + "body": "{\n \"data\": {\n \"category\": \"music\",\n \"date\": \"2023-12-31T18:00:00Z\",\n \"description\": \"konser lagu edm terbaik\",\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"price\": 50,\n \"quota\": 200,\n \"title\": \"Dragon\",\n \"update\": \"0001-01-01T00:00:00Z\"\n },\n \"message\": \"Ticket updated successfully\"\n}" + } + ] + }, + { + "name": "Delete Ticket", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "url": { + "raw": "{{URL}}/ticket/1", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "1" + ] + }, + "description": "berikut merupakan endpoint untuk menghapus data tiket yang ada pada daftar tiket, ketika data tiket tersebut sudah tidak digunakan kembali." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{URL}}/ticket/2", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "2" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:03:55 GMT" + }, + { + "key": "Content-Length", + "value": "42" + } + ], + "cookie": [], + "body": "{\n \"message\": \"Ticket deleted successfully\"\n}" + } + ] + } + ], + "description": "Folder ini berfungsi ketika seorang admin ingin membuat daftar tiket baru, melihat seluruh daftar tiket, melihat daftar tiket berdasrkan ID tiket yang diinginkan, memperbarui data tiket, dan menghapus data tiket yang sudah tidak digunakan." + }, + { + "name": "Blog", + "item": [ + { + "name": "Create Blog", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"title\": \"Kerusuhan DI Konser jakarta\",\r\n \"description\": \"beberapa orang menjadi tersangka\",\r\n \"image\": \"https/image\",\r\n \"date\": \"2023-12-31T18:00:00Z\"\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/blog", + "host": [ + "{{URL}}" + ], + "path": [ + "blog" + ] + }, + "description": "berikut merupakan endpoint untuk membuat blog baru mengenai event atau tiket yang sedang ada pada program tersebut." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"title\": \"Kerusuhan DI Konser jakarta\",\r\n \"description\": \"beberapa orang menjadi tersangka\",\r\n \"image\": \"https/image\",\r\n \"date\": \"2023-12-31T18:00:00Z\"\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/blog", + "host": [ + "{{URL}}" + ], + "path": [ + "blog" + ] + } + }, + "status": "Created", + "code": 201, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:06:46 GMT" + }, + { + "key": "Content-Length", + "value": "28" + } + ], + "cookie": [], + "body": "\"Blog created successfully\"" + } + ] + }, + { + "name": "Get All Blog", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/blog", + "host": [ + "{{URL}}" + ], + "path": [ + "blog" + ] + }, + "description": "berikut merupakan endpoint untuk menampilkan seluruh daftar blog yang sudah dibuat sebelumnya oleh admin." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/blog", + "host": [ + "{{URL}}" + ], + "path": [ + "blog" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:07:02 GMT" + }, + { + "key": "Content-Length", + "value": "742" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"ID\": 1,\n \"Image\": \"https/image\",\n \"Date\": \"2023-12-02T00:00:00Z\",\n \"Title\": \"Nama Tiket\",\n \"Description\": \"Deskripsi Tiket\",\n \"CreatedAt\": \"2023-12-02T04:13:07.807064Z\",\n \"UpdatedAt\": \"2023-12-02T04:13:07.807064Z\",\n \"DeletedAt\": \"0001-01-01T00:00:00Z\"\n },\n {\n \"ID\": 2,\n \"Image\": \"https/image\",\n \"Date\": \"2023-12-02T00:00:00Z\",\n \"Title\": \"Nama Tiket\",\n \"Description\": \"Deskripsi Tiket\",\n \"CreatedAt\": \"2023-12-02T06:05:44.914708Z\",\n \"UpdatedAt\": \"2023-12-02T06:05:44.914708Z\",\n \"DeletedAt\": \"0001-01-01T00:00:00Z\"\n },\n {\n \"ID\": 3,\n \"Image\": \"https/image\",\n \"Date\": \"2023-12-02T00:00:00Z\",\n \"Title\": \"Kerusuhan DI Konser jakarta\",\n \"Description\": \"beberapa orang menjadi tersangka\",\n \"CreatedAt\": \"2023-12-02T06:06:46.56004Z\",\n \"UpdatedAt\": \"2023-12-02T06:06:46.56004Z\",\n \"DeletedAt\": \"0001-01-01T00:00:00Z\"\n }\n ]\n}" + } + ] + }, + { + "name": "Update Blog", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"title\": \"Konser Tersukses\",\r\n \"description\": \"dikatakan oleh produser menjadi konser tersukses sepanjang masa\",\r\n \"image\": \"https/image\",\r\n \"date\": \"2023-12-31T18:00:00Z\"\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/blog/1", + "host": [ + "{{URL}}" + ], + "path": [ + "blog", + "1" + ] + }, + "description": "berikut merupakan endpoint untuk memperbarui data atau daftar blog yang sudah terdaftar berdasarkan ID blog." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"title\": \"Konser Tersukses\",\r\n \"description\": \"dikatakan oleh produser menjadi konser tersukses sepanjang masa\",\r\n \"image\": \"https/image\",\r\n \"date\": \"2023-12-31T18:00:00Z\"\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/blog/1", + "host": [ + "{{URL}}" + ], + "path": [ + "blog", + "1" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:07:55 GMT" + }, + { + "key": "Content-Length", + "value": "320" + } + ], + "cookie": [], + "body": "{\n \"Blog\": {\n \"ID\": 1,\n \"Image\": \"https/image\",\n \"Date\": \"2023-12-31T18:00:00Z\",\n \"Title\": \"Konser Tersukses\",\n \"Description\": \"dikatakan oleh produser menjadi konser tersukses sepanjang masa\",\n \"CreatedAt\": \"0001-01-01T00:00:00Z\",\n \"UpdatedAt\": \"0001-01-01T00:00:00Z\",\n \"DeletedAt\": \"0001-01-01T00:00:00Z\"\n },\n \"message\": \"Blog updated successfully\"\n}" + } + ] + }, + { + "name": "Delete Blog", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "url": { + "raw": "{{URL}}/blog/2", + "host": [ + "{{URL}}" + ], + "path": [ + "blog", + "2" + ] + }, + "description": "berikut merupakan endpoint untuk menghapus salah satu daftar blog yang sudah terdaftar." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{URL}}/blog/2", + "host": [ + "{{URL}}" + ], + "path": [ + "blog", + "2" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:08:57 GMT" + }, + { + "key": "Content-Length", + "value": "40" + } + ], + "cookie": [], + "body": "{\n \"message\": \"Blog deleted successfully\"\n}" + } + ] + }, + { + "name": "Search Blog", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/blog/search/Kerusuhan", + "host": [ + "{{URL}}" + ], + "path": [ + "blog", + "search", + "Kerusuhan" + ] + }, + "description": "berikut merupakan endpoint untuk melakukan pencarian blog berdasarkan keyword." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/blog/search/Kerusuhan", + "host": [ + "{{URL}}" + ], + "path": [ + "blog", + "search", + "Kerusuhan" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:08:32 GMT" + }, + { + "key": "Content-Length", + "value": "276" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"ID\": 3,\n \"Image\": \"https/image\",\n \"Date\": \"2023-12-02T00:00:00Z\",\n \"Title\": \"Kerusuhan DI Konser jakarta\",\n \"Description\": \"beberapa orang menjadi tersangka\",\n \"CreatedAt\": \"2023-12-02T06:06:46.56004Z\",\n \"UpdatedAt\": \"2023-12-02T06:06:46.56004Z\",\n \"DeletedAt\": \"0001-01-01T00:00:00Z\"\n }\n ]\n}" + } + ] + } + ], + "description": "Folder ini berfungsi ketika admin ingin membuat data blog, mengupdate atau memperbarui data blog, menghapus data blog, dan melakukan pencarian blog, serta menampilkan data blog secara keseluruhan yang sudah berhasil dibuat atau terdaftar." + }, + { + "name": "Order", + "item": [ + { + "name": "Create Order", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"ticket_id\": 3,\r\n \"quantity\": 2,\r\n \"user_id\": 10,\r\n \"status\": \"seleseai\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/order", + "host": [ + "{{URL}}" + ], + "path": [ + "order" + ] + }, + "description": "berikut merupakan endpoint ketika admin ingin membuat new order untuk customer yang tidak bisa order secara mandiri." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"ticket_id\": 1,\r\n \"quantity\": 2,\r\n \"user_id\": 5,\r\n \"status\": \"seleseai\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/order", + "host": [ + "{{URL}}" + ], + "path": [ + "order" + ] + } + }, + "status": "Created", + "code": 201, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:13:23 GMT" + }, + { + "key": "Content-Length", + "value": "29" + } + ], + "cookie": [], + "body": "\"Order created successfully\"" + } + ] + }, + { + "name": "Get All Order", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/order", + "host": [ + "{{URL}}" + ], + "path": [ + "order" + ] + }, + "description": "berikut merupakan endpoint untuk menampilkan seluruh data order yang sudah dibuat baik oleh admin maupun pembeli." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/order", + "host": [ + "{{URL}}" + ], + "path": [ + "order" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:14:57 GMT" + }, + { + "key": "Content-Length", + "value": "763" + } + ], + "cookie": [], + "body": "{\n \"message\": \"Get all orders success\",\n \"order_details\": [\n {\n \"ticket\": {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n \"user_id\": 1\n },\n {\n \"ticket\": {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n \"user_id\": 1\n },\n {\n \"ticket\": {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n \"user_id\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Get Order ID", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/order/1", + "host": [ + "{{URL}}" + ], + "path": [ + "order", + "1" + ] + }, + "description": "berikut merupakan endpoint untuk menampilkan data order bersadarkan ID yang sudah terdaftar pada database order." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/order/1", + "host": [ + "{{URL}}" + ], + "path": [ + "order", + "1" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:15:21 GMT" + }, + { + "key": "Content-Length", + "value": "763" + } + ], + "cookie": [], + "body": "{\n \"message\": \"Get all orders success\",\n \"order_details\": [\n {\n \"ticket\": {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n \"user_id\": 1\n },\n {\n \"ticket\": {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n \"user_id\": 1\n },\n {\n \"ticket\": {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n \"user_id\": 5\n }\n ]\n}" + } + ] + } + ], + "description": "Folder ini berfungsi ketika admin ingin membuat new order, menampilkan seluruh data order, dan menampilkan data order berdasarkan ID." + }, + { + "name": "Notification", + "item": [ + { + "name": "Create Notification", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"type\": \"Diskon Ticket\",\r\n \"message\": \"ada diskon loh cek berikut\",\r\n \"is_read\": false,\r\n \"create_at\": \"2023-11-30T12:34:56Z\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/notification", + "host": [ + "{{URL}}" + ], + "path": [ + "notification" + ] + }, + "description": "berikut merupakan endpoint ketika admin ingin membuat notifikasi." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"type\": \"Diskon Ticket\",\r\n \"message\": \"ada diskon loh cek berikut\",\r\n \"is_read\": false,\r\n \"create_at\": \"2023-11-30T12:34:56Z\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/notification", + "host": [ + "{{URL}}" + ], + "path": [ + "notification" + ] + } + }, + "status": "Created", + "code": 201, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:17:03 GMT" + }, + { + "key": "Content-Length", + "value": "220" + } + ], + "cookie": [], + "body": "{\n \"id\": 1,\n \"type\": \"Diskon Ticket\",\n \"message\": \"ada diskon loh cek berikut\",\n \"is_read\": false,\n \"created_at\": \"2023-12-02T06:17:03.6441118+07:00\",\n \"updated_at\": \"2023-12-02T06:17:03.647676+07:00\",\n \"deleted_at\": \"0001-01-01T00:00:00Z\"\n}" + } + ] + }, + { + "name": "Get Notification", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/notification", + "host": [ + "{{URL}}" + ], + "path": [ + "notification" + ] + }, + "description": "berikut merupakan endpoint yang digunakan untuk nampilkan notifikasi" + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/notification", + "host": [ + "{{URL}}" + ], + "path": [ + "notification" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:17:16 GMT" + }, + { + "key": "Content-Length", + "value": "220" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 1,\n \"type\": \"Diskon Ticket\",\n \"message\": \"ada diskon loh cek berikut\",\n \"is_read\": false,\n \"created_at\": \"2023-12-02T06:17:03.644111Z\",\n \"updated_at\": \"2023-12-02T06:17:03.647676Z\",\n \"deleted_at\": \"0001-01-01T00:00:00Z\"\n }\n ]\n}" + } + ] + } + ], + "description": "Folder ini berfungsi ketika admin ingin membuat suatu notifikasi yang nanti nya akan tampil di layar pembeli dan melihat notifikasi apa saja yang akan muncul." + }, + { + "name": "Topup Saldo", + "item": [ + { + "name": "Topup Saldo", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": \"topup123\",\r\n \"user_id\": 1,\r\n \"amount\": 10000,\r\n \"status\": 0\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/topup", + "host": [ + "{{URL}}" + ], + "path": [ + "topup" + ] + }, + "description": "berikut merupakan endpoint untuk menambahkan saldo pada suatu akun dari sisi admin." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": \"topup123\",\r\n \"user_id\": 1,\r\n \"amount\": 10000,\r\n \"status\": 0\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/topup", + "host": [ + "{{URL}}" + ], + "path": [ + "topup" + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 23:17:59 GMT" + }, + { + "key": "Content-Length", + "value": "415" + } + ], + "cookie": [], + "body": "{\n \"error\": \"Midtrans API is returning API error. HTTP status code: 401 API response: {\\\"status_code\\\":\\\"401\\\",\\\"status_message\\\":\\\"Unknown Merchant server_key/id\\\",\\\"id\\\":\\\"8cc092fc-f1eb-4d4e-9f03-f9b1b29c094d\\\"}: Midtrans API is returning API error. HTTP status code: 401 API response: {\\\"status_code\\\":\\\"401\\\",\\\"status_message\\\":\\\"Unknown Merchant server_key/id\\\",\\\"id\\\":\\\"8cc092fc-f1eb-4d4e-9f03-f9b1b29c094d\\\"}\"\n}" + } + ] + }, + { + "name": "History TopUP", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/transactions/history", + "host": [ + "{{URL}}" + ], + "path": [ + "transactions", + "history" + ] + } + }, + "response": [ + { + "name": "History TopUP", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/transactions/history", + "host": [ + "{{URL}}" + ], + "path": [ + "transactions", + "history" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Tue, 05 Dec 2023 17:07:13 GMT" + }, + { + "key": "Content-Length", + "value": "140" + } + ], + "cookie": [], + "body": "[\n {\n \"ID\": 1,\n \"OrderID\": \"topup\",\n \"UserID\": 1,\n \"Amount\": 10000,\n \"Status\": \"paid\"\n },\n {\n \"ID\": 2,\n \"OrderID\": \"topup\",\n \"UserID\": 1,\n \"Amount\": 50000,\n \"Status\": \"paid\"\n }\n]" + } + ] + }, + { + "name": "midtrans", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Admin}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n\t\"order_id\": \"topup\",\r\n\t\"amount\": 50000\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/transactions", + "host": [ + "{{URL}}" + ], + "path": [ + "transactions" + ] + }, + "description": "**INI GATAU**" + }, + "response": [ + { + "name": "midtrans", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n\t\"order_id\": \"topup\",\r\n\t\"amount\": 50000\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/transactions", + "host": [ + "{{URL}}" + ], + "path": [ + "transactions" + ] + } + }, + "status": "Created", + "code": 201, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Tue, 05 Dec 2023 16:29:17 GMT" + }, + { + "key": "Content-Length", + "value": "111" + } + ], + "cookie": [], + "body": "{\n \"url_pembayaran\": \"https://app.sandbox.midtrans.com/snap/v3/redirection/17f57236-6ef2-4deb-a044-e4724a5c291e\"\n}" + } + ] + } + ], + "description": "Folder ini berfungsi ketika admin ingin menambahkan saldo pada akun yang saldo nya ingin ditambahkan." + } + ], + "description": "Folder ini menyimpan API untuk fitur yang dapat diakses oleh admin. serta untuk mengakses atau menggunakan endpoint pada folder ini diharuskan memiliki token sesuai dengan role." + }, + { + "name": "User", + "item": [ + { + "name": "Profile", + "item": [ + { + "name": "Update Profile", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\": \"John Doe\",\r\n \"email\" : \"Buyer1@gmail.com\",\r\n \"number\": \"12345678901\",\r\n \"password\" : \"123123123123123123\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/users/profile", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "profile" + ] + }, + "description": "berikut merupakan endpoint untuk melakukan update profile dari sisi pembeli." + }, + "response": [ + { + "name": "Update Profile", + "originalRequest": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\": \"John Doe\",\r\n \"email\" : \"Buyer1@gmail.com\",\r\n \"number\": \"12345678901\",\r\n \"password\" : \"123123123123123123\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/users/profile", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "profile" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 21:43:48 GMT" + }, + { + "key": "Content-Length", + "value": "39" + } + ], + "cookie": [], + "body": "{\n \"success\": \"successfully update user\"\n}" + } + ] + }, + { + "name": "Delete Profile", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": 9\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/users/deleteprofile", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "deleteprofile" + ] + }, + "description": "berikut merupakan endpoint untuk menghapus profile pembeli." + }, + "response": [ + { + "name": "Delete Profile", + "originalRequest": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": 9\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/users/deleteprofile", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "deleteprofile" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 21:54:29 GMT" + }, + { + "key": "Content-Length", + "value": "40" + } + ], + "cookie": [], + "body": "{\n \"message\": \"User deleted successfully\"\n}" + } + ] + }, + { + "name": "Get Saldo", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/users/balance", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "balance" + ] + }, + "description": "endpoint berikut ini digunakan untuk melihat saldi yang dimiliki oleh pembeli." + }, + "response": [ + { + "name": "Get Saldo", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/users/balance", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "balance" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 21:49:43 GMT" + }, + { + "key": "Content-Length", + "value": "7" + } + ], + "cookie": [], + "body": "100000" + } + ] + }, + { + "name": "Get Profile", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/users/profile", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "profile" + ] + }, + "description": "endpoint berikut ini digunakan untuk melihat profile" + }, + "response": [ + { + "name": "Get Profile", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/users/profile", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "profile" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 21:36:00 GMT" + }, + { + "key": "Content-Length", + "value": "191" + } + ], + "cookie": [], + "body": "{\n \"id\": 9,\n \"name\": \"\",\n \"email\": \"Buyer1@gmail.com\",\n \"number\": \"00000000000\",\n \"roles\": \"Buyer\",\n \"saldo\": 0,\n \"created_at\": \"2023-12-03T01:29:58.477828+07:00\",\n \"updated_at\": \"2023-12-03T01:29:58.477828+07:00\"\n}" + } + ] + }, + { + "name": "Logout", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "url": { + "raw": "{{URL}}/user/logout", + "host": [ + "{{URL}}" + ], + "path": [ + "user", + "logout" + ] + }, + "description": "endpoint diatas digunakan untuk melakukan logout akun." + }, + "response": [ + { + "name": "Logout", + "originalRequest": { + "method": "POST", + "header": [], + "url": { + "raw": "{{URL}}/user/logout", + "host": [ + "{{URL}}" + ], + "path": [ + "user", + "logout" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Mon, 04 Dec 2023 20:08:02 GMT" + }, + { + "key": "Content-Length", + "value": "38" + } + ], + "cookie": [], + "body": "{\n \"success\": \"successfully logged out\"\n}" + } + ] + } + ], + "description": "Folder ini berfungsi ketika pembeli ingin melakukan update, delete, melihat saldo, dan melihat profile mereka." + }, + { + "name": "Ticket", + "item": [ + { + "name": "Get Ticket", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket" + ] + }, + "description": "endpoint diatas digunakan untuk melihat tiket secara keseluruhan tanpa adanya penyortiran." + }, + "response": [ + { + "name": "Get Ticket", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:35:09 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Get Ticket ID", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/1", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "1" + ] + }, + "description": "endpoint ini berfungsi untuk melihat tiket berdasarkan ID" + }, + "response": [ + { + "name": "Get Ticket ID", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/1", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "1" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:35:20 GMT" + }, + { + "key": "Content-Length", + "value": "229" + } + ], + "cookie": [], + "body": "{\n \"data\": {\n \"category\": \"music\",\n \"created\": \"2023-12-01T22:20:09.060639Z\",\n \"date\": \"2023-12-31T00:00:00Z\",\n \"description\": \"konser lagu edm terbaik\",\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"price\": 50,\n \"quota\": 198,\n \"title\": \"Dragon\"\n }\n}" + } + ] + }, + { + "name": "Search", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/search/Nama", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "search", + "Nama" + ] + }, + "description": "endpoing ini digunakan ketika pembeli ingin melakukan pecarian di button search." + }, + "response": [ + { + "name": "Search", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/search/cold", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "search", + "cold" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:35:42 GMT" + }, + { + "key": "Content-Length", + "value": "219" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "FIlter Location", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/location/GBK", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "location", + "GBK" + ] + }, + "description": "endpoint ini berfungsi untuk menampilkan data tiket atau event yang sedang berlangsung berdasarkan lokasi." + }, + "response": [ + { + "name": "FIlter Location", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/location/GBK", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "location", + "GBK" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:35:58 GMT" + }, + { + "key": "Content-Length", + "value": "219" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Filter Category", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/category/music", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "category", + "music" + ] + }, + "description": "endpoint diatas gunakan untuk menampikan data tiket berdasarkan category tiket yang tersedia." + }, + "response": [ + { + "name": "Filter Category", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/category/music", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "category", + "music" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:36:07 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Filter Time", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/range/:start/:end", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "range", + ":start", + ":end" + ], + "variable": [ + { + "key": "start", + "value": "2023-12-31" + }, + { + "key": "end", + "value": "2024-12-31" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampilkan data tiket yang tersedia berdasarka waktu." + }, + "response": [ + { + "name": "Filter Time", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/range/:start/:end", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "range", + ":start", + ":end" + ], + "variable": [ + { + "key": "start", + "value": "2023-12-31" + }, + { + "key": "end", + "value": "2024-12-31" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:36:50 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Filter Price", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/price/:min/:max", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "price", + ":min", + ":max" + ], + "variable": [ + { + "key": "min", + "value": "0" + }, + { + "key": "max", + "value": "100" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampilkan range harga tiket yang tersedia." + }, + "response": [ + { + "name": "Filter Price", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/price/:min/:max", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "price", + ":min", + ":max" + ], + "variable": [ + { + "key": "min", + "value": "0" + }, + { + "key": "max", + "value": "100" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:37:03 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Sort Cheap", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/cheapest?sort=termurah", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "cheapest" + ], + "query": [ + { + "key": "sort", + "value": "termurah" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampilkan data tiket mulai dari yang termurah." + }, + "response": [ + { + "name": "Sort Cheap", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/ticket/cheapest?sort=termurah", + "host": [ + "{{url}}" + ], + "path": [ + "ticket", + "cheapest" + ], + "query": [ + { + "key": "sort", + "value": "termurah" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:37:14 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Sort Bestseller", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/most-bought?sort=terbanyak", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "most-bought" + ], + "query": [ + { + "key": "sort", + "value": "terbanyak" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampilkan data tiket dengan pembeli terbanyak atau terfavorit." + }, + "response": [ + { + "name": "Sort Bestseller", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/ticket/most-bought?sort=terbanyak", + "host": [ + "{{url}}" + ], + "path": [ + "ticket", + "most-bought" + ], + "query": [ + { + "key": "sort", + "value": "terbanyak" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:37:21 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Sort Expenive", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/ticket/most-expensive?sort=termahal", + "host": [ + "{{url}}" + ], + "path": [ + "ticket", + "most-expensive" + ], + "query": [ + { + "key": "sort", + "value": "termahal" + } + ] + }, + "description": "endpoint ini berfungsi untuk melihat daftar tiket dengan harga termahal." + }, + "response": [ + { + "name": "Sort Expenive", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/most-expensive?sort=termahal", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "most-expensive" + ], + "query": [ + { + "key": "sort", + "value": "termahal" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:37:27 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Sort Lastest", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/terbaru?sort=terbaru", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "terbaru" + ], + "query": [ + { + "key": "sort", + "value": "terbaru" + } + ] + }, + "description": "endpoint diatas digunakan untuk menampilkan data tiket yang paling baru atau terbaru." + }, + "response": [ + { + "name": "Sort Lastest", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/terbaru?sort=terbaru", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "terbaru" + ], + "query": [ + { + "key": "sort", + "value": "terbaru" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:37:49 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Sort Avaliable", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/available?sort=tersedia", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "available" + ], + "query": [ + { + "key": "sort", + "value": "tersedia" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampilkan data tiket yang masih tersedia." + }, + "response": [ + { + "name": "Sort Avaliable", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/available?sort=tersedia", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "available" + ], + "query": [ + { + "key": "sort", + "value": "tersedia" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:38:00 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + } + ], + "description": "Folder ini berfungsi ketika pembeli ingin melihat daftar tiket secara keseluruhan, berdasarkan filter dan sortir mulai dari berdasarkan lokasi, kategori, waktu, harga, terfavorit, termurah, termahal, tersedia, dsb." + }, + { + "name": "Blog", + "item": [ + { + "name": "Get All Blog", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/blog", + "host": [ + "{{URL}}" + ], + "path": [ + "blog" + ] + }, + "description": "endpoin ini berfungsi untuk menampikan seluruh blog yang tersedia pada aplikasi ini." + }, + "response": [ + { + "name": "Get All Blog", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/blog", + "host": [ + "{{URL}}" + ], + "path": [ + "blog" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:38:18 GMT" + }, + { + "key": "Content-Length", + "value": "563" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"ID\": 3,\n \"Image\": \"https/image\",\n \"Date\": \"2023-12-02T00:00:00Z\",\n \"Title\": \"Kerusuhan DI Konser jakarta\",\n \"Description\": \"beberapa orang menjadi tersangka\",\n \"CreatedAt\": \"2023-12-02T06:06:46.56004Z\",\n \"UpdatedAt\": \"2023-12-02T06:06:46.56004Z\",\n \"DeletedAt\": \"0001-01-01T00:00:00Z\"\n },\n {\n \"ID\": 1,\n \"Image\": \"https/image\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"Title\": \"Konser Tersukses\",\n \"Description\": \"dikatakan oleh produser menjadi konser tersukses sepanjang masa\",\n \"CreatedAt\": \"2023-12-02T04:13:07.807064Z\",\n \"UpdatedAt\": \"2023-12-02T06:07:55.103818Z\",\n \"DeletedAt\": \"0001-01-01T00:00:00Z\"\n }\n ]\n}" + } + ] + }, + { + "name": "Get Blog By ID", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/blog/1", + "host": [ + "{{URL}}" + ], + "path": [ + "blog", + "1" + ] + }, + "description": "endpoint ini berfungsi untuk melihat blog berdasarkan ID" + }, + "response": [ + { + "name": "Get Blog By ID", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/blog/1", + "host": [ + "{{URL}}" + ], + "path": [ + "blog", + "1" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:38:35 GMT" + }, + { + "key": "Content-Length", + "value": "217" + } + ], + "cookie": [], + "body": "{\n \"data\": {\n \"created\": \"2023-12-02T04:13:07.807064Z\",\n \"date\": \"2023-12-31T00:00:00Z\",\n \"description\": \"dikatakan oleh produser menjadi konser tersukses sepanjang masa\",\n \"id\": 1,\n \"image\": \"https/image\",\n \"title\": \"Konser Tersukses\"\n }\n}" + } + ] + }, + { + "name": "Search Blog", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/blog/search/Kons", + "host": [ + "{{URL}}" + ], + "path": [ + "blog", + "search", + "Kons" + ] + }, + "description": "endpoint ini berfungsi ketika pembeli ingin melakukan pencarian pada button search menggunkan sebuah keyword." + }, + "response": [ + { + "name": "Search Blog", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/blog/search/Kons", + "host": [ + "{{url}}" + ], + "path": [ + "blog", + "search", + "Kons" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:39:08 GMT" + }, + { + "key": "Content-Length", + "value": "563" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"ID\": 3,\n \"Image\": \"https/image\",\n \"Date\": \"2023-12-02T00:00:00Z\",\n \"Title\": \"Kerusuhan DI Konser jakarta\",\n \"Description\": \"beberapa orang menjadi tersangka\",\n \"CreatedAt\": \"2023-12-02T06:06:46.56004Z\",\n \"UpdatedAt\": \"2023-12-02T06:06:46.56004Z\",\n \"DeletedAt\": \"0001-01-01T00:00:00Z\"\n },\n {\n \"ID\": 1,\n \"Image\": \"https/image\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"Title\": \"Konser Tersukses\",\n \"Description\": \"dikatakan oleh produser menjadi konser tersukses sepanjang masa\",\n \"CreatedAt\": \"2023-12-02T04:13:07.807064Z\",\n \"UpdatedAt\": \"2023-12-02T06:07:55.103818Z\",\n \"DeletedAt\": \"0001-01-01T00:00:00Z\"\n }\n ]\n}" + } + ] + } + ], + "description": "Folder ini digunakan ketika pembeli ingin melihat blog atau mencari blog berdasarkan keyword." + }, + { + "name": "Order", + "item": [ + { + "name": "Create Order", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"ticket_id\": 1,\r\n \"quantity\": 2,\r\n \"status\": \"seleseai\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}user/order", + "host": [ + "{{URL}}user" + ], + "path": [ + "order" + ] + }, + "description": "endpoint ini berfungsi untuk membuat new order oleh pembeli." + }, + "response": [ + { + "name": "Create Order", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"ticket_id\": 1,\r\n \"quantity\": 2,\r\n \"status\": \"seleseai\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}user/order", + "host": [ + "{{URL}}user" + ], + "path": [ + "order" + ] + } + }, + "status": "Created", + "code": 201, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sun, 03 Dec 2023 19:48:47 GMT" + }, + { + "key": "Content-Length", + "value": "29" + } + ], + "cookie": [], + "body": "\"Order created successfully\"" + } + ] + }, + { + "name": "Get History Order", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}user/order", + "host": [ + "{{URL}}user" + ], + "path": [ + "order" + ] + }, + "description": "endpoint ini berfungsi untuk melihat history order yang sudah pernah dilakukan." + }, + "response": [ + { + "name": "Get History Order", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}user/order", + "host": [ + "{{URL}}user" + ], + "path": [ + "order" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sun, 03 Dec 2023 19:48:32 GMT" + }, + { + "key": "Content-Length", + "value": "529" + } + ], + "cookie": [], + "body": "{\n \"message\": \"Get all orders success\",\n \"order_details\": [\n {\n \"ticket\": {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 194,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n \"user_id\": 10\n },\n {\n \"ticket\": {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 194,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n \"user_id\": 10\n }\n ]\n}" + } + ] + } + ], + "description": "Folder ini berfungsi ketika pembeli membuat new order, dan melihat history order" + }, + { + "name": "Notification", + "item": [ + { + "name": "User Get Notification", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}user/notification", + "host": [ + "{{URL}}user" + ], + "path": [ + "notification" + ] + }, + "description": "endpoint ini berfungsi untuk menampilkan notifikasi." + }, + "response": [ + { + "name": "User Get Notification", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}user/notification", + "host": [ + "{{URL}}user" + ], + "path": [ + "notification" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sun, 03 Dec 2023 19:57:58 GMT" + }, + { + "key": "Content-Length", + "value": "220" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 1,\n \"type\": \"Diskon Ticket\",\n \"message\": \"ada diskon loh cek berikut\",\n \"is_read\": false,\n \"created_at\": \"2023-12-02T06:17:03.644111Z\",\n \"updated_at\": \"2023-12-04T02:57:43.787024Z\",\n \"deleted_at\": \"0001-01-01T00:00:00Z\"\n }\n ]\n}" + } + ] + } + ], + "description": "Folder ini digunakan ketika untuk menampilkan notifikasi dilayar pembeli." + }, + { + "name": "Topup Saldo", + "item": [ + { + "name": "Manual Topup", + "item": [ + { + "name": "Topup Saldo", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"id\": \"topup123\",\r\n \"user_id\": 1,\r\n \"amount\": 10000,\r\n \"status\": 0\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{url}}/topup", + "host": [ + "{{url}}" + ], + "path": [ + "topup" + ] + }, + "description": "endpoin ini digunakan ketika pembeli ingin melakukan top up saldo." + }, + "response": [] + }, + { + "name": "User Topup", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"amount\": 1000 \r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/user/topup", + "host": [ + "{{URL}}" + ], + "path": [ + "user", + "topup" + ] + }, + "description": "**INI BELUM TAU FUNGSI NYA.**" + }, + "response": [] + }, + { + "name": "Topup Saldo", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"saldo\" : 1000\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/user/upgrade", + "host": [ + "{{URL}}" + ], + "path": [ + "user", + "upgrade" + ] + }, + "description": "endpoint ini berfungsi untuk melakukan upgrade pada saldo yang dimiliki oleh pembeli." + }, + "response": [ + { + "name": "topup sederhana", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"saldo\" : 1000\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/user/upgrade", + "host": [ + "{{URL}}" + ], + "path": [ + "user", + "upgrade" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Mon, 04 Dec 2023 19:50:33 GMT" + }, + { + "key": "Content-Length", + "value": "46" + } + ], + "cookie": [], + "body": "{\n \"success\": \"successfully updated user saldo\"\n}" + } + ] + } + ] + }, + { + "name": "Cek Saldo", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/users/balance", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "balance" + ] + }, + "description": "endpoint ini berfungsi untuk melihat jumlah saldo yang dimiliki." + }, + "response": [ + { + "name": "Get Saldo", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/users/balance", + "host": [ + "{{URL}}" + ], + "path": [ + "users", + "balance" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 21:49:43 GMT" + }, + { + "key": "Content-Length", + "value": "7" + } + ], + "cookie": [], + "body": "100000" + } + ] + }, + { + "name": "midtrans", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n\t\"order_id\": \"topup\",\r\n\t\"amount\": 50000\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/transactions", + "host": [ + "{{URL}}" + ], + "path": [ + "transactions" + ] + }, + "description": "**INI GATAU**" + }, + "response": [ + { + "name": "midtrans", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n\t\"order_id\": \"topup\",\r\n\t\"amount\": 50000\r\n}\r\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/transactions", + "host": [ + "{{URL}}" + ], + "path": [ + "transactions" + ] + } + }, + "status": "Created", + "code": 201, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Tue, 05 Dec 2023 16:29:17 GMT" + }, + { + "key": "Content-Length", + "value": "111" + } + ], + "cookie": [], + "body": "{\n \"url_pembayaran\": \"https://app.sandbox.midtrans.com/snap/v3/redirection/17f57236-6ef2-4deb-a044-e4724a5c291e\"\n}" + } + ] + }, + { + "name": "History TopUP", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{Token-Buyer}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/transactions/history", + "host": [ + "{{URL}}" + ], + "path": [ + "transactions", + "history" + ] + } + }, + "response": [ + { + "name": "History TopUP", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/transactions/history", + "host": [ + "{{URL}}" + ], + "path": [ + "transactions", + "history" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Tue, 05 Dec 2023 17:07:13 GMT" + }, + { + "key": "Content-Length", + "value": "140" + } + ], + "cookie": [], + "body": "[\n {\n \"ID\": 1,\n \"OrderID\": \"topup\",\n \"UserID\": 1,\n \"Amount\": 10000,\n \"Status\": \"paid\"\n },\n {\n \"ID\": 2,\n \"OrderID\": \"topup\",\n \"UserID\": 1,\n \"Amount\": 50000,\n \"Status\": \"paid\"\n }\n]" + } + ] + } + ], + "description": "Folder ini digunakan ketika pembeli ingin melakukan top up, cek saldo, dsb." + } + ], + "description": "Folder ini menyimpan API untuk fitur yang dapat diakses oleh pembeli. serta untuk mengakses atau menggunakan endpoint pada folder ini diharuskan memiliki token sesuai dengan role sebagai buyer/pembeli." + }, + { + "name": "Public", + "item": [ + { + "name": "Ticket", + "item": [ + { + "name": "Search Ticket", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/search/col", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "search", + "col" + ] + }, + "description": "endpoint ini digunakan untuk melakukan pencarian pada button search menggunakan suaty keyword." + }, + "response": [ + { + "name": "Search Ticket", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/search/col", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "search", + "col" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:52:52 GMT" + }, + { + "key": "Content-Length", + "value": "219" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Filter Location", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/location/GBK", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "location", + "GBK" + ] + }, + "description": "endpoint ini untuk menampilkan data berdaskan lokasi event." + }, + "response": [ + { + "name": "Filter Location", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/location/GBK", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "location", + "GBK" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:53:09 GMT" + }, + { + "key": "Content-Length", + "value": "219" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Fileter Category", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/category/music", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "category", + "music" + ] + }, + "description": "endpoint ini berfungsi untuk menampilkan data berdasarkan kategori tiket atau event." + }, + "response": [ + { + "name": "Fileter Category", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/category/music", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "category", + "music" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:53:26 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Filter Time", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/range/:start/:end", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "range", + ":start", + ":end" + ], + "variable": [ + { + "key": "start", + "value": "2023-12-31" + }, + { + "key": "end", + "value": "2024-12-31" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampilkan seluruh data tiket atau event berdasarkan waktu." + }, + "response": [ + { + "name": "Filter Time", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/range/:start/:end", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "range", + ":start", + ":end" + ], + "variable": [ + { + "key": "start", + "value": "2023-12-31" + }, + { + "key": "end", + "value": "2024-12-31" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:54:03 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Filter Price", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/price/:min/:max", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "price", + ":min", + ":max" + ], + "variable": [ + { + "key": "min", + "value": "0" + }, + { + "key": "max", + "value": "100" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampilkan data tiket berdasarkan range harga yang tersedia." + }, + "response": [ + { + "name": "Filter Price", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/price/:min/:max", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "price", + ":min", + ":max" + ], + "variable": [ + { + "key": "min", + "value": "0" + }, + { + "key": "max", + "value": "100" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:54:20 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Sort Cheap", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/cheapest?sort=termurah", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "cheapest" + ], + "query": [ + { + "key": "sort", + "value": "termurah" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampilkan data tiket berdasarkan harga termurah." + }, + "response": [ + { + "name": "Sort Cheap", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/cheapest?sort=termurah", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "cheapest" + ], + "query": [ + { + "key": "sort", + "value": "termurah" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:54:34 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Sort Bestseller", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/most-bought?sort=terbanyak", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "most-bought" + ], + "query": [ + { + "key": "sort", + "value": "terbanyak" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampikan data tiket berdasarkan terfavorit atau terbnyak di beli." + }, + "response": [ + { + "name": "Sort Bestseller", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/most-bought?sort=terbanyak", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "most-bought" + ], + "query": [ + { + "key": "sort", + "value": "terbanyak" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:54:47 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Sort Expensive", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/most-expensive?sort=termahal", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "most-expensive" + ], + "query": [ + { + "key": "sort", + "value": "termahal" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampikan data tiket berdasarkan harga termahal." + }, + "response": [ + { + "name": "Sort Expensive", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/most-expensive?sort=termahal", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "most-expensive" + ], + "query": [ + { + "key": "sort", + "value": "termahal" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:55:02 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Sort Latest", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/terbaru?sort=terbaru", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "terbaru" + ], + "query": [ + { + "key": "sort", + "value": "terbaru" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampikan data tiket berdasarkan terbaru" + }, + "response": [ + { + "name": "Sort Latest", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/terbaru?sort=terbaru", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "terbaru" + ], + "query": [ + { + "key": "sort", + "value": "terbaru" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:55:15 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + }, + { + "name": "Sort Available", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/available?sort=tersedia", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "available" + ], + "query": [ + { + "key": "sort", + "value": "tersedia" + } + ] + }, + "description": "endpoint ini berfungsi untuk menampikan data tiket yang masih tersedia." + }, + "response": [ + { + "name": "Sort Available", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/ticket/available?sort=tersedia", + "host": [ + "{{URL}}" + ], + "path": [ + "ticket", + "available" + ], + "query": [ + { + "key": "sort", + "value": "tersedia" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:55:31 GMT" + }, + { + "key": "Content-Length", + "value": "432" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 3,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"bekasi\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"Dragon\",\n \"description\": \"konser lagu edm terbaik\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 198,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + } + ], + "description": "Folder ini berisikan endpoint yang dapat digunakan untuk melakukan search, filter, dan sortir," + }, + { + "name": "Blog", + "item": [ + { + "name": "Search Blog", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/blog/search/Ker", + "host": [ + "{{URL}}" + ], + "path": [ + "blog", + "search", + "Ker" + ] + }, + "description": "endpoint untuk melakukan pencarian di button search berdasarkan keyword." + }, + "response": [ + { + "name": "Search Blog", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/blog/search/Ker", + "host": [ + "{{URL}}" + ], + "path": [ + "blog", + "search", + "Ker" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Sat, 02 Dec 2023 22:56:35 GMT" + }, + { + "key": "Content-Length", + "value": "276" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"ID\": 3,\n \"Image\": \"https/image\",\n \"Date\": \"2023-12-02T00:00:00Z\",\n \"Title\": \"Kerusuhan DI Konser jakarta\",\n \"Description\": \"beberapa orang menjadi tersangka\",\n \"CreatedAt\": \"2023-12-02T06:06:46.56004Z\",\n \"UpdatedAt\": \"2023-12-02T06:06:46.56004Z\",\n \"DeletedAt\": \"0001-01-01T00:00:00Z\"\n }\n ]\n}" + } + ] + } + ], + "description": "Folder ini berfungsi untuk menyimpan endpoint get atau menampilkan data mengenai blog yang ada pada aplikasi tersebut." + }, + { + "name": "Admin_Login", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"email\" : \"Admin@gmail.com\",\r\n \"password\" : \"123123123123123123\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/login", + "host": [ + "{{URL}}" + ], + "path": [ + "login" + ] + }, + "description": "endpoint ini digunakan admin untuk melakukan login atau sign in." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"email\" : \"Admin@gmail.com\",\r\n \"password\" : \"123123123123123123\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/login", + "host": [ + "{{URL}}" + ], + "path": [ + "login" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 21:00:34 GMT" + }, + { + "key": "Content-Length", + "value": "203" + } + ], + "cookie": [], + "body": "{\n \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibmFtZSI6IiIsImVtYWlsIjoiQWRtaW5AZ21haWwuY29tIiwicm9sZSI6IkFkbWluIiwiZXhwIjoxNzAxNDY1MDM0fQ.vnFmFEyiIVKQRYM8f3hv38hWM9CXbDq_2PUe4Kfsvw8\"\n}" + } + ] + }, + { + "name": "Admin_Register", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\" : \"Admin\",\r\n \"email\" : \"Admin@gmail.com\",\r\n \"roles\" : \"Admin\",\r\n \"number\" : \"00000000000\",\r\n \"password\" : \"123123123123123123\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/register", + "host": [ + "{{URL}}" + ], + "path": [ + "register" + ] + }, + "description": "endpoint ini digunakan untuk melakukan register atau sign up oleh admin." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\" : \"Admin\",\r\n \"email\" : \"Admin@gmail.com\",\r\n \"roles\" : \"Admin\",\r\n \"number\" : \"00000000000\",\r\n \"password\" : \"123123123123123123\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/register", + "host": [ + "{{URL}}" + ], + "path": [ + "register" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 21:00:08 GMT" + }, + { + "key": "Content-Length", + "value": "246" + } + ], + "cookie": [], + "body": "{\n \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MywibmFtZSI6IiIsImVtYWlsIjoiQWRtaW5AZ21haWwuY29tIiwicm9sZSI6IkFkbWluIiwiZXhwIjoxNzAxNDY1MDA4fQ.lXziH4jyjrRA4kVv6URMIVRNJRvmQCQ0yxR7j-2sOuk\",\n \"message\": \"User registration successfully\"\n}" + } + ] + }, + { + "name": "Buyer_Register", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\": \"Buyer\",\r\n \"email\": \"Buyer1@gmail.com\",\r\n \"number\": \"12345678901\",\r\n \"password\": \"123123123123123123\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/register", + "host": [ + "{{URL}}" + ], + "path": [ + "register" + ] + }, + "description": "endpoint ini digunakan untuk melakukan register atau sign up oleh oleh pembeli" + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\" : \"Buyer\",\r\n \"email\" : \"Buyer@gmail.com\",\r\n \"roles\" : \"Buyer\",\r\n \"number\" : \"00000000000\",\r\n \"password\" : \"123123123123123123\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/register", + "host": [ + "{{URL}}" + ], + "path": [ + "register" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 21:00:49 GMT" + }, + { + "key": "Content-Length", + "value": "246" + } + ], + "cookie": [], + "body": "{\n \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NCwibmFtZSI6IiIsImVtYWlsIjoiQnV5ZXJAZ21haWwuY29tIiwicm9sZSI6IkJ1eWVyIiwiZXhwIjoxNzAxNDY1MDQ5fQ.JDFd9WwL6kkwY5N8gkVjFCRQiRguwknrjHOW1EkD24E\",\n \"message\": \"User registration successfully\"\n}" + } + ] + }, + { + "name": "Buyer_Login", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"email\" : \"Buyer1@gmail.com\",\r\n \"password\" : \"123123123123123123\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/login", + "host": [ + "{{URL}}" + ], + "path": [ + "login" + ] + }, + "description": "endpoint ini digunakan untuk melakukan login atau sign in oleh pembeli." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"email\" : \"Buyer@gmail.com\",\r\n \"password\" : \"123123123123123123\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{URL}}/login", + "host": [ + "{{URL}}" + ], + "path": [ + "login" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 21:01:09 GMT" + }, + { + "key": "Content-Length", + "value": "203" + } + ], + "cookie": [], + "body": "{\n \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NCwibmFtZSI6IiIsImVtYWlsIjoiQnV5ZXJAZ21haWwuY29tIiwicm9sZSI6IkJ1eWVyIiwiZXhwIjoxNzAxNDY1MDY5fQ.snjN9_7b9EnViKmDNyDAw5oO4290DLCAF1IowwOXOaA\"\n}" + } + ] + }, + { + "name": "Blog", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/public/blog", + "host": [ + "{{URL}}" + ], + "path": [ + "public", + "blog" + ] + }, + "description": "endpoint ini digunakn untuk menampilkan seluruh blog yang tersedia pada bagian homepage" + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/public/blog", + "host": [ + "{{URL}}" + ], + "path": [ + "public", + "blog" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 21:18:24 GMT" + }, + { + "key": "Content-Length", + "value": "244" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"ID\": 1,\n \"Image\": \"https/image\",\n \"Date\": \"2023-12-02T00:00:00Z\",\n \"Title\": \"Nama Tiket\",\n \"Description\": \"Deskripsi Tiket\",\n \"CreatedAt\": \"2023-12-02T04:13:07.807064Z\",\n \"UpdatedAt\": \"2023-12-02T04:13:07.807064Z\",\n \"DeletedAt\": \"0001-01-01T00:00:00Z\"\n }\n ]\n}" + } + ] + }, + { + "name": "Ticket", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/public/ticket", + "host": [ + "{{URL}}" + ], + "path": [ + "public", + "ticket" + ] + }, + "description": "endpoint ini digunakan untuk menampilkan seluruh data tiket pada bagian homepage." + }, + "response": [ + { + "name": "Example", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{URL}}/public/ticket", + "host": [ + "{{URL}}" + ], + "path": [ + "public", + "ticket" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json; charset=UTF-8" + }, + { + "key": "Vary", + "value": "Origin" + }, + { + "key": "Date", + "value": "Fri, 01 Dec 2023 21:18:03 GMT" + }, + { + "key": "Content-Length", + "value": "428" + } + ], + "cookie": [], + "body": "{\n \"data\": [\n {\n \"id\": 1,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay3\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 96,\n \"category\": \"music\",\n \"Terjual\": 5\n },\n {\n \"id\": 2,\n \"image\": \"https/image\",\n \"location\": \"GBK\",\n \"Date\": \"2023-12-31T00:00:00Z\",\n \"title\": \"coldplay3\",\n \"description\": \"penyanyi luar negri\",\n \"price\": 50,\n \"Status\": \"available\",\n \"Quota\": 100,\n \"category\": \"music\",\n \"Terjual\": 5\n }\n ]\n}" + } + ] + } + ], + "description": "Folder ini digunakan untuk memanpilkan seluruh data atau homepage yang berisikan tiket, blog, sign in , dan sign up." + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "URL", + "value": " http://localhost:8080/api/v1" + }, + { + "key": "Token-Admin", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibmFtZSI6IiIsImVtYWlsIjoiQWRtaW5AZ21haWwuY29tIiwicm9sZSI6IkFkbWluIiwiZXhwIjoxNzAxNDcxNTAzfQ.7njlsyp_kFCSby44lBnzcEDjze3OZoKj5Ow4EcooCMs" + }, + { + "key": "Token-Buyer", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NywibmFtZSI6IiIsImVtYWlsIjoiQnV5ZXIxQGdtYWlsLmNvbSIsInJvbGUiOiJCdXllciIsImV4cCI6MTcwMTU0MjA2OH0.6vamjQugLq5kOXi12XJNxpLp3QD2fwKMRLmRS9W5rcE", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/output/backupdb/blogs.sql b/output/backupdb/blogs.sql new file mode 100644 index 0000000..51bda87 --- /dev/null +++ b/output/backupdb/blogs.sql @@ -0,0 +1,2 @@ +INSERT INTO "blogs" ("id", "image", "date", "title", "description", "created_at", "updated_at", "deleted_at") VALUES (3, 'https/image', '2023-12-02', 'Kerusuhan DI Konser jakarta', 'beberapa orang menjadi tersangka', '2023-12-02 06:06:46.56004', '2023-12-02 06:06:46.56004', '0001-01-01 00:00:00'); +INSERT INTO "blogs" ("id", "image", "date", "title", "description", "created_at", "updated_at", "deleted_at") VALUES (1, 'https/image', '2023-12-31', 'Konser Tersukses', 'dikatakan oleh produser menjadi konser tersukses sepanjang masa', '2023-12-02 04:13:07.807064', '2023-12-02 06:07:55.103818', '0001-01-01 00:00:00'); diff --git a/output/backupdb/notifications.sql b/output/backupdb/notifications.sql new file mode 100644 index 0000000..8ee7c5e --- /dev/null +++ b/output/backupdb/notifications.sql @@ -0,0 +1 @@ +INSERT INTO "notifications" ("id", "type", "message", "is_read", "created_at", "updated_at", "deleted_at") VALUES (1, 'Diskon Ticket', 'ada diskon loh cek berikut', 't', '2023-12-02 06:17:03.644111', '2023-12-04 02:57:58.692812', '0001-01-01 00:00:00'); diff --git a/output/backupdb/orders.sql b/output/backupdb/orders.sql new file mode 100644 index 0000000..39e99a7 --- /dev/null +++ b/output/backupdb/orders.sql @@ -0,0 +1,4 @@ +INSERT INTO "orders" ("id", "ticket_id", "user_id", "quantity", "total", "status", "order_at", "updated_at", "deleted_at", "order_by", "update_by", "delete_by") VALUES (4, 1, 10, 2, 100, 'seleseai', '2023-12-04 02:13:26.064555', '2023-12-04 02:13:26.071624', NULL, '', '', ''); +INSERT INTO "orders" ("id", "ticket_id", "user_id", "quantity", "total", "status", "order_at", "updated_at", "deleted_at", "order_by", "update_by", "delete_by") VALUES (5, 1, 10, 2, 100, 'seleseai', '2023-12-04 02:16:57.451074', '2023-12-04 02:16:57.453216', NULL, '', '', ''); +INSERT INTO "orders" ("id", "ticket_id", "user_id", "quantity", "total", "status", "order_at", "updated_at", "deleted_at", "order_by", "update_by", "delete_by") VALUES (6, 1, 10, 2, 100, 'seleseai', '2023-12-04 02:48:47.038808', '2023-12-04 02:48:47.041164', NULL, '', '', ''); +INSERT INTO "orders" ("id", "ticket_id", "user_id", "quantity", "total", "status", "order_at", "updated_at", "deleted_at", "order_by", "update_by", "delete_by") VALUES (7, 3, 10, 2, 100, 'seleseai', '2023-12-05 02:37:03.546694', '2023-12-05 02:37:03.553886', NULL, '', '', ''); diff --git a/output/backupdb/schema_migrations.sql b/output/backupdb/schema_migrations.sql new file mode 100644 index 0000000..244f121 --- /dev/null +++ b/output/backupdb/schema_migrations.sql @@ -0,0 +1 @@ +INSERT INTO "schema_migrations" ("version", "dirty") VALUES (20231127160034, 'f'); diff --git a/output/backupdb/tickets.sql b/output/backupdb/tickets.sql new file mode 100644 index 0000000..e99ea94 --- /dev/null +++ b/output/backupdb/tickets.sql @@ -0,0 +1,2 @@ +INSERT INTO "tickets" ("id", "image", "location", "date", "title", "description", "price", "status", "quota", "category", "terjual", "created_at", "updated_at", "deleted_at") VALUES (1, 'https/image', 'bekasi', '2023-12-31', 'Dragon', 'konser lagu edm terbaik', 50, 'available', 192, 'music', 5, '2023-12-01 22:20:09.060639', '2023-12-04 02:48:47.046753', '0001-01-01 00:00:00'); +INSERT INTO "tickets" ("id", "image", "location", "date", "title", "description", "price", "status", "quota", "category", "terjual", "created_at", "updated_at", "deleted_at") VALUES (3, 'https/image', 'GBK', '2023-12-31', 'coldplay', 'penyanyi luar negri', 50, 'available', 98, 'music', 5, '2023-12-02 05:58:11.779525', '2023-12-05 02:37:03.566609', '0001-01-01 00:00:00'); diff --git a/output/backupdb/topup.sql b/output/backupdb/topup.sql new file mode 100644 index 0000000..e69de29 diff --git a/output/backupdb/users.sql b/output/backupdb/users.sql new file mode 100644 index 0000000..d42c66a --- /dev/null +++ b/output/backupdb/users.sql @@ -0,0 +1,2 @@ +INSERT INTO "users" ("id", "name", "email", "number", "roles", "password", "saldo", "created_at", "updated_at", "deleted_at") VALUES (12, '', 'Admin@gmail.com', '00000000000', 'Admin', '$2a$10$ExuW4yqrh6aqVXwicsqtT.S/rhyXw8oGVn6yPOUA5N8.SlOAevyv.', 0, '2023-12-04 19:35:14.953697+00', '2023-12-04 19:35:14.953697+00', NULL); +INSERT INTO "users" ("id", "name", "email", "number", "roles", "password", "saldo", "created_at", "updated_at", "deleted_at") VALUES (10, '', 'Buyer1@gmail.com', '12345678901', 'Buyer', '$2a$10$ZmeCfVg8g4pGofxJu0H0U.lcpnnCpYy/CP401phabScNkW90L6E4S', 1002200, '2023-12-02 22:30:06.783922+00', '2023-12-04 20:08:40.249843+00', NULL); diff --git a/output/login.jpg b/output/login.jpg new file mode 100644 index 0000000..fda64a0 Binary files /dev/null and b/output/login.jpg differ diff --git a/output/step.md b/output/step.md new file mode 100644 index 0000000..648d61b --- /dev/null +++ b/output/step.md @@ -0,0 +1,29 @@ +# Membuat Struktur Data Tiket +1. Buat file tiket.go di dalam folder entity. +2. Dalam file tersebut, buatlah struktur data (struct) untuk entitas tiket dengan mendefinisikan kolom-kolom yang dibutuhkan. + +# Membuat Repository Tiket +1. Buat file tiket_repository.go di dalam folder repository. +2. Dalam file tersebut, buatlah interface untuk mengakses data tiket. Panggil fungsi-fungsi yang dibutuhkan dari tiket_repository.go. + +# Membuat Service Tiket +1. Buat file tiket_service.go di dalam folder service. +2. Dalam file tersebut, buatlah struktur service untuk logika terkait tiket. Panggil fungsi-fungsi yang dibutuhkan dari tiket_repository.go. + +# Membuat Handler Tiket +1. Buat file tiket_handler.go di dalam folder handler. +2. Dalam file tersebut, buatlah handler untuk menerima request dan menangani logika terkait tiket. Panggil fungsi-fungsi dari tiket_service.go dan sesuaikan dengan kebutuhan. + +# Logika Builder +1. Tentukan apakah entitas tiket akan diatur sebagai private atau public. Buat file builderPrivieRoute.go jika private. +2. Panggil use case, repository, dan objek lain yang diperlukan di dalam file tersebut. + +# Membuat Routing +1. Buka file PrivateRoute atau file yang sesuai dengan routing di aplikasi. +2. Tambahkan endpoint untuk operasi yang ingin Anda dukung menggunakan handler tiket yang sudah dibuat. + +# Menjalankan Server +1. Jalankan server menggunakan perintah go run cmd/server main.go. +2. Pastikan file PrivateRoute atau file routing yang sesuai di-load dan endpoint tiket sudah terdaftar. + +Dengan langkah-langkah tersebut, Anda akan memiliki struktur dasar untuk menangani entitas tiket dalam aplikasi Anda. Pastikan untuk menggantinya sesuai kebutuhan dan menyesuaikan setiap langkah dengan logika dan fitur aplikasi yang Anda buat \ No newline at end of file