diff --git a/.env b/.env
index 7de2f47..70ce67d 100644
--- a/.env
+++ b/.env
@@ -1,5 +1,5 @@
MONGO_URI="mongodb://adminLinh:linhporo1@localhost:27017"
API_TEST_PORT=":8080"
REDIS_URI="redis://localhost:6379/0"
-STMP_PASS="btmp judz ebys pfxw"
+SMTP_PASS="btmp judz ebys pfxw"
EMAIL_VERIFY_SECRET="WDc&4+&vYP(n'}?LHNE#5M?IE|g(c812"
diff --git a/Template/email.html b/Template/email.html
index 8dbd674..d227ca7 100644
--- a/Template/email.html
+++ b/Template/email.html
@@ -235,6 +235,14 @@
instead.
+
+

+
diff --git a/models/models.go b/models/models.go
index 47d14eb..ea58cfc 100644
--- a/models/models.go
+++ b/models/models.go
@@ -3,6 +3,7 @@ package models
import (
"github.com/go-playground/validator/v10"
"go.mongodb.org/mongo-driver/bson/primitive"
+ "html/template"
"strings"
"time"
"unicode"
@@ -81,12 +82,18 @@ func PasswordValidator(fl validator.FieldLevel) bool {
//Interal Type
-type EmailTemplate struct {
- Otp string `json:"otp"`
- AlternativeLink string `json:"alternativeLink"`
-}
-
type OtpGenerate struct {
PureOTP string
HashOTP string
}
+
+type MailVefiry struct {
+ LinkMail string
+ ImageBase64 string
+}
+
+type EmailTemplate struct {
+ Otp string `json:"otp"`
+ AlternativeLink string `json:"alternativeLink"`
+ QrCode template.URL
+}
diff --git a/rest-api/init-api.go b/rest-api/init-api.go
index 7a159e3..db07dfa 100644
--- a/rest-api/init-api.go
+++ b/rest-api/init-api.go
@@ -5,7 +5,6 @@ import (
"github.com/gorilla/mux"
"linhdevtran99/rest-api/models"
"linhdevtran99/rest-api/rest-api/routes"
- "linhdevtran99/rest-api/rest-api/services"
"linhdevtran99/rest-api/utils"
"log"
"net/http"
@@ -45,22 +44,6 @@ func (s *APIServer) TestRoute(w http.ResponseWriter, r *http.Request) error {
if r.Method == http.MethodGet {
fmt.Println("hit")
- //m := mail.NewMessage()
- //
- //emailBody := utils.BuildEmail()
- //m.SetHeader("From", "kotomi.poro1@gmail.com")
- //m.SetHeader("To", "nhocdl.poro1@gmail.com")
- //m.SetHeader("Subject", "Hello!")
- //m.SetBody("text/html", emailBody)
- //
- //d := mail.NewDialer("smtp.gmail.com", 587, "kotomi.poro1@gmail.com", "btmpjudzebyspfxw")
- //d.StartTLSPolicy = mail.MandatoryStartTLS
- //
- //// Send the email to Bob, Cora and Dan.
- //if err := d.DialAndSend(m); err != nil {
- // panic(err)
- //}
-
//serect := os.Getenv("EMAIL_VERIFY_SECRET")
//_, otp := services.GeneratorOtp("hello", "nhocdl.poro1@gmail.com", 12, serect)
@@ -68,23 +51,19 @@ func (s *APIServer) TestRoute(w http.ResponseWriter, r *http.Request) error {
//fmt.Println(otp.PureOTP)
//utils.EncryptAESMailLink("nhocdl.poro2@gmail.com", serect, w)
-
- //jsonData, _ := json.Marshal(map[string]string{"email": "nhocdl.poro1@gmail.com", "user": "thewind121212"})
- ////
- //utils.Redis.Set(context.Background(), "otp:nhocdl.poro1@gmail.com", string(jsonData), time.Minute)
- //
- //utils.CheckAndWriteRedis("nhocdl.poro1@gmail.com", "thewind121212", "lasdjflasdjlfj")
-
- services.CheckAndWritePreuser(&models.PreusersMongo{
+ preUserData := &models.PreusersMongo{
Username: "thewind121212",
- Email: "nhocdl.poro2@gmail.com",
+ Email: "nhocdl.poro1@gmail.com",
PhoneNumber: "0918327132",
- VerifySentCount: 0,
+ HashPassword: "it ok now ",
CreatedDate: time.Now(),
UpdateDate: time.Now(),
- })
+ VerifySentCount: 1,
+ }
- //
+ _ = preUserData
+
+ //services.WriteOTPInRedis(preUserData, "tranduy linh ", w)
}
diff --git a/rest-api/routes/auth.go b/rest-api/routes/auth.go
index a18e996..1bf4ec3 100644
--- a/rest-api/routes/auth.go
+++ b/rest-api/routes/auth.go
@@ -21,7 +21,7 @@ func RegisterNewAccount(w http.ResponseWriter, r *http.Request) error {
return utils.WriteJSON(w, responseAPI.Code, responseAPI.Err.Error())
}
//call function check data user use in past or not
- isValidData, responseAPI := services.CheckAccountExist(utils.MongoDB, registerInfo.Username, registerInfo.Email)
+ isValidData, responseAPI := services.CheckAccountExist(registerInfo.Username, registerInfo.Email)
if responseAPI != nil {
return utils.WriteJSON(w, responseAPI.Code, responseAPI.Err.Error())
}
@@ -36,7 +36,7 @@ func RegisterNewAccount(w http.ResponseWriter, r *http.Request) error {
VerifySentCount: 1,
}
- services.CheckAndWritePreuser(preUserData)
+ services.GenerateVerifyAccount(preUserData, w)
//debug
if isValidData == true || validRegisterInfo == true {
diff --git a/rest-api/services/register.go b/rest-api/services/register.go
index 4fe5e57..86df1e1 100644
--- a/rest-api/services/register.go
+++ b/rest-api/services/register.go
@@ -2,19 +2,20 @@ package services
import (
"context"
- "encoding/base32"
+ "encoding/base64"
"encoding/json"
"errors"
"fmt"
+ "github.com/go-mail/mail"
"github.com/go-playground/validator/v10"
- "github.com/pquerna/otp"
- "github.com/pquerna/otp/hotp"
"go.mongodb.org/mongo-driver/bson"
- "go.mongodb.org/mongo-driver/mongo"
"golang.org/x/crypto/bcrypt"
"linhdevtran99/rest-api/models"
"linhdevtran99/rest-api/utils"
+ "log"
"net/http"
+ "os"
+ "sync"
"time"
)
@@ -68,7 +69,7 @@ func CheckAndValidRegisterFiled(registerData *models.CreateUser) (bool, *Respons
}
// CheckAccountExist Checking in db is user input same data in
-func CheckAccountExist(mongoClient *mongo.Client, userName string, email string) (bool, *ResponseError) {
+func CheckAccountExist(userName string, email string) (bool, *ResponseError) {
//filter in mongodb
var isValid bool
var errAPI *ResponseError
@@ -80,7 +81,7 @@ func CheckAccountExist(mongoClient *mongo.Client, userName string, email string)
}},
}
- _, err := utils.PreUserData.FindOne(context.TODO(), filter).Raw()
+ _, err := utils.User.FindOne(context.TODO(), filter).Raw()
if err != nil {
isValid = true
errAPI = nil
@@ -96,55 +97,89 @@ func CheckAccountExist(mongoClient *mongo.Client, userName string, email string)
return isValid, errAPI
}
-// GeneratorOtp Generate HOtp for confirm information
+// GeneratorOtp Generate OTP Verify Link and Qr Link
-func GeneratorOtp(userName string, email string, counter uint64, serect string) (bool, *models.OtpGenerate) {
+const (
+ otpDigits = 6
+ mailValidTime = time.Hour * 24
+)
- serectBase32 := base32.StdEncoding.EncodeToString([]byte(serect + userName + email))
- passCode, err := hotp.GenerateCodeCustom(serectBase32, counter, hotp.ValidateOpts{
- Digits: 6,
- Algorithm: otp.AlgorithmSHA256,
- })
+func GenerateVerifyAccount(registerInfo *models.PreusersMongo, w http.ResponseWriter) {
+ var wg sync.WaitGroup
- if err != nil {
- fmt.Println("Fail to create OTP")
- return false, nil
- }
+ otpChannel := make(chan models.OtpGenerate, 1)
+ mailChannel := make(chan models.MailVefiry, 1)
- hashed, err := bcrypt.GenerateFromPassword([]byte(passCode), 5)
- if err != nil {
- fmt.Println("Fail to create OTP")
- return false, nil
- }
+ counter := CheckAndWritePreuser(registerInfo, w)
+ wg.Add(1)
+ go utils.GenOTP(registerInfo, counter, otpDigits, w, otpChannel, &wg)
+ wg.Add(1)
+ go utils.EncryptAESMailLink(registerInfo, w, mailChannel, &wg)
+ go createMailVerify(registerInfo, otpChannel, mailChannel, w)
+ go writeOTPInRedis(registerInfo, otpChannel, w)
+
+ wg.Wait()
- return true, &models.OtpGenerate{
- PureOTP: passCode,
- HashOTP: string(hashed),
- }
}
// CreateLinkVerify Create a alternative verify link
-func CreateLinkVerify(registerInfo string, secrect string) string {
- return "linh"
+func createMailVerify(registerInfo *models.PreusersMongo, otpChan chan models.OtpGenerate, mailChan chan models.MailVefiry, w http.ResponseWriter) {
+ smtpPass := os.Getenv("SMTP_PASS")
+ m := mail.NewMessage()
+ opt := <-otpChan
+ mailVerify := <-mailChan
+
+ decodedImage, err := base64.StdEncoding.DecodeString(mailVerify.ImageBase64)
+ if err != nil {
+ log.Println("Error decoding base64 image:", err)
+ }
+
+ qrFileName := registerInfo.Email + registerInfo.Username + ".png"
+ os.WriteFile("./temp/"+qrFileName, decodedImage, 0666)
+ emailBody := utils.BuildEmail(opt.PureOTP, mailVerify.LinkMail, qrFileName)
+
+ m.SetHeader("From", "kotomi.poro1@gmail.com")
+ m.SetHeader("To", registerInfo.Email)
+ m.SetHeader("Subject", "Thanks For Join My Business")
+ m.SetBody("text/html", emailBody)
+ m.Embed("./temp/" + qrFileName)
+
+ d := mail.NewDialer("smtp.gmail.com", 587, "kotomi.poro1@gmail.com", smtpPass)
+ d.StartTLSPolicy = mail.MandatoryStartTLS
+
+ // Send the email to Bob, Cora and Dan.
+ if err := d.DialAndSend(m); err != nil {
+ fmt.Println("Internal Log: Fail to send email check smtp")
+ _ = utils.WriteJSONInternalError(w, "Fail to send email check smtp")
+ panic(err)
+ }
+
+ defer func() {
+ os.Remove("./temp/" + qrFileName)
+ }()
}
// check and write pre use into mongo db
-func CheckAndWritePreuser(registerInfo *models.PreusersMongo) {
+func CheckAndWritePreuser(registerInfo *models.PreusersMongo, w http.ResponseWriter) uint64 {
//checking
email := registerInfo.Email
+ count := 1
filter := bson.D{{"email", email}}
- _, err := utils.PreUserData.FindOne(context.Background(), filter).Raw()
+ raw, err := utils.PreUserData.FindOne(context.Background(), filter).Raw()
if err != nil {
hashed, err := bcrypt.GenerateFromPassword([]byte(registerInfo.HashPassword), 10)
if err != nil {
- fmt.Println("Fail to create OTP")
+ fmt.Println("Internal Log :Fail to create OTP")
+ _ = utils.WriteJSONInternalError(w, "Fail to create OTP")
}
registerInfo.HashPassword = string(hashed)
_, err = utils.PreUserData.InsertOne(context.Background(), registerInfo)
if err != nil {
- fmt.Println("Something Went Wrong")
+ fmt.Println("Internal Log: Can't Insert Data To PreUser")
+ _ = utils.WriteJSONInternalError(w, "Can't Insert Data To PreUser")
}
+ return uint64(count)
} else {
update := bson.D{
{"$inc", bson.D{
@@ -157,9 +192,36 @@ func CheckAndWritePreuser(registerInfo *models.PreusersMongo) {
_, err := utils.PreUserData.UpdateOne(context.Background(), filter, update)
if err != nil {
- fmt.Println("Internal log: update document fail")
+ fmt.Println("Internal log: Update document fail")
+ _ = utils.WriteJSONInternalError(w, "Update document fail")
}
-
+ fmt.Println()
+ count = int(raw.Lookup("verify_sent_count").Int32() + 1)
+ return uint64(count)
}
}
+
+//write otp in redis for valid if otp expired
+
+func writeOTPInRedis(registerInfo *models.PreusersMongo, otpChan chan models.OtpGenerate, w http.ResponseWriter) {
+
+ otp := <-otpChan
+
+ data := map[string]string{
+ "email": registerInfo.Email,
+ "user": registerInfo.Username,
+ "create_date": registerInfo.CreatedDate.String(),
+ "hashOTP": otp.HashOTP,
+ }
+
+ jsonData, err := json.Marshal(data)
+ if err != nil {
+ fmt.Println("Internal log: Can't stringfy json data")
+ _ = utils.WriteJSONInternalError(w, "Can't stringfy json data")
+ }
+ status := utils.Redis.Set(context.Background(), "otp:nhocdl.poro1@gmail.com", string(jsonData), time.Minute*2)
+
+ fmt.Println(status.Err())
+
+}
diff --git a/utils/crypto.go b/utils/crypto.go
index 1ba2527..527c9c7 100644
--- a/utils/crypto.go
+++ b/utils/crypto.go
@@ -1,15 +1,26 @@
package utils
import (
+ "encoding/base32"
"encoding/base64"
"fmt"
"github.com/golang-jwt/jwt/v5"
+ "github.com/pquerna/otp"
+ "github.com/pquerna/otp/hotp"
"github.com/skip2/go-qrcode"
+ "golang.org/x/crypto/bcrypt"
+ "linhdevtran99/rest-api/models"
"net/http"
+ "os"
"strings"
+ "sync"
"time"
)
+//const
+
+var serect = os.Getenv("EMAIL_VERIFY_SECRET")
+
//Mail
type MyCustomClaims struct {
@@ -20,21 +31,19 @@ type MyCustomClaims struct {
func buildTokenLink(token string, w http.ResponseWriter) (string, string) {
tokenCustomTrim := strings.ReplaceAll(token, ".", "&")
emailVerifyLink := "http://www.totoday.com/?p=" + tokenCustomTrim
- fmt.Println(emailVerifyLink)
png, err := qrcode.Encode(emailVerifyLink, qrcode.Low, 200)
if err != nil {
fmt.Println("Internal log: error create qr ")
_ = WriteJSONInternalError(w, "error create QR code")
}
base64Image := base64.StdEncoding.EncodeToString(png)
- dataURL := "data:image/png;base64," + base64Image
- return emailVerifyLink, dataURL
+ return emailVerifyLink, base64Image
}
-func EncryptAESMailLink(data string, key string, w http.ResponseWriter) (string, string) {
+func EncryptAESMailLink(registerInfo *models.PreusersMongo, w http.ResponseWriter, mailChan chan models.MailVefiry, wg *sync.WaitGroup) chan models.MailVefiry {
claims := MyCustomClaims{
- data,
+ registerInfo.Email,
jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
@@ -46,13 +55,24 @@ func EncryptAESMailLink(data string, key string, w http.ResponseWriter) (string,
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
token.Header["purpose"] = "Email_Verify"
- tokenString, err := token.SignedString([]byte(key))
+ tokenString, err := token.SignedString([]byte(serect))
if err != nil {
fmt.Println("Internal Log: Error signed token fail", err.Error())
_ = WriteJSONInternalError(w, "Error signing token fail")
}
- return buildTokenLink(tokenString, w)
+ linkMail, imageB64 := buildTokenLink(tokenString, w)
+
+ data := models.MailVefiry{
+ LinkMail: linkMail,
+ ImageBase64: imageB64,
+ }
+
+ mailChan <- data
+
+ wg.Done()
+ return mailChan
+
}
//
@@ -60,24 +80,34 @@ func EncryptAESMailLink(data string, key string, w http.ResponseWriter) (string,
//}
//OTP
-//Write preuser to mongo db
-//func WriteToMongo(registerInfo *models.PreusersMongo) {
-//
-//
-//}
+func GenOTP(registerInfo *models.PreusersMongo, counter uint64, otpDigits int, w http.ResponseWriter, otpChan chan models.OtpGenerate, wg *sync.WaitGroup) chan models.OtpGenerate {
-//Write otp to redis
+ serectBase32 := base32.StdEncoding.EncodeToString([]byte(serect + registerInfo.Username + registerInfo.Email))
+ passCode, err := hotp.GenerateCodeCustom(serectBase32, counter, hotp.ValidateOpts{
+ Digits: otp.Digits(otpDigits),
+ Algorithm: otp.AlgorithmSHA256,
+ })
-//func CheckAndWriteRedis(email string, username string, hashOTP string) {
-// //checking does it valid or have in redis or not
-// //var count int
-// exists, err := Redis.Exists(context.Background(), "otp:nhocdl.poro1@gmail.com").Result()
-// if err != nil {
-// fmt.Println("something went wrong")
-// }
-// if exists == 0 {
-// //count = 0
-//
-// }
-//}
+ if err != nil {
+ fmt.Println("Internal log: Fail to create OTP")
+ _ = WriteJSONInternalError(w, "Fail to create OTP")
+ }
+
+ hashed, err := bcrypt.GenerateFromPassword([]byte(passCode), 5)
+ if err != nil {
+ fmt.Println("Internal log: Fail to encrypt OTP")
+ _ = WriteJSONInternalError(w, "Fail to encrypt OTP")
+ }
+
+ data := models.OtpGenerate{
+ PureOTP: passCode,
+ HashOTP: string(hashed),
+ }
+
+ otpChan <- data
+ otpChan <- data
+ wg.Done()
+ return otpChan
+
+}
diff --git a/utils/mail-template.go b/utils/mail-template.go
index 1062835..1826cd9 100644
--- a/utils/mail-template.go
+++ b/utils/mail-template.go
@@ -7,13 +7,17 @@ import (
"linhdevtran99/rest-api/models"
)
-func BuildEmail() string {
+func BuildEmail(otp string, mailLink string, fileName string) string {
+
+ qrcodeURL := template.URL("cid:" + fileName)
data := models.EmailTemplate{
- Otp: "11017",
- AlternativeLink: "https://www.google.com.vn",
+ Otp: otp,
+ AlternativeLink: mailLink,
+ QrCode: qrcodeURL,
}
+ //tmpl, err := template.ParseFiles("./Template/email.html")
tmpl, err := template.ParseFiles("./Template/email.html")
if err != nil {
diff --git a/utils/mongodb.go b/utils/mongodb.go
index 1bccfea..b54dfd8 100644
--- a/utils/mongodb.go
+++ b/utils/mongodb.go
@@ -12,6 +12,7 @@ import (
var MongoDB *mongo.Client
var PreUserData *mongo.Collection
+var User *mongo.Collection
func InitMongoDriver(mongoUri string) *mongo.Client {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
@@ -32,6 +33,7 @@ func InitMongoDriver(mongoUri string) *mongo.Client {
MongoDB = client
PreUserData = MongoDB.Database("Totoday-shop").Collection("preusers")
+ User = MongoDB.Database("Totoday-shop").Collection("users")
return client
}