use nodemailer/smtp instead of sendgrid

This commit is contained in:
Amruth Pillai
2022-08-22 19:26:13 +02:00
parent 02587255fe
commit 5b6f6b7621
11 changed files with 120 additions and 103 deletions
+9 -7
View File
@@ -6,7 +6,7 @@ import appConfig from './app.config';
import authConfig from './auth.config';
import databaseConfig from './database.config';
import googleConfig from './google.config';
import sendgridConfig from './sendgrid.config';
import mailConfig from './mail.config';
import storageConfig from './storage.config';
const validationSchema = Joi.object({
@@ -36,11 +36,13 @@ const validationSchema = Joi.object({
GOOGLE_CLIENT_SECRET: Joi.string().allow(''),
PUBLIC_GOOGLE_CLIENT_ID: Joi.string().allow(''),
// SendGrid
SENDGRID_API_KEY: Joi.string().allow(''),
SENDGRID_FORGOT_PASSWORD_TEMPLATE_ID: Joi.string().allow(''),
SENDGRID_FROM_NAME: Joi.string().allow(''),
SENDGRID_FROM_EMAIL: Joi.string().allow(''),
// Mail
MAIL_FROM_NAME: Joi.string().allow(''),
MAIL_FROM_EMAIL: Joi.string().allow(''),
MAIL_HOST: Joi.string().allow(''),
MAIL_PORT: Joi.string().allow(''),
MAIL_USERNAME: Joi.string().allow(''),
MAIL_PASSWORD: Joi.string().allow(''),
// Storage
STORAGE_BUCKET: Joi.string().allow(''),
@@ -54,7 +56,7 @@ const validationSchema = Joi.object({
@Module({
imports: [
NestConfigModule.forRoot({
load: [appConfig, authConfig, databaseConfig, googleConfig, sendgridConfig, storageConfig],
load: [appConfig, authConfig, databaseConfig, googleConfig, mailConfig, storageConfig],
validationSchema: validationSchema,
}),
],
+12
View File
@@ -0,0 +1,12 @@
import { registerAs } from '@nestjs/config';
export default registerAs('mail', () => ({
from: {
name: process.env.MAIL_FROM_NAME,
email: process.env.MAIL_FROM_EMAIL,
},
host: process.env.MAIL_HOST,
port: process.env.MAIL_PORT,
username: process.env.MAIL_USERNAME,
password: process.env.MAIL_PASSWORD,
}));
-8
View File
@@ -1,8 +0,0 @@
import { registerAs } from '@nestjs/config';
export default registerAs('sendgrid', () => ({
apiKey: process.env.SENDGRID_API_KEY,
forgotPasswordTemplateId: process.env.SENDGRID_FORGOT_PASSWORD_TEMPLATE_ID,
fromName: process.env.SENDGRID_FROM_NAME,
fromEmail: process.env.SENDGRID_FROM_EMAIL,
}));
+30
View File
@@ -0,0 +1,30 @@
import { Type } from 'class-transformer';
import { IsDefined, IsNotEmpty, IsString } from 'class-validator';
export class MailRecipient {
@IsNotEmpty()
@IsString()
name: string;
@IsNotEmpty()
@IsString()
email: string;
}
export class SendMailDto {
@IsDefined()
@Type(() => MailRecipient)
from: MailRecipient;
@IsDefined()
@Type(() => MailRecipient)
to: MailRecipient;
@IsString()
@IsNotEmpty()
subject: string;
@IsString()
@IsNotEmpty()
message: string;
}
+34 -18
View File
@@ -1,40 +1,56 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import SendGrid from '@sendgrid/mail';
import { createTransport, Transporter } from 'nodemailer';
import { User } from '@/users/entities/user.entity';
import { SendMailDto } from './dto/send-mail.dto';
@Injectable()
export class MailService {
constructor(private configService: ConfigService) {
const sendGridApiKey = this.configService.get<string>('sendgrid.apiKey');
transporter: Transporter;
if (sendGridApiKey) {
SendGrid.setApiKey(this.configService.get<string>('sendgrid.apiKey'));
}
constructor(private configService: ConfigService) {
this.transporter = createTransport({
host: this.configService.get<string>('mail.host'),
port: this.configService.get<number>('mail.port'),
pool: true,
secure: false,
tls: { ciphers: 'SSLv3' },
auth: {
user: this.configService.get<string>('mail.username'),
pass: this.configService.get<string>('mail.password'),
},
});
}
async sendEmail(mail: SendGrid.MailDataRequired) {
return SendGrid.send(mail);
async sendEmail(sendMailDto: SendMailDto) {
this.transporter.sendMail({
from: `${sendMailDto.from.name} <${sendMailDto.from.email}>`,
to: `${sendMailDto.to.name} <${sendMailDto.to.email}>`,
subject: sendMailDto.subject,
text: sendMailDto.message,
html: sendMailDto.message,
});
}
async sendForgotPasswordEmail(user: User, resetToken: string): Promise<void> {
const appUrl = this.configService.get<string>('app.url');
const url = `${appUrl}?modal=auth.reset&resetToken=${resetToken}`;
const mailData: SendGrid.MailDataRequired = {
const sendMailDto: SendMailDto = {
from: {
name: this.configService.get<string>('sendgrid.fromName'),
email: this.configService.get<string>('sendgrid.fromEmail'),
name: this.configService.get<string>('mail.from.name'),
email: this.configService.get<string>('mail.from.email'),
},
to: user.email,
hideWarnings: true,
dynamicTemplateData: { url },
templateId: this.configService.get<string>('sendgrid.forgotPasswordTemplateId'),
to: {
name: user.name,
email: user.email,
},
subject: 'Reset your Reactive Resume password',
message: `<p>Hey ${user.name}!</p> <p>You can reset your password by visiting this link: <a href="${url}">${url}</a>.</p> <p>But hurry, because it will expire in 30 minutes.</p>`,
};
await SendGrid.send(mailData);
return;
await this.sendEmail(sendMailDto);
}
}
+3 -3
View File
@@ -2,7 +2,7 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { SchedulerRegistry } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { randomBytes } from 'crypto';
import { Connection, Repository } from 'typeorm';
import { DataSource, Repository } from 'typeorm';
import { MailService } from '@/mail/mail.service';
@@ -19,7 +19,7 @@ export class UsersService {
@InjectRepository(User) private userRepository: Repository<User>,
private schedulerRegistry: SchedulerRegistry,
private mailService: MailService,
private connection: Connection
private dataSource: DataSource
) {}
async findById(id: number): Promise<User> {
@@ -93,7 +93,7 @@ export class UsersService {
const user = await this.findByEmail(email);
const resetToken = randomBytes(32).toString('hex');
const queryRunner = this.connection.createQueryRunner();
const queryRunner = this.dataSource.createQueryRunner();
const timeout = setTimeout(async () => {
await this.userRepository.update(user.id, { resetToken: null });