143 lines
3.8 KiB
Go
143 lines
3.8 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"crypto/rand"
|
||
"encoding/base64"
|
||
"errors"
|
||
"fmt"
|
||
"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) 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
|
||
}
|
||
|
||
if err := s.mailer.SendConfirmationEmail(user.Email, confirmationToken); err != nil {
|
||
return nil, fmt.Errorf("failed to send confirmation email: %w", 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) {
|
||
userID, err := s.tokenAuth.ValidateRefreshToken(refreshToken)
|
||
if err != nil {
|
||
return nil, errors.New("invalid refresh token")
|
||
}
|
||
|
||
if _, err = s.userRepo.GetByID(ctx, userID); err != nil {
|
||
return nil, errors.New("user not found")
|
||
}
|
||
|
||
return s.tokenAuth.GenerateTokens(userID)
|
||
}
|
||
|
||
func (s *authService) ConfirmEmail(ctx context.Context, token string) error {
|
||
user, err := s.userRepo.GetByConfirmationToken(ctx, token)
|
||
if err != nil {
|
||
return fmt.Errorf("invalid or expired confirmation token")
|
||
}
|
||
|
||
now := time.Now()
|
||
user.EmailConfirmedAt = &now
|
||
user.EmailConfirmationToken = ""
|
||
|
||
return s.userRepo.Update(ctx, user)
|
||
}
|