A supply chain platform connecting Canada's northern territories β linking Hunters/Harvestors, Communities/Schools, and Mass Suppliers through a modern mobile app with real-time chat.
- Frontend: React Native (Expo), React Navigation, Axios, Socket.io-client
- Backend: Node.js, Express.js, Socket.io, Mongoose (MongoDB)
- Auth: JWT + bcrypt + Expo SecureStore
- File Storage: Cloudinary (via multer memory storage)
- Database: MongoDB Atlas
- Go to https://cloud.mongodb.com and create a free account
- Click "Build a Database" β choose Free Shared tier
- Select a cloud provider & region (choose one closest to Canada for best performance)
- Create a Database User with a username and password (save these!)
- Under Network Access, click "Add IP Address" β "Allow Access from Anywhere" (for dev)
- Click "Connect" β "Connect your application"
- Copy the connection string β it looks like:
mongodb+srv://yourUsername:yourPassword@cluster0.xxxxx.mongodb.net/northern-harvest?retryWrites=true&w=majority - Replace
<password>with your actual database user password
- Go to https://cloudinary.com and create a free account
- After logging in, go to your Dashboard
- You'll see three values you need:
- Cloud Name (e.g.,
dxxxxxxxx) - API Key (e.g.,
123456789012345) - API Secret (e.g.,
abcdefg-hijklmn_opqrst)
- Cloud Name (e.g.,
- Copy all three values
cd backend
cp .env.example .envEdit backend/.env and fill in your values:
MONGO_URI=mongodb+srv://yourUser:yourPass@cluster0.xxxxx.mongodb.net/northern-harvest?retryWrites=true&w=majority
JWT_SECRET=generate_a_long_random_string_here
JWT_EXPIRE=30d
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
PORT=5001
FRONTEND_URL=http://localhost:8081π‘ The
backend/.envfile is included in the repo for convenience. If you're setting up your own instance, update the values above.
π‘ Generate a JWT secret:
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
cd backend
npm install
npm start # or: npm run dev (with nodemon for auto-restart)You should see:
πΎ Northern Harvest server running on port 5001
MongoDB Connected: cluster0-shard-00-00.xxxxx.mongodb.net
Edit frontend/services/api.js and frontend/services/socket.js:
- If using Expo Go on the same machine: use
http://localhost:5001 - If using Expo Go on a physical phone: use your computer's local IP or ngrok:
(Find your IP with
http://192.168.x.x:5001ifconfig | grep "inet "on macOS)
cd frontend
npx expo startScan the QR code with Expo Go app on your phone, or press w for web.
The app is fully responsive and works on desktop browsers, tablets, and mobile devices:
- π± Mobile (< 768px): Bottom tab navigation, optimized spacing and fonts
- π± Tablet (768px - 1024px): Similar to mobile with slightly larger text
- π₯οΈ Desktop (> 1024px): Sidebar navigation with professional layout, centered content with max-width
# Start frontend in web mode
npm start
# or with Expo
npx expo start
# then press 'w' to open in browserOpen your browser at http://localhost:8081 and resize to see the responsive layout in action.
The app uses a custom useResponsive hook that provides:
- Automatic screen size detection (mobile, tablet, desktop)
- Responsive spacing values
- Responsive font sizes
- Conditional rendering of navigation (sidebar vs bottom tabs)
- Content max-width on desktop for readability
To make other screens responsive like HunterDashboard:
-
Import the utilities:
import { useResponsive } from '../hooks/useResponsive'; import ResponsiveContainer from '../components/ResponsiveContainer';
-
Use in your screen:
const YourScreen = ({ navigation }) => { const { isDesktop, spacing, fontSize } = useResponsive(); return ( <ResponsiveContainer> <Text style={{ fontSize: fontSize.lg, marginVertical: spacing.md }}> This text scales on desktop! </Text> </ResponsiveContainer> ); };
hack-canada/
βββ backend/
β βββ server.js β Express + Socket.io entry point
β βββ .env β Environment secrets (included for contributors)
β βββ .env.example β Template for .env
β βββ config/
β β βββ db.js β MongoDB connection
β β βββ cloudinary.js β Cloudinary + Multer config
β βββ models/
β β βββ User.js β User schema (3 types + OTP fields)
β β βββ Listing.js β Harvest/supply posts
β β βββ Order.js β Order lifecycle
β β βββ Message.js β Chat messages
β βββ middleware/
β β βββ auth.js β JWT auth + role restriction
β βββ routes/
β βββ auth.js β Register, Login, OTP verify/resend, Profile
β βββ listings.js β CRUD listings
β βββ orders.js β Order management
β βββ chat.js β Chat history
β βββ users.js β Dashboard, nearby, profiles
β
βββ frontend/
βββ App.js β Navigation root (responsive: sidebar + tabs)
βββ context/
β βββ AuthContext.js β Auth state + JWT persistence + OTP flow
βββ hooks/
β βββ useResponsive.js β Responsive design utilities (screen size detection)
βββ services/
β βββ api.js β Axios + endpoint helpers
β βββ socket.js β Socket.io singleton
βββ screens/
β βββ LoginScreen.js β Phone + password login
β βββ RegisterScreen.js β Multi-step registration
β βββ OTPScreen.js β 6-digit OTP verification
β βββ HunterDashboard.js β Example responsive screen
β βββ CommunityDashboard.js
β βββ SupplierDashboard.js
β βββ ListingsScreen.js
β βββ ListingDetailScreen.js
β βββ CreateListingScreen.js
β βββ OrdersScreen.js
β βββ OrderDetailScreen.js
β βββ ChatListScreen.js
β βββ ChatRoomScreen.js
β βββ ProfileScreen.js
βββ components/
βββ ListingCard.js
βββ OrderCard.js
βββ StatusBadge.js
βββ ChatBubble.js
βββ CategoryPicker.js
βββ DesktopSidebar.js β Desktop navigation sidebar
βββ ResponsiveContainer.js β Responsive layout wrapper
| Type | Can Do | Dashboard Shows |
|---|---|---|
| Hunter/Harvestor | Post harvests, manage orders, chat with buyers | Active listings, pending/delivered orders |
| Community/School | Browse food, place orders, express interest, chat | Available food count, order tracking |
| Mass Supplier | Post bulk supplies, manage orders, chat with buyers | Supply inventory, order fulfillment stats |
| Method | Path | Access | Description |
|---|---|---|---|
| POST | /api/auth/register |
Public | Register + send OTP |
| POST | /api/auth/login |
Public | Login with phone + password, sends OTP |
| POST | /api/auth/verify-otp |
Public | Verify 6-digit OTP code |
| POST | /api/auth/resend-otp |
Public | Resend OTP to phone |
| GET | /api/auth/me |
Protected | Auto-login check |
| PUT | /api/auth/me |
Protected | Update profile |
| PUT | /api/auth/upload-document |
Protected | Upload verification doc |
| GET | /api/listings |
Protected | Browse listings |
| GET | /api/listings/mine |
Hunter/Supplier | My listings |
| POST | /api/listings |
Hunter/Supplier | Create listing |
| POST | /api/listings/:id/interest |
Community | Express interest |
| POST | /api/orders |
Community | Place order |
| GET | /api/orders/mine |
Protected | My orders |
| PUT | /api/orders/:id/status |
Hunter/Supplier | Update order status |
| GET | /api/chat |
Protected | List chat rooms |
| GET | /api/chat/:roomId |
Member only | Message history |
| GET | /api/users/dashboard |
Protected | Dashboard stats |
| GET | /api/users/nearby |
Community | Find nearby suppliers |
| Variable | Where to Get It |
|---|---|
MONGO_URI |
MongoDB Atlas β Connect β Connection String |
JWT_SECRET |
Generate: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))" |
CLOUDINARY_CLOUD_NAME |
Cloudinary dashboard |
CLOUDINARY_API_KEY |
Cloudinary Dashboard |
CLOUDINARY_API_SECRET |
Cloudinary Dashboard |
Built for Hack Canada π¨π¦
The easiest way to run the project β one command starts everything.
- Docker Desktop installed and running
# 1. Clone the repo
git clone https://github.com/generalgummy/hack-canada.git
cd hack-canada
# 2. Start the backend + ngrok tunnel
docker compose up --build# build & start only backend service
docker compose up --build backend
# or in background
docker compose up -d --build backend
# follow backend logs
docker compose logs -f backendNote: the frontend service runs the Expo dev server inside the container. When the frontend runs in Docker it cannot reach
http://localhost:5001on the host β inside Compose the backend is reachable athttp://backend:5001.
- Start both services (frontend is behind a profile
with-frontend):
docker compose --profile with-frontend up --build
# or detached
docker compose --profile with-frontend up -d --build- If you run the frontend inside Docker you should update the API base URL used by the app (temporary change for containerized frontend):
Edit frontend/services/api.js and set:
// when running inside docker-compose
const API_URL = "http://backend:5001/api";-
Open the Expo Metro URL printed by the frontend container (it will show
exp://<container-ip>:8081) or open web athttp://localhost:8081on the host. -
To let a physical phone reach the backend when frontend runs in Docker, use the included
ngrokservice (visithttp://localhost:4040to copy the public tunnel URL and set that asAPI_URLinfrontend/services/api.js).
- If the backend container becomes
unhealthycheck Mongo connection and.env(MONGO_URI) and then:
docker compose logs -f backend
# or inspect container status
docker ps --filter name=northern-harvest-backend- To see OTP codes (server prints them), follow backend logs:
docker compose logs -f backend
That's it! The `.env` file is already included in the repo with working credentials.
### What Gets Started
| Service | URL | Description |
| -------- | ----------------------- | ---------------------------- |
| Backend | `http://localhost:5001` | Express API server |
| ngrok UI | `http://localhost:4040` | Shows your public tunnel URL |
### Connecting the Frontend (Mobile App)
1. After `docker compose up`, open **http://localhost:4040** in your browser
2. Copy the **public URL** shown (e.g. `https://xxxx-xxx-xxx.ngrok-free.app`)
3. Edit `frontend/services/api.js`:
```js
const API_URL = "https://xxxx-xxx-xxx.ngrok-free.app/api";
- Edit
frontend/services/socket.js:const SOCKET_URL = "https://xxxx-xxx-xxx.ngrok-free.app";
- Run the frontend locally:
cd frontend npm install npx expo start --tunnel - Scan the QR code with Expo Go on your phone
Free ngrok gives a random URL every restart. To get a fixed URL:
- Create a free account at ngrok.com
- Go to Domains β claim a free static domain
- Set your auth token before starting:
export NGROK_AUTHTOKEN=your_token_here docker compose up --build
# Start everything
docker compose up --build
# Start in background
docker compose up --build -d
# View backend logs (to see OTP codes)
docker compose logs -f backend
# Stop everything
docker compose down
# Full rebuild (after code changes)
docker compose down && docker compose up --build| Problem | Solution |
|---|---|
| Port 5001 already in use | lsof -ti:5001 | xargs kill -9 then retry |
| ngrok "too many sessions" | Stop any local ngrok: pkill -f ngrok |
| Can't connect from phone | Check URL at http://localhost:4040, update api.js |
| OTP not showing | Run docker compose logs -f backend to see OTP in logs |
- Made for Hack Canada by Tarun philip, Isla Mukheef, Lukman β€οΈ