fix error at route

This commit is contained in:
Linh Tran committed 2024-06-01 19:59:44 +07:00
1 parent ed6776df74
commit ad8a9c2fa3
8 files changed
+217 -126

No files matched your search

+104 -65
View File
@@ -4,11 +4,13 @@ import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"github.com/go-mail/mail"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"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"
@@ -16,12 +18,13 @@ import (
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"
)
// CheckAndValidRegisterFiled Check the user register field is valid or not
func CheckAndValidRegisterFiled(registerData *models.CreateUser) (bool, *models.ResponseError) {
func CheckAndValidRegisterFiled(registerData *models.CreateUser, w http.ResponseWriter) (bool, *models.ResponseError) {
_ = models.Validate.RegisterValidation("customPassword", models.PasswordValidator)
errs := models.Validate.Struct(registerData)
type errStack []string
@@ -61,20 +64,25 @@ func CheckAndValidRegisterFiled(registerData *models.CreateUser) (bool, *models.
}
}
//handle repose error
return false, &models.ResponseError{Code: http.StatusTooManyRequests, Err: errorAPI}
return false, &models.ResponseError{Code: http.StatusBadRequest, Err: errorAPI}
}
return true, nil
}
// CheckAccountExist Checking in db is user input same data in
// CheckAccountExist Checking in db is user input same data in add time to resend verify mail
func CheckAccountValid(userName string, email string) (bool, *models.ResponseError) {
//filter in mongodb
var isValid bool
var isError bool
var errAPI *models.ResponseError
var dataRedisRetrive models.RedisOTP
//create context handler with timeout
timeout := 2 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
filter := bson.D{
{"$or", bson.A{
bson.D{{"username", userName}},
@@ -82,13 +90,25 @@ func CheckAccountValid(userName string, email string) (bool, *models.ResponseErr
}},
}
_, err := utils.User.FindOne(context.TODO(), filter).Raw()
cursor, err := utils.User.Find(ctx, filter)
if err != nil {
isValid = true
errAPI = nil
} else {
fmt.Println("Internal Log: Internal error")
isError = true
errAPI = &models.ResponseError{
Code: http.StatusInternalServerError,
Err: models.ErrorAPI{
Errors: []string{"Internal error"},
Message: "Internal error please try again later",
Type: "InternalError",
},
}
return isError, errAPI
}
if cursor.Next(ctx) {
fmt.Println("Internal Log: Email or username already had register")
isValid = false
isError = true
errAPI = &models.ResponseError{
Code: http.StatusBadRequest,
Err: models.ErrorAPI{
@@ -98,16 +118,34 @@ func CheckAccountValid(userName string, email string) (bool, *models.ResponseErr
},
}
}
retrievedValue, err := utils.Redis.Get(context.Background(), "otp:"+email).Result()
err = json.Unmarshal([]byte(retrievedValue), &dataRedisRetrive)
retrievedValue, err := utils.Redis.Get(ctx, "otp:"+email).Result()
if err != nil {
fmt.Println("Internal Log: Can't get data from redis")
if strings.Contains(err.Error(), "dial tcp 10.10.0.217:6391: connect: connection refused") {
fmt.Println("Internal Log: Can not connect to redis server")
isError = true
errAPI = &models.ResponseError{
Code: http.StatusInternalServerError,
Err: models.ErrorAPI{
Errors: []string{"Internal error"},
Message: "Internal error please try again later",
Type: "InternalError",
},
}
return isError, errAPI
}
if err.Error() == "redis: nil" {
fmt.Println("Internal Log: Can't get data from redis or data not exist or first time register")
}
}
err = json.Unmarshal([]byte(retrievedValue), &dataRedisRetrive)
timeDiff := time.Now().Unix() - dataRedisRetrive.CreatedDate
if timeDiff < 30 {
isValid = false
isError = true
fmt.Println("Internal Log: Rate limit send verify mail")
errAPI = &models.ResponseError{
Code: http.StatusBadRequest,
@@ -120,7 +158,7 @@ func CheckAccountValid(userName string, email string) (bool, *models.ResponseErr
}
return isValid, errAPI
return isError, errAPI
}
// GeneratorOtp Generate OTP Verify Link and Qr Link
@@ -129,30 +167,8 @@ const (
otpDigits = 6
)
func GenerateVerifyAccount(registerInfo *models.PreusersMongo, w http.ResponseWriter) error {
var wg sync.WaitGroup
otpChannel := make(chan models.OtpGenerate, 1)
mailChannel := make(chan models.MailVefiry, 1)
counter, timerCreate := 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, timerCreate)
go createMailVerify(registerInfo, otpChannel, mailChannel, w)
go writeOTPInRedis(counter, registerInfo, otpChannel, w)
wg.Wait()
err := utils.WriteJSON(w, http.StatusOK, models.SuccessAPI{
Message: "Success register account please verify your account",
})
return err
}
// CreateLinkVerify Create a alternative verify link
func createMailVerify(registerInfo *models.PreusersMongo, otpChan chan models.OtpGenerate, mailChan chan models.MailVefiry, w http.ResponseWriter) {
func CreateMailVerify(registerInfo *models.PreusersMongo, otpChan chan models.OtpGenerate, mailChan chan models.MailVefiry, w http.ResponseWriter, errChan chan error) {
smtpPass := os.Getenv("SMTP_PASS")
m := mail.NewMessage()
@@ -179,64 +195,87 @@ func createMailVerify(registerInfo *models.PreusersMongo, otpChan chan models.Ot
// 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)
errChan <- err
defer func() {
_ = os.Remove("./temp/" + qrFileName)
}()
return
}
errChan <- nil
defer func() {
_ = os.Remove("./temp/" + qrFileName)
}()
}
// check and write pre use into mongo db
func CheckAndWritePreuser(registerInfo *models.PreusersMongo, w http.ResponseWriter) (uint64, time.Time) {
// check and write pre use into mongodb
func CheckAndWritePreuser(registerInfo *models.PreusersMongo, wg *sync.WaitGroup, ctxCancel context.CancelFunc, errorChan error) (uint64, time.Time) {
//generate timestamp
timeCreate := time.Now()
registerInfo.CreatedDate = timeCreate
//create context handler with timeout
timeout := 2 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
//checking
email := registerInfo.Email
count := 1
filter := bson.D{{"email", email}}
raw, err := utils.PreUserData.FindOne(context.Background(), filter).Raw()
if err != nil {
raw, err := utils.PreUserData.FindOne(ctx, filter).Raw()
if errors.Is(err, mongo.ErrClientDisconnected) {
errorChan = errors.New("can't connect to mongodb")
ctxCancel()
wg.Done()
fmt.Println("Internal Log: Can't connect to mongodb")
}
if errors.Is(err, mongo.ErrNoDocuments) {
hashed, err := bcrypt.GenerateFromPassword([]byte(registerInfo.HashPassword), 10)
if err != nil {
errorChan = errors.New("fail to hash password")
ctxCancel()
wg.Done()
fmt.Println("Internal Log :Fail to hash password")
_ = utils.WriteJSONInternalError(w, "Fail to hash password")
}
registerInfo.HashPassword = string(hashed)
_, err = utils.PreUserData.InsertOne(context.Background(), registerInfo)
if err != nil {
errorChan = errors.New("can't insert data to PreUser")
ctxCancel()
wg.Done()
fmt.Println("Internal Log: Can't Insert Data To PreUser")
_ = utils.WriteJSONInternalError(w, "Can't Insert Data To PreUser")
}
return uint64(count), timeCreate
} else {
update := bson.D{
{"$inc", bson.D{
{"verify_sent_count", 1},
}},
{"$set", bson.D{
{"update_date", timeCreate},
},
}}
_, err := utils.PreUserData.UpdateOne(context.Background(), filter, update)
if err != nil {
fmt.Println("Internal log: Update document fail")
_ = utils.WriteJSONInternalError(w, "Update document fail")
}
fmt.Println()
count = int(raw.Lookup("verify_sent_count").Int32() + 1)
wg.Done()
return uint64(count), timeCreate
}
update := bson.D{
{"$inc", bson.D{
{"verify_sent_count", 1},
}},
{"$set", bson.D{
{"update_date", timeCreate},
},
}}
_, err = utils.PreUserData.UpdateOne(context.Background(), filter, update)
if err != nil {
errorChan = errors.New("update document fail")
ctxCancel()
wg.Done()
fmt.Println("Internal log: Update document fail")
}
count = int(raw.Lookup("verify_sent_count").Int32() + 1)
wg.Done()
return uint64(count), timeCreate
}
//write otp in redis for valid if otp expired
func writeOTPInRedis(counter uint64, registerInfo *models.PreusersMongo, otpChan chan models.OtpGenerate, w http.ResponseWriter) {
func WriteOTPInRedis(counter uint64, registerInfo *models.PreusersMongo, otpChan chan models.OtpGenerate, w http.ResponseWriter) {
otp := <-otpChan