const express = require('express'); const mongoose = require('mongoose'); const cors = require('cors'); const dotenv = require('dotenv'); const http = require('http'); const { Server } = require('socket.io'); dotenv.config(); const app = express(); const server = http.createServer(app); const io = new Server(server, { cors: { origin: process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS.split(',') : "*", methods: ["GET", "POST", "PUT", "DELETE"], credentials: true } }); // Socket.io Logic const Message = require('./models/Message'); const Notification = require('./models/Notification'); io.on('connection', (socket) => { console.log('User connected:', socket.id); socket.on('join', (userId) => { socket.join(userId); console.log(`User ${userId} joined their room`); }); socket.on('sendMessage', async (data) => { try { const { sender, receiver, content, bookingId } = data; const message = await Message.create({ sender, receiver, content, bookingId }); // Emit to receiver io.to(receiver).emit('receiveMessage', message); // Emit to sender for confirmation/sync io.to(sender).emit('receiveMessage', message); } catch (err) { console.error('Socket message error:', err); } }); socket.on('instantBookingRequest', async (data) => { try { const { customerId, photographerId, bookingDetails } = data; // Emit to specific photographer io.to(photographerId).emit('receiveBookingRequest', { customerId, bookingDetails, socketId: socket.id }); } catch (err) { console.error('Instant booking error:', err); } }); socket.on('bookingResponse', (data) => { const { customerSocketId, accepted, bookingId } = data; io.to(customerSocketId).emit('bookingStatusUpdate', { accepted, bookingId }); }); socket.on('disconnect', () => { console.log('User disconnected'); }); }); // Middleware to inject io into requests if needed app.use((req, res, next) => { req.io = io; next(); }); // Middleware app.use(cors({ origin: process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS.split(',') : "*", credentials: true })); app.use(express.json()); // Routes const authRoutes = require('./routes/authRoutes'); const bookingRoutes = require('./routes/bookingRoutes'); const userRoutes = require('./routes/userRoutes'); const reviewRoutes = require('./routes/reviewRoutes'); const chatRoutes = require('./routes/chatRoutes'); const notificationRoutes = require('./routes/notificationRoutes'); const recommendationRoutes = require('./routes/recommendationRoutes'); const packageRoutes = require('./routes/packageRoutes'); const verificationRoutes = require('./routes/verificationRoutes'); const deliveryRoutes = require('./routes/deliveryRoutes'); const invoiceRoutes = require('./routes/invoiceRoutes'); const reportRoutes = require('./routes/reportRoutes'); const loyaltyRoutes = require('./routes/loyaltyRoutes'); const tripPlanRoutes = require('./routes/tripPlanRoutes'); const dealRoutes = require('./routes/dealRoutes'); app.use('/api/auth', authRoutes); app.use('/api/bookings', bookingRoutes); app.use('/api/users', userRoutes); app.use('/api/reviews', reviewRoutes); app.use('/api/chat', chatRoutes); app.use('/api/notifications', notificationRoutes); app.use('/api/recommendations', recommendationRoutes); app.use('/api/packages', packageRoutes); app.use('/api/verification', verificationRoutes); app.use('/api/delivery', deliveryRoutes); app.use('/api/invoices', invoiceRoutes); app.use('/api/reports', reportRoutes); app.use('/api/loyalty', loyaltyRoutes); app.use('/api/trips', tripPlanRoutes); app.use('/api/deals', dealRoutes); const User = require('./models/User'); const seedDatabase = async () => { try { const userCount = await User.countDocuments(); if (userCount === 0) { console.log('Database is empty. Seeding dummy photographers...'); const dummyPhotographers = [ { firstName: 'Aisha', lastName: 'Sharma', email: 'aisha@example.com', password: 'password123', role: 'photographer', roles: ['photographer'], specialty: 'Portrait', address: 'Hyderabad, Telangana', price: 1500, experience: 5, rating: 4.9, isVerified: true, bio: 'Professional portrait photographer with 5 years of experience capturing moments in Hyderabad.', profilePicture: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&w=400&h=400', portfolio: [ { url: 'https://images.unsplash.com/photo-1511895426328-dc8714191300?auto=format&fit=crop&w=800&h=800' }, { url: 'https://images.unsplash.com/photo-1520854221256-17451cc331bf?auto=format&fit=crop&w=800&h=800' } ] }, { firstName: 'Vikram', lastName: 'Singh', email: 'vikram@example.com', password: 'password123', role: 'photographer', roles: ['photographer'], specialty: 'Wedding', address: 'Mumbai, Maharashtra', price: 3000, experience: 8, rating: 4.7, isVerified: true, bio: 'Specializing in cinematic wedding photography and candid moments.', profilePicture: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&w=400&h=400', portfolio: [ { url: 'https://images.unsplash.com/photo-1511285560929-80b456fea0bc?auto=format&fit=crop&w=800&h=800' } ] }, { firstName: 'Priya', lastName: 'Patel', email: 'priya@example.com', password: 'password123', role: 'photographer', roles: ['photographer'], specialty: 'Event', address: 'Bangalore, Karnataka', price: 2000, experience: 3, rating: 4.8, isVerified: false, bio: 'Energetic event photographer covering corporate events and private parties.', profilePicture: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?auto=format&fit=crop&w=400&h=400', portfolio: [ { url: 'https://images.unsplash.com/photo-1505236858219-8359eb29e329?auto=format&fit=crop&w=800&h=800' } ] }, { firstName: 'Rohan', lastName: 'Verma', email: 'rohan@example.com', password: 'password123', role: 'photographer', roles: ['photographer'], specialty: 'Real Estate', address: 'Delhi, NCR', price: 2500, experience: 6, rating: 4.9, isVerified: true, bio: 'Expert architectural and real estate photographer. I bring homes to life through dynamic lighting.', profilePicture: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&w=400&h=400', portfolio: [ { url: 'https://images.unsplash.com/photo-1600596542815-ffad4c1539a9?auto=format&fit=crop&w=800&h=800' }, { url: 'https://images.unsplash.com/photo-1600607687931-57dce8bb084b?auto=format&fit=crop&w=800&h=800' } ] }, { firstName: 'Neha', lastName: 'Gupta', email: 'neha@example.com', password: 'password123', role: 'photographer', roles: ['photographer'], specialty: 'Fashion', address: 'Hyderabad, Telangana', price: 4000, experience: 10, rating: 4.6, isVerified: true, bio: 'High-end fashion and editorial photography for bold personalities.', profilePicture: 'https://images.unsplash.com/photo-1517841905240-472988babdf9?auto=format&fit=crop&w=400&h=400', portfolio: [ { url: 'https://images.unsplash.com/photo-1515886657613-9f3515b0c78f?auto=format&fit=crop&w=800&h=800' }, { url: 'https://images.unsplash.com/photo-1483985988355-763728e1935b?auto=format&fit=crop&w=800&h=800' } ] }, { firstName: 'Arjun', lastName: 'Reddy', email: 'arjun@example.com', password: 'password123', role: 'photographer', roles: ['photographer'], specialty: 'Automotive', address: 'Chennai, Tamil Nadu', price: 1800, experience: 4, rating: 4.8, isVerified: false, bio: 'Capturing speed and aesthetics. I shoot cars, bikes, and motorsport events.', profilePicture: 'https://images.unsplash.com/photo-1492562080023-ab3db95bfbce?auto=format&fit=crop&w=400&h=400', portfolio: [ { url: 'https://images.unsplash.com/photo-1494976388531-d1058494cdd8?auto=format&fit=crop&w=800&h=800' } ] }, { firstName: 'Meera', lastName: 'Krishna', email: 'meera@example.com', password: 'password123', role: 'photographer', roles: ['photographer'], specialty: 'Food', address: 'Pune, Maharashtra', price: 1200, experience: 2, rating: 5.0, isVerified: true, bio: 'Food stylist and photographer for cafes, restaurants, and cloud kitchens.', profilePicture: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?auto=format&fit=crop&w=400&h=400', portfolio: [ { url: 'https://images.unsplash.com/photo-1504674900247-0877df9cc836?auto=format&fit=crop&w=800&h=800' }, { url: 'https://images.unsplash.com/photo-1414235077428-338989a2e8c0?auto=format&fit=crop&w=800&h=800' } ] } ]; for (const pg of dummyPhotographers) { const newUser = new User(pg); await newUser.save(); } console.log('Successfully seeded 7 dummy photographers.'); } } catch (err) { console.error('Failed to seed database:', err); } }; const connectDB = async () => { try { let uri = process.env.MONGODB_URI; let isMemory = false; if (!uri || uri === "undefined") { console.log('No MONGODB_URI found. Starting In-Memory MongoDB Server for demo purposes...'); const { MongoMemoryServer } = require('mongodb-memory-server'); const mongoServer = await MongoMemoryServer.create(); uri = mongoServer.getUri(); isMemory = true; console.log('In-Memory MongoDB started successfully!'); } await mongoose.connect(uri); console.log('MongoDB connected successfully'); if (isMemory) { await seedDatabase(); } } catch (err) { console.error('MongoDB connection error:', err); } }; connectDB(); // Serve Static Frontend (for Full-Stack Deployment) const path = require('path'); const clientDistPath = path.join(__dirname, '../../client/dist'); app.use(express.static(clientDistPath)); app.get(/(.*)/, (req, res, next) => { if (req.path.startsWith('/api')) return next(); res.sendFile(path.join(clientDistPath, 'index.html'), (err) => { if (err) res.send('SnapLocal Pro API is running with Real-time support... (Frontend not built yet)'); }); }); const PORT = process.env.PORT || 5000; server.listen(PORT, () => { console.log(`Server running on port ${PORT}`); });