5. Fitur Lupa Password
Pada Materi ini kita akan membahas bagaimana membuat fitur lupa password. Pada fitur ini biasanya backend akan mengirim link ke email untuk lupa password.
1. Instalasi Package nodemailer dan handlers
Petama kita akan instalasi package nodemailer untuk mengirimkan email pada nodejs.
npm install --save @nestjs-modules/mailer nodemailer
npm install --save-dev @types/nodemailer
npm install --save handlebars // library JavaScript yang digunakan untuk memfasilitasi proses templating di sisi klien. Dengan menggunakan Handlebars.js, Anda dapat menggabungkan data dengan template HTML untuk menghasilkan output HTML yang lebih dinamis dan fleksibel
2. Membuat Fitur Mail
3. Membuat akun di mailtrap.io
Buatlah akun pada https://mailtrap.io/ untuk membuat smtp server dummy. Pada proses development ini kita akan menggukan mailtrap untuk menerima email pada saat lupa password. Setelah membuat akun silakan buka url https://mailtrap.io/
Kemudian buka My Inbox dan pada bagian integrations pilih nodemailer
var transport = nodemailer.createTransport({
host: "sandbox.smtp.mailtrap.io",
port: 2525,
auth: {
user: "116b44e4fce785",
pass: "0a66404****"
}
})
Pada kode di atas kita akan mendapatkan konfigurasi untuk nanti simpan di nestjs. Terlihat pada bagian pass ada *** untuk mendapatkan string lengkap nya silahkan kalian klik button copy di sebelah kanan.
4. Konfigurasi Mail Module
Konfigurasi mail module dengan konfigurasi yang kita dapatkan dari mailtrap.io
import { MailerModule } from '@nestjs-modules/mailer';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import { Module } from '@nestjs/common';
import { MailService } from './mail.service';
import { join } from 'path';
@Module({
imports: [
MailerModule.forRoot({
transport: {
host: 'sandbox.smtp.mailtrap.io', //sesuaikan konfigurasi
port: 2525,
auth: {
user: '116b44e4fce785', //sesuaikan user
pass: '0a66404e26**', //sesuaikan password
},
},
defaults: {
from: '"No Reply" <noreply@example.com>',
},
template: {
dir: join(__dirname, 'templates'), // template akan di ambil dari handlebar yang ada pada folder templates
adapter: new HandlebarsAdapter(),
options: {
strict: true,
},
},
}),
],
providers: [MailService],
exports: [MailService], // 👈 export mailService agar bisa digunakan di luar module mail
})
export class MailModule {}
5. Membuat template pada mail
a. Folder Templates
Langkah pertama adalah buatlah folder dengan nama templates pada folder mail seperti gambar dibawah.
b. Template Lupa Password
<p>Hey {{ name }},</p>
<p>Please click below to confirm your email</p>
<p>
<a href="{{ link }}">Klik</a>
</p>
c. Konfigurasi nest-cli.json
Secara default NestJs hanya mendistibuskan mengcompile file .js dan .ts pada saat build. kita lihat bahwa template menggukaan extensi .hbs. Maka kita harus tambahkan konfiguasi pada nest-cli.json agar nestjs dapat mendistibusikan .hbs.
Tambahkan file di atas pada nest-cli.json, sehingga menjadi
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"compilerOptions": {
"assets": ["app/mail/templates/**/*"],
"watchAssets": true
},
"sourceRoot": "src"
}
6. Membuat DTO Reset Password
Membuat DTO MailResetPassword dan membuat method pada mail service
import { Injectable } from '@nestjs/common';
import { MailerService } from '@nestjs-modules/mailer'; //import MailerService
import { MailResetPasswordDto } from './mail.dto';
@Injectable()
export class MailService {
constructor(private mailService: MailerService) {}
async sendForgotPassword(payload: MailResetPasswordDto) {
await this.mailService.sendMail({
to: payload.email,
subject: 'Lupa Password', // subject pada email
template: './lupa_password', // template yang digunakan adalah lupa_password, kita bisa memembuat template yang lain
context: {
link: payload.link,
name: payload.name,
},
});
}
}
7. Membuat Entity Reset Password
Kemudian kita akan membuat tabel reset_password untuk menyimpan user_id dan token dari lupa passwod.
Buatlah file reset_password.entity.ts pada folder Auth
import {
Entity,
BaseEntity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
JoinColumn,
} from 'typeorm';
import { User } from './auth.entity';
@Entity()
export class ResetPassword extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => User) // relasikan many to one dengan table user
@JoinColumn({ name: 'user_id' })
user: User;
@Column({ nullable: true })
token: string;
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
created_at: Date;
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
updated_at: Date;
}
Karena ada relasi dengan tabel user, maka kita akan update pada file auth.entity.ts
import {
Entity,
BaseEntity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
JoinColumn,
OneToMany,
} from 'typeorm';
import { ResetPassword } from './reset_password.entity';
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ nullable: true })
avatar: string;
@Column({ nullable: false })
nama: string;
@Column({ unique: true, nullable: false })
email: string;
@Column({ nullable: true })
password: string;
@Column({ nullable: true })
refresh_token: string;
@Column({ nullable: true })
role: string;
@OneToMany(() => ResetPassword, (reset) => reset.user) // buat relasi one to many dengan tabel reset password
reset_password: ResetPassword;
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
created_at: Date;
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
updated_at: Date;
}
8. Import Entity Reset Password di auth module
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './auth.entity';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { jwt_config } from 'src/config/jwt.config';
import { JwtStrategy } from './jwt.strategy';
import { MailModule } from '../mail/mail.module';
import { ResetPassword } from './reset_password.entity';
@Module({
imports: [
TypeOrmModule.forFeature([User, ResetPassword]),
PassportModule.register({
defaultStrategy: 'jwt',
property: 'user',
session: false,
}),
JwtModule.register({
secret: jwt_config.secret,
signOptions: {
expiresIn: jwt_config.expired,
},
}),
MailModule, // import disini
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
})
export class AuthModule {}
9. Method forgotPassword pada auth service
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
...
import { MailService } from '../mail/mail.service'; // import mail service
import { ResetPassword } from './reset_password.entity'; // import reset password
import { randomBytes } from 'crypto'; // import cypto untuk membuat token dari random string
@Injectable()
export class AuthService extends BaseResponse {
constructor(
@InjectRepository(User) private readonly authRepository: Repository<User>,
@InjectRepository(ResetPassword) private readonly resetPasswordRepository: Repository<ResetPassword>, // inject repository reset password
private jwtService: JwtService,
private mailService: MailService,
) {
super();
}
async forgotPassword(email: string): Promise<ResponseSuccess> {
const user = await this.authRepository.findOne({
where: {
email: email,
},
});
if (!user) {
throw new HttpException(
'Email tidak ditemukan',
HttpStatus.UNPROCESSABLE_ENTITY,
);
}
const token = randomBytes(32).toString('hex'); // membuat token
const link = `http://localhost:5002/auth/reset-password/${user.id}/${token}`; //membuat link untuk reset password
await this.mailService.sendForgotPassword({
email: email,
name: user.nama,
link: link,
});
const payload = {
user: {
id: user.id,
},
token: token,
};
await this.resetPasswordRepository.save(payload); // menyimpan token dan id ke tabel reset password
return this._success('Silahkan Cek Email');
}
}
10. Membuat endpoint reset password pada auth controller
@Post('lupa-password')
async forgotPassowrd(@Body('email') email: string) {
console.log('email', email);
return this.authService.forgotPassword(email);
}
Membuat endpoint reset password pada auth controller
11. Pengujian pada Postman
Kemudian cek pada mailtrap apakan email berhasil masuk atau tidak