Files
adopt-a-street/backend/config/cloudinary.js
William Valentin 7c70a8d098 feat(backend): implement comments, image uploads, and data consistency
Implement additional backend features and improve data models:

Comments System:
- Create Comment model with user and post relationships
- Add comments routes: GET /api/posts/:postId/comments (paginated), POST (create), DELETE (own comments)
- Update Post model with commentsCount field
- Emit Socket.IO events for newComment and commentDeleted
- Pagination support for comment lists
- Authorization checks (users can only delete own comments)
- 500 character limit on comments

Image Upload System:
- Implement Cloudinary configuration (config/cloudinary.js)
- Add uploadImage() and deleteImage() helper functions
- Image optimization: max 1000x1000, auto quality, auto format (WebP)
- Integrate image upload in users routes (profile pictures)
- Integrate image upload in posts routes (post images with add/update endpoints)
- File validation: 5MB limit, JPG/PNG/GIF/WebP only
- Automatic image deletion when removing posts/reports

Data Consistency Improvements:
- Add cascade deletes in Street model (remove from user, delete associated tasks)
- Add cascade deletes in Task model (remove from user completedTasks)
- Add cascade deletes in Post model (remove from user posts)
- Update user relationships on save (adoptedStreets, completedTasks, posts, events)
- Add proper indexes for performance (2dsphere for location, compound indexes)
- Add virtual relationships and toJSON configurations

Model Updates:
- Street: Add cascade hooks, location 2dsphere index
- Task: Add cascade hooks, compound indexes for queries
- Post: Add imageUrl, cloudinaryPublicId, commentsCount fields
- Event: Add participants tracking
- Report: Add image upload support
- User: Add earnedBadges virtual, profilePicture, cloudinaryPublicId

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 10:43:08 -07:00

64 lines
1.7 KiB
JavaScript

const cloudinary = require("cloudinary").v2;
// Configure Cloudinary with environment variables
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
/**
* Upload image buffer to Cloudinary
* @param {Buffer} fileBuffer - Image file buffer from multer
* @param {string} folder - Cloudinary folder path
* @returns {Promise<Object>} Cloudinary upload result with url and public_id
*/
const uploadImage = (fileBuffer, folder = "adopt-a-street") => {
return new Promise((resolve, reject) => {
cloudinary.uploader
.upload_stream(
{
folder: folder,
resource_type: "image",
transformation: [
{ width: 1000, height: 1000, crop: "limit" }, // Limit max dimensions
{ quality: "auto" }, // Auto quality optimization
{ fetch_format: "auto" }, // Auto format selection (WebP, etc.)
],
},
(error, result) => {
if (error) {
reject(error);
} else {
resolve({
url: result.secure_url,
publicId: result.public_id,
});
}
},
)
.end(fileBuffer);
});
};
/**
* Delete image from Cloudinary
* @param {string} publicId - Cloudinary public_id of the image
* @returns {Promise<Object>} Cloudinary deletion result
*/
const deleteImage = async (publicId) => {
try {
const result = await cloudinary.uploader.destroy(publicId);
return result;
} catch (error) {
console.error("Error deleting image from Cloudinary:", error);
throw error;
}
};
module.exports = {
uploadImage,
deleteImage,
cloudinary,
};