package service import ( "context" "crypto/rand" "encoding/base64" "errors" "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) TerminateSession(ctx context.Context, sessionID int) error } type sessionService struct { sessionRepo repository.SessionRepository deviceRepo repository.DeviceRepository mailer MailService } 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) }