mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-13 22:37:14 +10:00
use nodemailer/smtp instead of sendgrid
This commit is contained in:
@@ -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,
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -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,
|
||||
}));
|
||||
@@ -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,
|
||||
}));
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
|
||||
Reference in New Issue
Block a user