Unboxd is a React + Firebase application with two deployable frontends:
apps/web: customer-facing mystery jersey experienceapps/admin: admin operations dashboard
Both apps use Vite + TypeScript and rely on Vercel Serverless Functions for protected writes and admin operations.
- React 18 + React Router 7
- TypeScript
- Vite 6
- Tailwind CSS v4 + tokenized theme CSS
- Redux Toolkit + React Redux
- Firebase Auth + Firestore + Realtime Database + Storage
- Vercel Functions (Node/TS)
- Vitest + Testing Library
- Playwright (smoke E2E)
.
|- apps/
| |- web/ # Web app entry + web API functions
| | |- main.tsx
| | |- index.html
| | |- api/
| |- admin/ # Admin app entry + admin API functions
| |- main.tsx
| |- index.html
| |- api/
|- src/
| |- apps/web/ # Web app source (routes, pages, store, services)
| |- apps/admin/ # Admin app source (routes, pages, store, services)
| |- styles/
| |- main.tsx # Web app runtime bootstrap
|- scripts/
| |- check-required-env.mjs
| |- check-required-prod-secrets.mjs
| |- set-admin-claim.mjs
|- docs/
| |- release-policy.md
| |- runbooks/
|- .github/workflows/ci.yml
- Node.js 20.x (matches CI)
- npm 10+
- Vercel CLI (
npm i -g vercel) for local/api/*emulation - Firebase project credentials for client + Admin SDK
npm ciSet in .env or .env.local:
VITE_FIREBASE_API_KEYVITE_FIREBASE_AUTH_DOMAINVITE_FIREBASE_PROJECT_IDVITE_FIREBASE_STORAGE_BUCKETVITE_FIREBASE_MESSAGING_SENDER_IDVITE_FIREBASE_APP_IDVITE_PAYMENT_PROVIDER_DEFAULT(codorwish)
VITE_FIREBASE_APP_CHECK_SITE_KEYVITE_WISH_ENABLED(true/false)VITE_SENTRY_DSNVITE_GA4_MEASUREMENT_IDVITE_API_PROXY_TARGET(defaults tohttp://localhost:3000)
FIREBASE_PROJECT_IDFIREBASE_CLIENT_EMAILFIREBASE_PRIVATE_KEY
WISH_API_BASE_URLWISH_API_KEYWISH_WEBHOOK_SECRET
Validation scripts:
node scripts/check-required-env.mjs
node scripts/check-required-prod-secrets.mjsnpm run devThis starts Vite for apps/web.
cd apps/web
vercel dev --listen 3000The Vite dev server proxies /api/* to http://localhost:3000 by default.
npm run dev:admincd apps/admin
vercel dev --listen 3001Then run admin Vite with proxy override:
set VITE_API_PROXY_TARGET=http://localhost:3001 && npm run dev:adminPowerShell alternative:
$env:VITE_API_PROXY_TARGET="http://localhost:3001"; npm run dev:adminnpm run dev- web app dev servernpm run dev:admin- admin app dev servernpm run build- build web app (apps/web/dist)npm run build:admin- build admin app (apps/admin/dist)npm run preview- preview web build on127.0.0.1:4173npm run preview:admin- preview admin build on127.0.0.1:4174npm run typecheck- TypeScript checksnpm run lint- ESLint onsrcnpm run test- Vitest test runnpm run test:watch- Vitest watch modenpm run test:e2e- Playwright smoke testnpm run audit- production dependency auditnpm run admin:claim -- <uid-or-email> <true|false>- set Firebase customadminclaim
- Public routes: home, auth, legal/help pages
- Protected routes: order flow, dashboard, closet, marketplace, trade flow, transactions, badges
- Auth bootstrap uses Firebase Auth state + user profile upsert
- Order and trade writes are server-mediated through
/api/*
Web API handlers:
/api/bootstrapUser/api/createCodOrder/api/createTradeOffer/api/transitionTradeOffer/api/createTransactionFromTrade/api/initiateWishPayment/api/wishWebhook
- Admin-only routes behind Firebase custom claim
admin: true - Dashboard, orders, order details, transactions, listings, trades, users
- Uses
/api/admin/*endpoints
Admin API handlers:
/api/admin/dashboardSummary/api/admin/listOrders/api/admin/getOrder/api/admin/updateOrder/api/admin/listTransactions/api/admin/listListings/api/admin/moderateListing/api/admin/listTrades/api/admin/transitionTrade/api/admin/listUsers/api/admin/setUserAccess/api/admin/deleteUser
Recommended model: two Vercel projects.
- Web project root:
apps/web - Admin project root:
apps/admin - Install command:
npm ci --include=dev - Build command:
npm run build - Output directory:
dist
See VERCEL_DEPLOY.md for the stable configuration details.
CI workflow: .github/workflows/ci.yml
Checks include:
- lint
- typecheck
- unit tests
- build
- dependency audit
- Playwright smoke test
- gitleaks secret scan
- production secret gate on
main
- Release policy:
docs/release-policy.md - Runbooks:
docs/runbooks/- auth outage fallback
- incident triage
- payment reconciliation
- rollback
- Web users are blocked from admin-only accounts; admin access is isolated to the admin app.
- Payment provider defaults are controlled by env (
codfallback, Wish optional). - API handlers enforce authentication and role checks via Firebase ID tokens.