package store import ( "context" "errors" "time" "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgerrcode" "github.com/jackc/pgx/v5" ) // CreateSession adds a new session record. func (s *Store) CreateSession(ctx context.Context, userID int64, refreshHash, userAgent, ip string, expiresAt time.Time) (*Session, error) { var sess Session err := s.pool.QueryRow(ctx, ` INSERT INTO sessions(user_id, refresh_hash, user_agent, ip, expires_at) VALUES($1, $2, $3, $4, $5) RETURNING id, user_id, refresh_hash, expires_at, revoked_at, created_at, user_agent, ip `, userID, refreshHash, userAgent, ip, expiresAt).Scan( &sess.ID, &sess.UserID, &sess.RefreshHash, &sess.ExpiresAt, &sess.RevokedAt, &sess.CreatedAt, &sess.UserAgent, &sess.IP, ) if err != nil { var pgErr *pgconn.PgError if errors.As(err, &pgErr) && pgErr.Code == pgerrcode.UniqueViolation { return nil, ErrDuplicate } return nil, err } return &sess, nil } // GetSessionByRefreshHash returns a session by its refresh hash or ErrNotFound. func (s *Store) GetSessionByRefreshHash(ctx context.Context, hash string) (*Session, error) { var sess Session err := s.pool.QueryRow(ctx, ` SELECT id, user_id, refresh_hash, expires_at, revoked_at, created_at, user_agent, ip FROM sessions WHERE refresh_hash=$1 `, hash).Scan( &sess.ID, &sess.UserID, &sess.RefreshHash, &sess.ExpiresAt, &sess.RevokedAt, &sess.CreatedAt, &sess.UserAgent, &sess.IP, ) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrNotFound } return nil, err } return &sess, nil } // RevokeSession sets revoked_at to now() for the given session ID. func (s *Store) RevokeSession(ctx context.Context, id int64) error { _, err := s.pool.Exec(ctx, ` UPDATE sessions SET revoked_at = now() WHERE id = $1 `, id) return err } // RevokeAllSessions revokes all sessions for a given user. func (s *Store) RevokeAllSessions(ctx context.Context, userID int64) error { _, err := s.pool.Exec(ctx, ` UPDATE sessions SET revoked_at = now() WHERE user_id = $1 `, userID) return err }