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_hashandis_admin = trueon a user row; there is no separate admin identity store. - Portal secrets stay in
portal.env.productionon the server, apart from backend secrets (mirrors Karat's env split).