Skip to content

0006 — Next.js admin portal against the existing admin API

Status: accepted (2026-07-23)

Context

The restored backend already ships a complete back office as JSON — /api/v1/admin/* covers fantasy operations, catalogue, gateways, users, orders/refunds/reconciliation, tickets, affiliates, and predictions, gated by the JWT claim role == "admin". What it lacked was any UI, and any way to obtain an admin token without going through the phone-OTP flow: OTP login requires the live SMS provider and a phone in hand, which is wrong for an operations console.

Decision

Build aim-store-admin/ as a Next.js 14 (App Router, standalone output) portal — the same shape as Karat's proven portal — served at admin.aimstoreorg.com (aim_admin_portal, :3002). Authentication is NextAuth credentials against a new backend endpoint, POST /api/v1/auth/admin/login: phone + password, verified against users.password_hash and users.is_admin, minting the same custom JWT the OTP flow issues so it works unchanged through the existing dual-auth middleware and x-sdk-access-token header. The endpoint returns a uniform 401 for every failure mode and rate-limits per phone+IP. The browser never holds the API token — server-side portal routes proxy and inject it.

Consequences

  • No parallel admin backend to build or keep in sync; the portal is a thin client over routes that already exist.
  • One new public auth endpoint to watch; its uniform-401 + rate-limit design keeps it from becoming an account-probing oracle.
  • Bootstrapping an operator means setting a password_hash and is_admin = true on a user row; there is no separate admin identity store.
  • Portal secrets stay in portal.env.production on the server, apart from backend secrets (mirrors Karat's env split).