tenseleyflow/shithub / cfbcdab

Browse files

Add usersdb sqlc package: queries for users, emails, resets, verifications, throttle

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
cfbcdab4a389d4777bcf7f4e5f3f2b0cda5563a3
Parents
562bd24
Tree
11e606d

14 changed files

StatusFile+-
A internal/users/queries/auth_throttle.sql 24 0
A internal/users/queries/email_verifications.sql 19 0
A internal/users/queries/password_resets.sql 19 0
A internal/users/queries/user_emails.sql 45 0
A internal/users/queries/users.sql 54 0
A internal/users/sqlc/auth_throttle.sql.go 73 0
A internal/users/sqlc/db.go 25 0
A internal/users/sqlc/email_verifications.sql.go 80 0
A internal/users/sqlc/models.go 76 0
A internal/users/sqlc/password_resets.sql.go 80 0
A internal/users/sqlc/querier.go 51 0
A internal/users/sqlc/user_emails.sql.go 190 0
A internal/users/sqlc/users.sql.go 196 0
M sqlc.yaml 16 0
internal/users/queries/auth_throttle.sqladded
@@ -0,0 +1,24 @@
1
+-- SPDX-License-Identifier: AGPL-3.0-or-later
2
+
3
+-- name: BumpAuthThrottle :one
4
+-- Increments the hit counter for (scope, identifier). When the existing
5
+-- window is older than the supplied window-start cutoff, resets to 1 and
6
+-- starts a new window. Returns the post-bump (hits, window_started_at).
7
+INSERT INTO auth_throttle (scope, identifier, hits, window_started_at)
8
+VALUES ($1, $2, 1, now())
9
+ON CONFLICT (scope, identifier) DO UPDATE
10
+SET hits = CASE
11
+        WHEN auth_throttle.window_started_at < $3 THEN 1
12
+        ELSE auth_throttle.hits + 1
13
+    END,
14
+    window_started_at = CASE
15
+        WHEN auth_throttle.window_started_at < $3 THEN now()
16
+        ELSE auth_throttle.window_started_at
17
+    END
18
+RETURNING hits, window_started_at;
19
+
20
+-- name: ResetAuthThrottle :exec
21
+DELETE FROM auth_throttle WHERE scope = $1 AND identifier = $2;
22
+
23
+-- name: PurgeStaleAuthThrottle :exec
24
+DELETE FROM auth_throttle WHERE window_started_at < $1;
internal/users/queries/email_verifications.sqladded
@@ -0,0 +1,19 @@
1
+-- SPDX-License-Identifier: AGPL-3.0-or-later
2
+
3
+-- name: CreateEmailVerification :one
4
+INSERT INTO email_verifications (user_email_id, token_hash, expires_at)
5
+VALUES ($1, $2, $3)
6
+RETURNING id, user_email_id, token_hash, expires_at, used_at, created_at;
7
+
8
+-- name: GetEmailVerificationByTokenHash :one
9
+SELECT id, user_email_id, token_hash, expires_at, used_at, created_at
10
+FROM email_verifications
11
+WHERE token_hash = $1;
12
+
13
+-- name: ConsumeEmailVerification :exec
14
+UPDATE email_verifications
15
+SET used_at = now()
16
+WHERE id = $1 AND used_at IS NULL;
17
+
18
+-- name: DeleteExpiredEmailVerifications :exec
19
+DELETE FROM email_verifications WHERE expires_at < now();
internal/users/queries/password_resets.sqladded
@@ -0,0 +1,19 @@
1
+-- SPDX-License-Identifier: AGPL-3.0-or-later
2
+
3
+-- name: CreatePasswordReset :one
4
+INSERT INTO password_resets (user_id, token_hash, expires_at)
5
+VALUES ($1, $2, $3)
6
+RETURNING id, user_id, token_hash, expires_at, used_at, created_at;
7
+
8
+-- name: GetPasswordResetByTokenHash :one
9
+SELECT id, user_id, token_hash, expires_at, used_at, created_at
10
+FROM password_resets
11
+WHERE token_hash = $1;
12
+
13
+-- name: ConsumePasswordReset :exec
14
+UPDATE password_resets
15
+SET used_at = now()
16
+WHERE id = $1 AND used_at IS NULL;
17
+
18
+-- name: DeleteExpiredPasswordResets :exec
19
+DELETE FROM password_resets WHERE expires_at < now();
internal/users/queries/user_emails.sqladded
@@ -0,0 +1,45 @@
1
+-- SPDX-License-Identifier: AGPL-3.0-or-later
2
+
3
+-- name: CreateUserEmail :one
4
+INSERT INTO user_emails (user_id, email, is_primary, verified, verification_token_hash, verification_sent_at)
5
+VALUES ($1, $2, $3, $4, $5, CASE WHEN $5::bytea IS NULL THEN NULL ELSE now() END)
6
+RETURNING id, user_id, email, is_primary, verified, verification_token_hash,
7
+          verification_sent_at, verified_at, created_at;
8
+
9
+-- name: GetUserEmailByAddress :one
10
+SELECT id, user_id, email, is_primary, verified, verification_token_hash,
11
+       verification_sent_at, verified_at, created_at
12
+FROM user_emails
13
+WHERE email = $1;
14
+
15
+-- name: GetUserEmailByID :one
16
+SELECT id, user_id, email, is_primary, verified, verification_token_hash,
17
+       verification_sent_at, verified_at, created_at
18
+FROM user_emails
19
+WHERE id = $1;
20
+
21
+-- name: ListUserEmailsForUser :many
22
+SELECT id, user_id, email, is_primary, verified, verification_token_hash,
23
+       verification_sent_at, verified_at, created_at
24
+FROM user_emails
25
+WHERE user_id = $1
26
+ORDER BY is_primary DESC, created_at;
27
+
28
+-- name: MarkUserEmailVerified :exec
29
+UPDATE user_emails
30
+SET verified                = true,
31
+    verified_at             = now(),
32
+    verification_token_hash = NULL
33
+WHERE id = $1;
34
+
35
+-- name: SetVerificationToken :exec
36
+UPDATE user_emails
37
+SET verification_token_hash = $2,
38
+    verification_sent_at    = now()
39
+WHERE id = $1;
40
+
41
+-- name: GetUserEmailByVerificationHash :one
42
+SELECT id, user_id, email, is_primary, verified, verification_token_hash,
43
+       verification_sent_at, verified_at, created_at
44
+FROM user_emails
45
+WHERE verification_token_hash = $1;
internal/users/queries/users.sqladded
@@ -0,0 +1,54 @@
1
+-- SPDX-License-Identifier: AGPL-3.0-or-later
2
+
3
+-- name: CreateUser :one
4
+INSERT INTO users (username, display_name, password_hash)
5
+VALUES ($1, $2, $3)
6
+RETURNING id, username, display_name, primary_email_id, password_hash, password_algo,
7
+          password_updated_at, email_verified, last_login_at, suspended_at,
8
+          suspended_reason, deleted_at, created_at, updated_at;
9
+
10
+-- name: GetUserByID :one
11
+SELECT id, username, display_name, primary_email_id, password_hash, password_algo,
12
+       password_updated_at, email_verified, last_login_at, suspended_at,
13
+       suspended_reason, deleted_at, created_at, updated_at
14
+FROM users
15
+WHERE id = $1 AND deleted_at IS NULL;
16
+
17
+-- name: GetUserByUsername :one
18
+SELECT id, username, display_name, primary_email_id, password_hash, password_algo,
19
+       password_updated_at, email_verified, last_login_at, suspended_at,
20
+       suspended_reason, deleted_at, created_at, updated_at
21
+FROM users
22
+WHERE username = $1 AND deleted_at IS NULL;
23
+
24
+-- name: UpdateUserPassword :exec
25
+UPDATE users
26
+SET password_hash       = $2,
27
+    password_algo       = $3,
28
+    password_updated_at = now()
29
+WHERE id = $1;
30
+
31
+-- name: SetUserPrimaryEmail :exec
32
+UPDATE users
33
+SET primary_email_id = $2,
34
+    email_verified   = true
35
+WHERE id = $1;
36
+
37
+-- name: TouchUserLastLogin :exec
38
+UPDATE users
39
+SET last_login_at = now()
40
+WHERE id = $1;
41
+
42
+-- name: SuspendUser :exec
43
+UPDATE users
44
+SET suspended_at     = now(),
45
+    suspended_reason = $2
46
+WHERE id = $1;
47
+
48
+-- name: SoftDeleteUser :exec
49
+UPDATE users
50
+SET deleted_at = now()
51
+WHERE id = $1;
52
+
53
+-- name: CountUsers :one
54
+SELECT count(*) FROM users WHERE deleted_at IS NULL;
internal/users/sqlc/auth_throttle.sql.goadded
@@ -0,0 +1,73 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+// source: auth_throttle.sql
5
+
6
+package usersdb
7
+
8
+import (
9
+	"context"
10
+
11
+	"github.com/jackc/pgx/v5/pgtype"
12
+)
13
+
14
+const bumpAuthThrottle = `-- name: BumpAuthThrottle :one
15
+
16
+INSERT INTO auth_throttle (scope, identifier, hits, window_started_at)
17
+VALUES ($1, $2, 1, now())
18
+ON CONFLICT (scope, identifier) DO UPDATE
19
+SET hits = CASE
20
+        WHEN auth_throttle.window_started_at < $3 THEN 1
21
+        ELSE auth_throttle.hits + 1
22
+    END,
23
+    window_started_at = CASE
24
+        WHEN auth_throttle.window_started_at < $3 THEN now()
25
+        ELSE auth_throttle.window_started_at
26
+    END
27
+RETURNING hits, window_started_at
28
+`
29
+
30
+type BumpAuthThrottleParams struct {
31
+	Scope           string
32
+	Identifier      string
33
+	WindowStartedAt pgtype.Timestamptz
34
+}
35
+
36
+type BumpAuthThrottleRow struct {
37
+	Hits            int32
38
+	WindowStartedAt pgtype.Timestamptz
39
+}
40
+
41
+// SPDX-License-Identifier: AGPL-3.0-or-later
42
+// Increments the hit counter for (scope, identifier). When the existing
43
+// window is older than the supplied window-start cutoff, resets to 1 and
44
+// starts a new window. Returns the post-bump (hits, window_started_at).
45
+func (q *Queries) BumpAuthThrottle(ctx context.Context, db DBTX, arg BumpAuthThrottleParams) (BumpAuthThrottleRow, error) {
46
+	row := db.QueryRow(ctx, bumpAuthThrottle, arg.Scope, arg.Identifier, arg.WindowStartedAt)
47
+	var i BumpAuthThrottleRow
48
+	err := row.Scan(&i.Hits, &i.WindowStartedAt)
49
+	return i, err
50
+}
51
+
52
+const purgeStaleAuthThrottle = `-- name: PurgeStaleAuthThrottle :exec
53
+DELETE FROM auth_throttle WHERE window_started_at < $1
54
+`
55
+
56
+func (q *Queries) PurgeStaleAuthThrottle(ctx context.Context, db DBTX, windowStartedAt pgtype.Timestamptz) error {
57
+	_, err := db.Exec(ctx, purgeStaleAuthThrottle, windowStartedAt)
58
+	return err
59
+}
60
+
61
+const resetAuthThrottle = `-- name: ResetAuthThrottle :exec
62
+DELETE FROM auth_throttle WHERE scope = $1 AND identifier = $2
63
+`
64
+
65
+type ResetAuthThrottleParams struct {
66
+	Scope      string
67
+	Identifier string
68
+}
69
+
70
+func (q *Queries) ResetAuthThrottle(ctx context.Context, db DBTX, arg ResetAuthThrottleParams) error {
71
+	_, err := db.Exec(ctx, resetAuthThrottle, arg.Scope, arg.Identifier)
72
+	return err
73
+}
internal/users/sqlc/db.goadded
@@ -0,0 +1,25 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+
5
+package usersdb
6
+
7
+import (
8
+	"context"
9
+
10
+	"github.com/jackc/pgx/v5"
11
+	"github.com/jackc/pgx/v5/pgconn"
12
+)
13
+
14
+type DBTX interface {
15
+	Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
16
+	Query(context.Context, string, ...interface{}) (pgx.Rows, error)
17
+	QueryRow(context.Context, string, ...interface{}) pgx.Row
18
+}
19
+
20
+func New() *Queries {
21
+	return &Queries{}
22
+}
23
+
24
+type Queries struct {
25
+}
internal/users/sqlc/email_verifications.sql.goadded
@@ -0,0 +1,80 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+// source: email_verifications.sql
5
+
6
+package usersdb
7
+
8
+import (
9
+	"context"
10
+
11
+	"github.com/jackc/pgx/v5/pgtype"
12
+)
13
+
14
+const consumeEmailVerification = `-- name: ConsumeEmailVerification :exec
15
+UPDATE email_verifications
16
+SET used_at = now()
17
+WHERE id = $1 AND used_at IS NULL
18
+`
19
+
20
+func (q *Queries) ConsumeEmailVerification(ctx context.Context, db DBTX, id int64) error {
21
+	_, err := db.Exec(ctx, consumeEmailVerification, id)
22
+	return err
23
+}
24
+
25
+const createEmailVerification = `-- name: CreateEmailVerification :one
26
+
27
+INSERT INTO email_verifications (user_email_id, token_hash, expires_at)
28
+VALUES ($1, $2, $3)
29
+RETURNING id, user_email_id, token_hash, expires_at, used_at, created_at
30
+`
31
+
32
+type CreateEmailVerificationParams struct {
33
+	UserEmailID int64
34
+	TokenHash   []byte
35
+	ExpiresAt   pgtype.Timestamptz
36
+}
37
+
38
+// SPDX-License-Identifier: AGPL-3.0-or-later
39
+func (q *Queries) CreateEmailVerification(ctx context.Context, db DBTX, arg CreateEmailVerificationParams) (EmailVerification, error) {
40
+	row := db.QueryRow(ctx, createEmailVerification, arg.UserEmailID, arg.TokenHash, arg.ExpiresAt)
41
+	var i EmailVerification
42
+	err := row.Scan(
43
+		&i.ID,
44
+		&i.UserEmailID,
45
+		&i.TokenHash,
46
+		&i.ExpiresAt,
47
+		&i.UsedAt,
48
+		&i.CreatedAt,
49
+	)
50
+	return i, err
51
+}
52
+
53
+const deleteExpiredEmailVerifications = `-- name: DeleteExpiredEmailVerifications :exec
54
+DELETE FROM email_verifications WHERE expires_at < now()
55
+`
56
+
57
+func (q *Queries) DeleteExpiredEmailVerifications(ctx context.Context, db DBTX) error {
58
+	_, err := db.Exec(ctx, deleteExpiredEmailVerifications)
59
+	return err
60
+}
61
+
62
+const getEmailVerificationByTokenHash = `-- name: GetEmailVerificationByTokenHash :one
63
+SELECT id, user_email_id, token_hash, expires_at, used_at, created_at
64
+FROM email_verifications
65
+WHERE token_hash = $1
66
+`
67
+
68
+func (q *Queries) GetEmailVerificationByTokenHash(ctx context.Context, db DBTX, tokenHash []byte) (EmailVerification, error) {
69
+	row := db.QueryRow(ctx, getEmailVerificationByTokenHash, tokenHash)
70
+	var i EmailVerification
71
+	err := row.Scan(
72
+		&i.ID,
73
+		&i.UserEmailID,
74
+		&i.TokenHash,
75
+		&i.ExpiresAt,
76
+		&i.UsedAt,
77
+		&i.CreatedAt,
78
+	)
79
+	return i, err
80
+}
internal/users/sqlc/models.goadded
@@ -0,0 +1,76 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+
5
+package usersdb
6
+
7
+import (
8
+	"github.com/jackc/pgx/v5/pgtype"
9
+)
10
+
11
+type AuthThrottle struct {
12
+	ID              int64
13
+	Scope           string
14
+	Identifier      string
15
+	Hits            int32
16
+	WindowStartedAt pgtype.Timestamptz
17
+}
18
+
19
+type EmailVerification struct {
20
+	ID          int64
21
+	UserEmailID int64
22
+	TokenHash   []byte
23
+	ExpiresAt   pgtype.Timestamptz
24
+	UsedAt      pgtype.Timestamptz
25
+	CreatedAt   pgtype.Timestamptz
26
+}
27
+
28
+type Meta struct {
29
+	Key       string
30
+	Value     []byte
31
+	UpdatedAt pgtype.Timestamptz
32
+}
33
+
34
+type PasswordReset struct {
35
+	ID        int64
36
+	UserID    int64
37
+	TokenHash []byte
38
+	ExpiresAt pgtype.Timestamptz
39
+	UsedAt    pgtype.Timestamptz
40
+	CreatedAt pgtype.Timestamptz
41
+}
42
+
43
+type User struct {
44
+	ID                int64
45
+	Username          string
46
+	DisplayName       string
47
+	PrimaryEmailID    pgtype.Int8
48
+	PasswordHash      string
49
+	PasswordAlgo      string
50
+	PasswordUpdatedAt pgtype.Timestamptz
51
+	EmailVerified     bool
52
+	LastLoginAt       pgtype.Timestamptz
53
+	SuspendedAt       pgtype.Timestamptz
54
+	SuspendedReason   pgtype.Text
55
+	DeletedAt         pgtype.Timestamptz
56
+	CreatedAt         pgtype.Timestamptz
57
+	UpdatedAt         pgtype.Timestamptz
58
+}
59
+
60
+type UserEmail struct {
61
+	ID                    int64
62
+	UserID                int64
63
+	Email                 string
64
+	IsPrimary             bool
65
+	Verified              bool
66
+	VerificationTokenHash []byte
67
+	VerificationSentAt    pgtype.Timestamptz
68
+	VerifiedAt            pgtype.Timestamptz
69
+	CreatedAt             pgtype.Timestamptz
70
+}
71
+
72
+type UsernameRedirect struct {
73
+	OldUsername string
74
+	UserID      int64
75
+	ChangedAt   pgtype.Timestamptz
76
+}
internal/users/sqlc/password_resets.sql.goadded
@@ -0,0 +1,80 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+// source: password_resets.sql
5
+
6
+package usersdb
7
+
8
+import (
9
+	"context"
10
+
11
+	"github.com/jackc/pgx/v5/pgtype"
12
+)
13
+
14
+const consumePasswordReset = `-- name: ConsumePasswordReset :exec
15
+UPDATE password_resets
16
+SET used_at = now()
17
+WHERE id = $1 AND used_at IS NULL
18
+`
19
+
20
+func (q *Queries) ConsumePasswordReset(ctx context.Context, db DBTX, id int64) error {
21
+	_, err := db.Exec(ctx, consumePasswordReset, id)
22
+	return err
23
+}
24
+
25
+const createPasswordReset = `-- name: CreatePasswordReset :one
26
+
27
+INSERT INTO password_resets (user_id, token_hash, expires_at)
28
+VALUES ($1, $2, $3)
29
+RETURNING id, user_id, token_hash, expires_at, used_at, created_at
30
+`
31
+
32
+type CreatePasswordResetParams struct {
33
+	UserID    int64
34
+	TokenHash []byte
35
+	ExpiresAt pgtype.Timestamptz
36
+}
37
+
38
+// SPDX-License-Identifier: AGPL-3.0-or-later
39
+func (q *Queries) CreatePasswordReset(ctx context.Context, db DBTX, arg CreatePasswordResetParams) (PasswordReset, error) {
40
+	row := db.QueryRow(ctx, createPasswordReset, arg.UserID, arg.TokenHash, arg.ExpiresAt)
41
+	var i PasswordReset
42
+	err := row.Scan(
43
+		&i.ID,
44
+		&i.UserID,
45
+		&i.TokenHash,
46
+		&i.ExpiresAt,
47
+		&i.UsedAt,
48
+		&i.CreatedAt,
49
+	)
50
+	return i, err
51
+}
52
+
53
+const deleteExpiredPasswordResets = `-- name: DeleteExpiredPasswordResets :exec
54
+DELETE FROM password_resets WHERE expires_at < now()
55
+`
56
+
57
+func (q *Queries) DeleteExpiredPasswordResets(ctx context.Context, db DBTX) error {
58
+	_, err := db.Exec(ctx, deleteExpiredPasswordResets)
59
+	return err
60
+}
61
+
62
+const getPasswordResetByTokenHash = `-- name: GetPasswordResetByTokenHash :one
63
+SELECT id, user_id, token_hash, expires_at, used_at, created_at
64
+FROM password_resets
65
+WHERE token_hash = $1
66
+`
67
+
68
+func (q *Queries) GetPasswordResetByTokenHash(ctx context.Context, db DBTX, tokenHash []byte) (PasswordReset, error) {
69
+	row := db.QueryRow(ctx, getPasswordResetByTokenHash, tokenHash)
70
+	var i PasswordReset
71
+	err := row.Scan(
72
+		&i.ID,
73
+		&i.UserID,
74
+		&i.TokenHash,
75
+		&i.ExpiresAt,
76
+		&i.UsedAt,
77
+		&i.CreatedAt,
78
+	)
79
+	return i, err
80
+}
internal/users/sqlc/querier.goadded
@@ -0,0 +1,51 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+
5
+package usersdb
6
+
7
+import (
8
+	"context"
9
+
10
+	"github.com/jackc/pgx/v5/pgtype"
11
+)
12
+
13
+type Querier interface {
14
+	// SPDX-License-Identifier: AGPL-3.0-or-later
15
+	// Increments the hit counter for (scope, identifier). When the existing
16
+	// window is older than the supplied window-start cutoff, resets to 1 and
17
+	// starts a new window. Returns the post-bump (hits, window_started_at).
18
+	BumpAuthThrottle(ctx context.Context, db DBTX, arg BumpAuthThrottleParams) (BumpAuthThrottleRow, error)
19
+	ConsumeEmailVerification(ctx context.Context, db DBTX, id int64) error
20
+	ConsumePasswordReset(ctx context.Context, db DBTX, id int64) error
21
+	CountUsers(ctx context.Context, db DBTX) (int64, error)
22
+	// SPDX-License-Identifier: AGPL-3.0-or-later
23
+	CreateEmailVerification(ctx context.Context, db DBTX, arg CreateEmailVerificationParams) (EmailVerification, error)
24
+	// SPDX-License-Identifier: AGPL-3.0-or-later
25
+	CreatePasswordReset(ctx context.Context, db DBTX, arg CreatePasswordResetParams) (PasswordReset, error)
26
+	// SPDX-License-Identifier: AGPL-3.0-or-later
27
+	CreateUser(ctx context.Context, db DBTX, arg CreateUserParams) (User, error)
28
+	// SPDX-License-Identifier: AGPL-3.0-or-later
29
+	CreateUserEmail(ctx context.Context, db DBTX, arg CreateUserEmailParams) (UserEmail, error)
30
+	DeleteExpiredEmailVerifications(ctx context.Context, db DBTX) error
31
+	DeleteExpiredPasswordResets(ctx context.Context, db DBTX) error
32
+	GetEmailVerificationByTokenHash(ctx context.Context, db DBTX, tokenHash []byte) (EmailVerification, error)
33
+	GetPasswordResetByTokenHash(ctx context.Context, db DBTX, tokenHash []byte) (PasswordReset, error)
34
+	GetUserByID(ctx context.Context, db DBTX, id int64) (User, error)
35
+	GetUserByUsername(ctx context.Context, db DBTX, username string) (User, error)
36
+	GetUserEmailByAddress(ctx context.Context, db DBTX, email string) (UserEmail, error)
37
+	GetUserEmailByID(ctx context.Context, db DBTX, id int64) (UserEmail, error)
38
+	GetUserEmailByVerificationHash(ctx context.Context, db DBTX, verificationTokenHash []byte) (UserEmail, error)
39
+	ListUserEmailsForUser(ctx context.Context, db DBTX, userID int64) ([]UserEmail, error)
40
+	MarkUserEmailVerified(ctx context.Context, db DBTX, id int64) error
41
+	PurgeStaleAuthThrottle(ctx context.Context, db DBTX, windowStartedAt pgtype.Timestamptz) error
42
+	ResetAuthThrottle(ctx context.Context, db DBTX, arg ResetAuthThrottleParams) error
43
+	SetUserPrimaryEmail(ctx context.Context, db DBTX, arg SetUserPrimaryEmailParams) error
44
+	SetVerificationToken(ctx context.Context, db DBTX, arg SetVerificationTokenParams) error
45
+	SoftDeleteUser(ctx context.Context, db DBTX, id int64) error
46
+	SuspendUser(ctx context.Context, db DBTX, arg SuspendUserParams) error
47
+	TouchUserLastLogin(ctx context.Context, db DBTX, id int64) error
48
+	UpdateUserPassword(ctx context.Context, db DBTX, arg UpdateUserPasswordParams) error
49
+}
50
+
51
+var _ Querier = (*Queries)(nil)
internal/users/sqlc/user_emails.sql.goadded
@@ -0,0 +1,190 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+// source: user_emails.sql
5
+
6
+package usersdb
7
+
8
+import (
9
+	"context"
10
+)
11
+
12
+const createUserEmail = `-- name: CreateUserEmail :one
13
+
14
+INSERT INTO user_emails (user_id, email, is_primary, verified, verification_token_hash, verification_sent_at)
15
+VALUES ($1, $2, $3, $4, $5, CASE WHEN $5::bytea IS NULL THEN NULL ELSE now() END)
16
+RETURNING id, user_id, email, is_primary, verified, verification_token_hash,
17
+          verification_sent_at, verified_at, created_at
18
+`
19
+
20
+type CreateUserEmailParams struct {
21
+	UserID                int64
22
+	Email                 string
23
+	IsPrimary             bool
24
+	Verified              bool
25
+	VerificationTokenHash []byte
26
+}
27
+
28
+// SPDX-License-Identifier: AGPL-3.0-or-later
29
+func (q *Queries) CreateUserEmail(ctx context.Context, db DBTX, arg CreateUserEmailParams) (UserEmail, error) {
30
+	row := db.QueryRow(ctx, createUserEmail,
31
+		arg.UserID,
32
+		arg.Email,
33
+		arg.IsPrimary,
34
+		arg.Verified,
35
+		arg.VerificationTokenHash,
36
+	)
37
+	var i UserEmail
38
+	err := row.Scan(
39
+		&i.ID,
40
+		&i.UserID,
41
+		&i.Email,
42
+		&i.IsPrimary,
43
+		&i.Verified,
44
+		&i.VerificationTokenHash,
45
+		&i.VerificationSentAt,
46
+		&i.VerifiedAt,
47
+		&i.CreatedAt,
48
+	)
49
+	return i, err
50
+}
51
+
52
+const getUserEmailByAddress = `-- name: GetUserEmailByAddress :one
53
+SELECT id, user_id, email, is_primary, verified, verification_token_hash,
54
+       verification_sent_at, verified_at, created_at
55
+FROM user_emails
56
+WHERE email = $1
57
+`
58
+
59
+func (q *Queries) GetUserEmailByAddress(ctx context.Context, db DBTX, email string) (UserEmail, error) {
60
+	row := db.QueryRow(ctx, getUserEmailByAddress, email)
61
+	var i UserEmail
62
+	err := row.Scan(
63
+		&i.ID,
64
+		&i.UserID,
65
+		&i.Email,
66
+		&i.IsPrimary,
67
+		&i.Verified,
68
+		&i.VerificationTokenHash,
69
+		&i.VerificationSentAt,
70
+		&i.VerifiedAt,
71
+		&i.CreatedAt,
72
+	)
73
+	return i, err
74
+}
75
+
76
+const getUserEmailByID = `-- name: GetUserEmailByID :one
77
+SELECT id, user_id, email, is_primary, verified, verification_token_hash,
78
+       verification_sent_at, verified_at, created_at
79
+FROM user_emails
80
+WHERE id = $1
81
+`
82
+
83
+func (q *Queries) GetUserEmailByID(ctx context.Context, db DBTX, id int64) (UserEmail, error) {
84
+	row := db.QueryRow(ctx, getUserEmailByID, id)
85
+	var i UserEmail
86
+	err := row.Scan(
87
+		&i.ID,
88
+		&i.UserID,
89
+		&i.Email,
90
+		&i.IsPrimary,
91
+		&i.Verified,
92
+		&i.VerificationTokenHash,
93
+		&i.VerificationSentAt,
94
+		&i.VerifiedAt,
95
+		&i.CreatedAt,
96
+	)
97
+	return i, err
98
+}
99
+
100
+const getUserEmailByVerificationHash = `-- name: GetUserEmailByVerificationHash :one
101
+SELECT id, user_id, email, is_primary, verified, verification_token_hash,
102
+       verification_sent_at, verified_at, created_at
103
+FROM user_emails
104
+WHERE verification_token_hash = $1
105
+`
106
+
107
+func (q *Queries) GetUserEmailByVerificationHash(ctx context.Context, db DBTX, verificationTokenHash []byte) (UserEmail, error) {
108
+	row := db.QueryRow(ctx, getUserEmailByVerificationHash, verificationTokenHash)
109
+	var i UserEmail
110
+	err := row.Scan(
111
+		&i.ID,
112
+		&i.UserID,
113
+		&i.Email,
114
+		&i.IsPrimary,
115
+		&i.Verified,
116
+		&i.VerificationTokenHash,
117
+		&i.VerificationSentAt,
118
+		&i.VerifiedAt,
119
+		&i.CreatedAt,
120
+	)
121
+	return i, err
122
+}
123
+
124
+const listUserEmailsForUser = `-- name: ListUserEmailsForUser :many
125
+SELECT id, user_id, email, is_primary, verified, verification_token_hash,
126
+       verification_sent_at, verified_at, created_at
127
+FROM user_emails
128
+WHERE user_id = $1
129
+ORDER BY is_primary DESC, created_at
130
+`
131
+
132
+func (q *Queries) ListUserEmailsForUser(ctx context.Context, db DBTX, userID int64) ([]UserEmail, error) {
133
+	rows, err := db.Query(ctx, listUserEmailsForUser, userID)
134
+	if err != nil {
135
+		return nil, err
136
+	}
137
+	defer rows.Close()
138
+	items := []UserEmail{}
139
+	for rows.Next() {
140
+		var i UserEmail
141
+		if err := rows.Scan(
142
+			&i.ID,
143
+			&i.UserID,
144
+			&i.Email,
145
+			&i.IsPrimary,
146
+			&i.Verified,
147
+			&i.VerificationTokenHash,
148
+			&i.VerificationSentAt,
149
+			&i.VerifiedAt,
150
+			&i.CreatedAt,
151
+		); err != nil {
152
+			return nil, err
153
+		}
154
+		items = append(items, i)
155
+	}
156
+	if err := rows.Err(); err != nil {
157
+		return nil, err
158
+	}
159
+	return items, nil
160
+}
161
+
162
+const markUserEmailVerified = `-- name: MarkUserEmailVerified :exec
163
+UPDATE user_emails
164
+SET verified                = true,
165
+    verified_at             = now(),
166
+    verification_token_hash = NULL
167
+WHERE id = $1
168
+`
169
+
170
+func (q *Queries) MarkUserEmailVerified(ctx context.Context, db DBTX, id int64) error {
171
+	_, err := db.Exec(ctx, markUserEmailVerified, id)
172
+	return err
173
+}
174
+
175
+const setVerificationToken = `-- name: SetVerificationToken :exec
176
+UPDATE user_emails
177
+SET verification_token_hash = $2,
178
+    verification_sent_at    = now()
179
+WHERE id = $1
180
+`
181
+
182
+type SetVerificationTokenParams struct {
183
+	ID                    int64
184
+	VerificationTokenHash []byte
185
+}
186
+
187
+func (q *Queries) SetVerificationToken(ctx context.Context, db DBTX, arg SetVerificationTokenParams) error {
188
+	_, err := db.Exec(ctx, setVerificationToken, arg.ID, arg.VerificationTokenHash)
189
+	return err
190
+}
internal/users/sqlc/users.sql.goadded
@@ -0,0 +1,196 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+// source: users.sql
5
+
6
+package usersdb
7
+
8
+import (
9
+	"context"
10
+
11
+	"github.com/jackc/pgx/v5/pgtype"
12
+)
13
+
14
+const countUsers = `-- name: CountUsers :one
15
+SELECT count(*) FROM users WHERE deleted_at IS NULL
16
+`
17
+
18
+func (q *Queries) CountUsers(ctx context.Context, db DBTX) (int64, error) {
19
+	row := db.QueryRow(ctx, countUsers)
20
+	var count int64
21
+	err := row.Scan(&count)
22
+	return count, err
23
+}
24
+
25
+const createUser = `-- name: CreateUser :one
26
+
27
+INSERT INTO users (username, display_name, password_hash)
28
+VALUES ($1, $2, $3)
29
+RETURNING id, username, display_name, primary_email_id, password_hash, password_algo,
30
+          password_updated_at, email_verified, last_login_at, suspended_at,
31
+          suspended_reason, deleted_at, created_at, updated_at
32
+`
33
+
34
+type CreateUserParams struct {
35
+	Username     string
36
+	DisplayName  string
37
+	PasswordHash string
38
+}
39
+
40
+// SPDX-License-Identifier: AGPL-3.0-or-later
41
+func (q *Queries) CreateUser(ctx context.Context, db DBTX, arg CreateUserParams) (User, error) {
42
+	row := db.QueryRow(ctx, createUser, arg.Username, arg.DisplayName, arg.PasswordHash)
43
+	var i User
44
+	err := row.Scan(
45
+		&i.ID,
46
+		&i.Username,
47
+		&i.DisplayName,
48
+		&i.PrimaryEmailID,
49
+		&i.PasswordHash,
50
+		&i.PasswordAlgo,
51
+		&i.PasswordUpdatedAt,
52
+		&i.EmailVerified,
53
+		&i.LastLoginAt,
54
+		&i.SuspendedAt,
55
+		&i.SuspendedReason,
56
+		&i.DeletedAt,
57
+		&i.CreatedAt,
58
+		&i.UpdatedAt,
59
+	)
60
+	return i, err
61
+}
62
+
63
+const getUserByID = `-- name: GetUserByID :one
64
+SELECT id, username, display_name, primary_email_id, password_hash, password_algo,
65
+       password_updated_at, email_verified, last_login_at, suspended_at,
66
+       suspended_reason, deleted_at, created_at, updated_at
67
+FROM users
68
+WHERE id = $1 AND deleted_at IS NULL
69
+`
70
+
71
+func (q *Queries) GetUserByID(ctx context.Context, db DBTX, id int64) (User, error) {
72
+	row := db.QueryRow(ctx, getUserByID, id)
73
+	var i User
74
+	err := row.Scan(
75
+		&i.ID,
76
+		&i.Username,
77
+		&i.DisplayName,
78
+		&i.PrimaryEmailID,
79
+		&i.PasswordHash,
80
+		&i.PasswordAlgo,
81
+		&i.PasswordUpdatedAt,
82
+		&i.EmailVerified,
83
+		&i.LastLoginAt,
84
+		&i.SuspendedAt,
85
+		&i.SuspendedReason,
86
+		&i.DeletedAt,
87
+		&i.CreatedAt,
88
+		&i.UpdatedAt,
89
+	)
90
+	return i, err
91
+}
92
+
93
+const getUserByUsername = `-- name: GetUserByUsername :one
94
+SELECT id, username, display_name, primary_email_id, password_hash, password_algo,
95
+       password_updated_at, email_verified, last_login_at, suspended_at,
96
+       suspended_reason, deleted_at, created_at, updated_at
97
+FROM users
98
+WHERE username = $1 AND deleted_at IS NULL
99
+`
100
+
101
+func (q *Queries) GetUserByUsername(ctx context.Context, db DBTX, username string) (User, error) {
102
+	row := db.QueryRow(ctx, getUserByUsername, username)
103
+	var i User
104
+	err := row.Scan(
105
+		&i.ID,
106
+		&i.Username,
107
+		&i.DisplayName,
108
+		&i.PrimaryEmailID,
109
+		&i.PasswordHash,
110
+		&i.PasswordAlgo,
111
+		&i.PasswordUpdatedAt,
112
+		&i.EmailVerified,
113
+		&i.LastLoginAt,
114
+		&i.SuspendedAt,
115
+		&i.SuspendedReason,
116
+		&i.DeletedAt,
117
+		&i.CreatedAt,
118
+		&i.UpdatedAt,
119
+	)
120
+	return i, err
121
+}
122
+
123
+const setUserPrimaryEmail = `-- name: SetUserPrimaryEmail :exec
124
+UPDATE users
125
+SET primary_email_id = $2,
126
+    email_verified   = true
127
+WHERE id = $1
128
+`
129
+
130
+type SetUserPrimaryEmailParams struct {
131
+	ID             int64
132
+	PrimaryEmailID pgtype.Int8
133
+}
134
+
135
+func (q *Queries) SetUserPrimaryEmail(ctx context.Context, db DBTX, arg SetUserPrimaryEmailParams) error {
136
+	_, err := db.Exec(ctx, setUserPrimaryEmail, arg.ID, arg.PrimaryEmailID)
137
+	return err
138
+}
139
+
140
+const softDeleteUser = `-- name: SoftDeleteUser :exec
141
+UPDATE users
142
+SET deleted_at = now()
143
+WHERE id = $1
144
+`
145
+
146
+func (q *Queries) SoftDeleteUser(ctx context.Context, db DBTX, id int64) error {
147
+	_, err := db.Exec(ctx, softDeleteUser, id)
148
+	return err
149
+}
150
+
151
+const suspendUser = `-- name: SuspendUser :exec
152
+UPDATE users
153
+SET suspended_at     = now(),
154
+    suspended_reason = $2
155
+WHERE id = $1
156
+`
157
+
158
+type SuspendUserParams struct {
159
+	ID              int64
160
+	SuspendedReason pgtype.Text
161
+}
162
+
163
+func (q *Queries) SuspendUser(ctx context.Context, db DBTX, arg SuspendUserParams) error {
164
+	_, err := db.Exec(ctx, suspendUser, arg.ID, arg.SuspendedReason)
165
+	return err
166
+}
167
+
168
+const touchUserLastLogin = `-- name: TouchUserLastLogin :exec
169
+UPDATE users
170
+SET last_login_at = now()
171
+WHERE id = $1
172
+`
173
+
174
+func (q *Queries) TouchUserLastLogin(ctx context.Context, db DBTX, id int64) error {
175
+	_, err := db.Exec(ctx, touchUserLastLogin, id)
176
+	return err
177
+}
178
+
179
+const updateUserPassword = `-- name: UpdateUserPassword :exec
180
+UPDATE users
181
+SET password_hash       = $2,
182
+    password_algo       = $3,
183
+    password_updated_at = now()
184
+WHERE id = $1
185
+`
186
+
187
+type UpdateUserPasswordParams struct {
188
+	ID           int64
189
+	PasswordHash string
190
+	PasswordAlgo string
191
+}
192
+
193
+func (q *Queries) UpdateUserPassword(ctx context.Context, db DBTX, arg UpdateUserPasswordParams) error {
194
+	_, err := db.Exec(ctx, updateUserPassword, arg.ID, arg.PasswordHash, arg.PasswordAlgo)
195
+	return err
196
+}
sqlc.yamlmodified
@@ -17,3 +17,19 @@ sql:
1717
         emit_methods_with_db_argument: true
1818
         rename:
1919
           tg_set_updated_at: TgSetUpdatedAt
20
+
21
+  - engine: postgresql
22
+    schema: internal/migrationsfs/migrations
23
+    queries: internal/users/queries
24
+    gen:
25
+      go:
26
+        package: usersdb
27
+        out: internal/users/sqlc
28
+        sql_package: pgx/v5
29
+        emit_json_tags: false
30
+        emit_pointers_for_null_types: false
31
+        emit_prepared_queries: false
32
+        emit_interface: true
33
+        emit_exact_table_names: false
34
+        emit_empty_slices: true
35
+        emit_methods_with_db_argument: true