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
/dataSources/
/dataSources.local.xml
# GitHub Copilot persisted chat sessions
/copilot/chatSessions

View File

@ -1 +1,100 @@
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/gorilla/mux"
"linhdevtran99/rest-api/models"
"linhdevtran99/rest-api/rest-api/controler"
"linhdevtran99/rest-api/rest-api/services"
"linhdevtran99/rest-api/utils"
"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 {
if r.Method == http.MethodPost {
@ -103,7 +53,7 @@ func VerifyWithJWT(w http.ResponseWriter, r *http.Request) error {
}
func AuthRouterSetup(router *mux.Router) {
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/verifyAccountJWT", utils.MakeHTTPHandlerFn(VerifyWithJWT)).Methods("POST")
}

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,40 +195,62 @@ 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")
}
wg.Done()
return uint64(count), timeCreate
} else {
}
update := bson.D{
{"$inc", bson.D{
{"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 {
errorChan = errors.New("update document fail")
ctxCancel()
wg.Done()
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
}
}
//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

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//
// 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 {
// Gen OTP and hash OTP
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))
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 {
errorChan = errors.New("fail to create OTP")
ctxCancel()
wg.Done()
fmt.Println("Internal log: Fail to create OTP")
_ = WriteJSONInternalError(w, "Fail to create OTP")
}
hashed, err := bcrypt.GenerateFromPassword([]byte(passCode), 5)
if err != nil {
errorChan = errors.New("fail to encrypt OTP")
ctxCancel()
wg.Done()
fmt.Println("Internal log: Fail to encrypt OTP")
_ = WriteJSONInternalError(w, "Fail to encrypt OTP")
}
data := models.OtpGenerate{
@ -162,10 +166,6 @@ func GenOTP(registerInfo *models.PreusersMongo, counter uint64, otpDigits int, w
}
otpChan <- data
otpChan <- data
wg.Done()
return otpChan
}
//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 {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.WriteHeader(http.StatusInternalServerError)
err := json.NewEncoder(w).Encode(v)
if err != nil {