From d56b26d3ea6ed6ec95188174a319a2347677cd47 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 28 Dec 2024 19:41:24 -0600 Subject: [PATCH 1/3] Add Mailtrap API token and invitation template functionality --- libs/common/src/config/email.config.ts | 3 ++ libs/common/src/constants/mail-trap.ts | 1 + libs/common/src/util/email.service.ts | 42 +++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 libs/common/src/constants/mail-trap.ts diff --git a/libs/common/src/config/email.config.ts b/libs/common/src/config/email.config.ts index 3a5c21e..e18d4e8 100644 --- a/libs/common/src/config/email.config.ts +++ b/libs/common/src/config/email.config.ts @@ -10,5 +10,8 @@ export default registerAs( SMTP_USER: process.env.SMTP_USER, SMTP_SENDER: process.env.SMTP_SENDER, SMTP_PASSWORD: process.env.SMTP_PASSWORD, + MAILTRAP_API_TOKEN: process.env.MAILTRAP_API_TOKEN, + MAILTRAP_INVITATION_TEMPLATE_UUID: + process.env.MAILTRAP_INVITATION_TEMPLATE_UUID, }), ); diff --git a/libs/common/src/constants/mail-trap.ts b/libs/common/src/constants/mail-trap.ts new file mode 100644 index 0000000..3d989bc --- /dev/null +++ b/libs/common/src/constants/mail-trap.ts @@ -0,0 +1 @@ +export const SEND_EMAIL_API_URL = 'https://send.api.mailtrap.io/api/send'; diff --git a/libs/common/src/util/email.service.ts b/libs/common/src/util/email.service.ts index f3a389d..c1ad34d 100644 --- a/libs/common/src/util/email.service.ts +++ b/libs/common/src/util/email.service.ts @@ -1,6 +1,8 @@ -import { Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import * as nodemailer from 'nodemailer'; +import axios from 'axios'; +import { SEND_EMAIL_API_URL } from '../constants/mail-trap'; @Injectable() export class EmailService { @@ -35,4 +37,42 @@ export class EmailService { await transporter.sendMail(mailOptions); } + async sendEmailWithInvitationTemplate( + email: string, + emailInvitationData: any, + ): Promise { + const API_TOKEN = this.configService.get( + 'email-config.MAILTRAP_API_TOKEN', + ); + const TEMPLATE_UUID = this.configService.get( + 'email-config.MAILTRAP_INVITATION_TEMPLATE_UUID', + ); + + const emailData = { + from: { + email: this.configService.get('email-config.SMTP_SENDER'), + }, + to: [ + { + email: email, + }, + ], + template_uuid: TEMPLATE_UUID, + template_variables: emailInvitationData, + }; + + try { + await axios.post(SEND_EMAIL_API_URL, emailData, { + headers: { + Authorization: `Bearer ${API_TOKEN}`, + 'Content-Type': 'application/json', + }, + }); + } catch (error) { + throw new HttpException( + error.message || 'Error sending email using Mailtrap template', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } From e3ebcff78a77ac84238323f00e7ac8ca1808625d Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 28 Dec 2024 19:41:30 -0600 Subject: [PATCH 2/3] Add EmailService and send invitation email in InviteUserService --- src/invite-user/invite-user.module.ts | 2 ++ .../services/invite-user.service.ts | 20 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/invite-user/invite-user.module.ts b/src/invite-user/invite-user.module.ts index e77f875..402ebb1 100644 --- a/src/invite-user/invite-user.module.ts +++ b/src/invite-user/invite-user.module.ts @@ -9,6 +9,7 @@ import { InviteUserRepository, InviteUserSpaceRepository, } from '@app/common/modules/Invite-user/repositiories'; +import { EmailService } from '@app/common/util/email.service'; @Module({ imports: [ConfigModule, InviteUserRepositoryModule], @@ -17,6 +18,7 @@ import { InviteUserService, InviteUserRepository, UserRepository, + EmailService, InviteUserSpaceRepository, ], exports: [InviteUserService], diff --git a/src/invite-user/services/invite-user.service.ts b/src/invite-user/services/invite-user.service.ts index 2180d0b..b49ed0b 100644 --- a/src/invite-user/services/invite-user.service.ts +++ b/src/invite-user/services/invite-user.service.ts @@ -4,7 +4,7 @@ import { BaseResponseDto } from '@app/common/dto/base.response.dto'; import { UserStatusEnum } from '@app/common/constants/user-status.enum'; import { SuccessResponseDto } from '@app/common/dto/success.response.dto'; import { generateRandomString } from '@app/common/helper/randomString'; -import { IsNull, Not } from 'typeorm'; +import { In, IsNull, Not } from 'typeorm'; import { DataSource } from 'typeorm'; import { UserEntity } from '@app/common/modules/user/entities'; import { RoleType } from '@app/common/constants/role.type.enum'; @@ -14,12 +14,15 @@ import { } from '@app/common/modules/Invite-user/repositiories'; import { CheckEmailDto } from '../dtos/check-email.dto'; import { UserRepository } from '@app/common/modules/user/repositories'; +import { EmailService } from '@app/common/util/email.service'; +import { SpaceEntity } from '@app/common/modules/space'; @Injectable() export class InviteUserService { constructor( private readonly inviteUserRepository: InviteUserRepository, private readonly userRepository: UserRepository, + private readonly emailService: EmailService, private readonly inviteUserSpaceRepository: InviteUserSpaceRepository, private readonly dataSource: DataSource, ) {} @@ -75,7 +78,16 @@ export class InviteUserService { }); const invitedUser = await queryRunner.manager.save(inviteUser); + const spaceRepo = queryRunner.manager.getRepository(SpaceEntity); + const spaces = await spaceRepo.find({ + where: { + uuid: In(spaceUuids), + }, + }); + const spaceNames = spaces.map((space) => space.spaceName); + + const spaceNamesString = spaceNames.join(', '); const spacePromises = spaceUuids.map(async (spaceUuid) => { const inviteUserSpace = this.inviteUserSpaceRepository.create({ inviteUser: { uuid: invitedUser.uuid }, @@ -85,6 +97,12 @@ export class InviteUserService { }); await Promise.all(spacePromises); + await this.emailService.sendEmailWithInvitationTemplate(email, { + name: firstName + ' ' + lastName, + invitationCode, + role: roleType, + spacesList: spaceNamesString, + }); await queryRunner.commitTransaction(); From 3cff60688768296009017c2f2210acd3d8a45419 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 30 Dec 2024 19:41:19 -0600 Subject: [PATCH 3/3] Add environment variables and update email service for different environments --- .env.example | 6 ++++++ libs/common/src/constants/mail-trap.ts | 4 +++- libs/common/src/util/email.service.ts | 18 +++++++++++++----- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 8fb1460..9525e51 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,5 @@ +NODE_ENV= + ACCESS_KEY= AZURE_POSTGRESQL_DATABASE= @@ -52,6 +54,10 @@ SMTP_SECURE= SMTP_USER= +MAILTRAP_API_TOKEN= + +MAILTRAP_INVITATION_TEMPLATE_UUID= + WEBSITES_ENABLE_APP_SERVICE_STORAGE= PORT= diff --git a/libs/common/src/constants/mail-trap.ts b/libs/common/src/constants/mail-trap.ts index 3d989bc..6642244 100644 --- a/libs/common/src/constants/mail-trap.ts +++ b/libs/common/src/constants/mail-trap.ts @@ -1 +1,3 @@ -export const SEND_EMAIL_API_URL = 'https://send.api.mailtrap.io/api/send'; +export const SEND_EMAIL_API_URL_PROD = 'https://send.api.mailtrap.io/api/send/'; +export const SEND_EMAIL_API_URL_DEV = + 'https://sandbox.api.mailtrap.io/api/send/2634012'; diff --git a/libs/common/src/util/email.service.ts b/libs/common/src/util/email.service.ts index c1ad34d..fd84ea6 100644 --- a/libs/common/src/util/email.service.ts +++ b/libs/common/src/util/email.service.ts @@ -2,7 +2,10 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import * as nodemailer from 'nodemailer'; import axios from 'axios'; -import { SEND_EMAIL_API_URL } from '../constants/mail-trap'; +import { + SEND_EMAIL_API_URL_DEV, + SEND_EMAIL_API_URL_PROD, +} from '../constants/mail-trap'; @Injectable() export class EmailService { @@ -41,16 +44,20 @@ export class EmailService { email: string, emailInvitationData: any, ): Promise { + const isProduction = process.env.NODE_ENV === 'production'; const API_TOKEN = this.configService.get( '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( 'email-config.MAILTRAP_INVITATION_TEMPLATE_UUID', ); const emailData = { from: { - email: this.configService.get('email-config.SMTP_SENDER'), + email: this.smtpConfig.sender, }, to: [ { @@ -62,7 +69,7 @@ export class EmailService { }; try { - await axios.post(SEND_EMAIL_API_URL, emailData, { + await axios.post(API_URL, emailData, { headers: { Authorization: `Bearer ${API_TOKEN}`, 'Content-Type': 'application/json', @@ -70,8 +77,9 @@ export class EmailService { }); } catch (error) { throw new HttpException( - error.message || 'Error sending email using Mailtrap template', - error.status || HttpStatus.INTERNAL_SERVER_ERROR, + error.response?.data?.message || + 'Error sending email using Mailtrap template', + error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR, ); } }