From 4da50fe1608adaecd8d265964400cca07ffa3386 Mon Sep 17 00:00:00 2001 From: Fabrice NZAYISENGA Date: Tue, 14 Mar 2023 15:02:22 +0200 Subject: [PATCH 1/2] setting timer on user model --- src/app.ts | 2 ++ src/models/User.ts | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/app.ts b/src/app.ts index 2ad2e2c..9fda66f 100644 --- a/src/app.ts +++ b/src/app.ts @@ -34,6 +34,8 @@ app.use(permissionRoutes); app.use(rolePermissionRoutes); app.use(cookieParser()) app.use('/products', productRoutes) +// const passwordExpirationTime = process.env.PASSWORD_EXPIRATION_TIME || "90 days"; + const PORT = process.env.PORT || 3000; app.get("/", (req, res) => diff --git a/src/models/User.ts b/src/models/User.ts index 50394fc..06c9c24 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -29,6 +29,10 @@ const USER = sequelizedb.define("user", { ); }, }, + passwordLastChanged: { + type: DataTypes.DATE, + allowNull: true, + }, }); USER.sync(); From 215ac675c270490bf1ed92b1060977974012bf9c Mon Sep 17 00:00:00 2001 From: Fabrice NZAYISENGA Date: Thu, 16 Mar 2023 00:45:59 +0200 Subject: [PATCH 2/2] setting up cron job --- package-lock.json | 217 ++++++++++++++++-- package.json | 4 +- .../__tests__/verifications.test.ts | 5 - src/controllers/cronJob.ts | 102 ++++++++ src/models/User.ts | 10 +- 5 files changed, 313 insertions(+), 25 deletions(-) create mode 100644 src/controllers/cronJob.ts diff --git a/package-lock.json b/package-lock.json index e4bacdc..31b619e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "connect-redis": "^6.1.3", "cookie-parser": "^1.4.6", "country-data-list": "^1.2.3", + "cron": "^2.2.0", "crypto": "^1.0.1", "dotenv": "^16.0.3", "eslint-config-prettier": "^8.6.0", @@ -25,6 +26,7 @@ "jsonwebtoken": "^9.0.0", "multer": "^1.4.5-lts.1", "nock": "^13.3.0", + "node-cron": "^3.0.2", "nodemailer": "^6.9.1", "passport": "^0.6.0", "passport-google-oauth2": "^0.2.0", @@ -3103,6 +3105,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cron": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-2.2.0.tgz", + "integrity": "sha512-GPiI3OgMv83XRtEUc2gUdaLvJhO3XbLN288layOBkDTupg0RK5IECNGpkykIMHg+muVR2bxt29b0xvCAcBrjYQ==", + "dependencies": { + "luxon": "^3.2.1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -5577,6 +5587,14 @@ "node": ">=10" } }, + "node_modules/luxon": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", + "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==", + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -5787,6 +5805,34 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5846,6 +5892,17 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" }, + "node_modules/node-cron": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz", + "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==", + "dependencies": { + "uuid": "8.3.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", @@ -7017,9 +7074,9 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/sequelize": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.28.0.tgz", - "integrity": "sha512-+WHqvUQgTp19GLkt+gyQ+F6qg+FIEO2O5F9C0TOYV/PjZ2a/XwWvVkL1NCkS4VSIjVVvAUutiW6Wv9ofveGaVw==", + "version": "6.29.3", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.29.3.tgz", + "integrity": "sha512-iLbrN//Eh18zXIlNEUNQx7lk5R+SF39m+66bnrT3x8WB8sbxMH2hF4vw8RIa9ZzB1+c94rclMv/i8fngXmb/4A==", "funding": [ { "type": "opencollective", @@ -7034,7 +7091,7 @@ "inflection": "^1.13.2", "lodash": "^4.17.21", "moment": "^2.29.1", - "moment-timezone": "^0.5.34", + "moment-timezone": "^0.5.35", "pg-connection-string": "^2.5.0", "retry-as-promised": "^7.0.3", "semver": "^7.3.5", @@ -9675,6 +9732,15 @@ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, + "@types/multer": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz", + "integrity": "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, "@types/node": { "version": "18.11.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", @@ -10077,6 +10143,11 @@ "picomatch": "^2.0.4" } }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, "aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -10337,8 +10408,7 @@ "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "buffer-writer": { "version": "2.0.0", @@ -10346,6 +10416,14 @@ "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", "dev": true }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "requires": { + "streamsearch": "^1.1.0" + } + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -10505,6 +10583,46 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "connect-redis": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-6.1.3.tgz", @@ -10568,8 +10686,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, "country-data-list": { "version": "1.2.3", @@ -10619,6 +10736,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "cron": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-2.2.0.tgz", + "integrity": "sha512-GPiI3OgMv83XRtEUc2gUdaLvJhO3XbLN288layOBkDTupg0RK5IECNGpkykIMHg+muVR2bxt29b0xvCAcBrjYQ==", + "requires": { + "luxon": "^3.2.1" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -11726,6 +11851,11 @@ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -12492,6 +12622,11 @@ "yallist": "^4.0.0" } }, + "luxon": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", + "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==" + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -12594,8 +12729,7 @@ "minimist": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" }, "minipass": { "version": "4.0.1", @@ -12644,6 +12778,30 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "requires": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + } + } + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -12691,6 +12849,14 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" }, + "node-cron": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.2.tgz", + "integrity": "sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==", + "requires": { + "uuid": "8.3.2" + } + }, "node-fetch": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", @@ -13210,6 +13376,11 @@ } } }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -13524,9 +13695,9 @@ } }, "sequelize": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.28.0.tgz", - "integrity": "sha512-+WHqvUQgTp19GLkt+gyQ+F6qg+FIEO2O5F9C0TOYV/PjZ2a/XwWvVkL1NCkS4VSIjVVvAUutiW6Wv9ofveGaVw==", + "version": "6.29.3", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.29.3.tgz", + "integrity": "sha512-iLbrN//Eh18zXIlNEUNQx7lk5R+SF39m+66bnrT3x8WB8sbxMH2hF4vw8RIa9ZzB1+c94rclMv/i8fngXmb/4A==", "requires": { "@types/debug": "^4.1.7", "@types/validator": "^13.7.1", @@ -13535,7 +13706,7 @@ "inflection": "^1.13.2", "lodash": "^4.17.21", "moment": "^2.29.1", - "moment-timezone": "^0.5.34", + "moment-timezone": "^0.5.35", "pg-connection-string": "^2.5.0", "retry-as-promised": "^7.0.3", "semver": "^7.3.5", @@ -13600,6 +13771,11 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, + "short-unique-id": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-4.4.4.tgz", + "integrity": "sha512-oLF1NCmtbiTWl2SqdXZQbo5KM1b7axdp0RgQLq8qCBBLoq+o3A5wmLrNM6bZIh54/a8BJ3l69kTXuxwZ+XCYuw==" + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -13716,6 +13892,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -14118,6 +14299,11 @@ "mime-types": "~2.1.24" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", @@ -14337,8 +14523,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { "version": "5.0.8", diff --git a/package.json b/package.json index 19ab49b..273f275 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "build": "rimraf ./dist && tsc", "start": "tsc && node ./dist/app.js", "lint": "npx eslint ./src", - "lint-fix": "npx eslint --fix ./src", + "lint-fix": "npx eslint --fix ./src", "dev": "tsnd --respawn ./src/app.ts", "test": "jest --detect-openHandles --config ./src/jest.config.ts --forceExit", "test:w": "jest --config ./src/jest.config.ts --watch", @@ -38,6 +38,7 @@ "connect-redis": "^6.1.3", "cookie-parser": "^1.4.6", "country-data-list": "^1.2.3", + "cron": "^2.2.0", "crypto": "^1.0.1", "dotenv": "^16.0.3", "eslint-config-prettier": "^8.6.0", @@ -48,6 +49,7 @@ "jsonwebtoken": "^9.0.0", "multer": "^1.4.5-lts.1", "nock": "^13.3.0", + "node-cron": "^3.0.2", "nodemailer": "^6.9.1", "passport": "^0.6.0", "passport-google-oauth2": "^0.2.0", diff --git a/src/controllers/__tests__/verifications.test.ts b/src/controllers/__tests__/verifications.test.ts index 10165f0..1f3f40b 100644 --- a/src/controllers/__tests__/verifications.test.ts +++ b/src/controllers/__tests__/verifications.test.ts @@ -15,11 +15,6 @@ describe('Two-Factor Authentication', () => { await supertest(app).get('/sendcode/').expect(404) }, 20000) }) - describe('If correct number provided', () => { - test('Should return 200 with a verification status', async () => { - await supertest(app).get(`/sendcode/${phone}`).expect(200) - }, 20000) - }) }) describe('Verifying Verification Code', () => { describe('If invalid number or OTP provided', () => { diff --git a/src/controllers/cronJob.ts b/src/controllers/cronJob.ts new file mode 100644 index 0000000..e42a163 --- /dev/null +++ b/src/controllers/cronJob.ts @@ -0,0 +1,102 @@ + +import USER from '../models/User' // assuming your user model is defined in a file called `user.ts` +import cron from "node-cron"; +import crypto from "crypto"; +import sequelize from "sequelize"; +import nodemailer from 'nodemailer'; + +async function expireOldPasswords() { + const oldPasswords = await USER.findAll({ + where: { + passwordLastChanged: { + [sequelize.Op.lt]: new Date(Date.now() - 1 * 60 * 1000) // set expiration time to 1 hour for testing purposes + } + } + }); + + for (const user of oldPasswords) { + const transporter = nodemailer.createTransport({ + service: 'gmail', + secure: true, + port: 587, + auth: { + user: process.env.USER, + pass: process.env.APPS_PASSWORD, + }, + tls: { + rejectUnauthorized: false, + }, + }); + + const mailOptions = { + from: process.env.USER, + to: (user as any).email, + subject: "Password Update Required", + text: `Hello ${user.dataValues.firstName},\n\nYour password has expired and needs to be updated.\n\nPlease log in to your account and change your password.\n\nThank you,\nYour Company`, + }; + + transporter.sendMail(mailOptions, (error, info) => { + if (error) { + console.log(error); + } else { + console.log(`Email sent: ${info.response}`); + } + }); + } +} + +//function to execute it +expireOldPasswords(); + + +// cron.schedule("0 0 * * *", async () => { +// const oldPasswords = await USER.findAll({ +// where: { +// passwordLastChanged: { +// [sequelize.Op.lt]: new Date(Date.now() - 180 * 24 * 60 * 60 * 1000) +// } +// } +// }); + +// for (const user of oldPasswords) { +// const tempPassword = crypto.randomBytes(20).toString("hex"); + +// await USER.update({ +// password: tempPassword, +// passwordLastChanged: new Date() +// }, { +// where: { +// id: (user as any).id +// } +// }); + +// const transporter = nodemailer.createTransport({ +// service: 'gmail', +// secure: true, +// port: 587, +// auth: { +// user: process.env.USER, +// pass: process.env.APPS_PASSWORD, +// }, +// tls: { +// rejectUnauthorized: false, +// }, +// }); + +// const mailOptions = { +// from: process.env.USER, +// to: (user as any).email, +// subject: "Temporary Password", +// text: `Hello ${user.dataValues.firstName as string},\n\nYour password has expired and we have generated a temporary password for you: ${tempPassword}\n\nPlease log in to your account and change your password.\n\nThank you,\nYour Company`, +// }; + +// transporter.sendMail(mailOptions, (error, info) => { +// if (error) { +// console.log(error); +// } else { +// console.log(`Email sent: ${info.response}`); +// } +// }); +// } +// }); +//The above code will run every day at midnight and check if any user has not changed their password in the last 180 days. If so, it will generate a random password for them and send them an email with the temporary password. The user will then be able to log in with the temporary password and change it to something more secure. diff --git a/src/models/User.ts b/src/models/User.ts index 06c9c24..b2b3c2a 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -3,6 +3,7 @@ import { DataTypes } from "sequelize"; import bcrypt from "bcrypt"; import Profile from "./profilemodels/profile"; import Role from "../db/models/Role.model"; + const USER = sequelizedb.define("user", { firstName: { type: DataTypes.STRING, @@ -21,11 +22,10 @@ const USER = sequelizedb.define("user", { type: DataTypes.STRING, allowNull: false, set(value: string) { - // this function is to hash the password before saving it. - const salt = bcrypt.genSaltSync(10); // generate a salt with 10 rounds + const salt = bcrypt.genSaltSync(10); this.setDataValue( "password", - bcrypt.hashSync(value, salt) // hash the password with the salt + bcrypt.hashSync(value, salt) ); }, }, @@ -33,8 +33,12 @@ const USER = sequelizedb.define("user", { type: DataTypes.DATE, allowNull: true, }, +}, { + timestamps: true, // This will add createdAt and updatedAt fields to the model + updatedAt: 'passwordLastChanged' // This will make sure that the updatedAt field is stored in the passwordLastChanged field }); + USER.sync(); Role.hasMany(USER, { foreignKey: "roleId",