tailly_back_v2/internal/service/auth_service.go
madipo2611 95bdb56e70 v0.0.4
2025-05-05 14:15:18 +03:00

159 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"context"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"log"
"tailly_back_v2/internal/domain"
"tailly_back_v2/internal/repository"
"tailly_back_v2/pkg/auth"
"time"
)
type AuthService interface {
Register(ctx context.Context, input RegisterInput) (*domain.User, error)
Login(ctx context.Context, email, password string) (*domain.Tokens, error)
RefreshTokens(ctx context.Context, refreshToken string) (*domain.Tokens, error)
ConfirmEmail(ctx context.Context, token string) (bool, error)
}
type authService struct {
userRepo repository.UserRepository
tokenAuth auth.TokenAuth
mailer MailService
}
func NewAuthService(userRepo repository.UserRepository, tokenAuth *auth.TokenAuth, mailer MailService) AuthService {
return &authService{
userRepo: userRepo,
tokenAuth: *tokenAuth,
mailer: mailer,
}
}
type RegisterInput struct {
Username string
Email string
Password string
}
func (s *authService) Register(ctx context.Context, input RegisterInput) (*domain.User, error) {
_, err := s.userRepo.GetByEmail(ctx, input.Email)
if err == nil {
return nil, errors.New("user with this email already exists")
} else if err != repository.ErrUserNotFound {
return nil, err
}
// Генерация токена подтверждения
token := make([]byte, 32)
if _, err := rand.Read(token); err != nil {
return nil, fmt.Errorf("failed to generate confirmation token: %w", err)
}
confirmationToken := base64.URLEncoding.EncodeToString(token)
hashedPassword, err := auth.HashPassword(input.Password)
if err != nil {
return nil, err
}
user := &domain.User{
Username: input.Username,
Email: input.Email,
Password: hashedPassword,
EmailConfirmationToken: confirmationToken,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if err := s.userRepo.Create(ctx, user); err != nil {
return nil, err
}
go func() {
if err := s.mailer.SendConfirmationEmail(user.Email, confirmationToken); err != nil {
log.Printf("Async email send failed: %v", err)
}
}()
return user, nil
}
func (s *authService) Login(ctx context.Context, email, password string) (*domain.Tokens, error) {
user, err := s.userRepo.GetByEmail(ctx, email)
if err != nil {
if err == repository.ErrUserNotFound {
return nil, errors.New("invalid credentials")
}
return nil, err
}
if !auth.CheckPasswordHash(password, user.Password) {
return nil, errors.New("invalid credentials")
}
// Проверка подтверждения email (добавьте эту проверку)
if user.EmailConfirmedAt == nil {
return nil, errors.New("email not confirmed")
}
tokens, err := s.tokenAuth.GenerateTokens(user.ID)
if err != nil {
return nil, err
}
//Получаем информацию об устройстве (если используется)
//if deviceInfo, ok := ctx.Value("device_info").(*domain.Device); ok {
// // Здесь должна быть проверка isNewDevice и вызов notificationService
// // if isNewDevice { ... }
//}
return tokens, nil
}
func (s *authService) RefreshTokens(ctx context.Context, refreshToken string) (*domain.Tokens, error) {
// Валидируем старый refresh token
userID, err := s.tokenAuth.ValidateRefreshToken(refreshToken)
if err != nil {
log.Printf("Refresh token validation failed: %v", err)
return nil, fmt.Errorf("invalid refresh token: %w", err)
}
// Проверяем существование пользователя
user, err := s.userRepo.GetByID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("user not found: %w", err)
}
// Генерируем новые токены, используя старый refresh token
tokens, err := s.tokenAuth.GenerateTokens(user.ID, refreshToken)
if err != nil {
return nil, fmt.Errorf("failed to generate tokens: %w", err)
}
// Обновляем время последней активности пользователя
//if err := s.userRepo.UpdateLastActive(ctx, user.ID); err != nil {
// log.Printf("Failed to update user last active time: %v", err)
//}
return tokens, nil
}
func (s *authService) ConfirmEmail(ctx context.Context, token string) (bool, error) {
user, err := s.userRepo.GetByConfirmationToken(ctx, token)
if err != nil {
return false, nil
}
now := time.Now()
user.EmailConfirmedAt = &now
user.EmailConfirmationToken = ""
err = s.userRepo.Update(ctx, user)
if err != nil {
return false, err
}
return true, nil
}