From 0db15640126d20d4b6bca1b8ba4374b22951005c Mon Sep 17 00:00:00 2001
From: Sergey Chubaryan {{.Text}}
This message was sent because you forgot a password
-To change a password, use thislink
- - -` - -type HtmlTemplate struct { - Link string -} - -func SendEmailForgotPassword(dialer *gomail.Dialer, from, to, body string) error { - m := gomail.NewMessage() - m.SetHeader("From", m.FormatAddress(from, "Pet Backend")) - m.SetHeader("To", to) - m.SetHeader("Subject", "Hello!") - m.SetBody("text/html", body) - - return dialer.DialAndSend(m) -} - -type Config struct { - App struct { - LogFile string `yaml:"logFile"` - ServiceUrl string `yaml:"serviceUrl"` - } - - Kafka struct { - Brokers []string `yaml:"brokers"` - Topic string `yaml:"topic"` - ConsumerGroupId string `yaml:"consumerGroupId"` - } `yaml:"kafka"` - - SMTP struct { - Server string `yaml:"server"` - Port int `yaml:"port"` - Login string `yaml:"login"` - Password string `yaml:"password"` - Email string `yaml:"email"` - } `yaml:"smtp"` +type SendEmailEvent struct { + Email string `json:"email"` + Token string `json:"token"` } func main() { ctx := context.Background() - configFile, err := os.ReadFile("config.yaml") + config, err := LoadConfig("config.yaml") if err != nil { log.Fatal(err.Error()) } - config := &Config{} - if err := yaml.Unmarshal(configFile, config); err != nil { + emailer, err := NewEmailer(config.SMTP) + if err != nil { log.Fatal(err.Error()) } - dialer := gomail.NewDialer(config.SMTP.Server, config.SMTP.Port, config.SMTP.Login, config.SMTP.Password) - r := kafka.NewReader(kafka.ReaderConfig{ Brokers: config.Kafka.Brokers, Topic: config.Kafka.Topic, GroupID: config.Kafka.ConsumerGroupId, }) - logger, err := logger.New( - ctx, - logger.NewLoggerOpts{ - Debug: true, - OutputFile: config.App.LogFile, - }, - ) + logger, err := logger.New(ctx, logger.NewLoggerOpts{ + Debug: true, + OutputFile: config.App.LogFile, + }) if err != nil { log.Fatal(err.Error()) } logger.Printf("coworker service started\n") - template, err := template.New("verify-email").Parse(MSG_TEXT) - if err != nil { - log.Fatal(err) - } - for { msg, err := r.FetchMessage(ctx) if err == io.EOF { @@ -119,27 +63,28 @@ func main() { continue } - value := struct { - Email string `json:"email"` - Token string `json:"token"` - }{} - - if err := json.Unmarshal(msg.Value, &value); err != nil { - log.Fatalf("failed to unmarshal: %s\n", err.Error()) - continue - } - - link := fmt.Sprintf("%s/verify-user?token=%s", config.App.ServiceUrl, value.Token) - - builder := &strings.Builder{} - if err := template.Execute(builder, HtmlTemplate{link}); err != nil { - log.Printf("failed to execute html template: %s\n", err.Error()) - continue - } - - if err := SendEmailForgotPassword(dialer, config.SMTP.Email, value.Email, builder.String()); err != nil { - log.Printf("failed to send email: %s\n", err.Error()) + if err := handleEvent(config, emailer, msg); err != nil { + log.Printf("failed to handle event: %s\n", err.Error()) continue } } } + +func handleEvent(config Config, emailer *Emailer, msg kafka.Message) error { + event := SendEmailEvent{} + if err := json.Unmarshal(msg.Value, &event); err != nil { + return err + } + + switch string(msg.Key) { + case "email_forgot_password": + return emailer.SendRestorePassword(event.Email, event.Token) + case "email_password_changed": + return emailer.SendPasswordChanged(event.Email) + case "email_verify_user": + link := fmt.Sprintf("%s/verify-user?token=%s", config.App.ServiceUrl, event.Token) + return emailer.SendVerifyUser(event.Email, link) + } + + return fmt.Errorf("unknown event type") +} diff --git a/internal/core/repos/event_repo.go b/internal/core/repos/event_repo.go index abba12e..ac3e588 100644 --- a/internal/core/repos/event_repo.go +++ b/internal/core/repos/event_repo.go @@ -6,6 +6,12 @@ import ( "encoding/json" ) +const ( + EventEmailPasswordChanged = "email_password_changed" + EventEmailForgotPassword = "email_forgot_password" + EventEmailVerifyUser = "email_verify_user" +) + func NewEventRepo(kafka *integrations.Kafka) *EventRepo { return &EventRepo{ kafka: kafka, @@ -32,10 +38,14 @@ func (e *EventRepo) sendEmail(ctx context.Context, email, actionToken, eventType return e.kafka.SendMessage(ctx, eventType, valueBytes) } -func (e *EventRepo) SendEmailForgotPassword(ctx context.Context, email, actionToken string) error { - return e.sendEmail(ctx, email, actionToken, "email_forgot_password") +func (e *EventRepo) SendEmailPasswordChanged(ctx context.Context, email string) error { + return e.sendEmail(ctx, email, "", EventEmailPasswordChanged) } -func (e *EventRepo) SendEmailVerifyEmail(ctx context.Context, email, actionToken string) error { - return e.sendEmail(ctx, email, actionToken, "email_verify_email") +func (e *EventRepo) SendEmailForgotPassword(ctx context.Context, email, actionToken string) error { + return e.sendEmail(ctx, email, actionToken, EventEmailForgotPassword) +} + +func (e *EventRepo) SendEmailVerifyUser(ctx context.Context, email, actionToken string) error { + return e.sendEmail(ctx, email, actionToken, EventEmailVerifyUser) } diff --git a/internal/core/services/user_service.go b/internal/core/services/user_service.go index 2d91add..6a47cf3 100644 --- a/internal/core/services/user_service.go +++ b/internal/core/services/user_service.go @@ -34,7 +34,7 @@ type UserService interface { VerifyEmail(ctx context.Context, actionToken string) error SendEmailForgotPassword(ctx context.Context, userId string) error - SendEmailVerifyEmail(ctx context.Context, email string) error + SendEmailVerifyUser(ctx context.Context, email string) error ChangePassword(ctx context.Context, userId, oldPassword, newPassword string) error ChangePasswordWithToken(ctx context.Context, actionToken, newPassword string) error @@ -94,7 +94,7 @@ func (u *userService) CreateUser(ctx context.Context, params UserCreateParams) ( return nil, err } - if err := u.sendEmailVerifyEmail(ctx, result.Id, user.Email); err != nil { + if err := u.sendEmailVerifyUser(ctx, result.Id, user.Email); err != nil { u.deps.Logger.Error().Err(err).Msg("error occured on sending email") } @@ -162,7 +162,7 @@ func (u *userService) SendEmailForgotPassword(ctx context.Context, email string) UserId: user.Id, Value: uuid.New().String(), Target: models.ActionTokenTargetForgotPassword, - Expiration: time.Now().Add(1 * time.Hour), + Expiration: time.Now().Add(15 * time.Minute), }, ) if err != nil { @@ -172,7 +172,7 @@ func (u *userService) SendEmailForgotPassword(ctx context.Context, email string) return u.deps.EventRepo.SendEmailForgotPassword(ctx, user.Email, actionToken.Value) } -func (u *userService) sendEmailVerifyEmail(ctx context.Context, userId, email string) error { +func (u *userService) sendEmailVerifyUser(ctx context.Context, userId, email string) error { actionToken, err := u.deps.ActionTokenRepo.CreateActionToken( ctx, models.ActionTokenDTO{ @@ -186,10 +186,10 @@ func (u *userService) sendEmailVerifyEmail(ctx context.Context, userId, email st return err } - return u.deps.EventRepo.SendEmailVerifyEmail(ctx, email, actionToken.Value) + return u.deps.EventRepo.SendEmailVerifyUser(ctx, email, actionToken.Value) } -func (u *userService) SendEmailVerifyEmail(ctx context.Context, email string) error { +func (u *userService) SendEmailVerifyUser(ctx context.Context, email string) error { //user, err := u.getUserById(ctx, userId) user, err := u.deps.UserRepo.GetUserByEmail(ctx, email) if err != nil { @@ -202,7 +202,7 @@ func (u *userService) SendEmailVerifyEmail(ctx context.Context, email string) er return fmt.Errorf("user already verified") } - return u.sendEmailVerifyEmail(ctx, user.Id, user.Email) + return u.sendEmailVerifyUser(ctx, user.Id, user.Email) } func (u *userService) ChangePasswordWithToken(ctx context.Context, actionToken, newPassword string) error { @@ -256,10 +256,18 @@ func (u *userService) updatePassword(ctx context.Context, user models.UserDTO, n return err } - return u.deps.UserRepo.UpdateUser(ctx, user.Id, models.UserUpdateDTO{ + if err = u.deps.UserRepo.UpdateUser(ctx, user.Id, models.UserUpdateDTO{ Secret: newSecret, Name: user.Name, - }) + }); err != nil { + return err + } + + if err := u.deps.EventRepo.SendEmailPasswordChanged(ctx, user.Email); err != nil { + u.deps.Logger.Error().Err(err).Msg("error occured on sending email") + } + + return nil } func (u *userService) getUserById(ctx context.Context, userId string) (*models.UserDTO, error) { diff --git a/internal/http_server/request_log.go b/internal/http_server/request_log.go index 0a32ec3..f3c7e5b 100644 --- a/internal/http_server/request_log.go +++ b/internal/http_server/request_log.go @@ -41,7 +41,7 @@ func NewRequestLogMiddleware(logger log.Logger, tracer trace.Tracer, prometheus ctxLogger := logger.WithContext(c) - msg := fmt.Sprintf("Request %s %s %d %v", method, path, statusCode, latency) + msg := fmt.Sprintf("%s %s %d %v", method, path, statusCode, latency) if statusCode >= 200 && statusCode < 400 { ctxLogger.Log().Msg(msg)