Admin Dashboard
Platform administration dashboard for managing users, organizations, abuse detection, and delivery monitoring.
The admin dashboard (apps/admin/) is a standalone Nuxt 3 application for platform-level administration. It is separate from the tenant-facing web app and provides tools for managing users, organizations, abuse detection, and delivery monitoring.
The admin dashboard is an internal tool and is not exposed to end users. It requires the platform-admin role to access.
Architecture
| Aspect | Detail |
|---|---|
| Framework | Nuxt 3 (CSR-only, ssr: false) |
| UI Layer | Extends packages/ui for shared components |
| Backend | Convex (same deployment as main app) |
| Auth | BetterAuth with platform-admin role check |
| Port | 3001 (separate from web app on 3000) |
The admin app shares the same Convex backend as the main web app. It uses platform-admin queries and mutations that are restricted to users with the platform-admin role in the database.
Authentication Flow
Auth Proxy Pattern
The admin app proxies authentication requests through a same-origin server route (server/api/auth/[...].ts) to avoid CORS issues. This route forwards requests to the Convex site URL, strips problematic headers (Host, Connection), and handles Set-Cookie domain adjustments for local development.
Browser → /api/auth/* → Nuxt server route → Convex site URL → response proxied back
Token Management
The lib/convex-auth.ts module manages JWT tokens for Convex:
- Caches the current token to avoid unnecessary auth calls
- Implements a 60-second refresh buffer (refreshes before expiry)
- Provides
getConvexAuthToken()for the Convex client's auth callback
Pages
| Route | Page | Middleware | Purpose |
|---|---|---|---|
/ | index.vue | auth, platform-admin | Dashboard overview: flagged organizations, abuse metrics, pending reviews |
/login | login.vue | — | Admin sign-in (email + password) |
/unauthorized | unauthorized.vue | — | Shown when user lacks platform-admin role |
/users | users.vue | auth, platform-admin | User management |
/organizations | organizations/index.vue | auth, platform-admin | Organization listing with status, tier, risk level, bounce rates |
/organizations/[id] | organizations/[id].vue | auth, platform-admin | Organization detail and actions |
/admins | admins.vue | auth, platform-admin | Platform admin management |
/waitlist | waitlist.vue | auth, platform-admin | Waitlist user management (approve/deny) |
/analytics | analytics.vue | auth, platform-admin | Platform-wide analytics |
/audit | audit.vue | auth, platform-admin | Audit log viewer |
/delivery | delivery.vue | auth, platform-admin | Delivery monitoring (bounce rates, blocked emails, active campaigns) |
/domains | domains.vue | auth, platform-admin | Domain health across all organizations |
/review | review.vue | auth, platform-admin | Content review queue |
Middleware
Two middleware layers protect admin routes:
auth.ts— redirects unauthenticated users to/login(with redirect query param)platform-admin.ts— queriesapi.platformAdmin.isPlatformAdminand redirects non-admins to/unauthorized
Composables
| Composable | Purpose |
|---|---|
useAuth() | BetterAuth session management: signInWithEmail(), signOut(), waitUntilReady(), refetch(). Exposes reactive status (pending/authenticated/unauthenticated/error). |
useConvexQuery() | Reactive Convex query wrapper. Returns { data, error, isLoading }. Deep-watches args for reactivity. Supports 'skip' pattern for conditional queries. |
useConvexMutation() | Convex mutation executor. Returns { mutate, isLoading, error }. |
useConvex() | Base composable returning the Convex client instance. |
Query Usage Pattern
const { data: organizations, isLoading } = useConvexQuery(
api.platformAdmin.listOrganizations,
() => ({ status: selectedStatus.value })
)
The args factory function is deep-watched — when selectedStatus changes, the query automatically re-subscribes with new arguments.
Environment Variables
Set in apps/admin/.env:
| Variable | Description | Default |
|---|---|---|
NUXT_PUBLIC_CONVEX_URL | Convex deployment URL (same as web app) | — |
NUXT_PUBLIC_CONVEX_SITE_URL | Convex site URL (same as web app, used for auth proxy) | — |
NUXT_PUBLIC_SITE_URL | Admin dashboard URL | http://localhost:3001 |
Security
The admin app is configured with strict security headers via nuxt-security:
- CSP — strict
script-src,object-src: 'none', Convex URL inconnect-src - HSTS — enabled with long max-age
- X-Frame-Options —
DENY(prevents embedding) - Referrer-Policy —
strict-origin-when-cross-origin
Key Files
| File | Purpose |
|---|---|
nuxt.config.ts | App configuration, security headers, runtime config |
app/lib/auth-client.ts | BetterAuth client initialization |
app/lib/convex-auth.ts | JWT token caching and refresh |
app/plugins/convex.client.ts | Convex client initialization and auth wiring |
server/api/auth/[...].ts | Auth proxy server route |
app/middleware/auth.ts | Authentication middleware |
app/middleware/platform-admin.ts | Platform admin role middleware |
app/layouts/default.vue | Sidebar layout with navigation |