/** * 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 { Op } = require("sequelize"); 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 { logActivity } = require("../services/activity.service"); const { sendMail } = require("../utils/mail.util"); const { createEmailVerification, verifyEmailVerificationToken, deleteEmailVerification} = require("../utils/emailVerification.util");; const User = db.User; const Profile = db.Profile; // const EmailVerification = db.EmailVerification; // Create a new user exports.createNewUser = async (req, res) => { const transaction = await db.sequelize.transaction(); try { const { firstName, lastName, email, password, } = req.body; if (!firstName || !lastName || !email || !password) { await transaction.rollback(); return res.status(400).send({ success: false, message: "First name, last name, email and password are required", }); } 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", }); } // let pass; // if(accountType === "admin" || accountType === "superadmin" || accountType === "manager" || accountType === "support_agent") { // pass = process.env.DEFAULT_PASSWORD; // }else{ // pass = password; // } 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 }, ); await transaction.commit(); const verificationToken = await createEmailVerification(newUser.id); const confirmationLink = `${process.env.FRONTEND_URL}/verify-email?token=${verificationToken}`; try { await sendMail({ to: email, subject: "Confirm Your ZUMRI Account", templateName: "emailVerification", templateVars: { customer_name: firstName, confirmation_link: confirmationLink, }, text: `Hello ${firstName}, please verify your ZUMRI account using this link: ${confirmationLink}`, }); } catch (mailError) { console.error( "VERIFICATION EMAIL ERROR:", mailError ); return res.status(201).send({ success: true, message: "Account created, but verification email could not be sent. Please request a new verification email.", data: { id: newUser.id, firstName: newUser.firstName, lastName: newUser.lastName, email: newUser.email, accountType: newUser.accountType, accountStatus: newUser.accountStatus, }, }); } await logActivity({ user: req.user, 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, // role: newUser.role, // department: newUser.department, }, }); } 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 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}`, }); } };