Add support for sending edit user email with template

This commit is contained in:
faris Aljohari
2025-01-04 22:42:07 -06:00
parent 81ec60d947
commit 5ce016f429
5 changed files with 148 additions and 1 deletions

View File

@ -58,6 +58,8 @@ MAILTRAP_API_TOKEN=
MAILTRAP_INVITATION_TEMPLATE_UUID=
MAILTRAP_EDIT_USER_TEMPLATE_UUID=
MAILTRAP_DISABLE_TEMPLATE_UUID=
MAILTRAP_ENABLE_TEMPLATE_UUID=

View File

@ -17,5 +17,7 @@ export default registerAs(
MAILTRAP_ENABLE_TEMPLATE_UUID: process.env.MAILTRAP_ENABLE_TEMPLATE_UUID,
MAILTRAP_DELETE_USER_TEMPLATE_UUID:
process.env.MAILTRAP_DELETE_USER_TEMPLATE_UUID,
MAILTRAP_EDIT_USER_TEMPLATE_UUID:
process.env.MAILTRAP_EDIT_USER_TEMPLATE_UUID,
}),
);

View File

@ -138,4 +138,82 @@ export class EmailService {
);
}
}
async sendEditUserEmailWithTemplate(
email: string,
emailEditData: any,
): Promise<void> {
const isProduction = process.env.NODE_ENV === 'production';
const API_TOKEN = this.configService.get<string>(
'email-config.MAILTRAP_API_TOKEN',
);
const API_URL = isProduction
? SEND_EMAIL_API_URL_PROD
: SEND_EMAIL_API_URL_DEV;
const TEMPLATE_UUID = this.configService.get<string>(
'email-config.MAILTRAP_EDIT_USER_TEMPLATE_UUID',
);
const emailData = {
from: {
email: this.smtpConfig.sender,
},
to: [
{
email: email,
},
],
template_uuid: TEMPLATE_UUID,
template_variables: emailEditData,
};
try {
await axios.post(API_URL, emailData, {
headers: {
Authorization: `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json',
},
});
} catch (error) {
throw new HttpException(
error.response?.data?.message ||
'Error sending email using Mailtrap template',
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
generateUserChangesEmailBody(
addedSpaceNames: string[],
removedSpaceNames: string[],
oldRole: string,
newRole: string,
oldName: string,
newName: string,
) {
const addedSpaceNamesChanged =
addedSpaceNames.length > 0
? `Access to the following spaces were added: ${addedSpaceNames.join(', ')}`
: '';
const removedSpaceNamesChanged =
removedSpaceNames.length > 0
? `Access to the following spaces were deleted: ${removedSpaceNames.join(', ')}`
: '';
const roleChanged =
oldRole !== newRole
? `Your user role has been changed from [${oldRole}] to [${newRole}]`
: '';
const nameChanged =
oldName !== newName
? `The name associated with your account has changed from [${oldName}] to [${newName}]`
: '';
return {
addedSpaceNamesChanged,
removedSpaceNamesChanged,
roleChanged,
nameChanged,
};
}
}

View File

@ -25,6 +25,7 @@ import { UserDevicePermissionService } from 'src/user-device-permission/services
import { DeviceUserPermissionRepository } from '@app/common/modules/device/repositories';
import { PermissionTypeRepository } from '@app/common/modules/permission/repositories';
import { ProjectUserService } from 'src/project/services/project-user.service';
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
@Module({
imports: [ConfigModule, InviteUserRepositoryModule],
@ -49,6 +50,7 @@ import { ProjectUserService } from 'src/project/services/project-user.service';
DeviceUserPermissionRepository,
PermissionTypeRepository,
ProjectUserService,
RoleTypeRepository,
],
exports: [InviteUserService],
})

View File

@ -28,6 +28,7 @@ import {
DisableUserInvitationDto,
UpdateUserInvitationDto,
} from '../dtos/update.invite-user.dto';
import { RoleTypeRepository } from '@app/common/modules/role-type/repositories';
@Injectable()
export class InviteUserService {
@ -39,6 +40,7 @@ export class InviteUserService {
private readonly userSpaceService: UserSpaceService,
private readonly spaceUserService: SpaceUserService,
private readonly spaceRepository: SpaceRepository,
private readonly roleTypeRepository: RoleTypeRepository,
private readonly dataSource: DataSource,
) {}
@ -272,7 +274,7 @@ export class InviteUserService {
dto: UpdateUserInvitationDto,
invitedUserUuid: string,
): Promise<BaseResponseDto> {
const { projectUuid } = dto;
const { projectUuid, spaceUuids } = dto;
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.startTransaction();
@ -281,6 +283,7 @@ export class InviteUserService {
// Fetch the user's existing data in the project
const userOldData = await this.inviteUserRepository.findOne({
where: { uuid: invitedUserUuid, project: { uuid: projectUuid } },
relations: ['project', 'spaces.space', 'roleType'],
});
if (!userOldData) {
@ -294,7 +297,60 @@ export class InviteUserService {
await this.updateWhenUserIsInvite(queryRunner, dto, invitedUserUuid);
await this.updateWhenUserIsActive(queryRunner, dto, invitedUserUuid);
}
// Extract existing space UUIDs
const oldSpaceUuids = userOldData.spaces.map((space) => space.space.uuid);
// Compare spaces
const addedSpaces = spaceUuids.filter(
(uuid) => !oldSpaceUuids.includes(uuid),
);
const removedSpaces = oldSpaceUuids.filter(
(uuid) => !spaceUuids.includes(uuid),
);
// Fetch the space names for added and removed spaces
const spaceRepo = queryRunner.manager.getRepository(SpaceEntity);
const addedSpacesDetails = await spaceRepo.find({
where: {
uuid: In(addedSpaces),
},
});
const removedSpacesDetails = await spaceRepo.find({
where: {
uuid: In(removedSpaces),
},
});
// Extract the names of the added and removed spaces
const addedSpaceNames = addedSpacesDetails.map(
(space) => space.spaceName,
);
const removedSpaceNames = removedSpacesDetails.map(
(space) => space.spaceName,
);
// Check for role and name change
const oldRole = userOldData.roleType.type;
const newRole = await this.getRoleTypeByUuid(dto.roleUuid);
const oldFullName = `${userOldData.firstName} ${userOldData.lastName}`;
const newFullName = `${dto.firstName} ${dto.lastName}`;
// Generate email body
const emailMessage = this.emailService.generateUserChangesEmailBody(
addedSpaceNames,
removedSpaceNames,
oldRole,
newRole,
oldFullName,
newFullName,
);
await this.emailService.sendEditUserEmailWithTemplate(
userOldData.email,
emailMessage,
);
// Proceed with other updates (e.g., roles, names, etc.)
await queryRunner.commitTransaction();
return new SuccessResponseDto({
@ -316,6 +372,13 @@ export class InviteUserService {
await queryRunner.release();
}
}
private async getRoleTypeByUuid(roleUuid: string) {
const role = await this.roleTypeRepository.findOne({
where: { uuid: roleUuid },
});
return role.type;
}
private async updateWhenUserIsInvite(
queryRunner: QueryRunner,