- Implemented Prisma schema with models for AdminUser, AppSettings, and Snapshot. - Created seed script to initialize the database with an admin user and JWT secret. - Developed admin login page with form handling and error management. - Added API routes for admin login, logout, change password, and JWT verification. - Integrated Stripe for payment intent management in admin orders. - Established middleware for protecting admin routes with JWT authentication. - Created Zustand stores for managing persona and snapshot states.
6.0 KiB
6.0 KiB
Plan: Admin Dashboard Auth + CRUD + Enhancements
TL;DR
Add password authentication to the admin dashboard, enable full CRUD for personas/pricing, and add useful admin features like order analytics and configurator settings management.
Phase 1: Admin Authentication (Database-backed with Prisma + SQLite)
Steps
-
Setup Prisma + SQLite
- Install
prismaand@prisma/client - Create
prisma/schema.prismawith SQLite provider - Models:
AdminUser(id, username, passwordHash, createdAt),AppSettings(key, value) - Run
npx prisma db pushto create the database - Create
src/lib/prisma.ts— singleton Prisma client
- Install
-
Create seed script
prisma/seed.ts- Creates default admin user with bcrypt-hashed password
- Generates and stores JWT secret in
AppSettingstable - Run with
npx prisma db seed
-
Create admin auth API routes
src/app/api/admin/login/route.ts— POST, accepts{ username, password }, verifies bcrypt hash from DB, returns JWT in httpOnly cookie (JWT secret from DB)src/app/api/admin/verify/route.ts— GET, checks JWT cookie validitysrc/app/api/admin/logout/route.ts— POST, clears auth cookiesrc/app/api/admin/change-password/route.ts— POST, accepts{ currentPassword, newPassword }, updates hash in DB
-
Create admin middleware
src/middleware.ts- Protects all
/admin/*routes (except/admin/login/) - Checks for valid JWT cookie, redirects to
/admin/login/if missing/invalid - Note: middleware can't use Prisma directly (edge runtime), so JWT secret needs to be in env OR verify via API call
- Protects all
-
Create admin login page
src/app/admin/login/page.tsx- Username + password form (styled to match existing admin design)
- Calls login API, redirects to
/admin/on success - Shows error on wrong credentials
-
Add JWT_SECRET to .env.local (only this one — password is in DB)
- Alternative: store JWT secret in DB and load at startup into a module-level cache
Dependencies to install
prisma(dev),@prisma/client,bcryptjs,@types/bcryptjs(dev),jose
Phase 2: Full CRUD for Pricing Items + Personas
Steps (depends on Phase 1)
-
Add/Remove pricing items in admin — Update
src/app/admin/page.tsx- "Add Item" button with fields: id (auto-slug from label), label, price
- Delete button per row (with confirmation)
- Update
usePricingStoreto supportaddItem()andremoveItem()actions
-
Persona management section — New section in admin page
- List current personas with edit capability (label, description, colors)
- Add new persona form
- Delete persona button
- Create
usePersonaStoreor extendusePricingStoreto manage persona definitions - Sync persona list with ConfigPanel's
PERSONA_OPTIONS(currently hardcoded)
Phase 3: Additional Admin Features
Steps (parallel with Phase 2)
-
Orders dashboard section — New section in admin page
- Show orders from Stripe API (list recent payments)
- Display: order ID, customer email, amount, status, date
- New API route
src/app/api/admin/orders/route.tsto fetch from Stripe
-
Analytics overview cards at top of admin page
- Total revenue (from Stripe)
- Number of orders
- Most popular persona
- Most popular color
-
Configurator settings section
- Edit default color (
#96a2b6) - Toggle available color options
- Set min/max price boundaries
- Edit default color (
-
Logout button in admin header
Relevant Files
src/app/admin/page.tsx— Main admin dashboard (add CRUD UI, analytics)src/app/admin/login/page.tsx— New login pagesrc/app/api/admin/login/route.ts— New auth APIsrc/app/api/admin/verify/route.ts— New verify APIsrc/app/api/admin/orders/route.ts— New orders APIsrc/middleware.ts— New, protects admin routessrc/lib/prisma.ts— New, Prisma client singletonprisma/schema.prisma— New, database schema (AdminUser, AppSettings)prisma/seed.ts— New, seeds default admin usersrc/store/usePricingStore.ts— AddaddItem(),removeItem()actionssrc/components/ConfigPanel.tsx— Persona options currently hardcoded (PERSONA_OPTIONS const).env.local— Add ADMIN_PASSWORD, ADMIN_JWT_SECRET
Verification
- Visit
/admin/without login → redirected to/admin/login/ - Enter wrong password → error message shown
- Enter correct password → redirected to
/admin/ - Add a pricing item → appears in configurator pricing breakdown
- Remove a pricing item → disappears from pricing
- Edit persona → reflected in ConfigPanel
- Orders section shows real Stripe payment data
- Refresh admin page → still logged in (cookie persists)
- Run
npx vitest run→ all existing tests pass
Decisions
- Database: Prisma + SQLite — file-based, no external server needed, real ORM
- Admin password stored as bcrypt hash in DB (not in env vars)
- JWT secret stored in AppSettings table in DB
- JWT in httpOnly cookie — secure, no localStorage tokens
- jose library for JWT — lightweight, edge-compatible (works in Next.js middleware)
- Personas need to move from hardcoded array to store-driven — breaking change in ConfigPanel
- Stripe orders fetched via Stripe API directly — no local DB needed for orders
Further Considerations
- Persona storage: Currently hardcoded in ConfigPanel. Moving to a store means ConfigPanel reads from store dynamically. This is required for admin CRUD to work. Recommended: create persona store with localStorage persistence (same pattern as pricing store)
- Stripe orders vs local orders: Currently orders only exist client-side. To show in admin, we fetch from Stripe's payment intents list. Recommended: use Stripe API directly, no local DB needed
- Multi-admin support: Currently single password. If needed later, can upgrade to NextAuth with credentials provider. Recommended: start simple, upgrade if needed