add forget password

This commit is contained in:
aditya.siregar
2024-07-23 01:36:25 +07:00
parent 7ea809cc09
commit 5a0dec6128
22 changed files with 907 additions and 76 deletions
+48 -1
View File
@@ -26,7 +26,8 @@ func (r *AuthRepository) CheckExistsUserAccount(ctx context.Context, email strin
err := r.db.
Table("users").
Select("users.*, user_roles.role_id, user_roles.partner_id, user_roles.site_id, sites.name, roles.role_name, partners.name as partner_name, partners.status as partner_status").
Select("users.*, user_roles.role_id, user_roles.partner_id, user_roles.site_id,"+
" sites.name, roles.role_name, partners.name as partner_name, partners.status as partner_status, users.reset_password").
Where("users.email = ?", email).
Joins("left join user_roles on users.id = user_roles.user_id").
Joins("left join roles on user_roles.role_id = roles.role_id").
@@ -49,3 +50,49 @@ func (r *AuthRepository) CheckExistsUserAccount(ctx context.Context, email strin
return &user, nil
}
func (r *AuthRepository) CheckExistsUserAccountByID(ctx context.Context, userID int64) (*entity.UserDB, error) {
var user entity.UserDB
err := r.db.
Table("users").
Select("users.*, user_roles.role_id, user_roles.partner_id, user_roles.site_id, sites.name, roles.role_name, partners.name as partner_name, partners.status as partner_status").
Where("users.id = ?", userID).
Joins("left join user_roles on users.id = user_roles.user_id").
Joins("left join roles on user_roles.role_id = roles.role_id").
Joins("left join partners on user_roles.partner_id = partners.id").
Joins("left join sites on user_roles.site_id = sites.id").
First(&user).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("user with not exist") // or use a custom error type
}
logger.ContextLogger(ctx).Error(fmt.Sprintf("Failed to get user"), zap.Error(err))
return nil, err
}
return &user, nil
}
func (r *AuthRepository) UpdatePassword(ctx context.Context, trx *gorm.DB, newHashedPassword string, userID int64, resetPassword bool) error {
// Perform the update using a single Updates call
err := trx.Model(&entity.UserDB{}).
Where("id = ?", userID).
Updates(map[string]interface{}{
"password": newHashedPassword,
"reset_password": resetPassword,
}).Error
if err != nil {
logger.ContextLogger(ctx).Error(fmt.Sprintf("Failed to update password for user with id: %d", userID), zap.Error(err))
return err
}
return nil
}
+88
View File
@@ -0,0 +1,88 @@
package brevo
import (
"bytes"
"context"
"fmt"
"furtuna-be/internal/entity"
"html/template"
"io/ioutil"
"log"
brevo "github.com/getbrevo/brevo-go/lib"
)
type Config interface {
GetApiKey() string
}
type ServiceImpl struct {
brevoConn *brevo.APIClient
}
func (s ServiceImpl) SendEmailTransactional(ctx context.Context, param entity.SendEmailNotificationParam) error {
templateFile, err := ioutil.ReadFile(param.TemplatePath)
if err != nil {
log.Println(err)
return err
}
renderedTemplate, err := template.New(param.TemplateName).Parse(string(templateFile))
if err != nil {
log.Println(err)
return err
}
return s.sendEmail(ctx, renderedTemplate, param)
}
func (s ServiceImpl) sendEmail(ctx context.Context, tmpl *template.Template, param entity.SendEmailNotificationParam) error {
var body bytes.Buffer
err := tmpl.Execute(&body, param.Data)
if err != nil {
log.Println(err)
return err
}
payload := brevo.SendSmtpEmail{
Sender: &brevo.SendSmtpEmailSender{
Email: "siregaraditya@gmail.com",
},
To: []brevo.SendSmtpEmailTo{
{
Email: "avranata01@gmail.com",
},
},
Subject: param.Subject,
HtmlContent: body.String(),
}
if len(param.CcEmails) != 0 {
for _, email := range param.CcEmails {
payload.Cc = append(payload.Cc, brevo.SendSmtpEmailCc{
Email: email,
})
}
}
_, _, err = s.brevoConn.TransactionalEmailsApi.SendTransacEmail(ctx, payload)
if err != nil {
return err
}
return nil
}
func New(conf Config) *ServiceImpl {
cfg := brevo.NewConfiguration()
cfg.AddDefaultHeader("api-key", conf.GetApiKey())
client := brevo.NewAPIClient(cfg)
result, resp, err := client.AccountApi.GetAccount(context.Background())
if err != nil {
fmt.Println("Error when calling AccountApi->get_account: ", err.Error())
log.Fatal("error")
}
fmt.Println("GetAccount Object:", result, " GetAccount Response: ", resp)
return &ServiceImpl{brevoConn: client}
}
+46
View File
@@ -23,6 +23,8 @@ type CryptoConfig interface {
AccessTokenOrderSecret() string
AccessTokenOrderExpiresDate() time.Time
AccessTokenExpiresDate() time.Time
AccessTokenResetPasswordSecret() string
AccessTokenResetPasswordExpire() time.Time
}
type CryptoImpl struct {
@@ -83,6 +85,30 @@ func (c *CryptoImpl) GenerateJWT(user *entity.User) (string, error) {
return token, nil
}
func (c *CryptoImpl) GenerateJWTReseetPassword(user *entity.User) (string, error) {
claims := &entity.JWTAuthClaims{
StandardClaims: jwt.StandardClaims{
Subject: strconv.FormatInt(user.ID, 10),
ExpiresAt: c.Config.AccessTokenResetPasswordExpire().Unix(),
IssuedAt: time.Now().Unix(),
NotBefore: time.Now().Unix(),
},
UserID: user.ID,
Name: user.Name,
Email: user.Email,
}
token, err := jwt.
NewWithClaims(jwt.SigningMethodHS256, claims).
SignedString([]byte(c.Config.AccessTokenResetPasswordSecret()))
if err != nil {
return "", err
}
return token, nil
}
func (c *CryptoImpl) ParseAndValidateJWT(tokenString string) (*entity.JWTAuthClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &entity.JWTAuthClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(c.Config.AccessTokenSecret()), nil
@@ -140,3 +166,23 @@ func (c *CryptoImpl) ValidateJWTOrder(tokenString string) (int64, int64, error)
return claims.PartnerID, claims.OrderID, nil
}
func (c *CryptoImpl) ValidateResetPassword(tokenString string) (int64, error) {
token, err := jwt.ParseWithClaims(tokenString, &entity.JWTOrderClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(c.Config.AccessTokenResetPasswordSecret()), nil
})
if err != nil {
return 0, err
}
claims, ok := token.Claims.(*entity.JWTAuthClaims)
if !ok || !token.Valid {
return 0, fmt.Errorf("invalid token %v", token.Header["alg"])
}
return claims.UserID, nil
}
+41 -30
View File
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"furtuna-be/internal/repository/branches"
"furtuna-be/internal/repository/brevo"
mdtrns "furtuna-be/internal/repository/midtrans"
"furtuna-be/internal/repository/orders"
"furtuna-be/internal/repository/oss"
@@ -26,45 +27,49 @@ import (
)
type RepoManagerImpl struct {
Crypto Crypto
Auth Auth
Event Event
User User
Branch Branch
Studio Studio
Product Product
Order Order
OSS OSSRepository
Partner PartnerRepository
Site SiteRepository
Trx TransactionManager
Wallet WalletRepository
Midtrans Midtrans
Payment Payment
Crypto Crypto
Auth Auth
Event Event
User User
Branch Branch
Studio Studio
Product Product
Order Order
OSS OSSRepository
Partner PartnerRepository
Site SiteRepository
Trx TransactionManager
Wallet WalletRepository
Midtrans Midtrans
Payment Payment
EmailService EmailService
}
func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl {
return &RepoManagerImpl{
Crypto: crypto.NewCrypto(cfg.Auth()),
Auth: auth.NewAuthRepository(db),
Event: event.NewEventRepo(db),
User: users.NewUserRepository(db),
Branch: branches.NewBranchRepository(db),
Studio: studios.NewStudioRepository(db),
Product: products.NewProductRepository(db),
Order: orders.NewOrderRepository(db),
OSS: oss.NewOssRepositoryImpl(cfg.OSSConfig),
Partner: partners.NewPartnerRepository(db),
Site: sites.NewSiteRepository(db),
Trx: trx.NewGormTransactionManager(db),
Wallet: repository.NewWalletRepository(db),
Midtrans: mdtrns.New(&cfg.Midtrans),
Payment: payment.NewPaymentRepository(db),
Crypto: crypto.NewCrypto(cfg.Auth()),
Auth: auth.NewAuthRepository(db),
Event: event.NewEventRepo(db),
User: users.NewUserRepository(db),
Branch: branches.NewBranchRepository(db),
Studio: studios.NewStudioRepository(db),
Product: products.NewProductRepository(db),
Order: orders.NewOrderRepository(db),
OSS: oss.NewOssRepositoryImpl(cfg.OSSConfig),
Partner: partners.NewPartnerRepository(db),
Site: sites.NewSiteRepository(db),
Trx: trx.NewGormTransactionManager(db),
Wallet: repository.NewWalletRepository(db),
Midtrans: mdtrns.New(&cfg.Midtrans),
Payment: payment.NewPaymentRepository(db),
EmailService: brevo.New(&cfg.Brevo),
}
}
type Auth interface {
CheckExistsUserAccount(ctx context.Context, email string) (*entity.UserDB, error)
CheckExistsUserAccountByID(ctx context.Context, userID int64) (*entity.UserDB, error)
UpdatePassword(ctx context.Context, trx *gorm.DB, newHashedPassword string, userID int64, resetPassword bool) error
}
type Event interface {
@@ -79,8 +84,10 @@ type Crypto interface {
CompareHashAndPassword(hash string, password string) bool
ValidateWT(tokenString string) (*jwt.Token, error)
GenerateJWT(user *entity.User) (string, error)
GenerateJWTReseetPassword(user *entity.User) (string, error)
GenerateJWTOrder(order *entity.Order) (string, error)
ValidateJWTOrder(tokenString string) (int64, int64, error)
ValidateResetPassword(tokenString string) (int64, error)
ParseAndValidateJWT(token string) (*entity.JWTAuthClaims, error)
}
@@ -171,3 +178,7 @@ type Payment interface {
FindByOrderAndPartnerID(ctx context.Context, orderID, partnerID int64) (*entity.Payment, error)
FindByReferenceID(ctx context.Context, db *gorm.DB, referenceID string) (*entity.Payment, error)
}
type EmailService interface {
SendEmailTransactional(ctx context.Context, param entity.SendEmailNotificationParam) error
}