all repos

onasty @ e4abd996ae0618efe2596787bd0a30b88cf81573

a one-time notes service

onasty/internal/store/psql/sessionrepo/sessionrepo.go (view raw)

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
feat: logout from all sessions (#130)..., 12 months ago
1
package sessionrepo
2
3
import (
4
	"context"
5
	"errors"
6
	"time"
7
8
	"github.com/gofrs/uuid/v5"
9
	"github.com/henvic/pgq"
10
	"github.com/jackc/pgx/v5"
11
	"github.com/olexsmir/onasty/internal/models"
12
	"github.com/olexsmir/onasty/internal/store/psqlutil"
13
)
14
15
type SessionStorer interface {
16
	Set(ctx context.Context, usedID uuid.UUID, refreshToken string, expiresAt time.Time) error
17
	GetUserIDByRefreshToken(ctx context.Context, refreshToken string) (uuid.UUID, error)
18
	Update(ctx context.Context, userID uuid.UUID, refreshToken string, newRefreshToken string) error
19
	Delete(ctx context.Context, userID uuid.UUID, refreshToken string) error
20
	DeleteAllByUserID(ctx context.Context, userID uuid.UUID) error
21
}
22
23
var _ SessionStorer = (*SessionRepo)(nil)
24
25
type SessionRepo struct {
26
	db *psqlutil.DB
27
}
28
29
func New(db *psqlutil.DB) *SessionRepo {
30
	return &SessionRepo{
31
		db: db,
32
	}
33
}
34
35
func (s *SessionRepo) Set(
36
	ctx context.Context,
37
	userID uuid.UUID,
38
	refreshToken string,
39
	expiresAt time.Time,
40
) error {
41
	query, args, err := pgq.
42
		Insert("sessions").
43
		Columns("user_id", "refresh_token", "expires_at").
44
		Values(userID, refreshToken, expiresAt).
45
		SQL()
46
	if err != nil {
47
		return err
48
	}
49
50
	_, err = s.db.Exec(ctx, query, args...)
51
	return err
52
}
53
54
func (s *SessionRepo) Update(
55
	ctx context.Context,
56
	userID uuid.UUID,
57
	refreshToken string,
58
	newRefreshToken string,
59
) error {
60
	query := `--sql
61
update sessions
62
set refresh_token = $1
63
where
64
  user_id = $2
65
  and refresh_token = $3
66
  -- and expires_at < now()
67
`
68
69
	res, err := s.db.Exec(ctx, query, newRefreshToken, userID, refreshToken)
70
	if res.RowsAffected() != 1 {
71
		return models.ErrSessionNotFound
72
	}
73
74
	return err
75
}
76
77
func (s *SessionRepo) GetUserIDByRefreshToken(
78
	ctx context.Context,
79
	refreshToken string,
80
) (uuid.UUID, error) {
81
	query, args, err := pgq.
82
		Select("user_id").
83
		From("sessions").
84
		Where(pgq.Eq{"refresh_token": refreshToken}).
85
		SQL()
86
	if err != nil {
87
		return uuid.UUID{}, err
88
	}
89
90
	var userID uuid.UUID
91
	err = s.db.QueryRow(ctx, query, args...).Scan(&userID)
92
	if errors.Is(err, pgx.ErrNoRows) {
93
		return uuid.UUID{}, models.ErrUserNotFound
94
	}
95
96
	return userID, err
97
}
98
99
func (s *SessionRepo) Delete(ctx context.Context, userID uuid.UUID, refreshToken string) error {
100
	query := `--sql
101
DELETE FROM sessions
102
WHERE user_id = $1
103
  AND refresh_token = $2`
104
105
	_, err := s.db.Exec(ctx, query, userID, refreshToken)
106
	return err
107
}
108
109
func (s *SessionRepo) DeleteAllByUserID(ctx context.Context, userID uuid.UUID) error {
110
	query := `--sql
111
delete from sessions
112
where user_id = $1`
113
114
	_, err := s.db.Exec(ctx, query, userID)
115
	return err
116
}