ChatPlay is a full-featured, real-time chat platform where users join rooms, share voice notes and images, react to messages, and challenge each other to multiplayer games — all powered by Supabase and deployed on Vercel.
| Feature | Details | |
|---|---|---|
| 🔐 | Auth | Username + password only — no OAuth. Auto-profile via Postgres trigger |
| 🏠 | Rooms | Public & private rooms up to 100 members; invitation-only access for private |
| 💬 | Messaging | Optimistic send · 120-message history · inline images (PNG/JPEG/WebP/GIF, 10 MiB) |
| 🎙️ | Voice notes | Browser-mic WebM recording up to 60 s · Supabase Storage · inline playback |
| 🔥 | Reactions | 🔥 😂 👏 💚 with live counts and per-user toggle |
| 👁️ | Presence | Real-time online list per room via Supabase Realtime presence |
| 🔔 | Notifications | Persistent alerts for room invites & game challenges over private channels |
| 🛡️ | Moderation | Owner / admin / member roles · kick members · role-change notifications |
| 🎮 | Games | Tic-Tac-Toe · Word Scramble · Trivia Sprint (live leaderboard) |
| 🧑💻 | Dev panel | List accounts · audit log · delete users via protected Edge Function |
| 🧹 | Data cleanup | Bulk-delete your own messages, voice files, and images |
| 🌙 | Themes | Dark (default) and light — switchable at runtime |
| Library | Role |
|---|---|
| React 19 + TypeScript | Core UI framework |
| Vite 7 | Build tool and dev server |
| Tailwind CSS 4 | Utility-first styling |
| Radix UI + shadcn primitives | 53 accessible UI components in client/src/components/ui/ |
| Framer Motion | Animated sidebar and spring transitions |
| Wouter | Lightweight client-side routing |
| Sonner | Toast notifications |
| React Hook Form + Zod | Type-safe form validation |
| Lucide React | Icon set |
| Service | Role |
|---|---|
| Supabase Auth | Session management; username stored in profiles table |
| Supabase Postgres + RLS | All data behind row-level security policies |
| Supabase Realtime | Private channels for messages, presence, notifications, game state |
| Supabase Storage | Buckets: voice-messages · chat-images (private) · avatars |
| Edge Functions | chatplay-auth (register/login) · chatplay-admin (dev management + audit) |
| Vercel | Frontend hosting with SPA rewrites |
| pnpm 10 | Package manager |
| Vitest | Unit test runner |
chatplay/
│
├── 📁 client/ # React SPA (Vite root)
│ └── src/
│ ├── 📄 pages/ChatPlay.tsx # Entire authenticated workspace
│ ├── 📁 components/ui/ # 53 Radix/shadcn UI primitives
│ ├── 📁 _core/hooks/ # useSupabaseAuth — session management
│ └── 📁 lib/ # Supabase clients, RPCs, game utils, media utils
│
├── 📁 server/ # Legacy Express/tRPC server (not needed for Vercel)
├── 📁 shared/ # Types and constants shared across client/server
│
├── 📁 supabase/
│ ├── 📁 migrations/ # 14 ordered SQL files (schema, RLS, Realtime, Storage)
│ └── 📁 functions/
│ ├── chatplay-auth/ # Register + login Edge Function
│ └── chatplay-admin/ # Developer user management + audit Edge Function
│
├── 📁 drizzle/ # Legacy Drizzle schema (scaffold only)
├── 📁 docs/ # Security and deployment notes
├── 📄 vercel.json # Vercel deployment config
└── 📄 package.jsonNode.js ≥ 20
pnpm ≥ 10
A Supabase project (free tier works)
git clone <YOUR_GITHUB_REPOSITORY_URL> chatplay
cd chatplay
pnpm installCreate .env.local at the repository root:
VITE_SUPABASE_URL=https://<your-project-ref>.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=<your-anon-key>
⚠️ Never putSUPABASE_SERVICE_ROLE_KEYor any server secret in a Vercel browser variable. Edge Functions manage their own secrets inside the Supabase dashboard.
# Verify order before applying
ls supabase/migrations/*.sql | sort
# Apply 0001 → 0012 via Supabase SQL Editor or CLIAfter applying, disable public Realtime channel access in your Supabase project. See docs/realtime-security-validation.md.
supabase functions deploy chatplay-auth
supabase functions deploy chatplay-admin# ✅ Recommended — fresh Supabase/Vercel path
pnpm dev:frontend
# Legacy Express + Vite compatibility server
pnpm dev🟢 Required — Browser (Vercel + local)
| Variable | Description |
|---|---|
VITE_SUPABASE_URL |
Supabase project URL |
VITE_SUPABASE_PUBLISHABLE_KEY |
Supabase anon key (safe for the browser) |
🔴 Secrets — Supabase dashboard only (never in Vercel)
| Variable | Description |
|---|---|
SUPABASE_SERVICE_ROLE_KEY |
Server-side admin operations in chatplay-admin |
SUPABASE_JWT_SECRET |
Auth bridge JWT validation |
⚪ Legacy — not needed for the Vercel fresh path
| Variable | Description |
|---|---|
DATABASE_URL |
MySQL connection for the legacy Express/Drizzle server |
JWT_SECRET |
Legacy server JWT signing secret |
PORT |
Server port; defaults to the platform-provided value |
NODE_ENV |
development or production |
| Command | Description |
|---|---|
pnpm dev:frontend |
⚡ Start the Vite dev server (Supabase-only path) |
pnpm dev |
Start the legacy Express + Vite compatibility server |
pnpm check |
🔍 TypeScript type-check without emitting |
pnpm test |
🧪 Run the Vitest suite |
pnpm build:frontend |
📦 Build SPA → dist/public for Vercel |
pnpm build |
Build browser bundle + bundled compatibility server |
pnpm start |
dist/ |
pnpm format |
🎨 Format source files with Prettier |
pnpm db:push |
Generate and apply Drizzle migrations (legacy only) |
The vercel.json is already wired up:
| Setting | Value |
|---|---|
| Build command | pnpm build:frontend |
| Output directory | dist/public |
| Install command | pnpm install --frozen-lockfile |
| Routes | All paths rewrite to index.html |
Set these two variables in Vercel → Project Settings → Environment Variables for Preview and Production:
VITE_SUPABASE_URL → https://<project-ref>.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY → <anon-key>
In Supabase Dashboard → Auth → URL Configuration add:
- Your Vercel production URL
https://*.vercel.appfor preview deployments
Full checklist → docs/vercel-supabase-runbook.md
pnpm check # type-check
pnpm test # run tests
pnpm build:frontend # Vercel bundle
pnpm build # full bundleFor schema changes: apply to a staging project, verify RLS with two separate accounts, then promote to production.
╔════════════════════════════════════════════════════════════════╗
║ NEVER expose SUPABASE_SERVICE_ROLE_KEY, JWT_SECRET, ║
║ SUPABASE_JWT_SECRET, or database credentials in browser ║
║ code, Vercel client variables, or Git history. ║
╚════════════════════════════════════════════════════════════════╝
- The browser only receives
VITE_SUPABASE_URLandVITE_SUPABASE_PUBLISHABLE_KEY. All secrets live exclusively in the Supabase Edge Function environment. - Authorization is enforced by Postgres RLS policies and
SECURITY DEFINERfunctions. UI-level hiding is not an authorization boundary. - Private room and image access is enforced by Supabase Storage policies regardless of client state.
- Developer privileges are governed by
profiles.is_developer, validated server-side in thechatplay-adminEdge Function.
