Item request notifications

This commit is contained in:
jackiettran
2025-11-18 22:28:47 -05:00
parent 026e748bf8
commit 413ac6b6e2
11 changed files with 875 additions and 224 deletions

View File

@@ -5,6 +5,8 @@ const { authenticateToken, requireAdmin, optionalAuth } = require('../middleware
const { uploadForumPostImages, uploadForumCommentImages } = require('../middleware/upload');
const logger = require('../utils/logger');
const emailServices = require('../services/email');
const googleMapsService = require('../services/googleMapsService');
const locationService = require('../services/locationService');
const router = express.Router();
// Helper function to build nested comment tree
@@ -238,7 +240,7 @@ router.get('/posts/:id', optionalAuth, async (req, res) => {
// POST /api/forum/posts - Create new post
router.post('/posts', authenticateToken, uploadForumPostImages, async (req, res) => {
try {
let { title, content, category, tags } = req.body;
let { title, content, category, tags, zipCode } = req.body;
// Parse tags if they come as JSON string (from FormData)
if (typeof tags === 'string') {
@@ -252,12 +254,42 @@ router.post('/posts', authenticateToken, uploadForumPostImages, async (req, res)
// Extract image filenames if uploaded
const images = req.files ? req.files.map(file => file.filename) : [];
// Initialize location fields
let latitude = null;
let longitude = null;
// Geocode zip code for item requests
if (category === 'item_request' && zipCode) {
try {
const geocodeResult = await googleMapsService.geocodeAddress(zipCode);
latitude = geocodeResult.latitude;
longitude = geocodeResult.longitude;
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Geocoded zip code for item request", {
zipCode,
latitude,
longitude
});
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Geocoding failed for item request", {
error: error.message,
zipCode
});
// Continue without coordinates - post will still be created
}
}
const post = await ForumPost.create({
title,
content,
category,
authorId: req.user.id,
images
images,
zipCode: zipCode || null,
latitude,
longitude
});
// Create tags if provided
@@ -295,6 +327,71 @@ router.post('/posts', authenticateToken, uploadForumPostImages, async (req, res)
});
res.status(201).json(postWithDetails);
// Send location-based notifications for item requests (asynchronously)
if (category === 'item_request' && latitude && longitude) {
(async () => {
try {
// Find all users within maximum radius (100 miles)
const nearbyUsers = await locationService.findUsersInRadius(
latitude,
longitude,
100
);
const postAuthor = await User.findByPk(req.user.id);
let notificationsSent = 0;
let usersChecked = 0;
for (const user of nearbyUsers) {
// Don't notify the requester
if (user.id !== req.user.id) {
usersChecked++;
// Get user's notification preference
const userProfile = await User.findByPk(user.id, {
attributes: ['itemRequestNotificationRadius']
});
const userPreferredRadius = userProfile?.itemRequestNotificationRadius || 10;
// Only notify if within user's preferred radius
if (parseFloat(user.distance) <= userPreferredRadius) {
try {
await emailServices.forum.sendItemRequestNotification(
user,
postAuthor,
post,
user.distance
);
notificationsSent++;
} catch (emailError) {
logger.error("Failed to send item request notification", {
error: emailError.message,
recipientId: user.id,
postId: post.id
});
}
}
}
}
logger.info("Item request notifications sent", {
postId: post.id,
totalNearbyUsers: nearbyUsers.length,
usersChecked,
notificationsSent
});
} catch (error) {
logger.error("Failed to process item request notifications", {
error: error.message,
stack: error.stack,
postId: post.id
});
}
})();
}
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Forum post creation failed", {