- NestJS backend: auth, restaurants, orders, drivers, payments, tracking, reviews, zones, admin, email - Next.js 14 frontend: landing, restaurants, checkout, tracking, dashboards, onboarding - Expo mobile app: driver orders and earnings screens - PostgreSQL + PostGIS schema with seed data - Docker Compose for local dev (Postgres, Redis, OSRM) - MapLibre GL + OpenStreetMap integration - Stripe subscription and payment processing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
220 lines
6.6 KiB
Markdown
220 lines
6.6 KiB
Markdown
# The Vibe — Fair-Trade Delivery Platform
|
||
|
||
> Flat-fee food delivery for the Greater Toronto Area.
|
||
> No commissions. No exploitation. Restaurants keep 100% of profits.
|
||
|
||
---
|
||
|
||
## Quick Start
|
||
|
||
```bash
|
||
# 1. Copy env and fill in your keys
|
||
cp .env.example .env
|
||
|
||
# 2. Start PostgreSQL/PostGIS + Redis
|
||
npm run docker:up
|
||
|
||
# 3. Install all dependencies
|
||
npm install
|
||
|
||
# 4. Start backend + frontend
|
||
npm run dev
|
||
```
|
||
|
||
Backend: http://localhost:3001
|
||
Frontend: http://localhost:3000
|
||
PgAdmin: http://localhost:5050 (after `docker-compose --profile dev up`)
|
||
|
||
---
|
||
|
||
## Project Structure
|
||
|
||
```
|
||
The_Vibe/
|
||
├── packages/
|
||
│ ├── database/
|
||
│ │ ├── schema.sql # Full PostgreSQL + PostGIS schema
|
||
│ │ └── seed.sql # GTA zones + dev data
|
||
│ ├── backend/ # NestJS API (port 3001)
|
||
│ │ └── src/
|
||
│ │ ├── modules/
|
||
│ │ │ ├── auth/ # JWT auth, roles guard
|
||
│ │ │ ├── drivers/ # Sessions, break-even algorithm
|
||
│ │ │ ├── restaurants/ # Menu, savings dashboard
|
||
│ │ │ ├── orders/ # Full order lifecycle
|
||
│ │ │ ├── payments/ # Stripe subscriptions + daily fee
|
||
│ │ │ ├── tracking/ # Socket.IO real-time gateway
|
||
│ │ │ ├── zones/ # GTA PostGIS geofencing
|
||
│ │ │ ├── menu/ # Menu CRUD
|
||
│ │ │ └── admin/ # Platform analytics
|
||
│ │ └── database/ # pg Pool + transaction helper
|
||
│ └── web/ # Next.js 14 frontend (port 3000)
|
||
│ └── src/
|
||
│ ├── app/
|
||
│ │ ├── page.tsx # Landing page
|
||
│ │ ├── restaurants/page.tsx # Browse + map
|
||
│ │ ├── orders/[id]/track/ # Real-time order tracking
|
||
│ │ ├── driver/dashboard/ # Break-even dashboard
|
||
│ │ ├── restaurant/dashboard/ # Savings dashboard
|
||
│ │ └── admin/page.tsx # Platform admin
|
||
│ ├── components/
|
||
│ │ └── map/MapView.tsx # MapLibre GL JS
|
||
│ ├── hooks/
|
||
│ │ └── useDriverTracking.ts # GPS + Socket.IO
|
||
│ └── lib/
|
||
│ ├── api.ts # Axios client
|
||
│ └── osrm.ts # OSRM routing client
|
||
└── docker-compose.yml
|
||
```
|
||
|
||
---
|
||
|
||
## Pricing Model
|
||
|
||
| Stakeholder | Model | Amount |
|
||
|---|---|---|
|
||
| Restaurant | Monthly subscription | $500/month |
|
||
| Restaurant | Per-order fee | $0.10/order |
|
||
| Restaurant | CC processing (Stripe) | 2.9% + $0.30 |
|
||
| Driver | Daily access fee | $20/day |
|
||
| Driver | Delivery fee | $5 per delivery (kept 100%) |
|
||
| Driver | Tips | 100% kept |
|
||
| Customer | Delivery fee | $5 flat |
|
||
| Customer | Hidden fees | $0 |
|
||
|
||
### Driver Break-Even
|
||
- 4 deliveries × $5 = $20 → break even
|
||
- Every delivery after #4 = pure profit
|
||
- Tips never counted against break-even
|
||
|
||
### Restaurant Savings Example (100 orders/day)
|
||
- UberEats at 30% on $35 avg: **$31,500/month**
|
||
- The Vibe: $500 + $300 + CC ≈ **$3,400/month**
|
||
- **Savings: ~$28,000/month**
|
||
|
||
---
|
||
|
||
## API Endpoints
|
||
|
||
### Auth
|
||
```
|
||
POST /api/v1/auth/register
|
||
POST /api/v1/auth/login
|
||
GET /api/v1/auth/me
|
||
```
|
||
|
||
### Restaurants
|
||
```
|
||
GET /api/v1/restaurants?lng=&lat=&radius=&cuisine=
|
||
GET /api/v1/restaurants/savings-calculator?orders=&avgValue=
|
||
GET /api/v1/restaurants/:slug
|
||
POST /api/v1/restaurants (restaurant_owner)
|
||
GET /api/v1/restaurants/dashboard/savings (restaurant_owner)
|
||
```
|
||
|
||
### Orders
|
||
```
|
||
POST /api/v1/orders (customer)
|
||
GET /api/v1/orders/mine (customer)
|
||
GET /api/v1/orders/:id
|
||
PATCH /api/v1/orders/:id/confirm (restaurant_owner)
|
||
PATCH /api/v1/orders/:id/ready (restaurant_owner)
|
||
PATCH /api/v1/orders/:id/pickup (driver)
|
||
PATCH /api/v1/orders/:id/delivered (driver)
|
||
```
|
||
|
||
### Drivers
|
||
```
|
||
GET /api/v1/drivers/me/session
|
||
POST /api/v1/drivers/me/session/start
|
||
POST /api/v1/drivers/me/session/end
|
||
PATCH /api/v1/drivers/me/location
|
||
GET /api/v1/drivers/nearby?lng=&lat=
|
||
```
|
||
|
||
### Payments (Stripe)
|
||
```
|
||
POST /api/v1/payments/restaurant/subscribe
|
||
POST /api/v1/payments/driver/daily-fee
|
||
POST /api/v1/payments/driver/payment-method
|
||
POST /api/v1/payments/orders/:orderId/intent
|
||
POST /api/v1/payments/webhook
|
||
```
|
||
|
||
### Zones (GTA Geofencing)
|
||
```
|
||
GET /api/v1/zones
|
||
GET /api/v1/zones/geojson # GeoJSON FeatureCollection
|
||
GET /api/v1/zones/check?lng=&lat=
|
||
```
|
||
|
||
### Admin
|
||
```
|
||
GET /api/v1/admin/stats
|
||
GET /api/v1/admin/revenue?days=30
|
||
GET /api/v1/admin/restaurants
|
||
GET /api/v1/admin/drivers
|
||
PATCH /api/v1/admin/drivers/:id/approve
|
||
```
|
||
|
||
---
|
||
|
||
## Map Stack
|
||
|
||
- **MapLibre GL JS** — open-source map renderer (browser)
|
||
- **OpenStreetMap** tiles via MapTiler (free tier)
|
||
- **OSRM** — self-hosted routing engine (ontario-latest.osm.pbf)
|
||
- **PostGIS** — geofencing, spatial queries, driver proximity
|
||
|
||
### GTA Zones (active at launch)
|
||
1. Downtown Toronto (priority 10)
|
||
2. Liberty Village (priority 9)
|
||
3. North York (priority 8)
|
||
4. Scarborough (priority 7)
|
||
5. Mississauga (priority 6)
|
||
|
||
---
|
||
|
||
## Real-Time Architecture
|
||
|
||
```
|
||
Driver App Socket.IO Gateway Customer App
|
||
│ │ │
|
||
├─ emit driver:location ──► │
|
||
│ ├─ emit driver:moved ────►│
|
||
│ │ │
|
||
│ ◄── emit join:order ──────┤
|
||
│ │ │
|
||
│ Orders Service │
|
||
│ ◄── order:new ────────────┤
|
||
│ ├─ emit to restaurant room │
|
||
```
|
||
|
||
Driver location updates every 5 seconds via WebSocket.
|
||
DB breadcrumbs written to `delivery_tracking` table.
|
||
|
||
---
|
||
|
||
## Environment Variables Required
|
||
|
||
See `.env.example` for full list. Key ones:
|
||
- `DATABASE_URL` — PostgreSQL + PostGIS connection string
|
||
- `STRIPE_SECRET_KEY` — Stripe API key
|
||
- `STRIPE_RESTAURANT_PRICE_ID` — $500/month Price ID in Stripe
|
||
- `JWT_SECRET` — random secret, min 32 chars
|
||
- `NEXT_PUBLIC_MAPTILER_KEY` — free MapTiler account for OSM tiles
|
||
|
||
---
|
||
|
||
## Performance Targets
|
||
|
||
| Metric | Target |
|
||
|---|---|
|
||
| Restaurants | 1,000+ |
|
||
| Active drivers | 5,000 |
|
||
| Orders/day | 50,000 |
|
||
| Location updates/sec | ~50,000 (drivers × 0.2hz) |
|
||
| DB connections | max 20 (pg pool) |
|
||
|
||
Scale path: Redis adapter for Socket.IO → horizontal Node scaling → Postgres read replicas.
|