feat(server/m7-user-mgmt): admin user CRUD endpoints (U2)
Four new admin endpoints under existing RequireAdmin middleware:
- POST /api/admin/users — admin-creates-user. Body
{username, password, display_name?, is_admin?}. Same username
+ password validation as the public /register handler. 409
on duplicate. Audits ActionCreateUserAdmin.
- DELETE /api/admin/users/{id} — hard delete. Last-admin guard
refuses delete when target is the only admin (409). Schema's
ON DELETE CASCADE on user-FK tables handles plays/likes/
sessions cleanup. Audits ActionDeleteUser with target's
username + was_admin flag.
- POST /api/admin/users/{id}/reset-password — body
{password}. 8-char minimum. Admin sets a new password
without knowing the old one. Audits ActionPasswordResetAdmin.
- PUT /api/admin/users/{id}/auto-approve — body
{auto_approve: bool}. Toggles the per-user flag added in T1
(the #355 sub-feature surface). Audits ActionAutoApproveToggle.
adminUserView shape extended with auto_approve_requests so the
list and toggle responses carry the flag. ListUsers SQL query
updated to include auto_approve_requests; generated Go updated
to match. Tests cover happy paths, last-admin guard on delete,
password validation, duplicate username, and the auto-approve
round-trip.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -279,17 +279,18 @@ func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User,
|
||||
}
|
||||
|
||||
const listUsers = `-- name: ListUsers :many
|
||||
SELECT id, username, display_name, is_admin, created_at
|
||||
SELECT id, username, display_name, is_admin, auto_approve_requests, created_at
|
||||
FROM users
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
type ListUsersRow struct {
|
||||
ID pgtype.UUID
|
||||
Username string
|
||||
DisplayName *string
|
||||
IsAdmin bool
|
||||
CreatedAt pgtype.Timestamptz
|
||||
ID pgtype.UUID
|
||||
Username string
|
||||
DisplayName *string
|
||||
IsAdmin bool
|
||||
AutoApproveRequests bool
|
||||
CreatedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
// Admin user-management list. Sort newest-first.
|
||||
@@ -307,6 +308,7 @@ func (q *Queries) ListUsers(ctx context.Context) ([]ListUsersRow, error) {
|
||||
&i.Username,
|
||||
&i.DisplayName,
|
||||
&i.IsAdmin,
|
||||
&i.AutoApproveRequests,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -55,7 +55,7 @@ WHERE id = $1;
|
||||
|
||||
-- name: ListUsers :many
|
||||
-- Admin user-management list. Sort newest-first.
|
||||
SELECT id, username, display_name, is_admin, created_at
|
||||
SELECT id, username, display_name, is_admin, auto_approve_requests, created_at
|
||||
FROM users
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user