refact: register now can handle otp from phone
This commit is contained in:
parent
ffd71d4fc6
commit
21d4f28cef
|
|
@ -8,6 +8,10 @@ import (
|
|||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func GetSecretKey() string {
|
||||
return os.Getenv("SECRET_KEY")
|
||||
}
|
||||
|
||||
func StartServer(app *fiber.App) {
|
||||
host := os.Getenv("SERVER_HOST")
|
||||
port := os.Getenv("SERVER_PORT")
|
||||
|
|
|
|||
|
|
@ -14,4 +14,5 @@ func SetupConfig() {
|
|||
|
||||
ConnectDatabase()
|
||||
ConnectRedis()
|
||||
InitWhatsApp()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,111 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/mdp/qrterminal/v3"
|
||||
"go.mau.fi/whatsmeow"
|
||||
"go.mau.fi/whatsmeow/proto/waE2E"
|
||||
"go.mau.fi/whatsmeow/store/sqlstore"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
waLog "go.mau.fi/whatsmeow/util/log"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var WhatsAppClient *whatsmeow.Client
|
||||
var container *sqlstore.Container
|
||||
|
||||
func InitWhatsApp() {
|
||||
dbLog := waLog.Stdout("Database", "DEBUG", true)
|
||||
|
||||
dsn := fmt.Sprintf(
|
||||
"postgres://%s:%s@%s:%s/%s?sslmode=disable",
|
||||
os.Getenv("DB_USER"),
|
||||
os.Getenv("DB_PASSWORD"),
|
||||
os.Getenv("DB_HOST"),
|
||||
os.Getenv("DB_PORT"),
|
||||
os.Getenv("DB_NAME"),
|
||||
)
|
||||
|
||||
var err error
|
||||
container, err = sqlstore.New("postgres", dsn, dbLog)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to WhatsApp database: %v", err)
|
||||
}
|
||||
|
||||
deviceStore, err := container.GetFirstDevice()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get WhatsApp device: %v", err)
|
||||
}
|
||||
|
||||
clientLog := waLog.Stdout("Client", "DEBUG", true)
|
||||
WhatsAppClient = whatsmeow.NewClient(deviceStore, clientLog)
|
||||
|
||||
if WhatsAppClient.Store.ID == nil {
|
||||
fmt.Println("WhatsApp Client is not logged in, generating QR Code...")
|
||||
|
||||
qrChan, _ := WhatsAppClient.GetQRChannel(context.Background())
|
||||
err = WhatsAppClient.Connect()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect WhatsApp client: %v", err)
|
||||
}
|
||||
|
||||
for evt := range qrChan {
|
||||
if evt.Event == "code" {
|
||||
fmt.Println("QR Code untuk login:")
|
||||
generateQRCode(evt.Code)
|
||||
} else {
|
||||
fmt.Println("Login event:", evt.Event)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Println("WhatsApp Client sudah login, langsung terhubung...")
|
||||
err = WhatsAppClient.Connect()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect WhatsApp client: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("WhatsApp client connected successfully!")
|
||||
go handleShutdown()
|
||||
}
|
||||
|
||||
func generateQRCode(qrString string) {
|
||||
qrterminal.GenerateHalfBlock(qrString, qrterminal.M, os.Stdout)
|
||||
}
|
||||
|
||||
|
||||
func handleShutdown() {
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
||||
<-sigChan
|
||||
|
||||
log.Println("Shutting down WhatsApp client...")
|
||||
WhatsAppClient.Disconnect()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func SendWhatsAppMessage(phone, message string) error {
|
||||
if WhatsAppClient == nil {
|
||||
return fmt.Errorf("WhatsApp client is not initialized")
|
||||
}
|
||||
|
||||
targetJID, _ := types.ParseJID(phone + "@s.whatsapp.net")
|
||||
msg := waE2E.Message{
|
||||
Conversation: proto.String(message),
|
||||
}
|
||||
|
||||
_, err := WhatsAppClient.SendMessage(context.Background(), targetJID, &msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to send WhatsApp message: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("WhatsApp message sent successfully to: %s", phone)
|
||||
return nil
|
||||
}
|
||||
234
dto/auth_dto.go
234
dto/auth_dto.go
|
|
@ -5,39 +5,46 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
type LoginDTO struct {
|
||||
RoleID string `json:"roleid"`
|
||||
Identifier string `json:"identifier"`
|
||||
Password string `json:"password"`
|
||||
type RegisterRequest struct {
|
||||
RoleID string `json:"role_id"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
type UserResponseWithToken struct {
|
||||
type VerifyOTPRequest struct {
|
||||
Phone string `json:"phone"`
|
||||
OTP string `json:"otp"`
|
||||
}
|
||||
|
||||
type MetaResponse struct {
|
||||
Status int `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// UserDataResponse untuk bagian data
|
||||
type UserDataResponse struct {
|
||||
UserID string `json:"user_id"`
|
||||
RoleName string `json:"role_name"`
|
||||
UserRole string `json:"user_role"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type RegisterDTO struct {
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
Phone string `json:"phone"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
ConfirmPassword string `json:"confirm_password"`
|
||||
RoleID string `json:"roleId,omitempty"`
|
||||
// Response struct utama
|
||||
type Response struct {
|
||||
Meta MetaResponse `json:"meta"`
|
||||
Data *UserDataResponse `json:"data,omitempty"` // Gunakan pointer agar bisa bernilai nil jika tidak diperlukan
|
||||
}
|
||||
|
||||
func (l *LoginDTO) Validate() (map[string][]string, bool) {
|
||||
func (l *RegisterRequest) Validate() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
// Validasi RoleID dan Phone
|
||||
if strings.TrimSpace(l.RoleID) == "" {
|
||||
errors["roleid"] = append(errors["roleid"], "Role ID is required")
|
||||
}
|
||||
if strings.TrimSpace(l.Identifier) == "" {
|
||||
errors["identifier"] = append(errors["identifier"], "Identifier (username, email, or phone) is required")
|
||||
}
|
||||
if strings.TrimSpace(l.Password) == "" {
|
||||
errors["password"] = append(errors["password"], "Password is required")
|
||||
|
||||
if strings.TrimSpace(l.Phone) == "" {
|
||||
errors["phone"] = append(errors["phone"], "Phone is required")
|
||||
} else if !IsValidPhoneNumber(l.Phone) {
|
||||
errors["phone"] = append(errors["phone"], "Invalid phone number format. Use 62 followed by 9-13 digits")
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
|
|
@ -46,76 +53,131 @@ func (l *LoginDTO) Validate() (map[string][]string, bool) {
|
|||
return nil, true
|
||||
}
|
||||
|
||||
func (r *RegisterDTO) Validate() (map[string][]string, bool) {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
r.validateRequiredFields(errors)
|
||||
|
||||
if r.Phone != "" && !IsValidPhoneNumber(r.Phone) {
|
||||
errors["phone"] = append(errors["phone"], "Invalid phone number format. Use +62 followed by 9-13 digits")
|
||||
}
|
||||
|
||||
if r.Email != "" && !IsValidEmail(r.Email) {
|
||||
errors["email"] = append(errors["email"], "Invalid email format")
|
||||
}
|
||||
|
||||
if r.Password != "" && !IsValidPassword(r.Password) {
|
||||
errors["password"] = append(errors["password"], "Password must be at least 8 characters long and contain at least one number")
|
||||
}
|
||||
|
||||
if r.ConfirmPassword != "" && r.Password != r.ConfirmPassword {
|
||||
errors["confirm_password"] = append(errors["confirm_password"], "Password and confirm password do not match")
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
}
|
||||
|
||||
return nil, true
|
||||
}
|
||||
|
||||
func (r *RegisterDTO) validateRequiredFields(errors map[string][]string) {
|
||||
|
||||
if strings.TrimSpace(r.Username) == "" {
|
||||
errors["username"] = append(errors["username"], "Username is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Name) == "" {
|
||||
errors["name"] = append(errors["name"], "Name is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Phone) == "" {
|
||||
errors["phone"] = append(errors["phone"], "Phone number is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Email) == "" {
|
||||
errors["email"] = append(errors["email"], "Email is required")
|
||||
}
|
||||
if strings.TrimSpace(r.Password) == "" {
|
||||
errors["password"] = append(errors["password"], "Password is required")
|
||||
}
|
||||
if strings.TrimSpace(r.ConfirmPassword) == "" {
|
||||
errors["confirm_password"] = append(errors["confirm_password"], "Confirm password is required")
|
||||
}
|
||||
if strings.TrimSpace(r.RoleID) == "" {
|
||||
errors["roleId"] = append(errors["roleId"], "RoleID is required")
|
||||
}
|
||||
}
|
||||
|
||||
// IsValidPhoneNumber untuk validasi format nomor telepon
|
||||
func IsValidPhoneNumber(phone string) bool {
|
||||
|
||||
re := regexp.MustCompile(`^\+62\d{9,13}$`)
|
||||
// Validasi format nomor telepon harus dimulai dengan 62 dan 9-13 digit setelahnya
|
||||
re := regexp.MustCompile(`^62\d{9,13}$`)
|
||||
return re.MatchString(phone)
|
||||
}
|
||||
|
||||
func IsValidEmail(email string) bool {
|
||||
// package dto
|
||||
|
||||
re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
||||
return re.MatchString(email)
|
||||
}
|
||||
// import (
|
||||
// "regexp"
|
||||
// "strings"
|
||||
// )
|
||||
|
||||
func IsValidPassword(password string) bool {
|
||||
if len(password) < 8 {
|
||||
return false
|
||||
}
|
||||
// type LoginDTO struct {
|
||||
// RoleID string `json:"roleid"`
|
||||
// Identifier string `json:"identifier"`
|
||||
// Password string `json:"password"`
|
||||
// }
|
||||
|
||||
re := regexp.MustCompile(`\d`)
|
||||
return re.MatchString(password)
|
||||
}
|
||||
// type UserResponseWithToken struct {
|
||||
// UserID string `json:"user_id"`
|
||||
// RoleName string `json:"role_name"`
|
||||
// Token string `json:"token"`
|
||||
// }
|
||||
|
||||
// type RegisterDTO struct {
|
||||
// Username string `json:"username"`
|
||||
// Name string `json:"name"`
|
||||
// Phone string `json:"phone"`
|
||||
// Email string `json:"email"`
|
||||
// Password string `json:"password"`
|
||||
// ConfirmPassword string `json:"confirm_password"`
|
||||
// RoleID string `json:"roleId,omitempty"`
|
||||
// }
|
||||
|
||||
// func (l *LoginDTO) Validate() (map[string][]string, bool) {
|
||||
// errors := make(map[string][]string)
|
||||
|
||||
// if strings.TrimSpace(l.RoleID) == "" {
|
||||
// errors["roleid"] = append(errors["roleid"], "Role ID is required")
|
||||
// }
|
||||
// if strings.TrimSpace(l.Identifier) == "" {
|
||||
// errors["identifier"] = append(errors["identifier"], "Identifier (username, email, or phone) is required")
|
||||
// }
|
||||
// if strings.TrimSpace(l.Password) == "" {
|
||||
// errors["password"] = append(errors["password"], "Password is required")
|
||||
// }
|
||||
|
||||
// if len(errors) > 0 {
|
||||
// return errors, false
|
||||
// }
|
||||
// return nil, true
|
||||
// }
|
||||
|
||||
// func (r *RegisterDTO) Validate() (map[string][]string, bool) {
|
||||
// errors := make(map[string][]string)
|
||||
|
||||
// r.validateRequiredFields(errors)
|
||||
|
||||
// if r.Phone != "" && !IsValidPhoneNumber(r.Phone) {
|
||||
// errors["phone"] = append(errors["phone"], "Invalid phone number format. Use +62 followed by 9-13 digits")
|
||||
// }
|
||||
|
||||
// if r.Email != "" && !IsValidEmail(r.Email) {
|
||||
// errors["email"] = append(errors["email"], "Invalid email format")
|
||||
// }
|
||||
|
||||
// if r.Password != "" && !IsValidPassword(r.Password) {
|
||||
// errors["password"] = append(errors["password"], "Password must be at least 8 characters long and contain at least one number")
|
||||
// }
|
||||
|
||||
// if r.ConfirmPassword != "" && r.Password != r.ConfirmPassword {
|
||||
// errors["confirm_password"] = append(errors["confirm_password"], "Password and confirm password do not match")
|
||||
// }
|
||||
|
||||
// if len(errors) > 0 {
|
||||
// return errors, false
|
||||
// }
|
||||
|
||||
// return nil, true
|
||||
// }
|
||||
|
||||
// func (r *RegisterDTO) validateRequiredFields(errors map[string][]string) {
|
||||
|
||||
// if strings.TrimSpace(r.Username) == "" {
|
||||
// errors["username"] = append(errors["username"], "Username is required")
|
||||
// }
|
||||
// if strings.TrimSpace(r.Name) == "" {
|
||||
// errors["name"] = append(errors["name"], "Name is required")
|
||||
// }
|
||||
// if strings.TrimSpace(r.Phone) == "" {
|
||||
// errors["phone"] = append(errors["phone"], "Phone number is required")
|
||||
// }
|
||||
// if strings.TrimSpace(r.Email) == "" {
|
||||
// errors["email"] = append(errors["email"], "Email is required")
|
||||
// }
|
||||
// if strings.TrimSpace(r.Password) == "" {
|
||||
// errors["password"] = append(errors["password"], "Password is required")
|
||||
// }
|
||||
// if strings.TrimSpace(r.ConfirmPassword) == "" {
|
||||
// errors["confirm_password"] = append(errors["confirm_password"], "Confirm password is required")
|
||||
// }
|
||||
// if strings.TrimSpace(r.RoleID) == "" {
|
||||
// errors["roleId"] = append(errors["roleId"], "RoleID is required")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func IsValidPhoneNumber(phone string) bool {
|
||||
|
||||
// re := regexp.MustCompile(`^\+62\d{9,13}$`)
|
||||
// return re.MatchString(phone)
|
||||
// }
|
||||
|
||||
// func IsValidEmail(email string) bool {
|
||||
|
||||
// re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
||||
// return re.MatchString(email)
|
||||
// }
|
||||
|
||||
// func IsValidPassword(password string) bool {
|
||||
// if len(password) < 8 {
|
||||
// return false
|
||||
// }
|
||||
|
||||
// re := regexp.MustCompile(`\d`)
|
||||
// return re.MatchString(password)
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ func (r *UpdateUserDTO) Validate() (map[string][]string, bool) {
|
|||
errors["phone"] = append(errors["phone"], "Invalid phone number format. Use +62 followed by 9-13 digits")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(r.Email) == "" {
|
||||
errors["email"] = append(errors["email"], "Email is required")
|
||||
} else if !IsValidEmail(r.Email) {
|
||||
errors["email"] = append(errors["email"], "Invalid email format")
|
||||
}
|
||||
// if strings.TrimSpace(r.Email) == "" {
|
||||
// errors["email"] = append(errors["email"], "Email is required")
|
||||
// } else if !IsValidEmail(r.Email) {
|
||||
// errors["email"] = append(errors["email"], "Invalid email format")
|
||||
// }
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors, false
|
||||
|
|
|
|||
27
go.mod
27
go.mod
|
|
@ -6,17 +6,24 @@ require (
|
|||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/gofiber/fiber/v2 v2.52.5
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/google/uuid v1.5.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
golang.org/x/crypto v0.19.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
gorm.io/driver/postgres v1.5.11
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
rsc.io/qr v0.2.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/gorilla/websocket 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
|
||||
|
|
@ -24,16 +31,22 @@ require (
|
|||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/lib/pq v1.10.9
|
||||
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/mdp/qrterminal/v3 v3.2.0
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/stretchr/testify v1.8.4 // indirect
|
||||
github.com/rs/zerolog v1.33.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/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
|
||||
go.mau.fi/libsignal v0.1.2 // indirect
|
||||
go.mau.fi/util v0.8.6 // indirect
|
||||
go.mau.fi/whatsmeow v0.0.0-20250316144733-e7e263bf2175
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5
|
||||
)
|
||||
|
|
|
|||
60
go.sum
60
go.sum
|
|
@ -1,7 +1,10 @@
|
|||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -11,12 +14,17 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
|
|||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
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/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
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=
|
||||
|
|
@ -33,46 +41,68 @@ 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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
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.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
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/mdp/qrterminal/v3 v3.2.0 h1:qteQMXO3oyTK4IHwj2mWsKYYRBOp1Pj2WRYFYYNTCdk=
|
||||
github.com/mdp/qrterminal/v3 v3.2.0/go.mod h1:XGGuua4Lefrl7TLEsSONiD+UEjQXJZ4mPzF+gWYIJkk=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
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/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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.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=
|
||||
go.mau.fi/libsignal v0.1.2 h1:Vs16DXWxSKyzVtI+EEXLCSy5pVWzzCzp/2eqFGvLyP0=
|
||||
go.mau.fi/libsignal v0.1.2/go.mod h1:JpnLSSJptn/s1sv7I56uEMywvz8x4YzxeF5OzdPb6PE=
|
||||
go.mau.fi/util v0.8.6 h1:AEK13rfgtiZJL2YsNK+W4ihhYCuukcRom8WPP/w/L54=
|
||||
go.mau.fi/util v0.8.6/go.mod h1:uNB3UTXFbkpp7xL1M/WvQks90B/L4gvbLpbS0603KOE=
|
||||
go.mau.fi/whatsmeow v0.0.0-20250316144733-e7e263bf2175 h1:BDShdc10qJzi3B0xPGA6HVQl+929wIFst8/W+8EnvbI=
|
||||
go.mau.fi/whatsmeow v0.0.0-20250316144733-e7e263bf2175/go.mod h1:WNhj4JeQ6YR6dUOEiCXKqmE4LavSFkwRoKmu4atRrRs=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
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.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=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
|
|
@ -85,3 +115,5 @@ 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=
|
||||
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
||||
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
|
||||
|
|
|
|||
|
|
@ -1,72 +1,88 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
)
|
||||
|
||||
type UserHandler struct {
|
||||
UserService services.UserService
|
||||
type AuthHandler struct {
|
||||
AuthService services.AuthService
|
||||
}
|
||||
|
||||
func NewUserHandler(userService services.UserService) *UserHandler {
|
||||
return &UserHandler{UserService: userService}
|
||||
func NewAuthHandler(authService services.AuthService) *AuthHandler {
|
||||
return &AuthHandler{AuthService: authService}
|
||||
}
|
||||
|
||||
func (h *UserHandler) Login(c *fiber.Ctx) error {
|
||||
var loginDTO dto.LoginDTO
|
||||
if err := c.BodyParser(&loginDTO); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
func (h *AuthHandler) Register(c *fiber.Ctx) error {
|
||||
var request dto.RegisterRequest
|
||||
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(400).SendString("Invalid input")
|
||||
}
|
||||
|
||||
validationErrors, valid := loginDTO.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, validationErrors)
|
||||
if errors, valid := request.Validate(); !valid {
|
||||
return c.Status(400).JSON(errors)
|
||||
}
|
||||
|
||||
user, err := h.UserService.Login(loginDTO)
|
||||
_, err := h.AuthService.RegisterUser(request)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, err.Error())
|
||||
return c.Status(500).SendString(err.Error())
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, user, "Login successful")
|
||||
return c.Status(201).JSON(fiber.Map{
|
||||
"meta": fiber.Map{
|
||||
"status": 201,
|
||||
"message": "The input register from the user has been successfully recorded. Please check the otp code sent to your number.",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (h *UserHandler) Register(c *fiber.Ctx) error {
|
||||
|
||||
var registerDTO dto.RegisterDTO
|
||||
if err := c.BodyParser(®isterDTO); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid request body"}})
|
||||
func (h *AuthHandler) VerifyOTP(c *fiber.Ctx) error {
|
||||
var request struct {
|
||||
Phone string `json:"phone"`
|
||||
OTP string `json:"otp"`
|
||||
}
|
||||
|
||||
errors, valid := registerDTO.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
if err := c.BodyParser(&request); err != nil {
|
||||
return c.Status(400).SendString("Invalid input")
|
||||
}
|
||||
|
||||
userResponse, err := h.UserService.Register(registerDTO)
|
||||
err := h.AuthService.VerifyOTP(request.Phone, request.OTP)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusConflict, err.Error())
|
||||
return c.Status(400).JSON(dto.Response{
|
||||
Meta: dto.MetaResponse{
|
||||
Status: 400,
|
||||
Message: "Invalid OTP",
|
||||
},
|
||||
Data: nil,
|
||||
})
|
||||
}
|
||||
|
||||
return utils.CreateResponse(c, userResponse, "Registration successful")
|
||||
}
|
||||
|
||||
func (h *UserHandler) Logout(c *fiber.Ctx) error {
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
log.Println("Unauthorized access: User ID not found in session")
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
|
||||
}
|
||||
|
||||
err := utils.DeleteSessionData(userID)
|
||||
user, err := h.AuthService.GetUserByPhone(request.Phone)
|
||||
if err != nil {
|
||||
return utils.InternalServerErrorResponse(c, "Error logging out")
|
||||
return c.Status(500).SendString("Error retrieving user")
|
||||
}
|
||||
if user == nil {
|
||||
return c.Status(404).SendString("User not found")
|
||||
}
|
||||
|
||||
return utils.SuccessResponse(c, nil, "Logout successful")
|
||||
token, err := h.AuthService.GenerateJWT(user)
|
||||
if err != nil {
|
||||
return c.Status(500).SendString("Error generating token")
|
||||
}
|
||||
|
||||
response := dto.Response{
|
||||
Meta: dto.MetaResponse{
|
||||
Status: 200,
|
||||
Message: "OTP yang dimasukkan valid",
|
||||
},
|
||||
Data: &dto.UserDataResponse{
|
||||
UserID: user.ID,
|
||||
UserRole: user.Role.RoleName,
|
||||
Token: token,
|
||||
},
|
||||
}
|
||||
|
||||
return c.Status(200).JSON(response)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,29 +97,29 @@ func (h *UserProfileHandler) UpdateUserProfile(c *fiber.Ctx) error {
|
|||
return utils.SuccessResponse(c, userResponse, "User profile updated successfully")
|
||||
}
|
||||
|
||||
func (h *UserProfileHandler) UpdateUserPassword(c *fiber.Ctx) error {
|
||||
var passwordData dto.UpdatePasswordDTO
|
||||
if err := c.BodyParser(&passwordData); err != nil {
|
||||
return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
}
|
||||
// func (h *UserProfileHandler) UpdateUserPassword(c *fiber.Ctx) error {
|
||||
// var passwordData dto.UpdatePasswordDTO
|
||||
// if err := c.BodyParser(&passwordData); err != nil {
|
||||
// return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}})
|
||||
// }
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
if !ok || userID == "" {
|
||||
return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
|
||||
}
|
||||
// userID, ok := c.Locals("userID").(string)
|
||||
// if !ok || userID == "" {
|
||||
// return utils.GenericResponse(c, fiber.StatusUnauthorized, "Unauthorized: User session not found")
|
||||
// }
|
||||
|
||||
errors, valid := passwordData.Validate()
|
||||
if !valid {
|
||||
return utils.ValidationErrorResponse(c, errors)
|
||||
}
|
||||
// errors, valid := passwordData.Validate()
|
||||
// if !valid {
|
||||
// return utils.ValidationErrorResponse(c, errors)
|
||||
// }
|
||||
|
||||
message, err := h.UserProfileService.UpdateUserPassword(userID, passwordData)
|
||||
if err != nil {
|
||||
return utils.GenericResponse(c, fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
// message, err := h.UserProfileService.UpdateUserPassword(userID, passwordData)
|
||||
// if err != nil {
|
||||
// return utils.GenericResponse(c, fiber.StatusBadRequest, err.Error())
|
||||
// }
|
||||
|
||||
return utils.GenericResponse(c, fiber.StatusOK, message)
|
||||
}
|
||||
// return utils.GenericResponse(c, fiber.StatusOK, message)
|
||||
// }
|
||||
func (h *UserProfileHandler) UpdateUserAvatar(c *fiber.Ctx) error {
|
||||
|
||||
userID, ok := c.Locals("userID").(string)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,14 @@
|
|||
package repositories
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserRepository interface {
|
||||
FindByIdentifierAndRole(identifier, roleID string) (*model.User, error)
|
||||
FindByEmailOrUsernameOrPhone(identifier string) (*model.User, error)
|
||||
FindByUsername(username string) (*model.User, error)
|
||||
FindByPhone(phone string) (*model.User, error)
|
||||
FindByPhoneAndRole(phone, roleID string) (*model.User, error)
|
||||
FindByEmailAndRole(email, roleID string) (*model.User, error)
|
||||
|
||||
Create(user *model.User) error
|
||||
CreateUser(user *model.User) error
|
||||
}
|
||||
|
||||
type userRepository struct {
|
||||
|
|
@ -25,22 +19,14 @@ func NewUserRepository(db *gorm.DB) UserRepository {
|
|||
return &userRepository{DB: db}
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByIdentifierAndRole(identifier, roleID string) (*model.User, error) {
|
||||
func (r *userRepository) FindByPhone(phone string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.DB.Preload("Role").Where("(email = ? OR username = ? OR phone = ?) AND role_id = ?", identifier, identifier, identifier, roleID).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user.Role == nil {
|
||||
return nil, fmt.Errorf("role not found for this user")
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByUsername(username string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.DB.Where("username = ?", username).First(&user).Error
|
||||
err := r.DB.Preload("Role").Where("phone = ?", phone).First(&user).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
|
|
@ -55,28 +41,6 @@ func (r *userRepository) FindByPhoneAndRole(phone, roleID string) (*model.User,
|
|||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByEmailAndRole(email, roleID string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.DB.Where("email = ? AND role_id = ?", email, roleID).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByEmailOrUsernameOrPhone(identifier string) (*model.User, error) {
|
||||
var user model.User
|
||||
err := r.DB.Where("email = ? OR username = ? OR phone = ?", identifier, identifier, identifier).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *userRepository) Create(user *model.User) error {
|
||||
err := r.DB.Create(user).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
func (r *userRepository) CreateUser(user *model.User) error {
|
||||
return r.DB.Create(user).Error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,171 +1,134 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/dto"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
const (
|
||||
ErrUsernameTaken = "username is already taken"
|
||||
ErrPhoneTaken = "phone number is already used for this role"
|
||||
ErrEmailTaken = "email is already used for this role"
|
||||
ErrInvalidRoleID = "invalid roleId"
|
||||
ErrPasswordMismatch = "password and confirm password do not match"
|
||||
ErrRoleIDRequired = "roleId is required"
|
||||
ErrFailedToHashPassword = "failed to hash password"
|
||||
ErrFailedToCreateUser = "failed to create user"
|
||||
ErrIncorrectPassword = "incorrect password"
|
||||
ErrAccountNotFound = "account not found"
|
||||
)
|
||||
|
||||
type UserService interface {
|
||||
Login(credentials dto.LoginDTO) (*dto.UserResponseWithToken, error)
|
||||
Register(user dto.RegisterDTO) (*dto.UserResponseDTO, error)
|
||||
type AuthService interface {
|
||||
RegisterUser(request dto.RegisterRequest) (*model.User, error)
|
||||
VerifyOTP(phone, otp string) error
|
||||
GetUserByPhone(phone string) (*model.User, error)
|
||||
GenerateJWT(user *model.User) (string, error)
|
||||
}
|
||||
|
||||
type userService struct {
|
||||
UserRepo repositories.UserRepository
|
||||
RoleRepo repositories.RoleRepository
|
||||
SecretKey string
|
||||
type authService struct {
|
||||
UserRepo repositories.UserRepository
|
||||
}
|
||||
|
||||
func NewUserService(userRepo repositories.UserRepository, roleRepo repositories.RoleRepository, secretKey string) UserService {
|
||||
return &userService{UserRepo: userRepo, RoleRepo: roleRepo, SecretKey: secretKey}
|
||||
func NewAuthService(userRepo repositories.UserRepository) AuthService {
|
||||
return &authService{UserRepo: userRepo}
|
||||
}
|
||||
|
||||
func (s *userService) Login(credentials dto.LoginDTO) (*dto.UserResponseWithToken, error) {
|
||||
if credentials.RoleID == "" {
|
||||
return nil, errors.New(ErrRoleIDRequired)
|
||||
func (s *authService) RegisterUser(request dto.RegisterRequest) (*model.User, error) {
|
||||
|
||||
user, err := s.UserRepo.FindByPhone(request.Phone)
|
||||
if err == nil && user != nil {
|
||||
return nil, fmt.Errorf("user with phone %s already exists", request.Phone)
|
||||
}
|
||||
|
||||
user, err := s.UserRepo.FindByIdentifierAndRole(credentials.Identifier, credentials.RoleID)
|
||||
user = &model.User{
|
||||
Phone: request.Phone,
|
||||
RoleID: request.RoleID,
|
||||
EmailVerified: false,
|
||||
}
|
||||
|
||||
err = s.UserRepo.CreateUser(user)
|
||||
if err != nil {
|
||||
return nil, errors.New(ErrAccountNotFound)
|
||||
return nil, fmt.Errorf("failed to create user: %v", err)
|
||||
}
|
||||
|
||||
if !CheckPasswordHash(credentials.Password, user.Password) {
|
||||
return nil, errors.New(ErrIncorrectPassword)
|
||||
}
|
||||
|
||||
token, err := s.generateJWT(user)
|
||||
_, err = s.SendOTP(request.Phone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to send OTP: %v", err)
|
||||
}
|
||||
|
||||
sessionKey := fmt.Sprintf("session:%s", user.ID)
|
||||
sessionData := map[string]interface{}{
|
||||
"userID": user.ID,
|
||||
"roleID": user.RoleID,
|
||||
"roleName": user.Role.RoleName,
|
||||
}
|
||||
|
||||
err = utils.SetJSONData(sessionKey, sessionData, time.Hour*24)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dto.UserResponseWithToken{
|
||||
RoleName: user.Role.RoleName,
|
||||
UserID: user.ID,
|
||||
Token: token,
|
||||
}, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *userService) generateJWT(user *model.User) (string, error) {
|
||||
func (s *authService) GetUserByPhone(phone string) (*model.User, error) {
|
||||
user, err := s.UserRepo.FindByPhone(phone)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error retrieving user by phone: %v", err)
|
||||
}
|
||||
if user == nil {
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *authService) SendOTP(phone string) (string, error) {
|
||||
otpCode := generateOTP()
|
||||
|
||||
message := fmt.Sprintf("Your OTP code is: %s", otpCode)
|
||||
err := config.SendWhatsAppMessage(phone, message)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to send OTP via WhatsApp: %v", err)
|
||||
}
|
||||
|
||||
expirationTime := 5 * time.Minute
|
||||
err = config.RedisClient.Set(config.Ctx, phone, otpCode, expirationTime).Err()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to store OTP in Redis: %v", err)
|
||||
}
|
||||
|
||||
return otpCode, nil
|
||||
}
|
||||
|
||||
func (s *authService) VerifyOTP(phone, otp string) error {
|
||||
|
||||
otpRecord, err := config.RedisClient.Get(config.Ctx, phone).Result()
|
||||
if err == redis.Nil {
|
||||
|
||||
return fmt.Errorf("OTP not found or expired")
|
||||
} else if err != nil {
|
||||
|
||||
return fmt.Errorf("failed to retrieve OTP from Redis: %v", err)
|
||||
}
|
||||
|
||||
if otp != otpRecord {
|
||||
return fmt.Errorf("invalid OTP")
|
||||
}
|
||||
|
||||
err = config.RedisClient.Del(config.Ctx, phone).Err()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete OTP from Redis: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *authService) GenerateJWT(user *model.User) (string, error) {
|
||||
if user == nil || user.Role == nil {
|
||||
return "", fmt.Errorf("user or user role is nil, cannot generate token")
|
||||
}
|
||||
|
||||
claims := jwt.MapClaims{
|
||||
"sub": user.ID,
|
||||
"iat": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||||
"sub": user.ID,
|
||||
"role": user.Role.RoleName,
|
||||
"iat": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
tokenString, err := token.SignedString([]byte(s.SecretKey))
|
||||
secretKey := config.GetSecretKey()
|
||||
|
||||
tokenString, err := token.SignedString([]byte(secretKey))
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", fmt.Errorf("failed to generate JWT token: %v", err)
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
func CheckPasswordHash(password, hashedPassword string) bool {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (s *userService) Register(user dto.RegisterDTO) (*dto.UserResponseDTO, error) {
|
||||
|
||||
if user.Password != user.ConfirmPassword {
|
||||
return nil, fmt.Errorf("%s", ErrPasswordMismatch)
|
||||
}
|
||||
|
||||
if user.RoleID == "" {
|
||||
return nil, fmt.Errorf("%s", ErrRoleIDRequired)
|
||||
}
|
||||
|
||||
role, err := s.RoleRepo.FindByID(user.RoleID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %v", ErrInvalidRoleID, err)
|
||||
}
|
||||
|
||||
if existingUser, _ := s.UserRepo.FindByUsername(user.Username); existingUser != nil {
|
||||
return nil, fmt.Errorf("%s", ErrUsernameTaken)
|
||||
}
|
||||
|
||||
if existingPhone, _ := s.UserRepo.FindByPhoneAndRole(user.Phone, user.RoleID); existingPhone != nil {
|
||||
return nil, fmt.Errorf("%s", ErrPhoneTaken)
|
||||
}
|
||||
|
||||
if existingEmail, _ := s.UserRepo.FindByEmailAndRole(user.Email, user.RoleID); existingEmail != nil {
|
||||
return nil, fmt.Errorf("%s", ErrEmailTaken)
|
||||
}
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %v", ErrFailedToHashPassword, err)
|
||||
}
|
||||
|
||||
newUser := model.User{
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Phone: user.Phone,
|
||||
Email: user.Email,
|
||||
Password: string(hashedPassword),
|
||||
RoleID: user.RoleID,
|
||||
}
|
||||
|
||||
err = s.UserRepo.Create(&newUser)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %v", ErrFailedToCreateUser, err)
|
||||
}
|
||||
|
||||
userResponse := s.prepareUserResponse(newUser, role)
|
||||
|
||||
return userResponse, nil
|
||||
}
|
||||
|
||||
func (s *userService) prepareUserResponse(user model.User, role *model.Role) *dto.UserResponseDTO {
|
||||
|
||||
createdAt, _ := utils.FormatDateToIndonesianFormat(user.CreatedAt)
|
||||
updatedAt, _ := utils.FormatDateToIndonesianFormat(user.UpdatedAt)
|
||||
|
||||
return &dto.UserResponseDTO{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Phone: user.Phone,
|
||||
Email: user.Email,
|
||||
EmailVerified: user.EmailVerified,
|
||||
RoleName: role.RoleName,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
func generateOTP() string {
|
||||
return fmt.Sprintf("%06d", time.Now().UnixNano()%1000000)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/model"
|
||||
"github.com/pahmiudahgede/senggoldong/utils"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
// "golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var allowedExtensions = []string{".jpg", ".jpeg", ".png"}
|
||||
|
|
@ -22,7 +22,7 @@ var allowedExtensions = []string{".jpg", ".jpeg", ".png"}
|
|||
type UserProfileService interface {
|
||||
GetUserProfile(userID string) (*dto.UserResponseDTO, error)
|
||||
UpdateUserProfile(userID string, updateData dto.UpdateUserDTO) (*dto.UserResponseDTO, error)
|
||||
UpdateUserPassword(userID string, passwordData dto.UpdatePasswordDTO) (string, error)
|
||||
// UpdateUserPassword(userID string, passwordData dto.UpdatePasswordDTO) (string, error)
|
||||
UpdateUserAvatar(userID string, file *multipart.FileHeader) (string, error)
|
||||
|
||||
GetAllUsers() ([]dto.UserResponseDTO, error)
|
||||
|
|
@ -162,12 +162,12 @@ func (s *userProfileService) UpdateUserProfile(userID string, updateData dto.Upd
|
|||
user.Phone = updateData.Phone
|
||||
}
|
||||
|
||||
if updateData.Email != "" && updateData.Email != user.Email {
|
||||
if err := s.updateEmailIfNeeded(user, updateData.Email); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.Email = updateData.Email
|
||||
}
|
||||
// if updateData.Email != "" && updateData.Email != user.Email {
|
||||
// if err := s.updateEmailIfNeeded(user, updateData.Email); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// user.Email = updateData.Email
|
||||
// }
|
||||
|
||||
err = s.UserProfileRepo.Update(user)
|
||||
if err != nil {
|
||||
|
|
@ -196,43 +196,43 @@ func (s *userProfileService) updatePhoneIfNeeded(user *model.User, newPhone stri
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *userProfileService) updateEmailIfNeeded(user *model.User, newEmail string) error {
|
||||
existingEmail, _ := s.UserRepo.FindByEmailAndRole(newEmail, user.RoleID)
|
||||
if existingEmail != nil {
|
||||
return fmt.Errorf("email is already used for this role")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// func (s *userProfileService) updateEmailIfNeeded(user *model.User, newEmail string) error {
|
||||
// existingEmail, _ := s.UserRepo.FindByEmailAndRole(newEmail, user.RoleID)
|
||||
// if existingEmail != nil {
|
||||
// return fmt.Errorf("email is already used for this role")
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (s *userProfileService) UpdateUserPassword(userID string, passwordData dto.UpdatePasswordDTO) (string, error) {
|
||||
// func (s *userProfileService) UpdateUserPassword(userID string, passwordData dto.UpdatePasswordDTO) (string, error) {
|
||||
|
||||
validationErrors, valid := passwordData.Validate()
|
||||
if !valid {
|
||||
return "", fmt.Errorf("validation failed: %v", validationErrors)
|
||||
}
|
||||
// validationErrors, valid := passwordData.Validate()
|
||||
// if !valid {
|
||||
// return "", fmt.Errorf("validation failed: %v", validationErrors)
|
||||
// }
|
||||
|
||||
user, err := s.UserProfileRepo.FindByID(userID)
|
||||
if err != nil {
|
||||
return "", errors.New("user not found")
|
||||
}
|
||||
// user, err := s.UserProfileRepo.FindByID(userID)
|
||||
// if err != nil {
|
||||
// return "", errors.New("user not found")
|
||||
// }
|
||||
|
||||
if !CheckPasswordHash(passwordData.OldPassword, user.Password) {
|
||||
return "", errors.New("old password is incorrect")
|
||||
}
|
||||
// if !CheckPasswordHash(passwordData.OldPassword, user.Password) {
|
||||
// return "", errors.New("old password is incorrect")
|
||||
// }
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(passwordData.NewPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to hash new password: %v", err)
|
||||
}
|
||||
// hashedPassword, err := bcrypt.GenerateFromPassword([]byte(passwordData.NewPassword), bcrypt.DefaultCost)
|
||||
// if err != nil {
|
||||
// return "", fmt.Errorf("failed to hash new password: %v", err)
|
||||
// }
|
||||
|
||||
user.Password = string(hashedPassword)
|
||||
err = s.UserProfileRepo.Update(user)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to update password: %v", err)
|
||||
}
|
||||
// user.Password = string(hashedPassword)
|
||||
// err = s.UserProfileRepo.Update(user)
|
||||
// if err != nil {
|
||||
// return "", fmt.Errorf("failed to update password: %v", err)
|
||||
// }
|
||||
|
||||
return "Password berhasil diupdate", nil
|
||||
}
|
||||
// return "Password berhasil diupdate", nil
|
||||
// }
|
||||
|
||||
func (s *userProfileService) UpdateUserAvatar(userID string, file *multipart.FileHeader) (string, error) {
|
||||
baseURL := os.Getenv("BASE_URL")
|
||||
|
|
|
|||
|
|
@ -1,31 +1,40 @@
|
|||
package presentation
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pahmiudahgede/senggoldong/config"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/handler"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/repositories"
|
||||
"github.com/pahmiudahgede/senggoldong/internal/services"
|
||||
"github.com/pahmiudahgede/senggoldong/middleware"
|
||||
// "gorm.io/gorm"
|
||||
// "github.com/pahmiudahgede/senggoldong/middleware"
|
||||
)
|
||||
|
||||
func AuthRouter(api fiber.Router) {
|
||||
secretKey := os.Getenv("SECRET_KEY")
|
||||
if secretKey == "" {
|
||||
log.Fatal("SECRET_KEY is not set in the environment variables")
|
||||
os.Exit(1)
|
||||
}
|
||||
// userRepo := repositories.NewUserRepository(config.DB)
|
||||
// roleRepo := repositories.NewRoleRepository(config.DB)
|
||||
// userService := services.NewUserService(userRepo, roleRepo, secretKey)
|
||||
// userHandler := handler.NewUserHandler(userService)
|
||||
|
||||
// api.Post("/login", userHandler.Login)
|
||||
// api.Post("/register", userHandler.Register)
|
||||
// api.Post("/logout", middleware.AuthMiddleware, userHandler.Logout)
|
||||
// userRepo := repositories.NewUserRepository(config.DB)
|
||||
// authService := services.NewAuthService(userRepo, secretKey)
|
||||
|
||||
// // Inisialisasi handler
|
||||
// authHandler := handler.NewAuthHandler(authService)
|
||||
|
||||
// // Endpoint OTP
|
||||
// authRoutes := api.Group("/auth")
|
||||
// authRoutes.Post("/send-otp", authHandler.SendOTP)
|
||||
// authRoutes.Post("/verify-otp", authHandler.VerifyOTP)
|
||||
userRepo := repositories.NewUserRepository(config.DB)
|
||||
roleRepo := repositories.NewRoleRepository(config.DB)
|
||||
userService := services.NewUserService(userRepo, roleRepo, secretKey)
|
||||
userHandler := handler.NewUserHandler(userService)
|
||||
authService := services.NewAuthService(userRepo)
|
||||
|
||||
api.Post("/login", userHandler.Login)
|
||||
api.Post("/register", userHandler.Register)
|
||||
api.Post("/logout", middleware.AuthMiddleware, userHandler.Logout)
|
||||
authHandler := handler.NewAuthHandler(authService)
|
||||
|
||||
// Routes
|
||||
api.Post("/register", authHandler.Register)
|
||||
api.Post("/verify-otp", authHandler.VerifyOTP)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,6 @@ func UserProfileRouter(api fiber.Router) {
|
|||
userProfilRoute.Get("/:roleid", middleware.AuthMiddleware, userProfileHandler.GetUsersByRoleID)
|
||||
|
||||
userProfilRoute.Put("/update-user", middleware.AuthMiddleware, userProfileHandler.UpdateUserProfile)
|
||||
userProfilRoute.Patch("/update-user-password", middleware.AuthMiddleware, userProfileHandler.UpdateUserPassword)
|
||||
// userProfilRoute.Patch("/update-user-password", middleware.AuthMiddleware, userProfileHandler.UpdateUserPassword)
|
||||
userProfilRoute.Patch("/upload-photoprofile", middleware.AuthMiddleware, userProfileHandler.UpdateUserAvatar)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,3 +97,44 @@ func logAndReturnError(message string, err error) error {
|
|||
log.Printf("%s: %v", message, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func SetStringData(key, value string, expiration time.Duration) error {
|
||||
if expiration == 0 {
|
||||
expiration = defaultExpiration
|
||||
}
|
||||
|
||||
err := config.RedisClient.Set(ctx, key, value, expiration).Err()
|
||||
if err != nil {
|
||||
return logAndReturnError(fmt.Sprintf("Error setting string data in Redis with key: %s", key), err)
|
||||
}
|
||||
|
||||
log.Printf("String data stored in Redis with key: %s", key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetStringData(key string) (string, error) {
|
||||
val, err := config.RedisClient.Get(ctx, key).Result()
|
||||
if err == redis.Nil {
|
||||
return "", nil
|
||||
} else if err != nil {
|
||||
return "", logAndReturnError(fmt.Sprintf("Error retrieving string data from Redis with key: %s", key), err)
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func StoreOTPInRedis(phone, otpCode string, expirationTime time.Duration) error {
|
||||
err := config.RedisClient.Set(config.Ctx, phone, otpCode, expirationTime).Err()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to store OTP in Redis: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetOTPFromRedis(phone string) (string, error) {
|
||||
otpCode, err := config.RedisClient.Get(config.Ctx, phone).Result()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get OTP from Redis: %v", err)
|
||||
}
|
||||
return otpCode, nil
|
||||
}
|
||||
Loading…
Reference in New Issue