tenseleyflow/shithub / 29578f8

Browse files

S34: admin sqlc package + queries (users/repos/jobs/audit/email)

Authored by espadonne
SHA
29578f8d2ef800ac926984cf662189532f224a5f
Parents
f7d75ba
Tree
401605c

6 changed files

StatusFile+-
A internal/admin/queries/admin.sql 193 0
A internal/admin/sqlc/admin.sql.go 629 0
A internal/admin/sqlc/db.go 25 0
A internal/admin/sqlc/models.go 1976 0
A internal/admin/sqlc/querier.go 63 0
M sqlc.yaml 16 0
internal/admin/queries/admin.sqladded
@@ -0,0 +1,193 @@
1
+-- SPDX-License-Identifier: AGPL-3.0-or-later
2
+--
3
+-- Admin-only query surface for /admin/*. Non-admin handlers should
4
+-- use the per-domain packages (usersdb, reposdb, …) directly. This
5
+-- file collects the cross-cutting reads + writes the admin UI needs:
6
+--   * dashboard counts
7
+--   * user/repo listings with admin-only filters (suspended, deleted)
8
+--   * site-admin flag toggle
9
+--   * transactional_email_log read/write
10
+--
11
+-- Queries that already exist in their domain package (e.g.
12
+-- users.SuspendUser) are reused directly — no duplication here.
13
+
14
+-- ─── dashboard counts ────────────────────────────────────────────
15
+
16
+-- name: CountActiveUsers :one
17
+SELECT COUNT(*) FROM users WHERE deleted_at IS NULL;
18
+
19
+-- name: CountSuspendedUsers :one
20
+SELECT COUNT(*) FROM users WHERE suspended_at IS NOT NULL AND deleted_at IS NULL;
21
+
22
+-- name: CountSiteAdmins :one
23
+SELECT COUNT(*) FROM users WHERE is_site_admin = true AND deleted_at IS NULL;
24
+
25
+-- name: CountActiveRepos :one
26
+SELECT COUNT(*) FROM repos WHERE deleted_at IS NULL;
27
+
28
+-- name: CountActiveOrgs :one
29
+SELECT COUNT(*) FROM orgs WHERE deleted_at IS NULL;
30
+
31
+-- name: CountJobsByStatus :many
32
+-- Status is derived from the timestamp columns since the jobs table
33
+-- doesn't carry an enum: completed > failed > running > queued.
34
+SELECT
35
+    CASE
36
+        WHEN completed_at IS NOT NULL THEN 'completed'
37
+        WHEN failed_at    IS NOT NULL THEN 'failed'
38
+        WHEN locked_at    IS NOT NULL THEN 'running'
39
+        ELSE 'queued'
40
+    END AS status,
41
+    COUNT(*)::bigint AS n
42
+FROM jobs
43
+GROUP BY 1;
44
+
45
+-- ─── users list ──────────────────────────────────────────────────
46
+
47
+-- name: ListUsersForAdmin :many
48
+-- Filters: optional username prefix, optional suspended/deleted-only.
49
+-- All filters use sqlc.narg so the empty case is "no filter".
50
+SELECT id, username, display_name, primary_email_id,
51
+       suspended_at, deleted_at, last_login_at, is_site_admin,
52
+       created_at
53
+FROM users
54
+WHERE
55
+    (sqlc.narg(username_prefix)::text IS NULL OR username::text ILIKE sqlc.narg(username_prefix)::text || '%')
56
+    AND (sqlc.narg(suspended_only)::bool IS NOT TRUE OR suspended_at IS NOT NULL)
57
+    AND (sqlc.narg(deleted_only)::bool IS NOT TRUE OR deleted_at IS NOT NULL)
58
+ORDER BY id DESC
59
+LIMIT $1 OFFSET $2;
60
+
61
+-- name: SetUserSiteAdmin :exec
62
+UPDATE users
63
+   SET is_site_admin = $2,
64
+       updated_at    = now()
65
+ WHERE id = $1;
66
+
67
+-- ─── repos list ──────────────────────────────────────────────────
68
+
69
+-- name: ListReposForAdmin :many
70
+SELECT r.id, r.owner_user_id, r.owner_org_id, r.name, r.visibility,
71
+       r.is_archived, r.deleted_at, r.disk_used_bytes, r.created_at,
72
+       u.username AS owner_user_username,
73
+       o.slug     AS owner_org_slug
74
+FROM repos r
75
+LEFT JOIN users u ON u.id = r.owner_user_id
76
+LEFT JOIN orgs  o ON o.id = r.owner_org_id
77
+WHERE
78
+    (sqlc.narg(name_prefix)::text IS NULL OR r.name ILIKE sqlc.narg(name_prefix)::text || '%')
79
+    AND (sqlc.narg(deleted_only)::bool IS NOT TRUE OR r.deleted_at IS NOT NULL)
80
+    AND (sqlc.narg(archived_only)::bool IS NOT TRUE OR r.is_archived = true)
81
+    AND (sqlc.narg(visibility_filter)::repo_visibility IS NULL OR r.visibility = sqlc.narg(visibility_filter)::repo_visibility)
82
+ORDER BY r.id DESC
83
+LIMIT $1 OFFSET $2;
84
+
85
+-- ─── jobs queue inspector ────────────────────────────────────────
86
+
87
+-- name: ListJobsForAdmin :many
88
+-- The admin can filter by kind + status (computed from the timestamp
89
+-- columns). Status filter values: queued | running | failed | completed.
90
+SELECT id, kind, payload, run_at, attempts, max_attempts,
91
+       last_error, locked_by, locked_at, completed_at, failed_at, created_at,
92
+       CASE
93
+           WHEN completed_at IS NOT NULL THEN 'completed'
94
+           WHEN failed_at    IS NOT NULL THEN 'failed'
95
+           WHEN locked_at    IS NOT NULL THEN 'running'
96
+           ELSE 'queued'
97
+       END AS status
98
+FROM jobs
99
+WHERE
100
+    (sqlc.narg(kind)::text IS NULL OR kind = sqlc.narg(kind)::text)
101
+    AND (
102
+        sqlc.narg(status_filter)::text IS NULL
103
+        OR (sqlc.narg(status_filter)::text = 'queued'    AND completed_at IS NULL AND failed_at IS NULL AND locked_at IS NULL)
104
+        OR (sqlc.narg(status_filter)::text = 'running'   AND completed_at IS NULL AND failed_at IS NULL AND locked_at IS NOT NULL)
105
+        OR (sqlc.narg(status_filter)::text = 'failed'    AND failed_at IS NOT NULL)
106
+        OR (sqlc.narg(status_filter)::text = 'completed' AND completed_at IS NOT NULL)
107
+    )
108
+ORDER BY id DESC
109
+LIMIT $1 OFFSET $2;
110
+
111
+-- name: GetJobForAdmin :one
112
+SELECT id, kind, payload, run_at, attempts, max_attempts,
113
+       last_error, locked_by, locked_at, completed_at, failed_at, created_at
114
+FROM jobs
115
+WHERE id = $1;
116
+
117
+-- name: AdminRetryJob :exec
118
+-- Re-arm a failed/dead job for immediate retry: clear failure state,
119
+-- reset attempts, and set run_at to now() so the pool picks it up
120
+-- on the next tick.
121
+UPDATE jobs
122
+   SET run_at       = now(),
123
+       attempts     = 0,
124
+       last_error   = NULL,
125
+       failed_at    = NULL,
126
+       completed_at = NULL,
127
+       locked_by    = NULL,
128
+       locked_at    = NULL
129
+ WHERE id = $1;
130
+
131
+-- name: AdminDiscardJob :exec
132
+-- Mark a job dead without running it. Caller has already confirmed.
133
+UPDATE jobs
134
+   SET failed_at  = now(),
135
+       last_error = 'discarded by site admin'
136
+ WHERE id = $1;
137
+
138
+-- ─── audit log viewer ────────────────────────────────────────────
139
+
140
+-- name: ListAuditForAdmin :many
141
+-- Filters: actor, action prefix, target type+id, time range. Keyset
142
+-- pagination on (created_at, id) keeps deep pages cheap.
143
+SELECT id, actor_id, action, target_type, target_id, meta, created_at
144
+FROM auth_audit_log
145
+WHERE
146
+    (sqlc.narg(actor_id)::bigint  IS NULL OR actor_id = sqlc.narg(actor_id)::bigint)
147
+    AND (sqlc.narg(action_prefix)::text IS NULL OR action ILIKE sqlc.narg(action_prefix)::text || '%')
148
+    AND (sqlc.narg(target_type)::text IS NULL OR target_type = sqlc.narg(target_type)::text)
149
+    AND (sqlc.narg(target_id)::bigint IS NULL OR target_id = sqlc.narg(target_id)::bigint)
150
+    AND (sqlc.narg(since)::timestamptz IS NULL OR created_at >= sqlc.narg(since)::timestamptz)
151
+    AND (sqlc.narg(until)::timestamptz IS NULL OR created_at <  sqlc.narg(until)::timestamptz)
152
+ORDER BY created_at DESC, id DESC
153
+LIMIT $1 OFFSET $2;
154
+
155
+-- ─── transactional email log ─────────────────────────────────────
156
+
157
+-- name: InsertTransactionalEmail :one
158
+INSERT INTO transactional_email_log (
159
+    recipient_user_id, recipient_email, kind, subject, provider_id,
160
+    status, error_summary
161
+) VALUES (
162
+    sqlc.narg(recipient_user_id)::bigint,
163
+    sqlc.arg(recipient_email),
164
+    sqlc.arg(kind),
165
+    sqlc.arg(subject),
166
+    sqlc.arg(provider_id),
167
+    sqlc.arg(status),
168
+    sqlc.narg(error_summary)::text
169
+)
170
+RETURNING id;
171
+
172
+-- name: ListRecentTransactionalEmails :many
173
+-- Admin email-queue surface. Most recent first; status filter optional.
174
+SELECT id, recipient_user_id, recipient_email, kind, subject,
175
+       provider_id, status, error_summary, sent_at, delivered_at
176
+FROM transactional_email_log
177
+WHERE (sqlc.narg(status)::transactional_email_status IS NULL
178
+       OR status = sqlc.narg(status)::transactional_email_status)
179
+ORDER BY sent_at DESC
180
+LIMIT $1;
181
+
182
+-- name: MarkTransactionalEmailDelivered :exec
183
+UPDATE transactional_email_log
184
+   SET status        = 'sent',
185
+       delivered_at  = now(),
186
+       error_summary = NULL
187
+ WHERE id = $1;
188
+
189
+-- name: MarkTransactionalEmailFailed :exec
190
+UPDATE transactional_email_log
191
+   SET status        = $2,
192
+       error_summary = $3
193
+ WHERE id = $1;
internal/admin/sqlc/admin.sql.goadded
@@ -0,0 +1,629 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+// source: admin.sql
5
+
6
+package admindb
7
+
8
+import (
9
+	"context"
10
+
11
+	"github.com/jackc/pgx/v5/pgtype"
12
+)
13
+
14
+const adminDiscardJob = `-- name: AdminDiscardJob :exec
15
+UPDATE jobs
16
+   SET failed_at  = now(),
17
+       last_error = 'discarded by site admin'
18
+ WHERE id = $1
19
+`
20
+
21
+// Mark a job dead without running it. Caller has already confirmed.
22
+func (q *Queries) AdminDiscardJob(ctx context.Context, db DBTX, id int64) error {
23
+	_, err := db.Exec(ctx, adminDiscardJob, id)
24
+	return err
25
+}
26
+
27
+const adminRetryJob = `-- name: AdminRetryJob :exec
28
+UPDATE jobs
29
+   SET run_at       = now(),
30
+       attempts     = 0,
31
+       last_error   = NULL,
32
+       failed_at    = NULL,
33
+       completed_at = NULL,
34
+       locked_by    = NULL,
35
+       locked_at    = NULL
36
+ WHERE id = $1
37
+`
38
+
39
+// Re-arm a failed/dead job for immediate retry: clear failure state,
40
+// reset attempts, and set run_at to now() so the pool picks it up
41
+// on the next tick.
42
+func (q *Queries) AdminRetryJob(ctx context.Context, db DBTX, id int64) error {
43
+	_, err := db.Exec(ctx, adminRetryJob, id)
44
+	return err
45
+}
46
+
47
+const countActiveOrgs = `-- name: CountActiveOrgs :one
48
+SELECT COUNT(*) FROM orgs WHERE deleted_at IS NULL
49
+`
50
+
51
+func (q *Queries) CountActiveOrgs(ctx context.Context, db DBTX) (int64, error) {
52
+	row := db.QueryRow(ctx, countActiveOrgs)
53
+	var count int64
54
+	err := row.Scan(&count)
55
+	return count, err
56
+}
57
+
58
+const countActiveRepos = `-- name: CountActiveRepos :one
59
+SELECT COUNT(*) FROM repos WHERE deleted_at IS NULL
60
+`
61
+
62
+func (q *Queries) CountActiveRepos(ctx context.Context, db DBTX) (int64, error) {
63
+	row := db.QueryRow(ctx, countActiveRepos)
64
+	var count int64
65
+	err := row.Scan(&count)
66
+	return count, err
67
+}
68
+
69
+const countActiveUsers = `-- name: CountActiveUsers :one
70
+
71
+
72
+SELECT COUNT(*) FROM users WHERE deleted_at IS NULL
73
+`
74
+
75
+// SPDX-License-Identifier: AGPL-3.0-or-later
76
+//
77
+// Admin-only query surface for /admin/*. Non-admin handlers should
78
+// use the per-domain packages (usersdb, reposdb, …) directly. This
79
+// file collects the cross-cutting reads + writes the admin UI needs:
80
+//   - dashboard counts
81
+//   - user/repo listings with admin-only filters (suspended, deleted)
82
+//   - site-admin flag toggle
83
+//   - transactional_email_log read/write
84
+//
85
+// Queries that already exist in their domain package (e.g.
86
+// users.SuspendUser) are reused directly — no duplication here.
87
+// ─── dashboard counts ────────────────────────────────────────────
88
+func (q *Queries) CountActiveUsers(ctx context.Context, db DBTX) (int64, error) {
89
+	row := db.QueryRow(ctx, countActiveUsers)
90
+	var count int64
91
+	err := row.Scan(&count)
92
+	return count, err
93
+}
94
+
95
+const countJobsByStatus = `-- name: CountJobsByStatus :many
96
+SELECT
97
+    CASE
98
+        WHEN completed_at IS NOT NULL THEN 'completed'
99
+        WHEN failed_at    IS NOT NULL THEN 'failed'
100
+        WHEN locked_at    IS NOT NULL THEN 'running'
101
+        ELSE 'queued'
102
+    END AS status,
103
+    COUNT(*)::bigint AS n
104
+FROM jobs
105
+GROUP BY 1
106
+`
107
+
108
+type CountJobsByStatusRow struct {
109
+	Status string
110
+	N      int64
111
+}
112
+
113
+// Status is derived from the timestamp columns since the jobs table
114
+// doesn't carry an enum: completed > failed > running > queued.
115
+func (q *Queries) CountJobsByStatus(ctx context.Context, db DBTX) ([]CountJobsByStatusRow, error) {
116
+	rows, err := db.Query(ctx, countJobsByStatus)
117
+	if err != nil {
118
+		return nil, err
119
+	}
120
+	defer rows.Close()
121
+	items := []CountJobsByStatusRow{}
122
+	for rows.Next() {
123
+		var i CountJobsByStatusRow
124
+		if err := rows.Scan(&i.Status, &i.N); err != nil {
125
+			return nil, err
126
+		}
127
+		items = append(items, i)
128
+	}
129
+	if err := rows.Err(); err != nil {
130
+		return nil, err
131
+	}
132
+	return items, nil
133
+}
134
+
135
+const countSiteAdmins = `-- name: CountSiteAdmins :one
136
+SELECT COUNT(*) FROM users WHERE is_site_admin = true AND deleted_at IS NULL
137
+`
138
+
139
+func (q *Queries) CountSiteAdmins(ctx context.Context, db DBTX) (int64, error) {
140
+	row := db.QueryRow(ctx, countSiteAdmins)
141
+	var count int64
142
+	err := row.Scan(&count)
143
+	return count, err
144
+}
145
+
146
+const countSuspendedUsers = `-- name: CountSuspendedUsers :one
147
+SELECT COUNT(*) FROM users WHERE suspended_at IS NOT NULL AND deleted_at IS NULL
148
+`
149
+
150
+func (q *Queries) CountSuspendedUsers(ctx context.Context, db DBTX) (int64, error) {
151
+	row := db.QueryRow(ctx, countSuspendedUsers)
152
+	var count int64
153
+	err := row.Scan(&count)
154
+	return count, err
155
+}
156
+
157
+const getJobForAdmin = `-- name: GetJobForAdmin :one
158
+SELECT id, kind, payload, run_at, attempts, max_attempts,
159
+       last_error, locked_by, locked_at, completed_at, failed_at, created_at
160
+FROM jobs
161
+WHERE id = $1
162
+`
163
+
164
+func (q *Queries) GetJobForAdmin(ctx context.Context, db DBTX, id int64) (Job, error) {
165
+	row := db.QueryRow(ctx, getJobForAdmin, id)
166
+	var i Job
167
+	err := row.Scan(
168
+		&i.ID,
169
+		&i.Kind,
170
+		&i.Payload,
171
+		&i.RunAt,
172
+		&i.Attempts,
173
+		&i.MaxAttempts,
174
+		&i.LastError,
175
+		&i.LockedBy,
176
+		&i.LockedAt,
177
+		&i.CompletedAt,
178
+		&i.FailedAt,
179
+		&i.CreatedAt,
180
+	)
181
+	return i, err
182
+}
183
+
184
+const insertTransactionalEmail = `-- name: InsertTransactionalEmail :one
185
+
186
+INSERT INTO transactional_email_log (
187
+    recipient_user_id, recipient_email, kind, subject, provider_id,
188
+    status, error_summary
189
+) VALUES (
190
+    $1::bigint,
191
+    $2,
192
+    $3,
193
+    $4,
194
+    $5,
195
+    $6,
196
+    $7::text
197
+)
198
+RETURNING id
199
+`
200
+
201
+type InsertTransactionalEmailParams struct {
202
+	RecipientUserID pgtype.Int8
203
+	RecipientEmail  string
204
+	Kind            string
205
+	Subject         string
206
+	ProviderID      string
207
+	Status          TransactionalEmailStatus
208
+	ErrorSummary    pgtype.Text
209
+}
210
+
211
+// ─── transactional email log ─────────────────────────────────────
212
+func (q *Queries) InsertTransactionalEmail(ctx context.Context, db DBTX, arg InsertTransactionalEmailParams) (int64, error) {
213
+	row := db.QueryRow(ctx, insertTransactionalEmail,
214
+		arg.RecipientUserID,
215
+		arg.RecipientEmail,
216
+		arg.Kind,
217
+		arg.Subject,
218
+		arg.ProviderID,
219
+		arg.Status,
220
+		arg.ErrorSummary,
221
+	)
222
+	var id int64
223
+	err := row.Scan(&id)
224
+	return id, err
225
+}
226
+
227
+const listAuditForAdmin = `-- name: ListAuditForAdmin :many
228
+
229
+SELECT id, actor_id, action, target_type, target_id, meta, created_at
230
+FROM auth_audit_log
231
+WHERE
232
+    ($3::bigint  IS NULL OR actor_id = $3::bigint)
233
+    AND ($4::text IS NULL OR action ILIKE $4::text || '%')
234
+    AND ($5::text IS NULL OR target_type = $5::text)
235
+    AND ($6::bigint IS NULL OR target_id = $6::bigint)
236
+    AND ($7::timestamptz IS NULL OR created_at >= $7::timestamptz)
237
+    AND ($8::timestamptz IS NULL OR created_at <  $8::timestamptz)
238
+ORDER BY created_at DESC, id DESC
239
+LIMIT $1 OFFSET $2
240
+`
241
+
242
+type ListAuditForAdminParams struct {
243
+	Limit        int32
244
+	Offset       int32
245
+	ActorID      pgtype.Int8
246
+	ActionPrefix pgtype.Text
247
+	TargetType   pgtype.Text
248
+	TargetID     pgtype.Int8
249
+	Since        pgtype.Timestamptz
250
+	Until        pgtype.Timestamptz
251
+}
252
+
253
+// ─── audit log viewer ────────────────────────────────────────────
254
+// Filters: actor, action prefix, target type+id, time range. Keyset
255
+// pagination on (created_at, id) keeps deep pages cheap.
256
+func (q *Queries) ListAuditForAdmin(ctx context.Context, db DBTX, arg ListAuditForAdminParams) ([]AuthAuditLog, error) {
257
+	rows, err := db.Query(ctx, listAuditForAdmin,
258
+		arg.Limit,
259
+		arg.Offset,
260
+		arg.ActorID,
261
+		arg.ActionPrefix,
262
+		arg.TargetType,
263
+		arg.TargetID,
264
+		arg.Since,
265
+		arg.Until,
266
+	)
267
+	if err != nil {
268
+		return nil, err
269
+	}
270
+	defer rows.Close()
271
+	items := []AuthAuditLog{}
272
+	for rows.Next() {
273
+		var i AuthAuditLog
274
+		if err := rows.Scan(
275
+			&i.ID,
276
+			&i.ActorID,
277
+			&i.Action,
278
+			&i.TargetType,
279
+			&i.TargetID,
280
+			&i.Meta,
281
+			&i.CreatedAt,
282
+		); err != nil {
283
+			return nil, err
284
+		}
285
+		items = append(items, i)
286
+	}
287
+	if err := rows.Err(); err != nil {
288
+		return nil, err
289
+	}
290
+	return items, nil
291
+}
292
+
293
+const listJobsForAdmin = `-- name: ListJobsForAdmin :many
294
+
295
+SELECT id, kind, payload, run_at, attempts, max_attempts,
296
+       last_error, locked_by, locked_at, completed_at, failed_at, created_at,
297
+       CASE
298
+           WHEN completed_at IS NOT NULL THEN 'completed'
299
+           WHEN failed_at    IS NOT NULL THEN 'failed'
300
+           WHEN locked_at    IS NOT NULL THEN 'running'
301
+           ELSE 'queued'
302
+       END AS status
303
+FROM jobs
304
+WHERE
305
+    ($3::text IS NULL OR kind = $3::text)
306
+    AND (
307
+        $4::text IS NULL
308
+        OR ($4::text = 'queued'    AND completed_at IS NULL AND failed_at IS NULL AND locked_at IS NULL)
309
+        OR ($4::text = 'running'   AND completed_at IS NULL AND failed_at IS NULL AND locked_at IS NOT NULL)
310
+        OR ($4::text = 'failed'    AND failed_at IS NOT NULL)
311
+        OR ($4::text = 'completed' AND completed_at IS NOT NULL)
312
+    )
313
+ORDER BY id DESC
314
+LIMIT $1 OFFSET $2
315
+`
316
+
317
+type ListJobsForAdminParams struct {
318
+	Limit        int32
319
+	Offset       int32
320
+	Kind         pgtype.Text
321
+	StatusFilter pgtype.Text
322
+}
323
+
324
+type ListJobsForAdminRow struct {
325
+	ID          int64
326
+	Kind        string
327
+	Payload     []byte
328
+	RunAt       pgtype.Timestamptz
329
+	Attempts    int32
330
+	MaxAttempts int32
331
+	LastError   pgtype.Text
332
+	LockedBy    pgtype.Text
333
+	LockedAt    pgtype.Timestamptz
334
+	CompletedAt pgtype.Timestamptz
335
+	FailedAt    pgtype.Timestamptz
336
+	CreatedAt   pgtype.Timestamptz
337
+	Status      string
338
+}
339
+
340
+// ─── jobs queue inspector ────────────────────────────────────────
341
+// The admin can filter by kind + status (computed from the timestamp
342
+// columns). Status filter values: queued | running | failed | completed.
343
+func (q *Queries) ListJobsForAdmin(ctx context.Context, db DBTX, arg ListJobsForAdminParams) ([]ListJobsForAdminRow, error) {
344
+	rows, err := db.Query(ctx, listJobsForAdmin,
345
+		arg.Limit,
346
+		arg.Offset,
347
+		arg.Kind,
348
+		arg.StatusFilter,
349
+	)
350
+	if err != nil {
351
+		return nil, err
352
+	}
353
+	defer rows.Close()
354
+	items := []ListJobsForAdminRow{}
355
+	for rows.Next() {
356
+		var i ListJobsForAdminRow
357
+		if err := rows.Scan(
358
+			&i.ID,
359
+			&i.Kind,
360
+			&i.Payload,
361
+			&i.RunAt,
362
+			&i.Attempts,
363
+			&i.MaxAttempts,
364
+			&i.LastError,
365
+			&i.LockedBy,
366
+			&i.LockedAt,
367
+			&i.CompletedAt,
368
+			&i.FailedAt,
369
+			&i.CreatedAt,
370
+			&i.Status,
371
+		); err != nil {
372
+			return nil, err
373
+		}
374
+		items = append(items, i)
375
+	}
376
+	if err := rows.Err(); err != nil {
377
+		return nil, err
378
+	}
379
+	return items, nil
380
+}
381
+
382
+const listRecentTransactionalEmails = `-- name: ListRecentTransactionalEmails :many
383
+SELECT id, recipient_user_id, recipient_email, kind, subject,
384
+       provider_id, status, error_summary, sent_at, delivered_at
385
+FROM transactional_email_log
386
+WHERE ($2::transactional_email_status IS NULL
387
+       OR status = $2::transactional_email_status)
388
+ORDER BY sent_at DESC
389
+LIMIT $1
390
+`
391
+
392
+type ListRecentTransactionalEmailsParams struct {
393
+	Limit  int32
394
+	Status NullTransactionalEmailStatus
395
+}
396
+
397
+// Admin email-queue surface. Most recent first; status filter optional.
398
+func (q *Queries) ListRecentTransactionalEmails(ctx context.Context, db DBTX, arg ListRecentTransactionalEmailsParams) ([]TransactionalEmailLog, error) {
399
+	rows, err := db.Query(ctx, listRecentTransactionalEmails, arg.Limit, arg.Status)
400
+	if err != nil {
401
+		return nil, err
402
+	}
403
+	defer rows.Close()
404
+	items := []TransactionalEmailLog{}
405
+	for rows.Next() {
406
+		var i TransactionalEmailLog
407
+		if err := rows.Scan(
408
+			&i.ID,
409
+			&i.RecipientUserID,
410
+			&i.RecipientEmail,
411
+			&i.Kind,
412
+			&i.Subject,
413
+			&i.ProviderID,
414
+			&i.Status,
415
+			&i.ErrorSummary,
416
+			&i.SentAt,
417
+			&i.DeliveredAt,
418
+		); err != nil {
419
+			return nil, err
420
+		}
421
+		items = append(items, i)
422
+	}
423
+	if err := rows.Err(); err != nil {
424
+		return nil, err
425
+	}
426
+	return items, nil
427
+}
428
+
429
+const listReposForAdmin = `-- name: ListReposForAdmin :many
430
+
431
+SELECT r.id, r.owner_user_id, r.owner_org_id, r.name, r.visibility,
432
+       r.is_archived, r.deleted_at, r.disk_used_bytes, r.created_at,
433
+       u.username AS owner_user_username,
434
+       o.slug     AS owner_org_slug
435
+FROM repos r
436
+LEFT JOIN users u ON u.id = r.owner_user_id
437
+LEFT JOIN orgs  o ON o.id = r.owner_org_id
438
+WHERE
439
+    ($3::text IS NULL OR r.name ILIKE $3::text || '%')
440
+    AND ($4::bool IS NOT TRUE OR r.deleted_at IS NOT NULL)
441
+    AND ($5::bool IS NOT TRUE OR r.is_archived = true)
442
+    AND ($6::repo_visibility IS NULL OR r.visibility = $6::repo_visibility)
443
+ORDER BY r.id DESC
444
+LIMIT $1 OFFSET $2
445
+`
446
+
447
+type ListReposForAdminParams struct {
448
+	Limit            int32
449
+	Offset           int32
450
+	NamePrefix       pgtype.Text
451
+	DeletedOnly      pgtype.Bool
452
+	ArchivedOnly     pgtype.Bool
453
+	VisibilityFilter NullRepoVisibility
454
+}
455
+
456
+type ListReposForAdminRow struct {
457
+	ID                int64
458
+	OwnerUserID       pgtype.Int8
459
+	OwnerOrgID        pgtype.Int8
460
+	Name              string
461
+	Visibility        RepoVisibility
462
+	IsArchived        bool
463
+	DeletedAt         pgtype.Timestamptz
464
+	DiskUsedBytes     int64
465
+	CreatedAt         pgtype.Timestamptz
466
+	OwnerUserUsername pgtype.Text
467
+	OwnerOrgSlug      pgtype.Text
468
+}
469
+
470
+// ─── repos list ──────────────────────────────────────────────────
471
+func (q *Queries) ListReposForAdmin(ctx context.Context, db DBTX, arg ListReposForAdminParams) ([]ListReposForAdminRow, error) {
472
+	rows, err := db.Query(ctx, listReposForAdmin,
473
+		arg.Limit,
474
+		arg.Offset,
475
+		arg.NamePrefix,
476
+		arg.DeletedOnly,
477
+		arg.ArchivedOnly,
478
+		arg.VisibilityFilter,
479
+	)
480
+	if err != nil {
481
+		return nil, err
482
+	}
483
+	defer rows.Close()
484
+	items := []ListReposForAdminRow{}
485
+	for rows.Next() {
486
+		var i ListReposForAdminRow
487
+		if err := rows.Scan(
488
+			&i.ID,
489
+			&i.OwnerUserID,
490
+			&i.OwnerOrgID,
491
+			&i.Name,
492
+			&i.Visibility,
493
+			&i.IsArchived,
494
+			&i.DeletedAt,
495
+			&i.DiskUsedBytes,
496
+			&i.CreatedAt,
497
+			&i.OwnerUserUsername,
498
+			&i.OwnerOrgSlug,
499
+		); err != nil {
500
+			return nil, err
501
+		}
502
+		items = append(items, i)
503
+	}
504
+	if err := rows.Err(); err != nil {
505
+		return nil, err
506
+	}
507
+	return items, nil
508
+}
509
+
510
+const listUsersForAdmin = `-- name: ListUsersForAdmin :many
511
+
512
+SELECT id, username, display_name, primary_email_id,
513
+       suspended_at, deleted_at, last_login_at, is_site_admin,
514
+       created_at
515
+FROM users
516
+WHERE
517
+    ($3::text IS NULL OR username::text ILIKE $3::text || '%')
518
+    AND ($4::bool IS NOT TRUE OR suspended_at IS NOT NULL)
519
+    AND ($5::bool IS NOT TRUE OR deleted_at IS NOT NULL)
520
+ORDER BY id DESC
521
+LIMIT $1 OFFSET $2
522
+`
523
+
524
+type ListUsersForAdminParams struct {
525
+	Limit          int32
526
+	Offset         int32
527
+	UsernamePrefix pgtype.Text
528
+	SuspendedOnly  pgtype.Bool
529
+	DeletedOnly    pgtype.Bool
530
+}
531
+
532
+type ListUsersForAdminRow struct {
533
+	ID             int64
534
+	Username       string
535
+	DisplayName    string
536
+	PrimaryEmailID pgtype.Int8
537
+	SuspendedAt    pgtype.Timestamptz
538
+	DeletedAt      pgtype.Timestamptz
539
+	LastLoginAt    pgtype.Timestamptz
540
+	IsSiteAdmin    bool
541
+	CreatedAt      pgtype.Timestamptz
542
+}
543
+
544
+// ─── users list ──────────────────────────────────────────────────
545
+// Filters: optional username prefix, optional suspended/deleted-only.
546
+// All filters use sqlc.narg so the empty case is "no filter".
547
+func (q *Queries) ListUsersForAdmin(ctx context.Context, db DBTX, arg ListUsersForAdminParams) ([]ListUsersForAdminRow, error) {
548
+	rows, err := db.Query(ctx, listUsersForAdmin,
549
+		arg.Limit,
550
+		arg.Offset,
551
+		arg.UsernamePrefix,
552
+		arg.SuspendedOnly,
553
+		arg.DeletedOnly,
554
+	)
555
+	if err != nil {
556
+		return nil, err
557
+	}
558
+	defer rows.Close()
559
+	items := []ListUsersForAdminRow{}
560
+	for rows.Next() {
561
+		var i ListUsersForAdminRow
562
+		if err := rows.Scan(
563
+			&i.ID,
564
+			&i.Username,
565
+			&i.DisplayName,
566
+			&i.PrimaryEmailID,
567
+			&i.SuspendedAt,
568
+			&i.DeletedAt,
569
+			&i.LastLoginAt,
570
+			&i.IsSiteAdmin,
571
+			&i.CreatedAt,
572
+		); err != nil {
573
+			return nil, err
574
+		}
575
+		items = append(items, i)
576
+	}
577
+	if err := rows.Err(); err != nil {
578
+		return nil, err
579
+	}
580
+	return items, nil
581
+}
582
+
583
+const markTransactionalEmailDelivered = `-- name: MarkTransactionalEmailDelivered :exec
584
+UPDATE transactional_email_log
585
+   SET status        = 'sent',
586
+       delivered_at  = now(),
587
+       error_summary = NULL
588
+ WHERE id = $1
589
+`
590
+
591
+func (q *Queries) MarkTransactionalEmailDelivered(ctx context.Context, db DBTX, id int64) error {
592
+	_, err := db.Exec(ctx, markTransactionalEmailDelivered, id)
593
+	return err
594
+}
595
+
596
+const markTransactionalEmailFailed = `-- name: MarkTransactionalEmailFailed :exec
597
+UPDATE transactional_email_log
598
+   SET status        = $2,
599
+       error_summary = $3
600
+ WHERE id = $1
601
+`
602
+
603
+type MarkTransactionalEmailFailedParams struct {
604
+	ID           int64
605
+	Status       TransactionalEmailStatus
606
+	ErrorSummary pgtype.Text
607
+}
608
+
609
+func (q *Queries) MarkTransactionalEmailFailed(ctx context.Context, db DBTX, arg MarkTransactionalEmailFailedParams) error {
610
+	_, err := db.Exec(ctx, markTransactionalEmailFailed, arg.ID, arg.Status, arg.ErrorSummary)
611
+	return err
612
+}
613
+
614
+const setUserSiteAdmin = `-- name: SetUserSiteAdmin :exec
615
+UPDATE users
616
+   SET is_site_admin = $2,
617
+       updated_at    = now()
618
+ WHERE id = $1
619
+`
620
+
621
+type SetUserSiteAdminParams struct {
622
+	ID          int64
623
+	IsSiteAdmin bool
624
+}
625
+
626
+func (q *Queries) SetUserSiteAdmin(ctx context.Context, db DBTX, arg SetUserSiteAdminParams) error {
627
+	_, err := db.Exec(ctx, setUserSiteAdmin, arg.ID, arg.IsSiteAdmin)
628
+	return err
629
+}
internal/admin/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 admindb
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/admin/sqlc/models.goadded
1976 lines changed — click to load
@@ -0,0 +1,1976 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+
5
+package admindb
6
+
7
+import (
8
+	"database/sql/driver"
9
+	"fmt"
10
+	"net/netip"
11
+
12
+	"github.com/jackc/pgx/v5/pgtype"
13
+)
14
+
15
+type CheckConclusion string
16
+
17
+const (
18
+	CheckConclusionSuccess        CheckConclusion = "success"
19
+	CheckConclusionFailure        CheckConclusion = "failure"
20
+	CheckConclusionNeutral        CheckConclusion = "neutral"
21
+	CheckConclusionCancelled      CheckConclusion = "cancelled"
22
+	CheckConclusionSkipped        CheckConclusion = "skipped"
23
+	CheckConclusionTimedOut       CheckConclusion = "timed_out"
24
+	CheckConclusionActionRequired CheckConclusion = "action_required"
25
+	CheckConclusionStale          CheckConclusion = "stale"
26
+)
27
+
28
+func (e *CheckConclusion) Scan(src interface{}) error {
29
+	switch s := src.(type) {
30
+	case []byte:
31
+		*e = CheckConclusion(s)
32
+	case string:
33
+		*e = CheckConclusion(s)
34
+	default:
35
+		return fmt.Errorf("unsupported scan type for CheckConclusion: %T", src)
36
+	}
37
+	return nil
38
+}
39
+
40
+type NullCheckConclusion struct {
41
+	CheckConclusion CheckConclusion
42
+	Valid           bool // Valid is true if CheckConclusion is not NULL
43
+}
44
+
45
+// Scan implements the Scanner interface.
46
+func (ns *NullCheckConclusion) Scan(value interface{}) error {
47
+	if value == nil {
48
+		ns.CheckConclusion, ns.Valid = "", false
49
+		return nil
50
+	}
51
+	ns.Valid = true
52
+	return ns.CheckConclusion.Scan(value)
53
+}
54
+
55
+// Value implements the driver Valuer interface.
56
+func (ns NullCheckConclusion) Value() (driver.Value, error) {
57
+	if !ns.Valid {
58
+		return nil, nil
59
+	}
60
+	return string(ns.CheckConclusion), nil
61
+}
62
+
63
+type CheckStatus string
64
+
65
+const (
66
+	CheckStatusQueued     CheckStatus = "queued"
67
+	CheckStatusInProgress CheckStatus = "in_progress"
68
+	CheckStatusCompleted  CheckStatus = "completed"
69
+	CheckStatusPending    CheckStatus = "pending"
70
+)
71
+
72
+func (e *CheckStatus) Scan(src interface{}) error {
73
+	switch s := src.(type) {
74
+	case []byte:
75
+		*e = CheckStatus(s)
76
+	case string:
77
+		*e = CheckStatus(s)
78
+	default:
79
+		return fmt.Errorf("unsupported scan type for CheckStatus: %T", src)
80
+	}
81
+	return nil
82
+}
83
+
84
+type NullCheckStatus struct {
85
+	CheckStatus CheckStatus
86
+	Valid       bool // Valid is true if CheckStatus is not NULL
87
+}
88
+
89
+// Scan implements the Scanner interface.
90
+func (ns *NullCheckStatus) Scan(value interface{}) error {
91
+	if value == nil {
92
+		ns.CheckStatus, ns.Valid = "", false
93
+		return nil
94
+	}
95
+	ns.Valid = true
96
+	return ns.CheckStatus.Scan(value)
97
+}
98
+
99
+// Value implements the driver Valuer interface.
100
+func (ns NullCheckStatus) Value() (driver.Value, error) {
101
+	if !ns.Valid {
102
+		return nil, nil
103
+	}
104
+	return string(ns.CheckStatus), nil
105
+}
106
+
107
+type CollabRole string
108
+
109
+const (
110
+	CollabRoleRead     CollabRole = "read"
111
+	CollabRoleTriage   CollabRole = "triage"
112
+	CollabRoleWrite    CollabRole = "write"
113
+	CollabRoleMaintain CollabRole = "maintain"
114
+	CollabRoleAdmin    CollabRole = "admin"
115
+)
116
+
117
+func (e *CollabRole) Scan(src interface{}) error {
118
+	switch s := src.(type) {
119
+	case []byte:
120
+		*e = CollabRole(s)
121
+	case string:
122
+		*e = CollabRole(s)
123
+	default:
124
+		return fmt.Errorf("unsupported scan type for CollabRole: %T", src)
125
+	}
126
+	return nil
127
+}
128
+
129
+type NullCollabRole struct {
130
+	CollabRole CollabRole
131
+	Valid      bool // Valid is true if CollabRole is not NULL
132
+}
133
+
134
+// Scan implements the Scanner interface.
135
+func (ns *NullCollabRole) Scan(value interface{}) error {
136
+	if value == nil {
137
+		ns.CollabRole, ns.Valid = "", false
138
+		return nil
139
+	}
140
+	ns.Valid = true
141
+	return ns.CollabRole.Scan(value)
142
+}
143
+
144
+// Value implements the driver Valuer interface.
145
+func (ns NullCollabRole) Value() (driver.Value, error) {
146
+	if !ns.Valid {
147
+		return nil, nil
148
+	}
149
+	return string(ns.CollabRole), nil
150
+}
151
+
152
+type IssueKind string
153
+
154
+const (
155
+	IssueKindIssue IssueKind = "issue"
156
+	IssueKindPr    IssueKind = "pr"
157
+)
158
+
159
+func (e *IssueKind) Scan(src interface{}) error {
160
+	switch s := src.(type) {
161
+	case []byte:
162
+		*e = IssueKind(s)
163
+	case string:
164
+		*e = IssueKind(s)
165
+	default:
166
+		return fmt.Errorf("unsupported scan type for IssueKind: %T", src)
167
+	}
168
+	return nil
169
+}
170
+
171
+type NullIssueKind struct {
172
+	IssueKind IssueKind
173
+	Valid     bool // Valid is true if IssueKind is not NULL
174
+}
175
+
176
+// Scan implements the Scanner interface.
177
+func (ns *NullIssueKind) Scan(value interface{}) error {
178
+	if value == nil {
179
+		ns.IssueKind, ns.Valid = "", false
180
+		return nil
181
+	}
182
+	ns.Valid = true
183
+	return ns.IssueKind.Scan(value)
184
+}
185
+
186
+// Value implements the driver Valuer interface.
187
+func (ns NullIssueKind) Value() (driver.Value, error) {
188
+	if !ns.Valid {
189
+		return nil, nil
190
+	}
191
+	return string(ns.IssueKind), nil
192
+}
193
+
194
+type IssueRefSource string
195
+
196
+const (
197
+	IssueRefSourceCommentBody   IssueRefSource = "comment_body"
198
+	IssueRefSourceIssueBody     IssueRefSource = "issue_body"
199
+	IssueRefSourceCommitMessage IssueRefSource = "commit_message"
200
+)
201
+
202
+func (e *IssueRefSource) Scan(src interface{}) error {
203
+	switch s := src.(type) {
204
+	case []byte:
205
+		*e = IssueRefSource(s)
206
+	case string:
207
+		*e = IssueRefSource(s)
208
+	default:
209
+		return fmt.Errorf("unsupported scan type for IssueRefSource: %T", src)
210
+	}
211
+	return nil
212
+}
213
+
214
+type NullIssueRefSource struct {
215
+	IssueRefSource IssueRefSource
216
+	Valid          bool // Valid is true if IssueRefSource is not NULL
217
+}
218
+
219
+// Scan implements the Scanner interface.
220
+func (ns *NullIssueRefSource) Scan(value interface{}) error {
221
+	if value == nil {
222
+		ns.IssueRefSource, ns.Valid = "", false
223
+		return nil
224
+	}
225
+	ns.Valid = true
226
+	return ns.IssueRefSource.Scan(value)
227
+}
228
+
229
+// Value implements the driver Valuer interface.
230
+func (ns NullIssueRefSource) Value() (driver.Value, error) {
231
+	if !ns.Valid {
232
+		return nil, nil
233
+	}
234
+	return string(ns.IssueRefSource), nil
235
+}
236
+
237
+type IssueState string
238
+
239
+const (
240
+	IssueStateOpen   IssueState = "open"
241
+	IssueStateClosed IssueState = "closed"
242
+)
243
+
244
+func (e *IssueState) Scan(src interface{}) error {
245
+	switch s := src.(type) {
246
+	case []byte:
247
+		*e = IssueState(s)
248
+	case string:
249
+		*e = IssueState(s)
250
+	default:
251
+		return fmt.Errorf("unsupported scan type for IssueState: %T", src)
252
+	}
253
+	return nil
254
+}
255
+
256
+type NullIssueState struct {
257
+	IssueState IssueState
258
+	Valid      bool // Valid is true if IssueState is not NULL
259
+}
260
+
261
+// Scan implements the Scanner interface.
262
+func (ns *NullIssueState) Scan(value interface{}) error {
263
+	if value == nil {
264
+		ns.IssueState, ns.Valid = "", false
265
+		return nil
266
+	}
267
+	ns.Valid = true
268
+	return ns.IssueState.Scan(value)
269
+}
270
+
271
+// Value implements the driver Valuer interface.
272
+func (ns NullIssueState) Value() (driver.Value, error) {
273
+	if !ns.Valid {
274
+		return nil, nil
275
+	}
276
+	return string(ns.IssueState), nil
277
+}
278
+
279
+type IssueStateReason string
280
+
281
+const (
282
+	IssueStateReasonCompleted  IssueStateReason = "completed"
283
+	IssueStateReasonNotPlanned IssueStateReason = "not_planned"
284
+	IssueStateReasonReopened   IssueStateReason = "reopened"
285
+	IssueStateReasonDuplicate  IssueStateReason = "duplicate"
286
+)
287
+
288
+func (e *IssueStateReason) Scan(src interface{}) error {
289
+	switch s := src.(type) {
290
+	case []byte:
291
+		*e = IssueStateReason(s)
292
+	case string:
293
+		*e = IssueStateReason(s)
294
+	default:
295
+		return fmt.Errorf("unsupported scan type for IssueStateReason: %T", src)
296
+	}
297
+	return nil
298
+}
299
+
300
+type NullIssueStateReason struct {
301
+	IssueStateReason IssueStateReason
302
+	Valid            bool // Valid is true if IssueStateReason is not NULL
303
+}
304
+
305
+// Scan implements the Scanner interface.
306
+func (ns *NullIssueStateReason) Scan(value interface{}) error {
307
+	if value == nil {
308
+		ns.IssueStateReason, ns.Valid = "", false
309
+		return nil
310
+	}
311
+	ns.Valid = true
312
+	return ns.IssueStateReason.Scan(value)
313
+}
314
+
315
+// Value implements the driver Valuer interface.
316
+func (ns NullIssueStateReason) Value() (driver.Value, error) {
317
+	if !ns.Valid {
318
+		return nil, nil
319
+	}
320
+	return string(ns.IssueStateReason), nil
321
+}
322
+
323
+type MilestoneState string
324
+
325
+const (
326
+	MilestoneStateOpen   MilestoneState = "open"
327
+	MilestoneStateClosed MilestoneState = "closed"
328
+)
329
+
330
+func (e *MilestoneState) Scan(src interface{}) error {
331
+	switch s := src.(type) {
332
+	case []byte:
333
+		*e = MilestoneState(s)
334
+	case string:
335
+		*e = MilestoneState(s)
336
+	default:
337
+		return fmt.Errorf("unsupported scan type for MilestoneState: %T", src)
338
+	}
339
+	return nil
340
+}
341
+
342
+type NullMilestoneState struct {
343
+	MilestoneState MilestoneState
344
+	Valid          bool // Valid is true if MilestoneState is not NULL
345
+}
346
+
347
+// Scan implements the Scanner interface.
348
+func (ns *NullMilestoneState) Scan(value interface{}) error {
349
+	if value == nil {
350
+		ns.MilestoneState, ns.Valid = "", false
351
+		return nil
352
+	}
353
+	ns.Valid = true
354
+	return ns.MilestoneState.Scan(value)
355
+}
356
+
357
+// Value implements the driver Valuer interface.
358
+func (ns NullMilestoneState) Value() (driver.Value, error) {
359
+	if !ns.Valid {
360
+		return nil, nil
361
+	}
362
+	return string(ns.MilestoneState), nil
363
+}
364
+
365
+type NotificationThreadKind string
366
+
367
+const (
368
+	NotificationThreadKindIssue NotificationThreadKind = "issue"
369
+	NotificationThreadKindPr    NotificationThreadKind = "pr"
370
+)
371
+
372
+func (e *NotificationThreadKind) Scan(src interface{}) error {
373
+	switch s := src.(type) {
374
+	case []byte:
375
+		*e = NotificationThreadKind(s)
376
+	case string:
377
+		*e = NotificationThreadKind(s)
378
+	default:
379
+		return fmt.Errorf("unsupported scan type for NotificationThreadKind: %T", src)
380
+	}
381
+	return nil
382
+}
383
+
384
+type NullNotificationThreadKind struct {
385
+	NotificationThreadKind NotificationThreadKind
386
+	Valid                  bool // Valid is true if NotificationThreadKind is not NULL
387
+}
388
+
389
+// Scan implements the Scanner interface.
390
+func (ns *NullNotificationThreadKind) Scan(value interface{}) error {
391
+	if value == nil {
392
+		ns.NotificationThreadKind, ns.Valid = "", false
393
+		return nil
394
+	}
395
+	ns.Valid = true
396
+	return ns.NotificationThreadKind.Scan(value)
397
+}
398
+
399
+// Value implements the driver Valuer interface.
400
+func (ns NullNotificationThreadKind) Value() (driver.Value, error) {
401
+	if !ns.Valid {
402
+		return nil, nil
403
+	}
404
+	return string(ns.NotificationThreadKind), nil
405
+}
406
+
407
+type OrgPlan string
408
+
409
+const (
410
+	OrgPlanFree       OrgPlan = "free"
411
+	OrgPlanTeam       OrgPlan = "team"
412
+	OrgPlanEnterprise OrgPlan = "enterprise"
413
+)
414
+
415
+func (e *OrgPlan) Scan(src interface{}) error {
416
+	switch s := src.(type) {
417
+	case []byte:
418
+		*e = OrgPlan(s)
419
+	case string:
420
+		*e = OrgPlan(s)
421
+	default:
422
+		return fmt.Errorf("unsupported scan type for OrgPlan: %T", src)
423
+	}
424
+	return nil
425
+}
426
+
427
+type NullOrgPlan struct {
428
+	OrgPlan OrgPlan
429
+	Valid   bool // Valid is true if OrgPlan is not NULL
430
+}
431
+
432
+// Scan implements the Scanner interface.
433
+func (ns *NullOrgPlan) Scan(value interface{}) error {
434
+	if value == nil {
435
+		ns.OrgPlan, ns.Valid = "", false
436
+		return nil
437
+	}
438
+	ns.Valid = true
439
+	return ns.OrgPlan.Scan(value)
440
+}
441
+
442
+// Value implements the driver Valuer interface.
443
+func (ns NullOrgPlan) Value() (driver.Value, error) {
444
+	if !ns.Valid {
445
+		return nil, nil
446
+	}
447
+	return string(ns.OrgPlan), nil
448
+}
449
+
450
+type OrgRole string
451
+
452
+const (
453
+	OrgRoleOwner  OrgRole = "owner"
454
+	OrgRoleMember OrgRole = "member"
455
+)
456
+
457
+func (e *OrgRole) Scan(src interface{}) error {
458
+	switch s := src.(type) {
459
+	case []byte:
460
+		*e = OrgRole(s)
461
+	case string:
462
+		*e = OrgRole(s)
463
+	default:
464
+		return fmt.Errorf("unsupported scan type for OrgRole: %T", src)
465
+	}
466
+	return nil
467
+}
468
+
469
+type NullOrgRole struct {
470
+	OrgRole OrgRole
471
+	Valid   bool // Valid is true if OrgRole is not NULL
472
+}
473
+
474
+// Scan implements the Scanner interface.
475
+func (ns *NullOrgRole) Scan(value interface{}) error {
476
+	if value == nil {
477
+		ns.OrgRole, ns.Valid = "", false
478
+		return nil
479
+	}
480
+	ns.Valid = true
481
+	return ns.OrgRole.Scan(value)
482
+}
483
+
484
+// Value implements the driver Valuer interface.
485
+func (ns NullOrgRole) Value() (driver.Value, error) {
486
+	if !ns.Valid {
487
+		return nil, nil
488
+	}
489
+	return string(ns.OrgRole), nil
490
+}
491
+
492
+type PrFileStatus string
493
+
494
+const (
495
+	PrFileStatusAdded    PrFileStatus = "added"
496
+	PrFileStatusModified PrFileStatus = "modified"
497
+	PrFileStatusDeleted  PrFileStatus = "deleted"
498
+	PrFileStatusRenamed  PrFileStatus = "renamed"
499
+	PrFileStatusCopied   PrFileStatus = "copied"
500
+)
501
+
502
+func (e *PrFileStatus) Scan(src interface{}) error {
503
+	switch s := src.(type) {
504
+	case []byte:
505
+		*e = PrFileStatus(s)
506
+	case string:
507
+		*e = PrFileStatus(s)
508
+	default:
509
+		return fmt.Errorf("unsupported scan type for PrFileStatus: %T", src)
510
+	}
511
+	return nil
512
+}
513
+
514
+type NullPrFileStatus struct {
515
+	PrFileStatus PrFileStatus
516
+	Valid        bool // Valid is true if PrFileStatus is not NULL
517
+}
518
+
519
+// Scan implements the Scanner interface.
520
+func (ns *NullPrFileStatus) Scan(value interface{}) error {
521
+	if value == nil {
522
+		ns.PrFileStatus, ns.Valid = "", false
523
+		return nil
524
+	}
525
+	ns.Valid = true
526
+	return ns.PrFileStatus.Scan(value)
527
+}
528
+
529
+// Value implements the driver Valuer interface.
530
+func (ns NullPrFileStatus) Value() (driver.Value, error) {
531
+	if !ns.Valid {
532
+		return nil, nil
533
+	}
534
+	return string(ns.PrFileStatus), nil
535
+}
536
+
537
+type PrMergeMethod string
538
+
539
+const (
540
+	PrMergeMethodMerge  PrMergeMethod = "merge"
541
+	PrMergeMethodSquash PrMergeMethod = "squash"
542
+	PrMergeMethodRebase PrMergeMethod = "rebase"
543
+)
544
+
545
+func (e *PrMergeMethod) Scan(src interface{}) error {
546
+	switch s := src.(type) {
547
+	case []byte:
548
+		*e = PrMergeMethod(s)
549
+	case string:
550
+		*e = PrMergeMethod(s)
551
+	default:
552
+		return fmt.Errorf("unsupported scan type for PrMergeMethod: %T", src)
553
+	}
554
+	return nil
555
+}
556
+
557
+type NullPrMergeMethod struct {
558
+	PrMergeMethod PrMergeMethod
559
+	Valid         bool // Valid is true if PrMergeMethod is not NULL
560
+}
561
+
562
+// Scan implements the Scanner interface.
563
+func (ns *NullPrMergeMethod) Scan(value interface{}) error {
564
+	if value == nil {
565
+		ns.PrMergeMethod, ns.Valid = "", false
566
+		return nil
567
+	}
568
+	ns.Valid = true
569
+	return ns.PrMergeMethod.Scan(value)
570
+}
571
+
572
+// Value implements the driver Valuer interface.
573
+func (ns NullPrMergeMethod) Value() (driver.Value, error) {
574
+	if !ns.Valid {
575
+		return nil, nil
576
+	}
577
+	return string(ns.PrMergeMethod), nil
578
+}
579
+
580
+type PrMergeableState string
581
+
582
+const (
583
+	PrMergeableStateUnknown PrMergeableState = "unknown"
584
+	PrMergeableStateClean   PrMergeableState = "clean"
585
+	PrMergeableStateDirty   PrMergeableState = "dirty"
586
+	PrMergeableStateBlocked PrMergeableState = "blocked"
587
+	PrMergeableStateBehind  PrMergeableState = "behind"
588
+)
589
+
590
+func (e *PrMergeableState) Scan(src interface{}) error {
591
+	switch s := src.(type) {
592
+	case []byte:
593
+		*e = PrMergeableState(s)
594
+	case string:
595
+		*e = PrMergeableState(s)
596
+	default:
597
+		return fmt.Errorf("unsupported scan type for PrMergeableState: %T", src)
598
+	}
599
+	return nil
600
+}
601
+
602
+type NullPrMergeableState struct {
603
+	PrMergeableState PrMergeableState
604
+	Valid            bool // Valid is true if PrMergeableState is not NULL
605
+}
606
+
607
+// Scan implements the Scanner interface.
608
+func (ns *NullPrMergeableState) Scan(value interface{}) error {
609
+	if value == nil {
610
+		ns.PrMergeableState, ns.Valid = "", false
611
+		return nil
612
+	}
613
+	ns.Valid = true
614
+	return ns.PrMergeableState.Scan(value)
615
+}
616
+
617
+// Value implements the driver Valuer interface.
618
+func (ns NullPrMergeableState) Value() (driver.Value, error) {
619
+	if !ns.Valid {
620
+		return nil, nil
621
+	}
622
+	return string(ns.PrMergeableState), nil
623
+}
624
+
625
+type PrReviewSide string
626
+
627
+const (
628
+	PrReviewSideLeft  PrReviewSide = "left"
629
+	PrReviewSideRight PrReviewSide = "right"
630
+)
631
+
632
+func (e *PrReviewSide) Scan(src interface{}) error {
633
+	switch s := src.(type) {
634
+	case []byte:
635
+		*e = PrReviewSide(s)
636
+	case string:
637
+		*e = PrReviewSide(s)
638
+	default:
639
+		return fmt.Errorf("unsupported scan type for PrReviewSide: %T", src)
640
+	}
641
+	return nil
642
+}
643
+
644
+type NullPrReviewSide struct {
645
+	PrReviewSide PrReviewSide
646
+	Valid        bool // Valid is true if PrReviewSide is not NULL
647
+}
648
+
649
+// Scan implements the Scanner interface.
650
+func (ns *NullPrReviewSide) Scan(value interface{}) error {
651
+	if value == nil {
652
+		ns.PrReviewSide, ns.Valid = "", false
653
+		return nil
654
+	}
655
+	ns.Valid = true
656
+	return ns.PrReviewSide.Scan(value)
657
+}
658
+
659
+// Value implements the driver Valuer interface.
660
+func (ns NullPrReviewSide) Value() (driver.Value, error) {
661
+	if !ns.Valid {
662
+		return nil, nil
663
+	}
664
+	return string(ns.PrReviewSide), nil
665
+}
666
+
667
+type PrReviewState string
668
+
669
+const (
670
+	PrReviewStateComment        PrReviewState = "comment"
671
+	PrReviewStateApprove        PrReviewState = "approve"
672
+	PrReviewStateRequestChanges PrReviewState = "request_changes"
673
+)
674
+
675
+func (e *PrReviewState) Scan(src interface{}) error {
676
+	switch s := src.(type) {
677
+	case []byte:
678
+		*e = PrReviewState(s)
679
+	case string:
680
+		*e = PrReviewState(s)
681
+	default:
682
+		return fmt.Errorf("unsupported scan type for PrReviewState: %T", src)
683
+	}
684
+	return nil
685
+}
686
+
687
+type NullPrReviewState struct {
688
+	PrReviewState PrReviewState
689
+	Valid         bool // Valid is true if PrReviewState is not NULL
690
+}
691
+
692
+// Scan implements the Scanner interface.
693
+func (ns *NullPrReviewState) Scan(value interface{}) error {
694
+	if value == nil {
695
+		ns.PrReviewState, ns.Valid = "", false
696
+		return nil
697
+	}
698
+	ns.Valid = true
699
+	return ns.PrReviewState.Scan(value)
700
+}
701
+
702
+// Value implements the driver Valuer interface.
703
+func (ns NullPrReviewState) Value() (driver.Value, error) {
704
+	if !ns.Valid {
705
+		return nil, nil
706
+	}
707
+	return string(ns.PrReviewState), nil
708
+}
709
+
710
+type PrincipalKind string
711
+
712
+const (
713
+	PrincipalKindUser PrincipalKind = "user"
714
+	PrincipalKindOrg  PrincipalKind = "org"
715
+)
716
+
717
+func (e *PrincipalKind) Scan(src interface{}) error {
718
+	switch s := src.(type) {
719
+	case []byte:
720
+		*e = PrincipalKind(s)
721
+	case string:
722
+		*e = PrincipalKind(s)
723
+	default:
724
+		return fmt.Errorf("unsupported scan type for PrincipalKind: %T", src)
725
+	}
726
+	return nil
727
+}
728
+
729
+type NullPrincipalKind struct {
730
+	PrincipalKind PrincipalKind
731
+	Valid         bool // Valid is true if PrincipalKind is not NULL
732
+}
733
+
734
+// Scan implements the Scanner interface.
735
+func (ns *NullPrincipalKind) Scan(value interface{}) error {
736
+	if value == nil {
737
+		ns.PrincipalKind, ns.Valid = "", false
738
+		return nil
739
+	}
740
+	ns.Valid = true
741
+	return ns.PrincipalKind.Scan(value)
742
+}
743
+
744
+// Value implements the driver Valuer interface.
745
+func (ns NullPrincipalKind) Value() (driver.Value, error) {
746
+	if !ns.Valid {
747
+		return nil, nil
748
+	}
749
+	return string(ns.PrincipalKind), nil
750
+}
751
+
752
+type RepoInitStatus string
753
+
754
+const (
755
+	RepoInitStatusInitialized RepoInitStatus = "initialized"
756
+	RepoInitStatusInitPending RepoInitStatus = "init_pending"
757
+	RepoInitStatusInitFailed  RepoInitStatus = "init_failed"
758
+)
759
+
760
+func (e *RepoInitStatus) Scan(src interface{}) error {
761
+	switch s := src.(type) {
762
+	case []byte:
763
+		*e = RepoInitStatus(s)
764
+	case string:
765
+		*e = RepoInitStatus(s)
766
+	default:
767
+		return fmt.Errorf("unsupported scan type for RepoInitStatus: %T", src)
768
+	}
769
+	return nil
770
+}
771
+
772
+type NullRepoInitStatus struct {
773
+	RepoInitStatus RepoInitStatus
774
+	Valid          bool // Valid is true if RepoInitStatus is not NULL
775
+}
776
+
777
+// Scan implements the Scanner interface.
778
+func (ns *NullRepoInitStatus) Scan(value interface{}) error {
779
+	if value == nil {
780
+		ns.RepoInitStatus, ns.Valid = "", false
781
+		return nil
782
+	}
783
+	ns.Valid = true
784
+	return ns.RepoInitStatus.Scan(value)
785
+}
786
+
787
+// Value implements the driver Valuer interface.
788
+func (ns NullRepoInitStatus) Value() (driver.Value, error) {
789
+	if !ns.Valid {
790
+		return nil, nil
791
+	}
792
+	return string(ns.RepoInitStatus), nil
793
+}
794
+
795
+type RepoVisibility string
796
+
797
+const (
798
+	RepoVisibilityPublic  RepoVisibility = "public"
799
+	RepoVisibilityPrivate RepoVisibility = "private"
800
+)
801
+
802
+func (e *RepoVisibility) Scan(src interface{}) error {
803
+	switch s := src.(type) {
804
+	case []byte:
805
+		*e = RepoVisibility(s)
806
+	case string:
807
+		*e = RepoVisibility(s)
808
+	default:
809
+		return fmt.Errorf("unsupported scan type for RepoVisibility: %T", src)
810
+	}
811
+	return nil
812
+}
813
+
814
+type NullRepoVisibility struct {
815
+	RepoVisibility RepoVisibility
816
+	Valid          bool // Valid is true if RepoVisibility is not NULL
817
+}
818
+
819
+// Scan implements the Scanner interface.
820
+func (ns *NullRepoVisibility) Scan(value interface{}) error {
821
+	if value == nil {
822
+		ns.RepoVisibility, ns.Valid = "", false
823
+		return nil
824
+	}
825
+	ns.Valid = true
826
+	return ns.RepoVisibility.Scan(value)
827
+}
828
+
829
+// Value implements the driver Valuer interface.
830
+func (ns NullRepoVisibility) Value() (driver.Value, error) {
831
+	if !ns.Valid {
832
+		return nil, nil
833
+	}
834
+	return string(ns.RepoVisibility), nil
835
+}
836
+
837
+type TeamPrivacy string
838
+
839
+const (
840
+	TeamPrivacyVisible TeamPrivacy = "visible"
841
+	TeamPrivacySecret  TeamPrivacy = "secret"
842
+)
843
+
844
+func (e *TeamPrivacy) Scan(src interface{}) error {
845
+	switch s := src.(type) {
846
+	case []byte:
847
+		*e = TeamPrivacy(s)
848
+	case string:
849
+		*e = TeamPrivacy(s)
850
+	default:
851
+		return fmt.Errorf("unsupported scan type for TeamPrivacy: %T", src)
852
+	}
853
+	return nil
854
+}
855
+
856
+type NullTeamPrivacy struct {
857
+	TeamPrivacy TeamPrivacy
858
+	Valid       bool // Valid is true if TeamPrivacy is not NULL
859
+}
860
+
861
+// Scan implements the Scanner interface.
862
+func (ns *NullTeamPrivacy) Scan(value interface{}) error {
863
+	if value == nil {
864
+		ns.TeamPrivacy, ns.Valid = "", false
865
+		return nil
866
+	}
867
+	ns.Valid = true
868
+	return ns.TeamPrivacy.Scan(value)
869
+}
870
+
871
+// Value implements the driver Valuer interface.
872
+func (ns NullTeamPrivacy) Value() (driver.Value, error) {
873
+	if !ns.Valid {
874
+		return nil, nil
875
+	}
876
+	return string(ns.TeamPrivacy), nil
877
+}
878
+
879
+type TeamRepoRole string
880
+
881
+const (
882
+	TeamRepoRoleRead     TeamRepoRole = "read"
883
+	TeamRepoRoleTriage   TeamRepoRole = "triage"
884
+	TeamRepoRoleWrite    TeamRepoRole = "write"
885
+	TeamRepoRoleMaintain TeamRepoRole = "maintain"
886
+	TeamRepoRoleAdmin    TeamRepoRole = "admin"
887
+)
888
+
889
+func (e *TeamRepoRole) Scan(src interface{}) error {
890
+	switch s := src.(type) {
891
+	case []byte:
892
+		*e = TeamRepoRole(s)
893
+	case string:
894
+		*e = TeamRepoRole(s)
895
+	default:
896
+		return fmt.Errorf("unsupported scan type for TeamRepoRole: %T", src)
897
+	}
898
+	return nil
899
+}
900
+
901
+type NullTeamRepoRole struct {
902
+	TeamRepoRole TeamRepoRole
903
+	Valid        bool // Valid is true if TeamRepoRole is not NULL
904
+}
905
+
906
+// Scan implements the Scanner interface.
907
+func (ns *NullTeamRepoRole) Scan(value interface{}) error {
908
+	if value == nil {
909
+		ns.TeamRepoRole, ns.Valid = "", false
910
+		return nil
911
+	}
912
+	ns.Valid = true
913
+	return ns.TeamRepoRole.Scan(value)
914
+}
915
+
916
+// Value implements the driver Valuer interface.
917
+func (ns NullTeamRepoRole) Value() (driver.Value, error) {
918
+	if !ns.Valid {
919
+		return nil, nil
920
+	}
921
+	return string(ns.TeamRepoRole), nil
922
+}
923
+
924
+type TeamRole string
925
+
926
+const (
927
+	TeamRoleMember     TeamRole = "member"
928
+	TeamRoleMaintainer TeamRole = "maintainer"
929
+)
930
+
931
+func (e *TeamRole) Scan(src interface{}) error {
932
+	switch s := src.(type) {
933
+	case []byte:
934
+		*e = TeamRole(s)
935
+	case string:
936
+		*e = TeamRole(s)
937
+	default:
938
+		return fmt.Errorf("unsupported scan type for TeamRole: %T", src)
939
+	}
940
+	return nil
941
+}
942
+
943
+type NullTeamRole struct {
944
+	TeamRole TeamRole
945
+	Valid    bool // Valid is true if TeamRole is not NULL
946
+}
947
+
948
+// Scan implements the Scanner interface.
949
+func (ns *NullTeamRole) Scan(value interface{}) error {
950
+	if value == nil {
951
+		ns.TeamRole, ns.Valid = "", false
952
+		return nil
953
+	}
954
+	ns.Valid = true
955
+	return ns.TeamRole.Scan(value)
956
+}
957
+
958
+// Value implements the driver Valuer interface.
959
+func (ns NullTeamRole) Value() (driver.Value, error) {
960
+	if !ns.Valid {
961
+		return nil, nil
962
+	}
963
+	return string(ns.TeamRole), nil
964
+}
965
+
966
+type TransactionalEmailStatus string
967
+
968
+const (
969
+	TransactionalEmailStatusQueued      TransactionalEmailStatus = "queued"
970
+	TransactionalEmailStatusSent        TransactionalEmailStatus = "sent"
971
+	TransactionalEmailStatusSoftBounced TransactionalEmailStatus = "soft_bounced"
972
+	TransactionalEmailStatusHardBounced TransactionalEmailStatus = "hard_bounced"
973
+	TransactionalEmailStatusDropped     TransactionalEmailStatus = "dropped"
974
+)
975
+
976
+func (e *TransactionalEmailStatus) Scan(src interface{}) error {
977
+	switch s := src.(type) {
978
+	case []byte:
979
+		*e = TransactionalEmailStatus(s)
980
+	case string:
981
+		*e = TransactionalEmailStatus(s)
982
+	default:
983
+		return fmt.Errorf("unsupported scan type for TransactionalEmailStatus: %T", src)
984
+	}
985
+	return nil
986
+}
987
+
988
+type NullTransactionalEmailStatus struct {
989
+	TransactionalEmailStatus TransactionalEmailStatus
990
+	Valid                    bool // Valid is true if TransactionalEmailStatus is not NULL
991
+}
992
+
993
+// Scan implements the Scanner interface.
994
+func (ns *NullTransactionalEmailStatus) Scan(value interface{}) error {
995
+	if value == nil {
996
+		ns.TransactionalEmailStatus, ns.Valid = "", false
997
+		return nil
998
+	}
999
+	ns.Valid = true
1000
+	return ns.TransactionalEmailStatus.Scan(value)
1001
+}
1002
+
1003
+// Value implements the driver Valuer interface.
1004
+func (ns NullTransactionalEmailStatus) Value() (driver.Value, error) {
1005
+	if !ns.Valid {
1006
+		return nil, nil
1007
+	}
1008
+	return string(ns.TransactionalEmailStatus), nil
1009
+}
1010
+
1011
+type TransferPrincipalKind string
1012
+
1013
+const (
1014
+	TransferPrincipalKindUser TransferPrincipalKind = "user"
1015
+	TransferPrincipalKindOrg  TransferPrincipalKind = "org"
1016
+)
1017
+
1018
+func (e *TransferPrincipalKind) Scan(src interface{}) error {
1019
+	switch s := src.(type) {
1020
+	case []byte:
1021
+		*e = TransferPrincipalKind(s)
1022
+	case string:
1023
+		*e = TransferPrincipalKind(s)
1024
+	default:
1025
+		return fmt.Errorf("unsupported scan type for TransferPrincipalKind: %T", src)
1026
+	}
1027
+	return nil
1028
+}
1029
+
1030
+type NullTransferPrincipalKind struct {
1031
+	TransferPrincipalKind TransferPrincipalKind
1032
+	Valid                 bool // Valid is true if TransferPrincipalKind is not NULL
1033
+}
1034
+
1035
+// Scan implements the Scanner interface.
1036
+func (ns *NullTransferPrincipalKind) Scan(value interface{}) error {
1037
+	if value == nil {
1038
+		ns.TransferPrincipalKind, ns.Valid = "", false
1039
+		return nil
1040
+	}
1041
+	ns.Valid = true
1042
+	return ns.TransferPrincipalKind.Scan(value)
1043
+}
1044
+
1045
+// Value implements the driver Valuer interface.
1046
+func (ns NullTransferPrincipalKind) Value() (driver.Value, error) {
1047
+	if !ns.Valid {
1048
+		return nil, nil
1049
+	}
1050
+	return string(ns.TransferPrincipalKind), nil
1051
+}
1052
+
1053
+type TransferStatus string
1054
+
1055
+const (
1056
+	TransferStatusPending  TransferStatus = "pending"
1057
+	TransferStatusAccepted TransferStatus = "accepted"
1058
+	TransferStatusDeclined TransferStatus = "declined"
1059
+	TransferStatusCanceled TransferStatus = "canceled"
1060
+	TransferStatusExpired  TransferStatus = "expired"
1061
+)
1062
+
1063
+func (e *TransferStatus) Scan(src interface{}) error {
1064
+	switch s := src.(type) {
1065
+	case []byte:
1066
+		*e = TransferStatus(s)
1067
+	case string:
1068
+		*e = TransferStatus(s)
1069
+	default:
1070
+		return fmt.Errorf("unsupported scan type for TransferStatus: %T", src)
1071
+	}
1072
+	return nil
1073
+}
1074
+
1075
+type NullTransferStatus struct {
1076
+	TransferStatus TransferStatus
1077
+	Valid          bool // Valid is true if TransferStatus is not NULL
1078
+}
1079
+
1080
+// Scan implements the Scanner interface.
1081
+func (ns *NullTransferStatus) Scan(value interface{}) error {
1082
+	if value == nil {
1083
+		ns.TransferStatus, ns.Valid = "", false
1084
+		return nil
1085
+	}
1086
+	ns.Valid = true
1087
+	return ns.TransferStatus.Scan(value)
1088
+}
1089
+
1090
+// Value implements the driver Valuer interface.
1091
+func (ns NullTransferStatus) Value() (driver.Value, error) {
1092
+	if !ns.Valid {
1093
+		return nil, nil
1094
+	}
1095
+	return string(ns.TransferStatus), nil
1096
+}
1097
+
1098
+type WatchLevel string
1099
+
1100
+const (
1101
+	WatchLevelAll           WatchLevel = "all"
1102
+	WatchLevelParticipating WatchLevel = "participating"
1103
+	WatchLevelIgnore        WatchLevel = "ignore"
1104
+)
1105
+
1106
+func (e *WatchLevel) Scan(src interface{}) error {
1107
+	switch s := src.(type) {
1108
+	case []byte:
1109
+		*e = WatchLevel(s)
1110
+	case string:
1111
+		*e = WatchLevel(s)
1112
+	default:
1113
+		return fmt.Errorf("unsupported scan type for WatchLevel: %T", src)
1114
+	}
1115
+	return nil
1116
+}
1117
+
1118
+type NullWatchLevel struct {
1119
+	WatchLevel WatchLevel
1120
+	Valid      bool // Valid is true if WatchLevel is not NULL
1121
+}
1122
+
1123
+// Scan implements the Scanner interface.
1124
+func (ns *NullWatchLevel) Scan(value interface{}) error {
1125
+	if value == nil {
1126
+		ns.WatchLevel, ns.Valid = "", false
1127
+		return nil
1128
+	}
1129
+	ns.Valid = true
1130
+	return ns.WatchLevel.Scan(value)
1131
+}
1132
+
1133
+// Value implements the driver Valuer interface.
1134
+func (ns NullWatchLevel) Value() (driver.Value, error) {
1135
+	if !ns.Valid {
1136
+		return nil, nil
1137
+	}
1138
+	return string(ns.WatchLevel), nil
1139
+}
1140
+
1141
+type WebhookContentType string
1142
+
1143
+const (
1144
+	WebhookContentTypeJson WebhookContentType = "json"
1145
+	WebhookContentTypeForm WebhookContentType = "form"
1146
+)
1147
+
1148
+func (e *WebhookContentType) Scan(src interface{}) error {
1149
+	switch s := src.(type) {
1150
+	case []byte:
1151
+		*e = WebhookContentType(s)
1152
+	case string:
1153
+		*e = WebhookContentType(s)
1154
+	default:
1155
+		return fmt.Errorf("unsupported scan type for WebhookContentType: %T", src)
1156
+	}
1157
+	return nil
1158
+}
1159
+
1160
+type NullWebhookContentType struct {
1161
+	WebhookContentType WebhookContentType
1162
+	Valid              bool // Valid is true if WebhookContentType is not NULL
1163
+}
1164
+
1165
+// Scan implements the Scanner interface.
1166
+func (ns *NullWebhookContentType) Scan(value interface{}) error {
1167
+	if value == nil {
1168
+		ns.WebhookContentType, ns.Valid = "", false
1169
+		return nil
1170
+	}
1171
+	ns.Valid = true
1172
+	return ns.WebhookContentType.Scan(value)
1173
+}
1174
+
1175
+// Value implements the driver Valuer interface.
1176
+func (ns NullWebhookContentType) Value() (driver.Value, error) {
1177
+	if !ns.Valid {
1178
+		return nil, nil
1179
+	}
1180
+	return string(ns.WebhookContentType), nil
1181
+}
1182
+
1183
+type WebhookDeliveryStatus string
1184
+
1185
+const (
1186
+	WebhookDeliveryStatusPending         WebhookDeliveryStatus = "pending"
1187
+	WebhookDeliveryStatusSucceeded       WebhookDeliveryStatus = "succeeded"
1188
+	WebhookDeliveryStatusFailedRetry     WebhookDeliveryStatus = "failed_retry"
1189
+	WebhookDeliveryStatusFailedPermanent WebhookDeliveryStatus = "failed_permanent"
1190
+)
1191
+
1192
+func (e *WebhookDeliveryStatus) Scan(src interface{}) error {
1193
+	switch s := src.(type) {
1194
+	case []byte:
1195
+		*e = WebhookDeliveryStatus(s)
1196
+	case string:
1197
+		*e = WebhookDeliveryStatus(s)
1198
+	default:
1199
+		return fmt.Errorf("unsupported scan type for WebhookDeliveryStatus: %T", src)
1200
+	}
1201
+	return nil
1202
+}
1203
+
1204
+type NullWebhookDeliveryStatus struct {
1205
+	WebhookDeliveryStatus WebhookDeliveryStatus
1206
+	Valid                 bool // Valid is true if WebhookDeliveryStatus is not NULL
1207
+}
1208
+
1209
+// Scan implements the Scanner interface.
1210
+func (ns *NullWebhookDeliveryStatus) Scan(value interface{}) error {
1211
+	if value == nil {
1212
+		ns.WebhookDeliveryStatus, ns.Valid = "", false
1213
+		return nil
1214
+	}
1215
+	ns.Valid = true
1216
+	return ns.WebhookDeliveryStatus.Scan(value)
1217
+}
1218
+
1219
+// Value implements the driver Valuer interface.
1220
+func (ns NullWebhookDeliveryStatus) Value() (driver.Value, error) {
1221
+	if !ns.Valid {
1222
+		return nil, nil
1223
+	}
1224
+	return string(ns.WebhookDeliveryStatus), nil
1225
+}
1226
+
1227
+type WebhookOwnerKind string
1228
+
1229
+const (
1230
+	WebhookOwnerKindRepo WebhookOwnerKind = "repo"
1231
+	WebhookOwnerKindOrg  WebhookOwnerKind = "org"
1232
+)
1233
+
1234
+func (e *WebhookOwnerKind) Scan(src interface{}) error {
1235
+	switch s := src.(type) {
1236
+	case []byte:
1237
+		*e = WebhookOwnerKind(s)
1238
+	case string:
1239
+		*e = WebhookOwnerKind(s)
1240
+	default:
1241
+		return fmt.Errorf("unsupported scan type for WebhookOwnerKind: %T", src)
1242
+	}
1243
+	return nil
1244
+}
1245
+
1246
+type NullWebhookOwnerKind struct {
1247
+	WebhookOwnerKind WebhookOwnerKind
1248
+	Valid            bool // Valid is true if WebhookOwnerKind is not NULL
1249
+}
1250
+
1251
+// Scan implements the Scanner interface.
1252
+func (ns *NullWebhookOwnerKind) Scan(value interface{}) error {
1253
+	if value == nil {
1254
+		ns.WebhookOwnerKind, ns.Valid = "", false
1255
+		return nil
1256
+	}
1257
+	ns.Valid = true
1258
+	return ns.WebhookOwnerKind.Scan(value)
1259
+}
1260
+
1261
+// Value implements the driver Valuer interface.
1262
+func (ns NullWebhookOwnerKind) Value() (driver.Value, error) {
1263
+	if !ns.Valid {
1264
+		return nil, nil
1265
+	}
1266
+	return string(ns.WebhookOwnerKind), nil
1267
+}
1268
+
1269
+type AuthAuditLog struct {
1270
+	ID         int64
1271
+	ActorID    pgtype.Int8
1272
+	Action     string
1273
+	TargetType string
1274
+	TargetID   pgtype.Int8
1275
+	Meta       []byte
1276
+	CreatedAt  pgtype.Timestamptz
1277
+}
1278
+
1279
+type AuthThrottle struct {
1280
+	ID              int64
1281
+	Scope           string
1282
+	Identifier      string
1283
+	Hits            int32
1284
+	WindowStartedAt pgtype.Timestamptz
1285
+}
1286
+
1287
+type BranchProtectionRule struct {
1288
+	ID                             int64
1289
+	RepoID                         int64
1290
+	Pattern                        string
1291
+	PreventForcePush               bool
1292
+	PreventDeletion                bool
1293
+	RequirePrForPush               bool
1294
+	AllowedPusherUserIds           []int64
1295
+	RequireSignedCommits           bool
1296
+	StatusChecksRequired           []string
1297
+	CreatedAt                      pgtype.Timestamptz
1298
+	UpdatedAt                      pgtype.Timestamptz
1299
+	CreatedByUserID                pgtype.Int8
1300
+	RequiredReviewCount            int32
1301
+	DismissStaleReviewsOnPush      bool
1302
+	RequireCodeOwnerReview         bool
1303
+	DismissStaleStatusChecksOnPush bool
1304
+}
1305
+
1306
+type CheckRun struct {
1307
+	ID          int64
1308
+	SuiteID     int64
1309
+	RepoID      int64
1310
+	HeadSha     string
1311
+	Name        string
1312
+	Status      CheckStatus
1313
+	Conclusion  NullCheckConclusion
1314
+	StartedAt   pgtype.Timestamptz
1315
+	CompletedAt pgtype.Timestamptz
1316
+	DetailsUrl  string
1317
+	Output      []byte
1318
+	ExternalID  pgtype.Text
1319
+	CreatedAt   pgtype.Timestamptz
1320
+	UpdatedAt   pgtype.Timestamptz
1321
+}
1322
+
1323
+type CheckSuite struct {
1324
+	ID         int64
1325
+	RepoID     int64
1326
+	HeadSha    string
1327
+	AppSlug    string
1328
+	Status     CheckStatus
1329
+	Conclusion NullCheckConclusion
1330
+	CreatedAt  pgtype.Timestamptz
1331
+	UpdatedAt  pgtype.Timestamptz
1332
+}
1333
+
1334
+type CodeSearchContent struct {
1335
+	RepoID      int64
1336
+	RefName     string
1337
+	Path        string
1338
+	ContentTsv  interface{}
1339
+	ContentTrgm string
1340
+}
1341
+
1342
+type CodeSearchPath struct {
1343
+	RepoID  int64
1344
+	RefName string
1345
+	Path    string
1346
+	Tsv     interface{}
1347
+}
1348
+
1349
+type DomainEvent struct {
1350
+	ID          int64
1351
+	ActorUserID pgtype.Int8
1352
+	Kind        string
1353
+	RepoID      pgtype.Int8
1354
+	SourceKind  string
1355
+	SourceID    int64
1356
+	Public      bool
1357
+	Payload     []byte
1358
+	CreatedAt   pgtype.Timestamptz
1359
+}
1360
+
1361
+type DomainEventsProcessed struct {
1362
+	Consumer    string
1363
+	LastEventID int64
1364
+	UpdatedAt   pgtype.Timestamptz
1365
+}
1366
+
1367
+type EmailVerification struct {
1368
+	ID          int64
1369
+	UserEmailID int64
1370
+	TokenHash   []byte
1371
+	ExpiresAt   pgtype.Timestamptz
1372
+	UsedAt      pgtype.Timestamptz
1373
+	CreatedAt   pgtype.Timestamptz
1374
+}
1375
+
1376
+type Issue struct {
1377
+	ID                int64
1378
+	RepoID            int64
1379
+	Number            int64
1380
+	Kind              IssueKind
1381
+	Title             string
1382
+	Body              string
1383
+	BodyHtmlCached    pgtype.Text
1384
+	MdPipelineVersion int32
1385
+	AuthorUserID      pgtype.Int8
1386
+	State             IssueState
1387
+	StateReason       NullIssueStateReason
1388
+	Locked            bool
1389
+	LockReason        pgtype.Text
1390
+	MilestoneID       pgtype.Int8
1391
+	CreatedAt         pgtype.Timestamptz
1392
+	UpdatedAt         pgtype.Timestamptz
1393
+	EditedAt          pgtype.Timestamptz
1394
+	ClosedAt          pgtype.Timestamptz
1395
+	ClosedByUserID    pgtype.Int8
1396
+}
1397
+
1398
+type IssueAssignee struct {
1399
+	IssueID          int64
1400
+	UserID           int64
1401
+	AssignedAt       pgtype.Timestamptz
1402
+	AssignedByUserID pgtype.Int8
1403
+}
1404
+
1405
+type IssueComment struct {
1406
+	ID                int64
1407
+	IssueID           int64
1408
+	AuthorUserID      pgtype.Int8
1409
+	Body              string
1410
+	BodyHtmlCached    pgtype.Text
1411
+	MdPipelineVersion int32
1412
+	CreatedAt         pgtype.Timestamptz
1413
+	UpdatedAt         pgtype.Timestamptz
1414
+	EditedAt          pgtype.Timestamptz
1415
+}
1416
+
1417
+type IssueEvent struct {
1418
+	ID          int64
1419
+	IssueID     int64
1420
+	ActorUserID pgtype.Int8
1421
+	Kind        string
1422
+	Meta        []byte
1423
+	RefTargetID pgtype.Int8
1424
+	CreatedAt   pgtype.Timestamptz
1425
+}
1426
+
1427
+type IssueLabel struct {
1428
+	IssueID         int64
1429
+	LabelID         int64
1430
+	AppliedAt       pgtype.Timestamptz
1431
+	AppliedByUserID pgtype.Int8
1432
+}
1433
+
1434
+type IssueReference struct {
1435
+	ID             int64
1436
+	SourceIssueID  pgtype.Int8
1437
+	TargetIssueID  int64
1438
+	SourceKind     IssueRefSource
1439
+	SourceObjectID pgtype.Int8
1440
+	CreatedAt      pgtype.Timestamptz
1441
+}
1442
+
1443
+type IssuesSearch struct {
1444
+	IssueID      int64
1445
+	RepoID       int64
1446
+	Kind         IssueKind
1447
+	State        IssueState
1448
+	AuthorUserID pgtype.Int8
1449
+	Tsv          interface{}
1450
+}
1451
+
1452
+type Job struct {
1453
+	ID          int64
1454
+	Kind        string
1455
+	Payload     []byte
1456
+	RunAt       pgtype.Timestamptz
1457
+	Attempts    int32
1458
+	MaxAttempts int32
1459
+	LastError   pgtype.Text
1460
+	LockedBy    pgtype.Text
1461
+	LockedAt    pgtype.Timestamptz
1462
+	CompletedAt pgtype.Timestamptz
1463
+	FailedAt    pgtype.Timestamptz
1464
+	CreatedAt   pgtype.Timestamptz
1465
+}
1466
+
1467
+type Label struct {
1468
+	ID          int64
1469
+	RepoID      int64
1470
+	Name        string
1471
+	Color       string
1472
+	Description string
1473
+	CreatedAt   pgtype.Timestamptz
1474
+}
1475
+
1476
+type Meta struct {
1477
+	Key       string
1478
+	Value     []byte
1479
+	UpdatedAt pgtype.Timestamptz
1480
+}
1481
+
1482
+type Milestone struct {
1483
+	ID          int64
1484
+	RepoID      int64
1485
+	Title       string
1486
+	Description string
1487
+	State       MilestoneState
1488
+	DueOn       pgtype.Timestamptz
1489
+	CreatedAt   pgtype.Timestamptz
1490
+	ClosedAt    pgtype.Timestamptz
1491
+}
1492
+
1493
+type Notification struct {
1494
+	ID              int64
1495
+	RecipientUserID int64
1496
+	Kind            string
1497
+	Reason          string
1498
+	RepoID          pgtype.Int8
1499
+	ThreadKind      NullNotificationThreadKind
1500
+	ThreadID        pgtype.Int8
1501
+	SourceEventID   pgtype.Int8
1502
+	Unread          bool
1503
+	LastEventAt     pgtype.Timestamptz
1504
+	LastActorUserID pgtype.Int8
1505
+	Summary         []byte
1506
+	CreatedAt       pgtype.Timestamptz
1507
+	UpdatedAt       pgtype.Timestamptz
1508
+}
1509
+
1510
+type NotificationEmailLog struct {
1511
+	ID              int64
1512
+	RecipientUserID int64
1513
+	NotificationID  pgtype.Int8
1514
+	ThreadKind      NullNotificationThreadKind
1515
+	ThreadID        pgtype.Int8
1516
+	SentAt          pgtype.Timestamptz
1517
+	MessageID       pgtype.Text
1518
+}
1519
+
1520
+type NotificationThread struct {
1521
+	RecipientUserID int64
1522
+	ThreadKind      NotificationThreadKind
1523
+	ThreadID        int64
1524
+	Subscribed      bool
1525
+	Reason          string
1526
+	UpdatedAt       pgtype.Timestamptz
1527
+}
1528
+
1529
+type Org struct {
1530
+	ID                    int64
1531
+	Slug                  string
1532
+	DisplayName           string
1533
+	Description           string
1534
+	AvatarObjectKey       pgtype.Text
1535
+	Location              string
1536
+	Website               string
1537
+	BillingEmail          string
1538
+	Plan                  OrgPlan
1539
+	AllowMemberRepoCreate bool
1540
+	CreatedByUserID       pgtype.Int8
1541
+	SuspendedAt           pgtype.Timestamptz
1542
+	SuspendedReason       pgtype.Text
1543
+	DeletedAt             pgtype.Timestamptz
1544
+	CreatedAt             pgtype.Timestamptz
1545
+	UpdatedAt             pgtype.Timestamptz
1546
+}
1547
+
1548
+type OrgInvitation struct {
1549
+	ID              int64
1550
+	OrgID           int64
1551
+	InvitedByUserID pgtype.Int8
1552
+	TargetUserID    pgtype.Int8
1553
+	TargetEmail     pgtype.Text
1554
+	Role            OrgRole
1555
+	TokenHash       []byte
1556
+	ExpiresAt       pgtype.Timestamptz
1557
+	AcceptedAt      pgtype.Timestamptz
1558
+	DeclinedAt      pgtype.Timestamptz
1559
+	CanceledAt      pgtype.Timestamptz
1560
+	CreatedAt       pgtype.Timestamptz
1561
+}
1562
+
1563
+type OrgMember struct {
1564
+	OrgID           int64
1565
+	UserID          int64
1566
+	Role            OrgRole
1567
+	InvitedByUserID pgtype.Int8
1568
+	JoinedAt        pgtype.Timestamptz
1569
+}
1570
+
1571
+type PasswordReset struct {
1572
+	ID        int64
1573
+	UserID    int64
1574
+	TokenHash []byte
1575
+	ExpiresAt pgtype.Timestamptz
1576
+	UsedAt    pgtype.Timestamptz
1577
+	CreatedAt pgtype.Timestamptz
1578
+}
1579
+
1580
+type PrReview struct {
1581
+	ID                int64
1582
+	PrIssueID         int64
1583
+	AuthorUserID      pgtype.Int8
1584
+	State             PrReviewState
1585
+	Body              string
1586
+	BodyHtmlCached    pgtype.Text
1587
+	SubmittedAt       pgtype.Timestamptz
1588
+	DismissedAt       pgtype.Timestamptz
1589
+	DismissedByUserID pgtype.Int8
1590
+	DismissalReason   string
1591
+}
1592
+
1593
+type PrReviewComment struct {
1594
+	ID                int64
1595
+	PrIssueID         int64
1596
+	ReviewID          pgtype.Int8
1597
+	AuthorUserID      pgtype.Int8
1598
+	FilePath          string
1599
+	Side              PrReviewSide
1600
+	OriginalCommitSha string
1601
+	OriginalLine      int32
1602
+	OriginalPosition  int32
1603
+	CurrentPosition   pgtype.Int4
1604
+	Body              string
1605
+	BodyHtmlCached    pgtype.Text
1606
+	InReplyToID       pgtype.Int8
1607
+	Pending           bool
1608
+	ResolvedAt        pgtype.Timestamptz
1609
+	ResolvedByUserID  pgtype.Int8
1610
+	CreatedAt         pgtype.Timestamptz
1611
+	UpdatedAt         pgtype.Timestamptz
1612
+	EditedAt          pgtype.Timestamptz
1613
+}
1614
+
1615
+type PrReviewRequest struct {
1616
+	ID                  int64
1617
+	PrIssueID           int64
1618
+	RequestedUserID     pgtype.Int8
1619
+	RequestedTeamID     pgtype.Int8
1620
+	RequestedByUserID   pgtype.Int8
1621
+	RequestedAt         pgtype.Timestamptz
1622
+	DismissedAt         pgtype.Timestamptz
1623
+	SatisfiedByReviewID pgtype.Int8
1624
+}
1625
+
1626
+type Principal struct {
1627
+	Slug string
1628
+	Kind PrincipalKind
1629
+	ID   int64
1630
+}
1631
+
1632
+type PullRequest struct {
1633
+	IssueID            int64
1634
+	BaseRef            string
1635
+	HeadRef            string
1636
+	HeadRepoID         int64
1637
+	BaseOid            string
1638
+	HeadOid            string
1639
+	Draft              bool
1640
+	Mergeable          pgtype.Bool
1641
+	MergeableState     PrMergeableState
1642
+	MergeCommitSha     pgtype.Text
1643
+	MergedAt           pgtype.Timestamptz
1644
+	MergedByUserID     pgtype.Int8
1645
+	MergeMethod        NullPrMergeMethod
1646
+	BaseOidAtMerge     pgtype.Text
1647
+	HeadOidAtMerge     pgtype.Text
1648
+	LastSynchronizedAt pgtype.Timestamptz
1649
+}
1650
+
1651
+type PullRequestCommit struct {
1652
+	PrID           int64
1653
+	Sha            string
1654
+	Position       int32
1655
+	AuthorName     string
1656
+	AuthorEmail    string
1657
+	CommitterName  string
1658
+	CommitterEmail string
1659
+	Subject        string
1660
+	Body           string
1661
+	AuthoredAt     pgtype.Timestamptz
1662
+	CommittedAt    pgtype.Timestamptz
1663
+}
1664
+
1665
+type PullRequestFile struct {
1666
+	PrID      int64
1667
+	Path      string
1668
+	Status    PrFileStatus
1669
+	OldPath   pgtype.Text
1670
+	Additions int32
1671
+	Deletions int32
1672
+	Changes   int32
1673
+}
1674
+
1675
+type PushEvent struct {
1676
+	ID           int64
1677
+	RepoID       int64
1678
+	PusherUserID pgtype.Int8
1679
+	BeforeSha    string
1680
+	AfterSha     string
1681
+	Ref          string
1682
+	Protocol     string
1683
+	RequestID    string
1684
+	ProcessedAt  pgtype.Timestamptz
1685
+	CreatedAt    pgtype.Timestamptz
1686
+}
1687
+
1688
+type Repo struct {
1689
+	ID                 int64
1690
+	OwnerUserID        pgtype.Int8
1691
+	OwnerOrgID         pgtype.Int8
1692
+	Name               string
1693
+	Description        string
1694
+	Visibility         RepoVisibility
1695
+	DefaultBranch      string
1696
+	IsArchived         bool
1697
+	ArchivedAt         pgtype.Timestamptz
1698
+	DeletedAt          pgtype.Timestamptz
1699
+	DiskUsedBytes      int64
1700
+	ForkOfRepoID       pgtype.Int8
1701
+	LicenseKey         pgtype.Text
1702
+	PrimaryLanguage    pgtype.Text
1703
+	HasIssues          bool
1704
+	HasPulls           bool
1705
+	CreatedAt          pgtype.Timestamptz
1706
+	UpdatedAt          pgtype.Timestamptz
1707
+	DefaultBranchOid   pgtype.Text
1708
+	AllowSquashMerge   bool
1709
+	AllowRebaseMerge   bool
1710
+	AllowMergeCommit   bool
1711
+	DefaultMergeMethod PrMergeMethod
1712
+	StarCount          int64
1713
+	WatcherCount       int64
1714
+	ForkCount          int64
1715
+	InitStatus         RepoInitStatus
1716
+	LastIndexedOid     pgtype.Text
1717
+}
1718
+
1719
+type RepoCollaborator struct {
1720
+	RepoID        int64
1721
+	UserID        int64
1722
+	Role          CollabRole
1723
+	AddedAt       pgtype.Timestamptz
1724
+	AddedByUserID pgtype.Int8
1725
+}
1726
+
1727
+type RepoIssueCounter struct {
1728
+	RepoID     int64
1729
+	NextNumber int64
1730
+}
1731
+
1732
+type RepoRedirect struct {
1733
+	OldOwnerUserID pgtype.Int8
1734
+	OldOwnerOrgID  pgtype.Int8
1735
+	OldName        string
1736
+	RepoID         int64
1737
+	RedirectedAt   pgtype.Timestamptz
1738
+}
1739
+
1740
+type RepoTopic struct {
1741
+	RepoID    int64
1742
+	Topic     string
1743
+	CreatedAt pgtype.Timestamptz
1744
+}
1745
+
1746
+type RepoTransferRequest struct {
1747
+	ID              int64
1748
+	RepoID          int64
1749
+	FromUserID      int64
1750
+	ToPrincipalKind TransferPrincipalKind
1751
+	ToPrincipalID   int64
1752
+	CreatedBy       int64
1753
+	CreatedAt       pgtype.Timestamptz
1754
+	ExpiresAt       pgtype.Timestamptz
1755
+	Status          TransferStatus
1756
+	AcceptedAt      pgtype.Timestamptz
1757
+	DeclinedAt      pgtype.Timestamptz
1758
+	CanceledAt      pgtype.Timestamptz
1759
+}
1760
+
1761
+type ReposSearch struct {
1762
+	RepoID int64
1763
+	Tsv    interface{}
1764
+}
1765
+
1766
+type Star struct {
1767
+	UserID    int64
1768
+	RepoID    int64
1769
+	StarredAt pgtype.Timestamptz
1770
+}
1771
+
1772
+type Team struct {
1773
+	ID              int64
1774
+	OrgID           int64
1775
+	Slug            string
1776
+	DisplayName     string
1777
+	Description     string
1778
+	ParentTeamID    pgtype.Int8
1779
+	Privacy         TeamPrivacy
1780
+	CreatedByUserID pgtype.Int8
1781
+	CreatedAt       pgtype.Timestamptz
1782
+	UpdatedAt       pgtype.Timestamptz
1783
+}
1784
+
1785
+type TeamMember struct {
1786
+	TeamID        int64
1787
+	UserID        int64
1788
+	Role          TeamRole
1789
+	AddedByUserID pgtype.Int8
1790
+	AddedAt       pgtype.Timestamptz
1791
+}
1792
+
1793
+type TeamRepoAccess struct {
1794
+	TeamID        int64
1795
+	RepoID        int64
1796
+	Role          TeamRepoRole
1797
+	AddedByUserID pgtype.Int8
1798
+	AddedAt       pgtype.Timestamptz
1799
+}
1800
+
1801
+type TransactionalEmailLog struct {
1802
+	ID              int64
1803
+	RecipientUserID pgtype.Int8
1804
+	RecipientEmail  string
1805
+	Kind            string
1806
+	Subject         string
1807
+	ProviderID      string
1808
+	Status          TransactionalEmailStatus
1809
+	ErrorSummary    pgtype.Text
1810
+	SentAt          pgtype.Timestamptz
1811
+	DeliveredAt     pgtype.Timestamptz
1812
+}
1813
+
1814
+type User struct {
1815
+	ID                int64
1816
+	Username          string
1817
+	DisplayName       string
1818
+	PrimaryEmailID    pgtype.Int8
1819
+	PasswordHash      string
1820
+	PasswordAlgo      string
1821
+	PasswordUpdatedAt pgtype.Timestamptz
1822
+	EmailVerified     bool
1823
+	LastLoginAt       pgtype.Timestamptz
1824
+	SuspendedAt       pgtype.Timestamptz
1825
+	SuspendedReason   pgtype.Text
1826
+	DeletedAt         pgtype.Timestamptz
1827
+	CreatedAt         pgtype.Timestamptz
1828
+	UpdatedAt         pgtype.Timestamptz
1829
+	Bio               string
1830
+	Location          string
1831
+	Website           string
1832
+	Company           string
1833
+	Pronouns          string
1834
+	AvatarObjectKey   pgtype.Text
1835
+	Theme             string
1836
+	SessionEpoch      int32
1837
+	IsSiteAdmin       bool
1838
+}
1839
+
1840
+type UserEmail struct {
1841
+	ID                    int64
1842
+	UserID                int64
1843
+	Email                 string
1844
+	IsPrimary             bool
1845
+	Verified              bool
1846
+	VerificationTokenHash []byte
1847
+	VerificationSentAt    pgtype.Timestamptz
1848
+	VerifiedAt            pgtype.Timestamptz
1849
+	CreatedAt             pgtype.Timestamptz
1850
+}
1851
+
1852
+type UserNotificationPref struct {
1853
+	UserID    int64
1854
+	Key       string
1855
+	Value     []byte
1856
+	UpdatedAt pgtype.Timestamptz
1857
+}
1858
+
1859
+type UserRecoveryCode struct {
1860
+	ID          int64
1861
+	UserID      int64
1862
+	CodeHash    []byte
1863
+	UsedAt      pgtype.Timestamptz
1864
+	GeneratedAt pgtype.Timestamptz
1865
+	CreatedAt   pgtype.Timestamptz
1866
+}
1867
+
1868
+type UserSshKey struct {
1869
+	ID                int64
1870
+	UserID            int64
1871
+	Title             string
1872
+	FingerprintSha256 string
1873
+	KeyType           string
1874
+	KeyBits           int32
1875
+	PublicKey         string
1876
+	LastUsedAt        pgtype.Timestamptz
1877
+	LastUsedIp        *netip.Addr
1878
+	CreatedAt         pgtype.Timestamptz
1879
+}
1880
+
1881
+type UserToken struct {
1882
+	ID          int64
1883
+	UserID      int64
1884
+	Name        string
1885
+	TokenHash   []byte
1886
+	TokenPrefix string
1887
+	Scopes      []string
1888
+	ExpiresAt   pgtype.Timestamptz
1889
+	LastUsedAt  pgtype.Timestamptz
1890
+	LastUsedIp  *netip.Addr
1891
+	RevokedAt   pgtype.Timestamptz
1892
+	CreatedAt   pgtype.Timestamptz
1893
+}
1894
+
1895
+type UserTotp struct {
1896
+	ID              int64
1897
+	UserID          int64
1898
+	SecretEncrypted []byte
1899
+	SecretNonce     []byte
1900
+	ConfirmedAt     pgtype.Timestamptz
1901
+	LastUsedCounter int64
1902
+	CreatedAt       pgtype.Timestamptz
1903
+	UpdatedAt       pgtype.Timestamptz
1904
+}
1905
+
1906
+type UsernameRedirect struct {
1907
+	OldUsername string
1908
+	UserID      int64
1909
+	ChangedAt   pgtype.Timestamptz
1910
+}
1911
+
1912
+type UsersSearch struct {
1913
+	UserID int64
1914
+	Tsv    interface{}
1915
+}
1916
+
1917
+type Watch struct {
1918
+	UserID    int64
1919
+	RepoID    int64
1920
+	Level     WatchLevel
1921
+	UpdatedAt pgtype.Timestamptz
1922
+}
1923
+
1924
+type Webhook struct {
1925
+	ID                   int64
1926
+	OwnerKind            WebhookOwnerKind
1927
+	OwnerID              int64
1928
+	Url                  string
1929
+	ContentType          WebhookContentType
1930
+	Events               []string
1931
+	SecretCiphertext     []byte
1932
+	SecretNonce          []byte
1933
+	Active               bool
1934
+	SslVerification      bool
1935
+	ConsecutiveFailures  int32
1936
+	AutoDisableThreshold int32
1937
+	DisabledAt           pgtype.Timestamptz
1938
+	DisabledReason       pgtype.Text
1939
+	LastSuccessAt        pgtype.Timestamptz
1940
+	LastFailureAt        pgtype.Timestamptz
1941
+	CreatedByUserID      pgtype.Int8
1942
+	CreatedAt            pgtype.Timestamptz
1943
+	UpdatedAt            pgtype.Timestamptz
1944
+}
1945
+
1946
+type WebhookDelivery struct {
1947
+	ID                int64
1948
+	WebhookID         int64
1949
+	EventKind         string
1950
+	EventID           pgtype.Int8
1951
+	DeliveryUuid      pgtype.UUID
1952
+	Payload           []byte
1953
+	RequestHeaders    []byte
1954
+	RequestBody       []byte
1955
+	ResponseStatus    pgtype.Int4
1956
+	ResponseHeaders   []byte
1957
+	ResponseBody      []byte
1958
+	ResponseTruncated bool
1959
+	StartedAt         pgtype.Timestamptz
1960
+	CompletedAt       pgtype.Timestamptz
1961
+	Attempt           int32
1962
+	MaxAttempts       int32
1963
+	NextRetryAt       pgtype.Timestamptz
1964
+	Status            WebhookDeliveryStatus
1965
+	IdempotencyKey    string
1966
+	ErrorSummary      pgtype.Text
1967
+	RedeliverOf       pgtype.Int8
1968
+}
1969
+
1970
+type WebhookEventsPending struct {
1971
+	ID        int64
1972
+	RepoID    int64
1973
+	EventKind string
1974
+	Payload   []byte
1975
+	CreatedAt pgtype.Timestamptz
1976
+}
internal/admin/sqlc/querier.goadded
@@ -0,0 +1,63 @@
1
+// Code generated by sqlc. DO NOT EDIT.
2
+// versions:
3
+//   sqlc v1.31.1
4
+
5
+package admindb
6
+
7
+import (
8
+	"context"
9
+)
10
+
11
+type Querier interface {
12
+	// Mark a job dead without running it. Caller has already confirmed.
13
+	AdminDiscardJob(ctx context.Context, db DBTX, id int64) error
14
+	// Re-arm a failed/dead job for immediate retry: clear failure state,
15
+	// reset attempts, and set run_at to now() so the pool picks it up
16
+	// on the next tick.
17
+	AdminRetryJob(ctx context.Context, db DBTX, id int64) error
18
+	CountActiveOrgs(ctx context.Context, db DBTX) (int64, error)
19
+	CountActiveRepos(ctx context.Context, db DBTX) (int64, error)
20
+	// SPDX-License-Identifier: AGPL-3.0-or-later
21
+	//
22
+	// Admin-only query surface for /admin/*. Non-admin handlers should
23
+	// use the per-domain packages (usersdb, reposdb, …) directly. This
24
+	// file collects the cross-cutting reads + writes the admin UI needs:
25
+	//   * dashboard counts
26
+	//   * user/repo listings with admin-only filters (suspended, deleted)
27
+	//   * site-admin flag toggle
28
+	//   * transactional_email_log read/write
29
+	//
30
+	// Queries that already exist in their domain package (e.g.
31
+	// users.SuspendUser) are reused directly — no duplication here.
32
+	// ─── dashboard counts ────────────────────────────────────────────
33
+	CountActiveUsers(ctx context.Context, db DBTX) (int64, error)
34
+	// Status is derived from the timestamp columns since the jobs table
35
+	// doesn't carry an enum: completed > failed > running > queued.
36
+	CountJobsByStatus(ctx context.Context, db DBTX) ([]CountJobsByStatusRow, error)
37
+	CountSiteAdmins(ctx context.Context, db DBTX) (int64, error)
38
+	CountSuspendedUsers(ctx context.Context, db DBTX) (int64, error)
39
+	GetJobForAdmin(ctx context.Context, db DBTX, id int64) (Job, error)
40
+	// ─── transactional email log ─────────────────────────────────────
41
+	InsertTransactionalEmail(ctx context.Context, db DBTX, arg InsertTransactionalEmailParams) (int64, error)
42
+	// ─── audit log viewer ────────────────────────────────────────────
43
+	// Filters: actor, action prefix, target type+id, time range. Keyset
44
+	// pagination on (created_at, id) keeps deep pages cheap.
45
+	ListAuditForAdmin(ctx context.Context, db DBTX, arg ListAuditForAdminParams) ([]AuthAuditLog, error)
46
+	// ─── jobs queue inspector ────────────────────────────────────────
47
+	// The admin can filter by kind + status (computed from the timestamp
48
+	// columns). Status filter values: queued | running | failed | completed.
49
+	ListJobsForAdmin(ctx context.Context, db DBTX, arg ListJobsForAdminParams) ([]ListJobsForAdminRow, error)
50
+	// Admin email-queue surface. Most recent first; status filter optional.
51
+	ListRecentTransactionalEmails(ctx context.Context, db DBTX, arg ListRecentTransactionalEmailsParams) ([]TransactionalEmailLog, error)
52
+	// ─── repos list ──────────────────────────────────────────────────
53
+	ListReposForAdmin(ctx context.Context, db DBTX, arg ListReposForAdminParams) ([]ListReposForAdminRow, error)
54
+	// ─── users list ──────────────────────────────────────────────────
55
+	// Filters: optional username prefix, optional suspended/deleted-only.
56
+	// All filters use sqlc.narg so the empty case is "no filter".
57
+	ListUsersForAdmin(ctx context.Context, db DBTX, arg ListUsersForAdminParams) ([]ListUsersForAdminRow, error)
58
+	MarkTransactionalEmailDelivered(ctx context.Context, db DBTX, id int64) error
59
+	MarkTransactionalEmailFailed(ctx context.Context, db DBTX, arg MarkTransactionalEmailFailedParams) error
60
+	SetUserSiteAdmin(ctx context.Context, db DBTX, arg SetUserSiteAdminParams) error
61
+}
62
+
63
+var _ Querier = (*Queries)(nil)
sqlc.yamlmodified
@@ -193,3 +193,19 @@ sql:
193193
         emit_exact_table_names: false
194194
         emit_empty_slices: true
195195
         emit_methods_with_db_argument: true
196
+
197
+  - engine: postgresql
198
+    schema: internal/migrationsfs/migrations
199
+    queries: internal/admin/queries
200
+    gen:
201
+      go:
202
+        package: admindb
203
+        out: internal/admin/sqlc
204
+        sql_package: pgx/v5
205
+        emit_json_tags: false
206
+        emit_pointers_for_null_types: false
207
+        emit_prepared_queries: false
208
+        emit_interface: true
209
+        emit_exact_table_names: false
210
+        emit_empty_slices: true
211
+        emit_methods_with_db_argument: true