Skip to content

Commit

Permalink
Merge pull request #73 from atlp-rwanda/ft-updating-password-after-x-…
Browse files Browse the repository at this point in the history
…amount-of-time

Ft updating password after x amount of time
  • Loading branch information
UmuhireJessie authored Jul 24, 2024
2 parents fe96437 + 93b04c3 commit 78a6c4b
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 29 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ CLOUDINARY_NAME="your clouinary_name"
CLOUDINARY_API_KEY='your cloudinary_api_key'
CLOUDINARY_API_SECRET='your cloudinary_api_secret'
STRIPE_SECRET_KEY='your stripe secret key'
PASSWORD_EXPIRATION_PERIOD_MINUTES='minutes'
1 change: 1 addition & 0 deletions src/controllers/userControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,7 @@ export async function resetPassword(
user.password = hashedPassword;
user.resetPasswordToken = undefined;
user.resetPasswordExpires = undefined;
user.passwordLastChanged = new Date();

await user.save();

Expand Down
14 changes: 12 additions & 2 deletions src/cronJob/password.cron.job.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import cron from 'node-cron';
import { db } from '../database/models/index';
import sendPasswordUpdateNotification from '../utils/sendChangePasswordNofication';
import { generateToken } from '../helps/generateToken';
import dotenv from 'dotenv';

dotenv.config();

const startCronJob = (): void => {
const cronSchedule = process.env.CRON_SCHEDULE || '* * * * *';
const passwordExpirationPeriodMinutes = parseInt(
process.env.PASSWORD_EXPIRATION_PERIOD_MINUTES || '131527',
process.env.PASSWORD_EXPIRATION_PERIOD_MINUTES || '261561.6',
10,
);

Expand All @@ -24,7 +25,16 @@ const startCronJob = (): void => {
minutesSinceLastChange >= passwordExpirationPeriodMinutes &&
minutesSinceLastChange < 2 * passwordExpirationPeriodMinutes
) {
sendPasswordUpdateNotification(user);
const token = generateToken(
user.userId,
user.email,
user.firstName,
user.lastName,
user.role,
user.passwordLastChanged,
user.isVerified,
);
sendPasswordUpdateNotification(user, token);
}
});
});
Expand Down
79 changes: 67 additions & 12 deletions src/helps/MailTemplate.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,73 @@
const millisecondsToDate = require('./datesConversion');
import dotenv from 'dotenv';
dotenv.config();

export const sendChangePasswordNoficationMail = `
<div style="font-family: Arial, sans-serif; background-color: #ffffff; padding: 20px; border-radius: 10px; box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.1);">
export const sendChangePasswordNotificationMail = (
userToken: any,
userName: string,
) => `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: Arial, sans-serif;
background-color: #ffffff;
padding: 20px;
}
.email-container {
background-color: #ffffff;
padding: 20px;
border-radius: 10px;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
border: 1px solid #FF6D18 ;
}
.email-content p {
color: #666666;
text-align: justify;
}
.email-content a {
display: inline-block;
width: 200px;
margin: 20px auto;
text-align: center;
background-color: #0F1848;
color: #ffffff;
text-decoration: none;
padding: 10px;
border-radius: 5px;
transition: background-color 0.3s ease;
}
.email-content a:hover {
background-color: #FF6D18;
}
.email-content p.center {
text-align: center;
}
</style>
</head>
<body>
<div class="email-container">
<div class="email-content">
<p style="color: #666666; text-align: left;">Dear ${userName},</p>
<p>We are writing to inform you that it is necessary to update your password for security purposes.</p>
<p>Failure to update your password will result in the inability to perform any actions until the update is completed.</p>
<p>Please follow the link below to proceed with the password update:</p>
<p class="center">
<a href="${process.env.CLIENT_URL}/reset/new-password?q=${userToken}">Update Password</a>
</p>
<p class="center">Thank you for your prompt attention to this matter.</p>
<p class="center">Sincerely,</p>
<p class="center">OnesAndZeroes Support Team</p>
</div>
</div>
</body>
</html>
<p style="color: #666666; text-align: justify;">We are writing to inform you that it is necessary to update your password for security purposes.</p>
<p style="color: #666666; text-align: justify;">Failure to update your password within one day will result in the inability to perform any actions until the update is completed.</p>
<p style="color: #666666; text-align: justify;">Please follow the link below to proceed with the password update:</p>
<p style="text-align: center;">
<a href="https://onesandzeroes/users/update" style="display: inline-block; width: 200px; margin: 20px auto; text-align: center; background-color: #007bff; color: #ffffff; text-decoration: none; padding: 10px; border-radius: 5px;">Update Password</a>
</p>
<p style="color: #666666; text-align: center;">Thank you for your prompt attention to this matter.</p>
<p style="color: #666666; text-align: center;">Sincerely,</p>
<p style="color: #666666; text-align: center;">OnesAndZeroes Support Team</p>
</div>
`;
export const sendExpiredEmail = (products: any[]) => {
const productRows = products
Expand Down
4 changes: 2 additions & 2 deletions src/helps/nodemailer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ async function sendEmail(
<head>
</head>
<body style="background-color: #f4f4f4; padding: 20px;">
<h1 style="color: #333333; text-align: center;">OnesAndZeroes</h1>
<p style="color: #666666; text-align: left;">Dear ${firstName},</p>
<h1 style="color: #333333; text-align: center;"></h1>
<p style="color: #666666; text-align: left;"></p>
${html}
</body>
</html>
Expand Down
23 changes: 14 additions & 9 deletions src/middleware/changePasswordIgnored.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Request, Response, NextFunction } from 'express';
import { verifyToken } from '../helps/verifyToken';

const JWT_SECRET = process.env.JWT_SECRET;

export function changePasswordIgnored(
req: Request,
res: Response,
Expand All @@ -13,18 +11,25 @@ export function changePasswordIgnored(
if (token) {
const decoded = verifyToken(token);
if (decoded) {
const lastPasswordChangeDate = new Date(
decoded.passwordLastChanged * 1000,
);
const lastPasswordChangeDate = new Date(decoded.passwordLastChanged);

const minutes = process.env.PASSWORD_EXPIRATION_PERIOD_MINUTES;
const expirationPeriod = minutes ? parseInt(minutes, 10) : undefined;

if (expirationPeriod === undefined) {
console.error('PASSWORD_EXPIRATION_PERIOD_MINUTES is not defined');
return res.status(500).send('Server configuration error');
}

const currentTime = new Date();
const oneDayInMillis = 24 * 60 * 60 * 1000;
const timeInMillis = expirationPeriod * 60 * 1000; // Correct conversion from minutes to milliseconds

if (
currentTime.getTime() - lastPasswordChangeDate.getTime() >
oneDayInMillis &&
req.path !== '/login'
timeInMillis &&
req.path !== `/login`
) {
return res.redirect('https://onesandzeroes/users/update');
res.redirect(`${process.env.CLIENT_URL}/reset/new-password?q=${token}`);
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const { productExpireTask } = require('./cronJob/productsCron');
import chats from './helps/chats';

import { cartExpiryJob } from './cronJob/cartExpiry.job';
import path from 'path';

dotenv.config();
productExpireTask.start();
Expand All @@ -33,6 +34,8 @@ const io = new Server(server, {
: ['http://localhost:5173', 'http://localhost:7000/api'],
},
});
// Register your middleware
// app.use(changePasswordIgnored);
app.use(cors());
app.use(express.json());
app.use(morgan('dev'));
Expand All @@ -56,7 +59,7 @@ app.use(passport.initialize());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(cookieParser());
app.use('/api', changePasswordIgnored, routes);
app.use('/api', routes);
app.use('/docs', swaggerUi.serve, swaggerUi.setup(specs));
app.use('/auth', AuthRouters);

Expand Down
10 changes: 7 additions & 3 deletions src/utils/sendChangePasswordNofication.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import sendMail from '../helps/nodemailer';
import { sendChangePasswordNoficationMail } from '../helps/MailTemplate';
function changePasswordUpdateNotification(user: any): void {
import { sendChangePasswordNotificationMail } from '../helps/MailTemplate';
function changePasswordUpdateNotification(user: any, token: string): void {
const email: string = user.email;
const userName: string = user.firstName;
const userToken: string = token;
const subject: string = 'You are required to update your password';
const htmlContent: string = sendChangePasswordNoficationMail;
const htmlContent: string = sendChangePasswordNotificationMail(
userToken,
userName,
);
sendMail(email, userName, subject, htmlContent);
}

Expand Down

0 comments on commit 78a6c4b

Please sign in to comment.