tailly_back_v2/internal/service/session_service.go
madipo2611 6f5298d420 v0.0.3
2025-05-03 02:37:08 +03:00

136 lines
3.9 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"
"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)
}