68 lines
2.1 KiB
Go
68 lines
2.1 KiB
Go
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
|
|
}
|