Order food. Deliver parcels. Earn money. All within your campus.
🌐 Live Demo •
✨ Features •
🏗️ Architecture •
🚀 Getting Started •
📡 API Reference
Cravora is a full-stack campus food ordering and peer-to-peer delivery platform built for university ecosystems. It connects three key players in campus life:
🎓 Students
🏪 Outlet Owners
🏃 Runners
Browse menus, order food, and send parcels from anywhere on campus
List your food stall, manage menus, and track incoming orders in real-time
Pick up deliveries, earn money, and track your stats — all from the same student account
💡 Unique Design: There is no separate "Runner" registration. Every student can seamlessly switch between ordering food and delivering it with a single tap. One account, two modes.
🎓 For Students (Customer + Runner Mode)
┌─────────────────────────────────────────────────────────────────┐
│ STUDENT DASHBOARD │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 🍔 Order │ │ 📦 Send │ │ 🏃 Switch to Runner │ │
│ │ Food │ │ Parcel │ │ Mode │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
│ │
│ 📊 Order History • 💰 Delivery Stats • 👤 Profile │
└─────────────────────────────────────────────────────────────────┘
Browse Campus Outlets — View all open food stalls with menus, images, and working hours
Smart Cart — Add items, auto-calculate totals, and your cart persists across page refreshes (localStorage)
Place Orders — Choose a drop location, review your order, and submit
Track Orders — Real-time order status: Pending → Accepted → Preparing → Out for Delivery → Delivered
Send Parcels — Request courier, Blinkit, or food pickups from the campus gate to your hostel
Switch to Runner Mode — Instantly see available deliveries, accept them, and earn delivery fees
Delivery History — Track all your past deliveries and earnings
Outlet Management — Create and configure your food stall with images, location, and working hours
Menu Builder — Add/edit/remove menu items with categories (Snacks, Main Course, Beverages, Dessert)
Order Queue — View incoming orders, accept them, and update preparation status
Analytics — Track total order count and revenue
JWT Authentication — Stateless, secure, HTTP-only cookie-based sessions
Password Reset — OTP-based password recovery via email (Nodemailer + Gmail SMTP)
Role-Based Access Control — Middleware-protected routes for Students, Outlets, and Runners
Session Persistence — Stay logged in across browser sessions without re-entering credentials
┌─────────────────────────────────────────────────────────────────────────┐
│ CLIENT (Vercel) │
│ │
│ React 19 + Vite 7 ────────────────────────────────────────────── │
│ │ │
│ ├── AuthContext ─── manages user state, JWT session via cookies │
│ ├── CartContext ─── useReducer + localStorage for cart persistence │
│ ├── API Layer ──── Axios instances with withCredentials: true │
│ └── Protected Routes ── role-based guards (Student, Outlet) │
│ │
└─────────────────────────────┬───────────────────────────────────────────┘
│ HTTPS (cookies, JSON)
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ SERVER (Render) │
│ │
│ Express 5 + Node.js ───────────────────────────────────────────── │
│ │ │
│ ├── Middleware Pipeline │
│ │ ├── cors (credentials: true, origin whitelist) │
│ │ ├── cookieParser (reads JWT from req.cookies) │
│ │ ├── authMiddleware (verifies JWT, attaches req.user) │
│ │ ├── roleMiddleware (checks user role permissions) │
│ │ └── imageMiddleware (Multer + Cloudinary upload) │
│ │ │
│ ├── Controllers (7 modules) │
│ │ ├── authController ─── register, login, logout, password reset │
│ │ ├── userController ─── get current user profile │
│ │ ├── dashboardController ─── student & runner dashboard data │
│ │ ├── outletController ─── CRUD for outlets │
│ │ ├── menuController ─── CRUD for menu items within outlets │
│ │ ├── orderController ─── full order lifecycle management │
│ │ └── packageController ─── parcel delivery management │
│ │ │
│ └── Models (4 Mongoose schemas) │
│ ├── User ─── name, email, role, deliveryStats, orderHistory │
│ ├── Outlet ─── owner, menu[], orders[], location, images │
│ ├── Order ─── customer, runner, outlet, items[], status │
│ └── Package ─── customer, runner, type, status, locations │
│ │
└─────────────────────────────┬───────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ EXTERNAL SERVICES │
│ │
│ ☁️ MongoDB Atlas ─── Primary database │
│ ☁️ Cloudinary ─────── Image storage (menu/outlet) │
│ 📧 Gmail SMTP ─────── Transactional emails │
└──────────────────────────────────────────────────┘
Browser ──► Axios (withCredentials) ──► Express Server
│
┌──────┴──────┐
│ CORS Check │
│ JSON Parse │
│ Cookie Parse │
└──────┬──────┘
│
┌──────┴──────┐
│ Auth MW │──► 401 if no token
│ (JWT verify) │
└──────┬──────┘
│
┌──────┴──────┐
│ Role MW │──► 403 if wrong role
│ (permission) │
└──────┬──────┘
│
┌──────┴──────┐
│ Controller │
│ (business │──► MongoDB query
│ logic) │
└──────┬──────┘
│
JSON Response ──► Browser
Technology
Version
Purpose
React
19.2
Component-based UI library
Vite
7.3
Lightning-fast dev server and bundler
React Router DOM
7.13
Client-side routing with protected routes
Axios
1.13
HTTP client with cookie support (withCredentials)
Lucide React
0.564
Beautiful, consistent icon set
React Hot Toast
2.6
Elegant notification toasts
Technology
Version
Purpose
Node.js
22+
JavaScript runtime
Express
5.2
Web framework (latest major version with async error handling)
Mongoose
9.2
MongoDB ODM with schema validation
JSON Web Token
9.0
Stateless authentication via signed tokens
bcrypt.js
3.0
Password hashing (10 salt rounds)
cookie-parser
1.4
Parse HTTP-only cookies from requests
Multer
2.0
Multipart file upload handling
Cloudinary
1.41
Cloud-based image storage and CDN
Nodemailer
8.0
Email delivery (SMTP via Gmail)
dotenv
17.2
Environment variable management
Service
Purpose
MongoDB Atlas
Managed cloud database (M0 free tier)
Render
Backend hosting with auto-deploy from GitHub
Vercel
Frontend hosting with instant preview deployments
Cloudinary
Image CDN for menu items and outlet photos
Gmail SMTP
Transactional emails (welcome, OTP)
Entity Relationship Diagram
erDiagram
USER {
ObjectId _id PK
String name
String email UK
String phoneNumber
String password
String role "Student | Outlet"
Object deliveryStats
Array orderHistory
Date lastDeliveryAt
Number otp
Date otpExpires
}
OUTLET {
ObjectId _id PK
ObjectId owner FK
String name
String description
String location
String contactNumber
Array images
Object WorkingHours
Boolean isOpen
Array menu "embedded subdocuments"
Number orderCount
Array orders
}
ORDER {
ObjectId _id PK
ObjectId customer FK
ObjectId runner FK "nullable"
ObjectId outlet FK
Array items
String pickupLocation
String dropLocation
String status "Pending|Accepted|Preparing|OutForDelivery|Delivered|Cancelled"
Number totalAmount
Number deliveryFee
Date deliveredAt
}
PACKAGE {
ObjectId _id PK
ObjectId customer FK
ObjectId runner FK "nullable"
String type "Courier|Blinket|Food"
String description
Number quantity
String pickupLocation
String dropLocation
String status "Pending|Accepted|PickedUp|Delivered|Cancelled"
Number deliveryFee
String instructions
Date deliveredAt
}
USER ||--o{ OUTLET : "owns"
USER ||--o{ ORDER : "places as customer"
USER ||--o{ ORDER : "delivers as runner"
OUTLET ||--o{ ORDER : "receives"
USER ||--o{ PACKAGE : "creates as customer"
USER ||--o{ PACKAGE : "delivers as runner"
Loading
Menu Item (Embedded in Outlet)
{
name : String , // "Paneer Tikka"
description : String , // "Juicy grilled paneer with spices"
price : Number , // 120
category : Enum , // "Snacks" | "Main Course" | "Beverages" | "Dessert" | "Other"
image : String , // Cloudinary URL
isAvailable : Boolean // true
}
🔐 Authentication — /api/auth
Method
Endpoint
Auth
Description
POST
/register
❌
Create a new account (Student or Outlet)
POST
/login
❌
Authenticate and receive a session cookie
POST
/logout
✅
Clear the session cookie
POST
/password-reset-otp
❌
Send a 6-digit OTP to email
POST
/reset-password
❌
Verify OTP and set new password
Method
Endpoint
Auth
Description
GET
/me
✅
Get the currently logged-in user's profile
📊 Dashboard — /api/dashboard
Method
Endpoint
Auth
Description
GET
/
✅
Student dashboard: profile + all open outlets
GET
/profile
✅
Detailed profile with order/delivery stats
GET
/runner
✅
Runner dashboard: available & active deliveries
GET
/runner/stats
✅
Runner earnings, delivery count, and history
Method
Endpoint
Auth
Role
Description
POST
/addOutlet
✅
Outlet
Create a new outlet with images
GET
/me/outlet
✅
Outlet
Get the owner's outlet(s)
PUT
/updateOutlet/:id
✅
Outlet
Update outlet info
DELETE
/deleteOutlet/:id
✅
Outlet
Delete an outlet
GET
/allOutlet
✅
Any
Get all outlets (for browsing)
GET
/getOutlet/:id
✅
Any
Get a specific outlet with full menu
🍽️ Menu — /api/menu
Method
Endpoint
Auth
Role
Description
POST
/addMenu-item/:id
✅
Outlet
Add a menu item to an outlet
PUT
/:id/item/:itemId
✅
Outlet
Update a menu item
DELETE
/:id/item/:itemId
✅
Outlet
Remove a menu item
Method
Endpoint
Auth
Role
Description
POST
/
✅
Student
Place a new food order
GET
/my-orders
✅
Student
Get all my orders
GET
/outlet-orders
✅
Outlet
Get orders for my outlet
PUT
/:id/accept
✅
Outlet
Accept an incoming order
GET
/available
✅
Student
Get orders available for delivery
PUT
/:id/accept-delivery
✅
Student
Accept a delivery as a runner
PUT
/:id/status
✅
Outlet/Runner
Update order status
PUT
/:id/cancel
✅
Student
Cancel an order
GET
/:id
✅
Any
Get order details by ID
GET
/my-deliveries
✅
Student
Get my delivery history (as runner)
📦 Packages — /api/packages
Method
Endpoint
Auth
Role
Description
POST
/
✅
Student
Create a parcel delivery request
GET
/my-packages
✅
Student
Get all my packages
GET
/available
✅
Student
Get packages available for pickup
PUT
/:id/accept
✅
Student
Accept a package delivery
PUT
/:id/status
✅
Runner
Update package status (PickedUp/Delivered)
PUT
/:id/cancel
✅
Student
Cancel a package (if Pending)
GET
/:id
✅
Any
Get package details
GET
/my-deliveries
✅
Student
Get all packages I delivered
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Browser │ │ Server │ │ MongoDB │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
│ POST /api/auth/register │
│ { name, email, password, role } │
│────────────────────►│ │
│ │ Hash password │
│ │ (bcrypt, 10 rounds) │
│ │────────────────────►│
│ │ Save user │
│ │◄────────────────────│
│ │ │
│ │ Sign JWT │
│ │ (id, role, 7 days) │
│ │ │
│ Set-Cookie: token │ │
│ (httpOnly, secure) │ │
│◄────────────────────│ │
│ │ │
│ GET /api/users/me │ │
│ Cookie: token │ │
│────────────────────►│ │
│ │ Verify JWT │
│ │ Find user by ID │
│ │────────────────────►│
│ │◄────────────────────│
│ { user } │ │
│◄────────────────────│ │
│ │ │
Attribute
Development
Production
httpOnly
true
true
secure
false
true (HTTPS only)
sameSite
lax
none (cross-origin)
maxAge
7 days
7 days
Customer places order Outlet accepts Outlet prepares
│ │ │
▼ ▼ ▼
┌─────────┐ accept() ┌──────────┐ preparing() ┌────────────┐
│ PENDING │───────────────►│ ACCEPTED │────────────────►│ PREPARING │
└─────────┘ └──────────┘ └─────┬──────┘
│ │
│ cancel() Runner picks up
▼ │
┌───────────┐ ▼
│ CANCELLED │ ┌─────────────────┐
└───────────┘ │ OUT FOR DELIVERY│
└────────┬────────┘
│
Runner delivers
│
▼
┌───────────┐
│ DELIVERED │
└───────────┘
Package Delivery Status Flow
┌─────────┐ accept() ┌──────────┐ pickedUp() ┌──────────┐ delivered() ┌───────────┐
│ PENDING │────────────►│ ACCEPTED │──────────────►│ PICKED UP│──────────────►│ DELIVERED │
└─────────┘ └──────────┘ └──────────┘ └───────────┘
│
│ cancel()
▼
┌───────────┐
│ CANCELLED │
└───────────┘
Cravora/
│
├── 📂 Client/Cravora-Client/ # Frontend (React + Vite)
│ ├── 📂 src/
│ │ ├── 📂 api/ # Axios API modules
│ │ │ ├── authApi.js # Auth endpoints (register, login, logout)
│ │ │ ├── userApi.js # User profile endpoint
│ │ │ ├── dashboardApi.js # Dashboard data fetching
│ │ │ ├── outletApi.js # Outlet CRUD operations
│ │ │ ├── menuApi.js # Menu item management
│ │ │ ├── orderApi.js # Order lifecycle management
│ │ │ └── packageApi.js # Package delivery operations
│ │ │
│ │ ├── 📂 components/ # Reusable UI components
│ │ │ ├── 📂 Landing/ # Homepage sections
│ │ │ ├── 📂 Login/ # Login form component
│ │ │ ├── 📂 Register/ # Registration form component
│ │ │ ├── 📂 FormInput/ # Reusable form input wrapper
│ │ │ ├── 📂 Outlet/ # Outlet management components
│ │ │ ├── Navbar.jsx # Global navigation bar
│ │ │ ├── ProtectedRoute.jsx # Role-based route guard
│ │ │ ├── OutletCard.jsx # Outlet preview card
│ │ │ ├── MenuItemCard.jsx # Menu item display card
│ │ │ ├── OrderTracker.jsx # Real-time order status tracker
│ │ │ └── ViewCheckoutButton.jsx # Floating cart button
│ │ │
│ │ ├── 📂 context/ # React Context providers
│ │ │ ├── AuthContext.jsx # Auth state (user, loading, session check)
│ │ │ └── CartContext.jsx # Cart state (useReducer + localStorage)
│ │ │
│ │ ├── 📂 hooks/ # Custom React hooks
│ │ │ └── useAuth.js # Shortcut to consume AuthContext
│ │ │
│ │ ├── 📂 pages/ # Route-level page components
│ │ │ ├── Home.jsx # Landing page
│ │ │ ├── Login.jsx # Login page wrapper
│ │ │ ├── Register.jsx # Registration page wrapper
│ │ │ ├── ForgotPassword.jsx # OTP-based password reset
│ │ │ ├── Dashboard.jsx # Student dashboard (browse outlets)
│ │ │ ├── OutletDetail.jsx # Single outlet view with menu
│ │ │ ├── Checkout.jsx # Cart review and order placement
│ │ │ ├── RunnerDashboard.jsx # Available deliveries + active jobs
│ │ │ ├── OrderParcel.jsx # Create parcel delivery request
│ │ │ ├── DeliveryHistory.jsx # Past deliveries and earnings
│ │ │ └── 📂 outlet/ # Outlet owner pages
│ │ │ ├── MyOutlet.jsx # Outlet management dashboard
│ │ │ ├── ManageMenu.jsx # Add/edit/delete menu items
│ │ │ └── OutletOrders.jsx # Incoming order queue
│ │ │
│ │ ├── 📂 utils/ # Utility functions
│ │ │ └── API_paths.js # Centralized API route constants
│ │ │
│ │ ├── App.jsx # Route definitions
│ │ ├── App.css # Global styles
│ │ ├── index.css # CSS reset and variables
│ │ └── main.jsx # App entry point with providers
│ │
│ ├── package.json
│ └── vite.config.js
│
├── 📂 Server/ # Backend (Express + Node.js)
│ ├── 📂 src/
│ │ ├── 📂 Config/ # Configuration modules
│ │ │ ├── Connection_DB.js # MongoDB Atlas connection (with timeout)
│ │ │ ├── Cloudinary.js # Cloudinary image service config
│ │ │ └── Transporter.js # Nodemailer Gmail SMTP config
│ │ │
│ │ ├── 📂 Controllers/ # Business logic (7 controllers)
│ │ │ ├── authController.js # Register, Login, Logout, OTP, Reset
│ │ │ ├── userController.js # Get current user
│ │ │ ├── dashboardController.js # Student + Runner dashboard data
│ │ │ ├── outletController.js # Outlet CRUD with image upload
│ │ │ ├── menuController.js # Menu item CRUD (embedded in Outlet)
│ │ │ ├── orderController.js # Full order lifecycle + stats tracking
│ │ │ └── packageController.js # Package delivery lifecycle
│ │ │
│ │ ├── 📂 Middlewares/ # Express middleware
│ │ │ ├── authMiddleware.js # JWT verification + req.user injection
│ │ │ ├── roleMiddleware.js # Role-based access control
│ │ │ └── imageMiddleware.js # Multer + Cloudinary storage engine
│ │ │
│ │ ├── 📂 Models/ # Mongoose schemas (4 models)
│ │ │ ├── User.js # User schema + bcrypt helpers
│ │ │ ├── Outlet.js # Outlet schema with embedded menu
│ │ │ ├── Order.js # Food order schema
│ │ │ └── Package.js # Parcel delivery schema
│ │ │
│ │ ├── 📂 Routes/ # Express route definitions (7 routers)
│ │ │ ├── authRoutes.js # /api/auth/*
│ │ │ ├── userRoutes.js # /api/users/*
│ │ │ ├── dashboardRoutes.js # /api/dashboard/*
│ │ │ ├── outletRoutes.js # /api/outlets/*
│ │ │ ├── menuRoutes.js # /api/menu/*
│ │ │ ├── orderRoutes.js # /api/orders/*
│ │ │ └── packageRoutes.js # /api/packages/*
│ │ │
│ │ ├── 📂 utils/ # Utility functions
│ │ │ └── sendEmail.js # Reusable email sender
│ │ │
│ │ └── server.js # Express app entry point
│ │
│ ├── .env # Environment variables (gitignored)
│ └── package.json
│
└── README.md # ← You are here
git clone https://github.com/Ronit178693/Cravora.git
cd Cravora
Create a .env file in the Server/ directory:
PORT = 5000
JWT_SECRET = your_super_secret_jwt_key_here
NODE_ENV = development
MONGODB_URI = mongodb+srv://<user>:<pass>@cluster0.xxxxx.mongodb.net/cravora_db?retryWrites=true&w=majority
EMAIL_USER = your_email@gmail.com
EMAIL_PASSWORD = your_gmail_app_password
CLOUDINARY_URL = cloudinary://API_KEY:API_SECRET@CLOUD_NAME
API_KEY = your_cloudinary_api_key
API_SECRET = your_cloudinary_api_secret
CLOUD_NAME = your_cloudinary_cloud_name
CLIENT_URL = http://localhost:5173
⚠️ Important: Use NODE_ENV=development locally and NODE_ENV=production on Render. Do not add spaces around = signs.
Start the development server:
cd ../Client/Cravora-Client
npm install
npm run dev
The app will be running at http://localhost:5173 🎉
Variable
Required
Description
PORT
✅
Server port (default: 5000)
JWT_SECRET
✅
Secret key for signing JWT tokens
NODE_ENV
✅
development or production
MONGODB_URI
✅
MongoDB Atlas connection string
EMAIL_USER
✅
Gmail address for sending emails
EMAIL_PASSWORD
✅
Gmail App Password (16-character code)
CLOUDINARY_URL
✅
Full Cloudinary connection URL
API_KEY
✅
Cloudinary API key
API_SECRET
✅
Cloudinary API secret
CLOUD_NAME
✅
Cloudinary cloud name
CLIENT_URL
✅
Frontend URL (no trailing slash)
Create a Web Service on Render
Connect your GitHub repository
Configure:
Root Directory: Server
Build Command: npm install
Start Command: npm start
Add all environment variables from the table above
Set NODE_ENV=production and CLIENT_URL=https://your-frontend.vercel.app
Import your repository on Vercel
Configure:
Root Directory: Client/Cravora-Client
Build Command: npm run build
Output Directory: dist
Deploy — Vercel auto-detects Vite and handles the rest
Contributions are welcome! Here's how you can help:
Fork the repository
Create a feature branch (git checkout -b feature/amazing-feature)
Commit your changes (git commit -m 'Add amazing feature')
Push to the branch (git push origin feature/amazing-feature)
Open a Pull Request
If you found this project useful, consider giving it a ⭐