Files
rentall-app/backend/services/UserService.js
2025-12-25 18:41:42 -05:00

239 lines
6.5 KiB
JavaScript

const { User, UserAddress } = require("../models");
const emailServices = require("./email");
const logger = require("../utils/logger");
/**
* UserService handles user-related business logic
* Including profile updates and associated notifications
*/
class UserService {
/**
* Update user profile and send notification if personal info changed
* @param {string} userId - User ID
* @param {Object} rawUpdateData - Data to update
* @param {Object} options - Optional transaction or other options
* @returns {Promise<User>} Updated user (without password field)
*/
async updateProfile(userId, rawUpdateData, options = {}) {
const user = await User.findByPk(userId);
if (!user) {
throw new Error("User not found");
}
// Store original values for comparison
const originalValues = {
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
address1: user.address1,
address2: user.address2,
city: user.city,
state: user.state,
zipCode: user.zipCode,
country: user.country,
};
// Prepare update data with preprocessing
const updateData = { ...rawUpdateData };
// Only include email if it's not empty
if (updateData.email !== undefined) {
if (updateData.email && updateData.email.trim() !== "") {
updateData.email = updateData.email.trim();
} else {
delete updateData.email; // Don't update if empty
}
}
// Handle phone: convert empty strings to null to avoid unique constraint issues
if (updateData.phone !== undefined) {
updateData.phone =
updateData.phone && updateData.phone.trim() !== ""
? updateData.phone.trim()
: null;
}
// Perform the update
await user.update(updateData, options);
// Check if personal information changed
const personalInfoFields = [
"email",
"firstName",
"lastName",
"address1",
"address2",
"city",
"state",
"zipCode",
"country",
];
const changedFields = personalInfoFields.filter(
(field) =>
updateData[field] !== undefined &&
originalValues[field] !== updateData[field]
);
// Send notification email if personal info changed
if (changedFields.length > 0 && process.env.NODE_ENV !== "test") {
try {
await emailServices.auth.sendPersonalInfoChangedEmail(user);
logger.info("Personal information changed notification sent", {
userId: user.id,
email: user.email,
changedFields,
});
} catch (emailError) {
logger.error(
"Failed to send personal information changed notification",
{
error: emailError.message,
stack: emailError.stack,
userId: user.id,
email: user.email,
changedFields,
}
);
// Don't throw - email failure shouldn't fail the update
}
}
// Return user without password
const updatedUser = await User.findByPk(user.id, {
attributes: { exclude: ["password"] },
});
return updatedUser;
}
/**
* Create a new address for a user and send notification
* @param {string} userId - User ID
* @param {Object} addressData - Address data
* @returns {Promise<UserAddress>} Created address
*/
async createUserAddress(userId, addressData) {
const user = await User.findByPk(userId);
if (!user) {
throw new Error("User not found");
}
const address = await UserAddress.create({
...addressData,
userId,
});
// Send notification for address creation
if (process.env.NODE_ENV !== "test") {
try {
await emailServices.auth.sendPersonalInfoChangedEmail(user);
logger.info(
"Personal information changed notification sent (address created)",
{
userId: user.id,
email: user.email,
addressId: address.id,
}
);
} catch (emailError) {
logger.error("Failed to send notification for address creation", {
error: emailError.message,
stack: emailError.stack,
userId: user.id,
addressId: address.id,
});
}
}
return address;
}
/**
* Update a user address and send notification
* @param {string} userId - User ID
* @param {string} addressId - Address ID
* @param {Object} updateData - Data to update
* @returns {Promise<UserAddress>} Updated address
*/
async updateUserAddress(userId, addressId, updateData) {
const address = await UserAddress.findOne({
where: { id: addressId, userId },
});
if (!address) {
throw new Error("Address not found");
}
await address.update(updateData);
// Send notification for address update
if (process.env.NODE_ENV !== "test") {
try {
const user = await User.findByPk(userId);
await emailServices.auth.sendPersonalInfoChangedEmail(user);
logger.info(
"Personal information changed notification sent (address updated)",
{
userId: user.id,
email: user.email,
addressId: address.id,
}
);
} catch (emailError) {
logger.error("Failed to send notification for address update", {
error: emailError.message,
stack: emailError.stack,
userId,
addressId: address.id,
});
}
}
return address;
}
/**
* Delete a user address and send notification
* @param {string} userId - User ID
* @param {string} addressId - Address ID
* @returns {Promise<void>}
*/
async deleteUserAddress(userId, addressId) {
const address = await UserAddress.findOne({
where: { id: addressId, userId },
});
if (!address) {
throw new Error("Address not found");
}
await address.destroy();
// Send notification for address deletion
if (process.env.NODE_ENV !== "test") {
try {
const user = await User.findByPk(userId);
await emailServices.auth.sendPersonalInfoChangedEmail(user);
logger.info(
"Personal information changed notification sent (address deleted)",
{
userId: user.id,
email: user.email,
addressId,
}
);
} catch (emailError) {
logger.error("Failed to send notification for address deletion", {
error: emailError.message,
stack: emailError.stack,
userId,
addressId,
});
}
}
}
}
module.exports = new UserService();