- Replace Event model with CouchDB version using couchdbService - Replace Reward model with CouchDB version using couchdbService - Update event and reward routes to use new model interfaces - Handle participant management with embedded user data - Maintain status transitions for events (upcoming, ongoing, completed, cancelled) - Preserve catalog functionality and premium vs regular rewards - Update validators to accept CouchDB document IDs - Add rewards design document to couchdbService - Update test helpers for new model structure - Initialize CouchDB alongside MongoDB in server.js for backward compatibility - Fix linting issues in migrated routes 🤖 Generated with [AI Assistant] Co-Authored-By: AI Assistant <noreply@ai-assistant.com>
159 lines
4.5 KiB
JavaScript
159 lines
4.5 KiB
JavaScript
require("dotenv").config();
|
|
const express = require("express");
|
|
const mongoose = require("mongoose");
|
|
const couchdbService = require("./services/couchdbService");
|
|
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,
|
|
});
|
|
|
|
// Database Connections
|
|
// MongoDB (for backward compatibility during migration)
|
|
mongoose
|
|
.connect(process.env.MONGO_URI, {
|
|
useNewUrlParser: true,
|
|
useUnifiedTopology: true,
|
|
})
|
|
.then(() => console.log("MongoDB connected"))
|
|
.catch((err) => console.log("MongoDB connection error:", err));
|
|
|
|
// CouchDB (primary database)
|
|
couchdbService.initialize()
|
|
.then(() => console.log("CouchDB initialized"))
|
|
.catch((err) => console.log("CouchDB initialization 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);
|
|
|
|
// Health check endpoint (for Kubernetes liveness/readiness probes)
|
|
app.get("/api/health", (req, res) => {
|
|
res.status(200).json({
|
|
status: "healthy",
|
|
timestamp: new Date().toISOString(),
|
|
uptime: process.uptime(),
|
|
mongodb: mongoose.connection.readyState === 1 ? "connected" : "disconnected",
|
|
});
|
|
});
|
|
|
|
// 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}`);
|
|
});
|