Files
adopt-a-street/backend/models/Street.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

70 lines
1.6 KiB
JavaScript

const mongoose = require("mongoose");
const StreetSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
location: {
type: {
type: String,
enum: ["Point"],
required: true,
},
coordinates: {
type: [Number],
required: true,
},
},
adoptedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
status: {
type: String,
enum: ["available", "adopted"],
default: "available",
},
},
{
timestamps: true,
},
);
StreetSchema.index({ location: "2dsphere" });
StreetSchema.index({ adoptedBy: 1 });
StreetSchema.index({ status: 1 });
// Cascade cleanup when a street is deleted
StreetSchema.pre("deleteOne", { document: true, query: false }, async function () {
const User = mongoose.model("User");
const Task = mongoose.model("Task");
// Remove street from user's adoptedStreets
if (this.adoptedBy) {
await User.updateOne(
{ _id: this.adoptedBy },
{ $pull: { adoptedStreets: this._id } }
);
}
// Delete all tasks associated with this street
await Task.deleteMany({ street: this._id });
});
// Update user relationship when street is adopted
StreetSchema.post("save", async function (doc) {
if (doc.adoptedBy && doc.status === "adopted") {
const User = mongoose.model("User");
// Add street to user's adoptedStreets if not already there
await User.updateOne(
{ _id: doc.adoptedBy },
{ $addToSet: { adoptedStreets: doc._id } }
);
}
});
module.exports = mongoose.model("Street", StreetSchema);