diff --git a/backend/routes/forum.js b/backend/routes/forum.js
index 52379b5..ac75b73 100644
--- a/backend/routes/forum.js
+++ b/backend/routes/forum.js
@@ -240,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, zipCode } = req.body;
+ let { title, content, category, tags, zipCode, latitude: providedLat, longitude: providedLng } = req.body;
// Parse tags if they come as JSON string (from FormData)
if (typeof tags === 'string') {
@@ -258,26 +258,53 @@ router.post('/posts', authenticateToken, uploadForumPostImages, async (req, res)
let latitude = null;
let longitude = null;
- // Geocode zip code for item requests
+ // Use provided coordinates if available, otherwise geocode zip code
if (category === 'item_request' && zipCode) {
- try {
- const geocodeResult = await googleMapsService.geocodeAddress(zipCode);
- latitude = geocodeResult.latitude;
- longitude = geocodeResult.longitude;
+ // If coordinates were provided from a saved address, use them directly
+ if (providedLat && providedLng) {
+ latitude = parseFloat(providedLat);
+ longitude = parseFloat(providedLng);
const reqLogger = logger.withRequestId(req.id);
- reqLogger.info("Geocoded zip code for item request", {
+ reqLogger.info("Using provided coordinates for item request", {
zipCode,
latitude,
- longitude
+ longitude,
+ source: 'saved_address'
});
- } 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
+ } else {
+ // Otherwise, geocode the zip code
+ try {
+ const geocodeResult = await googleMapsService.geocodeAddress(zipCode);
+
+ // Check if geocoding was successful
+ if (geocodeResult.error) {
+ const reqLogger = logger.withRequestId(req.id);
+ reqLogger.error("Geocoding failed for item request", {
+ error: geocodeResult.error,
+ status: geocodeResult.status,
+ zipCode
+ });
+ } else if (geocodeResult.latitude && geocodeResult.longitude) {
+ latitude = geocodeResult.latitude;
+ longitude = geocodeResult.longitude;
+
+ const reqLogger = logger.withRequestId(req.id);
+ reqLogger.info("Geocoded zip code for item request", {
+ zipCode,
+ latitude,
+ longitude,
+ source: 'geocoded'
+ });
+ }
+ } 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
+ }
}
}
@@ -332,6 +359,13 @@ router.post('/posts', authenticateToken, uploadForumPostImages, async (req, res)
if (category === 'item_request' && latitude && longitude) {
(async () => {
try {
+ logger.info("Starting item request notifications", {
+ postId: post.id,
+ latitude,
+ longitude,
+ zipCode
+ });
+
// Find all users within maximum radius (100 miles)
const nearbyUsers = await locationService.findUsersInRadius(
latitude,
@@ -339,10 +373,17 @@ router.post('/posts', authenticateToken, uploadForumPostImages, async (req, res)
100
);
+ logger.info("Found nearby users", {
+ postId: post.id,
+ count: nearbyUsers.length,
+ users: nearbyUsers.map(u => ({ id: u.id, distance: u.distance }))
+ });
+
const postAuthor = await User.findByPk(req.user.id);
let notificationsSent = 0;
let usersChecked = 0;
+ let usersSkipped = 0;
for (const user of nearbyUsers) {
// Don't notify the requester
@@ -356,6 +397,17 @@ router.post('/posts', authenticateToken, uploadForumPostImages, async (req, res)
const userPreferredRadius = userProfile?.itemRequestNotificationRadius || 10;
+ logger.info("Checking user notification eligibility", {
+ postId: post.id,
+ userId: user.id,
+ userEmail: user.email,
+ userCoordinates: { lat: user.latitude, lng: user.longitude },
+ postCoordinates: { lat: latitude, lng: longitude },
+ userDistance: user.distance,
+ userPreferredRadius,
+ willNotify: parseFloat(user.distance) <= userPreferredRadius
+ });
+
// Only notify if within user's preferred radius
if (parseFloat(user.distance) <= userPreferredRadius) {
try {
@@ -366,6 +418,11 @@ router.post('/posts', authenticateToken, uploadForumPostImages, async (req, res)
user.distance
);
notificationsSent++;
+ logger.info("Sent notification to user", {
+ postId: post.id,
+ userId: user.id,
+ distance: user.distance
+ });
} catch (emailError) {
logger.error("Failed to send item request notification", {
error: emailError.message,
@@ -373,14 +430,17 @@ router.post('/posts', authenticateToken, uploadForumPostImages, async (req, res)
postId: post.id
});
}
+ } else {
+ usersSkipped++;
}
}
}
- logger.info("Item request notifications sent", {
+ logger.info("Item request notifications complete", {
postId: post.id,
totalNearbyUsers: nearbyUsers.length,
usersChecked,
+ usersSkipped,
notificationsSent
});
} catch (error) {
@@ -391,6 +451,13 @@ router.post('/posts', authenticateToken, uploadForumPostImages, async (req, res)
});
}
})();
+ } else if (category === 'item_request') {
+ logger.warn("Item request created without location", {
+ postId: post.id,
+ zipCode,
+ hasLatitude: !!latitude,
+ hasLongitude: !!longitude
+ });
}
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
diff --git a/backend/services/locationService.js b/backend/services/locationService.js
index 18c76de..1c5dc72 100644
--- a/backend/services/locationService.js
+++ b/backend/services/locationService.js
@@ -20,6 +20,12 @@ class LocationService {
throw new Error('Radius must be between 1 and 100 miles');
}
+ console.log('Finding users in radius:', {
+ centerLatitude: latitude,
+ centerLongitude: longitude,
+ radiusMiles
+ });
+
try {
// Haversine formula:
// distance = 3959 * acos(cos(radians(lat1)) * cos(radians(lat2))
@@ -27,26 +33,28 @@ class LocationService {
// + sin(radians(lat1)) * sin(radians(lat2)))
// Note: 3959 is Earth's radius in miles
const query = `
- SELECT
- u.id,
- u.email,
- u."firstName",
- u."lastName",
- ua.latitude,
- ua.longitude,
- (3959 * acos(
- LEAST(1.0,
- cos(radians(:lat)) * cos(radians(ua.latitude))
- * cos(radians(ua.longitude) - radians(:lng))
- + sin(radians(:lat)) * sin(radians(ua.latitude))
- )
- )) AS distance
- FROM "Users" u
- INNER JOIN "UserAddresses" ua ON u.id = ua."userId"
- WHERE ua."isPrimary" = true
- AND ua.latitude IS NOT NULL
- AND ua.longitude IS NOT NULL
- HAVING distance < :radiusMiles
+ SELECT * FROM (
+ SELECT
+ u.id,
+ u.email,
+ u."firstName",
+ u."lastName",
+ ua.latitude,
+ ua.longitude,
+ (3959 * acos(
+ LEAST(1.0,
+ cos(radians(:lat)) * cos(radians(ua.latitude))
+ * cos(radians(ua.longitude) - radians(:lng))
+ + sin(radians(:lat)) * sin(radians(ua.latitude))
+ )
+ )) AS distance
+ FROM "Users" u
+ INNER JOIN "UserAddresses" ua ON u.id = ua."userId"
+ WHERE ua."isPrimary" = true
+ AND ua.latitude IS NOT NULL
+ AND ua.longitude IS NOT NULL
+ ) AS user_distances
+ WHERE distance < :radiusMiles
ORDER BY distance ASC
`;
@@ -59,6 +67,13 @@ class LocationService {
type: QueryTypes.SELECT
});
+ console.log('Users found in radius:', users.map(u => ({
+ id: u.id,
+ userLat: u.latitude,
+ userLng: u.longitude,
+ distance: parseFloat(u.distance).toFixed(2)
+ })));
+
return users.map(user => ({
id: user.id,
email: user.email,
diff --git a/backend/templates/emails/forumItemRequestNotification.html b/backend/templates/emails/forumItemRequestNotification.html
index 4fe6692..a02733a 100644
--- a/backend/templates/emails/forumItemRequestNotification.html
+++ b/backend/templates/emails/forumItemRequestNotification.html
@@ -1,285 +1,295 @@
-
-
-
-
+
+
+
+
Item Request Near You
-
-
+
+
-