Admin Panel
Reference for the MyClaude admin panel: role-based access, report management, product quarantine, user moderation, API routes, and audit logging.
The MyClaude admin panel lives at /admin and requires role=admin on the authenticated user. All admin actions are audit-logged to the audit_logs Firestore collection.
Accessing the admin panel
Navigate to https://myclaude.sh/admin. The server checks the user's role via getUserRole() before rendering. Users without role=admin receive a 403 response. There is no link to the admin panel in the public navigation.
RBAC model
MyClaude uses three special roles (hardcoded by UID, not stored in Firestore) plus two Firestore-level user roles.
| Role | Source | Publish | Purchase | Admin panel | Ban users | Remove products |
|---|---|---|---|---|---|---|
user | Firestore | No | Yes | No | No | No |
creator | Firestore | Yes | Yes | No | No | No |
verified_creator | Firestore | Yes (auto-published) | Yes | No | No | No |
moderator | Hardcoded UID | Yes | Yes | No | No | No |
admin | Hardcoded UID | Yes | Yes | Yes | Yes | Yes |
ceo | Hardcoded UID | Yes | Yes | Yes | Yes | Yes |
Key constraints:
- Moderators have zero admin panel access. Every admin API route checks
if (!role || role === "moderator")and returns 403. Moderators exist for future use but currently have no elevated privileges. - Only
adminandceoroles can access admin operations. TheisAdmin()helper checksrole === "ceo" || role === "admin". - Verified creators bypass quarantine. Their products publish immediately as
publishedinstead ofpending_review. - Special roles are hardcoded in
src/lib/roles.tsby UID mapping, not stored in Firestore (prevents privilege escalation via database tampering).
Reports tab
Displays all content reports sorted by creation date (newest first).
Report fields
| Field | Type | Description |
|---|---|---|
id | string | Report document ID |
type | enum | product, review, or user |
targetId | string | ID of the reported product, review, or user |
reporterUid | string | UID of the user who filed the report |
reason | string | Selected reason from the report dialog |
description | string | Free-text description provided by the reporter |
status | enum | pending, resolved, dismissed |
createdAt | timestamp | When the report was filed |
resolvedAt | timestamp | When the report was resolved or dismissed (null if pending) |
resolvedBy | string | Admin UID who resolved the report (null if pending) |
Actions
- Resolve -- Marks the report as
resolvedand triggers the appropriate enforcement action (remove product, delete review, or ban user). - Dismiss -- Marks the report as
dismissedwith no enforcement action.
Both actions are recorded in the audit log.
Quarantine tab
Lists all products with status: pending_review. Products enter quarantine when a non-verified user publishes.
Actions
| Action | Effect |
|---|---|
| Approve | Sets product status to published. Product becomes visible in search and explore. |
| Remove | Sets product status to removed. Product is hidden from all public surfaces. |
Verified creators (verified_creator, moderator, admin) skip quarantine entirely -- their products publish as published on creation.
Users tab
Search users by username or email. Displays role, verification status, and product count.
Ban user
Banning a user triggers a cascade:
- User's
bannedfield is set totrue - All of the user's products are set to
status: removed - User receives
403 Forbiddenon all subsequent mutation API calls (ban check runs after token verification)
Ban requires a confirmation dialog. Only admin role can execute bans.
Admin API routes
All admin routes require Authorization: Bearer {token} with a decoded user whose role passes the getUserRole() check.
| Method | Path | Description | Required role | Rate limit | Mode |
|---|---|---|---|---|---|
GET | /api/admin/reports | List all reports | admin, ceo | 30/min | standard |
POST | /api/admin/reports/[id]/resolve | Resolve or dismiss a report | admin, ceo | 10/min | strict |
POST | /api/admin/products/[id]/approve | Approve a quarantined product | admin, ceo | 10/min | strict |
POST | /api/admin/products/[id]/remove | Remove a product (cascade) | admin, ceo | 10/min | strict |
POST | /api/admin/users/[id]/ban | Ban a user (cascade products) | admin, ceo | 5/min | strict |
Mutation routes use strict (fail-closed) rate limiting — if the rate limiter fails, the request is denied. The reports list endpoint uses standard (fail-open) mode at a higher limit since it is read-only.
Audit logging
Every admin action writes a document to the audit_logs collection.
| Field | Type | Description |
|---|---|---|
action | string | The action performed (e.g., resolve_report, approve_product, ban_user, remove_product, dismiss_report) |
adminUid | string | UID of the admin who performed the action |
targetId | string | ID of the affected resource (report, product, or user) |
timestamp | timestamp | When the action was performed |
Audit logs are append-only. There is no API to delete or modify audit log entries.
Security model
| Control | Implementation |
|---|---|
| Authentication | Firebase JWT verified via admin.auth().verifyIdToken() |
| Authorization | getUserRole() checks role on every request; fail-closed |
| Rate limiting | 5-30/min per user. Mutations use strict mode (blocks on limiter failure). |
| RBAC granularity | Destructive operations (ban, remove) restricted to admin only |
| Audit trail | All actions logged to audit_logs with admin UID and timestamp |
Related pages
- Security Model -- authentication and authorization architecture
- Content Reporting -- how users file reports
- Trust & Safety -- enforcement policy and dispute resolution
- API Overview -- base URL, error format, rate limiting