all repos

onasty @ 5495cb456caa00f4564d1c1eed06a766811debaf

a one-time notes service

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

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
refactor: fix annoyances (#97)..., 1 year ago
1
package vertokrepo
2
3
import (
4
	"context"
5
	"time"
6
7
	"github.com/gofrs/uuid/v5"
8
	"github.com/henvic/pgq"
9
	"github.com/olexsmir/onasty/internal/models"
10
	"github.com/olexsmir/onasty/internal/store/psqlutil"
11
)
12
13
type VerificationTokenStorer interface {
14
	Create(
15
		ctx context.Context,
16
		token string,
17
		userID uuid.UUID,
18
		createdAt, expiresAt time.Time,
19
	) error
20
21
	GetUserIDByTokenAndMarkAsUsed(
22
		ctx context.Context,
23
		token string,
24
		usedAT time.Time,
25
	) (uuid.UUID, error)
26
27
	GetTokenOrUpdateTokenByUserID(
28
		ctx context.Context,
29
		userID uuid.UUID,
30
		token string,
31
		tokenExpirationTime time.Time,
32
	) (string, error)
33
}
34
35
var _ VerificationTokenStorer = (*VerificationTokenRepo)(nil)
36
37
type VerificationTokenRepo struct {
38
	db *psqlutil.DB
39
}
40
41
func New(db *psqlutil.DB) *VerificationTokenRepo {
42
	return &VerificationTokenRepo{
43
		db: db,
44
	}
45
}
46
47
func (r *VerificationTokenRepo) Create(
48
	ctx context.Context,
49
	token string,
50
	userID uuid.UUID,
51
	createdAt, expiresAt time.Time,
52
) error {
53
	query, aggs, err := pgq.
54
		Insert("verification_tokens").
55
		Columns("user_id", "token", "created_at", "expires_at").
56
		Values(userID, token, createdAt, expiresAt).
57
		SQL()
58
	if err != nil {
59
		return err
60
	}
61
62
	_, err = r.db.Exec(ctx, query, aggs...)
63
	return err
64
}
65
66
func (r *VerificationTokenRepo) GetUserIDByTokenAndMarkAsUsed(
67
	ctx context.Context,
68
	token string,
69
	usedAt time.Time,
70
) (uuid.UUID, error) {
71
	tx, err := r.db.Begin(ctx)
72
	if err != nil {
73
		return uuid.Nil, err
74
	}
75
	defer tx.Rollback(ctx) //nolint:errcheck
76
77
	var isUsed bool
78
	err = tx.QueryRow(ctx, "select used_at is not null from verification_tokens where token = $1", token).
79
		Scan(&isUsed)
80
	if err != nil {
81
		return uuid.Nil, err
82
	}
83
84
	if isUsed {
85
		return uuid.Nil, models.ErrUserIsAlreadyVerified
86
	}
87
88
	query := `--sql
89
update verification_tokens
90
set used_at = $1
91
where token = $2
92
returning user_id`
93
94
	var userID uuid.UUID
95
	err = tx.QueryRow(ctx, query, usedAt, token).Scan(&userID)
96
	if err != nil {
97
		return uuid.Nil, err
98
	}
99
100
	return userID, tx.Commit(ctx)
101
}
102
103
func (r *VerificationTokenRepo) GetTokenOrUpdateTokenByUserID(
104
	ctx context.Context,
105
	userID uuid.UUID,
106
	token string,
107
	tokenExpirationTime time.Time,
108
) (string, error) {
109
	query := `--sql
110
insert into verification_tokens (user_id, token, expires_at)
111
values ($1, $2, $3)
112
on conflict (user_id)
113
  do update set
114
    token = $2,
115
    expires_at = $3
116
returning token`
117
118
	var res string
119
	err := r.db.QueryRow(ctx, query, userID, token, tokenExpirationTime).Scan(&res)
120
	return res, err
121
}