Skip to Content
StackBlaze Templates — operator documentation

Admin portal

A platform-owner portal at /admin, separate from the tenant dashboards. It’s for you (the SaaS operator), not your customers — analytics, tenant and user management, platform settings, and a visual theme builder.

PathWhat it does
/adminPlatform analytics.
/admin/tenantsEvery organisation, with plan and usage.
/admin/usersEvery user — search, change plan, suspend.
/admin/settingsMaintenance mode and shared sidebar links.
/admin/themeVisual theme builder (theme-customizer feature).

API routes live under app/api/admin/; the queries and operations behind them live under lib/admin/.

Access control

The portal is gated on the user’s role column: every admin route and action checks session.user.role === "admin" (see lib/admin/guard.ts and app/(admin)/layout.tsx). Anyone else — including workspace owners — is redirected to their dashboard.

role is a platform-wide privilege, separate from a user’s per-workspace membership role (owner/member). Granting admin gives access to every tenant’s data through the portal. Hand it out deliberately.

Seeding the first admin

New accounts always sign up as regular users — there’s no “make me an admin” button. You promote the first platform admin from the command line with the bundled seed script (scripts/seed-admin.mts), which sets a user’s role to admin by email.

Register the account first

Sign up through the app at /register with the email you want to promote. The script looks the user up by email and exits if no matching account exists, so the account must already be there.

Set the target email

SUPER_ADMIN_EMAIL ships in .env.example, so the key is already in your .env.local after you copy it. Give it a value:

SUPER_ADMIN_EMAIL="you@example.com"

The script reads SUPER_ADMIN_EMAIL and connects with DATABASE_URL, both from .env.local. For a one-off promotion you can also pass it inline instead:

SUPER_ADMIN_EMAIL=you@example.com npm run db:seed-admin

Run the seed script

npm run db:seed-admin

On success it prints ✓ Promoted you@example.com to admin. If it reports no user found, register that email first (previous step).

Sign in and open the portal

The role is read from the session, so sign out and back in to refresh it, then visit http://localhost:3000/admin .

To add more admins later, repeat the steps with a different SUPER_ADMIN_EMAIL — the script promotes whichever registered account that email belongs to.

What you can do

Analytics (/admin)

The landing view aggregates the whole platform: total users, new sign-ups in the last 30 days, total AI tokens consumed, active organisations this month, plan distribution, and a daily sign-up trend.

Tenants (/admin/tenants)

Lists every organisation (newest first) with its plan, member count, tokens used this month, and creation date — a read-only view of who’s on the platform and how much they’re using.

Users (/admin/users)

Lists users (newest first) and lets you:

  • Search by email.
  • Change a user’s plan — sets the plan on their primary organisation.
  • Suspend or restore — a suspended user is banned from signing in.

There is deliberately no “promote to admin” button here. Granting the admin role is a command-line action — see Seeding the first admin.

Changing a plan here writes the subscription row directly — it does not call Stripe. For a paid customer, this overrides their plan in your database while Stripe keeps billing the old one, so the two drift apart. Use it for comps and manual overrides; for a normal plan change, let the customer go through billing so Stripe stays in sync.

Settings (/admin/settings)

Two platform-wide controls, each stored as a system_settings row:

  • Maintenance mode — when on, non-admin users get an “Under Maintenance” page instead of the dashboard. Admins (and active impersonation sessions) bypass it, so you can keep working while it’s enabled.
  • Sidebar links — the Support and Feedback links shown in every workspace sidebar. Set each URL and toggle whether it appears. Both default to visible with no URL until you set one.

Theme (/admin/theme)

A visual theme builder that restyles the entire app. Changes apply to every workspace the moment you save. It controls:

  • Brand palettes — primary, secondary, and tertiary (accent), each with base, foreground, and optional hover/active colors.
  • Base colors — background, foreground, card, border, ring, destructive.
  • Radius, shadow, and shell background — including solid, gradient, or image backgrounds and a glass-tint overlay.
  • Component toggles — button ripple and frosted-glass surfaces.
  • Light and dark variants, with a live preview gallery beside the controls.

The saved theme is a single system_settings row, serialized to CSS and injected into the root layout — so it takes effect on the next page load with no rebuild.

The theme builder is the theme-customizer feature, which depends on the admin portal. If you didn’t select it at scaffold time, /admin/theme isn’t part of your project.

Dependencies

The admin portal implies multi-tenancy — selecting it at scaffold time enables the tenant model automatically, since the portal manages tenants. The theme customizer, in turn, implies the admin portal.