refactor: use safe request-scoped backend error logging

This commit is contained in:
Nico 2026-02-16 01:36:39 -08:00
parent e2e9ec9eb4
commit 9cb0ac19e5
7 changed files with 66 additions and 44 deletions

View File

@ -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");
}
};

View File

@ -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");
}
};

View File

@ -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");
}
};

View File

@ -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);
}

View File

@ -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");
}
};

View File

@ -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");
}
};

20
backend/utils/logger.js Normal file
View File

@ -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,
};