package store import ( "context" "errors" "sync" "time" "5803025020/w04/internal/models" "github.com/jackc/pgx/v5" ) // ErrSessionNotFound dikembalikan saat token sesi tidak ditemukan (atau sudah dihapus). var ErrSessionNotFound = errors.New("session not found") type SessionStore struct { conn *pgx.Conn mu *sync.Mutex } func NewSessionStore(conn *pgx.Conn, optMu ...*sync.Mutex) *SessionStore { var mu *sync.Mutex if len(optMu) > 0 && optMu[0] != nil { mu = optMu[0] } else { mu = &sync.Mutex{} } return &SessionStore{conn: conn, mu: mu} } func (s *SessionStore) Create(ctx context.Context, session *models.Session) error { s.mu.Lock() defer s.mu.Unlock() if session.CreatedAt.IsZero() { session.CreatedAt = time.Now() } query := `INSERT INTO sessions (token, user_id, created_at) VALUES ($1, $2, $3)` _, err := s.conn.Exec(ctx, query, session.Token, session.UserID, session.CreatedAt) return err } func (s *SessionStore) Get(ctx context.Context, token string) (*models.Session, error) { s.mu.Lock() defer s.mu.Unlock() var sess models.Session query := `SELECT token, user_id, created_at FROM sessions WHERE token = $1` err := s.conn.QueryRow(ctx, query, token).Scan(&sess.Token, &sess.UserID, &sess.CreatedAt) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrSessionNotFound } return nil, err } return &sess, nil } // Delete menghapus token sesi. Jika token tidak ditemukan (sudah dihapus atau // tidak pernah ada), ia mengembalikan ErrSessionNotFound tanpa panic — memanggil // Delete dua kali dengan token yang sama aman dan tidak menghapus token lain. func (s *SessionStore) Delete(ctx context.Context, token string) error { s.mu.Lock() defer s.mu.Unlock() query := `DELETE FROM sessions WHERE token = $1` tag, err := s.conn.Exec(ctx, query, token) if err != nil { return err } if tag.RowsAffected() == 0 { return ErrSessionNotFound } return nil }