# 08 — Admin Console (Next.js) Map > Surface: `apps/admin` > Stack: Next.js 16 App Router + MUI (`@d3ro/ui` theme) + Supabase service role + .NET proxy > Role: back office CRM/ops — users, subscriptions, models, usage, audit, releases, ads --- ## 1. Route tree (`src/app/`) ### Public | Route | Purpose | |---|---| | `/login` | Email/password → `/api/auth/login`; Google OAuth via Supabase → `/auth/callback`; maps error keys | | `/unauthorized` | 403 screen | | `/auth/callback` | OAuth code → session exchange | ### Protected `(admin)` (guarded by `(admin)/layout.tsx` → `requireManager()`) | Route | Purpose | |---|---| | `/` | Dashboard: backend stats, node health, MRR/ARR/active subscriptions (Supabase), recent errors | | `/pipelines` | AI/voice pipeline telemetry; explicit "unavailable" card when no measured data | | `/models` | LLM model + STT provider manager (presets, CRUD, test) | | `/releases` | Forgejo live release hub (assets, platforms, sizes, downloads, SHA-256) | | `/users` | User directory: search + tier/role filters | | `/users/[id]` | User 360: profile, subscription, 30-day usage, role change (admin+), payment history | | `/subscriptions` | Subscription ops list + filters; license issuer (super_admin) | | `/subscriptions/new` | Grant VIP subscription (admin+) | | `/subscriptions/[id]` | Subscription detail: edit (manager+) / delete (admin+) | | `/ads` | Ad mediation console (10 networks, all fail_closed) + reward stats | | `/support` | **Stub** — explicit "not configured" panel | | `/usage` | Combined LLM + STT usage/cost analytics | | `/audit-log` | Supabase audit log list + target filter + pagination | | `/audit-log/[id]` | Audit detail with before/after diff | --- ## 2. API route handlers (`src/app/api/`) | Route | Methods | Behavior | |---|---|---| | `/api/auth/login` | POST | Validate body, reject honeypot `trap`, in-memory rate limit/lockout, proxy to `.NET /api/auth/login` (7s timeout, HTTPS in prod), validate token/role/email/expiry, sign HMAC session cookie `d3ro_admin_session` | | `/api/auth/logout` | POST/GET | Clear cookie; GET redirects `/login` | | `/api/admin/backend/[...segments]` | GET/POST/PUT/DELETE | Allow-list proxy to `.NET /api/admin/*`; required role by path; same-origin for non-GET; ≤64KB; UUID idempotency-key for mutations | | `/api/admin/license` | POST | Same-origin + `requireVerifiedBackendSession('super_admin')`; sign Ed25519 key with `ADMIN_LICENSE_PRIVATE_KEY` (`@d3ro/core/utils/crypto-license`); best-effort audit | | `/api/admin/supabase/[operation]` | GET/POST/PATCH/DELETE | `admin-users`, `admin-subscriptions`, `admin-payments` via RPCs; strict allow-lists; Payple live history returns `501` | | `/auth/callback` | GET | Supabase OAuth exchange | --- ## 3. Libraries (`src/lib/`) | File | Purpose | |---|---| | `admin-session.ts` | Session types, secret validation (≥32 bytes), strict cookie parse, `adminCookieSecure()` escape hatch | | `security.ts` | server-only HMAC-SHA256 sign/verify, in-memory rate limit/lockout, runtime security validation | | `admin-guard.ts` | RSC guards `requireManager`/`requireAdmin`/`requireSuperAdmin`, role helpers | | `edge-session.ts` | Edge-runtime HMAC verify via WebCrypto (used by `proxy.ts`) | | `backend-session.ts` | `requireApiServerOrigin`, `requireVerifiedBackendSession(minRole)` (verifies cookie + `.NET /api/auth/me`), `fetchAdminBackend` | | `api-server.ts` | server-only data access to .NET backend (stats, users, endpoints, usage reports) | | `backend-admin-client.ts` | client CRUD for model/STT endpoints with auto idempotency keys | | `admin-api.ts` | client `callAdminApi` for Supabase admin operations | | `supabase-admin.ts` | service-role client, actor resolution RPC, product user fetch | | `supabase-browser.ts` / `supabase-server.ts` | client/server Supabase wrappers | | `ad-monetization.ts` | `MEDIATION_ROSTER` (fail_closed) + ad reward stats | | `subscription-metrics.ts` | MRR/ARR/active/tier breakdown | | `audit-sanitize.ts` | recursive redaction of sensitive keys in audit snapshots | | `forgejo-releases.ts` | Forgejo release feed parser (`RELEASE_REPO_URL`) | | `console-theme.ts` | design tokens + MUI style presets | Root files: `instrumentation.ts` (startup security validation), `proxy.ts` (edge middleware: public paths, auth redirect, security headers), `robots.ts` (disallow all). --- ## 4. Components (`src/components/`) `admin-sidebar` (nav island: Core Platform / Customer & Revenue / Intelligence & Security), `unavailable-admin-panel` (reusable "NOT CONNECTED", no sample data), `audit-diff-viewer`, `payment-history`, `subscription-form`, `role-change-dialog` + `role-change-button`, `memo-dialog`, `license-issuer-button` + `license-issuer-dialog`, `checksum-copy`, charts (`dau-chart`, `feature-usage-chart`, `top-users-chart`). --- ## 5. Admin status summary - Dashboard, models, releases, users, subscriptions, usage, audit log, ads: **implemented** against real backend/Supabase data. - Security: HMAC signed sessions, RSC + edge guards, rate limit/lockout, honeypot, strict origin/allow-list, no-store, robots disallow, audit redaction. Red-team scenarios were exercised (see `memory/project_status.md`). - Explicit fail-closed / not-configured states (by design, not bugs): - `/support` stub — no ticket/SLA/diagnostics contract. - `/ads` — all 10 networks `fail_closed`; no live bids. - `admin-payments` Payple live history → `501`. - `UnavailableAdminPanel` whenever Supabase env absent; writes disabled, no sample metrics. - `/pipelines` and dashboard node/error sections render only measured data. - Deploy: `Dockerfile.admin` / `apps/admin/Dockerfile` → GHCR + NAS compose; GitLab admin NAS deploy job disabled.