package service import ( "context" "crypto/rand" "encoding/base64" "errors" "fmt" "tailly_back_v2/internal/domain" "tailly_back_v2/internal/repository" "time" ) type SessionService interface { InitiateSession(ctx context.Context, device *domain.Device) error ConfirmSession(ctx context.Context, token string) (*domain.Session, error) GetActiveSessions(ctx context.Context, userID int) ([]*domain.Session, error) Terminate(ctx context.Context, userID, sessionID int) error // Изменили сигнатуру GetUserSessions(ctx context.Context, userID int) ([]*domain.Session, error) // Добавили этот метод } type sessionService struct { sessionRepo repository.SessionRepository deviceRepo repository.DeviceRepository userRepo repository.UserRepository // Добавлено mailer MailService } func (s *sessionService) GetActiveSessions(ctx context.Context, userID int) ([]*domain.Session, error) { // Используем метод репозитория GetActiveByUser, который уже фильтрует активные сессии sessions, err := s.sessionRepo.GetActiveByUser(ctx, userID) if err != nil { return nil, fmt.Errorf("failed to get active sessions: %w", err) } // Дополнительная обработка, если нужна for _, session := range sessions { // Устанавливаем флаг IsCurrent, если нужно if session.Device != nil { // Можно добавить дополнительную логику проверки текущего устройства // Например, сравнение с текущим устройством пользователя session.IsCurrent = false // Замените на реальную логику } } return sessions, nil } func NewSessionService(sessionRepo repository.SessionRepository, deviceRepo repository.DeviceRepository, userRepo repository.UserRepository, mailer MailService) SessionService { return &sessionService{ sessionRepo: sessionRepo, deviceRepo: deviceRepo, userRepo: userRepo, mailer: mailer, } } func (s *sessionService) InitiateSession(ctx context.Context, device *domain.Device) error { // Генерация токена подтверждения token := make([]byte, 32) if _, err := rand.Read(token); err != nil { return err } device.ConfirmationToken = base64.URLEncoding.EncodeToString(token) device.ExpiresAt = time.Now().Add(24 * time.Hour) if err := s.deviceRepo.Save(ctx, device); err != nil { return err } // Отправка письма user, err := s.userRepo.GetByID(ctx, device.UserID) if err != nil { return err } return s.mailer.SendSessionConfirmation( user.Email, device.ConfirmationToken, device.IPAddress, device.UserAgent, ) } func (s *sessionService) ConfirmSession(ctx context.Context, token string) (*domain.Session, error) { device, err := s.deviceRepo.GetByToken(ctx, token) if err != nil { return nil, err } if time.Now().After(device.ExpiresAt) { return nil, errors.New("confirmation token expired") } session := &domain.Session{ UserID: device.UserID, DeviceID: device.ID, StartedAt: time.Now(), } if err := s.sessionRepo.Save(ctx, session); err != nil { return nil, err } return session, nil } func (s *sessionService) GetUserSessions(ctx context.Context, userID int) ([]*domain.Session, error) { sessions, err := s.sessionRepo.GetActiveByUser(ctx, userID) if err != nil { return nil, err } currentSessionID, _ := ctx.Value("sessionID").(int) for _, session := range sessions { session.IsCurrent = (session.ID == currentSessionID) } return sessions, nil } func (s *sessionService) Terminate(ctx context.Context, userID, sessionID int) error { session, err := s.sessionRepo.GetByID(ctx, sessionID) if err != nil { return err } if session.UserID != userID { return errors.New("unauthorized") } return s.sessionRepo.Terminate(ctx, sessionID) }