Admin Routes
Admin-only endpoints powering /admin.
Every route in /api/admin/* requires:
- Auth —
supabase.auth.getUser()returns a user - Admin flag —
isAdmin()helper returns true (checksusers.is_admin)
If either check fails, the route returns 403 Forbidden.
Rate-limited with the admin tier.
Projects
GET /api/admin/projects
List all projects including pending, rejected, deleted. Supports the same query params as /api/projects plus:
status=all— include everythingq— substring search on name, url
PUT /api/admin/projects/[id]/approve
Flip status pending → live. Fires webhookEvents.projectApproved() and sends the approval email.
PUT /api/admin/projects/[id]/reject
Flip status pending → rejected.
Body:
{ "reason": "Optional reason shown to the submitter" }Sends the rejection email with the reason attached.
PUT /api/admin/projects/[id]
General admin edit. Can change any field (status, category assignments, pricing, etc.) bypassing ownership checks.
DELETE /api/admin/projects/[id]
Hard-delete. Irrecoverable. For soft-delete, use PUT with { "deleted_at": "..." }.
Users
GET /api/admin/users
List users with aggregated stats (project count, comment count, join date).
Query params: role, q, page, limit.
PUT /api/admin/users/[id]
Update is_admin, role, is_banned, ban_reason.
Remember: is_admin and role must be kept in sync when granting/revoking admin.
Categories
POST /api/admin/categories
Create a new category.
PUT /api/admin/categories/reorder
Persist drag-and-drop ordering. Body is an array of { id, sort_order, sphere }.
DELETE /api/admin/categories/[id]
Delete a category. Projects referencing it are not removed — they just lose that category in their categories array.
Promotions
GET /api/admin/promotions
List active + expired promotions.
PUT /api/admin/promotions/[id]
Approve, extend, revoke, change end date.
Analytics
GET /api/admin/analytics
Returns the aggregated data shown on /admin/analytics:
- Traffic by day / week / month
- Top projects by views
- Top countries, browsers, devices
- Submission funnel conversion
Query params:
| Param | Default | Notes |
|---|---|---|
range | 30d | 7d / 30d / 90d / all |
groupBy | day | day / week / month |
Settings
GET /api/admin/settings
Current values of DB-backed site settings.
PUT /api/admin/settings
Update settings. Body: any subset of { site_banner, maintenance_mode, custom_theme, ... }.
CSV import
POST /api/admin/projects/import
Import projects from a CSV file. Multipart request with:
file— the CSV blobmapping— JSON describing how CSV columns map to fields