Files
tp/main_dc/BB/api_bb/internal/service/email_service.go
T
valitovgaziz 15357fd3c0 create and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarba
2025-10-24 05:22:44 +05:00

298 lines
8.3 KiB
Go

// service/email_service.go
package service
import (
"fmt"
"time"
"api_bb/internal/models"
"api_bb/internal/repository"
"api_bb/pkg/email"
"api_bb/pkg/logger"
"github.com/google/uuid"
"go.uber.org/zap"
)
type EmailService struct {
emailRepo repository.EmailRepository
userRepo repository.UserRepository
emailSender email.Service
logger *zap.Logger
tokenExpiry time.Duration
passwordExpiry time.Duration
}
func NewEmailService(
emailRepo repository.EmailRepository,
userRepo repository.UserRepository,
emailSender email.Service,
) EmailService {
// Создаем логгер с контекстом для сервиса
serviceLogger := logger.Get().With(zap.String("service", "email"))
return EmailService{
emailRepo: emailRepo,
userRepo: userRepo,
emailSender: emailSender,
logger: serviceLogger,
tokenExpiry: 24 * time.Hour, // 24 часа для верификации
passwordExpiry: 1 * time.Hour, // 1 час для сброса пароля
}
}
func (s *EmailService) SendVerificationEmail(userID uint, email, userName string) error {
s.logger.Info("Sending verification email",
zap.Uint("user_id", userID),
zap.String("email", email),
)
token := uuid.New().String()
verification := &models.EmailVerification{
UserID: userID,
Token: token,
Email: email,
Type: "verification",
ExpiresAt: time.Now().Add(s.tokenExpiry),
}
if err := s.emailRepo.CreateVerificationToken(verification); err != nil {
s.logger.Error("Failed to create verification token",
zap.Uint("user_id", userID),
zap.String("email", email),
zap.Error(err),
)
return fmt.Errorf("failed to create verification token: %w", err)
}
if err := s.emailSender.SendVerificationEmail(email, userName, token); err != nil {
s.logger.Error("Failed to send verification email",
zap.Uint("user_id", userID),
zap.String("email", email),
zap.Error(err),
)
return fmt.Errorf("failed to send verification email: %w", err)
}
s.logger.Info("Verification email sent successfully",
zap.Uint("user_id", userID),
zap.String("email", email))
return nil
}
func (s *EmailService) VerifyEmail(token string) error {
s.logger.Info("Verifying email token",
zap.String("token", token),
)
verification, err := s.emailRepo.GetVerificationToken(token)
if err != nil {
s.logger.Error("Invalid or expired verification token",
zap.String("token", token),
zap.Error(err),
)
return fmt.Errorf("invalid or expired token: %w", err)
}
if verification.Type != "verification" {
s.logger.Error("Invalid token type for email verification",
zap.String("token", token),
zap.String("type", verification.Type),
)
return fmt.Errorf("invalid token type")
}
// Обновляем пользователя
if err := s.userRepo.MarkEmailAsVerified(verification.UserID); err != nil {
s.logger.Error("Failed to verify email in user repository",
zap.Uint("user_id", verification.UserID),
zap.String("email", verification.Email),
zap.Error(err),
)
return fmt.Errorf("failed to verify email: %w", err)
}
// Помечаем токен как использованный
if err := s.emailRepo.MarkTokenAsUsed(token); err != nil {
s.logger.Error("Failed to mark token as used",
zap.Error(err),
zap.String("token", token))
}
s.logger.Info("Email verified successfully",
zap.Uint("user_id", verification.UserID),
zap.String("email", verification.Email))
return nil
}
func (s *EmailService) SendPasswordResetEmail(email string) error {
s.logger.Info("Sending password reset email",
zap.String("email", email),
)
user, err := s.userRepo.FindByEmail(email)
if err != nil {
// Для безопасности не сообщаем, существует ли email
s.logger.Info("Password reset requested for non-existent email",
zap.String("email", email))
return nil
}
token := uuid.New().String()
resetRequest := &models.EmailVerification{
UserID: user.ID,
Token: token,
Email: email,
Type: "password_reset",
ExpiresAt: time.Now().Add(s.passwordExpiry),
}
if err := s.emailRepo.CreateVerificationToken(resetRequest); err != nil {
s.logger.Error("Failed to create password reset token",
zap.Uint("user_id", user.ID),
zap.String("email", email),
zap.Error(err),
)
return fmt.Errorf("failed to create password reset token: %w", err)
}
if err := s.emailSender.SendPasswordResetEmail(email, user.FirstName, token); err != nil {
s.logger.Error("Failed to send password reset email",
zap.Uint("user_id", user.ID),
zap.String("email", email),
zap.Error(err),
)
return fmt.Errorf("failed to send password reset email: %w", err)
}
s.logger.Info("Password reset email sent successfully",
zap.Uint("user_id", user.ID),
zap.String("email", email))
return nil
}
func (s *EmailService) ResetPassword(token, newPassword string) error {
s.logger.Info("Resetting password with token",
zap.String("token", token),
)
verification, err := s.emailRepo.GetVerificationToken(token)
if err != nil {
s.logger.Error("Invalid or expired password reset token",
zap.String("token", token),
zap.Error(err),
)
return fmt.Errorf("invalid or expired token: %w", err)
}
if verification.Type != "password_reset" {
s.logger.Error("Invalid token type for password reset",
zap.String("token", token),
zap.String("type", verification.Type),
)
return fmt.Errorf("invalid token type")
}
// Обновляем пароль пользователя
if err := s.userRepo.UpdatePassword(verification.UserID, newPassword); err != nil {
s.logger.Error("Failed to update password",
zap.Uint("user_id", verification.UserID),
zap.Error(err),
)
return fmt.Errorf("failed to update password: %w", err)
}
// Помечаем токен как использованный
if err := s.emailRepo.MarkTokenAsUsed(token); err != nil {
s.logger.Error("Failed to mark token as used",
zap.Error(err),
zap.String("token", token))
}
s.logger.Info("Password reset successfully",
zap.Uint("user_id", verification.UserID))
return nil
}
func (s *EmailService) SendNewsletterToSubscribers(subject, content string) error {
s.logger.Info("Sending newsletter to subscribers",
zap.String("subject", subject),
)
subscribers, err := s.emailRepo.GetUsersWithNewsletter()
if err != nil {
s.logger.Error("Failed to get subscribers",
zap.Error(err),
)
return fmt.Errorf("failed to get subscribers: %w", err)
}
s.logger.Debug("Found subscribers for newsletter",
zap.Int("count", len(subscribers)),
)
var errors []error
for _, user := range subscribers {
if err := s.emailSender.SendNewsletterEmail(user.Email, user.FirstName, subject, content); err != nil {
s.logger.Error("Failed to send newsletter to user",
zap.Uint("user_id", user.ID),
zap.String("email", user.Email),
zap.Error(err))
errors = append(errors, err)
continue
}
s.logger.Debug("Newsletter sent to user",
zap.Uint("user_id", user.ID),
zap.String("email", user.Email))
}
if len(errors) > 0 {
s.logger.Error("Failed to send newsletter to some users",
zap.Int("failed_count", len(errors)),
zap.Int("total_subscribers", len(subscribers)),
)
return fmt.Errorf("failed to send newsletter to %d users", len(errors))
}
s.logger.Info("Newsletter sent to all subscribers",
zap.Int("total_subscribers", len(subscribers)))
return nil
}
func (s *EmailService) CleanupExpiredTokens() error {
s.logger.Info("Cleaning up expired tokens")
if err := s.emailRepo.DeleteExpiredTokens(); err != nil {
s.logger.Error("Failed to cleanup expired tokens",
zap.Error(err),
)
return fmt.Errorf("failed to cleanup expired tokens: %w", err)
}
s.logger.Info("Expired tokens cleaned up successfully")
return nil
}
// GetUserByID возвращает пользователя по ID
func (s *EmailService) GetUserByID(userID uint) (*models.User, error) {
s.logger.Info("Getting user by ID",
zap.Uint("user_id", userID),
)
user, err := s.userRepo.GetUserByID(userID)
if err != nil {
s.logger.Error("Failed to get user by ID",
zap.Uint("user_id", userID),
zap.Error(err),
)
return nil, fmt.Errorf("failed to get user: %w", err)
}
s.logger.Debug("User retrieved successfully",
zap.Uint("user_id", userID),
zap.String("email", user.Email),
)
return user, nil
}