fix error at route

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

2
.idea/.gitignore vendored
View File

@ -6,3 +6,5 @@
# Datasource local storage ignored files # Datasource local storage ignored files
/dataSources/ /dataSources/
/dataSources.local.xml /dataSources.local.xml
# GitHub Copilot persisted chat sessions
/copilot/chatSessions

View File

@ -1 +1,100 @@
package controler package controler
import (
"context"
"encoding/json"
"fmt"
"linhdevtran99/rest-api/models"
"linhdevtran99/rest-api/rest-api/services"
"linhdevtran99/rest-api/utils"
"net/http"
"sync"
"time"
)
func RegisterNewAccount(w http.ResponseWriter, r *http.Request) error {
if r.Method == http.MethodPost {
var registerInfo models.CreateUser
// get data from body
_ = json.NewDecoder(r.Body).Decode(&registerInfo)
//call function check info user type in
_, responseAPI := services.CheckAndValidRegisterFiled(&registerInfo, w)
//[fairy ok]
if responseAPI != nil {
return utils.WriteJSON(w, responseAPI.Code, responseAPI.Err)
}
//call function check data user use in past or not
isError, responseAPI := services.CheckAccountValid(registerInfo.Username, registerInfo.Email)
if isError == true {
return utils.WriteJSON(w, responseAPI.Code, responseAPI.Err)
}
preUserData := &models.PreusersMongo{
Username: registerInfo.Username,
Email: registerInfo.Email,
PhoneNumber: registerInfo.PhoneNumber,
HashPassword: registerInfo.Password,
UUID: registerInfo.UUID,
CreatedDate: time.Now(),
UpdateDate: time.Now(),
VerifySentCount: 1,
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var wg sync.WaitGroup
//otpChannel := make(chan models.OtpGenerate, 1)
//mailChannel := make(chan models.MailVefiry, 1)
done := make(chan struct{})
var pipelineError error
wg.Add(1)
counter, timerCreate := services.CheckAndWritePreuser(preUserData, &wg, cancel, pipelineError)
fmt.Println("counter", counter)
fmt.Println("timerCreate", timerCreate)
time.Sleep(20 * time.Second)
cancel()
//wg.Add(1)
//go utils.GenOTP(preUserData, counter, 6, w, otpChannel, pipelineError, cancel, &wg)
//wg.Add(1)
////go utils.EncryptAESMailLink(preUserData, w, mailChannel, &wg, timerCreate)
////wg.Add(1)
////go services.CreateMailVerify(preUserData, otpChannel, mailChannel, w, errChan)
////go services.WriteOTPInRedis(counter, preUserData, otpChannel, w)
////wg.Done()
////err := <-errChan
////close(errChan)
////if err != nil {
//// fmt.Println("Internal Log: Fail to send email check smtp")
//// return err
////}
////if err == nil {
//// _ = utils.WriteJSON(w, http.StatusOK, models.SuccessAPI{
//// Message: "Success register account please verify your account",
//// })
////}
////
wg.Wait()
////create dummy error
//err = fmt.Errorf("dummy error")
select {
case <-done:
// Error occurred, wait for all goroutines to finish
wg.Wait()
case <-ctx.Done():
// Context cancelled, wait for all goroutines to finish
wg.Wait()
}
return nil
}
return nil
}

View File

@ -6,62 +6,12 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"linhdevtran99/rest-api/models" "linhdevtran99/rest-api/models"
"linhdevtran99/rest-api/rest-api/controler"
"linhdevtran99/rest-api/rest-api/services" "linhdevtran99/rest-api/rest-api/services"
"linhdevtran99/rest-api/utils" "linhdevtran99/rest-api/utils"
"net/http" "net/http"
"time"
) )
func RegisterNewAccount(w http.ResponseWriter, r *http.Request) error {
if r.Method == http.MethodPost {
var registerInfo models.CreateUser
_ = json.NewDecoder(r.Body).Decode(&registerInfo)
//[ok]
//call function check info user type in
validRegisterInfo, responseAPI := services.CheckAndValidRegisterFiled(&registerInfo)
//[fairy ok]
if responseAPI != nil {
return utils.WriteJSON(w, responseAPI.Code, responseAPI.Err)
}
fmt.Println(validRegisterInfo)
//call function check data user use in past or not
isValidData, responseAPI := services.CheckAccountValid(registerInfo.Username, registerInfo.Email)
fmt.Println(isValidData)
if isValidData != true {
return utils.WriteJSON(w, responseAPI.Code, responseAPI.Err)
}
preUserData := &models.PreusersMongo{
Username: registerInfo.Username,
Email: registerInfo.Email,
PhoneNumber: registerInfo.PhoneNumber,
HashPassword: registerInfo.Password,
UUID: registerInfo.UUID,
CreatedDate: time.Now(),
UpdateDate: time.Now(),
VerifySentCount: 1,
}
services.GenerateVerifyAccount(preUserData, w)
//debug
//if isValidData == true || validRegisterInfo == true {
// return utils.WriteJSON(w, http.StatusOK, "USER HAVE VALID INFO FOR REGISTER ACCOUNT")
//}
//debug
}
return nil
}
func VerifyWithOTP(w http.ResponseWriter, r *http.Request) error { func VerifyWithOTP(w http.ResponseWriter, r *http.Request) error {
if r.Method == http.MethodPost { if r.Method == http.MethodPost {
@ -103,7 +53,7 @@ func VerifyWithJWT(w http.ResponseWriter, r *http.Request) error {
} }
func AuthRouterSetup(router *mux.Router) { func AuthRouterSetup(router *mux.Router) {
authRouter := router.PathPrefix("/account").Subrouter() authRouter := router.PathPrefix("/account").Subrouter()
authRouter.Handle("/register", utils.MakeHTTPHandlerFn(RegisterNewAccount)).Methods("POST") authRouter.Handle("/register", utils.MakeHTTPHandlerFn(controler.RegisterNewAccount)).Methods("POST")
authRouter.Handle("/register/verifyAccountOTP", utils.MakeHTTPHandlerFn(VerifyWithOTP)).Methods("POST") authRouter.Handle("/register/verifyAccountOTP", utils.MakeHTTPHandlerFn(VerifyWithOTP)).Methods("POST")
authRouter.Handle("/register/verifyAccountJWT", utils.MakeHTTPHandlerFn(VerifyWithJWT)).Methods("POST") authRouter.Handle("/register/verifyAccountJWT", utils.MakeHTTPHandlerFn(VerifyWithJWT)).Methods("POST")
} }

View File

@ -4,11 +4,13 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"github.com/go-mail/mail" "github.com/go-mail/mail"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/google/uuid" "github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"linhdevtran99/rest-api/models" "linhdevtran99/rest-api/models"
"linhdevtran99/rest-api/utils" "linhdevtran99/rest-api/utils"
@ -16,12 +18,13 @@ import (
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
"strings"
"sync" "sync"
"time" "time"
) )
// CheckAndValidRegisterFiled Check the user register field is valid or not // 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) _ = models.Validate.RegisterValidation("customPassword", models.PasswordValidator)
errs := models.Validate.Struct(registerData) errs := models.Validate.Struct(registerData)
type errStack []string type errStack []string
@ -61,20 +64,25 @@ func CheckAndValidRegisterFiled(registerData *models.CreateUser) (bool, *models.
} }
} }
//handle repose error //handle repose error
return false, &models.ResponseError{Code: http.StatusTooManyRequests, Err: errorAPI} return false, &models.ResponseError{Code: http.StatusBadRequest, Err: errorAPI}
} }
return true, nil 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) { func CheckAccountValid(userName string, email string) (bool, *models.ResponseError) {
//filter in mongodb //filter in mongodb
var isValid bool var isError bool
var errAPI *models.ResponseError var errAPI *models.ResponseError
var dataRedisRetrive models.RedisOTP 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{ filter := bson.D{
{"$or", bson.A{ {"$or", bson.A{
bson.D{{"username", userName}}, 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 { if err != nil {
isValid = true fmt.Println("Internal Log: Internal error")
errAPI = nil isError = true
} else { 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") fmt.Println("Internal Log: Email or username already had register")
isValid = false isError = true
errAPI = &models.ResponseError{ errAPI = &models.ResponseError{
Code: http.StatusBadRequest, Code: http.StatusBadRequest,
Err: models.ErrorAPI{ 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 { 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 timeDiff := time.Now().Unix() - dataRedisRetrive.CreatedDate
if timeDiff < 30 { if timeDiff < 30 {
isValid = false isError = true
fmt.Println("Internal Log: Rate limit send verify mail") fmt.Println("Internal Log: Rate limit send verify mail")
errAPI = &models.ResponseError{ errAPI = &models.ResponseError{
Code: http.StatusBadRequest, 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 // GeneratorOtp Generate OTP Verify Link and Qr Link
@ -129,30 +167,8 @@ const (
otpDigits = 6 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 // 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") smtpPass := os.Getenv("SMTP_PASS")
m := mail.NewMessage() m := mail.NewMessage()
@ -179,40 +195,62 @@ func createMailVerify(registerInfo *models.PreusersMongo, otpChan chan models.Ot
// Send the email to Bob, Cora and Dan. // Send the email to Bob, Cora and Dan.
if err := d.DialAndSend(m); err != nil { if err := d.DialAndSend(m); err != nil {
fmt.Println("Internal Log: Fail to send email check smtp") errChan <- err
_ = utils.WriteJSONInternalError(w, "Fail to send email check smtp") defer func() {
panic(err) _ = os.Remove("./temp/" + qrFileName)
}()
return
} }
errChan <- nil
defer func() { defer func() {
_ = os.Remove("./temp/" + qrFileName) _ = os.Remove("./temp/" + qrFileName)
}() }()
} }
// check and write pre use into mongo db // check and write pre use into mongodb
func CheckAndWritePreuser(registerInfo *models.PreusersMongo, w http.ResponseWriter) (uint64, time.Time) { func CheckAndWritePreuser(registerInfo *models.PreusersMongo, wg *sync.WaitGroup, ctxCancel context.CancelFunc, errorChan error) (uint64, time.Time) {
//generate timestamp //generate timestamp
timeCreate := time.Now() timeCreate := time.Now()
registerInfo.CreatedDate = timeCreate registerInfo.CreatedDate = timeCreate
//create context handler with timeout
timeout := 2 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
//checking //checking
email := registerInfo.Email email := registerInfo.Email
count := 1 count := 1
filter := bson.D{{"email", email}} 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) hashed, err := bcrypt.GenerateFromPassword([]byte(registerInfo.HashPassword), 10)
if err != nil { if err != nil {
errorChan = errors.New("fail to hash password")
ctxCancel()
wg.Done()
fmt.Println("Internal Log :Fail to hash password") fmt.Println("Internal Log :Fail to hash password")
_ = utils.WriteJSONInternalError(w, "Fail to hash password")
} }
registerInfo.HashPassword = string(hashed) registerInfo.HashPassword = string(hashed)
_, err = utils.PreUserData.InsertOne(context.Background(), registerInfo) _, err = utils.PreUserData.InsertOne(context.Background(), registerInfo)
if err != nil { 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") fmt.Println("Internal Log: Can't Insert Data To PreUser")
_ = utils.WriteJSONInternalError(w, "Can't Insert Data To PreUser")
} }
wg.Done()
return uint64(count), timeCreate return uint64(count), timeCreate
} else { }
update := bson.D{ update := bson.D{
{"$inc", bson.D{ {"$inc", bson.D{
{"verify_sent_count", 1}, {"verify_sent_count", 1},
@ -222,21 +260,22 @@ func CheckAndWritePreuser(registerInfo *models.PreusersMongo, w http.ResponseWri
}, },
}} }}
_, err := utils.PreUserData.UpdateOne(context.Background(), filter, update) _, err = utils.PreUserData.UpdateOne(context.Background(), filter, update)
if err != nil { if err != nil {
errorChan = errors.New("update document fail")
ctxCancel()
wg.Done()
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) count = int(raw.Lookup("verify_sent_count").Int32() + 1)
wg.Done()
return uint64(count), timeCreate return uint64(count), timeCreate
}
} }
//write otp in redis for valid if otp expired //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 otp := <-otpChan

View File

@ -1 +1 @@
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1

BIN
tmp/main

Binary file not shown.

View File

@ -136,8 +136,8 @@ func DecryptAESMailLink(linkVerifyInfo *models.LinkVerify, w http.ResponseWriter
//OTP+++++++++++++OTP// //OTP+++++++++++++OTP//
// Gen Otp and hash otp // Gen OTP and hash OTP
func GenOTP(registerInfo *models.PreusersMongo, counter uint64, otpDigits int, w http.ResponseWriter, otpChan chan models.OtpGenerate, wg *sync.WaitGroup) chan models.OtpGenerate { func GenOTP(registerInfo *models.PreusersMongo, counter uint64, otpDigits int, w http.ResponseWriter, otpChan chan models.OtpGenerate, errorChan error, ctxCancel context.CancelFunc, wg *sync.WaitGroup) {
serectBase32 := base32.StdEncoding.EncodeToString([]byte(serect + registerInfo.UUID + registerInfo.Email)) serectBase32 := base32.StdEncoding.EncodeToString([]byte(serect + registerInfo.UUID + registerInfo.Email))
passCode, err := hotp.GenerateCodeCustom(serectBase32, counter, hotp.ValidateOpts{ passCode, err := hotp.GenerateCodeCustom(serectBase32, counter, hotp.ValidateOpts{
@ -146,14 +146,18 @@ func GenOTP(registerInfo *models.PreusersMongo, counter uint64, otpDigits int, w
}) })
if err != nil { if err != nil {
errorChan = errors.New("fail to create OTP")
ctxCancel()
wg.Done()
fmt.Println("Internal log: Fail to create OTP") fmt.Println("Internal log: Fail to create OTP")
_ = WriteJSONInternalError(w, "Fail to create OTP")
} }
hashed, err := bcrypt.GenerateFromPassword([]byte(passCode), 5) hashed, err := bcrypt.GenerateFromPassword([]byte(passCode), 5)
if err != nil { if err != nil {
errorChan = errors.New("fail to encrypt OTP")
ctxCancel()
wg.Done()
fmt.Println("Internal log: Fail to encrypt OTP") fmt.Println("Internal log: Fail to encrypt OTP")
_ = WriteJSONInternalError(w, "Fail to encrypt OTP")
} }
data := models.OtpGenerate{ data := models.OtpGenerate{
@ -162,10 +166,6 @@ func GenOTP(registerInfo *models.PreusersMongo, counter uint64, otpDigits int, w
} }
otpChan <- data otpChan <- data
otpChan <- data
wg.Done()
return otpChan
} }
//Decrypt OTP and verify otp //Decrypt OTP and verify otp

View File

@ -19,6 +19,7 @@ func WriteJSON(w http.ResponseWriter, status int, v any) error {
func WriteJSONInternalError(w http.ResponseWriter, v any) error { func WriteJSONInternalError(w http.ResponseWriter, v any) error {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
err := json.NewEncoder(w).Encode(v) err := json.NewEncoder(w).Encode(v)
if err != nil { if err != nil {