exp-node-server 1.1.7 → 2.0.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 (181) hide show
  1. package/README.md +82 -20
  2. package/dist/cli.js +525 -0
  3. package/package.json +29 -9
  4. package/templates/mongoose/.dockerignore +10 -0
  5. package/templates/mongoose/.env.example +56 -7
  6. package/templates/mongoose/.prettierignore +3 -0
  7. package/templates/mongoose/.prettierrc +7 -0
  8. package/templates/mongoose/Dockerfile +33 -0
  9. package/templates/mongoose/_gitignore +32 -0
  10. package/templates/mongoose/docker-compose.yml +21 -0
  11. package/templates/mongoose/eslint.config.js +35 -0
  12. package/templates/mongoose/package-lock.json +6377 -0
  13. package/templates/mongoose/package.json +49 -10
  14. package/templates/mongoose/readme.md +85 -0
  15. package/templates/mongoose/src/app/configs/index.ts +178 -26
  16. package/templates/mongoose/src/app/errors/zodError.ts +16 -16
  17. package/templates/mongoose/src/app/lib/db.ts +18 -0
  18. package/templates/mongoose/src/app/lib/logger.ts +12 -0
  19. package/templates/mongoose/src/app/middlewares/auth.ts +48 -44
  20. package/templates/mongoose/src/app/middlewares/global_error_handler.ts +59 -65
  21. package/templates/mongoose/src/app/middlewares/not_found_api.ts +7 -7
  22. package/templates/mongoose/src/app/middlewares/rate_limiter.ts +153 -0
  23. package/templates/mongoose/src/app/middlewares/request_validator.ts +11 -10
  24. package/templates/mongoose/src/app/middlewares/uploader.ts +17 -13
  25. package/templates/mongoose/src/app/modules/account/account.controller.ts +156 -0
  26. package/templates/mongoose/src/app/modules/account/account.interface.ts +65 -0
  27. package/templates/mongoose/src/app/modules/account/account.route.ts +101 -0
  28. package/templates/mongoose/src/app/modules/account/account.schema.ts +97 -0
  29. package/templates/mongoose/src/app/modules/account/account.service.ts +422 -0
  30. package/templates/mongoose/src/app/modules/account/account.swagger.ts +278 -0
  31. package/templates/mongoose/src/app/modules/account/account.validation.ts +63 -0
  32. package/templates/mongoose/src/app/modules/profile/profile.controller.ts +17 -0
  33. package/templates/mongoose/src/app/modules/profile/profile.interface.ts +7 -0
  34. package/templates/mongoose/src/app/modules/profile/profile.route.ts +28 -0
  35. package/templates/mongoose/src/app/modules/profile/profile.schema.ts +13 -0
  36. package/templates/mongoose/src/app/modules/profile/profile.service.ts +28 -0
  37. package/templates/mongoose/src/app/modules/profile/profile.swagger.ts +34 -0
  38. package/templates/mongoose/src/app/modules/profile/profile.validation.ts +8 -0
  39. package/templates/mongoose/src/app/passport-js/index.ts +87 -0
  40. package/templates/mongoose/src/app/passport-js/oauth-state.ts +150 -0
  41. package/templates/mongoose/src/app/passport-js/providers.ts +258 -0
  42. package/templates/mongoose/src/app/passport-js/social-account.ts +141 -0
  43. package/templates/mongoose/src/app/queues/agenda.ts +24 -0
  44. package/templates/mongoose/src/app/queues/email.queue.ts +53 -0
  45. package/templates/mongoose/src/app/queues/index.ts +13 -0
  46. package/templates/mongoose/src/app/templates/otpEmailTemplate.ts +185 -0
  47. package/templates/mongoose/src/app/templates/resetPasswordEmailTemplate.ts +157 -0
  48. package/templates/mongoose/src/app/types/error.d.ts +7 -7
  49. package/templates/mongoose/src/app/types/express.d.ts +11 -0
  50. package/templates/mongoose/src/app/types/passport-oauth1.d.ts +51 -0
  51. package/templates/mongoose/src/app/utils/JWT.ts +38 -18
  52. package/templates/mongoose/src/app/utils/app_error.ts +10 -10
  53. package/templates/mongoose/src/app/utils/auth_cookie.ts +27 -0
  54. package/templates/mongoose/src/app/utils/catch_async.ts +8 -8
  55. package/templates/mongoose/src/app/utils/cloudinary.ts +26 -59
  56. package/templates/mongoose/src/app/utils/mail_sender.ts +26 -92
  57. package/templates/mongoose/src/app/utils/manage_response.ts +19 -19
  58. package/templates/mongoose/src/app/utils/otp.ts +20 -0
  59. package/templates/mongoose/src/app/utils/session.ts +55 -0
  60. package/templates/mongoose/src/app.ts +73 -42
  61. package/templates/mongoose/src/routes.ts +13 -13
  62. package/templates/mongoose/src/server.ts +38 -18
  63. package/templates/mongoose/src/swaggerOptions.ts +23 -23
  64. package/templates/mongoose/tests/app.test.ts +97 -0
  65. package/templates/mongoose/tests/global-setup.ts +15 -0
  66. package/templates/mongoose/tests/helpers/fixtures.ts +42 -0
  67. package/templates/mongoose/tests/routes/account.test.ts +614 -0
  68. package/templates/mongoose/tests/routes/profile.test.ts +95 -0
  69. package/templates/mongoose/tests/routes/social.test.ts +310 -0
  70. package/templates/mongoose/tests/setup.ts +25 -0
  71. package/templates/mongoose/tsconfig.json +16 -20
  72. package/templates/mongoose/vitest.config.ts +43 -0
  73. package/templates/prisma/.dockerignore +2 -0
  74. package/templates/prisma/.env.example +62 -6
  75. package/templates/prisma/.prettierignore +3 -0
  76. package/templates/prisma/.prettierrc +7 -0
  77. package/templates/prisma/.projectrc.json +1 -1
  78. package/templates/prisma/Dockerfile +21 -7
  79. package/templates/prisma/_gitignore +35 -0
  80. package/templates/prisma/docker-compose.yml +8 -7
  81. package/templates/prisma/eslint.config.js +35 -0
  82. package/templates/prisma/package-lock.json +7327 -0
  83. package/templates/prisma/package.json +47 -9
  84. package/templates/prisma/prisma/schema/account.schema.prisma +92 -10
  85. package/templates/prisma/prisma/schema/profile.schema.prisma +8 -3
  86. package/templates/prisma/prisma/schema/schema.prisma +1 -2
  87. package/templates/prisma/prisma.config.ts +5 -5
  88. package/templates/prisma/readme.md +81 -0
  89. package/templates/prisma/src/app/configs/index.ts +175 -26
  90. package/templates/prisma/src/app/errors/zodError.ts +16 -16
  91. package/templates/prisma/src/app/lib/logger.ts +12 -0
  92. package/templates/prisma/src/app/lib/prisma.ts +4 -6
  93. package/templates/prisma/src/app/middlewares/auth.ts +37 -16
  94. package/templates/prisma/src/app/middlewares/global_error_handler.ts +42 -35
  95. package/templates/prisma/src/app/middlewares/not_found_api.ts +7 -7
  96. package/templates/prisma/src/app/middlewares/rate_limiter.ts +124 -0
  97. package/templates/prisma/src/app/middlewares/request_validator.ts +4 -3
  98. package/templates/prisma/src/app/middlewares/uploader.ts +17 -13
  99. package/templates/prisma/src/app/modules/account/account.controller.ts +74 -34
  100. package/templates/prisma/src/app/modules/account/account.route.ts +68 -22
  101. package/templates/prisma/src/app/modules/account/account.service.ts +329 -309
  102. package/templates/prisma/src/app/modules/account/account.swagger.ts +167 -76
  103. package/templates/prisma/src/app/modules/account/account.validation.ts +41 -19
  104. package/templates/prisma/src/app/modules/profile/profile.controller.ts +4 -4
  105. package/templates/prisma/src/app/modules/profile/profile.route.ts +18 -12
  106. package/templates/prisma/src/app/modules/profile/profile.service.ts +11 -18
  107. package/templates/prisma/src/app/modules/profile/profile.swagger.ts +11 -11
  108. package/templates/prisma/src/app/modules/profile/profile.validation.ts +2 -2
  109. package/templates/prisma/src/app/passport-js/index.ts +87 -0
  110. package/templates/prisma/src/app/passport-js/oauth-state.ts +150 -0
  111. package/templates/prisma/src/app/passport-js/providers.ts +263 -0
  112. package/templates/prisma/src/app/passport-js/social-account.ts +147 -0
  113. package/templates/prisma/src/app/queues/boss.ts +8 -0
  114. package/templates/prisma/src/app/queues/email.queue.ts +56 -0
  115. package/templates/prisma/src/app/queues/index.ts +14 -0
  116. package/templates/prisma/src/app/queues/maintenance.queue.ts +30 -0
  117. package/templates/prisma/src/app/templates/otpEmailTemplate.ts +185 -0
  118. package/templates/prisma/src/app/templates/resetPasswordEmailTemplate.ts +157 -0
  119. package/templates/prisma/src/app/types/error.d.ts +7 -7
  120. package/templates/prisma/src/app/types/express.d.ts +11 -0
  121. package/templates/prisma/src/app/types/passport-oauth1.d.ts +51 -0
  122. package/templates/prisma/src/app/utils/JWT.ts +36 -17
  123. package/templates/prisma/src/app/utils/app_error.ts +10 -10
  124. package/templates/prisma/src/app/utils/auth_cookie.ts +27 -0
  125. package/templates/prisma/src/app/utils/catch_async.ts +8 -8
  126. package/templates/prisma/src/app/utils/cloudinary.ts +26 -59
  127. package/templates/prisma/src/app/utils/mail_sender.ts +26 -92
  128. package/templates/prisma/src/app/utils/manage_response.ts +19 -19
  129. package/templates/prisma/src/app/utils/otp.ts +20 -0
  130. package/templates/prisma/src/app/utils/session.ts +57 -0
  131. package/templates/prisma/src/app.ts +73 -42
  132. package/templates/prisma/src/routes.ts +12 -10
  133. package/templates/prisma/src/server.ts +37 -19
  134. package/templates/prisma/src/swaggerOptions.ts +22 -26
  135. package/templates/prisma/tests/app.test.ts +86 -0
  136. package/templates/prisma/tests/helpers/fixtures.ts +35 -0
  137. package/templates/prisma/tests/helpers/prisma.ts +32 -0
  138. package/templates/prisma/tests/routes/account.test.ts +627 -0
  139. package/templates/prisma/tests/routes/profile.test.ts +91 -0
  140. package/templates/prisma/tests/routes/social.test.ts +317 -0
  141. package/templates/prisma/tests/setup.ts +10 -0
  142. package/templates/prisma/tsconfig.json +16 -23
  143. package/templates/prisma/vitest.config.ts +38 -0
  144. package/bin/cli.js +0 -41
  145. package/src/createProject.js +0 -61
  146. package/src/generateModule.js +0 -93
  147. package/src/utils/getProjectType.js +0 -13
  148. package/src/utils/mongoose/controllerGenerator.js +0 -70
  149. package/src/utils/mongoose/routeGenerator.js +0 -27
  150. package/src/utils/mongoose/schemaGenerator.js +0 -11
  151. package/src/utils/mongoose/serviceGenerator.js +0 -49
  152. package/src/utils/mongoose/swaggerGenerator.js +0 -112
  153. package/src/utils/mongoose/validationGenerator.js +0 -13
  154. package/src/utils/prisma/prismaSchemaGenerator.js +0 -10
  155. package/src/utils/prisma/prismaServiceGenerator.js +0 -47
  156. package/src/utils/routeInjector.js +0 -40
  157. package/src/utils/swaggerInjector.js +0 -52
  158. package/src/utils/validateModuleName.js +0 -17
  159. package/src/utils/validateProjectName.js +0 -15
  160. package/src/utils.js +0 -26
  161. package/templates/mongoose/src/app/errors/castError.ts +0 -23
  162. package/templates/mongoose/src/app/errors/duplicateError.ts +0 -26
  163. package/templates/mongoose/src/app/errors/validationError.ts +0 -25
  164. package/templates/mongoose/src/app/modules/auth/auth.controller.ts +0 -127
  165. package/templates/mongoose/src/app/modules/auth/auth.interface.ts +0 -24
  166. package/templates/mongoose/src/app/modules/auth/auth.route.ts +0 -46
  167. package/templates/mongoose/src/app/modules/auth/auth.schema.ts +0 -19
  168. package/templates/mongoose/src/app/modules/auth/auth.service.ts +0 -324
  169. package/templates/mongoose/src/app/modules/auth/auth.swagger.ts +0 -214
  170. package/templates/mongoose/src/app/modules/auth/auth.validation.ts +0 -37
  171. package/templates/mongoose/src/app/modules/user/user.controller.ts +0 -17
  172. package/templates/mongoose/src/app/modules/user/user.interface.ts +0 -15
  173. package/templates/mongoose/src/app/modules/user/user.route.ts +0 -20
  174. package/templates/mongoose/src/app/modules/user/user.schema.ts +0 -22
  175. package/templates/mongoose/src/app/modules/user/user.service.ts +0 -22
  176. package/templates/mongoose/src/app/modules/user/user.swagger.ts +0 -75
  177. package/templates/mongoose/src/app/modules/user/user.validation.ts +0 -18
  178. package/templates/mongoose/src/app/types/index.d.ts +0 -12
  179. package/templates/mongoose/src/app/utils/isAccountExist.ts +0 -31
  180. package/templates/prisma/src/app/types/index.d.ts +0 -12
  181. package/templates/prisma/src/app/utils/otpGenerator.ts +0 -29
@@ -1,186 +1,277 @@
1
1
  export const accountSwaggerDocs = {
2
- "/api/auth/sign-up": {
2
+ '/api/auth/sign-up': {
3
3
  post: {
4
- tags: ["account"],
5
- summary: "Create new account",
6
- description: "",
4
+ tags: ['auth'],
5
+ summary: 'Create new account',
6
+ description: '',
7
7
  requestBody: {
8
8
  required: true,
9
9
  content: {
10
- "application/json": {
10
+ 'application/json': {
11
11
  example: JSON.stringify({
12
- email: "user@gmail.com",
13
- password: "password",
14
- fullName: "User",
12
+ email: 'user@gmail.com',
13
+ password: 'password',
14
+ fullName: 'User',
15
15
  }),
16
16
  },
17
17
  },
18
18
  },
19
19
  responses: {
20
- 201: { description: "account created successfully" },
21
- 500: { description: "Validation error or internal server error" },
20
+ 201: { description: 'account created successfully' },
21
+ 500: { description: 'Validation error or internal server error' },
22
22
  },
23
23
  },
24
24
  },
25
- "/api/auth/sign-in": {
25
+ '/api/auth/sign-in': {
26
26
  post: {
27
- tags: ["account"],
28
- summary: "Sign In your account",
29
- description: "",
27
+ tags: ['auth'],
28
+ summary: 'Sign In your account',
29
+ description: '',
30
30
  requestBody: {
31
31
  required: true,
32
32
  content: {
33
- "application/json": {
33
+ 'application/json': {
34
34
  example: JSON.stringify({
35
- email: "user@gmail.com",
36
- password: "password",
35
+ email: 'user@gmail.com',
36
+ password: 'password',
37
37
  }),
38
38
  },
39
39
  },
40
40
  },
41
41
  responses: {
42
- 201: { description: "User signed in successfully" },
43
- 500: { description: "Validation error or internal server error" },
42
+ 201: { description: 'User signed in successfully' },
43
+ 500: { description: 'Validation error or internal server error' },
44
44
  },
45
45
  },
46
46
  },
47
- "/api/auth/verify-otp": {
47
+ '/api/auth/verify-otp': {
48
48
  put: {
49
- tags: ["account"],
50
- summary: "Verify OTP",
51
- description: "",
49
+ tags: ['auth'],
50
+ summary: 'Verify OTP',
51
+ description: '',
52
52
  requestBody: {
53
53
  required: true,
54
54
  content: {
55
- "application/json": {
55
+ 'application/json': {
56
56
  example: JSON.stringify({
57
- email: "user@gmail.com",
58
- otp: "654321",
57
+ email: 'user@gmail.com',
58
+ otp: '654321',
59
59
  }),
60
60
  },
61
61
  },
62
62
  },
63
63
  responses: {
64
- 201: { description: "OTP verification successfully" },
65
- 500: { description: "Validation error or internal server error" },
64
+ 201: { description: 'OTP verification successfully' },
65
+ 500: { description: 'Validation error or internal server error' },
66
66
  },
67
67
  },
68
68
  },
69
- "/api/auth/verify-link": {
69
+ '/api/auth/verify-link': {
70
70
  put: {
71
- tags: ["account"],
72
- summary: "Verify Link",
73
- description: "",
71
+ tags: ['auth'],
72
+ summary: 'Verify Link',
73
+ description: '',
74
74
  requestBody: {
75
75
  required: true,
76
76
  content: {
77
- "application/json": {
77
+ 'application/json': {
78
78
  example: JSON.stringify({
79
- token: "dsakfjasdkj",
79
+ token: 'dsakfjasdkj',
80
80
  }),
81
81
  },
82
82
  },
83
83
  },
84
84
  responses: {
85
- 201: { description: "Token verification successfully" },
86
- 500: { description: "Validation error or internal server error" },
85
+ 201: { description: 'Token verification successfully' },
86
+ 500: { description: 'Validation error or internal server error' },
87
87
  },
88
88
  },
89
89
  },
90
- "/api/auth/me": {
90
+ '/api/auth/me': {
91
91
  get: {
92
- tags: ["account"],
93
- summary: "Get me account",
94
- description: "",
92
+ tags: ['auth'],
93
+ summary: 'Get me account',
94
+ description: '',
95
95
  responses: {
96
- 200: { description: "account fetched successfully" },
97
- 401: { description: "unauthorized" },
96
+ 200: { description: 'account fetched successfully' },
97
+ 401: { description: 'unauthorized' },
98
98
  },
99
99
  },
100
100
  },
101
- "/api/auth/change-password": {
101
+ '/api/auth/change-password': {
102
102
  put: {
103
- tags: ["account"],
104
- summary: "Change Password",
105
- description: "",
103
+ tags: ['auth'],
104
+ summary: 'Change Password',
105
+ description: '',
106
106
  requestBody: {
107
107
  required: true,
108
108
  content: {
109
- "application/json": {
109
+ 'application/json': {
110
110
  example: JSON.stringify({
111
- oldPassword: "123456",
112
- newPassword: "654321",
111
+ oldPassword: '123456',
112
+ newPassword: '654321',
113
113
  }),
114
114
  },
115
115
  },
116
116
  },
117
117
  responses: {
118
- 201: { description: "Passwrod change successfully" },
119
- 500: { description: "Validation error or internal server error" },
118
+ 201: { description: 'Passwrod change successfully' },
119
+ 500: { description: 'Validation error or internal server error' },
120
120
  },
121
121
  },
122
122
  },
123
- "/api/auth/resend-otp": {
123
+ '/api/auth/resend-otp': {
124
124
  put: {
125
- tags: ["account"],
126
- summary: "Resend OTP",
127
- description: "",
125
+ tags: ['auth'],
126
+ summary: 'Resend OTP',
127
+ description: '',
128
128
  requestBody: {
129
129
  required: true,
130
130
  content: {
131
- "application/json": {
131
+ 'application/json': {
132
132
  example: JSON.stringify({
133
- email: "user@gmail.com",
133
+ email: 'user@gmail.com',
134
134
  }),
135
135
  },
136
136
  },
137
137
  },
138
138
  responses: {
139
- 201: { description: "OTP resend successfully" },
140
- 500: { description: "Validation error or internal server error" },
139
+ 201: { description: 'OTP resend successfully' },
140
+ 500: { description: 'Validation error or internal server error' },
141
141
  },
142
142
  },
143
143
  },
144
- "/api/auth/forget-password": {
144
+ '/api/auth/forget-password': {
145
145
  put: {
146
- tags: ["account"],
147
- summary: "Forget Password",
148
- description: "",
146
+ tags: ['auth'],
147
+ summary: 'Forget Password',
148
+ description: '',
149
149
  requestBody: {
150
150
  required: true,
151
151
  content: {
152
- "application/json": {
152
+ 'application/json': {
153
153
  example: JSON.stringify({
154
- email: "user@gmail.com",
154
+ email: 'user@gmail.com',
155
155
  }),
156
156
  },
157
157
  },
158
158
  },
159
159
  responses: {
160
- 201: { description: "Forget password successfully" },
161
- 500: { description: "Validation error or internal server error" },
160
+ 201: { description: 'Forget password successfully' },
161
+ 500: { description: 'Validation error or internal server error' },
162
162
  },
163
163
  },
164
164
  },
165
- "/api/auth/reset-password": {
165
+ '/api/auth/reset-password': {
166
166
  put: {
167
- tags: ["account"],
168
- summary: "Reset Password",
169
- description: "",
167
+ tags: ['auth'],
168
+ summary: 'Reset Password',
169
+ description: '',
170
170
  requestBody: {
171
171
  required: true,
172
172
  content: {
173
- "application/json": {
173
+ 'application/json': {
174
174
  example: JSON.stringify({
175
- token: "dkfjadskfds",
176
- newPass: "newpass",
175
+ token: 'dkfjadskfds',
176
+ newPass: 'newpass',
177
177
  }),
178
178
  },
179
179
  },
180
180
  },
181
181
  responses: {
182
- 201: { description: "Password reset successfully" },
183
- 500: { description: "Validation error or internal server error" },
182
+ 201: { description: 'Password reset successfully' },
183
+ 500: { description: 'Validation error or internal server error' },
184
+ },
185
+ },
186
+ },
187
+ '/api/auth/refresh-token': {
188
+ post: {
189
+ tags: ['auth'],
190
+ summary: 'Get a new access token (and a rotated refresh token)',
191
+ description:
192
+ 'Browsers send the `refresh_token` cookie automatically; other clients send it in the body. ' +
193
+ 'Every call returns a new refresh token and invalidates the old one. Reusing an old ' +
194
+ 'refresh token ends the whole session.',
195
+ requestBody: {
196
+ required: false,
197
+ content: {
198
+ 'application/json': {
199
+ example: JSON.stringify({ refreshToken: 'only-for-non-browser-clients' }),
200
+ },
201
+ },
202
+ },
203
+ responses: {
204
+ 200: { description: '{ accessToken, refreshToken }, both also set as httpOnly cookies' },
205
+ 401: { description: 'Missing, invalid, expired or reused refresh token' },
206
+ },
207
+ },
208
+ },
209
+ '/api/auth/sign-out': {
210
+ post: {
211
+ tags: ['auth'],
212
+ summary: 'Sign out this device (revokes its refresh token, clears the cookies)',
213
+ responses: { 200: { description: 'Signed out' } },
214
+ },
215
+ },
216
+ '/api/auth/sign-out-all': {
217
+ post: {
218
+ tags: ['auth'],
219
+ summary: 'Sign out from all devices (revokes every access token)',
220
+ responses: {
221
+ 200: { description: 'Signed out everywhere' },
222
+ 401: { description: 'unauthorized' },
223
+ },
224
+ },
225
+ },
226
+ '/api/auth/social/providers': {
227
+ get: {
228
+ tags: ['social auth'],
229
+ summary: 'List the enabled social login providers',
230
+ responses: { 200: { description: 'Providers, with the URL that starts each login' } },
231
+ },
232
+ },
233
+ '/api/auth/social/{provider}': {
234
+ get: {
235
+ tags: ['social auth'],
236
+ summary: 'Start a social login (open in the browser, not via fetch)',
237
+ description:
238
+ "Link to it from the front-end: <a href='/api/auth/social/google?returnTo=/dashboard'>Login with Google</a>. " +
239
+ 'After the provider, the browser is redirected to SOCIAL_REDIRECT_URL with `?code=` (and `returnTo`) on success, ' +
240
+ 'or `?error=` (access_denied, invalid_state, email_unverified, account_disabled, social_login_failed).',
241
+ parameters: [
242
+ {
243
+ name: 'provider',
244
+ in: 'path',
245
+ required: true,
246
+ schema: {
247
+ type: 'string',
248
+ enum: ['google', 'facebook', 'github', 'twitter', 'linkedin', 'discord'],
249
+ },
250
+ },
251
+ {
252
+ name: 'returnTo',
253
+ in: 'query',
254
+ required: false,
255
+ schema: { type: 'string', example: '/dashboard' },
256
+ description: 'Path on the front-end to come back to. Only same-site paths are kept.',
257
+ },
258
+ ],
259
+ responses: { 302: { description: 'Redirect to the provider' } },
260
+ },
261
+ },
262
+ '/api/auth/social/exchange': {
263
+ post: {
264
+ tags: ['social auth'],
265
+ summary: 'Trade the single-use code from the social login redirect for an access token',
266
+ requestBody: {
267
+ required: true,
268
+ content: {
269
+ 'application/json': { example: JSON.stringify({ code: 'code-from-redirect' }) },
270
+ },
271
+ },
272
+ responses: {
273
+ 200: { description: 'Logged in (token in body and httpOnly cookie)' },
274
+ 401: { description: 'Invalid, expired or already used code' },
184
275
  },
185
276
  },
186
277
  },
@@ -1,41 +1,63 @@
1
- import z from "zod";
1
+ import z from 'zod';
2
+
3
+ const email = z.email('A valid email is required.').transform((e) => e.toLowerCase());
4
+ const password = z
5
+ .string('Password is required.')
6
+ .min(8, 'Password must be at least 8 characters.')
7
+ .max(72, 'Password must be at most 72 characters.'); // bcrypt ignores bytes past 72
2
8
 
3
9
  const sign_up = z.object({
4
- email: z.string("Email is required."),
5
- password: z.string("Password is required."),
6
- fullName: z.string("Full name is required."),
10
+ email,
11
+ password,
12
+ fullName: z.string('Full name is required.').trim().min(1).max(100),
7
13
  });
8
14
 
9
- const sing_in = z.object({
10
- email: z.string("Email is required."),
11
- password: z.string("Password is required."),
15
+ const sign_in = z.object({
16
+ email,
17
+ password: z.string('Password is required.').max(72),
12
18
  });
13
19
 
14
20
  const change_password = z.object({
15
- oldPassword: z.string("Old Password is requied"),
16
- newPassword: z.string("New Password is required"),
21
+ oldPassword: z.string('Old Password is required').max(72),
22
+ newPassword: password,
17
23
  });
18
24
 
19
25
  const verify_otp = z.object({
20
- email: z.string("Email is requied"),
21
- otp: z.string("OTP is required"),
26
+ email,
27
+ otp: z.string('OTP is required').regex(/^\d{6}$/, 'OTP must be 6 digits'),
22
28
  });
29
+
30
+ const token = z.string('Token is required').min(1).max(2048);
31
+
23
32
  const verify_link = z.object({
24
- token: z.string("Token is required "),
33
+ token,
25
34
  });
26
- const resend_otp = z.object({
27
- email: z.string("Email is requied"),
35
+
36
+ const email_only = z.object({
37
+ email,
28
38
  });
39
+
29
40
  const reset_pass = z.object({
30
- token: z.string("Token is required"),
31
- newPass: z.string("Password is required"),
41
+ token,
42
+ newPass: password,
32
43
  });
44
+
45
+ // optional: browsers send the refresh token as a cookie instead
46
+ const refresh_token = z.object({ refreshToken: z.string().min(1).max(256).optional() }).optional();
47
+
48
+ const social_exchange = z.object({
49
+ code: z.string('Code is required').min(1).max(256),
50
+ });
51
+
33
52
  export const account_validation = {
34
53
  sign_up,
35
- sing_in,
54
+ sign_in,
36
55
  change_password,
37
56
  verify_otp,
38
- resend_otp,
57
+ resend_otp: email_only,
58
+ forget_password: email_only,
39
59
  verify_link,
40
- reset_pass
60
+ reset_pass,
61
+ social_exchange,
62
+ refresh_token,
41
63
  };
@@ -1,13 +1,13 @@
1
- import catchAsync from "../../utils/catch_async";
2
- import manageResponse from "../../utils/manage_response";
3
- import { profile_service } from "./profile.service";
1
+ import catchAsync from '../../utils/catch_async.js';
2
+ import manageResponse from '../../utils/manage_response.js';
3
+ import { profile_service } from './profile.service.js';
4
4
 
5
5
  const update_profile = catchAsync(async (req, res) => {
6
6
  const result = await profile_service.update_profile_into_db(req);
7
7
  manageResponse(res, {
8
8
  success: true,
9
9
  statusCode: 200,
10
- message: "profile updated successfully.",
10
+ message: 'profile updated successfully.',
11
11
  data: result,
12
12
  });
13
13
  });
@@ -1,19 +1,25 @@
1
- import { Router } from "express";
2
- import RequestValidator from "../../middlewares/request_validator";
3
- import { profile_controller } from "./profile.controller";
4
- import { profile_validations } from "./profile.validation";
5
- import auth from "../../middlewares/auth";
6
- import uploader from "../../middlewares/uploader";
1
+ import { Router } from 'express';
2
+ import auth from '../../middlewares/auth.js';
3
+ import RequestValidator from '../../middlewares/request_validator.js';
4
+ import uploader from '../../middlewares/uploader.js';
5
+ import { AppError } from '../../utils/app_error.js';
6
+ import { profile_controller } from './profile.controller.js';
7
+ import { profile_validations } from './profile.validation.js';
7
8
 
8
9
  const router = Router();
9
10
 
10
11
  router.patch(
11
- "/",
12
- auth("USER"),
13
- uploader.single("file"),
14
- (req, res, next) => {
15
- req.body = JSON.parse(req?.body?.data);
16
- next();
12
+ '/',
13
+ auth(),
14
+ uploader.single('file'),
15
+ // multipart forms send the JSON fields as a `data` string
16
+ (req, _res, next) => {
17
+ try {
18
+ req.body = req.body?.data ? JSON.parse(req.body.data) : {};
19
+ next();
20
+ } catch {
21
+ next(new AppError('`data` must be valid JSON', 400));
22
+ }
17
23
  },
18
24
  RequestValidator(profile_validations.update_profile),
19
25
  profile_controller.update_profile,
@@ -1,24 +1,17 @@
1
- import { Request } from "express";
2
- import uploadCloud from "../../utils/cloudinary";
3
- import { prisma } from "../../lib/prisma";
4
- import { JwtPayloadType } from "../../utils/JWT";
1
+ import { Request } from 'express';
2
+ import { prisma } from '../../lib/prisma.js';
3
+ import uploadCloud from '../../utils/cloudinary.js';
5
4
 
6
5
  const update_profile_into_db = async (req: Request) => {
7
- const user = req?.user as JwtPayloadType;
8
- const payload = req?.body;
9
- const file = req?.file;
10
- // check file and upload to cloud
11
- if (file) {
12
- const cloudRes = await uploadCloud(file);
13
- payload.profilePhoto = cloudRes?.secure_url;
14
- }
15
- const result = await prisma.profile.update({
16
- where: {
17
- accountId: user.accountId as string,
18
- },
19
- data: payload,
6
+ const payload: { fullName?: string } = req.body;
7
+
8
+ // validation already ran (see profile.route.ts), so only valid requests upload
9
+ const profilePhoto = req.file ? (await uploadCloud(req.file, 'profiles')).secure_url : undefined;
10
+
11
+ return prisma.profile.update({
12
+ where: { accountId: req.user!.accountId },
13
+ data: { fullName: payload.fullName, profilePhoto },
20
14
  });
21
- return result;
22
15
  };
23
16
 
24
17
  export const profile_service = {
@@ -1,24 +1,24 @@
1
1
  export const profileSwaggerDocs = {
2
- "/api/profile": {
2
+ '/api/profile': {
3
3
  patch: {
4
- tags: ["profile"],
5
- summary: "Update profile",
4
+ tags: ['profile'],
5
+ summary: 'Update profile',
6
6
  requestBody: {
7
7
  required: true,
8
8
  content: {
9
- "multipart/form-data": {
9
+ 'multipart/form-data': {
10
10
  schema: {
11
- type: "object",
11
+ type: 'object',
12
12
  properties: {
13
13
  data: {
14
- type: "object",
14
+ type: 'object',
15
15
  properties: {
16
- fullName: { type: "string" },
16
+ fullName: { type: 'string' },
17
17
  },
18
18
  },
19
19
  file: {
20
- type: "string",
21
- format: "binary",
20
+ type: 'string',
21
+ format: 'binary',
22
22
  },
23
23
  },
24
24
  },
@@ -26,8 +26,8 @@ export const profileSwaggerDocs = {
26
26
  },
27
27
  },
28
28
  responses: {
29
- 200: { description: "profile updated successfully" },
30
- 500: { description: "Validation error or internal server error" },
29
+ 200: { description: 'profile updated successfully' },
30
+ 500: { description: 'Validation error or internal server error' },
31
31
  },
32
32
  },
33
33
  },
@@ -1,6 +1,6 @@
1
- import { z } from "zod";
1
+ import { z } from 'zod';
2
2
  const update_profile = z.object({
3
- fullName: z.string().optional(),
3
+ fullName: z.string().trim().min(1).max(100).optional(),
4
4
  });
5
5
 
6
6
  export const profile_validations = {