exp-node-server 1.1.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 (57) hide show
  1. package/README.md +180 -0
  2. package/bin/cli.js +41 -0
  3. package/package.json +32 -0
  4. package/src/createProject.js +58 -0
  5. package/src/generateModule.js +61 -0
  6. package/src/utils/controllerGenerator.js +70 -0
  7. package/src/utils/routeGenerator.js +27 -0
  8. package/src/utils/routeInjector.js +40 -0
  9. package/src/utils/schemaGenerator.js +11 -0
  10. package/src/utils/serviceGenerator.js +49 -0
  11. package/src/utils/swaggerGenerator.js +112 -0
  12. package/src/utils/swaggerInjector.js +52 -0
  13. package/src/utils/validateModuleName.js +17 -0
  14. package/src/utils/validateProjectName.js +15 -0
  15. package/src/utils/validationGenerator.js +13 -0
  16. package/src/utils.js +26 -0
  17. package/templates/mongoose/.env.example +23 -0
  18. package/templates/mongoose/package.json +45 -0
  19. package/templates/mongoose/readme.md +180 -0
  20. package/templates/mongoose/src/app/configs/index.ts +26 -0
  21. package/templates/mongoose/src/app/errors/castError.ts +23 -0
  22. package/templates/mongoose/src/app/errors/duplicateError.ts +26 -0
  23. package/templates/mongoose/src/app/errors/validationError.ts +25 -0
  24. package/templates/mongoose/src/app/errors/zodError.ts +20 -0
  25. package/templates/mongoose/src/app/middlewares/auth.ts +50 -0
  26. package/templates/mongoose/src/app/middlewares/global_error_handler.ts +69 -0
  27. package/templates/mongoose/src/app/middlewares/not_found_api.ts +10 -0
  28. package/templates/mongoose/src/app/middlewares/request_validator.ts +14 -0
  29. package/templates/mongoose/src/app/middlewares/uploader.ts +16 -0
  30. package/templates/mongoose/src/app/modules/auth/auth.controller.ts +125 -0
  31. package/templates/mongoose/src/app/modules/auth/auth.interface.ts +24 -0
  32. package/templates/mongoose/src/app/modules/auth/auth.route.ts +46 -0
  33. package/templates/mongoose/src/app/modules/auth/auth.schema.ts +19 -0
  34. package/templates/mongoose/src/app/modules/auth/auth.service.ts +306 -0
  35. package/templates/mongoose/src/app/modules/auth/auth.swagger.ts +214 -0
  36. package/templates/mongoose/src/app/modules/auth/auth.validation.ts +37 -0
  37. package/templates/mongoose/src/app/modules/user/user.controller.ts +17 -0
  38. package/templates/mongoose/src/app/modules/user/user.interface.ts +15 -0
  39. package/templates/mongoose/src/app/modules/user/user.route.ts +20 -0
  40. package/templates/mongoose/src/app/modules/user/user.schema.ts +22 -0
  41. package/templates/mongoose/src/app/modules/user/user.service.ts +22 -0
  42. package/templates/mongoose/src/app/modules/user/user.swagger.ts +75 -0
  43. package/templates/mongoose/src/app/modules/user/user.validation.ts +18 -0
  44. package/templates/mongoose/src/app/types/error.d.ts +10 -0
  45. package/templates/mongoose/src/app/types/index.d.ts +12 -0
  46. package/templates/mongoose/src/app/utils/JWT.ts +26 -0
  47. package/templates/mongoose/src/app/utils/app_error.ts +12 -0
  48. package/templates/mongoose/src/app/utils/catch_async.ts +13 -0
  49. package/templates/mongoose/src/app/utils/cloudinary.ts +66 -0
  50. package/templates/mongoose/src/app/utils/isAccountExist.ts +31 -0
  51. package/templates/mongoose/src/app/utils/mail_sender.ts +102 -0
  52. package/templates/mongoose/src/app/utils/manage_response.ts +24 -0
  53. package/templates/mongoose/src/app.ts +42 -0
  54. package/templates/mongoose/src/routes.ts +13 -0
  55. package/templates/mongoose/src/server.ts +18 -0
  56. package/templates/mongoose/src/swaggerOptions.ts +44 -0
  57. package/templates/mongoose/tsconfig.json +20 -0
@@ -0,0 +1,125 @@
1
+ import { configs } from "../../configs";
2
+ import catchAsync from "../../utils/catch_async";
3
+ import manageResponse from "../../utils/manage_response";
4
+ import { auth_services } from "./auth.service";
5
+
6
+ const register_user = catchAsync(async (req, res) => {
7
+ const result = await auth_services.register_user_into_db(req?.body)
8
+ manageResponse(res, {
9
+ success: true,
10
+ message: "Account created successful",
11
+ statusCode: httpStatus.OK,
12
+ data: result
13
+ })
14
+ })
15
+
16
+ const login_user = catchAsync(async (req, res) => {
17
+ const result = await auth_services.login_user_from_db(req.body);
18
+
19
+ res.cookie('refreshToken', result.refreshToken, {
20
+ secure: configs.env == 'production',
21
+ httpOnly: true,
22
+ });
23
+ manageResponse(res, {
24
+ statusCode: httpStatus.OK,
25
+ success: true,
26
+ message: 'User is logged in successful !',
27
+ data: {
28
+ accessToken: result.accessToken,
29
+ role: result?.role
30
+ },
31
+ });
32
+ });
33
+
34
+ const get_my_profile = catchAsync(async (req, res) => {
35
+ const { email } = req.user!;
36
+ const result = await auth_services.get_my_profile_from_db(email);
37
+ manageResponse(res, {
38
+ statusCode: httpStatus.OK,
39
+ success: true,
40
+ message: 'User profile fetched successfully!',
41
+ data: result,
42
+ });
43
+ });
44
+
45
+ const refresh_token = catchAsync(async (req, res) => {
46
+ const { refreshToken } = req.cookies;
47
+ const result = await auth_services.refresh_token_from_db(refreshToken);
48
+ manageResponse(res, {
49
+ statusCode: httpStatus.OK,
50
+ success: true,
51
+ message: 'Refresh token generated successfully!',
52
+ data: result,
53
+ });
54
+ });
55
+
56
+ const change_password = catchAsync(async (req, res) => {
57
+ const user = req?.user;
58
+ const result = await auth_services.change_password_from_db(user!, req.body);
59
+
60
+ manageResponse(res, {
61
+ statusCode: httpStatus.OK,
62
+ success: true,
63
+ message: 'Password changed successfully!',
64
+ data: result,
65
+ });
66
+ });
67
+
68
+ const forget_password = catchAsync(async (req, res) => {
69
+ const { email } = req?.body
70
+ await auth_services.forget_password_from_db(email);
71
+ manageResponse(res, {
72
+ statusCode: httpStatus.OK,
73
+ success: true,
74
+ message: 'Reset password link sent to your email!',
75
+ data: null,
76
+ });
77
+ });
78
+
79
+ const reset_password = catchAsync(async (req, res) => {
80
+ const { token, newPassword, email } = req.body;
81
+ const result = await auth_services.reset_password_into_db(
82
+ token,
83
+ email,
84
+ newPassword,
85
+ );
86
+ manageResponse(res, {
87
+ statusCode: httpStatus.OK,
88
+ success: true,
89
+ message: 'Password reset successfully!',
90
+ data: result,
91
+ });
92
+ });
93
+
94
+ const verified_account = catchAsync(async (req, res) => {
95
+ const result = await auth_services.verified_account_into_db(req?.body?.token)
96
+
97
+ manageResponse(res, {
98
+ statusCode: httpStatus.OK,
99
+ success: true,
100
+ message: "Account Verification successful.",
101
+ data: result
102
+ })
103
+ })
104
+
105
+ const get_new_verification_link = catchAsync(async (req, res) => {
106
+ const result = await auth_services.get_new_verification_link_from_db(req?.body?.email)
107
+ manageResponse(res, {
108
+ statusCode: httpStatus.OK,
109
+ success: true,
110
+ message: "New Verification link is send on email.",
111
+ data: result
112
+ })
113
+ })
114
+
115
+ export const auth_controllers = {
116
+ register_user,
117
+ login_user,
118
+ get_my_profile,
119
+ refresh_token,
120
+ change_password,
121
+ reset_password,
122
+ forget_password,
123
+ verified_account,
124
+ get_new_verification_link
125
+ }
@@ -0,0 +1,24 @@
1
+ export type TAccount = {
2
+ email: string;
3
+ password: string;
4
+ lastPasswordChange?: Date;
5
+ isDeleted?: boolean;
6
+ accountStatus?: "ACTIVE" | "INACTIVE" | "SUSPENDED";
7
+ role?: "USER" | "ADMIN",
8
+ isVerified?: boolean,
9
+ }
10
+
11
+
12
+ export interface TRegisterPayload extends TAccount {
13
+ name: string
14
+ }
15
+
16
+ export type TLoginPayload = {
17
+ email: string;
18
+ password: string
19
+ }
20
+
21
+ export type TJwtUser = {
22
+ email: string,
23
+ role?: "USER" | "ADMIN",
24
+ }
@@ -0,0 +1,46 @@
1
+ import { Router } from "express";
2
+ import { auth_controllers } from "./auth.controller";
3
+ import RequestValidator from "../../middlewares/request_validator";
4
+ import { auth_validation } from "./auth.validation";
5
+ import auth from "../../middlewares/auth";
6
+
7
+ const authRoute = Router()
8
+
9
+ authRoute.post("/register", RequestValidator(auth_validation.register_validation), auth_controllers.register_user)
10
+ authRoute.post("/login", RequestValidator(auth_validation.login_validation), auth_controllers.login_user)
11
+
12
+ authRoute.get(
13
+ '/me',
14
+ auth("ADMIN", "USER"),
15
+ auth_controllers.get_my_profile,
16
+ );
17
+
18
+ authRoute.post('/refresh-token', auth_controllers.refresh_token);
19
+ authRoute.post(
20
+ '/change-password',
21
+ auth("ADMIN", "USER"),
22
+ RequestValidator(auth_validation.changePassword),
23
+ auth_controllers.change_password,
24
+ );
25
+ authRoute.post(
26
+ '/forgot-password',
27
+ RequestValidator(auth_validation.forgotPassword),
28
+ auth_controllers.forget_password,
29
+ );
30
+ authRoute.post(
31
+ '/reset-password',
32
+ RequestValidator(auth_validation.resetPassword),
33
+ auth_controllers.reset_password,
34
+ );
35
+
36
+ authRoute.post(
37
+ "/verified-account",
38
+ RequestValidator(auth_validation.verified_account),
39
+ auth_controllers.verified_account
40
+ )
41
+ authRoute.post(
42
+ "/new-verification-link",
43
+ RequestValidator(auth_validation.forgotPassword),
44
+ auth_controllers.get_new_verification_link
45
+ )
46
+ export default authRoute;
@@ -0,0 +1,19 @@
1
+ import { model, Schema } from "mongoose";
2
+ import { TAccount } from "./auth.interface";
3
+
4
+
5
+ const authSchema = new Schema<TAccount>({
6
+ email: { type: String, required: true },
7
+ password: { type: String, required: true },
8
+ lastPasswordChange: { type: String },
9
+ isDeleted: { type: Boolean, default: false },
10
+ accountStatus: { type: String, default: "ACTIVE" },
11
+ role: { type: String, default: "USER" },
12
+ isVerified: { type: Boolean, default: false }
13
+ }, {
14
+ versionKey: false,
15
+ timestamps: true
16
+ });
17
+
18
+
19
+ export const Account_Model = model("account", authSchema)
@@ -0,0 +1,306 @@
1
+ import { AppError } from "../../utils/app_error";
2
+ import { TAccount, TLoginPayload, TRegisterPayload } from "./auth.interface";
3
+ import { Account_Model } from "./auth.schema";
4
+ import bcrypt from "bcrypt";
5
+ import { TUser } from "../user/user.interface";
6
+ import { User_Model } from "../user/user.schema";
7
+ import mongoose from "mongoose";
8
+ import { jwtHelpers } from "../../utils/JWT";
9
+ import { configs } from "../../configs";
10
+ import { JwtPayload, Secret } from "jsonwebtoken";
11
+ import sendMail from "../../utils/mail_sender";
12
+ import { isAccountExist } from "../../utils/isAccountExist";
13
+ // register user
14
+ const register_user_into_db = async (payload: TRegisterPayload) => {
15
+ const session = await mongoose.startSession();
16
+ session.startTransaction();
17
+ try {
18
+ // Check if the account already exists
19
+ const isExistAccount = await Account_Model.findOne(
20
+ { email: payload?.email },
21
+ null,
22
+ { session }
23
+ );
24
+ if (isExistAccount) {
25
+ throw new AppError("Account already exist!!", httpStatus.BAD_REQUEST);
26
+ }
27
+
28
+ // Hash the password
29
+ const hashPassword = bcrypt.hashSync(payload?.password, 10);
30
+
31
+ // Create account
32
+ const accountPayload: TAccount = {
33
+ email: payload.email,
34
+ password: hashPassword,
35
+ lastPasswordChange: new Date()
36
+ };
37
+ const newAccount = await Account_Model.create([accountPayload], { session });
38
+
39
+ // Create user
40
+ const userPayload: TUser = {
41
+ name: payload.name,
42
+ accountId: newAccount[0]._id,
43
+ };
44
+ await User_Model.create([userPayload], { session });
45
+ // make verified link
46
+ const verifiedToken = jwtHelpers.generateToken(
47
+ {
48
+ email: payload?.email
49
+ },
50
+ configs.jwt.verified_token as Secret,
51
+ '5m'
52
+ );
53
+ const verificationLink = `${configs.jwt.front_end_url}/verified?token=${verifiedToken}`;
54
+ // Commit the transaction
55
+ await session.commitTransaction();
56
+ await sendMail({
57
+ to: payload?.email,
58
+ subject: "Thanks for creating account!",
59
+ textBody: `New Account successfully created on ${new Date().toLocaleDateString()}`,
60
+ name: payload?.name,
61
+ htmlBody: `
62
+ <p>Thanks for creating an account with us. We’re excited to have you on board! Click the button below to
63
+ verify your email and activate your account:</p>
64
+
65
+
66
+ <div style="text-align: center; margin: 30px 0;">
67
+ <a href="${verificationLink}" target="_blank"
68
+ style="background-color: #4CAF50; color: #ffffff; padding: 14px 28px; text-decoration: none; font-weight: bold; border-radius: 5px; display: inline-block; font-size: 18px;"
69
+ class="btn">
70
+ Verify My Email
71
+ </a>
72
+ </div>
73
+
74
+ <p>If you did not create this account, please ignore this email.</p>
75
+ `
76
+ })
77
+ return newAccount;
78
+ } catch (error) {
79
+ console.log(error)
80
+ // Rollback the transaction
81
+ await session.abortTransaction();
82
+ throw error;
83
+ } finally {
84
+ session.endSession();
85
+ }
86
+ };
87
+
88
+
89
+ // login user
90
+ const login_user_from_db = async (payload: TLoginPayload) => {
91
+ // check account info
92
+ const isExistAccount = await isAccountExist(payload?.email)
93
+
94
+ const isPasswordMatch = await bcrypt.compare(
95
+ payload.password,
96
+ isExistAccount.password,
97
+ );
98
+ if (!isPasswordMatch) {
99
+ throw new AppError('Invalid password', httpStatus.UNAUTHORIZED);
100
+ }
101
+ const accessToken = jwtHelpers.generateToken(
102
+ {
103
+ email: isExistAccount.email,
104
+ role: isExistAccount.role,
105
+ },
106
+ configs.jwt.access_token as Secret,
107
+ configs.jwt.access_expires as string,
108
+ );
109
+
110
+ const refreshToken = jwtHelpers.generateToken(
111
+ {
112
+ email: isExistAccount.email,
113
+ role: isExistAccount.role,
114
+ },
115
+ configs.jwt.refresh_token as Secret,
116
+ configs.jwt.refresh_expires as string,
117
+ );
118
+ return {
119
+ accessToken: accessToken,
120
+ refreshToken: refreshToken,
121
+ role: isExistAccount.role
122
+ };
123
+
124
+ }
125
+
126
+ const get_my_profile_from_db = async (email: string) => {
127
+ const isExistAccount = await isAccountExist(email)
128
+ const accountProfile = await User_Model.findOne({ accountId: isExistAccount._id })
129
+ isExistAccount.password = ""
130
+ return {
131
+ account: isExistAccount,
132
+ profile: accountProfile
133
+ };
134
+ };
135
+
136
+ const refresh_token_from_db = async (token: string) => {
137
+ let decodedData;
138
+ try {
139
+ decodedData = jwtHelpers.verifyToken(
140
+ token,
141
+ configs.jwt.refresh_token as Secret,
142
+ );
143
+ } catch (err) {
144
+ throw new Error('You are not authorized!');
145
+ }
146
+
147
+ const userData = await Account_Model.findOne({ email: decodedData.email, status: "ACTIVE", isDeleted: false })
148
+
149
+ const accessToken = jwtHelpers.generateToken(
150
+ {
151
+ email: userData!.email,
152
+ role: userData!.role,
153
+ },
154
+ configs.jwt.access_token as Secret,
155
+ configs.jwt.access_expires as string,
156
+ );
157
+
158
+ return accessToken;
159
+ };
160
+
161
+ const change_password_from_db = async (
162
+ user: JwtPayload,
163
+ payload: {
164
+ oldPassword: string;
165
+ newPassword: string;
166
+ },
167
+ ) => {
168
+ const isExistAccount = await isAccountExist(user?.email)
169
+
170
+ const isCorrectPassword: boolean = await bcrypt.compare(
171
+ payload.oldPassword,
172
+ isExistAccount.password,
173
+ );
174
+
175
+ if (!isCorrectPassword) {
176
+ throw new AppError('Old password is incorrect', httpStatus.UNAUTHORIZED);
177
+ }
178
+
179
+ const hashedPassword: string = await bcrypt.hash(payload.newPassword, 10);
180
+ await Account_Model.findOneAndUpdate({ email: isExistAccount.email }, {
181
+ password: hashedPassword,
182
+ lastPasswordChange: Date()
183
+ })
184
+ return 'Password changed successful.';
185
+ };
186
+
187
+ const forget_password_from_db = async (email: string) => {
188
+ const isAccountExists = await isAccountExist(email)
189
+ const resetToken = jwtHelpers.generateToken(
190
+ {
191
+ email: isAccountExists.email,
192
+ role: isAccountExists.role,
193
+ },
194
+ configs.jwt.reset_secret as Secret,
195
+ configs.jwt.reset_expires as string,
196
+ );
197
+
198
+ const resetPasswordLink = `${configs.jwt.front_end_url}/reset?token=${resetToken}&email=${isAccountExists.email}`;
199
+ const emailTemplate = `<p>Click the link below to reset your password:</p><a href="${resetPasswordLink}">Reset Password</a>`;
200
+
201
+ await sendMail({
202
+ to: email,
203
+ subject: "Password reset successful!",
204
+ textBody: "Your password is successfully reset.",
205
+ htmlBody: emailTemplate
206
+ });
207
+
208
+ return 'Check your email for reset link';
209
+ };
210
+
211
+ const reset_password_into_db = async (
212
+ token: string,
213
+ email: string,
214
+ newPassword: string,
215
+ ) => {
216
+ let decodedData: JwtPayload;
217
+ try {
218
+ decodedData = jwtHelpers.verifyToken(
219
+ token,
220
+ configs.jwt.reset_secret as Secret,
221
+ );
222
+ } catch (err) {
223
+ throw new AppError(
224
+ 'Your reset link is expire. Submit new link request!!',
225
+ httpStatus.UNAUTHORIZED,
226
+ );
227
+ }
228
+
229
+ const isAccountExists = await isAccountExist(email)
230
+
231
+ const hashedPassword: string = await bcrypt.hash(newPassword, 10);
232
+
233
+ await Account_Model.findOneAndUpdate({ email: isAccountExists.email }, {
234
+ password: hashedPassword,
235
+ lastPasswordChange: Date()
236
+ })
237
+ return 'Password reset successfully!';
238
+ };
239
+
240
+ const verified_account_into_db = async (token: string) => {
241
+ try {
242
+ const { email } = jwtHelpers.verifyToken(token, configs.jwt.verified_token as string)
243
+ // check account is already verified or blocked
244
+ const isExistAccount = await Account_Model.findOne({ email })
245
+ // check account
246
+ if (!isExistAccount) {
247
+ throw new AppError("Account not found!!", httpStatus.NOT_FOUND)
248
+ }
249
+ if (isExistAccount.isDeleted) {
250
+ throw new AppError("Account deleted !!", httpStatus.BAD_REQUEST)
251
+ }
252
+ const result = await Account_Model.findOneAndUpdate({ email }, { isVerified: true }, { new: true })
253
+
254
+ return result
255
+ } catch (error) {
256
+ throw new AppError("Invalid or Expired token!!!", httpStatus.BAD_REQUEST)
257
+ }
258
+
259
+ }
260
+
261
+ const get_new_verification_link_from_db = async (email: string) => {
262
+ const isExistAccount = await isAccountExist(email)
263
+
264
+ const verifiedToken = jwtHelpers.generateToken(
265
+ {
266
+ email
267
+ },
268
+ configs.jwt.verified_token as Secret,
269
+ '5m'
270
+ );
271
+ const verificationLink = `${configs.jwt.front_end_url}/verified?token=${verifiedToken}`;
272
+ await sendMail({
273
+ to: email,
274
+ subject: "New Verification link",
275
+ textBody: `New Account verification link is successfully created on ${new Date().toLocaleDateString()}`,
276
+ htmlBody: `
277
+ <p>Thanks for creating an account with us. We’re excited to have you on board! Click the button below to
278
+ verify your email and activate your account:</p>
279
+
280
+
281
+ <div style="text-align: center; margin: 30px 0;">
282
+ <a href="${verificationLink}" target="_blank"
283
+ style="background-color: #4CAF50; color: #ffffff; padding: 14px 28px; text-decoration: none; font-weight: bold; border-radius: 5px; display: inline-block; font-size: 18px;"
284
+ class="btn">
285
+ Verify My Email
286
+ </a>
287
+ </div>
288
+
289
+ <p>If you did not create this account, please ignore this email.</p>
290
+ `
291
+ })
292
+
293
+ return null
294
+ }
295
+
296
+ export const auth_services = {
297
+ register_user_into_db,
298
+ login_user_from_db,
299
+ get_my_profile_from_db,
300
+ refresh_token_from_db,
301
+ change_password_from_db,
302
+ forget_password_from_db,
303
+ reset_password_into_db,
304
+ verified_account_into_db,
305
+ get_new_verification_link_from_db
306
+ }