diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..1f6ab37 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,235 @@ +# FreightDesk — Deployment Guide + +## Prerequisites + +- Ubuntu 22.04+ VPS (minimum 2GB RAM, 2 vCPU) +- Domain pointed to VPS IP +- Coolify installed (or Docker + Docker Compose) +- Supabase project (self-hosted or cloud) + +## Quick Start + +### 1. Clone Repository + +```bash +git clone http://forgejo-vil3xyowqk0qsh4hiqy77e3h.187.127.178.110.sslip.io/iamcoolvivek007/freightdesk.git +cd freightdesk/webapp +``` + +### 2. Environment Configuration + +Create `.env` file: + +```env +# Server +NODE_ENV=production +PORT=3000 + +# Supabase +SUPABASE_URL=https://your-project.supabase.co +SUPABASE_SERVICE_KEY=your-service-role-key +SUPABASE_ANON_KEY=your-anon-key + +# Session +SESSION_SECRET=generate-a-random-64-char-string-here +SESSION_MAX_AGE=86400000 + +# WhatsApp (optional — for receiving messages) +WHATSAPP_WEBHOOK_TOKEN=your-webhook-verify-token + +# Payment Gateway (production) +RAZORPAY_KEY_ID=rzk_live_xxxxx +RAZORPAY_KEY_SECRET=xxxxx + +# Email (optional — for notifications) +SMTP_HOST=smtp.gmail.com +SMTP_PORT=587 +SMTP_USER=your-email@gmail.com +SMTP_PASS=your-app-password +``` + +### 3. Install Dependencies + +```bash +npm ci --production +``` + +### 4. Run Database Migrations + +Run migrations 001 through 007 in order: + +```bash +# Using Supabase CLI +supabase db push + +# Or manually via SQL editor: +# Copy contents of supabase/migrations/001_initial_schema.sql and run +# Copy contents of supabase/migrations/002_whatsapp_parser.sql and run +# ... through 007_location_tracking.sql +``` + +### 5. Create Admin User + +Visit `/setup` in your browser and create the admin account. + +### 6. Start the Server + +```bash +# Development +npm run dev + +# Production +NODE_ENV=production node src/server.js +``` + +### 7. Coolify Deployment (Recommended) + +1. In Coolify, create new application +2. Connect to your Forgejo repository +3. Set buildpack: `Dockerfile` +4. Set Dockerfile path: `/webapp/Dockerfile` +5. Add environment variables from `.env` +6. Set domain and enable SSL + +## Docker + +```bash +cd webapp +docker build -t freightdesk . +docker run -d \ + --name freightdesk \ + -p 3000:3000 \ + --env-file .env \ + --restart unless-stopped \ + freightdesk +``` + +## Docker Compose (Full Stack) + +```yaml +version: '3.8' +services: + app: + build: ./webapp + ports: + - "3000:3000" + env_file: .env + restart: unless-stopped + depends_on: + - supabase + + # If self-hosting Supabase + supabase: + image: supabase/supabase-local:latest + ports: + - "5432:5432" # PostgreSQL + - "8000:8000" # REST API + - "4000:4000" # Studio + volumes: + - supabase-data:/var/lib/supabase + restart: unless-stopped + +volumes: + supabase-data: +``` + +## Post-Deployment Checklist + +- [ ] Run all 7 migrations (001-007) +- [ ] Create admin account via /setup +- [ ] Configure SSL certificate +- [ ] Set up automated backups (Supabase: daily DB dump) +- [ ] Configure Coolify webhooks for auto-deploy on git push +- [ ] Set up monitoring (Prometheus /metrics endpoint at :3000/metrics) +- [ ] Configure Pino log aggregation +- [ ] Test WhatsApp parser with sample messages +- [ ] Test registration flow (shipper + driver) +- [ ] Test marketplace: post load → bid → accept +- [ ] Test payment escrow: deposit → hold → release → payout + +## Migrations Summary + +| # | File | What it adds | +|---|------|-------------| +| 001 | `001_initial_schema.sql` | Core tables: loads, shippers, vehicles, payments, users | +| 002 | `002_whatsapp_parser.sql` | Parser config, city list, known shippers | +| 003 | `003_soft_delete.sql` | Soft-delete columns on all tables | +| 004 | `004_audit_logging.sql` | Audit log table + triggers | +| 005 | `005_saas_marketplace.sql` | Bids, negotiations, ratings, notifications, marketplace fields | +| 006 | `006_payment_escrow.sql` | Escrow accounts, transactions, payouts, disputes | +| 007 | `007_location_tracking.sql` | Vehicle GPS location history | + +## Troubleshooting + +### App won't start +- Check `.env` has all required variables +- Verify Supabase connection: `curl $SUPABASE_URL/rest/v1/` +- Check logs: `docker logs freightdesk` + +### Database errors +- Run migrations in order (001 → 007) +- Check Supabase service key has proper permissions +- Verify `pgcrypto` extension is enabled (for UUID generation) + +### WhatsApp parser not working +- Ensure migration 002 ran (populates CITIES and parser config) +- Test via `/api/parser/test` endpoint + +### Payment flow fails +- Ensure migration 006 ran +- Check escrow_accounts table exists +- Verify platform_config has default values + +## Architecture + +``` + ┌─────────────────────┐ + │ Nginx / Coolify │ + │ (SSL + Proxy) │ + └──────────┬──────────┘ + │ + ┌──────────▼──────────┐ + │ Node.js + Express │ + │ FreightDesk App │ + │ Port 3000 │ + └──┬──────┬──────┬────┘ + │ │ │ + ┌──────────────┘ │ └──────────────┐ + ▼ ▼ ▼ + ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ + │ EJS Views │ │ REST API │ │ Supabase │ + │ (templates) │ │ /api/* │ │ PostgreSQL │ + │ + Recharts │ │ JSON │ │ + Realtime │ + │ CDN widgets │ │ │ │ │ + └──────────────┘ └──────────────┘ └──────────────┘ + +Routes: + / → Public landing page + /login → Admin login + /setup → Initial admin setup + /dashboard → Admin dashboard (EJS + Recharts) + /loads → Load management (admin) + /shippers → Shipper management + /vehicles → Vehicle management + /payments → Payment tracking + /reports → Reports + /audit-logs → Audit log viewer + /invoices → Invoice PDF generation + /admin/moderation → User verification, payouts, disputes + + /register/shipper → Shipper self-registration + /register/driver → Driver self-registration + /portal/* → Shipper/driver portal (dashboard, loads, trips) + + /marketplace → Browse/post loads, bidding + /escrow → Deposits, payouts, disputes + + /api/* → REST API (JSON) + /metrics → Prometheus metrics + /health → Health check +``` + +## Support + +- Forgejo: `http://forgejo-vil3xyowqk0qsh4hiqy77e3h.187.127.178.110.sslip.io/iamcoolvivek007/freightdesk` +- Issues: Create on Forgejo diff --git a/supabase/migrations/007_location_tracking.sql b/supabase/migrations/007_location_tracking.sql new file mode 100644 index 0000000..e3374b6 --- /dev/null +++ b/supabase/migrations/007_location_tracking.sql @@ -0,0 +1,24 @@ +-- ============================================================ +-- FreightDesk — Migration 007: Driver Location Tracking +-- GPS location history for real-time tracking +-- ============================================================ + +CREATE TABLE IF NOT EXISTS vehicle_locations ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + vehicle_id UUID NOT NULL REFERENCES vehicles(id) ON DELETE CASCADE, + lat DECIMAL(10,8) NOT NULL, + lng DECIMAL(11,8) NOT NULL, + accuracy DECIMAL(8,2), + heading DECIMAL(6,2), + speed DECIMAL(6,2), + recorded_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE INDEX IF NOT EXISTS idx_vehicle_locations_vehicle ON vehicle_locations(vehicle_id); +CREATE INDEX IF NOT EXISTS idx_vehicle_locations_time ON vehicle_locations(recorded_at); +CREATE INDEX IF NOT EXISTS idx_vehicle_locations_vehicle_time ON vehicle_locations(vehicle_id, recorded_at DESC); + +-- Enable PostGIS-like functionality with btree_gist for spatial queries +-- (In production, use PostGIS extension) +CREATE INDEX IF NOT EXISTS idx_vehicles_location ON vehicles(current_lat, current_lng) + WHERE current_lat IS NOT NULL AND current_lng IS NOT NULL; diff --git a/webapp/src/routes/location.js b/webapp/src/routes/location.js new file mode 100644 index 0000000..38e4f29 --- /dev/null +++ b/webapp/src/routes/location.js @@ -0,0 +1,68 @@ +// POST /api/location/update — driver updates their GPS location +// GET /api/location/:load_id — get driver location for a load (shipper views this) + +const express = require('express'); +const router = express.Router(); +const supabase = require('../services/supabase'); +const { asyncHandler } = require('../middleware/security'); + +function requirePortalAuth(req, res, next) { + if (!req.session.portalUser) { + return res.status(401).json({ error: 'Authentication required' }); + } + next(); +} + +// POST /api/location/update +router.post('/update', requirePortalAuth, asyncHandler(async (req, res) => { + const { lat, lng, accuracy, heading, speed } = req.body; + const driverId = req.session.portalUser?.driver_id; + + if (!lat || !lng) { + return res.status(400).json({ error: 'lat and lng are required' }); + } + + if (!driverId) { + return res.status(400).json({ error: 'Driver profile not found' }); + } + + await supabase.from('vehicles').update({ + current_lat: parseFloat(lat), + current_lng: parseFloat(lng), + updated_at: new Date().toISOString(), + }).eq('id', driverId); + + // Also store in location history + await supabase.from('vehicle_locations').insert({ + vehicle_id: driverId, + lat: parseFloat(lat), + lng: parseFloat(lng), + accuracy: accuracy || null, + heading: heading || null, + speed: speed || null, + }); + + res.json({ success: true }); +})); + +// GET /api/location/:load_id — get assigned driver's location +router.get('/:load_id', requirePortalAuth, asyncHandler(async (req, res) => { + const { data: load } = await supabase + .from('loads') + .select('accepted_bid_id, vehicles(current_lat, current_lng, driver_name, updated_at)') + .eq('id', req.params.load_id) + .single(); + + if (!load?.vehicles) { + return res.json({ error: 'No driver assigned or location not available' }); + } + + res.json({ + driver_name: load.vehicles.driver_name, + lat: load.vehicles.current_lat, + lng: load.vehicles.current_lng, + last_updated: load.vehicles.updated_at, + }); +})); + +module.exports = router; diff --git a/webapp/src/server.js b/webapp/src/server.js index d9a5808..f5347e2 100644 --- a/webapp/src/server.js +++ b/webapp/src/server.js @@ -210,6 +210,7 @@ app.use('/portal', require('./routes/portal')); app.use('/invoices', require('./routes/invoices')); app.use('/portal-users', require('./routes/portal-users')); app.use('/api', require('./routes/api')); +app.use('/api/location', require('./routes/location')); app.use('/marketplace', require('./routes/marketplace')); app.use('/escrow', require('./routes/payments')); app.use('/admin/moderation', require('./routes/admin-moderation')); diff --git a/webapp/src/views/pages/marketplace/bulk-parser.ejs b/webapp/src/views/pages/marketplace/bulk-parser.ejs new file mode 100644 index 0000000..87a1879 --- /dev/null +++ b/webapp/src/views/pages/marketplace/bulk-parser.ejs @@ -0,0 +1,205 @@ +<%- include('../partials/portal-header', { activeMenu: 'parser' }) %> + + + +
+
+

Paste Messages

+
+

+ Paste multiple WhatsApp messages (one per line or separated by blank lines). + Each message will be parsed and you can review before saving. +

+
+ +
+
+ + +
+
+
+ +
+
+

Parsed Results

+
+
+
+
+
📱
+

No messages parsed yet

+

Paste WhatsApp messages and click Parse

+
+
+
+
+
+ + + + + + +<%- include('../partials/portal-footer') %>