backend logging

This commit is contained in:
jackiettran
2025-09-22 18:38:51 -04:00
parent 6199609a4d
commit 3e76769a3e
17 changed files with 1225 additions and 110 deletions

View File

@@ -2,6 +2,7 @@ const express = require("express");
const jwt = require("jsonwebtoken");
const { OAuth2Client } = require("google-auth-library");
const { User } = require("../models"); // Import from models/index.js to get models with associations
const logger = require("../utils/logger");
const {
sanitizeInput,
validateRegistration,
@@ -84,6 +85,13 @@ router.post(
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("User registration successful", {
userId: user.id,
username: user.username,
email: user.email
});
res.status(201).json({
user: {
id: user.id,
@@ -95,7 +103,13 @@ router.post(
// Don't send token in response body for security
});
} catch (error) {
console.error("Registration error:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Registration error", {
error: error.message,
stack: error.stack,
email: req.body.email,
username: req.body.username
});
res.status(500).json({ error: "Registration failed. Please try again." });
}
}
@@ -164,6 +178,12 @@ router.post(
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("User login successful", {
userId: user.id,
email: user.email
});
res.json({
user: {
id: user.id,
@@ -175,7 +195,12 @@ router.post(
// Don't send token in response body for security
});
} catch (error) {
console.error("Login error:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Login error", {
error: error.message,
stack: error.stack,
email: req.body.email
});
res.status(500).json({ error: "Login failed. Please try again." });
}
}
@@ -271,6 +296,13 @@ router.post(
maxAge: 7 * 24 * 60 * 60 * 1000,
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Google authentication successful", {
userId: user.id,
email: user.email,
isNewUser: !user.createdAt || (Date.now() - new Date(user.createdAt).getTime()) < 1000
});
res.json({
user: {
id: user.id,
@@ -298,7 +330,12 @@ router.post(
.status(400)
.json({ error: "Malformed Google token. Please try again." });
}
console.error("Google auth error:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Google auth error", {
error: error.message,
stack: error.stack,
tokenInfo: logger.sanitize({ idToken: req.body.idToken })
});
res
.status(500)
.json({ error: "Google authentication failed. Please try again." });
@@ -341,6 +378,11 @@ router.post("/refresh", async (req, res) => {
maxAge: 15 * 60 * 1000,
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Token refresh successful", {
userId: user.id
});
res.json({
user: {
id: user.id,
@@ -351,13 +393,23 @@ router.post("/refresh", async (req, res) => {
},
});
} catch (error) {
console.error("Token refresh error:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Token refresh error", {
error: error.message,
stack: error.stack,
userId: req.user?.id
});
res.status(401).json({ error: "Invalid or expired refresh token" });
}
});
// Logout endpoint
router.post("/logout", (req, res) => {
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("User logout", {
userId: req.user?.id || 'anonymous'
});
// Clear cookies
res.clearCookie("accessToken");
res.clearCookie("refreshToken");

View File

@@ -2,6 +2,7 @@ const express = require('express');
const { Op } = require('sequelize');
const { ItemRequest, ItemRequestResponse, User, Item } = require('../models');
const { authenticateToken } = require('../middleware/auth');
const logger = require('../utils/logger');
const router = express.Router();
router.get('/', async (req, res) => {
@@ -38,6 +39,15 @@ router.get('/', async (req, res) => {
order: [['createdAt', 'DESC']]
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item requests fetched", {
search,
status,
requestsCount: count,
page: parseInt(page),
limit: parseInt(limit)
});
res.json({
requests: rows,
totalPages: Math.ceil(count / limit),
@@ -45,6 +55,12 @@ router.get('/', async (req, res) => {
totalRequests: count
});
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item requests fetch failed", {
error: error.message,
stack: error.stack,
query: req.query
});
res.status(500).json({ error: error.message });
}
});
@@ -78,8 +94,20 @@ router.get('/my-requests', authenticateToken, async (req, res) => {
order: [['createdAt', 'DESC']]
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("User item requests fetched", {
userId: req.user.id,
requestsCount: requests.length
});
res.json(requests);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("User item requests fetch failed", {
error: error.message,
stack: error.stack,
userId: req.user.id
});
res.status(500).json({ error: error.message });
}
});
@@ -115,8 +143,20 @@ router.get('/:id', async (req, res) => {
return res.status(404).json({ error: 'Item request not found' });
}
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item request fetched", {
requestId: req.params.id,
requesterId: request.requesterId
});
res.json(request);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item request fetch failed", {
error: error.message,
stack: error.stack,
requestId: req.params.id
});
res.status(500).json({ error: error.message });
}
});
@@ -138,8 +178,22 @@ router.post('/', authenticateToken, async (req, res) => {
]
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item request created", {
requestId: request.id,
requesterId: req.user.id,
title: req.body.title
});
res.status(201).json(requestWithRequester);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item request creation failed", {
error: error.message,
stack: error.stack,
requesterId: req.user.id,
requestData: logger.sanitize(req.body)
});
res.status(500).json({ error: error.message });
}
});
@@ -168,8 +222,21 @@ router.put('/:id', authenticateToken, async (req, res) => {
]
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item request updated", {
requestId: req.params.id,
requesterId: req.user.id
});
res.json(updatedRequest);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item request update failed", {
error: error.message,
stack: error.stack,
requestId: req.params.id,
requesterId: req.user.id
});
res.status(500).json({ error: error.message });
}
});
@@ -187,8 +254,22 @@ router.delete('/:id', authenticateToken, async (req, res) => {
}
await request.destroy();
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item request deleted", {
requestId: req.params.id,
requesterId: req.user.id
});
res.status(204).send();
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item request deletion failed", {
error: error.message,
stack: error.stack,
requestId: req.params.id,
requesterId: req.user.id
});
res.status(500).json({ error: error.message });
}
});
@@ -231,8 +312,22 @@ router.post('/:id/responses', authenticateToken, async (req, res) => {
]
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item request response created", {
requestId: req.params.id,
responseId: response.id,
responderId: req.user.id
});
res.status(201).json(responseWithDetails);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item request response creation failed", {
error: error.message,
stack: error.stack,
requestId: req.params.id,
responderId: req.user.id
});
res.status(500).json({ error: error.message });
}
});
@@ -277,8 +372,23 @@ router.put('/responses/:responseId/status', authenticateToken, async (req, res)
]
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item request response status updated", {
responseId: req.params.responseId,
newStatus: status,
requesterId: req.user.id,
requestFulfilled: status === 'accepted'
});
res.json(updatedResponse);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item request response status update failed", {
error: error.message,
stack: error.stack,
responseId: req.params.responseId,
requesterId: req.user.id
});
res.status(500).json({ error: error.message });
}
});

View File

@@ -2,6 +2,7 @@ const express = require("express");
const { Op } = require("sequelize");
const { Item, User, Rental } = require("../models"); // Import from models/index.js to get models with associations
const { authenticateToken } = require("../middleware/auth");
const logger = require("../utils/logger");
const router = express.Router();
router.get("/", async (req, res) => {
@@ -60,6 +61,14 @@ router.get("/", async (req, res) => {
return itemData;
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Items search completed", {
filters: { minPrice, maxPrice, city, zipCode, search },
resultsCount: count,
page: parseInt(page),
limit: parseInt(limit)
});
res.json({
items: itemsWithRoundedCoords,
totalPages: Math.ceil(count / limit),
@@ -67,6 +76,12 @@ router.get("/", async (req, res) => {
totalItems: count,
});
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Items search failed", {
error: error.message,
stack: error.stack,
query: req.query
});
res.status(500).json({ error: error.message });
}
});
@@ -87,8 +102,20 @@ router.get("/recommendations", authenticateToken, async (req, res) => {
order: [["createdAt", "DESC"]],
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Recommendations fetched", {
userId: req.user.id,
recommendationsCount: recommendations.length
});
res.json(recommendations);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Recommendations fetch failed", {
error: error.message,
stack: error.stack,
userId: req.user.id
});
res.status(500).json({ error: error.message });
}
});
@@ -120,12 +147,25 @@ router.get('/:id/reviews', async (req, res) => {
? reviews.reduce((sum, review) => sum + review.itemRating, 0) / reviews.length
: 0;
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item reviews fetched", {
itemId: req.params.id,
reviewsCount: reviews.length,
averageRating
});
res.json({
reviews,
averageRating,
totalReviews: reviews.length
});
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item reviews fetch failed", {
error: error.message,
stack: error.stack,
itemId: req.params.id
});
res.status(500).json({ error: error.message });
}
});
@@ -155,8 +195,20 @@ router.get("/:id", async (req, res) => {
itemResponse.longitude = Math.round(parseFloat(itemResponse.longitude) * 100) / 100;
}
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item fetched", {
itemId: req.params.id,
ownerId: item.ownerId
});
res.json(itemResponse);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item fetch failed", {
error: error.message,
stack: error.stack,
itemId: req.params.id
});
res.status(500).json({ error: error.message });
}
});
@@ -178,8 +230,22 @@ router.post("/", authenticateToken, async (req, res) => {
],
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item created", {
itemId: item.id,
ownerId: req.user.id,
itemName: req.body.name
});
res.status(201).json(itemWithOwner);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item creation failed", {
error: error.message,
stack: error.stack,
ownerId: req.user.id,
itemData: logger.sanitize(req.body)
});
res.status(500).json({ error: error.message });
}
});
@@ -208,8 +274,21 @@ router.put("/:id", authenticateToken, async (req, res) => {
],
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item updated", {
itemId: req.params.id,
ownerId: req.user.id
});
res.json(updatedItem);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item update failed", {
error: error.message,
stack: error.stack,
itemId: req.params.id,
ownerId: req.user.id
});
res.status(500).json({ error: error.message });
}
});
@@ -227,8 +306,22 @@ router.delete("/:id", authenticateToken, async (req, res) => {
}
await item.destroy();
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Item deleted", {
itemId: req.params.id,
ownerId: req.user.id
});
res.status(204).send();
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Item deletion failed", {
error: error.message,
stack: error.stack,
itemId: req.params.id,
ownerId: req.user.id
});
res.status(500).json({ error: error.message });
}
});

View File

@@ -3,6 +3,7 @@ const router = express.Router();
const { authenticateToken } = require("../middleware/auth");
const rateLimiter = require("../middleware/rateLimiter");
const googleMapsService = require("../services/googleMapsService");
const logger = require("../utils/logger");
// Input validation middleware
const validateInput = (req, res, next) => {
@@ -34,8 +35,12 @@ const validateInput = (req, res, next) => {
};
// Error handling middleware
const handleServiceError = (error, res) => {
console.error("Maps service error:", error.message);
const handleServiceError = (error, res, req) => {
const reqLogger = logger.withRequestId(req?.id);
reqLogger.error("Maps service error", {
error: error.message,
stack: error.stack
});
if (error.message.includes("API key not configured")) {
return res.status(503).json({
@@ -87,17 +92,16 @@ router.post(
);
// Log request for monitoring (without sensitive data)
console.log(
`Places Autocomplete: user=${
req.user?.id || "anonymous"
}, query_length=${input.length}, results=${
result.predictions?.length || 0
}`
);
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Places Autocomplete request", {
userId: req.user?.id || "anonymous",
queryLength: input.length,
resultsCount: result.predictions?.length || 0
});
res.json(result);
} catch (error) {
handleServiceError(error, res);
handleServiceError(error, res, req);
}
}
);
@@ -127,15 +131,15 @@ router.post(
const result = await googleMapsService.getPlaceDetails(placeId, options);
// Log request for monitoring
console.log(
`Place Details: user=${
req.user?.id || "anonymous"
}, placeId=${placeId.substring(0, 10)}...`
);
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Place Details request", {
userId: req.user?.id || "anonymous",
placeIdPrefix: placeId.substring(0, 10) + "..."
});
res.json(result);
} catch (error) {
handleServiceError(error, res);
handleServiceError(error, res, req);
}
}
);
@@ -165,15 +169,15 @@ router.post(
const result = await googleMapsService.geocodeAddress(address, options);
// Log request for monitoring
console.log(
`Geocoding: user=${req.user?.id || "anonymous"}, address_length=${
address.length
}`
);
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Geocoding request", {
userId: req.user?.id || "anonymous",
addressLength: address.length
});
res.json(result);
} catch (error) {
handleServiceError(error, res);
handleServiceError(error, res, req);
}
}
);

View File

@@ -1,6 +1,7 @@
const express = require('express');
const { Message, User } = require('../models');
const { authenticateToken } = require('../middleware/auth');
const logger = require('../utils/logger');
const router = express.Router();
// Get all messages for the current user (inbox)
@@ -17,8 +18,21 @@ router.get('/', authenticateToken, async (req, res) => {
],
order: [['createdAt', 'DESC']]
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Messages inbox fetched", {
userId: req.user.id,
messageCount: messages.length
});
res.json(messages);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Messages inbox fetch failed", {
error: error.message,
stack: error.stack,
userId: req.user.id
});
res.status(500).json({ error: error.message });
}
});
@@ -37,8 +51,21 @@ router.get('/sent', authenticateToken, async (req, res) => {
],
order: [['createdAt', 'DESC']]
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Sent messages fetched", {
userId: req.user.id,
messageCount: messages.length
});
res.json(messages);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Sent messages fetch failed", {
error: error.message,
stack: error.stack,
userId: req.user.id
});
res.status(500).json({ error: error.message });
}
});
@@ -86,8 +113,22 @@ router.get('/:id', authenticateToken, async (req, res) => {
await message.update({ isRead: true });
}
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Message fetched", {
userId: req.user.id,
messageId: req.params.id,
markedAsRead: message.receiverId === req.user.id && !message.isRead
});
res.json(message);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Message fetch failed", {
error: error.message,
stack: error.stack,
userId: req.user.id,
messageId: req.params.id
});
res.status(500).json({ error: error.message });
}
});
@@ -124,8 +165,23 @@ router.post('/', authenticateToken, async (req, res) => {
}]
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Message sent", {
senderId: req.user.id,
receiverId: receiverId,
messageId: message.id,
isReply: !!parentMessageId
});
res.status(201).json(messageWithSender);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Message send failed", {
error: error.message,
stack: error.stack,
senderId: req.user.id,
receiverId: req.body.receiverId
});
res.status(500).json({ error: error.message });
}
});
@@ -145,8 +201,22 @@ router.put('/:id/read', authenticateToken, async (req, res) => {
}
await message.update({ isRead: true });
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Message marked as read", {
userId: req.user.id,
messageId: req.params.id
});
res.json(message);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Message mark as read failed", {
error: error.message,
stack: error.stack,
userId: req.user.id,
messageId: req.params.id
});
res.status(500).json({ error: error.message });
}
});
@@ -160,8 +230,20 @@ router.get('/unread/count', authenticateToken, async (req, res) => {
isRead: false
}
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Unread message count fetched", {
userId: req.user.id,
unreadCount: count
});
res.json({ count });
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Unread message count fetch failed", {
error: error.message,
stack: error.stack,
userId: req.user.id
});
res.status(500).json({ error: error.message });
}
});

View File

@@ -4,6 +4,7 @@ const { Rental, Item, User } = require("../models"); // Import from models/index
const { authenticateToken } = require("../middleware/auth");
const FeeCalculator = require("../utils/feeCalculator");
const RefundService = require("../services/refundService");
const logger = require("../utils/logger");
const router = express.Router();
// Helper function to check and update review visibility
@@ -67,7 +68,12 @@ router.get("/my-rentals", authenticateToken, async (req, res) => {
res.json(rentals);
} catch (error) {
console.error("Error in my-rentals route:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Error in my-rentals route", {
error: error.message,
stack: error.stack,
userId: req.user.id
});
res.status(500).json({ error: "Failed to fetch rentals" });
}
});
@@ -90,7 +96,12 @@ router.get("/my-listings", authenticateToken, async (req, res) => {
res.json(rentals);
} catch (error) {
console.error("Error in my-listings route:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Error in my-listings route", {
error: error.message,
stack: error.stack,
userId: req.user.id
});
res.status(500).json({ error: "Failed to fetch listings" });
}
});
@@ -125,7 +136,13 @@ router.get("/:id", authenticateToken, async (req, res) => {
res.json(rental);
} catch (error) {
console.error("Error fetching rental:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Error fetching rental", {
error: error.message,
stack: error.stack,
rentalId: req.params.id,
userId: req.user.id
});
res.status(500).json({ error: "Failed to fetch rental" });
}
});
@@ -355,7 +372,13 @@ router.put("/:id/status", authenticateToken, async (req, res) => {
res.json(updatedRental);
return;
} catch (paymentError) {
console.error("Payment failed during approval:", paymentError);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Payment failed during approval", {
error: paymentError.message,
stack: paymentError.stack,
rentalId: req.params.id,
userId: req.user.id
});
// Keep rental as pending, but inform of payment failure
return res.status(400).json({
error: "Payment failed during approval",
@@ -538,7 +561,15 @@ router.post("/calculate-fees", authenticateToken, async (req, res) => {
display: displayFees,
});
} catch (error) {
console.error("Error calculating fees:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Error calculating fees", {
error: error.message,
stack: error.stack,
userId: req.user.id,
startDate: req.query.startDate,
endDate: req.query.endDate,
itemId: req.query.itemId
});
res.status(500).json({ error: "Failed to calculate fees" });
}
});
@@ -566,7 +597,12 @@ router.get("/earnings/status", authenticateToken, async (req, res) => {
res.json(ownerRentals);
} catch (error) {
console.error("Error getting earnings status:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Error getting earnings status", {
error: error.message,
stack: error.stack,
userId: req.user.id
});
res.status(500).json({ error: error.message });
}
});
@@ -580,7 +616,13 @@ router.get("/:id/refund-preview", authenticateToken, async (req, res) => {
);
res.json(preview);
} catch (error) {
console.error("Error getting refund preview:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Error getting refund preview", {
error: error.message,
stack: error.stack,
rentalId: req.params.id,
userId: req.user.id
});
res.status(400).json({ error: error.message });
}
});
@@ -618,7 +660,13 @@ router.post("/:id/cancel", authenticateToken, async (req, res) => {
refund: result.refund,
});
} catch (error) {
console.error("Error cancelling rental:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Error cancelling rental", {
error: error.message,
stack: error.stack,
rentalId: req.params.id,
userId: req.user.id
});
res.status(400).json({ error: error.message });
}
});

View File

@@ -2,6 +2,7 @@ const express = require("express");
const { authenticateToken } = require("../middleware/auth");
const { User, Item } = require("../models");
const StripeService = require("../services/stripeService");
const logger = require("../utils/logger");
const router = express.Router();
// Get checkout session status
@@ -11,6 +12,14 @@ router.get("/checkout-session/:sessionId", async (req, res) => {
const session = await StripeService.getCheckoutSession(sessionId);
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Stripe checkout session retrieved", {
sessionId: sessionId,
status: session.status,
payment_status: session.payment_status,
metadata: session.metadata,
});
res.json({
status: session.status,
payment_status: session.payment_status,
@@ -19,7 +28,12 @@ router.get("/checkout-session/:sessionId", async (req, res) => {
metadata: session.metadata,
});
} catch (error) {
console.error("Error retrieving checkout session:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Stripe checkout session retrieval failed", {
error: error.message,
stack: error.stack,
sessionId: sessionId,
});
res.status(500).json({ error: error.message });
}
});
@@ -51,12 +65,23 @@ router.post("/accounts", authenticateToken, async (req, res) => {
stripeConnectedAccountId: account.id,
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Stripe connected account created", {
userId: req.user.id,
stripeConnectedAccountId: account.id,
});
res.json({
stripeConnectedAccountId: account.id,
success: true,
});
} catch (error) {
console.error("Error creating connected account:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Stripe connected account creation failed", {
error: error.message,
stack: error.stack,
userId: req.user.id,
});
res.status(500).json({ error: error.message });
}
});
@@ -84,12 +109,25 @@ router.post("/account-links", authenticateToken, async (req, res) => {
returnUrl
);
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Stripe account link created", {
userId: req.user.id,
stripeConnectedAccountId: user.stripeConnectedAccountId,
expiresAt: accountLink.expires_at,
});
res.json({
url: accountLink.url,
expiresAt: accountLink.expires_at,
});
} catch (error) {
console.error("Error creating account link:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Stripe account link creation failed", {
error: error.message,
stack: error.stack,
userId: req.user.id,
stripeConnectedAccountId: user?.stripeConnectedAccountId,
});
res.status(500).json({ error: error.message });
}
});
@@ -107,6 +145,14 @@ router.get("/account-status", authenticateToken, async (req, res) => {
user.stripeConnectedAccountId
);
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Stripe account status retrieved", {
userId: req.user.id,
stripeConnectedAccountId: user.stripeConnectedAccountId,
detailsSubmitted: accountStatus.details_submitted,
payoutsEnabled: accountStatus.payouts_enabled,
});
res.json({
accountId: accountStatus.id,
detailsSubmitted: accountStatus.details_submitted,
@@ -115,59 +161,85 @@ router.get("/account-status", authenticateToken, async (req, res) => {
requirements: accountStatus.requirements,
});
} catch (error) {
console.error("Error getting account status:", error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Stripe account status retrieval failed", {
error: error.message,
stack: error.stack,
userId: req.user.id,
stripeConnectedAccountId: user?.stripeConnectedAccountId,
});
res.status(500).json({ error: error.message });
}
});
// Create embedded setup checkout session for collecting payment method
router.post("/create-setup-checkout-session", authenticateToken, async (req, res) => {
try {
const { rentalData } = req.body;
router.post(
"/create-setup-checkout-session",
authenticateToken,
async (req, res) => {
try {
const { rentalData } = req.body;
const user = await User.findByPk(req.user.id);
if (!user) {
return res.status(404).json({ error: "User not found" });
}
const user = await User.findByPk(req.user.id);
// Create or get Stripe customer
let stripeCustomerId = user.stripeCustomerId;
if (!stripeCustomerId) {
// Create new Stripe customer
const customer = await StripeService.createCustomer({
email: user.email,
name: `${user.firstName} ${user.lastName}`,
metadata: {
userId: user.id.toString()
}
if (!user) {
return res.status(404).json({ error: "User not found" });
}
// Create or get Stripe customer
let stripeCustomerId = user.stripeCustomerId;
if (!stripeCustomerId) {
// Create new Stripe customer
const customer = await StripeService.createCustomer({
email: user.email,
name: `${user.firstName} ${user.lastName}`,
metadata: {
userId: user.id.toString(),
},
});
stripeCustomerId = customer.id;
// Save customer ID to user record
await user.update({ stripeCustomerId });
}
// Add rental data to metadata if provided
const metadata = rentalData
? {
rentalData: JSON.stringify(rentalData),
}
: {};
const session = await StripeService.createSetupCheckoutSession({
customerId: stripeCustomerId,
metadata,
});
stripeCustomerId = customer.id;
// Save customer ID to user record
await user.update({ stripeCustomerId });
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Stripe setup checkout session created", {
userId: req.user.id,
stripeCustomerId: stripeCustomerId,
sessionId: session.id,
hasRentalData: !!rentalData,
});
res.json({
clientSecret: session.client_secret,
sessionId: session.id,
});
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Stripe setup checkout session creation failed", {
error: error.message,
stack: error.stack,
userId: req.user.id,
stripeCustomerId: user?.stripeCustomerId,
});
res.status(500).json({ error: error.message });
}
// Add rental data to metadata if provided
const metadata = rentalData ? {
rentalData: JSON.stringify(rentalData)
} : {};
const session = await StripeService.createSetupCheckoutSession({
customerId: stripeCustomerId,
metadata
});
res.json({
clientSecret: session.client_secret,
sessionId: session.id
});
} catch (error) {
console.error("Error creating setup checkout session:", error);
res.status(500).json({ error: error.message });
}
});
);
module.exports = router;

View File

@@ -2,6 +2,7 @@ const express = require('express');
const { User, UserAddress } = require('../models'); // Import from models/index.js to get models with associations
const { authenticateToken } = require('../middleware/auth');
const { uploadProfileImage } = require('../middleware/upload');
const logger = require('../utils/logger');
const fs = require('fs').promises;
const path = require('path');
const router = express.Router();
@@ -11,8 +12,20 @@ router.get('/profile', authenticateToken, async (req, res) => {
const user = await User.findByPk(req.user.id, {
attributes: { exclude: ['password'] }
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("User profile fetched", {
userId: req.user.id
});
res.json(user);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("User profile fetch failed", {
error: error.message,
stack: error.stack,
userId: req.user.id
});
res.status(500).json({ error: error.message });
}
});
@@ -24,8 +37,20 @@ router.get('/addresses', authenticateToken, async (req, res) => {
where: { userId: req.user.id },
order: [['isPrimary', 'DESC'], ['createdAt', 'ASC']]
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("User addresses fetched", {
userId: req.user.id,
addressCount: addresses.length
});
res.json(addresses);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("User addresses fetch failed", {
error: error.message,
stack: error.stack,
userId: req.user.id
});
res.status(500).json({ error: error.message });
}
});
@@ -36,8 +61,21 @@ router.post('/addresses', authenticateToken, async (req, res) => {
...req.body,
userId: req.user.id
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("User address created", {
userId: req.user.id,
addressId: address.id
});
res.status(201).json(address);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("User address creation failed", {
error: error.message,
stack: error.stack,
userId: req.user.id,
addressData: logger.sanitize(req.body)
});
res.status(500).json({ error: error.message });
}
});
@@ -55,8 +93,22 @@ router.put('/addresses/:id', authenticateToken, async (req, res) => {
}
await address.update(req.body);
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("User address updated", {
userId: req.user.id,
addressId: req.params.id
});
res.json(address);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("User address update failed", {
error: error.message,
stack: error.stack,
userId: req.user.id,
addressId: req.params.id
});
res.status(500).json({ error: error.message });
}
});
@@ -74,8 +126,22 @@ router.delete('/addresses/:id', authenticateToken, async (req, res) => {
}
await address.destroy();
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("User address deleted", {
userId: req.user.id,
addressId: req.params.id
});
res.status(204).send();
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("User address deletion failed", {
error: error.message,
stack: error.stack,
userId: req.user.id,
addressId: req.params.id
});
res.status(500).json({ error: error.message });
}
});
@@ -121,13 +187,24 @@ router.get('/:id', async (req, res) => {
const user = await User.findByPk(req.params.id, {
attributes: { exclude: ['password', 'email', 'phone', 'address'] }
});
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Public user profile fetched", {
requestedUserId: req.params.id
});
res.json(user);
} catch (error) {
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Public user profile fetch failed", {
error: error.message,
stack: error.stack,
requestedUserId: req.params.id
});
res.status(500).json({ error: error.message });
}
});
@@ -185,7 +262,11 @@ router.put('/profile', authenticateToken, async (req, res) => {
router.post('/profile/image', authenticateToken, (req, res) => {
uploadProfileImage(req, res, async (err) => {
if (err) {
console.error('Upload error:', err);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Profile image upload error", {
error: err.message,
userId: req.user.id
});
return res.status(400).json({ error: err.message });
}
@@ -201,7 +282,12 @@ router.post('/profile/image', authenticateToken, (req, res) => {
try {
await fs.unlink(oldImagePath);
} catch (unlinkErr) {
console.error('Error deleting old image:', unlinkErr);
const reqLogger = logger.withRequestId(req.id);
reqLogger.warn("Error deleting old profile image", {
error: unlinkErr.message,
userId: req.user.id,
oldImagePath
});
}
}
@@ -210,13 +296,24 @@ router.post('/profile/image', authenticateToken, (req, res) => {
profileImage: req.file.filename
});
const reqLogger = logger.withRequestId(req.id);
reqLogger.info("Profile image uploaded successfully", {
userId: req.user.id,
filename: req.file.filename
});
res.json({
message: 'Profile image uploaded successfully',
filename: req.file.filename,
imageUrl: `/uploads/profiles/${req.file.filename}`
});
} catch (error) {
console.error('Database update error:', error);
const reqLogger = logger.withRequestId(req.id);
reqLogger.error("Profile image database update failed", {
error: error.message,
stack: error.stack,
userId: req.user.id
});
res.status(500).json({ error: 'Failed to update profile image' });
}
});