const express = require("express"); const mongoose = require("mongoose"); const Street = require("../models/Street"); const User = require("../models/User"); const auth = require("../middleware/auth"); const { asyncHandler } = require("../middleware/errorHandler"); const { createStreetValidation, streetIdValidation, } = require("../middleware/validators/streetValidator"); const { awardStreetAdoptionPoints, checkAndAwardBadges, } = require("../services/gamificationService"); const router = express.Router(); // Get all streets (with pagination) router.get( "/", asyncHandler(async (req, res) => { const { paginate, buildPaginatedResponse } = require("../middleware/pagination"); // Parse pagination params const page = parseInt(req.query.page) || 1; const limit = Math.min(parseInt(req.query.limit) || 10, 100); const skip = (page - 1) * limit; const streets = await Street.find() .sort({ name: 1 }) .skip(skip) .limit(limit) .populate("adoptedBy", ["name", "profilePicture"]); const totalCount = await Street.countDocuments(); res.json(buildPaginatedResponse(streets, totalCount, page, limit)); }), ); // Get single street router.get( "/:id", streetIdValidation, asyncHandler(async (req, res) => { const street = await Street.findById(req.params.id); if (!street) { return res.status(404).json({ msg: "Street not found" }); } res.json(street); }), ); // Create a street router.post( "/", auth, createStreetValidation, asyncHandler(async (req, res) => { const { name, location } = req.body; const newStreet = new Street({ name, location, }); const street = await newStreet.save(); res.json(street); }), ); // Adopt a street router.put( "/adopt/:id", auth, streetIdValidation, asyncHandler(async (req, res) => { const session = await mongoose.startSession(); session.startTransaction(); try { const street = await Street.findById(req.params.id).session(session); if (!street) { await session.abortTransaction(); session.endSession(); return res.status(404).json({ msg: "Street not found" }); } if (street.status === "adopted") { await session.abortTransaction(); session.endSession(); return res.status(400).json({ msg: "Street already adopted" }); } // Check if user has already adopted this street const user = await User.findById(req.user.id).session(session); if (user.adoptedStreets.includes(req.params.id)) { await session.abortTransaction(); session.endSession(); return res .status(400) .json({ msg: "You have already adopted this street" }); } // Update street street.adoptedBy = req.user.id; street.status = "adopted"; await street.save({ session }); // Update user's adoptedStreets array user.adoptedStreets.push(street._id); await user.save({ session }); // Award points for street adoption const { transaction } = await awardStreetAdoptionPoints( req.user.id, street._id, session, ); // Check and award badges const newBadges = await checkAndAwardBadges(req.user.id, session); await session.commitTransaction(); session.endSession(); res.json({ street, pointsAwarded: transaction.amount, newBalance: transaction.balanceAfter, badgesEarned: newBadges, }); } catch (err) { await session.abortTransaction(); session.endSession(); throw err; } }), ); module.exports = router;