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