feat(server/m7-user-mgmt): admin endpoints (invites, users, promote/demote)
Five admin endpoints under existing RequireAdmin middleware:
- GET /api/admin/invites — list active + recently-redeemed
- POST /api/admin/invites — generate 24h invite, returns
{token, expires_at, ...}. Audits ActionInviteCreate.
- DELETE /api/admin/invites/{token} — revoke unredeemed invite.
Audits ActionInviteRevoke.
- GET /api/admin/users — list all users (id, username,
display_name, is_admin, created_at).
- PUT /api/admin/users/{id}/admin — toggle is_admin with
last-admin guard. Audits ActionPromoteAdmin / ActionDemoteAdmin.
Last-admin guard counts admins, refuses demotion of the sole
admin with 409 'last_admin'. Race window between count and update
is acceptable for v1 — worst case is 'no admins left,' which the
env-driven bootstrap or CLI reset can recover from. Common path
('admin demotes themselves') is now blocked.
Adds ListUsers, CountAdmins, UpdateUserAdmin sqlc queries to
users.sql.
Tests cover: invite create/list/delete round-trip, non-admin gets
403, user list, promote happy path, last-admin demotion refused,
two-admins demotion allowed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,23 @@ UPDATE users
|
||||
SET listenbrainz_enabled = $2
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: ListUsers :many
|
||||
-- Admin user-management list. Sort newest-first.
|
||||
SELECT id, username, display_name, is_admin, created_at
|
||||
FROM users
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: CountAdmins :one
|
||||
SELECT count(*) FROM users WHERE is_admin = true;
|
||||
|
||||
-- name: UpdateUserAdmin :one
|
||||
-- Sets is_admin to the given value. Returns the updated row so the
|
||||
-- handler can echo it back to the caller.
|
||||
UPDATE users
|
||||
SET is_admin = $2
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetListenBrainzConfig :one
|
||||
-- Returns the user's LB token + enabled flag and the most recent
|
||||
-- play_events.scrobbled_at for last-scrobbled-at status.
|
||||
|
||||
Reference in New Issue
Block a user