diff --git a/backend/controllers/households.controller.js b/backend/controllers/households.controller.js index 22c08dd..07b3f02 100644 --- a/backend/controllers/households.controller.js +++ b/backend/controllers/households.controller.js @@ -1,6 +1,7 @@ const householdModel = require("../models/household.model"); const { sendError } = require("../utils/http"); -const { inviteCodeLast4, safeErrorMessage } = require("../utils/redaction"); +const { inviteCodeLast4 } = require("../utils/redaction"); +const { logError } = require("../utils/logger"); // Get all households user belongs to exports.getUserHouseholds = async (req, res) => { @@ -8,7 +9,7 @@ exports.getUserHouseholds = async (req, res) => { const households = await householdModel.getUserHouseholds(req.user.id); res.json(households); } catch (error) { - console.error("Get user households error:", error); + logError(req, "households.getUserHouseholds", error); sendError(res, 500, "Failed to fetch households"); } }; @@ -27,7 +28,7 @@ exports.getHousehold = async (req, res) => { res.json(household); } catch (error) { - console.error("Get household error:", error); + logError(req, "households.getHousehold", error); sendError(res, 500, "Failed to fetch household"); } }; @@ -55,7 +56,7 @@ exports.createHousehold = async (req, res) => { household }); } catch (error) { - console.error("Create household error:", error); + logError(req, "households.createHousehold", error); sendError(res, 500, "Failed to create household"); } }; @@ -83,7 +84,7 @@ exports.updateHousehold = async (req, res) => { household }); } catch (error) { - console.error("Update household error:", error); + logError(req, "households.updateHousehold", error); sendError(res, 500, "Failed to update household"); } }; @@ -94,7 +95,7 @@ exports.deleteHousehold = async (req, res) => { await householdModel.deleteHousehold(req.params.householdId); res.json({ message: "Household deleted successfully" }); } catch (error) { - console.error("Delete household error:", error); + logError(req, "households.deleteHousehold", error); sendError(res, 500, "Failed to delete household"); } }; @@ -108,11 +109,9 @@ exports.refreshInviteCode = async (req, res) => { household }); } catch (error) { - console.error( - `Refresh invite code error request_id=${req.request_id} invite_last4=${inviteCodeLast4( - req.body?.inviteCode - )} message=${safeErrorMessage(error)}` - ); + logError(req, "households.refreshInviteCode", error, { + invite_last4: inviteCodeLast4(req.body?.inviteCode), + }); sendError(res, 500, "Failed to refresh invite code"); } }; @@ -144,11 +143,7 @@ exports.joinHousehold = async (req, res) => { household: { id: result.id, name: result.name } }); } catch (error) { - console.error( - `Join household error request_id=${req.request_id} invite_last4=${inviteLast4} message=${safeErrorMessage( - error - )}` - ); + logError(req, "households.joinHousehold", error, { invite_last4: inviteLast4 }); sendError(res, 500, "Failed to join household"); } }; @@ -159,7 +154,7 @@ exports.getMembers = async (req, res) => { const members = await householdModel.getHouseholdMembers(req.params.householdId); res.json(members); } catch (error) { - console.error("Get members error:", error); + logError(req, "households.getMembers", error); sendError(res, 500, "Failed to fetch members"); } }; @@ -190,7 +185,7 @@ exports.updateMemberRole = async (req, res) => { member: updated }); } catch (error) { - console.error("Update member role error:", error); + logError(req, "households.updateMemberRole", error); sendError(res, 500, "Failed to update member role"); } }; @@ -210,7 +205,7 @@ exports.removeMember = async (req, res) => { res.json({ message: "Member removed successfully" }); } catch (error) { - console.error("Remove member error:", error); + logError(req, "households.removeMember", error); sendError(res, 500, "Failed to remove member"); } }; diff --git a/backend/controllers/lists.controller.js b/backend/controllers/lists.controller.js index 674bfa9..90b92bf 100644 --- a/backend/controllers/lists.controller.js +++ b/backend/controllers/lists.controller.js @@ -1,6 +1,7 @@ const List = require("../models/list.model"); const { isValidItemType, isValidItemGroup, isValidZone } = require("../constants/classifications"); const { sendError } = require("../utils/http"); +const { logError } = require("../utils/logger"); exports.getList = async (req, res) => { @@ -114,7 +115,7 @@ exports.updateItemWithClassification = async (req, res) => { res.json({ message: "Item updated successfully" }); } catch (error) { - console.error("Error updating item with classification:", error); + logError(req, "listsLegacy.updateItemWithClassification", error); sendError(res, 500, "Failed to update item"); } }; diff --git a/backend/controllers/lists.controller.v2.js b/backend/controllers/lists.controller.v2.js index f232057..11b66b1 100644 --- a/backend/controllers/lists.controller.v2.js +++ b/backend/controllers/lists.controller.v2.js @@ -1,6 +1,7 @@ const List = require("../models/list.model.v2"); const { isValidItemType, isValidItemGroup, isValidZone } = require("../constants/classifications"); const { sendError } = require("../utils/http"); +const { logError } = require("../utils/logger"); /** * Get list items for household and store @@ -12,7 +13,7 @@ exports.getList = async (req, res) => { const items = await List.getHouseholdStoreList(householdId, storeId); res.json({ items }); } catch (error) { - console.error("Error getting list:", error); + logError(req, "listsV2.getList", error); sendError(res, 500, "Failed to get list"); } }; @@ -37,7 +38,7 @@ exports.getItemByName = async (req, res) => { res.json(item); } catch (error) { - console.error("Error getting item:", error); + logError(req, "listsV2.getItemByName", error); sendError(res, 500, "Failed to get item"); } }; @@ -84,7 +85,7 @@ exports.addItem = async (req, res) => { } }); } catch (error) { - console.error("Error adding item:", error); + logError(req, "listsV2.addItem", error); sendError(res, 500, "Failed to add item"); } }; @@ -109,7 +110,7 @@ exports.markBought = async (req, res) => { res.json({ message: bought ? "Item marked as bought" : "Item unmarked" }); } catch (error) { - console.error("Error marking bought:", error); + logError(req, "listsV2.markBought", error); sendError(res, 500, "Failed to update item"); } }; @@ -146,7 +147,7 @@ exports.updateItem = async (req, res) => { } }); } catch (error) { - console.error("Error updating item:", error); + logError(req, "listsV2.updateItem", error); sendError(res, 500, "Failed to update item"); } }; @@ -174,7 +175,7 @@ exports.deleteItem = async (req, res) => { res.json({ message: "Item deleted" }); } catch (error) { - console.error("Error deleting item:", error); + logError(req, "listsV2.deleteItem", error); sendError(res, 500, "Failed to delete item"); } }; @@ -191,7 +192,7 @@ exports.getSuggestions = async (req, res) => { const suggestions = await List.getSuggestions(query || "", householdId, storeId); res.json(suggestions); } catch (error) { - console.error("Error getting suggestions:", error); + logError(req, "listsV2.getSuggestions", error); sendError(res, 500, "Failed to get suggestions"); } }; @@ -206,7 +207,7 @@ exports.getRecentlyBought = async (req, res) => { const items = await List.getRecentlyBoughtItems(householdId, storeId); res.json(items); } catch (error) { - console.error("Error getting recent items:", error); + logError(req, "listsV2.getRecentlyBought", error); sendError(res, 500, "Failed to get recent items"); } }; @@ -233,7 +234,7 @@ exports.getClassification = async (req, res) => { const classification = await List.getClassification(householdId, item.item_id); res.json({ classification }); } catch (error) { - console.error("Error getting classification:", error); + logError(req, "listsV2.getClassification", error); sendError(res, 500, "Failed to get classification"); } }; @@ -290,7 +291,7 @@ exports.setClassification = async (req, res) => { res.json({ message: "Classification set", classification }); } catch (error) { - console.error("Error setting classification:", error); + logError(req, "listsV2.setClassification", error); sendError(res, 500, "Failed to set classification"); } }; @@ -318,7 +319,7 @@ exports.updateItemImage = async (req, res) => { res.json({ message: "Image updated successfully" }); } catch (error) { - console.error("Error updating image:", error); + logError(req, "listsV2.updateItemImage", error); sendError(res, 500, "Failed to update image"); } }; diff --git a/backend/controllers/stores.controller.js b/backend/controllers/stores.controller.js index 711f157..6e4adee 100644 --- a/backend/controllers/stores.controller.js +++ b/backend/controllers/stores.controller.js @@ -1,5 +1,6 @@ const storeModel = require("../models/store.model"); const { sendError } = require("../utils/http"); +const { logError } = require("../utils/logger"); // Get all available stores exports.getAllStores = async (req, res) => { @@ -7,7 +8,7 @@ exports.getAllStores = async (req, res) => { const stores = await storeModel.getAllStores(); res.json(stores); } catch (error) { - console.error("Get all stores error:", error); + logError(req, "stores.getAllStores", error); sendError(res, 500, "Failed to fetch stores"); } }; @@ -18,7 +19,7 @@ exports.getHouseholdStores = async (req, res) => { const stores = await storeModel.getHouseholdStores(req.params.householdId); res.json(stores); } catch (error) { - console.error("Get household stores error:", error); + logError(req, "stores.getHouseholdStores", error); sendError(res, 500, "Failed to fetch household stores"); } }; @@ -48,7 +49,7 @@ exports.addStoreToHousehold = async (req, res) => { store }); } catch (error) { - console.error("Add store to household error:", error); + logError(req, "stores.addStoreToHousehold", error); sendError(res, 500, "Failed to add store to household"); } }; @@ -63,7 +64,7 @@ exports.removeStoreFromHousehold = async (req, res) => { res.json({ message: "Store removed from household successfully" }); } catch (error) { - console.error("Remove store from household error:", error); + logError(req, "stores.removeStoreFromHousehold", error); sendError(res, 500, "Failed to remove store from household"); } }; @@ -78,7 +79,7 @@ exports.setDefaultStore = async (req, res) => { res.json({ message: "Default store updated successfully" }); } catch (error) { - console.error("Set default store error:", error); + logError(req, "stores.setDefaultStore", error); sendError(res, 500, "Failed to set default store"); } }; @@ -99,7 +100,7 @@ exports.createStore = async (req, res) => { store }); } catch (error) { - console.error("Create store error:", error); + logError(req, "stores.createStore", error); if (error.code === '23505') { // Unique violation return sendError(res, 400, "Store with this name already exists"); } @@ -126,7 +127,7 @@ exports.updateStore = async (req, res) => { store }); } catch (error) { - console.error("Update store error:", error); + logError(req, "stores.updateStore", error); sendError(res, 500, "Failed to update store"); } }; @@ -137,7 +138,7 @@ exports.deleteStore = async (req, res) => { await storeModel.deleteStore(req.params.storeId); res.json({ message: "Store deleted successfully" }); } catch (error) { - console.error("Delete store error:", error); + logError(req, "stores.deleteStore", error); if (error.message.includes('in use')) { return sendError(res, 400, error.message); } diff --git a/backend/controllers/users.controller.js b/backend/controllers/users.controller.js index 1031583..e39452e 100644 --- a/backend/controllers/users.controller.js +++ b/backend/controllers/users.controller.js @@ -1,6 +1,7 @@ const User = require("../models/user.model"); const bcrypt = require("bcryptjs"); const { sendError } = require("../utils/http"); +const { logError } = require("../utils/logger"); exports.test = async (req, res) => { console.log("User route is working"); @@ -27,6 +28,7 @@ exports.updateUserRole = async (req, res) => { res.json({ message: "Role updated", id, role }); } catch (err) { + logError(req, "users.updateUserRole", err); sendError(res, 500, "Failed to update role"); } }; @@ -42,6 +44,7 @@ exports.deleteUser = async (req, res) => { res.json({ message: "User deleted", id }); } catch (err) { + logError(req, "users.deleteUser", err); sendError(res, 500, "Failed to delete user"); } }; @@ -63,7 +66,7 @@ exports.getCurrentUser = async (req, res) => { res.json(user); } catch (err) { - console.error("Error getting current user:", err); + logError(req, "users.getCurrentUser", err); sendError(res, 500, "Failed to get user profile"); } }; @@ -89,7 +92,7 @@ exports.updateCurrentUser = async (req, res) => { res.json({ message: "Profile updated successfully", user: updated }); } catch (err) { - console.error("Error updating user profile:", err); + logError(req, "users.updateCurrentUser", err); sendError(res, 500, "Failed to update profile"); } }; @@ -131,7 +134,7 @@ exports.changePassword = async (req, res) => { res.json({ message: "Password changed successfully" }); } catch (err) { - console.error("Error changing password:", err); + logError(req, "users.changePassword", err); sendError(res, 500, "Failed to change password"); } }; diff --git a/backend/middleware/household.js b/backend/middleware/household.js index 38ca922..9f29200 100644 --- a/backend/middleware/household.js +++ b/backend/middleware/household.js @@ -1,5 +1,6 @@ const householdModel = require("../models/household.model"); const { sendError } = require("../utils/http"); +const { logError } = require("../utils/logger"); // Middleware to check if user belongs to household exports.householdAccess = async (req, res, next) => { @@ -29,7 +30,7 @@ exports.householdAccess = async (req, res, next) => { next(); } catch (error) { - console.error("Household access check error:", error); + logError(req, "middleware.householdAccess", error); sendError(res, 500, "Server error checking household access"); } }; @@ -84,7 +85,7 @@ exports.storeAccess = async (req, res, next) => { next(); } catch (error) { - console.error("Store access check error:", error); + logError(req, "middleware.storeAccess", error); sendError(res, 500, "Server error checking store access"); } }; diff --git a/backend/utils/logger.js b/backend/utils/logger.js new file mode 100644 index 0000000..8c9d4a9 --- /dev/null +++ b/backend/utils/logger.js @@ -0,0 +1,20 @@ +const { safeErrorMessage } = require("./redaction"); + +function formatExtra(extra = {}) { + return Object.entries(extra) + .filter(([, value]) => value !== undefined && value !== null && value !== "") + .map(([key, value]) => `${key}=${String(value)}`) + .join(" "); +} + +function logError(req, context, error, extra = {}) { + const requestId = req?.request_id || "unknown"; + const message = safeErrorMessage(error); + const extraText = formatExtra(extra); + const suffix = extraText ? ` ${extraText}` : ""; + console.error(`[${context}] request_id=${requestId} message=${message}${suffix}`); +} + +module.exports = { + logError, +};