// Load environment-specific config const env = process.env.NODE_ENV || "dev"; const envFile = `.env.${env}`; require("dotenv").config({ path: envFile, }); const express = require("express"); const cors = require("cors"); const bodyParser = require("body-parser"); const path = require("path"); const helmet = require("helmet"); const { sequelize } = require("./models"); // Import from models/index.js to ensure associations are loaded const { cookieParser } = require("./middleware/csrf"); const logger = require("./utils/logger"); const morgan = require("morgan"); const authRoutes = require("./routes/auth"); const userRoutes = require("./routes/users"); const itemRoutes = require("./routes/items"); const rentalRoutes = require("./routes/rentals"); const messageRoutes = require("./routes/messages"); const itemRequestRoutes = require("./routes/itemRequests"); const stripeRoutes = require("./routes/stripe"); const mapsRoutes = require("./routes/maps"); const conditionCheckRoutes = require("./routes/conditionChecks"); const PayoutProcessor = require("./jobs/payoutProcessor"); const RentalStatusJob = require("./jobs/rentalStatusJob"); const ConditionCheckReminderJob = require("./jobs/conditionCheckReminder"); const app = express(); // Import security middleware const { enforceHTTPS, securityHeaders, addRequestId, sanitizeError, } = require("./middleware/security"); const { generalLimiter } = require("./middleware/rateLimiter"); const errorLogger = require("./middleware/errorLogger"); const apiLogger = require("./middleware/apiLogger"); // Apply security middleware app.use(enforceHTTPS); app.use(addRequestId); app.use(securityHeaders); // Security headers with Helmet app.use( helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "https://cdn.jsdelivr.net"], fontSrc: ["'self'"], scriptSrc: ["'self'", "https://accounts.google.com"], imgSrc: ["'self'"], connectSrc: ["'self'"], frameSrc: ["'self'", "https://accounts.google.com"], }, }, }) ); // Cookie parser for CSRF app.use(cookieParser); // HTTP request logging app.use(morgan("combined", { stream: logger.stream })); // API request/response logging app.use("/api/", apiLogger); // General rate limiting for all routes app.use("/api/", generalLimiter); // CORS with security settings app.use( cors({ origin: process.env.FRONTEND_URL || "http://localhost:3000", credentials: true, optionsSuccessStatus: 200, }) ); // Body parsing with size limits app.use( bodyParser.json({ limit: "1mb", verify: (req, res, buf) => { // Store raw body for webhook verification req.rawBody = buf; }, }) ); app.use( bodyParser.urlencoded({ extended: true, limit: "1mb", parameterLimit: 100, // Limit number of parameters }) ); // Serve static files from uploads directory app.use("/uploads", express.static(path.join(__dirname, "uploads"))); app.use("/api/auth", authRoutes); app.use("/api/users", userRoutes); app.use("/api/items", itemRoutes); app.use("/api/rentals", rentalRoutes); app.use("/api/messages", messageRoutes); app.use("/api/item-requests", itemRequestRoutes); app.use("/api/stripe", stripeRoutes); app.use("/api/maps", mapsRoutes); app.use("/api/condition-checks", conditionCheckRoutes); app.get("/", (req, res) => { res.json({ message: "CommunityRentals.App API is running!" }); }); // Error handling middleware (must be last) app.use(errorLogger); app.use(sanitizeError); const PORT = process.env.PORT || 5000; sequelize .sync({ alter: true }) .then(() => { logger.info("Database synced successfully"); // Start the payout processor const payoutJobs = PayoutProcessor.startScheduledPayouts(); logger.info("Payout processor started"); // Start the rental status job const rentalStatusJobs = RentalStatusJob.startScheduledStatusUpdates(); logger.info("Rental status job started"); // Start the condition check reminder job const conditionCheckJobs = ConditionCheckReminderJob.startScheduledReminders(); logger.info("Condition check reminder job started"); app.listen(PORT, () => { logger.info(`Server is running on port ${PORT}`, { port: PORT, environment: env, }); }); }) .catch((err) => { logger.error("Unable to sync database", { error: err.message, stack: err.stack, }); });