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.

Internal tool

The admin dashboard is an internal tool and is not exposed to end users. It requires the platform-admin role to access.

Architecture

AspectDetail
FrameworkNuxt 3 (CSR-only, ssr: false)
UI LayerExtends packages/ui for shared components
BackendConvex (same deployment as main app)
AuthBetterAuth with platform-admin role check
Port3001 (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

Admin navigates to /loginEmail + password form
BetterAuthAuth proxy forwards to Convex
Session createdJWT token issued
platform-admin role checkedQuery: isPlatformAdmin
Dashboard access granted

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

RoutePageMiddlewarePurpose
/index.vueauth, platform-adminDashboard overview: flagged organizations, abuse metrics, pending reviews
/loginlogin.vueAdmin sign-in (email + password)
/unauthorizedunauthorized.vueShown when user lacks platform-admin role
/usersusers.vueauth, platform-adminUser management
/organizationsorganizations/index.vueauth, platform-adminOrganization listing with status, tier, risk level, bounce rates
/organizations/[id]organizations/[id].vueauth, platform-adminOrganization detail and actions
/adminsadmins.vueauth, platform-adminPlatform admin management
/waitlistwaitlist.vueauth, platform-adminWaitlist user management (approve/deny)
/analyticsanalytics.vueauth, platform-adminPlatform-wide analytics
/auditaudit.vueauth, platform-adminAudit log viewer
/deliverydelivery.vueauth, platform-adminDelivery monitoring (bounce rates, blocked emails, active campaigns)
/domainsdomains.vueauth, platform-adminDomain health across all organizations
/reviewreview.vueauth, platform-adminContent review queue

Middleware

Two middleware layers protect admin routes:

  1. auth.ts — redirects unauthenticated users to /login (with redirect query param)
  2. platform-admin.ts — queries api.platformAdmin.isPlatformAdmin and redirects non-admins to /unauthorized

Composables

ComposablePurpose
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:

VariableDescriptionDefault
NUXT_PUBLIC_CONVEX_URLConvex deployment URL (same as web app)
NUXT_PUBLIC_CONVEX_SITE_URLConvex site URL (same as web app, used for auth proxy)
NUXT_PUBLIC_SITE_URLAdmin dashboard URLhttp://localhost:3001

Security

The admin app is configured with strict security headers via nuxt-security:

  • CSP — strict script-src, object-src: 'none', Convex URL in connect-src
  • HSTS — enabled with long max-age
  • X-Frame-OptionsDENY (prevents embedding)
  • Referrer-Policystrict-origin-when-cross-origin

Key Files

FilePurpose
nuxt.config.tsApp configuration, security headers, runtime config
app/lib/auth-client.tsBetterAuth client initialization
app/lib/convex-auth.tsJWT token caching and refresh
app/plugins/convex.client.tsConvex client initialization and auth wiring
server/api/auth/[...].tsAuth proxy server route
app/middleware/auth.tsAuthentication middleware
app/middleware/platform-admin.tsPlatform admin role middleware
app/layouts/default.vueSidebar layout with navigation