costco-grocery-list/backend/controllers/households.controller.js
Nico 77ae5be445
All checks were successful
Build & Deploy Costco Grocery List / build (push) Successful in 1m10s
Build & Deploy Costco Grocery List / verify-images (push) Successful in 3s
Build & Deploy Costco Grocery List / deploy (push) Successful in 11s
Build & Deploy Costco Grocery List / notify (push) Successful in 1s
refactor
2026-02-22 01:27:03 -08:00

225 lines
6.3 KiB
JavaScript

const householdModel = require("../models/household.model");
const { sendError } = require("../utils/http");
const { inviteCodeLast4 } = require("../utils/redaction");
const { logError } = require("../utils/logger");
// Get all households user belongs to
exports.getUserHouseholds = async (req, res) => {
try {
const households = await householdModel.getUserHouseholds(req.user.id);
res.json(households);
} catch (error) {
logError(req, "households.getUserHouseholds", error);
sendError(res, 500, "Failed to fetch households");
}
};
// Get household details
exports.getHousehold = async (req, res) => {
try {
const household = await householdModel.getHouseholdById(
req.params.householdId,
req.user.id
);
if (!household) {
return sendError(res, 404, "Household not found");
}
res.json(household);
} catch (error) {
logError(req, "households.getHousehold", error);
sendError(res, 500, "Failed to fetch household");
}
};
// Create new household
exports.createHousehold = async (req, res) => {
try {
const { name } = req.body;
if (!name || name.trim().length === 0) {
return sendError(res, 400, "Household name is required");
}
if (name.length > 100) {
return sendError(res, 400, "Household name must be 100 characters or less");
}
const household = await householdModel.createHousehold(
name.trim(),
req.user.id
);
res.status(201).json({
message: "Household created successfully",
household
});
} catch (error) {
logError(req, "households.createHousehold", error);
sendError(res, 500, "Failed to create household");
}
};
// Update household
exports.updateHousehold = async (req, res) => {
try {
const { name } = req.body;
if (!name || name.trim().length === 0) {
return sendError(res, 400, "Household name is required");
}
if (name.length > 100) {
return sendError(res, 400, "Household name must be 100 characters or less");
}
const household = await householdModel.updateHousehold(
req.params.householdId,
{ name: name.trim() }
);
res.json({
message: "Household updated successfully",
household
});
} catch (error) {
logError(req, "households.updateHousehold", error);
sendError(res, 500, "Failed to update household");
}
};
// Delete household
exports.deleteHousehold = async (req, res) => {
try {
await householdModel.deleteHousehold(req.params.householdId);
res.json({ message: "Household deleted successfully" });
} catch (error) {
logError(req, "households.deleteHousehold", error);
sendError(res, 500, "Failed to delete household");
}
};
// Refresh invite code
exports.refreshInviteCode = async (req, res) => {
try {
const household = await householdModel.refreshInviteCode(req.params.householdId);
res.json({
message: "Invite code refreshed successfully",
household
});
} catch (error) {
logError(req, "households.refreshInviteCode", error, {
invite_last4: inviteCodeLast4(req.body?.inviteCode),
});
sendError(res, 500, "Failed to refresh invite code");
}
};
// Join household via invite code
exports.joinHousehold = async (req, res) => {
const inviteLast4 = inviteCodeLast4(req.params.inviteCode);
try {
const { inviteCode } = req.params;
if (!inviteCode) return sendError(res, 400, "Invite code is required");
const result = await householdModel.joinHousehold(
inviteCode.toUpperCase(),
req.user.id
);
if (!result) return sendError(res, 404, "Invalid or expired invite code");
if (result.alreadyMember) {
return res.status(200).json({
message: "You are already a member of this household",
household: { id: result.id, name: result.name }
});
}
res.status(200).json({
message: `Successfully joined ${result.name}`,
household: { id: result.id, name: result.name }
});
} catch (error) {
logError(req, "households.joinHousehold", error, { invite_last4: inviteLast4 });
sendError(res, 500, "Failed to join household");
}
};
// Get household members
exports.getMembers = async (req, res) => {
try {
const members = await householdModel.getHouseholdMembers(req.params.householdId);
res.json(members);
} catch (error) {
logError(req, "households.getMembers", error);
sendError(res, 500, "Failed to fetch members");
}
};
// Update member role
exports.updateMemberRole = async (req, res) => {
try {
const { userId } = req.params;
const { role } = req.body;
if (!role || !['admin', 'member'].includes(role)) {
return sendError(res, 400, "Invalid role. Must be 'admin' or 'member'");
}
// Can't change own role
if (parseInt(userId) === req.user.id) {
return sendError(res, 400, "Cannot change your own role");
}
const targetRole = await householdModel.getUserRole(req.params.householdId, userId);
if (!targetRole) {
return sendError(res, 404, "Member not found");
}
if (targetRole === "owner") {
return sendError(res, 403, "Owner role cannot be changed");
}
const updated = await householdModel.updateMemberRole(
req.params.householdId,
userId,
role
);
res.json({
message: "Member role updated successfully",
member: updated
});
} catch (error) {
logError(req, "households.updateMemberRole", error);
sendError(res, 500, "Failed to update member role");
}
};
// Remove member
exports.removeMember = async (req, res) => {
try {
const { userId } = req.params;
const targetUserId = parseInt(userId);
// Allow users to remove themselves, or admins to remove others
if (targetUserId !== req.user.id && !["owner", "admin"].includes(req.household.role)) {
return sendError(res, 403, "Only admins or owners can remove other members");
}
const targetRole = await householdModel.getUserRole(req.params.householdId, userId);
if (targetRole === "owner") {
return sendError(res, 403, "Owner cannot be removed");
}
await householdModel.removeMember(req.params.householdId, userId);
res.json({ message: "Member removed successfully" });
} catch (error) {
logError(req, "households.removeMember", error);
sendError(res, 500, "Failed to remove member");
}
};