Mhamdans17 commited on
Commit
c587f34
·
1 Parent(s): f086228

feat: implement ForgotPassword and ResetPassword routes using WhatsApp OTP

Browse files
Files changed (2) hide show
  1. controllers/auth_controller.go +107 -0
  2. main.go +2 -0
controllers/auth_controller.go CHANGED
@@ -97,6 +97,16 @@ type ResendOTPRequest struct {
97
  Phone string `json:"phone" binding:"required"`
98
  }
99
 
 
 
 
 
 
 
 
 
 
 
100
  type UpdateUserRequest struct {
101
  Name string `json:"name"`
102
  Email string `json:"email"`
@@ -380,6 +390,103 @@ func ResendOTP(c *gin.Context) {
380
  })
381
  }
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  func GetMe(c *gin.Context) {
384
  userVal, _ := c.Get("user")
385
  user := userVal.(*models.User)
 
97
  Phone string `json:"phone" binding:"required"`
98
  }
99
 
100
+ type ForgotPasswordRequest struct {
101
+ Phone string `json:"phone" binding:"required"`
102
+ }
103
+
104
+ type ResetPasswordRequest struct {
105
+ Phone string `json:"phone" binding:"required"`
106
+ OTPCode string `json:"otp_code" binding:"required"`
107
+ NewPassword string `json:"new_password" binding:"required"`
108
+ }
109
+
110
  type UpdateUserRequest struct {
111
  Name string `json:"name"`
112
  Email string `json:"email"`
 
390
  })
391
  }
392
 
393
+ func ForgotPassword(c *gin.Context) {
394
+ var req ForgotPasswordRequest
395
+ if err := c.ShouldBindJSON(&req); err != nil {
396
+ c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
397
+ return
398
+ }
399
+
400
+ // Cari user berdasarkan nomor telepon (OwnerPhone atau Phone)
401
+ var user models.User
402
+ if err := config.DB.Where("phone = ?", req.Phone).First(&user).Error; err != nil {
403
+ c.JSON(http.StatusNotFound, gin.H{"detail": "Nomor WhatsApp tidak terdaftar"})
404
+ return
405
+ }
406
+
407
+ // Generate OTP 6 digit
408
+ code := fmt.Sprintf("%06d", rand.Intn(900000)+100000)
409
+
410
+ // Simpan ke database otps
411
+ otpEntry := models.OTP{
412
+ PhoneNumber: req.Phone,
413
+ OTPCode: code,
414
+ IsVerified: false,
415
+ ExpiresAt: time.Now().Add(10 * time.Minute), // Kadaluarsa dalam 10 menit
416
+ }
417
+ if err := config.DB.Create(&otpEntry).Error; err != nil {
418
+ c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal membuat OTP reset password"})
419
+ return
420
+ }
421
+
422
+ // Kirim Notifikasi OTP Reset Password via WA
423
+ go services.SendOtpWa(req.Phone, code)
424
+
425
+ c.JSON(http.StatusOK, gin.H{
426
+ "status": "success",
427
+ "message": "Kode OTP reset password telah dikirim ke WhatsApp Anda",
428
+ })
429
+ }
430
+
431
+ func ResetPassword(c *gin.Context) {
432
+ var req ResetPasswordRequest
433
+ if err := c.ShouldBindJSON(&req); err != nil {
434
+ c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
435
+ return
436
+ }
437
+
438
+ // Validasi OTP
439
+ var record models.OTP
440
+ if err := config.DB.Where("phone_number = ? AND otp_code = ? AND is_verified = ?", req.Phone, req.OTPCode, false).Order("id desc").First(&record).Error; err != nil {
441
+ c.JSON(http.StatusBadRequest, gin.H{"detail": "Kode OTP salah atau tidak ditemukan"})
442
+ return
443
+ }
444
+
445
+ if record.ExpiresAt.Before(time.Now()) {
446
+ c.JSON(http.StatusBadRequest, gin.H{"detail": "Kode OTP sudah kadaluarsa"})
447
+ return
448
+ }
449
+
450
+ // Temukan user
451
+ var user models.User
452
+ if err := config.DB.Where("phone = ?", req.Phone).First(&user).Error; err != nil {
453
+ c.JSON(http.StatusNotFound, gin.H{"detail": "User tidak ditemukan"})
454
+ return
455
+ }
456
+
457
+ // Hash password baru
458
+ hashed, err := middleware.HashPassword(req.NewPassword)
459
+ if err != nil {
460
+ c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal enkripsi password"})
461
+ return
462
+ }
463
+
464
+ // Mulai Transaksi Database untuk memastikan data konsisten
465
+ tx := config.DB.Begin()
466
+
467
+ // Simpan password baru ke user
468
+ if err := tx.Model(&user).Update("password", hashed).Error; err != nil {
469
+ tx.Rollback()
470
+ c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal mereset password"})
471
+ return
472
+ }
473
+
474
+ // Tandai OTP telah digunakan
475
+ record.IsVerified = true
476
+ if err := tx.Save(&record).Error; err != nil {
477
+ tx.Rollback()
478
+ c.JSON(http.StatusInternalServerError, gin.H{"detail": "Gagal memperbarui status OTP"})
479
+ return
480
+ }
481
+
482
+ tx.Commit()
483
+
484
+ c.JSON(http.StatusOK, gin.H{
485
+ "status": "success",
486
+ "message": "Password Anda berhasil diperbarui. Silakan login kembali dengan password baru Anda.",
487
+ })
488
+ }
489
+
490
  func GetMe(c *gin.Context) {
491
  userVal, _ := c.Get("user")
492
  user := userVal.(*models.User)
main.go CHANGED
@@ -76,6 +76,8 @@ func main() {
76
  auth.POST("/register-tenant", controllers.RegisterTenant)
77
  auth.POST("/verify-otp", controllers.VerifyOTP)
78
  auth.POST("/resend-otp", controllers.ResendOTP)
 
 
79
 
80
  // Authenticated Auth Routes
81
  auth.Use(middleware.GetCurrentUser())
 
76
  auth.POST("/register-tenant", controllers.RegisterTenant)
77
  auth.POST("/verify-otp", controllers.VerifyOTP)
78
  auth.POST("/resend-otp", controllers.ResendOTP)
79
+ auth.POST("/forgot-password", controllers.ForgotPassword)
80
+ auth.POST("/reset-password", controllers.ResetPassword)
81
 
82
  // Authenticated Auth Routes
83
  auth.Use(middleware.GetCurrentUser())