@@ -12,6 +12,7 @@ import {
12
12
updateUserById as _updateUserById ,
13
13
updateUserPrivilegeById as _updateUserPrivilegeById ,
14
14
updateUserVerification as _updateUserVerification ,
15
+ updateUserPassword as _updateUserPassword ,
15
16
} from "../model/repository" ;
16
17
import {
17
18
validateEmail ,
@@ -25,8 +26,13 @@ import { upload } from "../config/multer";
25
26
import { uploadFileToFirebase } from "../utils/utils" ;
26
27
import redisClient from "../config/redis" ;
27
28
import crypto from "crypto" ;
28
- import { sendAccVerificationMail } from "../utils/mailer" ;
29
- import { ACCOUNT_VERIFICATION_SUBJ } from "../utils/constants" ;
29
+ import { sendMail } from "../utils/mailer" ;
30
+ import {
31
+ ACCOUNT_VERIFICATION_SUBJ ,
32
+ ACCOUNT_VERIFICATION_TEMPLATE ,
33
+ RESET_PASSWORD_SUBJ ,
34
+ RESET_PASSWORD_TEMPLATE ,
35
+ } from "../utils/constants" ;
30
36
31
37
export async function createUser (
32
38
req : Request ,
@@ -113,11 +119,14 @@ export const sendVerificationMail = async (
113
119
}
114
120
115
121
const emailToken = crypto . randomBytes ( 16 ) . toString ( "hex" ) ;
116
- await redisClient . set ( email , emailToken , { EX : 60 * 5 } ) ; // expire in 5 minutes
117
- await sendAccVerificationMail (
122
+ await redisClient . set ( `email_verification:${ email } ` , emailToken , {
123
+ EX : 60 * 5 ,
124
+ } ) ; // expire in 5 minutes
125
+ await sendMail (
118
126
email ,
119
127
ACCOUNT_VERIFICATION_SUBJ ,
120
128
user . username ,
129
+ ACCOUNT_VERIFICATION_TEMPLATE ,
121
130
emailToken
122
131
) ;
123
132
@@ -145,7 +154,7 @@ export const verifyUser = async (
145
154
return res . status ( 404 ) . json ( { message : `User ${ email } not found` } ) ;
146
155
}
147
156
148
- const expectedToken = await redisClient . get ( email ) ;
157
+ const expectedToken = await redisClient . get ( `email_verification: ${ email } ` ) ;
149
158
150
159
if ( expectedToken !== token ) {
151
160
return res
@@ -155,12 +164,10 @@ export const verifyUser = async (
155
164
156
165
const updatedUser = await _updateUserVerification ( email ) ;
157
166
if ( ! updatedUser ) {
158
- return res . status ( 404 ) . json ( { message : `User ${ email } not verified.` } ) ;
167
+ return res . status ( 404 ) . json ( { message : `User not verified.` } ) ;
159
168
}
160
169
161
- return res
162
- . status ( 200 )
163
- . json ( { message : `User ${ email } verified successfully.` } ) ;
170
+ return res . status ( 200 ) . json ( { message : `User verified successfully.` } ) ;
164
171
} catch ( error ) {
165
172
return res
166
173
. status ( 500 )
@@ -334,6 +341,93 @@ export async function updateUser(
334
341
}
335
342
}
336
343
344
+ export const sendResetPasswordMail = async (
345
+ req : Request ,
346
+ res : Response
347
+ ) : Promise < Response > => {
348
+ try {
349
+ const { email } = req . body ;
350
+ const user = await _findUserByEmail ( email ) ;
351
+
352
+ if ( ! user ) {
353
+ return res . status ( 404 ) . json ( { message : `User not found` } ) ;
354
+ }
355
+
356
+ if ( ! user . isVerified ) {
357
+ return res . status ( 403 ) . json ( {
358
+ message : "User is not verified. Please verify your account first." ,
359
+ } ) ;
360
+ }
361
+
362
+ const emailToken = crypto . randomBytes ( 16 ) . toString ( "hex" ) ;
363
+ await redisClient . set ( `password_reset:${ email } ` , emailToken , {
364
+ EX : 60 * 5 ,
365
+ } ) ; // expire in 5 minutes
366
+ await sendMail (
367
+ email ,
368
+ RESET_PASSWORD_SUBJ ,
369
+ user . username ,
370
+ RESET_PASSWORD_TEMPLATE ,
371
+ emailToken
372
+ ) ;
373
+
374
+ return res . status ( 200 ) . json ( {
375
+ message : "Reset password email sent. Please check your inbox." ,
376
+ data : { email, id : user . id } ,
377
+ } ) ;
378
+ } catch ( error ) {
379
+ return res . status ( 500 ) . json ( {
380
+ message : "Unknown error when sending reset password email!" ,
381
+ error,
382
+ } ) ;
383
+ }
384
+ } ;
385
+
386
+ export const resetPassword = async (
387
+ req : Request ,
388
+ res : Response
389
+ ) : Promise < Response > => {
390
+ try {
391
+ const { email, token, password } = req . body ;
392
+
393
+ const user = await _findUserByEmail ( email ) ;
394
+ if ( ! user ) {
395
+ return res . status ( 404 ) . json ( { message : `User not found` } ) ;
396
+ }
397
+
398
+ const expectedToken = await redisClient . get ( `password_reset:${ email } ` ) ;
399
+
400
+ if ( expectedToken !== token ) {
401
+ return res
402
+ . status ( 400 )
403
+ . json ( { message : "Invalid token. Please request for a new one." } ) ;
404
+ }
405
+
406
+ const { isValid : isValidPassword , message : passwordMessage } =
407
+ validatePassword ( password ) ;
408
+ if ( ! isValidPassword ) {
409
+ return res . status ( 400 ) . json ( { message : passwordMessage } ) ;
410
+ }
411
+
412
+ const salt = bcrypt . genSaltSync ( 10 ) ;
413
+ const hashedPassword = bcrypt . hashSync ( password , salt ) ;
414
+
415
+ const updatedUser = await _updateUserPassword ( email , hashedPassword ) ;
416
+
417
+ if ( ! updatedUser ) {
418
+ return res . status ( 404 ) . json ( { message : `User's password not reset.` } ) ;
419
+ }
420
+
421
+ return res
422
+ . status ( 200 )
423
+ . json ( { message : `User's password successfully reset.` } ) ;
424
+ } catch ( error ) {
425
+ return res
426
+ . status ( 500 )
427
+ . json ( { message : "Unknown error when resetting user password!" , error } ) ;
428
+ }
429
+ } ;
430
+
337
431
export async function updateUserPrivilege (
338
432
req : Request ,
339
433
res : Response
0 commit comments