Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Servers/controllers/project.ctrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ import {
ValidationException,
BusinessLogicException
} from "../domain.layer/exceptions/custom.exception";
import { sendProjectCreatedNotification } from "../services/projectNotification/projectCreationNotification";
import { sendUserAddedToProjectNotification, ProjectRole } from "../services/userNotification/userAddedToProjectNotification"
import { sendProjectCreatedNotification } from "../services/userNotification/projectNotifications";
import { sendUserAddedToProjectNotification, ProjectRole } from "../services/userNotification/projectNotifications"

export async function getAllProjects(req: Request, res: Response): Promise<any> {
logProcessing({
Expand Down
91 changes: 89 additions & 2 deletions Servers/controllers/user.ctrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import {
resetPasswordQuery,
updateUserByIdQuery,
} from "../utils/user.utils";
import { sendMemberRoleChangedEditorToAdminNotification } from "../services/userNotification/projectNotifications";
import { logFailure } from "../utils/logger/logHelper";
import bcrypt from "bcrypt";
import { STATUS_CODE } from "../utils/statusCode.utils";
import {
Expand Down Expand Up @@ -605,7 +607,10 @@ async function resetPassword(req: Request, res: Response) {
async function updateUserById(req: Request, res: Response) {
const transaction = await sequelize.transaction();
const id = parseInt(req.params.id);
const { name, surname, email, roleId, last_login } = req.body;
const { name, surname, email, roleId: roleIdRaw, last_login } = req.body;

// Convert roleId to number if it exists (frontend may send as string)
const roleId = roleIdRaw ? parseInt(roleIdRaw) : undefined;

logStructured('processing', `updating user ID ${id}`, 'updateUserById', 'user.ctrl.ts');
logger.debug(`✏️ Update requested for user ID ${id}`);
Expand Down Expand Up @@ -642,6 +647,9 @@ async function updateUserById(req: Request, res: Response) {
const user = await getUserByIdQuery(id);

if (user) {
// Capture the old role before updating (if roleId is being changed)
const oldRoleId = user.role_id;

await user.updateCurrentUser({ name, surname, email });
await user.validateUserData();

Expand All @@ -659,6 +667,47 @@ async function updateUserById(req: Request, res: Response) {
await transaction.commit();
logStructured('successful', `user updated: ID ${id}`, 'updateUserById', 'user.ctrl.ts');
await logEvent('Update', `User updated: ID ${id}, email: ${updatedUser.email}`);


// Convert to numbers explicitly for comparison
const oldRoleIdNum = Number(oldRoleId);
const newRoleIdNum = Number(roleId);

if (newRoleIdNum === 1 && oldRoleIdNum === 3) {

// Get all projects where the user is a member
try {
const userProjects = await getUserProjects(id, req.tenantId!);

// Send notification for each project (fire-and-forget)
for (const project of userProjects) {
sendMemberRoleChangedEditorToAdminNotification({
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For functions like this, its better to name them like this:
sendMemberRoleChangedNotification, and then you have two props, oldRole, newRole.
Please modify it, so the logic is handled like that

projectId: project.id!,
projectName: project.project_title,
actorId: currentUserId || id, // Use currentUserId if available, otherwise use the user's own id
userId: id,
}).catch(async (emailError) => {
await logFailure({
eventType: "Update",
description: `Failed to send role changed notification for project ${project.id} to user ${id}`,
functionName: "updateUserById",
fileName: "user.ctrl.ts",
error: emailError as Error,
});
});
}
} catch (projectError) {
// Log error but don't fail the user update
await logFailure({
eventType: "Update",
description: `Failed to fetch user projects for role change notification: user ${id}`,
functionName: "updateUserById",
fileName: "user.ctrl.ts",
error: projectError as Error,
});
}
}

return res.status(202).json(STATUS_CODE[202](updatedUser.toSafeJSON()));
}

Expand Down Expand Up @@ -788,7 +837,7 @@ async function calculateProgress(
logger.debug(`📊 Starting progress calculation for user ID ${id}`);

try {
const userProjects = await getUserProjects(id);
const userProjects = await getUserProjects(id, req.tenantId!);

let assessmentsMetadata = [];
let allTotalAssessments = 0;
Expand Down Expand Up @@ -989,6 +1038,9 @@ async function updateUserRole(req: Request, res: Response) {
return res.status(404).json({ message: 'Current user not found' });
}

// Capture the old role before updating
const oldRoleId = targetUser.role_id;

await targetUser.updateRole(newRoleId, currentUser);

const updatedUser = (await updateUserByIdQuery(
Expand All @@ -1001,6 +1053,41 @@ async function updateUserRole(req: Request, res: Response) {
logStructured('successful', `role updated for user ID ${id}`, 'updateUserRole', 'user.ctrl.ts');
await logEvent('Update', `User role updated: ID ${id}, new role ID: ${newRoleId}, by admin ID: ${currentUserId}`);

// Send email notifications for role change from Editor (3) to Admin (1)
if (oldRoleId === 3 && newRoleId === 1) {
// Get all projects where the user is a member
try {
const userProjects = await getUserProjects(parseInt(id), req.tenantId!);

// Send notification for each project (fire-and-forget)
for (const project of userProjects) {
sendMemberRoleChangedEditorToAdminNotification({
projectId: project.id!,
projectName: project.project_title,
actorId: currentUserId,
userId: parseInt(id),
}).catch(async (emailError) => {
await logFailure({
eventType: "Update",
description: `Failed to send role changed notification for project ${project.id} to user ${id}`,
functionName: "updateUserRole",
fileName: "user.ctrl.ts",
error: emailError as Error,
});
});
}
} catch (projectError) {
// Log error but don't fail the role update
await logFailure({
eventType: "Update",
description: `Failed to fetch user projects for role change notification: user ${id}`,
functionName: "updateUserRole",
fileName: "user.ctrl.ts",
error: projectError as Error,
});
}
}
Comment thread
solan117 marked this conversation as resolved.

return res.status(202).json({
message: 'User role updated successfully',
data: updatedUser.toSafeJSON(),
Expand Down

This file was deleted.

Loading