/** * Copyright (c) 2026 Niolla * All rights reserved. * * This source code is proprietary and confidential. * Unauthorized copying, modification, distribution, or use * of this file, via any medium, is strictly prohibited. */ // app/controllers/user.controller.js const db = require("../models"); const { hashPassword } = require("../utils/hashPassword.util"); const { validatePassword, } = require("../utils/validation/validatePassword.util"); const { validateEmail } = require("../utils/validation/validateEmail.util"); const { generateUserId, generateId } = require("../utils/idGen.util"); const { createCustomerDetails, } = require("../utils/users/createCustomerDetails.util"); const { createBusinessCustomerDetails, } = require("../utils/users/createBusinessCustomer.util"); const { logActivity } = require("../services/activity.service"); const { sendMail } = require("../utils/mail.util"); const { createEmailVerification, verifyEmailVerificationToken, sendVerificationEmail, deleteEmailVerification, } = require("../utils/emailVerification.util"); const { getUserProfile } = require("../utils/users/userProfileDetails.util"); const User = db.User; const Profile = db.Profile; // Create a new user exports.createNewUser = async (req, res) => { const transaction = await db.sequelize.transaction(); try { const { firstName, lastName, email, password, accountType, address, phoneNumber, businessName, businessRegistrationNumber, businessType, contactName, businessEmail, expectedMonthlyVolume, note, } = req.body; if (!firstName || !lastName || !email || !password || !accountType) { await transaction.rollback(); return res.status(400).send({ success: false, message: "First name, last name, email, password and account type are required", }); } if (accountType !== "customer" && accountType !== "business_customer") { await transaction.rollback(); return res.status(400).send({ success: false, message: "Invalid account type. Must be either 'customer' or 'business_customer'", }); } const emailValid = validateEmail(email); if (!emailValid) { await transaction.rollback(); return res.status(400).send({ success: false, message: "Invalid email address", }); } const userExists = await User.findOne({ where: { email } }); if (userExists) { await transaction.rollback(); return res.status(400).send({ success: false, message: "User with this email already exists", }); } const validatePasswordResult = validatePassword(password); if (!validatePasswordResult) { await transaction.rollback(); return res.status(400).send({ success: false, message: "Password does not meet the required criteria", }); } const hashedPassword = await hashPassword(password); const userID = generateUserId(); const newUser = await User.create( { id: userID, firstName, lastName, email, password: hashedPassword, accountType, accountStatus: "PENDING_VERIFICATION", emailVerifiedAt: null, }, { transaction }, ); const newProfile = await Profile.create( { profile_id: generateId(), user_id: newUser.id, theme: "light", notificationsEnabled: true, profilePicture_id: "N/A", backgroundImage_id: "N/A", dob: null, phone_number: null, }, { transaction }, ); //create customer and business customer if (accountType === "customer") { const customerData = { address,phoneNumber, }; await createCustomerDetails( newUser.id, customerData, transaction, ); } else if (accountType === "business_customer") { const businessData = { businessName, businessRegistrationNumber, businessType, contactName, phoneNumber, businessEmail, expectedMonthlyVolume, note, }; await createBusinessCustomerDetails( newUser.id, businessData, transaction, ); } await transaction.commit(); const verificationToken = await createEmailVerification(newUser.id); try { await sendVerificationEmail( newUser.email, newUser.firstName, verificationToken, ); } catch (error) { console.error("Error sending verification email:", error); } await logActivity({ user: newUser, description: `Created New User with ID: ${newUser.id}`, type: "CREATE_USER", module: "User Management", }); res.status(201).send({ success: true, message: "User Created Successfully", data: { id: newUser.id, firstName: newUser.firstName, lastName: newUser.lastName, email: newUser.email, accountType: newUser.accountType, }, }); } catch (error) { if (!transaction.finished) { await transaction.rollback(); } console.error("CREATE USER ERROR:", error); res.status(500).send({ success: false, message: "Failed to create user", error: error.message, }); } }; // Verify customer email exports.verifyEmail = async (req, res) => { const transaction = await db.sequelize.transaction(); try { const { token } = req.body; if (!token) { await transaction.rollback(); return res.status(400).send({ success: false, message: "Verification token is required", }); } const verification = await verifyEmailVerificationToken(token); if (!verification) { await transaction.rollback(); return res.status(400).send({ success: false, message: "Verification token is invalid or expired", }); } const { userId, redisKey } = verification; const user = await User.findOne({ where: { id: userId, }, transaction, }); if (!user) { await transaction.rollback(); await deleteEmailVerification(redisKey); return res.status(404).send({ success: false, message: "User not found", }); } if (user.accountStatus === "ACTIVE" && user.emailVerifiedAt) { await transaction.rollback(); // Token is no longer needed await deleteEmailVerification(redisKey); return res.status(400).send({ success: false, message: "Email is already verified", }); } user.accountStatus = "ACTIVE"; user.emailVerifiedAt = new Date(); await user.save({ transaction, }); await transaction.commit(); await deleteEmailVerification(redisKey); return res.status(200).send({ success: true, message: "Email verified successfully. Your account is now active.", }); } catch (error) { if (!transaction.finished) { await transaction.rollback(); } console.error("VERIFY EMAIL ERROR:", error); return res.status(500).send({ success: false, message: "Failed to verify email", }); } }; // Get users with pagination (20 per page) exports.getAllUsers = async (req, res) => { try { const page = parseInt(req.query.page) || 1; // default page = 1 const limit = 20; const offset = (page - 1) * limit; const { count, rows: users } = await User.findAndCountAll({ attributes: { exclude: ["password"] }, limit, offset, order: [["createdAt", "DESC"]], // optional sorting }); res.status(200).send({ success: true, data: users, pagination: { totalUsers: count, totalPages: Math.ceil(count / limit), currentPage: page, pageSize: limit, }, }); } catch (error) { res.status(500).send({ success: false, message: "Failed to retrieve users", error: error.message, }); } }; //get user profile details exports.userProfile = async (req, res) => { try { const userId = req.user.id; const profile = await getUserProfile(userId); if (!profile) { return res.status(404).send({ success: false, message: "User not found", }); } return res.status(200).send({ success: true, message: "User profile retrieved successfully", data: profile, }); } catch (error) { console.error( "GET USER PROFILE ERROR:", error ); return res.status(500).send({ success: false, message: "Failed to retrieve user profile", }); } }; // Get user details by ID // exports.getUserById = async (req, res) => { // try { // const { id } = req.params; // const user = await User.findOne({ // where: { id }, // attributes: { exclude: ["password"] }, // include: [{ model: Profile, as: "profile" }], // }); // if (!user) { // return res.status(404).send({ // success: false, // message: "User not found", // }); // } // res.status(200).send({ // success: true, // data: user, // }); // } catch (error) { // res.status(500).send({ // success: false, // message: "Failed to retrieve user", // error: error.message, // }); // } // }; // Update user details exports.updateUser = async (req, res) => { const transaction = await db.sequelize.transaction(); try { const { id } = req.params; const { firstName, lastName, email, role, roleID, accountType, department, theme, notificationsEnabled, profilePicture_id, backgroundImage_id, dob, phone_number, } = req.body; const user = await User.findOne({ where: { id }, }); const UserProfile = await Profile.findOne({ where: { user_id: id }, }); if (!user) { return res.status(404).send({ success: false, message: "User not found", }); } if (!UserProfile) { return res.status(404).send({ success: false, message: "User profile not found", }); } user.firstName = firstName || user.firstName; user.lastName = lastName || user.lastName; user.role = role || user.role; user.roleID = roleID || user.roleID || "N/A"; user.accountType = accountType || user.accountType; user.department = department || user.department; UserProfile.theme = theme || UserProfile.theme; UserProfile.notificationsEnabled = notificationsEnabled !== undefined ? notificationsEnabled : UserProfile.notificationsEnabled; UserProfile.profilePicture_id = profilePicture_id || UserProfile.profilePicture_id || "N/A"; UserProfile.backgroundImage_id = backgroundImage_id || UserProfile.backgroundImage_id || "N/A"; UserProfile.dob = dob || UserProfile.dob; UserProfile.phone_number = phone_number || UserProfile.phone_number; await UserProfile.save({ transaction }); await user.save({ transaction }); await transaction.commit(); await logActivity({ user: req.user, description: `Updated User with ID: ${user.id}`, type: "UPDATE_USER", module: "User Management", }); const data = { id: user.id, firstName: user.firstName, lastName: user.lastName, email: user.email, accountType: user.accountType, role: user.role, department: user.department, profile: { theme: UserProfile.theme, notificationsEnabled: UserProfile.notificationsEnabled, profilePicture_id: UserProfile.profilePicture_id, backgroundImage_id: UserProfile.backgroundImage_id, dob: UserProfile.dob, phone_number: UserProfile.phone_number, }, }; res.status(200).send({ success: true, message: "User updated successfully", data, }); } catch (error) { await transaction.rollback(); res.status(500).send({ success: false, message: `Error: ${error.message}`, }); } }; // Delete user exports.deleteUser = async (req, res) => { const transaction = await db.sequelize.transaction(); try { const { id } = req.params; const user = await User.findOne({ where: { id }, }); if (!user) { return res.status(404).send({ success: false, message: "User not found", }); } await user.destroy({ transaction }); await transaction.commit(); await logActivity({ user: req.user, description: `Deleted User with ID: ${user.id}`, type: "DELETE_USER", module: "User Management", }); res.status(200).send({ success: true, message: "User deleted successfully", }); } catch (error) { await transaction.rollback(); res.status(500).send({ success: false, message: `Error: ${error.message}`, }); } };