npm install at the root).DATABASE_URL in .env.local (see DB Setup).zooly-app backend running locally (port :3004) for the API.| Service | Port | Notes |
|---|---|---|
| face-app (Vite dev) | :3009 | Moved off :3008 to avoid colliding with the merch admin dev default |
| zooly-app (backend API) | :3004 | Public + admin routes |
The face-app reads these (Vite inlines them at build time):
| Variable | Example (local) | Example (staging) |
|---|---|---|
VITE_APP_URL | http://localhost:3004 | https://dev.zooly.ai |
VITE_AUTH_URL | http://localhost:3004 | https://auth.zooly.ai |
See apps/face-app/example.env.
Always include the
https://scheme in production/preview values. Without it, client fetches resolve relative to the current origin and return the SPA HTML fallback instead of JSON (a common cause of "404 / Board not found" symptoms in deployed environments).
Add the face-app origins to ALLOWED_DOMAINS_CORS in zooly-app (apps/zooly-app/.env.local): the dev origin (http://localhost:3009) and https://face.zooly.ai (plus https://dev-face.zooly.ai for staging). The routes reuse the shared CORS helper apps/zooly-app/app/api/merch/cors.ts.
Migrations are hand-written and applied locally only against .env.local:
# from packages/db
npm run db:migrate
Seed the boards (idempotent — world-cup default board + 48 BoardTeams, nba board + 2 BoardTeams; resolves the default campaign by slug and sets each BoardTeam's campaignId pointer):
# from packages/db (uses apps/zooly-app/.env.local)
npm run db:seed:leaderboard
The seed script is packages/db/src/seed/seedTeamLeaderboard.ts. Override the shared campaign slug with FACE_DEFAULT_CAMPAIGN_SLUG (defaults to mock). If that campaign is not present in the target DB, teams seed with campaignId = null (no store link) and a warning is logged.
Seeding a remote/staging DB writes data to a remote database — only do this with explicit approval. To target a specific DB, run the seed with a different env file, e.g.
npx tsx --env-file=../../.env.dev src/seed/seedTeamLeaderboard.ts. Neverdb push; never run migrations on a remote DB without approval.
Hero face-paint images live in S3 under img/face-app/ (Brazil, Germany, USA, Japan), uploaded via scripts/upload-files-to-s3.ts.
# backend
npm run dev # in apps/zooly-app (port 3004)
# face-app
npm run dev # in apps/face-app (port 3009)
Then open http://localhost:3009/ — it redirects to /world-cup.
The face-app deploys as a Vercel project (zoolyai/face-app-dev for staging). apps/face-app/vercel.json provides the SPA rewrite so board slugs and /admin/* deep links resolve to the SPA. Backend API + DB are shared with zooly-app (zoolyai/zooly-app-dev for staging).