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
225 lines
6.3 KiB
JavaScript
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");
|
|
}
|
|
};
|