[OWL] Critical fixes: registration routes, duplicate payments mount, missing views
Critical Fixes: - Registration routes: changed /shipper → /register/shipper, /driver → /register/driver (was causing 404s — landing page linked to /register/* but routes were /shipper and /driver) - Registration form actions: fixed to match /register/shipper and /register/driver - Removed duplicate /payments route mount in server.js (payments.js is escrow, mounted at /escrow) - Supabase client: now uses service key (falls back to anon key) - Created missing pages/errors/403.ejs view Documentation: - README.md: full project documentation - .env.example: environment variable template - .dockerignore: exclude dev files from Docker image
This commit is contained in:
parent
9b5e568e72
commit
ec6ec234ac
7 changed files with 215 additions and 11 deletions
161
README.md
Normal file
161
README.md
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
# 🚛 FreightDesk
|
||||||
|
|
||||||
|
**India's Freight Marketplace Platform** — Connect shippers with truck drivers, manage loads, track payments through escrow, and grow your freight business.
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### For Everyone
|
||||||
|
- 🌐 Public landing page with registration
|
||||||
|
- 🔐 Secure authentication (admin + portal users)
|
||||||
|
- 📱 Mobile-responsive design
|
||||||
|
- 🌙 Dark mode
|
||||||
|
|
||||||
|
### Admin Panel
|
||||||
|
- 📊 Dashboard with Recharts visualizations
|
||||||
|
- 🚛 Load management (CRUD + WhatsApp parser)
|
||||||
|
- 🏢 Shipper & vehicle management
|
||||||
|
- 💳 Payment tracking & commission calculator
|
||||||
|
- 📋 Reports & audit logs
|
||||||
|
- 🧾 Invoice PDF generation
|
||||||
|
- 🛡️ User moderation (verify, dispute resolution)
|
||||||
|
- 📈 Prometheus metrics + Pino logging
|
||||||
|
|
||||||
|
### Shipper Portal
|
||||||
|
- 📦 Post loads to marketplace
|
||||||
|
- 💰 Deposit funds to escrow
|
||||||
|
- 📊 View bids, accept/counter-offer
|
||||||
|
- 💸 Release payment after delivery
|
||||||
|
- 📝 Rate drivers
|
||||||
|
- 🔔 Real-time notifications
|
||||||
|
|
||||||
|
### Driver Portal
|
||||||
|
- 🔍 Browse & filter available loads
|
||||||
|
- 💵 Place bids on loads
|
||||||
|
- 📍 GPS location tracking
|
||||||
|
- 📊 Earnings dashboard
|
||||||
|
- 💸 Request payouts (UPI/Bank)
|
||||||
|
- ⭐ View ratings & reviews
|
||||||
|
|
||||||
|
### Marketplace
|
||||||
|
- 🏪 Browse loads with filters (city, type, budget)
|
||||||
|
- 💲 Bidding system with negotiation
|
||||||
|
- 🔔 Real-time notifications
|
||||||
|
- 📊 Bid comparison with driver profiles
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
| Layer | Technology |
|
||||||
|
|-------|-----------|
|
||||||
|
| Backend | Node.js + Express |
|
||||||
|
| Frontend | EJS server-rendered + React (Recharts via CDN) |
|
||||||
|
| Database | Supabase PostgreSQL |
|
||||||
|
| Auth | bcrypt + express-session |
|
||||||
|
| Security | Helmet + CSRF + rate limiting |
|
||||||
|
| Logging | Pino structured logging |
|
||||||
|
| Metrics | Prometheus |
|
||||||
|
| Testing | Jest + Supertest |
|
||||||
|
| Deployment | Docker + Coolify + Forgejo CI/CD |
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Node.js 20+
|
||||||
|
- Supabase project (self-hosted or cloud)
|
||||||
|
|
||||||
|
### 1. Clone & Install
|
||||||
|
```bash
|
||||||
|
git clone http://forgejo-vil3xyowqk0qsh4hiqy77e3h.187.127.178.110.sslip.io/iamcoolvivek007/freightdesk.git
|
||||||
|
cd freightdesk/webapp
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Configure
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
# Edit .env with your Supabase credentials
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Run Migrations
|
||||||
|
Run `supabase/migrations/001_initial_schema.sql` through `007_location_tracking.sql` in your Supabase SQL editor.
|
||||||
|
|
||||||
|
### 4. Create Admin Account
|
||||||
|
Visit `http://localhost:3000/setup`
|
||||||
|
|
||||||
|
### 5. Seed Demo Data (optional)
|
||||||
|
```bash
|
||||||
|
node scripts/seed-demo.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Start
|
||||||
|
```bash
|
||||||
|
npm run dev # Development with nodemon
|
||||||
|
npm start # Production
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd webapp
|
||||||
|
docker build -t freightdesk .
|
||||||
|
docker run -p 3000:3000 --env-file .env freightdesk
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
freightdesk/
|
||||||
|
├── .github/workflows/deploy.yml # CI/CD pipeline
|
||||||
|
├── DEPLOYMENT.md # Full deployment guide
|
||||||
|
├── supabase/migrations/ # 7 migrations (001-007)
|
||||||
|
└── webapp/
|
||||||
|
├── Dockerfile
|
||||||
|
├── package.json
|
||||||
|
├── scripts/seed-demo.js # Demo data seeder
|
||||||
|
└── src/
|
||||||
|
├── server.js # Express app entry
|
||||||
|
├── config/env.js # Environment config
|
||||||
|
├── middleware/ # CSRF, auth, security
|
||||||
|
├── routes/ # 14 route files
|
||||||
|
│ ├── dashboard.js
|
||||||
|
│ ├── loads.js
|
||||||
|
│ ├── payments.js # Escrow payments
|
||||||
|
│ ├── marketplace.js # Bidding system
|
||||||
|
│ ├── admin-moderation.js
|
||||||
|
│ └── ...
|
||||||
|
├── services/ # Business logic
|
||||||
|
│ ├── supabase.js
|
||||||
|
│ ├── parser.js # WhatsApp parser
|
||||||
|
│ ├── invoice-pdf.js
|
||||||
|
│ ├── logger.js
|
||||||
|
│ └── metrics.js
|
||||||
|
├── views/pages/ # EJS templates
|
||||||
|
│ ├── public/ # Landing, register
|
||||||
|
│ ├── marketplace/ # Browse, post, bid
|
||||||
|
│ ├── portal/ # Shipper/driver portal
|
||||||
|
│ ├── payments/ # Deposit, payout
|
||||||
|
│ └── admin/ # Moderation
|
||||||
|
├── public/ # Static assets
|
||||||
|
│ └── css/style.css # Govt-app aesthetic
|
||||||
|
└── lib/ # India locale helpers
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Schema
|
||||||
|
|
||||||
|
7 migrations totaling ~20 tables:
|
||||||
|
|
||||||
|
| Migration | Tables Added |
|
||||||
|
|-----------|-------------|
|
||||||
|
| 001 | loads, shippers, vehicles, payments, portal_users |
|
||||||
|
| 002 | parser config, city list |
|
||||||
|
| 003 | soft-delete columns |
|
||||||
|
| 004 | audit_logs |
|
||||||
|
| 005 | bids, negotiations, ratings, notifications, load_views |
|
||||||
|
| 006 | escrow_accounts, escrow_transactions, payout_requests, disputes, platform_config |
|
||||||
|
| 007 | vehicle_locations, GPS columns on vehicles |
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
11
webapp/.dockerignore
Normal file
11
webapp/.dockerignore
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
.env
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
README.md
|
||||||
|
DEPLOYMENT.md
|
||||||
|
.DS_Store
|
||||||
|
*.md
|
||||||
|
docker-compose*.yml
|
||||||
|
.github
|
||||||
|
|
@ -1,9 +1,25 @@
|
||||||
|
# FreightDesk — Environment Variables
|
||||||
|
# Copy to .env and fill in values
|
||||||
|
|
||||||
|
# Server
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
PORT=3000
|
PORT=3000
|
||||||
APP_URL=http://localhost:3000
|
APP_URL=http://localhost:3000
|
||||||
|
|
||||||
SUPABASE_URL=https://your-project.supabase.co
|
# Session secret (generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")
|
||||||
SUPABASE_KEY=your-anon-key
|
|
||||||
SUPABASE_SERVICE_KEY=your-service-role-key
|
|
||||||
|
|
||||||
SESSION_SECRET=change-this-to-a-random-string-in-production
|
SESSION_SECRET=change-this-to-a-random-string-in-production
|
||||||
|
|
||||||
|
# Supabase
|
||||||
|
SUPABASE_URL=https://your-project.supabase.co
|
||||||
|
SUPABASE_SERVICE_KEY=your-service-role-key
|
||||||
|
SUPABASE_KEY=your-anon-key
|
||||||
|
|
||||||
|
# Payment Gateway (production — Razorpay)
|
||||||
|
RAZORPAY_KEY_ID=
|
||||||
|
RAZORPAY_KEY_SECRET=
|
||||||
|
|
||||||
|
# Email (optional)
|
||||||
|
SMTP_HOST=
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_USER=
|
||||||
|
SMTP_PASS=
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ const { asyncHandler } = require('../middleware/security');
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// GET /register/shipper
|
// GET /register/shipper
|
||||||
router.get('/shipper', (req, res) => {
|
router.get('/register/shipper', (req, res) => {
|
||||||
if (req.session.portalUser) {
|
if (req.session.portalUser) {
|
||||||
return res.redirect('/portal/dashboard');
|
return res.redirect('/portal/dashboard');
|
||||||
}
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ router.get('/shipper', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// POST /register/shipper
|
// POST /register/shipper
|
||||||
router.post('/shipper', asyncHandler(async (req, res) => {
|
router.post('/register/shipper', asyncHandler(async (req, res) => {
|
||||||
const { name, email, phone, password, confirm_password, company_name, gst_number, city, state, pincode } = req.body;
|
const { name, email, phone, password, confirm_password, company_name, gst_number, city, state, pincode } = req.body;
|
||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
|
|
@ -104,7 +104,7 @@ router.post('/shipper', asyncHandler(async (req, res) => {
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
|
|
||||||
// GET /register/driver
|
// GET /register/driver
|
||||||
router.get('/driver', (req, res) => {
|
router.get('/register/driver', (req, res) => {
|
||||||
if (req.session.portalUser) {
|
if (req.session.portalUser) {
|
||||||
return res.redirect('/portal/dashboard');
|
return res.redirect('/portal/dashboard');
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +112,7 @@ router.get('/driver', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// POST /register/driver
|
// POST /register/driver
|
||||||
router.post('/driver', asyncHandler(async (req, res) => {
|
router.post('/register/driver', asyncHandler(async (req, res) => {
|
||||||
const { name, email, phone, password, confirm_password, vehicle_number, vehicle_type, capacity_tons, driver_license, current_city } = req.body;
|
const { name, email, phone, password, confirm_password, vehicle_number, vehicle_type, capacity_tons, driver_license, current_city } = req.body;
|
||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,6 @@ app.use('/setup', require('./routes/setup'));
|
||||||
app.use('/loads', require('./routes/loads'));
|
app.use('/loads', require('./routes/loads'));
|
||||||
app.use('/shippers', require('./routes/shippers'));
|
app.use('/shippers', require('./routes/shippers'));
|
||||||
app.use('/vehicles', require('./routes/vehicles'));
|
app.use('/vehicles', require('./routes/vehicles'));
|
||||||
app.use('/payments', require('./routes/payments'));
|
|
||||||
app.use('/reports', require('./routes/reports'));
|
app.use('/reports', require('./routes/reports'));
|
||||||
app.use('/audit-logs', require('./routes/audit'));
|
app.use('/audit-logs', require('./routes/audit'));
|
||||||
app.use('/portal', require('./routes/portal'));
|
app.use('/portal', require('./routes/portal'));
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ const { createClient } = require('@supabase/supabase-js');
|
||||||
const config = require('../config/env');
|
const config = require('../config/env');
|
||||||
|
|
||||||
const supabaseUrl = config.supabase.url;
|
const supabaseUrl = config.supabase.url;
|
||||||
const supabaseKey = config.supabase.key;
|
const supabaseKey = config.supabase.serviceKey || config.supabase.key;
|
||||||
|
|
||||||
if (!supabaseUrl || !supabaseKey) {
|
if (!supabaseUrl || !supabaseKey) {
|
||||||
console.error('Missing SUPABASE_URL or SUPABASE_KEY. Check .env file.');
|
console.error('Missing SUPABASE_URL or SUPABASE_SERVICE_KEY. Check .env file.');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
17
webapp/src/views/pages/errors/403.ejs
Normal file
17
webapp/src/views/pages/errors/403.ejs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Access Denied — FreightDesk</title>
|
||||||
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body style="display:flex;align-items:center;justify-content:center;min-height:100vh;background:#f8f9fa;">
|
||||||
|
<div style="text-align:center;padding:48px;">
|
||||||
|
<div style="font-size:72px;margin-bottom:16px;">🔒</div>
|
||||||
|
<h1 style="font-size:28px;color:#000080;margin-bottom:8px;">Access Denied</h1>
|
||||||
|
<p style="color:#666;margin-bottom:24px;"><%= typeof message !== 'undefined' ? message : 'You do not have permission to access this page.' %></p>
|
||||||
|
<a href="/" class="btn btn-primary">Go to Dashboard</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Reference in a new issue