mirror of
http://forgejo-oa09toasww4dgii9cj3gpzda.187.127.164.61.sslip.io/iamcoolvivek007/bharath.git
synced 2026-06-11 00:06:51 +00:00
- Govt-app styled freight marketplace - Role-based auth (driver/shipper/broker/admin) - Load board with bidding system - Trip tracking with status flow - In-app messaging - Admin panel - Mobile bottom nav + PWA - Docker + Coolify ready
138 lines
5.5 KiB
JavaScript
138 lines
5.5 KiB
JavaScript
require('dotenv').config();
|
|
const express = require('express');
|
|
const path = require('path');
|
|
const helmet = require('helmet');
|
|
const compression = require('compression');
|
|
const session = require('express-session');
|
|
const rateLimit = require('express-rate-limit');
|
|
const config = require('./config/env');
|
|
|
|
const app = express();
|
|
|
|
// Security
|
|
app.use(helmet({
|
|
contentSecurityPolicy: {
|
|
directives: {
|
|
defaultSrc: ["'self'"],
|
|
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
|
|
fontSrc: ["'self'", "https://fonts.gstatic.com"],
|
|
imgSrc: ["'self'", "data:", "https:"],
|
|
scriptSrc: ["'self'", "'unsafe-inline'"],
|
|
},
|
|
},
|
|
}));
|
|
app.use(compression());
|
|
app.use(rateLimit({ windowMs: 60 * 1000, max: 100 }));
|
|
|
|
// Body parsing
|
|
app.use(express.json());
|
|
app.use(express.urlencoded({ extended: true }));
|
|
|
|
// Static files
|
|
app.use(express.static(path.join(__dirname, 'public')));
|
|
|
|
// View engine
|
|
app.set('view engine', 'ejs');
|
|
app.set('views', path.join(__dirname, 'views'));
|
|
|
|
// Session
|
|
app.use(session({
|
|
secret: config.session.secret,
|
|
resave: false,
|
|
saveUninitialized: false,
|
|
cookie: { secure: config.nodeEnv === 'production', maxAge: 24 * 60 * 60 * 1000 },
|
|
}));
|
|
|
|
// Make user available to all views
|
|
app.use((req, res, next) => {
|
|
res.locals.user = req.session.user || null;
|
|
res.locals.appName = 'भारत ट्रक्स';
|
|
res.locals.appNameEn = 'BharathTrucks';
|
|
next();
|
|
});
|
|
|
|
// Routes
|
|
const authRoutes = require('./routes/auth');
|
|
const loadRoutes = require('./routes/loads');
|
|
const tripRoutes = require('./routes/trips');
|
|
const adminRoutes = require('./routes/admin');
|
|
const messageRoutes = require('./routes/messages');
|
|
app.use('/', authRoutes);
|
|
app.use('/loadboard', loadRoutes);
|
|
app.use('/trips', tripRoutes);
|
|
app.use('/admin', adminRoutes);
|
|
app.use('/messages', messageRoutes);
|
|
|
|
const { requireAuth, requireDriver, requireShipper, requireBroker } = require('./middleware/auth');
|
|
const supabase = require('./services/supabase');
|
|
|
|
app.get('/health', (req, res) => res.json({ status: 'ok', ts: Date.now() }));
|
|
app.get('/', (req, res) => {
|
|
if (req.session && req.session.user) {
|
|
const { ROLES } = require('./config/constants');
|
|
if (req.session.user.role === ROLES.DRIVER) return res.redirect('/driver');
|
|
if (req.session.user.role === ROLES.SHIPPER) return res.redirect('/shipper');
|
|
if (req.session.user.role === ROLES.BROKER) return res.redirect('/broker');
|
|
}
|
|
res.render('pages/landing');
|
|
});
|
|
|
|
// Dashboards
|
|
app.get('/profile', requireAuth, async (req, res) => {
|
|
const { data: profile } = await supabase.from('app_users').select('*').eq('id', req.session.user.id).single();
|
|
res.render('pages/profile', { profile: profile || req.session.user, success: req.query.ok });
|
|
});
|
|
app.post('/profile', requireAuth, async (req, res) => {
|
|
const { name, phone, city, state } = req.body;
|
|
await supabase.from('app_users').update({ name: name.trim(), phone: phone || null, city: city || null, state: state || null }).eq('id', req.session.user.id);
|
|
req.session.user.name = name.trim();
|
|
res.redirect('/profile?ok=1');
|
|
});
|
|
|
|
app.get('/driver', requireAuth, requireDriver, async (req, res) => {
|
|
const userId = req.session.user.id;
|
|
const { data: bids } = await supabase.from('bids').select('status').eq('driver_id', userId);
|
|
const { data: trips } = await supabase.from('trips').select('*, load:load_id(origin_city, destination_city)').eq('driver_id', userId).order('created_at', { ascending: false });
|
|
const activeTrips = (trips || []).filter(t => !['delivered', 'cancelled'].includes(t.status));
|
|
const delivered = (trips || []).filter(t => t.status === 'delivered');
|
|
const earnings = delivered.reduce((s, t) => s + (parseFloat(t.amount) || 0), 0);
|
|
res.render('pages/driver-dashboard', {
|
|
stats: { totalTrips: (trips || []).length, activeBids: (bids || []).filter(b => b.status === 'pending').length, earnings },
|
|
activeTrips,
|
|
});
|
|
});
|
|
|
|
app.get('/shipper', requireAuth, requireShipper, async (req, res) => {
|
|
const userId = req.session.user.id;
|
|
const { data: loads } = await supabase.from('loads').select('*').eq('posted_by', userId).order('created_at', { ascending: false }).limit(10);
|
|
const { data: trips } = await supabase.from('trips').select('status').eq('shipper_id', userId);
|
|
const allLoads = loads || [];
|
|
res.render('pages/shipper-dashboard', {
|
|
stats: { totalLoads: allLoads.length, openLoads: allLoads.filter(l => l.status === 'open').length, activeTrips: (trips || []).filter(t => !['delivered', 'cancelled'].includes(t.status)).length },
|
|
recentLoads: allLoads.slice(0, 5),
|
|
});
|
|
});
|
|
|
|
app.get('/broker', requireAuth, requireBroker, async (req, res) => {
|
|
const userId = req.session.user.id;
|
|
const { data: loads } = await supabase.from('loads').select('*').eq('posted_by', userId).order('created_at', { ascending: false }).limit(10);
|
|
const { data: trips } = await supabase.from('trips').select('status').eq('shipper_id', userId);
|
|
const allLoads = loads || [];
|
|
res.render('pages/broker-dashboard', {
|
|
stats: { totalLoads: allLoads.length, bookedLoads: allLoads.filter(l => l.status === 'booked').length, activeTrips: (trips || []).filter(t => !['delivered', 'cancelled'].includes(t.status)).length },
|
|
recentLoads: allLoads.slice(0, 5),
|
|
});
|
|
});
|
|
|
|
// 404
|
|
app.use((req, res) => res.status(404).render('pages/404'));
|
|
|
|
// Error handler
|
|
app.use((err, req, res, next) => {
|
|
console.error(err.stack);
|
|
res.status(500).render('pages/500');
|
|
});
|
|
|
|
app.listen(config.port, '0.0.0.0', () => {
|
|
console.log(`BharathTrucks running at http://localhost:${config.port}`);
|
|
});
|