Address production-readiness review: clearer errors, safer votes, and ops hardening.

Distinguish auth/lookup failures, make votes idempotent on visible questions, bound shutdown, page admin users, LRU throttle, trusted-proxy CIDRs, avatar cleanup, versioned migrations, and session cleanup logging.
This commit is contained in:
2026-08-22 12:16:59 -07:00
parent 5bdaa8977f
commit 29b0536215
26 changed files with 612 additions and 146 deletions
+7 -4
View File
@@ -10,14 +10,17 @@ import (
"time"
)
const deleteExpiredSessions = `-- name: DeleteExpiredSessions :exec
const deleteExpiredSessions = `-- name: DeleteExpiredSessions :execrows
DELETE FROM sessions
WHERE expiry <= now()
`
func (q *Queries) DeleteExpiredSessions(ctx context.Context) error {
_, err := q.db.ExecContext(ctx, deleteExpiredSessions)
return err
func (q *Queries) DeleteExpiredSessions(ctx context.Context) (int64, error) {
result, err := q.db.ExecContext(ctx, deleteExpiredSessions)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
const deleteSession = `-- name: DeleteSession :exec
+26 -4
View File
@@ -129,10 +129,27 @@ func (q *Queries) GetUserRole(ctx context.Context, id string) (string, error) {
const listUsers = `-- name: ListUsers :many
SELECT id, username, name, role, avatar_url, state, created_at
FROM users
ORDER BY created_at ASC
LIMIT $1
WHERE (
$1 = ''
OR username ILIKE '%' || $1 || '%'
OR name ILIKE '%' || $1 || '%'
)
AND (
$2 = ''
OR created_at < $2
OR (created_at = $2 AND id < $3)
)
ORDER BY created_at DESC, id DESC
LIMIT $4
`
type ListUsersParams struct {
Search interface{}
CursorCreated interface{}
CursorID string
RowLimit int32
}
type ListUsersRow struct {
ID string
Username string
@@ -143,8 +160,13 @@ type ListUsersRow struct {
CreatedAt string
}
func (q *Queries) ListUsers(ctx context.Context, rowLimit int32) ([]ListUsersRow, error) {
rows, err := q.db.QueryContext(ctx, listUsers, rowLimit)
func (q *Queries) ListUsers(ctx context.Context, arg ListUsersParams) ([]ListUsersRow, error) {
rows, err := q.db.QueryContext(ctx, listUsers,
arg.Search,
arg.CursorCreated,
arg.CursorID,
arg.RowLimit,
)
if err != nil {
return nil, err
}
+30 -9
View File
@@ -42,20 +42,41 @@ func (q *Queries) GetVote(ctx context.Context, arg GetVoteParams) (int32, error)
return value, err
}
const upsertVote = `-- name: UpsertVote :exec
INSERT INTO votes (user_id, question_id, value)
VALUES ($1, $2, $3)
ON CONFLICT (user_id, question_id) DO UPDATE
SET value = excluded.value
const questionIsVisible = `-- name: QuestionIsVisible :one
SELECT EXISTS(
SELECT 1 FROM questions WHERE id = $1 AND hidden = 0
)::bool
`
type UpsertVoteParams struct {
func (q *Queries) QuestionIsVisible(ctx context.Context, id string) (bool, error) {
row := q.db.QueryRowContext(ctx, questionIsVisible, id)
var column_1 bool
err := row.Scan(&column_1)
return column_1, err
}
const upsertVoteOnVisible = `-- name: UpsertVoteOnVisible :execrows
INSERT INTO votes (user_id, question_id, value)
SELECT $1, $2, $3
FROM questions q
WHERE q.id = $2 AND q.hidden = 0
ON CONFLICT (user_id, question_id) DO UPDATE
SET value = excluded.value
WHERE EXISTS (
SELECT 1 FROM questions q2 WHERE q2.id = excluded.question_id AND q2.hidden = 0
)
`
type UpsertVoteOnVisibleParams struct {
UserID string
QuestionID string
Value int32
}
func (q *Queries) UpsertVote(ctx context.Context, arg UpsertVoteParams) error {
_, err := q.db.ExecContext(ctx, upsertVote, arg.UserID, arg.QuestionID, arg.Value)
return err
func (q *Queries) UpsertVoteOnVisible(ctx context.Context, arg UpsertVoteOnVisibleParams) (int64, error) {
result, err := q.db.ExecContext(ctx, upsertVoteOnVisible, arg.UserID, arg.QuestionID, arg.Value)
if err != nil {
return 0, err
}
return result.RowsAffected()
}