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
+21 -6
View File
@@ -82,14 +82,14 @@ func (u *User) Create(ctx context.Context) error {
if u.CreatedAt == "" {
u.CreatedAt = time.Now().UTC().Format(time.RFC3339)
}
return sqlc.New(u.db).CreateUser(ctx, sqlc.CreateUserParams{
return mapUniqueViolation(sqlc.New(u.db).CreateUser(ctx, sqlc.CreateUserParams{
ID: u.ID,
Username: u.Username,
Name: u.Name,
PasswordHash: u.PasswordHash,
Role: string(u.Role),
CreatedAt: u.CreatedAt,
})
}))
}
// adminRoleLockKey serializes SetRole so concurrent demotions cannot bypass the
@@ -171,17 +171,32 @@ func CountAdmins(ctx context.Context, db *sql.DB) (int, error) {
return int(n), err
}
func ListUsers(ctx context.Context, db *sql.DB) ([]User, error) {
rows, err := sqlc.New(db).ListUsers(ctx, AdminUsersLimit)
func ListUsers(ctx context.Context, db *sql.DB, q ListUsersQuery) ([]User, string, string, error) {
limit := q.Limit
if limit <= 0 {
limit = AdminUsersLimit
}
rows, err := sqlc.New(db).ListUsers(ctx, sqlc.ListUsersParams{
Search: q.Search,
CursorCreated: q.CursorCreated,
CursorID: q.CursorID,
RowLimit: int32(limit + 1),
})
if err != nil {
return nil, err
return nil, "", "", err
}
out := make([]User, 0, len(rows))
for _, r := range rows {
u := toUser(db, r.ID, r.Username, r.Name, r.Role, r.AvatarUrl, r.State, r.CreatedAt, "")
out = append(out, *u)
}
return out, nil
var nextCreated, nextID string
if len(out) > limit {
last := out[limit-1]
nextCreated, nextID = last.CreatedAt, last.ID
out = out[:limit]
}
return out, nextCreated, nextID, nil
}
func UserByID(ctx context.Context, db *sql.DB, id string) (*User, error) {