A full-stack ecosystem for food service management — storefront, POS, kitchen display, and admin dashboard.
A comprehensive full-stack application for food service businesses: a Customer Storefront, a Point of Sale (POS) system, a Kitchen Display System (KDS), and a full Admin Dashboard.
- Key Features
- Architecture & Tech Stack
- Project Structure
- Getting Started (Docker)
- Local Development
- Environment Variables
- Production Deployment
- Online Payments (Razorpay)
- Testing
- Security Notes
- License
- Dynamic menus with rich product detail pages
- Guest checkout, secure cart management, and online payments via Razorpay
- Digital wallet, loyalty points, and a coupon catalog
- Real-time order status tracking from kitchen to delivery
- Touch-friendly order entry with QR code generation for walk-ins
- Staff clock-in/out, shift management, and PIN-secured POS lock screens
- Kitchen Display System (KDS) with real-time order sync and ticket management
- Multi-outlet stock depletion and raw material batch tracking
- JWT auth with token versioning (instant global revocation on password change) and Redis-backed blocklisting
- Rate limiting on sensitive endpoints (login, OTP) backed by Redis
- ORM-parameterized queries, input sanitization, and HTML escaping against SQLi/XSS
- MIME-validated file uploads via
python-magic - Fernet-encrypted payment credentials, HMAC-verified payment webhooks
| Component | Technology |
|---|---|
| Backend API | Python, Flask, SQLAlchemy, Alembic, Flask-JWT-Extended, APScheduler |
| Frontend (Customer) | React 19 (Vite), Tailwind CSS |
| Frontend (Admin) | React 19 (Vite), Tailwind CSS, Recharts |
| Database & Cache | MySQL 8.0, Redis |
| Infrastructure | Docker, Docker Compose, Gunicorn |
.
├── backend/ # Flask API (app.py, models.py, migrations, tests)
├── frontend-customer/ # Customer storefront (React + Vite)
├── frontend-admin/ # Admin dashboard + POS + KDS (React + Vite)
├── docker-compose.yml # Full stack: backend, MySQL, Redis, both frontends
├── .env.example # Environment variable template
└── BACKEND_AUTH.md # Auth flow reference
Note: one-off maintenance scripts (
fix_*.py,migrate_mysql.py,refactor*.py,remove_whatsapp.py,test_create_staff.py) live at the repo root for historical reference. They are not part of the running application and should not be deployed — see Security Notes.
The fastest way to run the full stack (backend, both frontends, MySQL, Redis):
git clone <your-repository-url>
cd food
cp .env.example .env
# Edit .env with real secrets before starting
docker-compose up --build -d| Service | URL |
|---|---|
| Customer Storefront | http://localhost:3000 |
| Admin Dashboard | http://localhost:3001 |
| Backend API | http://localhost:5000 |
Backend
cd backend
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
# Redis is required even in dev (rate limiting, token blocklist)
docker run --name my-redis -p 6379:6379 -d redis:alpine
# Defaults to local SQLite unless MYSQL_* / DATABASE_URL is set
flask db upgrade
flask runFrontends (separate terminals)
cd frontend-admin && npm install && npm run dev
cd frontend-customer && npm install && npm run devCopy .env.example to .env and fill in real values. Key variables:
| Variable | Required | Purpose |
|---|---|---|
FLASK_ENV |
Yes | production enforces strict startup checks (fails closed if secrets are missing) |
SECRET_KEY / JWT_SECRET_KEY |
Yes | Cryptographically random strings — generate fresh per deployment, never reuse dev values |
PAYMENT_ENCRYPTION_KEY |
Yes (if using payments) | Fernet key encrypting stored payment credentials |
DATABASE_URL or MYSQL_* |
Yes | Production database connection |
REDIS_URL |
Yes | Token blocklist + rate limiting store |
FRONTEND_URL / CORS_ORIGINS |
Yes | Comma-separated list of allowed frontend origins |
VITE_API_URL |
Yes (frontend build-time) | Backend URL baked into the frontend build — must be set before npm run build |
MAIL_*, ADMIN_EMAIL |
Recommended | Transactional email (password resets, order notifications) |
.env file. If one has ever been shared (e.g. zipped and sent elsewhere), rotate every secret in it immediately.
This project targets a split deployment: Flask backend on a VM (e.g. Oracle Cloud), both frontends on a static host (e.g. Netlify).
- Provision an instance (e.g.
VM.Standard.A1.Flex), open port 443 only (plus restricted SSH). - Run the backend behind Gunicorn, reverse-proxied through Nginx or Caddy for TLS termination — never expose Flask's dev server directly.
- Set all required env vars (
FLASK_ENV=productionand everything in the table above). The app refuses to start in production mode ifSECRET_KEY,JWT_SECRET_KEY,REDIS_URL, orDATABASE_URLare missing. - Point
CORS_ORIGINS/FRONTEND_URLat your actual Netlify domains. - Schedule regular database backups to object storage.
- Deploy
frontend-customerandfrontend-adminas two separate Netlify sites. - Set
VITE_API_URLas a Netlify build environment variable pointing to your backend's public HTTPS URL — Vite bakes this in at build time, so a local.envvalue won't carry over. - Use distinct subdomains (e.g.
app.yourdomain.com,admin.yourdomain.com).
- Resource limits (
cpus,memory) prevent runaway processes - Healthchecks on MySQL, Redis, and backend ensure safe startup ordering
- Log rotation (10MB × 3 files) via the
json-filedriver - Cross-process file locking so APScheduler jobs (daily reports, ticket cleanup) run exactly once across Gunicorn workers
Credentials are stored encrypted (Fernet, via PAYMENT_ENCRYPTION_KEY) in StoreSetting and managed from Admin → Payment Gateway.
| Endpoint | Auth | Purpose |
|---|---|---|
POST /api/payments/razorpay/order |
JWT | Creates a Razorpay order, returns keys for checkout |
POST /api/payments/razorpay/verify |
JWT | Verifies HMAC signature, marks order paid (idempotent) |
POST /api/payments/razorpay/webhook |
Signature | Server-to-server fallback, marks orders paid even if the browser closes |
Every payment event is logged to payment_transactions for reconciliation.
cd backend
REDIS_URL=memory:// python -m unittest discover tests/ -vREDIS_URL=memory:// avoids polluting the real Redis cache during test runs.
Add your license here (e.g. MIT, proprietary/all rights reserved) before distributing this project.