1package usecase
2
3import (
4 "context"
5 "fmt"
6 "time"
7
8 "github.com/bnema/dumber/internal/application/port"
9 "github.com/bnema/dumber/internal/domain/entity"
10 "github.com/bnema/dumber/internal/domain/repository"
11 "github.com/bnema/dumber/internal/logging"
12 "github.com/rs/zerolog"
13)
14
15type ManageSessionUseCase struct {
16 sessionRepo repository.SessionRepository
17 loggerPort port.SessionLogger
18}
19
20func NewManageSessionUseCase(sessionRepo repository.SessionRepository, loggerPort port.SessionLogger) *ManageSessionUseCase {
21 return &ManageSessionUseCase{sessionRepo: sessionRepo, loggerPort: loggerPort}
22}
23
24type StartSessionInput struct {
25 Type entity.SessionType
26 SessionID entity.SessionID
27 Now time.Time
28 LogConfig port.SessionLogConfig
29}
30
31type StartSessionOutput struct {
32 Session *entity.Session
33 Logger zerolog.Logger
34 LogCleanup func()
35}
36
37func (uc *ManageSessionUseCase) StartSession(ctx context.Context, input StartSessionInput) (*StartSessionOutput, error) {
38 log := logging.FromContext(ctx)
39
40 now := input.Now
41 if now.IsZero() {
42 now = time.Now()
43 }
44
45 sessionID := input.SessionID
46 if sessionID == "" {
47 sessionID = entity.SessionID(logging.GenerateSessionID())
48 }
49
50 sType := input.Type
51 if sType == "" {
52 sType = entity.SessionTypeBrowser
53 }
54
55 session := &entity.Session{
56 ID: sessionID,
57 Type: sType,
58 StartedAt: now.UTC(),
59 }
60 if err := session.Validate(); err != nil {
61 return nil, err
62 }
63
64 logger, cleanup, err := uc.loggerPort.CreateLogger(ctx, session.ID, input.LogConfig)
65 if err != nil {
66 log.Warn().Err(err).Msg("failed to create session logger")
67 // proceed with fallback logger returned by adapter
68 }
69
70 sessionCtx := logging.WithContext(ctx, logger)
71 if saveErr := uc.sessionRepo.Save(sessionCtx, session); saveErr != nil {
72 if cleanup != nil {
73 cleanup()
74 }
75 return nil, fmt.Errorf("save session: %w", saveErr)
76 }
77
78 logger.Info().Str("session_id", string(session.ID)).Str("type", string(session.Type)).Msg("session started")
79 return &StartSessionOutput{Session: session, Logger: logger, LogCleanup: cleanup}, nil
80}
81
82func (uc *ManageSessionUseCase) EndSession(ctx context.Context, sessionID entity.SessionID, endedAt time.Time) error {
83 log := logging.FromContext(ctx)
84 if sessionID == "" {
85 return fmt.Errorf("session id required")
86 }
87 if endedAt.IsZero() {
88 endedAt = time.Now()
89 }
90 if err := uc.sessionRepo.MarkEnded(ctx, sessionID, endedAt); err != nil {
91 return fmt.Errorf("mark session ended: %w", err)
92 }
93 log.Info().Str("session_id", string(sessionID)).Msg("session ended")
94 return nil
95}
96
97func (uc *ManageSessionUseCase) GetActiveSession(ctx context.Context) (*entity.Session, error) {
98 return uc.sessionRepo.GetActive(ctx)
99}
100
101func (uc *ManageSessionUseCase) GetRecentSessions(ctx context.Context, limit int) ([]*entity.Session, error) {
102 return uc.sessionRepo.GetRecent(ctx, limit)
103}