Admin panel: users, password resets, roles, audit log #39

Merged
anas merged 1 commits from feature/admin-panel into main 2026-10-08 21:10:05 +00:00
Owner

Closes #27. Step 3 of the plan. Moderators and scoped grants come next in #29 (IAM).

For admins (/admin, linked as "ایڈمن" in the header)

  • Users: search by email, joined date, last sign-in, status.
  • Password reset on a reader's request (no email server yet): generates a 12-character temporary password (no look-alike characters), shown once to the admin, and signs the reader out everywhere. The reader signs in with it and changes it on their account page.
  • Disable / enable: a disabled account cannot sign in ("یہ اکاؤنٹ معطل ہے") and its sessions end.
  • Roles: reader ⇄ admin.
  • Delete an account and its data (confirmation prompt).
  • An admin cannot disable, demote or delete themself, so there is always an admin.
  • /admin/audit: every admin action with time, admin, user and detail; entries stay after users are deleted.

First admin (server only): sign up on the site, then in api/:

npm run make-admin -- you@example.com

Database: users.role, users.disabled_at, audit_log (in db/schema.sql).

Tested

  • npm test: 21 pass, including the admin flow: readers get 403; search; reset (old password and sessions stop working, temporary one works); disable blocks sign-in, enable restores; invalid role refused; self-protection; delete; audit entries in order.
  • On the running site: reader → 403 at /admin; make-admin promotes; header link appears; reset shows the temporary password and the reader signs in with it; disable shows the Urdu message at sign-in; a cross-site form post to /admin is refused (403); audit page lists the actions.

🤖 Generated with Claude Code

Closes #27. Step 3 of the plan. Moderators and scoped grants come next in #29 (IAM). **For admins** (`/admin`, linked as "ایڈمن" in the header) - Users: search by email, joined date, last sign-in, status. - **Password reset on a reader's request** (no email server yet): generates a 12-character temporary password (no look-alike characters), shown once to the admin, and signs the reader out everywhere. The reader signs in with it and changes it on their account page. - Disable / enable: a disabled account cannot sign in ("یہ اکاؤنٹ معطل ہے") and its sessions end. - Roles: reader ⇄ admin. - Delete an account and its data (confirmation prompt). - An admin cannot disable, demote or delete themself, so there is always an admin. - `/admin/audit`: every admin action with time, admin, user and detail; entries stay after users are deleted. **First admin** (server only): sign up on the site, then in `api/`: ``` npm run make-admin -- you@example.com ``` **Database**: `users.role`, `users.disabled_at`, `audit_log` (in `db/schema.sql`). **Tested** - `npm test`: 21 pass, including the admin flow: readers get 403; search; reset (old password and sessions stop working, temporary one works); disable blocks sign-in, enable restores; invalid role refused; self-protection; delete; audit entries in order. - On the running site: reader → 403 at `/admin`; `make-admin` promotes; header link appears; reset shows the temporary password and the reader signs in with it; disable shows the Urdu message at sign-in; a cross-site form post to `/admin` is refused (403); audit page lists the actions. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
anas added 1 commit 2026-10-08 21:08:34 +00:00
- API (api/src/admin.ts, admins only): user list and search; password reset generates a temporary
  password shown once and ends the user's sessions; disable/enable (ends sessions, blocks sign-in);
  roles reader/admin; delete; an admin cannot disable, demote or delete themself. Every action is
  written to audit_log (kept when users are deleted).
- First admin from the server: npm run make-admin -- <email> (after signing up).
- Site: /admin (users) and /admin/audit; 'ایڈمن' link in the header for admins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anas merged commit 48b106857d into main 2026-10-08 21:10:05 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: anas/divan#39
No description provided.