From 099274318fb659a74f85c20f5df6083384b62566 Mon Sep 17 00:00:00 2001 From: Suraj Mahto Date: Mon, 9 Sep 2024 19:13:53 +0530 Subject: [PATCH] feat : added mail verification --- controllers/send_otp_handler_controller.go | 33 ++++++++++++++ controllers/verify_otp_handler_controller.go | 26 +++++++++++ models/otpstore.go | 45 ++++++++++++++++++++ server/server.go | 4 ++ utils/generate_otp.go | 17 ++++++++ utils/send_otp.go | 19 +++++++++ 6 files changed, 144 insertions(+) create mode 100644 controllers/send_otp_handler_controller.go create mode 100644 controllers/verify_otp_handler_controller.go create mode 100644 models/otpstore.go create mode 100644 utils/generate_otp.go create mode 100644 utils/send_otp.go diff --git a/controllers/send_otp_handler_controller.go b/controllers/send_otp_handler_controller.go new file mode 100644 index 0000000..6bb9256 --- /dev/null +++ b/controllers/send_otp_handler_controller.go @@ -0,0 +1,33 @@ +package controllers + +import ( + "net/http" + "app.myriadflow.com/utils" + "app.myriadflow.com/models" + "github.com/gin-gonic/gin" +) + +func SendOTPHandler(c *gin.Context) { + var request struct { + Email string `json:"email"` + } + + if err := c.BindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"}) + return + } + + // Generate OTP and expiration time + otp, expiration := utils.GenerateOTP() + + // Send OTP via email + if err := utils.SendOTP(request.Email, otp); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err}) + return + } + + // Save OTP and expiration in the store (model) + models.SaveOTP(request.Email, otp, expiration) + + c.JSON(http.StatusOK, gin.H{"message": "OTP sent successfully"}) +} diff --git a/controllers/verify_otp_handler_controller.go b/controllers/verify_otp_handler_controller.go new file mode 100644 index 0000000..e4dd440 --- /dev/null +++ b/controllers/verify_otp_handler_controller.go @@ -0,0 +1,26 @@ +package controllers + +import ( + "net/http" + "app.myriadflow.com/models" + "github.com/gin-gonic/gin" +) + +func VerifyOTPHandler(c *gin.Context) { + var request struct { + Email string `json:"email"` + OTP string `json:"otp"` + } + + if err := c.BindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"}) + return + } + + // Verify OTP from the store (model) + if models.VerifyOTP(request.Email, request.OTP) { + c.JSON(http.StatusOK, gin.H{"message": "OTP verified successfully"}) + } else { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired OTP"}) + } +} diff --git a/models/otpstore.go b/models/otpstore.go new file mode 100644 index 0000000..8276e37 --- /dev/null +++ b/models/otpstore.go @@ -0,0 +1,45 @@ +package models + +import ( + "sync" + "time" +) + +type OTPData struct { + otp string + expiration time.Time +} + +type OTPStore struct { + data map[string]OTPData + mu sync.RWMutex +} + +var store = OTPStore{ + data: make(map[string]OTPData), +} + +func SaveOTP(email, otp string, expiration time.Time) { + store.mu.Lock() + defer store.mu.Unlock() + store.data[email] = OTPData{ + otp: otp, + expiration: expiration, + } +} + +func VerifyOTP(email, otp string) bool { + store.mu.RLock() + defer store.mu.RUnlock() + + // Fetch the stored OTP data for the email + data, exists := store.data[email] + + // Return false if the OTP doesn't exist or if it has expired + if !exists || time.Now().After(data.expiration) { + return false + } + + // Return true if the OTP matches + return data.otp == otp +} diff --git a/server/server.go b/server/server.go index 5ff8c90..eeb12ee 100644 --- a/server/server.go +++ b/server/server.go @@ -134,6 +134,10 @@ func Routes(r *gin.Engine) { r.DELETE("/cart/:wallet_address/:phygital_id", controllers.RemoveFromCart) r.GET("/cart/:wallet_address", controllers.GetCartItems) + // OTP routes + r.POST("/send-otp", controllers.SendOTPHandler) + r.POST("/verify-otp", controllers.VerifyOTPHandler) + } diff --git a/utils/generate_otp.go b/utils/generate_otp.go new file mode 100644 index 0000000..79af2b1 --- /dev/null +++ b/utils/generate_otp.go @@ -0,0 +1,17 @@ +package utils + +import ( + "crypto/rand" + "time" +) + +func GenerateOTP() (string, time.Time) { + otp := make([]byte, 6) + rand.Read(otp) + for i := range otp { + otp[i] = (otp[i] % 10) + '0' // generates numbers 0-9 + } + // OTP valid for 5 minutes + expiration := time.Now().Add(5 * time.Minute) + return string(otp), expiration +} diff --git a/utils/send_otp.go b/utils/send_otp.go new file mode 100644 index 0000000..9b7305f --- /dev/null +++ b/utils/send_otp.go @@ -0,0 +1,19 @@ +package utils + +import ( + "fmt" + "net/smtp" +) + +func SendOTP(email, otp string) error { + from := "contact@myriadflow.com" + // password := os.Getenv("SMTP_PASSWORD") + password := "" + smtpHost := "smtp.zoho.com" + smtpPort := "587" + + msg := []byte(fmt.Sprintf("Subject: OTP Verification\n\nYour OTP is: %s ", otp)) + + auth := smtp.PlainAuth("", from, password, smtpHost) + return smtp.SendMail(smtpHost+":"+smtpPort, auth, from, []string{email}, msg) +}