235 lines
6.4 KiB
JavaScript
235 lines
6.4 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,
|
|
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,
|
|
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,
|
|
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,
|
|
userId,
|
|
addressId,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new UserService();
|