Skip to content

Commit

Permalink
add generate mail confirm code API
Browse files Browse the repository at this point in the history
  • Loading branch information
le-xuan-quynh committed May 4, 2022
1 parent 161ef12 commit 2e8fa1a
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 4 deletions.
27 changes: 25 additions & 2 deletions pkg/authorization/endpoints/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Set struct {
GetForgetPasswordCodeEndpoint endpoint.Endpoint
ResetPasswordEndpoint endpoint.Endpoint
GenerateAccessTokenEndpoint endpoint.Endpoint
GetVerifyMailCodeEndpoint endpoint.Endpoint
}

func NewEndpointSet(svc authorization.Service,
Expand Down Expand Up @@ -86,6 +87,11 @@ func NewEndpointSet(svc authorization.Service,
generateAccessTokenEndpoint = middleware.ValidateParamRequest(validator, logger)(generateAccessTokenEndpoint)
generateAccessTokenEndpoint = middleware.ValidateRefreshToken(auth, r, logger)(generateAccessTokenEndpoint)

getVerifyMailCodeEndpoint := MakeGetVerifyMailCodeEndpoint(svc)
getVerifyMailCodeEndpoint = middleware.RateLimitRequest(tb, logger)(getVerifyMailCodeEndpoint)
getVerifyMailCodeEndpoint = middleware.ValidateParamRequest(validator, logger)(getVerifyMailCodeEndpoint)
getVerifyMailCodeEndpoint = middleware.ValidateAccessToken(auth, logger)(getVerifyMailCodeEndpoint)

return Set{
HealthCheckEndpoint: healthCheckEndpoint,
RegisterEndpoint: registerEndpoint,
Expand All @@ -99,6 +105,7 @@ func NewEndpointSet(svc authorization.Service,
GetForgetPasswordCodeEndpoint: getForgetPasswordCodeEndpoint,
ResetPasswordEndpoint: resetPasswordEndpoint,
GenerateAccessTokenEndpoint: generateAccessTokenEndpoint,
GetVerifyMailCodeEndpoint: getVerifyMailCodeEndpoint,
}
}

Expand Down Expand Up @@ -292,15 +299,31 @@ func MakeCreateNewPasswordWithCodeEndpoint(svc authorization.Service) endpoint.E
// MakeGenerateAccessTokenEndpoint returns an endpoint that invokes GenerateAccessToken on the service.
func MakeGenerateAccessTokenEndpoint(svc authorization.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req, ok := request.(authorization.GenerateAccessTokenRequest)
_, ok := request.(authorization.GenerateAccessTokenRequest)
if !ok {
cusErr := utils.NewErrorResponse(utils.BadRequest)
return nil, cusErr
}
token, err := svc.GenerateAccessToken(ctx, &req)
token, err := svc.GenerateAccessToken(ctx)
if err != nil {
return nil, err
}
return token, nil
}
}

// MakeGetVerifyMailCodeEndpoint returns an endpoint that invokes GetVerifyMailCode on the service.
func MakeGetVerifyMailCodeEndpoint(svc authorization.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
_, ok := request.(authorization.GetVerifyMailCodeRequest)
if !ok {
cusErr := utils.NewErrorResponse(utils.BadRequest)
return nil, cusErr
}
err := svc.GetVerifyMailCode(ctx)
if err != nil {
return nil, err
}
return "successfully mailed verification code. Please check your email.", nil
}
}
5 changes: 5 additions & 0 deletions pkg/authorization/reqresponse.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,8 @@ type GenerateAccessResponse struct {
AccessToken string `json:"access_token,omitempty"`
Username string `json:"username,omitempty"`
}

// GetVerifyMailCodeRequest is used to get verify mail code
type GetVerifyMailCodeRequest struct {
AccessToken string `json:"access_token" validate:"required"`
}
3 changes: 2 additions & 1 deletion pkg/authorization/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ type Service interface {
UpdatePassword(ctx context.Context, request *UpdatePasswordRequest) (string, error)
GetForgetPasswordCode(ctx context.Context, email string) error
ResetPassword(ctx context.Context, request *CreateNewPasswordWithCodeRequest) error
GenerateAccessToken(ctx context.Context, request *GenerateAccessTokenRequest) (interface{}, error)
GenerateAccessToken(ctx context.Context) (interface{}, error)
GetVerifyMailCode(ctx context.Context) error
}
24 changes: 24 additions & 0 deletions pkg/authorization/transport/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ func NewHTTPHandler(ep endpoints.Set) http.Handler {
encodeResponse,
options...,
))
m.Handle("/get-verify-mail-code", httptransport.NewServer(
ep.GetVerifyMailCodeEndpoint,
decodeHTTPGetVerifyMailCodeRequest,
encodeResponse,
options...,
))

mux := http.NewServeMux()
mux.Handle("/api/v1/", http.StripPrefix("/api/v1", m))
Expand Down Expand Up @@ -323,6 +329,24 @@ func decodeHTTPGenerateAccessTokenRequest(_ context.Context, r *http.Request) (i
}
}

// decodeHTTPGetVerifyMailCodeRequest decode request
func decodeHTTPGetVerifyMailCodeRequest(_ context.Context, r *http.Request) (interface{}, error) {
if r.Method == "POST" {
var req authorization.GetVerifyMailCodeRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
return nil, utils.NewErrorResponse(utils.BadRequest)
}
if req.AccessToken == "" {
return nil, utils.NewErrorResponse(utils.AccessTokenRequired)
}
return req, nil
} else {
cusErr := utils.NewErrorResponse(utils.MethodNotAllowed)
return nil, cusErr
}
}

func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")

Expand Down
92 changes: 91 additions & 1 deletion pkg/authorization/users-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ func (s *userService) ResetPassword(ctx context.Context, request *CreateNewPassw
}

// GenerateAccessToken generate access token
func (s *userService) GenerateAccessToken(ctx context.Context, request *GenerateAccessTokenRequest) (interface{}, error) {
func (s *userService) GenerateAccessToken(ctx context.Context) (interface{}, error) {
userID, ok := ctx.Value(middleware.UserIDKey{}).(string)
if !ok {
s.logger.Error("Error getting userID from context")
Expand Down Expand Up @@ -809,3 +809,93 @@ func (s *userService) GenerateAccessToken(ctx context.Context, request *Generate
Username: user.Username,
}, nil
}

// GetVerifyMailCode get verify mail code
func (s *userService) GetVerifyMailCode(ctx context.Context) error {
userID, ok := ctx.Value(middleware.UserIDKey{}).(string)
if !ok {
s.logger.Error("Error getting userID from context")
cusErr := utils.NewErrorResponse(utils.InternalServerError)
return cusErr
}
user, err := s.repo.GetUserByID(ctx, userID)
if err != nil {
s.logger.Error("Cannot get user", "error", err)
cusErr := utils.NewErrorResponse(utils.InternalServerError)
return cusErr
}
// Check if user is banned
if user.Banned {
s.logger.Error("User is banned", "error", err)
cusErr := utils.NewErrorResponse(utils.Forbidden)
return cusErr
}
// Get limit data
isInsert := false
limitData, err := s.repo.GetLimitData(ctx, user.ID)
if err != nil {
s.logger.Error("Empty row get limit data", "error", err)
// No row, need insert
isInsert = true
limitData.UserID = user.ID
limitData.NumOfSendMail = 1
} else {
limitData.NumOfSendMail += 1
}
// Check if user has reached limit send mail
if limitData.NumOfSendMail > s.configs.SendMailLimit {
s.logger.Error("User has reached limit send mail.", "error", err)
cusErr := utils.NewErrorResponse(utils.TooManyRequests)
return cusErr

}
// Insert or update limit data
err = s.repo.InsertOrUpdateLimitData(ctx, limitData, isInsert)
if err != nil {
s.logger.Error("Cannot update limit data", "error", err)
cusErr := utils.NewErrorResponse(utils.InternalServerError)
return cusErr
}
// Generate forget password code
forgetPasswordCode := utils.GenerateRandomString(8)

// store the password reset code to db
verificationData := &database.VerificationData{
Email: user.Email,
Code: forgetPasswordCode,
Type: database.MailConfirmation,
ExpiresAt: time.Now().Add(time.Minute * time.Duration(s.configs.PassResetCodeExpiration)),
}
// Check insert or update
isInsert = false
_, err = s.repo.GetVerificationData(ctx, user.Email, database.MailConfirmation)
if err != nil {
isInsert = true
s.logger.Error("Cannot get verification data", "error", err)
}

err = s.repo.StoreVerificationData(ctx, verificationData, isInsert)
if err != nil {
s.logger.Error("unable to store password reset verification data", "error", err)
cusErr := utils.NewErrorResponse(utils.InternalServerError)
return cusErr
}
// Send verification mail
from := s.configs.MailSender
to := []string{user.Email}
subject := s.configs.MailTitle
mailType := MailConfirmation
mailData := &MailData{
Username: user.Username,
Code: forgetPasswordCode,
}
mailReq := s.mailService.NewMail(from, to, subject, mailType, mailData)
err = s.mailService.SendMail(mailReq)
if err != nil {
s.logger.Error("unable to send mail", "error", err)
cusErr := utils.NewErrorResponse(utils.InternalServerError)
return cusErr
}
s.logger.Debug("successfully mailed password reset code")
return nil
}
5 changes: 5 additions & 0 deletions run_service.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,8 @@ Hiển thị database:
```
\c database_name
```

### Test request trên server:

```
wrk -c 10 -d 10s -t10 http://localhost:8081/api/v1/health

0 comments on commit 2e8fa1a

Please sign in to comment.