diff --git a/.env.example b/.env.example
index 02c9df4..6784fa8 100644
--- a/.env.example
+++ b/.env.example
@@ -23,3 +23,4 @@ POSTGRES_USER = database user
POSTGRES_DB = name of your database
POSTGRES_HOST = your database host
DOCKER_DB_CONNECTION = postgres://postgres:postgres_password@database_host:postgres_port/postgres_db
+TIME_FOR_PASSWORD_EXPIRATION = days --> password expiration time
diff --git a/__test__/product.test.ts b/__test__/product.test.ts
index d4045a9..dc1701e 100644
--- a/__test__/product.test.ts
+++ b/__test__/product.test.ts
@@ -422,6 +422,7 @@ expect(response.body).toEqual({
test("Return 500 for handle error", async () => {
const response = await request(app)
.get("/api/v1/products/review")
+ console.log(response.body);
expect(response.status).toBe(500)
})
test('It should return status 200 for removed Product',async() =>{
diff --git a/__test__/user.test.ts b/__test__/user.test.ts
index 0019e84..9939a2e 100644
--- a/__test__/user.test.ts
+++ b/__test__/user.test.ts
@@ -1,4 +1,5 @@
import request from "supertest";
+import { mocked } from "jest-mock";
import { beforeAll, beforeEach, afterEach, afterAll, test } from "@jest/globals";
import app from "../src/utils/server";
import User from "../src/sequelize/models/users";
@@ -25,14 +26,15 @@ const userData: any = {
username: "testuser5",
email: "test15@gmail.com",
password: "test12345",
+ lastPasswordUpdateTime: new Date()
};
-
const dummySeller = {
name: "dummy1234",
username: "username1234",
email: "soleilcyber00@gmail.com",
password: "1234567890",
+ lastPasswordUpdateTime: "3000, 11, 18"
};
const userTestData = {
newPassword: "Test@123",
@@ -62,7 +64,8 @@ const updateData:any = {
country: "Rwanda",
}
-
+ jest.mock('../src/jobs/isPasswordExpired');
+
describe("Testing user Routes", () => {
beforeAll(async () => {
try {
@@ -103,7 +106,6 @@ describe("Testing user Routes", () => {
const response = await request(app)
.post("/api/v1/users/register")
.send(userData);
-
expect(response.status).toBe(201);
}, 20000);
@@ -225,6 +227,7 @@ describe("Testing user Routes", () => {
roleId: 2,
})
.set("Authorization", "Bearer " + adminToken);
+
expect(response.status).toBe(200);
});
diff --git a/package.json b/package.json
index 7fe1d90..3becaa5 100644
--- a/package.json
+++ b/package.json
@@ -87,10 +87,12 @@
"cryptr": "^6.3.0",
"dotenv": "^16.4.5",
"email-validator": "^2.0.4",
+ "events": "^3.3.0",
"express": "^4.19.2",
"express-session": "^1.18.0",
"husky": "^9.0.11",
"ioredis": "^5.4.1",
+ "jest-mock": "^29.7.0",
"joi": "^17.13.0",
"jsonwebtoken": "^9.0.2",
"lint-staged": "^15.2.2",
diff --git a/src/controllers/userControllers.ts b/src/controllers/userControllers.ts
index 26219d5..4947f6a 100644
--- a/src/controllers/userControllers.ts
+++ b/src/controllers/userControllers.ts
@@ -15,7 +15,8 @@ import { updateUserRoleService } from "../services/user.service";
import { generateRandomNumber } from "../utils/generateRandomNumber";
import { env } from "../utils/env";
import { Emailschema, resetPasswordSchema } from "../schemas/resetPasswordSchema";
-import Joi from "joi";
+import { clearExpiredUserData } from "../jobs/isPasswordExpired";
+import { use } from "passport";
export const fetchAllUsers = async (req: Request, res: Response) => {
@@ -95,8 +96,9 @@ export const createUserController = async (req: Request, res: Response) => {
const { name, email, username, password, role } = req.body;
try {
+ let currentUpdateTime = new Date();
const { name, email, username, password } = req.body;
- const user = await createUserService(name, email, username, password);
+ const user = await createUserService(name, email, username, password,currentUpdateTime);
if (!user || user == null) {
return res.status(409).json({
status: 409,
@@ -136,9 +138,12 @@ export const updatePassword = async (req: Request, res: Response) => {
}
const password = await hashedPassword(newPassword);
+ const currentUpdateTime = new Date();
// @ts-ignore
- const update = await updateUserPassword(user, password);
+ const update = await updateUserPassword(user, password,currentUpdateTime);
if(update){
+ //@ts-ignore
+ clearExpiredUserData(user.id)
return res.status(200).json({ message: "Password updated successfully" });
}
} catch (err: any) {
diff --git a/src/email-templates/passwordExpiredNotification.ts b/src/email-templates/passwordExpiredNotification.ts
new file mode 100644
index 0000000..34d85fb
--- /dev/null
+++ b/src/email-templates/passwordExpiredNotification.ts
@@ -0,0 +1,57 @@
+const passwordExpirationHtmlContent = (userName: string): string => {
+ const htmlContent = `
+
+
+
+
+
+ Password Updated Confirmation
+
+
+
+
+
Password Expiration
+
Dear ${userName},
+
Your password has been Expired.
+ Vist a website to update it to continue using the system.
+
Thank you.
+
Your Website Team
+
+
+
+
+ `;
+ return htmlContent;
+ };
+
+ export { passwordExpirationHtmlContent };
+
\ No newline at end of file
diff --git a/src/jobs/isPasswordExpired.ts b/src/jobs/isPasswordExpired.ts
new file mode 100644
index 0000000..dfc0d0c
--- /dev/null
+++ b/src/jobs/isPasswordExpired.ts
@@ -0,0 +1,42 @@
+import cron from 'node-cron';
+import EventEmitter from 'events';
+import { getAllUsers } from '../services/user.service';
+import { env } from '../utils/env';
+import { sendEmailService } from '../services/mail.service';
+import { passwordExpirationHtmlContent } from '../email-templates/passwordExpiredNotification';
+
+let latestExpiredUserData = new Set();
+export let expiredUserData = new Set();
+
+class UpdatePasswordEventsEmitter extends EventEmitter {};
+export const passwordEventEmitter = new UpdatePasswordEventsEmitter();
+
+export const isPasswordExpired = () =>{
+ const millisecondsPerDay = 1000 * 60 * 60 * 24;
+ cron.schedule('* * * * * *', async() => {
+ const currentTime = Date.now();
+ const users = await getAllUsers();
+ const emailPromises = [];
+ for (const user of users) {
+ const lastPasswordUpdateTime:any = user.dataValues.lastPasswordUpdateTime;
+ const timeDifference:any = currentTime - lastPasswordUpdateTime;
+ if(timeDifference >= (millisecondsPerDay * parseInt(env.password_expiration_time))&& !latestExpiredUserData.has(user.id)){
+ passwordEventEmitter.emit("password expired",user);
+ latestExpiredUserData.add(user.id);
+ emailPromises.push(
+ sendEmailService(user, 'Password expired', passwordExpirationHtmlContent(user.name))
+ );
+ };
+ };
+ await Promise.all(emailPromises);
+ expiredUserData = new Set([...latestExpiredUserData]);
+ });
+}
+passwordEventEmitter.on("password expired", (user) => {
+ expiredUserData.add(user);
+});
+
+export const clearExpiredUserData = (userId:any) => {
+ latestExpiredUserData.delete(userId);
+ expiredUserData.delete(userId);
+};
\ No newline at end of file
diff --git a/src/middlewares/isPasswordOutOfDate.ts b/src/middlewares/isPasswordOutOfDate.ts
new file mode 100644
index 0000000..30e1283
--- /dev/null
+++ b/src/middlewares/isPasswordOutOfDate.ts
@@ -0,0 +1,24 @@
+import { Request,Response,NextFunction } from 'express';
+import { isLoggedIn } from './isLoggedIn';
+import { expiredUserData } from '../jobs/isPasswordExpired';
+
+export const isPasswordOutOfDate = async(req:Request,res:Response,next:NextFunction) =>{
+ try {
+ await isLoggedIn(req,res,() => {});
+ //@ts-ignore
+ const loggedInUserId: any = req.user.id;
+ const expiredUserIds = new Set([...expiredUserData].map((user: any) => user));
+ if (expiredUserIds.has(loggedInUserId)) {
+ return res.status(403).json({
+ message: "Your password expired, Update it to continue"
+ });
+ };
+ next();
+ } catch (error:any) {
+ console.log('Error has occured',error.message);
+ next(error)
+ }
+}
+
+
+
diff --git a/src/routes/cartRoutes.ts b/src/routes/cartRoutes.ts
index dfcc4ca..578705f 100644
--- a/src/routes/cartRoutes.ts
+++ b/src/routes/cartRoutes.ts
@@ -4,14 +4,14 @@ import { isLoggedIn } from "../middlewares/isLoggedIn";
import { isQuantityValid } from "../middlewares/isQuantityValid";
import { isProductFound } from "../middlewares/isProductFound";
import { validateCart, validateRemoveProductQty, validateUpdateProductQty } from "../middlewares/cartValidation";
+import { isPasswordOutOfDate } from "../middlewares/isPasswordOutOfDate";
const cartRoutes = Router();
-cartRoutes.get("/",isLoggedIn,viewUserCart);
-cartRoutes.post("/",isLoggedIn,validateCart, isQuantityValid, addItemToCart);
-// cartRoutes.post("/",isLoggedIn,validateCart, isQuantityValid, addItemToCart);
-cartRoutes.put("/",isLoggedIn,validateRemoveProductQty, isProductFound,removeProductFromCart);
-cartRoutes.delete("/",isLoggedIn, clearAllProductFromCart);
-cartRoutes.patch("/",isLoggedIn,validateUpdateProductQty, isProductFound, updateProductQuantity);
+cartRoutes.get("/",isLoggedIn,isPasswordOutOfDate,viewUserCart);
+cartRoutes.post("/",isLoggedIn,isPasswordOutOfDate,validateCart, isQuantityValid, addItemToCart);
+cartRoutes.put("/",isLoggedIn,isPasswordOutOfDate,validateRemoveProductQty, isProductFound,removeProductFromCart);
+cartRoutes.delete("/",isLoggedIn,isPasswordOutOfDate, clearAllProductFromCart);
+cartRoutes.patch("/",isLoggedIn,isPasswordOutOfDate,validateUpdateProductQty, isProductFound, updateProductQuantity);
export default cartRoutes;
diff --git a/src/routes/categoriesRoutes.ts b/src/routes/categoriesRoutes.ts
index 93122aa..6007672 100644
--- a/src/routes/categoriesRoutes.ts
+++ b/src/routes/categoriesRoutes.ts
@@ -11,12 +11,13 @@ import {
import { categoriesDataSchema } from "../schemas/categorySchema";
import { isAseller } from "../middlewares/sellerAuth";
import { isLoggedIn } from "../middlewares/isLoggedIn";
+import { isPasswordOutOfDate } from "../middlewares/isPasswordOutOfDate";
const categoriesRouter = Router();
-categoriesRouter.get("/",isLoggedIn,isAseller,fetchCategories);
-categoriesRouter.get("/:id",isLoggedIn,isAseller,fetchSingleCategory);
-categoriesRouter.post("/",isLoggedIn,isAseller,upload.single('image'),validateSchema(categoriesDataSchema)
+categoriesRouter.get("/",isLoggedIn,isPasswordOutOfDate,isAseller,fetchCategories);
+categoriesRouter.get("/:id",isLoggedIn,isPasswordOutOfDate,isAseller,fetchSingleCategory);
+categoriesRouter.post("/",isLoggedIn,isPasswordOutOfDate,isAseller,upload.single('image'),validateSchema(categoriesDataSchema)
,addCategories);
-categoriesRouter.patch("/:id",isAseller,upload.single('image'),categoriesUpdate);
-categoriesRouter.delete("/:id",isLoggedIn,isAseller,removeCategories);
+categoriesRouter.patch("/:id",isAseller,isPasswordOutOfDate,upload.single('image'),categoriesUpdate);
+categoriesRouter.delete("/:id",isLoggedIn,isPasswordOutOfDate,isAseller,removeCategories);
export default categoriesRouter;
\ No newline at end of file
diff --git a/src/routes/notificationRoutes.ts b/src/routes/notificationRoutes.ts
index 5392919..29f90e3 100644
--- a/src/routes/notificationRoutes.ts
+++ b/src/routes/notificationRoutes.ts
@@ -1,13 +1,14 @@
import { Router } from "express";
import { getUserNotifications, readNotification } from "../controllers/notificationController";
import { isLoggedIn } from "../middlewares/isLoggedIn";
+import { isPasswordOutOfDate } from "../middlewares/isPasswordOutOfDate";
const notificationRoutes = Router()
-notificationRoutes.get("/",isLoggedIn,getUserNotifications)
-notificationRoutes.get("/:id",isLoggedIn,readNotification)
+notificationRoutes.get("/",isLoggedIn,isPasswordOutOfDate,getUserNotifications)
+notificationRoutes.get("/:id",isLoggedIn,isPasswordOutOfDate,readNotification)
diff --git a/src/routes/paymentRoutes.ts b/src/routes/paymentRoutes.ts
index bae37d8..31b6d7a 100644
--- a/src/routes/paymentRoutes.ts
+++ b/src/routes/paymentRoutes.ts
@@ -3,10 +3,11 @@ import * as paymentController from "../controllers/paymentController"
import { isLoggedIn } from "../middlewares/isLoggedIn";
import { hasItemsInCart } from "../middlewares/payments";
import { isAbuyer } from "../middlewares/isAbuyer";
+import { isPasswordOutOfDate } from "../middlewares/isPasswordOutOfDate";
const paymentRouter = express.Router()
-paymentRouter.post('/checkout', isLoggedIn, isAbuyer, hasItemsInCart, paymentController.createCheckoutSession);
+paymentRouter.post('/checkout', isLoggedIn,isPasswordOutOfDate, isAbuyer, hasItemsInCart, paymentController.createCheckoutSession);
paymentRouter.get('/success', paymentController.handleSuccess);
paymentRouter.get('/canceled', paymentController.handleFailure);
diff --git a/src/routes/productsRoute.ts b/src/routes/productsRoute.ts
index cd8f5f3..c445721 100644
--- a/src/routes/productsRoute.ts
+++ b/src/routes/productsRoute.ts
@@ -9,20 +9,21 @@ import { isCategoryExist } from "../middlewares/isCategoryExist";
import { addReviewController, deleteReviewController, getreviewController, updateReviewController} from "../controllers/productControllers"
import { addReviewValidate, updateReviewValidate } from "../schemas/review";
import { hasPurchasedProduct } from "../middlewares/hasPurchased";
+import { isPasswordOutOfDate } from "../middlewares/isPasswordOutOfDate";
const productsRouter = Router();
-productsRouter.get("/search", searchProductController)
+productsRouter.get("/search",isPasswordOutOfDate, searchProductController)
-productsRouter.get("/",fetchProducts);
-productsRouter.get("/:id",fetchSingleProduct);
-productsRouter.post("/",isLoggedIn,isAseller,upload.array('images'),
+productsRouter.get("/",isLoggedIn,isPasswordOutOfDate,fetchProducts);
+productsRouter.get("/:id",isLoggedIn,isPasswordOutOfDate,fetchSingleProduct);
+productsRouter.post("/",isLoggedIn,isPasswordOutOfDate,isAseller,upload.array('images'),
validateSchema(productDataSchema),isCategoryExist,addProducts);
-productsRouter.patch("/:id",isLoggedIn,isAseller,upload.array('images'),productsUpdate);
-productsRouter.patch("/:id/status",isLoggedIn,isAseller,productAvailability);
-productsRouter.delete("/:id",isLoggedIn,isAseller,removeProducts);
+productsRouter.patch("/:id",isLoggedIn,isPasswordOutOfDate,isAseller,upload.array('images'),productsUpdate);
+productsRouter.patch("/:id/status",isLoggedIn,isPasswordOutOfDate,isAseller,productAvailability);
+productsRouter.delete("/:id",isLoggedIn,isPasswordOutOfDate,isAseller,removeProducts);
productsRouter.get("/:pid/reviews", getreviewController)
-productsRouter.post("/:pid/reviews",isLoggedIn, validateSchema(addReviewValidate), hasPurchasedProduct, addReviewController)
-productsRouter.delete("/:pid/reviews", isLoggedIn, deleteReviewController)
-productsRouter.patch("/:pid/reviews", isLoggedIn, validateSchema(updateReviewValidate), updateReviewController)
+productsRouter.post("/:pid/reviews",isLoggedIn,isPasswordOutOfDate,validateSchema(addReviewValidate), hasPurchasedProduct, addReviewController)
+productsRouter.delete("/:pid/reviews", isLoggedIn,isPasswordOutOfDate, deleteReviewController)
+productsRouter.patch("/:pid/reviews", isLoggedIn,isPasswordOutOfDate, validateSchema(updateReviewValidate), updateReviewController)
export default productsRouter;
\ No newline at end of file
diff --git a/src/routes/roleRoutes.ts b/src/routes/roleRoutes.ts
index 132324a..284011d 100644
--- a/src/routes/roleRoutes.ts
+++ b/src/routes/roleRoutes.ts
@@ -4,12 +4,13 @@ import { isLoggedIn } from '../middlewares/isLoggedIn';
import{isAdmin} from '../middlewares/isAdmin';
import { validateSchema } from '../middlewares/validator';
import {roleSchema} from '../schemas/roleSchema';
+import { isPasswordOutOfDate } from '../middlewares/isPasswordOutOfDate';
const RoleRouter = express.Router();
-RoleRouter.post('/', isLoggedIn, isAdmin, validateSchema(roleSchema), roleController.createRole);
-RoleRouter.get('/', roleController.getRoles);
-RoleRouter.patch('/:id', isLoggedIn, isAdmin, validateSchema(roleSchema),roleController.updateRole);
-RoleRouter.delete('/:id', isLoggedIn, isAdmin, roleController.deleteRole);
+RoleRouter.post('/', isLoggedIn,isPasswordOutOfDate, isAdmin, validateSchema(roleSchema), roleController.createRole);
+RoleRouter.get('/',roleController.getRoles);
+RoleRouter.patch('/:id', isLoggedIn,isPasswordOutOfDate, isAdmin, validateSchema(roleSchema),roleController.updateRole);
+RoleRouter.delete('/:id', isLoggedIn,isPasswordOutOfDate, isAdmin, roleController.deleteRole);
export default RoleRouter;
\ No newline at end of file
diff --git a/src/routes/userRoutes.ts b/src/routes/userRoutes.ts
index d41bdca..c3e9f89 100644
--- a/src/routes/userRoutes.ts
+++ b/src/routes/userRoutes.ts
@@ -17,33 +17,29 @@ import { roleExist } from "../middlewares/roleExist";
import { userExist } from "../middlewares/userExist";
import { isDisabled } from "../middlewares/isDisabled";
import { verifyToken } from "../middlewares/verifyToken";
-
-
-
+import { isPasswordOutOfDate } from "../middlewares/isPasswordOutOfDate";
const userRoutes = Router();
userRoutes.get("/", fetchAllUsers);
-userRoutes.put("/passwordupdate", isLoggedIn, validateSchema(passwordUpdateSchema), updatePassword)
+userRoutes.put("/passwordupdate", isLoggedIn,validateSchema(passwordUpdateSchema), updatePassword)
userRoutes.post("/login", emailValidation,validateSchema(logInSchema),isDisabled,userLogin);
-userRoutes.post("/register", emailValidation, validateSchema(signUpSchema), createUserController);
-userRoutes.put("/passwordupdate", isLoggedIn, validateSchema(passwordUpdateSchema), updatePassword);
+userRoutes.post("/register", emailValidation,validateSchema(signUpSchema), createUserController);
userRoutes.get("/2fa-verify/:token",tokenVerification);
userRoutes.post("/2fa-verify",otpVerification);
userRoutes.get('/profile',
- isLoggedIn,
+ isLoggedIn, isPasswordOutOfDate,
getProfileController
);
userRoutes.post('/logout', isLoggedIn, logout);
userRoutes.patch('/profile',
- isLoggedIn,
+ isLoggedIn,isPasswordOutOfDate,
upload.single('profileImage'),
validateSchema(profileSchemas),
isUploadedFileImage,
updateProfileController
-)
-
-userRoutes.patch("/:id/role",isLoggedIn, isAdmin, validateSchema(roleUpdateSchema), userExist, roleExist, updateUserRole)
-userRoutes.patch('/:userId/status',isLoggedIn, isAdmin, changeUserAccountStatus);
+);
+userRoutes.patch("/:id/role",isLoggedIn,isPasswordOutOfDate, isAdmin, validateSchema(roleUpdateSchema), userExist, roleExist, updateUserRole)
+userRoutes.patch('/:userId/status',isLoggedIn,isPasswordOutOfDate, isAdmin, changeUserAccountStatus);
userRoutes.get("/auth/google", authenticateUser);
userRoutes.get("/auth/google/callback", callbackFn);
diff --git a/src/routes/wishesRoutes.ts b/src/routes/wishesRoutes.ts
index 87185bc..18857e7 100644
--- a/src/routes/wishesRoutes.ts
+++ b/src/routes/wishesRoutes.ts
@@ -3,12 +3,13 @@ import * as wishesController from '../controllers/wishesController'
import { isLoggedIn } from '../middlewares/isLoggedIn'
import { isAseller } from '../middlewares/sellerAuth'
import { isAbuyer } from '../middlewares/isAbuyer'
+import { isPasswordOutOfDate } from '../middlewares/isPasswordOutOfDate'
const wishesRouter = express.Router()
-wishesRouter.post('/wishes', isLoggedIn, isAbuyer, wishesController.addToWishes)
-wishesRouter.get('/wishes', isLoggedIn, wishesController.getUserWishes)
-wishesRouter.delete('/products/:id/wishes', isLoggedIn, isAbuyer, wishesController.deleteWish)
-wishesRouter.get('/products/:id/wishes', isLoggedIn, isAseller, wishesController.getProductWishes)
+wishesRouter.post('/wishes', isLoggedIn,isPasswordOutOfDate,isAbuyer, wishesController.addToWishes)
+wishesRouter.get('/wishes', isLoggedIn,isPasswordOutOfDate, wishesController.getUserWishes)
+wishesRouter.delete('/products/:id/wishes', isLoggedIn,isPasswordOutOfDate, isAbuyer, wishesController.deleteWish)
+wishesRouter.get('/products/:id/wishes', isLoggedIn,isPasswordOutOfDate, isAseller, wishesController.getProductWishes)
export default wishesRouter
\ No newline at end of file
diff --git a/src/schemas/signUpSchema.ts b/src/schemas/signUpSchema.ts
index 9d1c448..2a435b2 100644
--- a/src/schemas/signUpSchema.ts
+++ b/src/schemas/signUpSchema.ts
@@ -5,6 +5,7 @@ export const signUpSchema = Joi.object({
username: Joi.string().min(4).required(),
email: Joi.string().min(6).required().email(),
password: Joi.string().min(6).max(20).required(),
+ lastPasswordUpdateTime:Joi.date(),
role: Joi.string().optional(),
}).options({ allowUnknown: false });
diff --git a/src/sequelize/config/config.js b/src/sequelize/config/config.js
index 6560c78..28f37cb 100644
--- a/src/sequelize/config/config.js
+++ b/src/sequelize/config/config.js
@@ -10,7 +10,6 @@ module.exports = {
test: {
url: process.env.TEST_DB,
dialect: "postgres",
-
dialectOptions:
process.env.IS_REMOTE === "true"
? {
diff --git a/src/sequelize/migrations/b20240502074237-user.js b/src/sequelize/migrations/b20240502074237-user.js
index 780c390..471ec5d 100644
--- a/src/sequelize/migrations/b20240502074237-user.js
+++ b/src/sequelize/migrations/b20240502074237-user.js
@@ -28,6 +28,9 @@ module.exports = {
type: Sequelize.STRING,
allowNull: true,
},
+ lastPasswordUpdateTime:{
+ type: Sequelize.DATE,
+ },
createdAt: {
allowNull: false,
type: Sequelize.DATE,
diff --git a/src/sequelize/models/users.ts b/src/sequelize/models/users.ts
index b8c864b..c467133 100644
--- a/src/sequelize/models/users.ts
+++ b/src/sequelize/models/users.ts
@@ -12,6 +12,7 @@ export interface UserAttributes {
username: string;
email: string;
password: string | undefined;
+ lastPasswordUpdateTime?: Date;
roleId: number | undefined;
isActive?:boolean;
createdAt?: Date;
@@ -24,6 +25,7 @@ class User extends Model implements UserAttributes {
username!: string;
email!: string;
password!: string;
+ lastPasswordUpdateTime!:Date | undefined;
isActive: boolean | undefined;
roleId!: number | undefined;
createdAt!: Date | undefined;
@@ -54,6 +56,9 @@ User.init(
allowNull: true,
type: DataTypes.STRING,
},
+ lastPasswordUpdateTime:{
+ type: DataTypes.DATE
+ },
roleId: {
type: DataTypes.NUMBER,
allowNull: false,
diff --git a/src/sequelize/seeders/b20240412144111-demo-user.js b/src/sequelize/seeders/b20240412144111-demo-user.js
index e07fc49..a15c350 100644
--- a/src/sequelize/seeders/b20240412144111-demo-user.js
+++ b/src/sequelize/seeders/b20240412144111-demo-user.js
@@ -29,6 +29,7 @@ module.exports = {
username: "soleil00",
email: "soleil@soleil00.com",
password: await bcrypt.hash("soleil00", 10),
+ lastPasswordUpdateTime:new Date(),
roleId: 3,
createdAt: new Date(),
updatedAt: new Date(),
@@ -38,6 +39,7 @@ module.exports = {
username: "yes",
email: "soleil@soleil0w.com",
password: await bcrypt.hash("soleil00", 10),
+ lastPasswordUpdateTime:new Date(),
roleId: 1,
createdAt: new Date(),
updatedAt: new Date(),
@@ -47,6 +49,7 @@ module.exports = {
username: "jehovanis",
email: "mugabo.kefa00@gmail.com",
password: await bcrypt.hash("Test@123", 10),
+ lastPasswordUpdateTime:new Date(),
roleId: 2,
createdAt: new Date(),
updatedAt: new Date(),
@@ -56,6 +59,7 @@ module.exports = {
username: "Jabo24",
email: "jaboinnovates@gmail.com",
password: await bcrypt.hash("Test@123", 10),
+ lastPasswordUpdateTime:new Date(),
roleId: 2,
createdAt: new Date(),
updatedAt: new Date(),
diff --git a/src/services/user.service.ts b/src/services/user.service.ts
index a30d7c5..4ee6984 100644
--- a/src/services/user.service.ts
+++ b/src/services/user.service.ts
@@ -29,7 +29,7 @@ export const callbackFn = passport.authenticate("google", {
export const getAllUsers = async () => {
try {
const users = await User.findAll({
- attributes: ['id', 'name', 'username', 'email', 'roleId', 'createdAt', 'updatedAt','isActive'],
+ attributes: ['id', 'name', 'username', 'email','lastPasswordUpdateTime', 'roleId', 'createdAt', 'updatedAt','isActive'],
});
if (users.length === 0) {
console.log("no user");
@@ -52,7 +52,8 @@ export const loggedInUser = async (email: string) => {
throw new Error(err.message);
}
};
-export const createUserService = async (name: string, email: string, username: string, password: string): Promise => {
+ //changes
+export const createUserService = async (name: string, email: string, username: string, password: string,lastPasswordUpdateTime:Date): Promise => {
const existingUser = await User.findOne({ where: { email } });
if (existingUser) {
return null;
@@ -67,6 +68,8 @@ export const createUserService = async (name: string, email: string, username: s
name,
email,
username,
+ //changes
+ lastPasswordUpdateTime,
password: hashPassword,
});
await Profile.create({
@@ -110,8 +113,8 @@ export const findUserById = async (id: string) => {
throw new Error(error.message);
}
};
-export const updateUserPassword = async (user: User, password: string) => {
- const update = await User.update({ password: password}, { where: { id: user.id}})
+export const updateUserPassword = async (user: User, password: string, lastPasswordUpdateTime: Date) => {
+ const update = await User.update({ password: password,lastPasswordUpdateTime: lastPasswordUpdateTime}, { where: { id: user.id}})
return update
};
diff --git a/src/types.ts b/src/types.ts
index dc44242..26ea412 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -6,6 +6,7 @@ export interface IUser {
username: string;
email: string;
password: string;
+ lastPasswordUpdateTime?:Date;
roleId?: number;
userRole?:IRole;
createdAt?: Date;
diff --git a/src/utils/env.ts b/src/utils/env.ts
index 1f03768..345c50e 100644
--- a/src/utils/env.ts
+++ b/src/utils/env.ts
@@ -17,5 +17,6 @@ export const env = {
local_url: `${process.env.LOCAL_URL}:${process.env.PORT}/api/v1/users/2fa-verify`,
redis_url: process.env.REDIS_URL as string,
client_url: process.env.CLIENT_URL as string,
- stripe_secret: process.env.STRIPE_SECRET_KEY as string
+ stripe_secret: process.env.STRIPE_SECRET_KEY as string,
+ password_expiration_time: process.env.TIME_FOR_PASSWORD_EXPIRATION as string
};
\ No newline at end of file
diff --git a/src/utils/server.ts b/src/utils/server.ts
index f1012fb..c650cee 100644
--- a/src/utils/server.ts
+++ b/src/utils/server.ts
@@ -5,6 +5,7 @@ import { createServer, Server as HTTPServer } from "http";
import path from "path";
import { Server as SocketIOServer } from "socket.io";
import socket from "../config/socketCofing";
+import { isPasswordExpired } from "../jobs/isPasswordExpired";
import appROutes from "../routes";
import homeRoute from "../routes/homeRoutes";
@@ -55,7 +56,7 @@ app.use(
app.use(passport.initialize());
app.use(passport.session());
-app.use("/api/v1/chats", express.static(path.join(__dirname, "../../public")));
+app.use("/api/v1/chats", express.static(path.join(__dirname, '../../public')));
app.use("/", homeRoute);
app.use("/api/v1", appROutes);
@@ -65,4 +66,5 @@ app.use("/api/v1/roles", RoleRouter);
socket(io);
findExpiredProduct()
+isPasswordExpired();
export default server;