require("dotenv").config(); const express = require("express"); const mongoose = require("mongoose"); const cors = require("cors"); const http = require("http"); const socketio = require("socket.io"); const helmet = require("helmet"); const rateLimit = require("express-rate-limit"); const { errorHandler } = require("./middleware/errorHandler"); const socketAuth = require("./middleware/socketAuth"); const app = express(); const server = http.createServer(app); const io = socketio(server, { cors: { origin: process.env.FRONTEND_URL || "http://localhost:3000", methods: ["GET", "POST"], credentials: true, }, }); const port = process.env.PORT || 5000; // Security Headers - Helmet app.use(helmet()); // CORS Configuration app.use( cors({ origin: process.env.FRONTEND_URL || "http://localhost:3000", credentials: true, }), ); // Body Parser app.use(express.json()); // Rate Limiting for Auth Routes (5 requests per 15 minutes) const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // 5 requests per windowMs message: { success: false, error: "Too many authentication attempts, please try again later", }, standardHeaders: true, legacyHeaders: false, }); // General API Rate Limiting (100 requests per 15 minutes) const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // 100 requests per windowMs message: { success: false, error: "Too many requests, please try again later", }, standardHeaders: true, legacyHeaders: false, }); // MongoDB Connection mongoose .connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true, }) .then(() => console.log("MongoDB connected")) .catch((err) => console.log("MongoDB connection error:", err)); // Socket.IO Authentication Middleware io.use(socketAuth); // Socket.IO Setup with Authentication io.on("connection", (socket) => { console.log(`Client connected: ${socket.user.id}`); socket.on("joinEvent", (eventId) => { socket.join(`event_${eventId}`); console.log(`User ${socket.user.id} joined event ${eventId}`); }); socket.on("joinPost", (postId) => { socket.join(`post_${postId}`); console.log(`User ${socket.user.id} joined post ${postId}`); }); socket.on("eventUpdate", (data) => { io.to(`event_${data.eventId}`).emit("update", data.message); }); socket.on("disconnect", () => { console.log(`Client disconnected: ${socket.user.id}`); }); }); // Make io available to routes app.set("io", io); // Routes const authRoutes = require("./routes/auth"); const streetRoutes = require("./routes/streets"); const taskRoutes = require("./routes/tasks"); const postRoutes = require("./routes/posts"); const commentsRoutes = require("./routes/comments"); const eventRoutes = require("./routes/events"); const rewardRoutes = require("./routes/rewards"); const reportRoutes = require("./routes/reports"); const badgesRoutes = require("./routes/badges"); const aiRoutes = require("./routes/ai"); const paymentRoutes = require("./routes/payments"); const userRoutes = require("./routes/users"); // Apply rate limiters app.use("/api/auth/register", authLimiter); app.use("/api/auth/login", authLimiter); app.use("/api", apiLimiter); // Routes app.use("/api/auth", authRoutes); app.use("/api/streets", streetRoutes); app.use("/api/tasks", taskRoutes); app.use("/api/posts", postRoutes); app.use("/api/posts", commentsRoutes); // Comments are nested under posts app.use("/api/events", eventRoutes); app.use("/api/rewards", rewardRoutes); app.use("/api/reports", reportRoutes); app.use("/api/badges", badgesRoutes); app.use("/api/ai", aiRoutes); app.use("/api/payments", paymentRoutes); app.use("/api/users", userRoutes); app.get("/", (req, res) => { res.send("Street Adoption App Backend"); }); // Error Handler Middleware (must be last) app.use(errorHandler); server.listen(port, () => { console.log(`Server running on port ${port}`); });