feat: create api for login and register
This commit is contained in:
commit
601dc1d088
|
@ -0,0 +1,13 @@
|
|||
# SERVER SETTINGS
|
||||
SERVER_HOST=localhost
|
||||
SERVER_PORT= # isi listen port anda (bebas)
|
||||
|
||||
# DATABASE SETTINGS
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432 # port default postgres
|
||||
DB_NAME= # nama_database di postgres
|
||||
DB_USER= # username yang digunakan di postgres
|
||||
DB_PASSWORD= # password yang digunakan di postgres
|
||||
|
||||
# api keyauth
|
||||
API_KEY=
|
|
@ -0,0 +1,25 @@
|
|||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# env file
|
||||
.env
|
|
@ -0,0 +1,2 @@
|
|||
# build_api_golang
|
||||
this is rest API using go lang and go fiber with postgreql database for my personal project.
|
|
@ -0,0 +1,63 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
DB *gorm.DB
|
||||
DBHost string
|
||||
DBPort string
|
||||
DBName string
|
||||
DBUser string
|
||||
DBPassword string
|
||||
|
||||
APIKey string
|
||||
ServerHost string
|
||||
ServerPort string
|
||||
)
|
||||
|
||||
func InitConfig() {
|
||||
ServerHost = os.Getenv("SERVER_HOST")
|
||||
ServerPort = os.Getenv("SERVER_PORT")
|
||||
DBHost = os.Getenv("DB_HOST")
|
||||
DBPort = os.Getenv("DB_PORT")
|
||||
DBName = os.Getenv("DB_NAME")
|
||||
DBUser = os.Getenv("DB_USER")
|
||||
DBPassword = os.Getenv("DB_PASSWORD")
|
||||
APIKey = os.Getenv("API_KEY")
|
||||
|
||||
if ServerHost == "" || ServerPort == "" || DBHost == "" || DBPort == "" || DBName == "" || DBUser == "" || DBPassword == "" || APIKey == "" {
|
||||
log.Fatal("Error: environment variables yang dibutuhkan tidak ada")
|
||||
}
|
||||
}
|
||||
|
||||
func InitDatabase() {
|
||||
InitConfig()
|
||||
|
||||
dsn := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=disable",
|
||||
DBHost, DBPort, DBUser, DBName, DBPassword)
|
||||
|
||||
var err error
|
||||
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatal("gagal terhubung ke database: ", err)
|
||||
}
|
||||
|
||||
err = DB.AutoMigrate(
|
||||
&domain.User{},
|
||||
&domain.UserRole{},
|
||||
&domain.Address{},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal("Error: Failed to auto migrate domain:", err)
|
||||
}
|
||||
|
||||
fmt.Println("Koneksi ke database berhasil dan migrasi dilakukan")
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Address struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
|
||||
UserID string `gorm:"not null" json:"userId"`
|
||||
User User `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"user"`
|
||||
Province string `gorm:"not null" json:"province"`
|
||||
District string `gorm:"not null" json:"district"`
|
||||
Subdistrict string `gorm:"not null" json:"subdistrict"`
|
||||
PostalCode int `gorm:"not null" json:"postalCode"`
|
||||
Village string `gorm:"not null" json:"village"`
|
||||
Detail string `gorm:"not null" json:"detail"`
|
||||
Geography string `gorm:"not null" json:"geography"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package domain
|
||||
|
||||
type UserRole struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
|
||||
RoleName string `gorm:"unique;not null" json:"roleName"`
|
||||
Users []User `gorm:"foreignKey:RoleID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"users"`
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"`
|
||||
Avatar *string `json:"avatar,omitempty"`
|
||||
Username string `gorm:"unique;not null" json:"username"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Phone string `gorm:"not null" json:"phone"`
|
||||
Email string `gorm:"unique;not null" json:"email"`
|
||||
EmailVerified bool `gorm:"default:false" json:"emailVerified"`
|
||||
Password string `gorm:"not null" json:"password"`
|
||||
CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"`
|
||||
RoleID string `gorm:"not null" json:"roleId"`
|
||||
Role UserRole `gorm:"foreignKey:RoleID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"role"`
|
||||
AddressId *string `gorm:"default:null" json:"addressId"`
|
||||
Addresses []Address `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"addresses"`
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package dto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type AddressInput struct {
|
||||
Province string `json:"province" validate:"required"`
|
||||
District string `json:"district" validate:"required"`
|
||||
Subdistrict string `json:"subdistrict" validate:"required"`
|
||||
PostalCode int `json:"postalCode" validate:"required,numeric"`
|
||||
Village string `json:"village" validate:"required"`
|
||||
Detail string `json:"detail" validate:"required"`
|
||||
Geography string `json:"geography" validate:"required"`
|
||||
}
|
||||
|
||||
var validate = validator.New()
|
||||
|
||||
func (c *AddressInput) ValidatePost() error {
|
||||
err := validate.Struct(c)
|
||||
if err != nil {
|
||||
|
||||
for _, e := range err.(validator.ValidationErrors) {
|
||||
|
||||
switch e.Field() {
|
||||
case "Province":
|
||||
return fmt.Errorf("provinsi harus diisisi")
|
||||
case "District":
|
||||
return fmt.Errorf("kabupaten harus diisi")
|
||||
case "Subdistrict":
|
||||
return fmt.Errorf("kecamatan harus diisi")
|
||||
case "PostalCode":
|
||||
return fmt.Errorf("postal code harus diisi dan berupa angka")
|
||||
case "Village":
|
||||
return fmt.Errorf("desa harus diisi")
|
||||
case "Detail":
|
||||
return fmt.Errorf("detail wajib diisi")
|
||||
case "Geography":
|
||||
return fmt.Errorf("lokasi kordinat harus diisi")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AddressInput) ValidateUpdate() error {
|
||||
err := validate.Struct(c)
|
||||
if err != nil {
|
||||
|
||||
for _, e := range err.(validator.ValidationErrors) {
|
||||
|
||||
switch e.Field() {
|
||||
case "Province":
|
||||
return fmt.Errorf("provinsi harus diisisi")
|
||||
case "District":
|
||||
return fmt.Errorf("kabupaten harus diisi")
|
||||
case "Subdistrict":
|
||||
return fmt.Errorf("kecamatan harus diisi")
|
||||
case "PostalCode":
|
||||
return fmt.Errorf("postal code harus diisi dan berupa angka")
|
||||
case "Village":
|
||||
return fmt.Errorf("desa harus diisi")
|
||||
case "Detail":
|
||||
return fmt.Errorf("detail wajib diisi")
|
||||
case "Geography":
|
||||
return fmt.Errorf("lokasi kordinat harus diisi")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package dto
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func ValidateEmail(email string) error {
|
||||
if email == "" {
|
||||
return errors.New("email harus diisi")
|
||||
}
|
||||
|
||||
emailRegex := `^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$`
|
||||
re := regexp.MustCompile(emailRegex)
|
||||
if !re.MatchString(email) {
|
||||
return errors.New("format email belum sesuai")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidatePhone(phone string) error {
|
||||
if phone == "" {
|
||||
return errors.New("nomor telepon harus diisi")
|
||||
}
|
||||
|
||||
phoneRegex := `^\+?[0-9]{10,15}$`
|
||||
re := regexp.MustCompile(phoneRegex)
|
||||
if !re.MatchString(phone) {
|
||||
return errors.New("nomor telepon tidak valid")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidatePassword(password string) error {
|
||||
if password == "" {
|
||||
return errors.New("password harus diisi")
|
||||
}
|
||||
|
||||
if len(password) < 8 {
|
||||
return errors.New("password minimal 8 karakter")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RegisterUserInput struct {
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
Password string `json:"password"`
|
||||
RoleId string `json:"roleId"`
|
||||
}
|
||||
|
||||
func (input *RegisterUserInput) Validate() error {
|
||||
|
||||
if input.Username == "" {
|
||||
return errors.New("username harus diisi")
|
||||
}
|
||||
|
||||
if input.Name == "" {
|
||||
return errors.New("nama harus diisi")
|
||||
}
|
||||
|
||||
if err := ValidateEmail(input.Email); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ValidatePhone(input.Phone); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ValidatePassword(input.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if input.RoleId == "" {
|
||||
return errors.New("roleId harus diisi")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
module github.com/pahmiudahgede/senggoldong
|
||||
|
||||
go 1.23.3
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.23.0 // indirect
|
||||
github.com/gofiber/fiber/v2 v2.52.5 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lucsky/cuid v1.2.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
gorm.io/driver/postgres v1.5.11 // indirect
|
||||
gorm.io/gorm v1.25.12 // indirect
|
||||
)
|
|
@ -0,0 +1,78 @@
|
|||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
|
||||
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
||||
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
||||
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lucsky/cuid v1.2.1 h1:MtJrL2OFhvYufUIn48d35QGXyeTC8tn0upumW9WwTHg=
|
||||
github.com/lucsky/cuid v1.2.1/go.mod h1:QaaJqckboimOmhRSJXSx/+IT+VTfxfPGSo/6mfgUfmE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
|
||||
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
|
||||
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
|
@ -0,0 +1,20 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/controllers"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/middleware"
|
||||
)
|
||||
|
||||
func AppRouter(app *fiber.App) {
|
||||
app.Post("/register", controllers.Register)
|
||||
app.Post("/login", controllers.Login)
|
||||
|
||||
app.Get("/user", middleware.AuthMiddleware, controllers.GetUserInfo)
|
||||
|
||||
app.Get("/list-address", middleware.AuthMiddleware, controllers.GetListAddress)
|
||||
app.Get("/address/:id", middleware.AuthMiddleware, controllers.GetAddressByID)
|
||||
app.Post("/create-address", middleware.AuthMiddleware, controllers.CreateAddress)
|
||||
app.Put("/address/:id", middleware.AuthMiddleware, controllers.UpdateAddress)
|
||||
app.Delete("/address/:id", middleware.AuthMiddleware, controllers.DeleteAddress)
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func CreateAddress(c *fiber.Ctx) error {
|
||||
var input dto.AddressInput
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Mohon masukkan alamat dengan benar",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := input.ValidatePost(); err != nil {
|
||||
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
address, err := services.CreateAddress(userID, input)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to create address",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
addressResponse := map[string]interface{}{
|
||||
"id": address.ID,
|
||||
"province": address.Province,
|
||||
"district": address.District,
|
||||
"subdistrict": address.Subdistrict,
|
||||
"postalCode": address.PostalCode,
|
||||
"village": address.Village,
|
||||
"detail": address.Detail,
|
||||
"geography": address.Geography,
|
||||
"createdAt": address.CreatedAt,
|
||||
"updatedAt": address.UpdatedAt,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Address created successfully",
|
||||
addressResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func GetListAddress(c *fiber.Ctx) error {
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
addresses, err := services.GetAllAddressesByUserID(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Addresses not found",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
addressResponses := []map[string]interface{}{}
|
||||
|
||||
for _, address := range addresses {
|
||||
addressResponse := map[string]interface{}{
|
||||
"id": address.ID,
|
||||
"province": address.Province,
|
||||
"district": address.District,
|
||||
"subdistrict": address.Subdistrict,
|
||||
"postalCode": address.PostalCode,
|
||||
"village": address.Village,
|
||||
"detail": address.Detail,
|
||||
"geography": address.Geography,
|
||||
"createdAt": address.CreatedAt,
|
||||
"updatedAt": address.UpdatedAt,
|
||||
}
|
||||
addressResponses = append(addressResponses, addressResponse)
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Addresses fetched successfully",
|
||||
addressResponses,
|
||||
))
|
||||
}
|
||||
|
||||
func GetAddressByID(c *fiber.Ctx) error {
|
||||
|
||||
addressID := c.Params("id")
|
||||
|
||||
address, err := services.GetAddressByID(addressID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"Address not found",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
addressResponse := map[string]interface{}{
|
||||
"id": address.ID,
|
||||
"province": address.Province,
|
||||
"district": address.District,
|
||||
"subdistrict": address.Subdistrict,
|
||||
"postalCode": address.PostalCode,
|
||||
"village": address.Village,
|
||||
"detail": address.Detail,
|
||||
"geography": address.Geography,
|
||||
"createdAt": address.CreatedAt,
|
||||
"updatedAt": address.UpdatedAt,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Address fetched successfully",
|
||||
addressResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func UpdateAddress(c *fiber.Ctx) error {
|
||||
|
||||
addressID := c.Params("id")
|
||||
|
||||
var input dto.AddressInput
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := input.ValidateUpdate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
address, err := services.UpdateAddress(addressID, input)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to update address",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
addressResponse := map[string]interface{}{
|
||||
"id": address.ID,
|
||||
"province": address.Province,
|
||||
"district": address.District,
|
||||
"subdistrict": address.Subdistrict,
|
||||
"postalCode": address.PostalCode,
|
||||
"village": address.Village,
|
||||
"detail": address.Detail,
|
||||
"geography": address.Geography,
|
||||
"createdAt": address.CreatedAt,
|
||||
"updatedAt": address.UpdatedAt,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Address updated successfully",
|
||||
addressResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func DeleteAddress(c *fiber.Ctx) error {
|
||||
|
||||
addressID := c.Params("id")
|
||||
|
||||
err := services.DeleteAddress(addressID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to delete address",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Address deleted successfully",
|
||||
nil,
|
||||
))
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
func Register(c *fiber.Ctx) error {
|
||||
var userInput dto.RegisterUserInput
|
||||
|
||||
if err := c.BodyParser(&userInput); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
if err := userInput.Validate(); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
err := services.RegisterUser(userInput.Username, userInput.Name, userInput.Email, userInput.Phone, userInput.Password, userInput.RoleId)
|
||||
if err != nil {
|
||||
|
||||
if err.Error() == "email is already registered" {
|
||||
return c.Status(fiber.StatusConflict).JSON(utils.FormatResponse(
|
||||
fiber.StatusConflict,
|
||||
"Email is already registered",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
if err.Error() == "username is already registered" {
|
||||
return c.Status(fiber.StatusConflict).JSON(utils.FormatResponse(
|
||||
fiber.StatusConflict,
|
||||
"Username is already registered",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
if err.Error() == "phone number is already registered" {
|
||||
return c.Status(fiber.StatusConflict).JSON(utils.FormatResponse(
|
||||
fiber.StatusConflict,
|
||||
"Phone number is already registered",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to create user",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
user, err := repositories.GetUserByEmailOrUsername(userInput.Email)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(utils.FormatResponse(
|
||||
fiber.StatusInternalServerError,
|
||||
"Failed to fetch user after registration",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userResponse := map[string]interface{}{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"name": user.Name,
|
||||
"email": user.Email,
|
||||
"phone": user.Phone,
|
||||
"roleId": user.RoleID,
|
||||
"createdAt": user.CreatedAt,
|
||||
"updatedAt": user.UpdatedAt,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"User registered successfully",
|
||||
userResponse,
|
||||
))
|
||||
}
|
||||
|
||||
func Login(c *fiber.Ctx) error {
|
||||
var credentials struct {
|
||||
EmailOrUsername string `json:"email_or_username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
if err := c.BodyParser(&credentials); err != nil {
|
||||
|
||||
return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse(
|
||||
fiber.StatusBadRequest,
|
||||
"Invalid input data",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
token, err := services.LoginUser(credentials.EmailOrUsername, credentials.Password)
|
||||
if err != nil {
|
||||
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse(
|
||||
fiber.StatusUnauthorized,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"Login successful",
|
||||
map[string]string{"token": token},
|
||||
))
|
||||
}
|
||||
|
||||
func GetUserInfo(c *fiber.Ctx) error {
|
||||
|
||||
userID := c.Locals("userID").(string)
|
||||
|
||||
user, err := services.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(utils.FormatResponse(
|
||||
fiber.StatusNotFound,
|
||||
"user tidak ditemukan",
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
userResponse := map[string]interface{}{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"nama": user.Name,
|
||||
"nohp": user.Phone,
|
||||
"email": user.Email,
|
||||
"statusverifikasi": user.EmailVerified,
|
||||
"role": user.Role.RoleName,
|
||||
"createdAt": user.CreatedAt,
|
||||
"updatedAt": user.UpdatedAt,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(utils.FormatResponse(
|
||||
fiber.StatusOK,
|
||||
"data user berhasil ditampilkan",
|
||||
userResponse,
|
||||
))
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
func AuthMiddleware(c *fiber.Ctx) error {
|
||||
tokenString := c.Get("Authorization")
|
||||
tokenString = strings.TrimPrefix(tokenString, "Bearer ")
|
||||
|
||||
if tokenString == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"message": "Missing or invalid token",
|
||||
})
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(os.Getenv("API_KEY")), nil
|
||||
})
|
||||
|
||||
if err != nil || !token.Valid {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"message": "Invalid or expired token",
|
||||
})
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"message": "Invalid token claims",
|
||||
})
|
||||
}
|
||||
|
||||
userID := claims["sub"].(string)
|
||||
|
||||
c.Locals("userID", userID)
|
||||
|
||||
return c.Next()
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
func CreateAddress(address *domain.Address) error {
|
||||
result := config.DB.Create(address)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAddressesByUserID(userID string) ([]domain.Address, error) {
|
||||
var addresses []domain.Address
|
||||
err := config.DB.Where("user_id = ?", userID).Find(&addresses).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
func GetAddressByID(addressID string) (domain.Address, error) {
|
||||
var address domain.Address
|
||||
if err := config.DB.Where("id = ?", addressID).First(&address).Error; err != nil {
|
||||
return address, errors.New("address not found")
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func UpdateAddress(address domain.Address) (domain.Address, error) {
|
||||
if err := config.DB.Save(&address).Error; err != nil {
|
||||
return address, err
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func DeleteAddress(addressID string) error {
|
||||
var address domain.Address
|
||||
if err := config.DB.Where("id = ?", addressID).Delete(&address).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
)
|
||||
|
||||
func IsEmailExist(email string) bool {
|
||||
var user domain.User
|
||||
if err := config.DB.Where("email = ?", email).First(&user).Error; err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsUsernameExist(username string) bool {
|
||||
var user domain.User
|
||||
if err := config.DB.Where("username = ?", username).First(&user).Error; err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsPhoneExist(phone string) bool {
|
||||
var user domain.User
|
||||
if err := config.DB.Where("phone = ?", phone).First(&user).Error; err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CreateUser(username, name, email, phone, password, roleId string) error {
|
||||
|
||||
if IsEmailExist(email) {
|
||||
return errors.New("email is already registered")
|
||||
}
|
||||
|
||||
if IsUsernameExist(username) {
|
||||
return errors.New("username is already registered")
|
||||
}
|
||||
|
||||
if IsPhoneExist(phone) {
|
||||
return errors.New("phone number is already registered")
|
||||
}
|
||||
|
||||
user := domain.User{
|
||||
Username: username,
|
||||
Name: name,
|
||||
Email: email,
|
||||
Phone: phone,
|
||||
Password: password,
|
||||
RoleID: roleId,
|
||||
}
|
||||
|
||||
result := config.DB.Create(&user)
|
||||
if result.Error != nil {
|
||||
return errors.New("failed to create user")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetUserByEmailOrUsername(emailOrUsername string) (domain.User, error) {
|
||||
var user domain.User
|
||||
if err := config.DB.Where("email = ? OR username = ?", emailOrUsername, emailOrUsername).First(&user).Error; err != nil {
|
||||
return user, errors.New("user not found")
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func GetUserByID(userID string) (domain.User, error) {
|
||||
var user domain.User
|
||||
if err := config.DB.
|
||||
Preload("Role").
|
||||
Where("id = ?", userID).
|
||||
First(&user).Error; err != nil {
|
||||
return user, errors.New("user not found")
|
||||
}
|
||||
|
||||
fmt.Printf("User ID: %s, Role: %v\n", user.ID, user.Role)
|
||||
|
||||
return user, nil
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
)
|
||||
|
||||
func CreateAddress(userID string, input dto.AddressInput) (domain.Address, error) {
|
||||
address := domain.Address{
|
||||
UserID: userID,
|
||||
Province: input.Province,
|
||||
District: input.District,
|
||||
Subdistrict: input.Subdistrict,
|
||||
PostalCode: input.PostalCode,
|
||||
Village: input.Village,
|
||||
Detail: input.Detail,
|
||||
Geography: input.Geography,
|
||||
}
|
||||
|
||||
err := repositories.CreateAddress(&address)
|
||||
if err != nil {
|
||||
return domain.Address{}, err
|
||||
}
|
||||
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func GetAllAddressesByUserID(userID string) ([]domain.Address, error) {
|
||||
|
||||
addresses, err := repositories.GetAddressesByUserID(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
func GetAddressByID(addressID string) (domain.Address, error) {
|
||||
address, err := repositories.GetAddressByID(addressID)
|
||||
if err != nil {
|
||||
return address, errors.New("address not found")
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
func UpdateAddress(addressID string, input dto.AddressInput) (domain.Address, error) {
|
||||
|
||||
address, err := repositories.GetAddressByID(addressID)
|
||||
if err != nil {
|
||||
return address, errors.New("address not found")
|
||||
}
|
||||
|
||||
address.Province = input.Province
|
||||
address.District = input.District
|
||||
address.Subdistrict = input.Subdistrict
|
||||
address.PostalCode = input.PostalCode
|
||||
address.Village = input.Village
|
||||
address.Detail = input.Detail
|
||||
address.Geography = input.Geography
|
||||
|
||||
updatedAddress, err := repositories.UpdateAddress(address)
|
||||
if err != nil {
|
||||
return updatedAddress, errors.New("failed to update address")
|
||||
}
|
||||
|
||||
return updatedAddress, nil
|
||||
}
|
||||
|
||||
func DeleteAddress(addressID string) error {
|
||||
err := repositories.DeleteAddress(addressID)
|
||||
if err != nil {
|
||||
return errors.New("failed to delete address")
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/pahmiudahgede/senggoldong/domain"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func RegisterUser(username, name, email, phone, password, roleId string) error {
|
||||
|
||||
if repositories.IsEmailExist(email) {
|
||||
return errors.New("email is already registered")
|
||||
}
|
||||
if repositories.IsUsernameExist(username) {
|
||||
return errors.New("username is already registered")
|
||||
}
|
||||
if repositories.IsPhoneExist(phone) {
|
||||
return errors.New("phone number is already registered")
|
||||
}
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return errors.New("failed to hash password")
|
||||
}
|
||||
|
||||
err = repositories.CreateUser(username, name, email, phone, string(hashedPassword), roleId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoginUser(emailOrUsername, password string) (string, error) {
|
||||
if emailOrUsername == "" || password == "" {
|
||||
return "", errors.New("email/username and password must be provided")
|
||||
}
|
||||
|
||||
user, err := repositories.GetUserByEmailOrUsername(emailOrUsername)
|
||||
if err != nil {
|
||||
return "", errors.New("invalid email/username or password")
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
|
||||
return "", errors.New("invalid email/username or password")
|
||||
}
|
||||
|
||||
token := generateJWT(user.ID)
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func generateJWT(userID string) string {
|
||||
claims := jwt.MapClaims{
|
||||
"sub": userID,
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
t, err := token.SignedString([]byte(os.Getenv("API_KEY")))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func GetUserByID(userID string) (domain.User, error) {
|
||||
user, err := repositories.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return user, errors.New("user not found")
|
||||
}
|
||||
return user, nil
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/api"
|
||||
)
|
||||
|
||||
func init() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("error saat memuat file .env")
|
||||
}
|
||||
|
||||
config.InitConfig()
|
||||
config.InitDatabase()
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := fiber.New()
|
||||
|
||||
api.AppRouter(app)
|
||||
|
||||
log.Fatal(app.Listen(":" + os.Getenv("SERVER_PORT")))
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package utils
|
||||
|
||||
type Meta struct {
|
||||
StatusCode int `json:"statusCode"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type ApiResponse struct {
|
||||
Meta Meta `json:"meta"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
func FormatResponse(statusCode int, message string, data interface{}) ApiResponse {
|
||||
return ApiResponse{
|
||||
Meta: Meta{
|
||||
StatusCode: statusCode,
|
||||
Message: message,
|
||||
},
|
||||
Data: data,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue