nodejs-express-starter 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.dockerignore +3 -0
  2. package/.editorconfig +9 -0
  3. package/.env.example +22 -0
  4. package/.eslintignore +2 -0
  5. package/.eslintrc.json +32 -0
  6. package/.gitignore +14 -0
  7. package/.prettierignore +3 -0
  8. package/.prettierrc.json +4 -0
  9. package/Dockerfile +15 -0
  10. package/LICENSE +21 -0
  11. package/README.md +440 -0
  12. package/bin/createNodejsApp.js +105 -0
  13. package/docker-compose.dev.yml +4 -0
  14. package/docker-compose.prod.yml +4 -0
  15. package/docker-compose.test.yml +4 -0
  16. package/docker-compose.yml +30 -0
  17. package/jest.config.js +9 -0
  18. package/package.json +117 -0
  19. package/src/app.js +82 -0
  20. package/src/config/config.js +64 -0
  21. package/src/config/logger.js +26 -0
  22. package/src/config/morgan.js +25 -0
  23. package/src/config/passport.js +30 -0
  24. package/src/config/roles.js +12 -0
  25. package/src/config/tokens.js +10 -0
  26. package/src/controllers/auth.controller.js +59 -0
  27. package/src/controllers/index.js +2 -0
  28. package/src/controllers/user.controller.js +43 -0
  29. package/src/docs/components.yml +92 -0
  30. package/src/docs/swaggerDef.js +21 -0
  31. package/src/index.js +57 -0
  32. package/src/middlewares/auth.js +33 -0
  33. package/src/middlewares/error.js +47 -0
  34. package/src/middlewares/rateLimiter.js +11 -0
  35. package/src/middlewares/requestId.js +14 -0
  36. package/src/middlewares/validate.js +21 -0
  37. package/src/models/index.js +2 -0
  38. package/src/models/plugins/index.js +2 -0
  39. package/src/models/plugins/paginate.plugin.js +70 -0
  40. package/src/models/plugins/toJSON.plugin.js +43 -0
  41. package/src/models/token.model.js +44 -0
  42. package/src/models/user.model.js +91 -0
  43. package/src/routes/v1/auth.route.js +291 -0
  44. package/src/routes/v1/docs.route.js +21 -0
  45. package/src/routes/v1/health.route.js +43 -0
  46. package/src/routes/v1/index.js +39 -0
  47. package/src/routes/v1/user.route.js +252 -0
  48. package/src/services/auth.service.js +99 -0
  49. package/src/services/email.service.js +63 -0
  50. package/src/services/index.js +4 -0
  51. package/src/services/token.service.js +123 -0
  52. package/src/services/user.service.js +89 -0
  53. package/src/utils/ApiError.js +14 -0
  54. package/src/utils/catchAsync.js +5 -0
  55. package/src/utils/pick.js +17 -0
  56. package/src/validations/auth.validation.js +60 -0
  57. package/src/validations/custom.validation.js +21 -0
  58. package/src/validations/index.js +2 -0
  59. package/src/validations/user.validation.js +54 -0
  60. package/tests/fixtures/token.fixture.js +14 -0
  61. package/tests/fixtures/user.fixture.js +46 -0
  62. package/tests/integration/auth.test.js +587 -0
  63. package/tests/integration/docs.test.js +14 -0
  64. package/tests/integration/health.test.js +32 -0
  65. package/tests/integration/user.test.js +625 -0
  66. package/tests/unit/middlewares/error.test.js +168 -0
  67. package/tests/unit/models/plugins/paginate.plugin.test.js +61 -0
  68. package/tests/unit/models/plugins/toJSON.plugin.test.js +89 -0
  69. package/tests/unit/models/user.model.test.js +57 -0
  70. package/tests/utils/setupTestDB.js +18 -0
@@ -0,0 +1,99 @@
1
+ const httpStatus = require('http-status');
2
+ const tokenService = require('./token.service');
3
+ const userService = require('./user.service');
4
+ const Token = require('../models/token.model');
5
+ const ApiError = require('../utils/ApiError');
6
+ const { tokenTypes } = require('../config/tokens');
7
+
8
+ /**
9
+ * Login with username and password
10
+ * @param {string} email
11
+ * @param {string} password
12
+ * @returns {Promise<User>}
13
+ */
14
+ const loginUserWithEmailAndPassword = async (email, password) => {
15
+ const user = await userService.getUserByEmail(email);
16
+ if (!user || !(await user.isPasswordMatch(password))) {
17
+ throw new ApiError(httpStatus.UNAUTHORIZED, 'Incorrect email or password');
18
+ }
19
+ return user;
20
+ };
21
+
22
+ /**
23
+ * Logout
24
+ * @param {string} refreshToken
25
+ * @returns {Promise}
26
+ */
27
+ const logout = async (refreshToken) => {
28
+ const refreshTokenDoc = await Token.findOne({ token: refreshToken, type: tokenTypes.REFRESH, blacklisted: false });
29
+ if (!refreshTokenDoc) {
30
+ throw new ApiError(httpStatus.NOT_FOUND, 'Not found');
31
+ }
32
+ await refreshTokenDoc.deleteOne();
33
+ };
34
+
35
+ /**
36
+ * Refresh auth tokens
37
+ * @param {string} refreshToken
38
+ * @returns {Promise<Object>}
39
+ */
40
+ const refreshAuth = async (refreshToken) => {
41
+ try {
42
+ const refreshTokenDoc = await tokenService.verifyToken(refreshToken, tokenTypes.REFRESH);
43
+ const user = await userService.getUserById(refreshTokenDoc.user);
44
+ if (!user) {
45
+ throw new Error();
46
+ }
47
+ await refreshTokenDoc.deleteOne();
48
+ return tokenService.generateAuthTokens(user);
49
+ } catch (error) {
50
+ throw new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate');
51
+ }
52
+ };
53
+
54
+ /**
55
+ * Reset password
56
+ * @param {string} resetPasswordToken
57
+ * @param {string} newPassword
58
+ * @returns {Promise}
59
+ */
60
+ const resetPassword = async (resetPasswordToken, newPassword) => {
61
+ try {
62
+ const resetPasswordTokenDoc = await tokenService.verifyToken(resetPasswordToken, tokenTypes.RESET_PASSWORD);
63
+ const user = await userService.getUserById(resetPasswordTokenDoc.user);
64
+ if (!user) {
65
+ throw new Error();
66
+ }
67
+ await userService.updateUserById(user.id, { password: newPassword });
68
+ await Token.deleteMany({ user: user.id, type: tokenTypes.RESET_PASSWORD });
69
+ } catch (error) {
70
+ throw new ApiError(httpStatus.UNAUTHORIZED, 'Password reset failed');
71
+ }
72
+ };
73
+
74
+ /**
75
+ * Verify email
76
+ * @param {string} verifyEmailToken
77
+ * @returns {Promise}
78
+ */
79
+ const verifyEmail = async (verifyEmailToken) => {
80
+ try {
81
+ const verifyEmailTokenDoc = await tokenService.verifyToken(verifyEmailToken, tokenTypes.VERIFY_EMAIL);
82
+ const user = await userService.getUserById(verifyEmailTokenDoc.user);
83
+ if (!user) {
84
+ throw new Error();
85
+ }
86
+ await Token.deleteMany({ user: user.id, type: tokenTypes.VERIFY_EMAIL });
87
+ await userService.updateUserById(user.id, { isEmailVerified: true });
88
+ } catch (error) {
89
+ throw new ApiError(httpStatus.UNAUTHORIZED, 'Email verification failed');
90
+ }
91
+ };
92
+
93
+ module.exports = {
94
+ loginUserWithEmailAndPassword,
95
+ logout,
96
+ refreshAuth,
97
+ resetPassword,
98
+ verifyEmail,
99
+ };
@@ -0,0 +1,63 @@
1
+ const nodemailer = require('nodemailer');
2
+ const config = require('../config/config');
3
+ const logger = require('../config/logger');
4
+
5
+ const transport = nodemailer.createTransport(config.email.smtp);
6
+ /* istanbul ignore next */
7
+ if (config.env !== 'test') {
8
+ transport
9
+ .verify()
10
+ .then(() => logger.info('Connected to email server'))
11
+ .catch(() => logger.warn('Unable to connect to email server. Make sure you have configured the SMTP options in .env'));
12
+ }
13
+
14
+ /**
15
+ * Send an email
16
+ * @param {string} to
17
+ * @param {string} subject
18
+ * @param {string} text
19
+ * @returns {Promise}
20
+ */
21
+ const sendEmail = async (to, subject, text) => {
22
+ const msg = { from: config.email.from, to, subject, text };
23
+ await transport.sendMail(msg);
24
+ };
25
+
26
+ /**
27
+ * Send reset password email
28
+ * @param {string} to
29
+ * @param {string} token
30
+ * @returns {Promise}
31
+ */
32
+ const sendResetPasswordEmail = async (to, token) => {
33
+ const subject = 'Reset password';
34
+ // replace this url with the link to the reset password page of your front-end app
35
+ const resetPasswordUrl = `http://link-to-app/reset-password?token=${token}`;
36
+ const text = `Dear user,
37
+ To reset your password, click on this link: ${resetPasswordUrl}
38
+ If you did not request any password resets, then ignore this email.`;
39
+ await sendEmail(to, subject, text);
40
+ };
41
+
42
+ /**
43
+ * Send verification email
44
+ * @param {string} to
45
+ * @param {string} token
46
+ * @returns {Promise}
47
+ */
48
+ const sendVerificationEmail = async (to, token) => {
49
+ const subject = 'Email Verification';
50
+ // replace this url with the link to the email verification page of your front-end app
51
+ const verificationEmailUrl = `http://link-to-app/verify-email?token=${token}`;
52
+ const text = `Dear user,
53
+ To verify your email, click on this link: ${verificationEmailUrl}
54
+ If you did not create an account, then ignore this email.`;
55
+ await sendEmail(to, subject, text);
56
+ };
57
+
58
+ module.exports = {
59
+ transport,
60
+ sendEmail,
61
+ sendResetPasswordEmail,
62
+ sendVerificationEmail,
63
+ };
@@ -0,0 +1,4 @@
1
+ module.exports.authService = require('./auth.service');
2
+ module.exports.emailService = require('./email.service');
3
+ module.exports.tokenService = require('./token.service');
4
+ module.exports.userService = require('./user.service');
@@ -0,0 +1,123 @@
1
+ const jwt = require('jsonwebtoken');
2
+ const moment = require('moment');
3
+ const httpStatus = require('http-status');
4
+ const config = require('../config/config');
5
+ const userService = require('./user.service');
6
+ const { Token } = require('../models');
7
+ const ApiError = require('../utils/ApiError');
8
+ const { tokenTypes } = require('../config/tokens');
9
+
10
+ /**
11
+ * Generate token
12
+ * @param {ObjectId} userId
13
+ * @param {Moment} expires
14
+ * @param {string} type
15
+ * @param {string} [secret]
16
+ * @returns {string}
17
+ */
18
+ const generateToken = (userId, expires, type, secret = config.jwt.secret) => {
19
+ const payload = {
20
+ sub: userId,
21
+ iat: moment().unix(),
22
+ exp: expires.unix(),
23
+ type,
24
+ };
25
+ return jwt.sign(payload, secret);
26
+ };
27
+
28
+ /**
29
+ * Save a token
30
+ * @param {string} token
31
+ * @param {ObjectId} userId
32
+ * @param {Moment} expires
33
+ * @param {string} type
34
+ * @param {boolean} [blacklisted]
35
+ * @returns {Promise<Token>}
36
+ */
37
+ const saveToken = async (token, userId, expires, type, blacklisted = false) => {
38
+ const tokenDoc = await Token.create({
39
+ token,
40
+ user: userId,
41
+ expires: expires.toDate(),
42
+ type,
43
+ blacklisted,
44
+ });
45
+ return tokenDoc;
46
+ };
47
+
48
+ /**
49
+ * Verify token and return token doc (or throw an error if it is not valid)
50
+ * @param {string} token
51
+ * @param {string} type
52
+ * @returns {Promise<Token>}
53
+ */
54
+ const verifyToken = async (token, type) => {
55
+ const payload = jwt.verify(token, config.jwt.secret);
56
+ const tokenDoc = await Token.findOne({ token, type, user: payload.sub, blacklisted: false });
57
+ if (!tokenDoc) {
58
+ throw new Error('Token not found');
59
+ }
60
+ return tokenDoc;
61
+ };
62
+
63
+ /**
64
+ * Generate auth tokens
65
+ * @param {User} user
66
+ * @returns {Promise<Object>}
67
+ */
68
+ const generateAuthTokens = async (user) => {
69
+ const accessTokenExpires = moment().add(config.jwt.accessExpirationMinutes, 'minutes');
70
+ const accessToken = generateToken(user.id, accessTokenExpires, tokenTypes.ACCESS);
71
+
72
+ const refreshTokenExpires = moment().add(config.jwt.refreshExpirationDays, 'days');
73
+ const refreshToken = generateToken(user.id, refreshTokenExpires, tokenTypes.REFRESH);
74
+ await saveToken(refreshToken, user.id, refreshTokenExpires, tokenTypes.REFRESH);
75
+
76
+ return {
77
+ access: {
78
+ token: accessToken,
79
+ expires: accessTokenExpires.toDate(),
80
+ },
81
+ refresh: {
82
+ token: refreshToken,
83
+ expires: refreshTokenExpires.toDate(),
84
+ },
85
+ };
86
+ };
87
+
88
+ /**
89
+ * Generate reset password token
90
+ * @param {string} email
91
+ * @returns {Promise<string>}
92
+ */
93
+ const generateResetPasswordToken = async (email) => {
94
+ const user = await userService.getUserByEmail(email);
95
+ if (!user) {
96
+ throw new ApiError(httpStatus.NOT_FOUND, 'No users found with this email');
97
+ }
98
+ const expires = moment().add(config.jwt.resetPasswordExpirationMinutes, 'minutes');
99
+ const resetPasswordToken = generateToken(user.id, expires, tokenTypes.RESET_PASSWORD);
100
+ await saveToken(resetPasswordToken, user.id, expires, tokenTypes.RESET_PASSWORD);
101
+ return resetPasswordToken;
102
+ };
103
+
104
+ /**
105
+ * Generate verify email token
106
+ * @param {User} user
107
+ * @returns {Promise<string>}
108
+ */
109
+ const generateVerifyEmailToken = async (user) => {
110
+ const expires = moment().add(config.jwt.verifyEmailExpirationMinutes, 'minutes');
111
+ const verifyEmailToken = generateToken(user.id, expires, tokenTypes.VERIFY_EMAIL);
112
+ await saveToken(verifyEmailToken, user.id, expires, tokenTypes.VERIFY_EMAIL);
113
+ return verifyEmailToken;
114
+ };
115
+
116
+ module.exports = {
117
+ generateToken,
118
+ saveToken,
119
+ verifyToken,
120
+ generateAuthTokens,
121
+ generateResetPasswordToken,
122
+ generateVerifyEmailToken,
123
+ };
@@ -0,0 +1,89 @@
1
+ const httpStatus = require('http-status');
2
+ const { User } = require('../models');
3
+ const ApiError = require('../utils/ApiError');
4
+
5
+ /**
6
+ * Create a user
7
+ * @param {Object} userBody
8
+ * @returns {Promise<User>}
9
+ */
10
+ const createUser = async (userBody) => {
11
+ if (await User.isEmailTaken(userBody.email)) {
12
+ throw new ApiError(httpStatus.BAD_REQUEST, 'Email already taken');
13
+ }
14
+ return User.create(userBody);
15
+ };
16
+
17
+ /**
18
+ * Query for users
19
+ * @param {Object} filter - Mongo filter
20
+ * @param {Object} options - Query options
21
+ * @param {string} [options.sortBy] - Sort option in the format: sortField:(desc|asc)
22
+ * @param {number} [options.limit] - Maximum number of results per page (default = 10)
23
+ * @param {number} [options.page] - Current page (default = 1)
24
+ * @returns {Promise<QueryResult>}
25
+ */
26
+ const queryUsers = async (filter, options) => {
27
+ const users = await User.paginate(filter, options);
28
+ return users;
29
+ };
30
+
31
+ /**
32
+ * Get user by id
33
+ * @param {ObjectId} id
34
+ * @returns {Promise<User>}
35
+ */
36
+ const getUserById = async (id) => {
37
+ return User.findById(id);
38
+ };
39
+
40
+ /**
41
+ * Get user by email
42
+ * @param {string} email
43
+ * @returns {Promise<User>}
44
+ */
45
+ const getUserByEmail = async (email) => {
46
+ return User.findOne({ email });
47
+ };
48
+
49
+ /**
50
+ * Update user by id
51
+ * @param {ObjectId} userId
52
+ * @param {Object} updateBody
53
+ * @returns {Promise<User>}
54
+ */
55
+ const updateUserById = async (userId, updateBody) => {
56
+ const user = await getUserById(userId);
57
+ if (!user) {
58
+ throw new ApiError(httpStatus.NOT_FOUND, 'User not found');
59
+ }
60
+ if (updateBody.email && (await User.isEmailTaken(updateBody.email, userId))) {
61
+ throw new ApiError(httpStatus.BAD_REQUEST, 'Email already taken');
62
+ }
63
+ Object.assign(user, updateBody);
64
+ await user.save();
65
+ return user;
66
+ };
67
+
68
+ /**
69
+ * Delete user by id
70
+ * @param {ObjectId} userId
71
+ * @returns {Promise<User>}
72
+ */
73
+ const deleteUserById = async (userId) => {
74
+ const user = await getUserById(userId);
75
+ if (!user) {
76
+ throw new ApiError(httpStatus.NOT_FOUND, 'User not found');
77
+ }
78
+ await user.deleteOne();
79
+ return user;
80
+ };
81
+
82
+ module.exports = {
83
+ createUser,
84
+ queryUsers,
85
+ getUserById,
86
+ getUserByEmail,
87
+ updateUserById,
88
+ deleteUserById,
89
+ };
@@ -0,0 +1,14 @@
1
+ class ApiError extends Error {
2
+ constructor(statusCode, message, isOperational = true, stack = '') {
3
+ super(message);
4
+ this.statusCode = statusCode;
5
+ this.isOperational = isOperational;
6
+ if (stack) {
7
+ this.stack = stack;
8
+ } else {
9
+ Error.captureStackTrace(this, this.constructor);
10
+ }
11
+ }
12
+ }
13
+
14
+ module.exports = ApiError;
@@ -0,0 +1,5 @@
1
+ const catchAsync = (fn) => (req, res, next) => {
2
+ Promise.resolve(fn(req, res, next)).catch((err) => next(err));
3
+ };
4
+
5
+ module.exports = catchAsync;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Create an object composed of the picked object properties
3
+ * @param {Object} object
4
+ * @param {string[]} keys
5
+ * @returns {Object}
6
+ */
7
+ const pick = (object, keys) => {
8
+ return keys.reduce((obj, key) => {
9
+ if (object && Object.prototype.hasOwnProperty.call(object, key)) {
10
+ // eslint-disable-next-line no-param-reassign
11
+ obj[key] = object[key];
12
+ }
13
+ return obj;
14
+ }, {});
15
+ };
16
+
17
+ module.exports = pick;
@@ -0,0 +1,60 @@
1
+ const Joi = require('joi');
2
+ const { password } = require('./custom.validation');
3
+
4
+ const register = {
5
+ body: Joi.object().keys({
6
+ email: Joi.string().required().email(),
7
+ password: Joi.string().required().custom(password),
8
+ name: Joi.string().required(),
9
+ }),
10
+ };
11
+
12
+ const login = {
13
+ body: Joi.object().keys({
14
+ email: Joi.string().required(),
15
+ password: Joi.string().required(),
16
+ }),
17
+ };
18
+
19
+ const logout = {
20
+ body: Joi.object().keys({
21
+ refreshToken: Joi.string().required(),
22
+ }),
23
+ };
24
+
25
+ const refreshTokens = {
26
+ body: Joi.object().keys({
27
+ refreshToken: Joi.string().required(),
28
+ }),
29
+ };
30
+
31
+ const forgotPassword = {
32
+ body: Joi.object().keys({
33
+ email: Joi.string().email().required(),
34
+ }),
35
+ };
36
+
37
+ const resetPassword = {
38
+ query: Joi.object().keys({
39
+ token: Joi.string().required(),
40
+ }),
41
+ body: Joi.object().keys({
42
+ password: Joi.string().required().custom(password),
43
+ }),
44
+ };
45
+
46
+ const verifyEmail = {
47
+ query: Joi.object().keys({
48
+ token: Joi.string().required(),
49
+ }),
50
+ };
51
+
52
+ module.exports = {
53
+ register,
54
+ login,
55
+ logout,
56
+ refreshTokens,
57
+ forgotPassword,
58
+ resetPassword,
59
+ verifyEmail,
60
+ };
@@ -0,0 +1,21 @@
1
+ const objectId = (value, helpers) => {
2
+ if (!value.match(/^[0-9a-fA-F]{24}$/)) {
3
+ return helpers.message('"{{#label}}" must be a valid mongo id');
4
+ }
5
+ return value;
6
+ };
7
+
8
+ const password = (value, helpers) => {
9
+ if (value.length < 8) {
10
+ return helpers.message('password must be at least 8 characters');
11
+ }
12
+ if (!value.match(/\d/) || !value.match(/[a-zA-Z]/)) {
13
+ return helpers.message('password must contain at least 1 letter and 1 number');
14
+ }
15
+ return value;
16
+ };
17
+
18
+ module.exports = {
19
+ objectId,
20
+ password,
21
+ };
@@ -0,0 +1,2 @@
1
+ module.exports.authValidation = require('./auth.validation');
2
+ module.exports.userValidation = require('./user.validation');
@@ -0,0 +1,54 @@
1
+ const Joi = require('joi');
2
+ const { password, objectId } = require('./custom.validation');
3
+
4
+ const createUser = {
5
+ body: Joi.object().keys({
6
+ email: Joi.string().required().email(),
7
+ password: Joi.string().required().custom(password),
8
+ name: Joi.string().required(),
9
+ role: Joi.string().required().valid('user', 'admin'),
10
+ }),
11
+ };
12
+
13
+ const getUsers = {
14
+ query: Joi.object().keys({
15
+ name: Joi.string(),
16
+ role: Joi.string(),
17
+ sortBy: Joi.string(),
18
+ limit: Joi.number().integer(),
19
+ page: Joi.number().integer(),
20
+ }),
21
+ };
22
+
23
+ const getUser = {
24
+ params: Joi.object().keys({
25
+ userId: Joi.string().custom(objectId),
26
+ }),
27
+ };
28
+
29
+ const updateUser = {
30
+ params: Joi.object().keys({
31
+ userId: Joi.required().custom(objectId),
32
+ }),
33
+ body: Joi.object()
34
+ .keys({
35
+ email: Joi.string().email(),
36
+ password: Joi.string().custom(password),
37
+ name: Joi.string(),
38
+ })
39
+ .min(1),
40
+ };
41
+
42
+ const deleteUser = {
43
+ params: Joi.object().keys({
44
+ userId: Joi.string().custom(objectId),
45
+ }),
46
+ };
47
+
48
+ module.exports = {
49
+ createUser,
50
+ getUsers,
51
+ getUser,
52
+ updateUser,
53
+ deleteUser,
54
+ };
@@ -0,0 +1,14 @@
1
+ const moment = require('moment');
2
+ const config = require('../../src/config/config');
3
+ const { tokenTypes } = require('../../src/config/tokens');
4
+ const tokenService = require('../../src/services/token.service');
5
+ const { userOne, admin } = require('./user.fixture');
6
+
7
+ const accessTokenExpires = moment().add(config.jwt.accessExpirationMinutes, 'minutes');
8
+ const userOneAccessToken = tokenService.generateToken(userOne._id, accessTokenExpires, tokenTypes.ACCESS);
9
+ const adminAccessToken = tokenService.generateToken(admin._id, accessTokenExpires, tokenTypes.ACCESS);
10
+
11
+ module.exports = {
12
+ userOneAccessToken,
13
+ adminAccessToken,
14
+ };
@@ -0,0 +1,46 @@
1
+ const mongoose = require('mongoose');
2
+ const bcrypt = require('bcryptjs');
3
+ const { faker } = require('@faker-js/faker');
4
+ const User = require('../../src/models/user.model');
5
+
6
+ const password = 'password1';
7
+ const salt = bcrypt.genSaltSync(8);
8
+ const hashedPassword = bcrypt.hashSync(password, salt);
9
+
10
+ const userOne = {
11
+ _id: new mongoose.Types.ObjectId(),
12
+ name: faker.person.fullName(),
13
+ email: faker.internet.email().toLowerCase(),
14
+ password,
15
+ role: 'user',
16
+ isEmailVerified: false,
17
+ };
18
+
19
+ const userTwo = {
20
+ _id: new mongoose.Types.ObjectId(),
21
+ name: faker.person.fullName(),
22
+ email: faker.internet.email().toLowerCase(),
23
+ password,
24
+ role: 'user',
25
+ isEmailVerified: false,
26
+ };
27
+
28
+ const admin = {
29
+ _id: new mongoose.Types.ObjectId(),
30
+ name: faker.person.fullName(),
31
+ email: faker.internet.email().toLowerCase(),
32
+ password,
33
+ role: 'admin',
34
+ isEmailVerified: false,
35
+ };
36
+
37
+ const insertUsers = async (users) => {
38
+ await User.insertMany(users.map((user) => ({ ...user, password: hashedPassword })));
39
+ };
40
+
41
+ module.exports = {
42
+ userOne,
43
+ userTwo,
44
+ admin,
45
+ insertUsers,
46
+ };