Skip to content

Commit

Permalink
mailing service scaffold
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeh committed Sep 14, 2024
1 parent 507157d commit 48ff15c
Show file tree
Hide file tree
Showing 8 changed files with 1,082 additions and 1 deletion.
3 changes: 3 additions & 0 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"test": "jest --config ./test/jest-config.json -i --detectOpenHandles"
},
"dependencies": {
"@aws-sdk/client-ses": "^3.651.1",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.2.3",
"@nestjs/core": "^10.0.0",
Expand All @@ -26,6 +27,7 @@
"bcrypt": "catalog:",
"class-transformer": "catalog:",
"lodash": "^4.17.21",
"nodemailer": "^6.9.15",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
Expand All @@ -43,6 +45,7 @@
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.7",
"@types/node": "^20.3.1",
"@types/nodemailer": "^6.4.15",
"@types/passport-jwt": "^4.0.1",
"@types/passport-local": "^1.0.38",
"@types/supertest": "^6.0.0",
Expand Down
3 changes: 2 additions & 1 deletion api/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { APP_GUARD } from '@nestjs/core';
import { AuthModule } from '@api/modules/auth/auth.module';
import { JwtAuthGuard } from '@api/modules/auth/guards/jwt-auth.guard';
import { RolesGuard } from '@api/modules/auth/guards/roles.guard';
import { NotificationsModule } from '@api/modules/notifications/notifications.module';

@Module({
imports: [ApiConfigModule, AuthModule],
imports: [ApiConfigModule, AuthModule, NotificationsModule],
controllers: [AppController],
providers: [
AppService,
Expand Down
2 changes: 2 additions & 0 deletions api/src/modules/auth/authentication/authentication.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { UsersService } from '@api/modules/users/users.service';
import { UsersModule } from '@api/modules/users/users.module';
import { LocalStrategy } from '@api/modules/auth/strategies/local.strategy';
import { JwtStrategy } from '@api/modules/auth/strategies/jwt.strategy';
import { NotificationsModule } from '@api/modules/notifications/notifications.module';

@Module({
imports: [
Expand All @@ -22,6 +23,7 @@ import { JwtStrategy } from '@api/modules/auth/strategies/jwt.strategy';
}),
}),
UsersModule,
NotificationsModule,
],
providers: [
AuthenticationService,
Expand Down
17 changes: 17 additions & 0 deletions api/src/modules/notifications/email/email-service.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Logger } from '@nestjs/common';

export class SendMailDTO {
from: string;
to: string;
subject: string;
html: string;
text?: string;
}

export const IEmailServiceToken = 'IEmailServiceInterface';

export interface IEmailServiceInterface {
logger: Logger;

sendMail(sendMailDTO: SendMailDTO): Promise<void>;
}
11 changes: 11 additions & 0 deletions api/src/modules/notifications/email/email.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { IEmailServiceToken } from '@api/modules/notifications/email/email-service.interface';
import { NodemailerEmailService } from '@api/modules/notifications/email/nodemailer.email.service';

@Module({
providers: [
{ provide: IEmailServiceToken, useClass: NodemailerEmailService },
],
exports: [IEmailServiceToken],
})
export class EmailModule {}
62 changes: 62 additions & 0 deletions api/src/modules/notifications/email/nodemailer.email.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {
Injectable,
Logger,
ServiceUnavailableException,
} from '@nestjs/common';

import * as nodemailer from 'nodemailer';
import * as aws from '@aws-sdk/client-ses';
import {
IEmailServiceInterface,
SendMailDTO,
} from '@api/modules/notifications/email/email-service.interface';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class NodemailerEmailService implements IEmailServiceInterface {
logger: Logger = new Logger(NodemailerEmailService.name);
private transporter: nodemailer.Transporter;
private readonly domain: string;

constructor(private readonly configService: ConfigService) {
const { accessKeyId, secretAccessKey, region, domain } =
this.getMailConfig();
const ses = new aws.SESClient({
region,
credentials: { accessKeyId, secretAccessKey },
});
this.transporter = nodemailer.createTransport({ SES: { ses, aws } });
this.domain = domain;
}

async sendMail(sendMailDTO: SendMailDTO): Promise<void> {
try {
await this.transporter.sendMail({
from: `${sendMailDTO.from}@${this.domain}`,
to: sendMailDTO.to,
subject: sendMailDTO.subject,
html: sendMailDTO.html,
text: sendMailDTO.text,
});
} catch (e) {
this.logger.error(`Error sending email: ${JSON.stringify(e)}`);
throw new ServiceUnavailableException('Could not send email');
}
}

private getMailConfig() {
const accessKeyId = this.configService.get<string>('AWS_SES_ACCESS_KEY_ID');
const secretAccessKey = this.configService.get<string>(
'AWS_SES_SECRET_ACCESS_KEY',
);
const region = this.configService.get<string>('AWS_SES_REGION');
const domain = this.configService.get<string>('AWS_SES_DOMAIN');
console.log(accessKeyId, secretAccessKey, region, domain);
if (!accessKeyId || !secretAccessKey || !region || !domain) {
this.logger.error(
'Variables for Email Service not set. Email not available',
);
}
return { accessKeyId, secretAccessKey, region, domain };
}
}
7 changes: 7 additions & 0 deletions api/src/modules/notifications/notifications.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Module } from '@nestjs/common';
import { EmailModule } from './email/email.module';

@Module({
imports: [EmailModule],
})
export class NotificationsModule {}
Loading

0 comments on commit 48ff15c

Please sign in to comment.