Files
adopt-a-street/backend/routes/reports.js
William Valentin df94c17e1f feat: complete MongoDB to CouchDB migration
- Migrate Report model to CouchDB with embedded street/user data
- Migrate UserBadge model to CouchDB with badge population
- Update all remaining routes (reports, users, badges, payments) to use CouchDB
- Add CouchDB health check and graceful shutdown to server.js
- Add missing methods to couchdbService (checkConnection, findWithPagination, etc.)
- Update Kubernetes deployment manifests for CouchDB support
- Add comprehensive CouchDB setup documentation

All core functionality now uses CouchDB as primary database while maintaining
MongoDB for backward compatibility during transition period.

🤖 Generated with [AI Assistant]

Co-Authored-By: AI Assistant <noreply@ai-assistant.com>
2025-11-01 13:29:48 -07:00

151 lines
3.6 KiB
JavaScript

const express = require("express");
const Report = require("../models/Report");
const User = require("../models/User");
const Street = require("../models/Street");
const auth = require("../middleware/auth");
const { asyncHandler } = require("../middleware/errorHandler");
const {
createReportValidation,
reportIdValidation,
} = require("../middleware/validators/reportValidator");
const { upload, handleUploadError } = require("../middleware/upload");
const { uploadImage, deleteImage } = require("../config/cloudinary");
const router = express.Router();
// Get all reports (with pagination)
router.get(
"/",
asyncHandler(async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = Math.min(parseInt(req.query.limit) || 10, 100);
const result = await Report.findWithPagination({
page,
limit,
sort: { createdAt: -1 },
});
res.json({
reports: result.docs,
totalCount: result.totalDocs,
currentPage: result.page,
totalPages: result.totalPages,
hasNext: result.hasNextPage,
hasPrev: result.hasPrevPage,
});
}),
);
// Create a report with optional image
router.post(
"/",
auth,
upload.single("image"),
handleUploadError,
createReportValidation,
asyncHandler(async (req, res) => {
const { street: streetId, issue } = req.body;
// Get street and user data for embedding
const street = await Street.findById(streetId);
if (!street) {
return res.status(404).json({ msg: "Street not found" });
}
const user = await User.findById(req.user.id);
if (!user) {
return res.status(404).json({ msg: "User not found" });
}
const reportData = {
street: {
_id: street._id,
name: street.name,
},
user: {
_id: user._id,
name: user.name,
profilePicture: user.profilePicture,
},
issue,
};
// Upload image if provided
if (req.file) {
const result = await uploadImage(
req.file.buffer,
"adopt-a-street/reports",
);
reportData.imageUrl = result.url;
reportData.cloudinaryPublicId = result.publicId;
}
const report = await Report.create(reportData);
res.json(report);
}),
);
// Add image to existing report
router.post(
"/:id/image",
auth,
upload.single("image"),
handleUploadError,
reportIdValidation,
asyncHandler(async (req, res) => {
const report = await Report.findById(req.params.id);
if (!report) {
return res.status(404).json({ msg: "Report not found" });
}
// Verify user owns the report
if (report.user._id !== req.user.id) {
return res.status(403).json({ msg: "Not authorized" });
}
if (!req.file) {
return res.status(400).json({ msg: "No image file provided" });
}
// Delete old image if exists
if (report.cloudinaryPublicId) {
await deleteImage(report.cloudinaryPublicId);
}
// Upload new image
const result = await uploadImage(
req.file.buffer,
"adopt-a-street/reports",
);
const updatedReport = await Report.update(req.params.id, {
imageUrl: result.url,
cloudinaryPublicId: result.publicId,
});
res.json(updatedReport);
}),
);
// Resolve a report
router.put(
"/:id",
auth,
reportIdValidation,
asyncHandler(async (req, res) => {
const report = await Report.findById(req.params.id);
if (!report) {
return res.status(404).json({ msg: "Report not found" });
}
const updatedReport = await Report.update(req.params.id, {
status: "resolved",
});
res.json(updatedReport);
}),
);
module.exports = router;