Skip to content

Commit

Permalink
reset password server code completed
Browse files Browse the repository at this point in the history
  • Loading branch information
sinanptm committed Aug 31, 2024
1 parent c41c1dd commit 771c6b6
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 54 deletions.
1 change: 1 addition & 0 deletions server/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ pnpm-debug.log*
*.sw?

# Mac
.vercel
68 changes: 68 additions & 0 deletions server/public/resetPasswordTemplate.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reset Your Password</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 0;
}
.email-container {
max-width: 600px;
margin: 40px auto;
background-color: #ffffff;
padding: 30px;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
h1 {
color: #2c3e50;
font-size: 26px;
margin-bottom: 20px;
}
p {
color: #7f8c8d;
font-size: 16px;
line-height: 1.6;
}
.reset-link {
display: inline-block;
padding: 10px 20px;
font-size: 16px;
color: #ffffff;
background-color: #e74c3c;
border-radius: 5px;
text-decoration: none;
margin: 20px 0;
}
.footer {
margin-top: 30px;
font-size: 14px;
color: #95a5a6;
text-align: center;
}
.warning {
color: #e67e22;
font-size: 14px;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="email-container">
<h1>Hello, {{name}}!</h1>
<p>We received a request to reset your password. You can reset your password by clicking the button below:</p>
<a href="{{resetLink}}" class="reset-link">Reset Password</a>
<p>This link is valid for only <strong>1 hour</strong>. Please make sure to use it before it expires.</p>
<p class="warning">If you did not request a password reset, please ignore this email or contact our support team immediately.</p>
<div class="footer">
<p>Best regards,<br><strong>AVM Ayurvedic</strong></p>
</div>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import IOtp from "../../../domain/entities/IOtp";
import IOtpRepository from "../../../interface/repositories/IOtpRepository";
import OtpModel from "../models/OtpModel";
import IOtp from "../../domain/entities/IOtp";
import IOtpRepository from "../../interface/repositories/IOtpRepository";
import OtpModel from "../database/models/OtpModel";

export default class OtpRepository implements IOtpRepository {
model = OtpModel;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IPatient } from "../../../domain/entities/Patient";
import IPatientRepository from "../../../interface/repositories/IPatientRepository";
import { isValidObjectId } from "../isValidObjId";
import PatientModel from "../models/PatientModel";
import { IPatient } from "../../domain/entities/Patient";
import IPatientRepository from "../../interface/repositories/IPatientRepository";
import { isValidObjectId } from "../database/isValidObjId";
import PatientModel from "../database/models/PatientModel";

export default class PatientRepository implements IPatientRepository {
model = PatientModel;
Expand Down
83 changes: 54 additions & 29 deletions server/src/infrastructure/services/EmailService.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,60 @@
import IEmailService from "../../interface/services/IEmailService";
import nodemailer from "nodemailer";
import {promisify} from 'util'
import fs from 'fs'
import path from 'path'

import { promisify } from "util";
import fs from "fs";
import path from "path";

const readFileAsync = promisify(fs.readFile);

export default class EmailService implements IEmailService{

async sendOtp(email: string, name: string, otp: number): Promise<void> {
let htmlTemplate = await readFileAsync(path.join(__dirname, '../../../public/otpEmailTemplate.html'), 'utf-8');

htmlTemplate = htmlTemplate.replace('{{name}}', name);
htmlTemplate = htmlTemplate.replace('{{otp}}', otp.toString());

const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.SENDER_EMAIL,
pass: process.env.NODEMAILER_PASSKEY,
},
});

await transporter.sendMail({
from: process.env.SENDER_MAIL,
to: email,
subject: 'Your OTP for Verification',
html: htmlTemplate,
});

}

export default class EmailService implements IEmailService {
async sendOtp(email: string, name: string, otp: number): Promise<void> {
let htmlTemplate = await readFileAsync(path.join(__dirname, "../../../public/otpEmailTemplate.html"), "utf-8");

htmlTemplate = htmlTemplate.replace("{{name}}", name);
htmlTemplate = htmlTemplate.replace("{{otp}}", otp.toString());

const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.SENDER_EMAIL,
pass: process.env.NODEMAILER_PASSKEY,
},
});

const id = await transporter.sendMail({
from: process.env.SENDER_MAIL,
to: email,
subject: "No Reply Mail: Otp Verification",
html: htmlTemplate,
});

console.log(id.messageId);

}

async sendResetMail(email: string, name: string, resetLink: string): Promise<void> {
let htmlTemplate = await readFileAsync(
path.join(__dirname, "../../../public/resetPasswordTemplate.html"),
"utf-8"
);

htmlTemplate = htmlTemplate.replace("{{name}}", name);
htmlTemplate = htmlTemplate.replace("{{resetLink}}", resetLink);

const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.SENDER_EMAIL,
pass: process.env.NODEMAILER_PASSKEY,
},
});

const mail = await transporter.sendMail({
from: process.env.SENDER_MAIL,
to: email,
subject: "No Reply Mail: Password Reset",
html: htmlTemplate,
});

};
}
1 change: 1 addition & 0 deletions server/src/interface/services/IEmailService.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export default interface IEmailService {
sendOtp(email: string, name: string, otp: number): Promise<void>;
sendResetMail(email:string,name:string,resetLink:string):Promise<void>;
}
12 changes: 11 additions & 1 deletion server/src/presentation/controllers/PatientController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default class PatientController {
const { email } = req.body;
if (!email) return res.status(400).json({ message: "Email is required" });
await this.authPatientUseCase.resendOtp(email);
res.status(200).json({message:"Otp Sended to the mail Address"});
res.status(200).json({ message: "Otp Sended to the mail Address" });
} catch (error: any) {
if (error.message === "Patient Not Found") {
return res.status(422).json({ message: "Invalid Credentials" });
Expand Down Expand Up @@ -93,6 +93,16 @@ export default class PatientController {
}
}

async resetPassword(req: Request, res: Response, next: NextFunction) {
try {
const { email } = req.body;
if (!email) return res.status(400).json({ message: "Email is Required" });
await this.authPatientUseCase.sendForgetPasswordMail(email);
} catch (error: any) {
next(error);
}
}

async refreshAccessToken(req: Request, res: Response, next: NextFunction) {
try {
const cookies = req.cookies;
Expand Down
2 changes: 1 addition & 1 deletion server/src/presentation/middlewares/errorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const errorHandler = (err: any, req: Request, res: Response, next: NextFu
return res.status(401).json({ message: err.message });
} else if (err.message === "Patient is blocked") {
return res.status(403).json({ message: err.message });
}else if (err.message==="Patient not found"){
}else if (err.message==="Patient not Found"){
return res.status(404).json({message:err.message})
}else if (err.message===' getaddrinfo ENOTFOUND smtp.gmail.com'){
return res.status(500).json({message:"We are Having Issue with Email Service"})
Expand Down
41 changes: 29 additions & 12 deletions server/src/presentation/routers/patient/AuthPatientRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
import express from "express";
import PatientRepository from "../../../infrastructure/database/repositories/PatientRepository";
import PatientRepository from "../../../infrastructure/repositories/PatientRepository";
import PasswordService from "../../../infrastructure/services/PasswordService";
import RegisterPatientUseCase from "../../../use_case/patient/RegisterPatientUseCase";
import PatientController from "../../controllers/PatientController";
import AuthPatientUseCase from "../../../use_case/patient/AuthPatientUseCase";
import EmailService from "../../../infrastructure/services/EmailService";
import OtpRepository from "../../../infrastructure/database/repositories/OtpRepository";
import OtpRepository from "../../../infrastructure/repositories/OtpRepository";
import TokenService from "../../../infrastructure/services/TokenService";
import PatientAuthMiddleware from "../../middlewares/PatientAuthMiddleware";
import PatientAuthMiddleware from "../../middlewares/PatientAuthMiddleware";

const route = express();
const route = express.Router();

// Services and Repositories
const emailService = new EmailService();
const tokenService = new TokenService();
const otpRepository = new OtpRepository();
const passwordService = new PasswordService();
const patientRepository = new PatientRepository();

// Use Cases
const registerPatientUseCase = new RegisterPatientUseCase(patientRepository, passwordService);
const authPatientUseCase = new AuthPatientUseCase(patientRepository, passwordService, emailService, otpRepository, tokenService);
const authPatientUseCase = new AuthPatientUseCase(
patientRepository,
passwordService,
emailService,
otpRepository,
tokenService
);

// Controllers
const patientController = new PatientController(registerPatientUseCase, authPatientUseCase);
const patientAuthMiddleWare = new PatientAuthMiddleware (tokenService);

route.post("/", (req, res, next) => {
patientController.register(req, res, next);
// Middleware
const patientAuthMiddleware = new PatientAuthMiddleware(tokenService);


route.post("/register", (req, res, next) => {
patientController.register(req, res, next);
});
route.post("/login", (req, res, next) => {
patientController.login(req, res, next);
Expand All @@ -33,11 +47,14 @@ route.post("/resend-otp", (req, res, next) => {
route.post("/otp-verification", (req, res, next) => {
patientController.validateOtp(req, res, next);
});
route.get("/refresh",(req,res,next)=>{
patientController.refreshAccessToken(req,res,next);
route.get("/refresh", (req, res, next) => {
patientController.refreshAccessToken(req, res, next);
});
route.post("/reset-password", (req, res, next) => {
patientController.resetPassword(req, res, next);
});
route.post('/logout',patientAuthMiddleWare.exec,(req,res,next)=>{
patientController.logout(req,res,next)
route.post("/logout", patientAuthMiddleware.exec, (req, res, next) => {
patientController.logout(req, res, next);
});

export default route;
15 changes: 11 additions & 4 deletions server/src/use_case/patient/AuthPatientUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export default class LoginPatientUseCase {

if (foundedPatient.isBlocked) throw new Error("Unauthorized");

let otp = parseInt(generateOTP(6),10);
let otp = parseInt(generateOTP(6), 10);
while (otp.toString().length !== 6) {
otp = parseInt(generateOTP(6),10);
otp = parseInt(generateOTP(6), 10);
}
await this.otpRepository.create(otp, foundedPatient.email!);

Expand All @@ -39,9 +39,9 @@ export default class LoginPatientUseCase {
const patient = await this.patientRepository.findByEmail(email);
if (!patient) throw new Error("Patient Not Found");

let otp = parseInt(generateOTP(6),10);
let otp = parseInt(generateOTP(6), 10);
while (otp.toString().length !== 6) {
otp = parseInt(generateOTP(6),10);
otp = parseInt(generateOTP(6), 10);
}
await this.otpRepository.create(otp, email);
await this.emailService.sendOtp(email, patient.name!, otp);
Expand Down Expand Up @@ -78,4 +78,11 @@ export default class LoginPatientUseCase {

return { accessToken };
}

async sendForgetPasswordMail(email: string): Promise<void> {
const patient = await this.patientRepository.findByEmail(email);
if (!patient) throw new Error("Patient Not Found");
if (patient.isBlocked) throw new Error("Patient is Blocked");
await this.emailService.sendResetMail(email, patient.name!, `${process.env.CLIENT_URL}/signin/reset-password`!);
}
}

1 comment on commit 771c6b6

@vercel
Copy link

@vercel vercel bot commented on 771c6b6 Aug 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.