Documentation: English · Tiếng Việt · 日本語
FoodFlow is a multi-tenant food-delivery system with a NestJS API, professional Admin and Restaurant dashboards, and Flutter customer/driver applications. Its managed-production design uses Supabase for PostgreSQL/PostGIS, Realtime, and Storage; Railway for the API, worker, and Redis; and Vercel for the Admin and Restaurant dashboards. Docker Compose keeps a separate Socket.IO/Redis/MinIO compatibility profile for local development and self-hosting.
Release status — 2026-07-14: runtime candidate
52f433641d5093f6d064cfba6c1cd99c8cb035e9passed 144 Jest suites / 1065 tests, typecheck, lint, build, all triggered GitHub workflows, multi-architecture runtime smoke, and High/Critical image scans. Railway migrate/API/worker run the immutable SHA images; all 38 migrations are applied and public health/readiness report database, Redis, and Supabase Storage up. Current Admin and Restaurant Vercel deployments are Ready and their production health/login routes return 200. A controlled production GPS update reached private Supabase Broadcast and PostGIS in 1437 ms, with test data removed afterward. This is not full production certification: controlled-device FCM, the Android/iOS background-location matrix, authenticated browser role journeys, and configured payment/messaging/AI/owned-routing integrations still need evidence.
FoodFlow has four distinct product surfaces. The media below is historical non-production evidence: the manifest records capturedAt 2026-07-10 but no source SHA or image reference, so it does not prove the current source head or a release candidate. Start with the Customer guide for the ordering app, then see the full product gallery and Customer and Driver mobile overview.
| Surface | Product runtime | Current visual evidence | How to review the product |
|---|---|---|---|
| Admin | Next.js web dashboard | Historical stills and GIF | Run the Admin web app; see the gallery. |
| Restaurant | Next.js web dashboard | Historical stills and GIF | Run the Restaurant web app; see the gallery. |
| Customer | Flutter/Riverpod Android/iOS app | One test-only Android emulator still | Read the Customer guide, then launch main_customer.dart on a device/emulator. |
| Driver | Flutter/Riverpod Android/iOS app | Four test-only Android emulator captures | Launch main_driver.dart; existing GPS/notification captures are not release media. |
The mobile captures use simulated GPS and the isolated local stack. Their manifest records a dirty workspace, so authentic release media still requires a clean-head device/emulator recapture from the chosen release candidate. The documentation deliberately does not relabel local media as production evidence.
| Admin sign-in to overview | Restaurant orders to menu |
|---|---|
![]() |
![]() |
| Surface | Source | Runtime | Local target | Primary guide |
|---|---|---|---|---|
| Backend API | backend/ |
NestJS 11, Prisma 6 | http://localhost:3001/api |
— |
| Admin | web/apps/admin/ |
Next.js 15, React 18, next-intl | http://localhost:3000 |
— |
| Restaurant | web/apps/restaurant/ |
Next.js 15, React 18, next-intl | http://localhost:3002 |
— |
| Customer | main_customer.dart |
Flutter/Riverpod native mobile app (Android/iOS) | device or emulator; Android customer flavor |
Customer guide |
| Driver | main_driver.dart |
Flutter/Riverpod native mobile app (Android/iOS) | device or emulator; Android driver flavor |
Customer and Driver guide |
Admin and Restaurant routes are locale-prefixed for vi, en, and ja. The API uses a shared success envelope ({ success: true, data, meta? }) and RFC 7807 Problem Details for errors.
Customer and Driver have no local web URLs. Use their explicit Flutter entrypoints; the --flavor commands below select the Android product flavors.
- Customer ordering, carts, addresses, vouchers, wallet/COD/SePay, reviews, support, and AI assistance.
- Driver online state, dispatch offers, fresh GPS validation, route guidance, ETA, heatmaps, earnings, KYC, and incentives.
- Restaurant order kanban, menu/options, promotions, revenue, reviews, notifications, staff, opening hours, and insights.
- Admin KPIs, orders, restaurants, users, drivers, live maps, promotions, audit, support, export, and AI telemetry.
- Tenant-scoped authorization for restaurant staff, realtime channels, tracking, exports, and administrative resources.
- Keyless MapLibre/OpenFreeMap basemaps plus backend Google Directions/owned OSRM routing when configured; route snapshots and telemetry fail closed instead of inventing coordinates, polylines, or ETA.
- DeepSeek-backed support through the backend adapter, with fail-closed configuration/provider errors, persisted usage telemetry, and no embedded provider key.
Google Maps is not required to boot FoodFlow. When neither Google Directions nor an owned OSRM service is configured, route calculation returns 503 DIRECTIONS_PROVIDER_NOT_CONFIGURED; the API and worker remain healthy. Railway currently runs with RAG_ENABLED=false because no DeepSeek credential is configured.
Flutter customer/driver ─┐
Admin + Restaurant web ──┼── HTTPS ──> NestJS API on Railway
│ │
│ ├── Supabase PostgreSQL/PostGIS
│ ├── Supabase private Broadcast + scoped JWT
│ ├── Supabase Storage
│ ├── Railway managed Redis
│ └── Postgres job outbox + worker
└── authorized Supabase channel subscriptions
Local/self-hosted compatibility: PostgreSQL + Socket.IO + Redis/BullMQ + MinIO
Provider selection is explicit:
| Concern | Managed production | Local/self-hosted compatibility |
|---|---|---|
| Database | Supabase PostgreSQL/PostGIS | PostGIS container |
| Realtime | REALTIME_PROVIDER=supabase |
socketio |
| Storage | STORAGE_PROVIDER=supabase |
minio |
| Queue | QUEUE_PROVIDER=supabase-postgres |
bullmq |
Admin, Restaurant, Customer, and Driver clients obtain short-lived, tenant-scoped realtime credentials from POST /api/realtime/token in managed mode. Mobile publishes GPS and dispatch decisions through authenticated REST and receives only allow-listed private Supabase Broadcast events; Socket.IO remains an explicit local/self-hosted compatibility provider.
The table below records the matching multi-architecture Docker Hub and public GHCR manifests produced by the SHA-only workflow for evidence commit ed25399298c01975c7943ff967d4178e0ceafdfa. Both registries were queried after publication. These SHA manifests still require a clean pull/runtime smoke; they are not proof of Railway health. latest remains unchanged until the exact SHA artifacts pass provider deployment and production smoke.
| Artifact | SHA tag | Matching remote digest |
|---|---|---|
| API + worker | nguyenson1710/foodflow-backend:sha-ed25399298c01975c7943ff967d4178e0ceafdfa |
sha256:b1a24c929d7178548c407c019aa75347da78fe5c1dd135177f2b5e4024e4143b |
| Prisma migrate | nguyenson1710/foodflow-migrate:sha-ed25399298c01975c7943ff967d4178e0ceafdfa |
sha256:feb11569b66cb88cdeafbc92c3e64ca9eaed8801859f42f3600237eb55ad3bb4 |
| Admin | nguyenson1710/foodflow-admin:sha-ed25399298c01975c7943ff967d4178e0ceafdfa |
sha256:43d8908d5a77efb7142744ce76ce6355631a3b406b5e8d5e6bed884a4ac02b12 |
| Restaurant | nguyenson1710/foodflow-restaurant:sha-ed25399298c01975c7943ff967d4178e0ceafdfa |
sha256:7ba5838752a699f7dd3fb46d98110b2b37ef0c6a53f6f21aa2493c9e398da97e |
The worker runs from the backend image with dist/workers/main.js; it is not a separately maintained release artifact. All four GHCR packages are public, linked to JasonTM17/FoodDelivery_App, and grant the repository Actions write access. Docker Hub and GHCR digests match for every SHA tag. No semver or latest tag was promoted.
Release promotion order:
- Build and push
sha-<full-commit>. - Pull that SHA in a clean environment; run compose smoke and block High/Critical image findings.
- Pass Supabase/Railway/Vercel production health and authenticated tracking smoke.
- Build and scan every supported architecture, then create immutable
v4.0.0. - Promote
latestonly through an explicit manual release action.
For self-hosting, pin IMAGE_TAG=v4.0.0 or sha-<full-commit> and use the base Compose file plus docker-compose.prod.yml. Never deploy an unverified latest tag.
| Path | Purpose |
|---|---|
backend/ |
API, Prisma schema/migrations, Vercel handler, worker entry |
web/ |
pnpm workspace: Admin, Restaurant, shared packages, Playwright |
mobile/ |
Flutter customer and driver entry points plus shared API client |
infra/ |
Compose overlays, preflight, security, and release tooling |
docs/ |
Architecture, API/OpenAPI, deployment, testing, security, product media |
e2e/ |
Cross-surface and AI smoke scenarios |
Generated output, local agent files, dotenv files, credentials, and private plans are ignored and must not be committed.
- Node.js 22.13 or newer
- Corepack + pnpm 11.11.0
- Docker Desktop/Engine
- Flutter SDK matching
mobile/pubspec.yaml - Railway CLI authenticated to the project that owns the API, worker, migrator, and Redis
- Provider credentials only for the integration being exercised
Copy only example files to ignored local dotenv files. Do not put real credentials in tracked files.
# Infrastructure
docker compose up -d postgres redis minio
# API
cd backend
corepack pnpm install --frozen-lockfile
corepack pnpm prisma generate
corepack pnpm prisma migrate dev
corepack pnpm db:seed
corepack pnpm start:dev
# Admin + Restaurant
cd ../web
corepack pnpm install --frozen-lockfile
corepack pnpm dev
# Customer or Driver
cd ../mobile
flutter pub get --enforce-lockfile
flutter run --flavor customer -t lib/main_customer.dart
flutter run --flavor driver -t lib/main_driver.dartOr build the isolated full stack:
docker compose up -d --buildHealth endpoints:
- API:
GET http://localhost:3001/api/healthz - Admin:
GET http://localhost:3000/api/healthz - Restaurant:
GET http://localhost:3002/api/healthz
- Treat any key pasted into chat, logs, screenshots, tickets, or git history as exposed and rotate it before production.
- Never commit
.env, database URLs, service-role keys, JWT secrets, private keys, provider tokens, or mobile signing files. - The Supabase anon/publishable key is a public identifier but still requires RLS and origin controls. MapLibre/OpenFreeMap needs no browser API key or billing account.
- Keep DeepSeek, Supabase service role/JWT, SePay, SMTP, FCM, Twilio, database, and deployment credentials server-side.
Run preflights without printing values:
powershell -File infra/scripts/supabase-preflight.ps1
powershell -File infra/scripts/railway-preflight.ps1
powershell -File infra/scripts/vercel-web-preflight.ps1Current blockers are recorded in the Batch 4 release report. No deploy is authorized while either preflight fails.
# Complete local gate; production env/auth must be configured
powershell -File infra/scripts/local-release-gate.ps1 -RunE2EThe gate covers frozen installs, Prisma validation, backend typecheck/lint/Jest/build, web typecheck/ESLint/Vitest/build, OpenAPI Spectral, Compose config, Chromium + Firefox, Flutter analyze/test, and high-confidence secret checks. Additional release evidence includes axe serious/critical, visual regression, tenant isolation, realtime authorization, maps/routes, AI fail-closed/live smoke, and multi-architecture image scans.
The 2026-07-14 clean-volume Docker project foodflow-batch4-e2e applied all 38 migrations, seeded 201 users, 50 restaurants, 352 menu items, 509 orders, and 123 reviews, then indexed 402 RAG documents. A transaction check rejected a second default address for the same user. With explicit E2E URLs, the current rebuilt stack passed all 68 cases in Chrome desktop (173.0 s), Firefox (172.9 s), and Pixel 5 mobile Chrome (117.3 s): 204/204 total. flutter analyze remained clean and the full Customer/Driver suite passed 367 tests. Direct Chrome review also found the public Admin and Restaurant sign-in forms usable at desktop and Pixel 5 widths, with no console errors or horizontal overflow. None of this local evidence validates remote provider state, a deployed image, or live Firebase delivery.
- Rotate exposed credentials and keep all configured Railway/provider values in sealed provider stores.
- Recheck all 38 production migrations and checksums before rollout; never infer provider state from local Docker.
- Preserve the verified Railway API/worker deployments, then deploy future releases from one immutable SHA and recheck health/readiness/worker polling.
- Run authenticated Supabase private-Broadcast allow/deny, token refresh, Storage, GPS snapshot/delta, reconnect, and tenant-isolation smoke through the live API.
- Re-smoke the exact Admin and Restaurant Vercel deployments against the current Railway API, then smoke configured maps/routes, chatbot, notifications, exports, payments, and one controlled-device FCM delivery.
- Pull and smoke the four matching Docker Hub/GHCR SHA manifests from a clean environment, verify scans, and promote semver/
latestonly after provider production smoke passes.
- Customer guide — ordering, permissions, map-based address selection, checkout, tracking, and support
- Product gallery
- Customer and Driver mobile guide
- Project overview and requirements
- System architecture
- API contract and OpenAPI
- Deployment guide
- Docker/local guide
- Testing guide
- AI chatbot guide
- Security guide
- Roadmap
- Branch disposition
- Batch 4 release report
At the 2026-07-14 check, the remote advertised only origin/master; the retained linked worktree ref is already an ancestor of master, not pending work. Do not merge, recreate, push, or delete local worktree refs without explicit direction. Local branch ancestry and local tests are not production approval; see the evidence-backed branch disposition.



