package memory import ( "context" "log/slog" "sync" "time" "git.threesix.ai/jordan/persona-community-3/services/persona-api/internal/domain" "git.threesix.ai/jordan/persona-community-3/services/persona-api/internal/port" ) // Compile-time interface check. var _ port.AuthCodeRepository = (*AuthCodeRepository)(nil) // AuthCodeRepository is an in-memory auth code store for standalone development. type AuthCodeRepository struct { mu sync.RWMutex codes map[string]*domain.AuthCode } // NewAuthCodeRepository creates a new in-memory auth code repository. func NewAuthCodeRepository() *AuthCodeRepository { return &AuthCodeRepository{ codes: make(map[string]*domain.AuthCode), } } func (r *AuthCodeRepository) Create(_ context.Context, code *domain.AuthCode) error { r.mu.Lock() defer r.mu.Unlock() cp := *code r.codes[code.ID] = &cp // In standalone dev mode the code lives only in memory and is lost on restart. // Always log it so the developer can copy-paste the code from the terminal // even when NOTIFY_URL is set and an email is also being delivered. slog.Warn("[DEV] auth code created — use this code to log in", "email", code.Email, "purpose", code.Purpose, "code", code.Code, "expires_at", code.ExpiresAt.Format("15:04:05"), ) return nil } func (r *AuthCodeRepository) FindValid(_ context.Context, email string, code string, purpose domain.AuthCodePurpose) (*domain.AuthCode, error) { r.mu.RLock() defer r.mu.RUnlock() for _, c := range r.codes { if c.Email == email && c.Code == code && c.Purpose == purpose && c.IsValid() { cp := *c return &cp, nil } } return nil, domain.ErrInvalidAuthCode } func (r *AuthCodeRepository) MarkUsed(_ context.Context, id string) error { r.mu.Lock() defer r.mu.Unlock() c, ok := r.codes[id] if !ok { return domain.ErrInvalidAuthCode } now := time.Now() c.UsedAt = &now return nil } func (r *AuthCodeRepository) DeleteExpired(_ context.Context) (int, error) { r.mu.Lock() defer r.mu.Unlock() now := time.Now() deleted := 0 for id, c := range r.codes { if now.After(c.ExpiresAt) { delete(r.codes, id) deleted++ } } return deleted, nil }