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}`); });