• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

atlp-rwanda / e-commerce-ones-and-zeroes-bn / 7d8be62f-1191-4ca4-84de-5a4c5c76912c

23 Jul 2024 08:47PM UTC coverage: 91.503% (+1.6%) from 89.874%
7d8be62f-1191-4ca4-84de-5a4c5c76912c

push

circleci

web-flow
Merge pull request #76 from atlp-rwanda/bg-fix-crud-seller-collection

[#187355114] bg-fix  seller create collection

293 of 347 branches covered (84.44%)

Branch coverage included in aggregate %.

48 of 50 new or added lines in 2 files covered. (96.0%)

45 existing lines in 5 files now uncovered.

1010 of 1077 relevant lines covered (93.78%)

3.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.49
/src/controllers/userControllers.ts
1
import { Request, Response } from 'express';
2
import bcrypt from 'bcrypt';
10✔
3
import speakeasy from 'speakeasy';
10✔
4
import { generateToken } from '../helps/generateToken';
10✔
5
import { validateEmail, validatePassword } from '../validations/validations';
10✔
6
import {
10✔
7
  registerMessageTemplate,
8
  nodeMail,
9
  successfullyverifiedTemplate,
10
  successfullyDisabledAccountTemplate,
11
  successfullyRestoredAccountTemplate,
12
  twoFAMessageTemplate,
13
  resetPasswordEmail,
14
} from '../utils/emails';
15
import { db } from '../database/models';
10✔
16
import passport from '../config/google.auth';
10✔
17
import { registerToken } from '../config/jwt.token';
10✔
18
const jwt = require('jsonwebtoken');
10✔
19
const sgMail = require('@sendgrid/mail');
10✔
20
const dotenv = require('dotenv');
10✔
21
dotenv.config();
10✔
22
const secret = process.env.JWT_SECRET;
10✔
23

24
interface User {
25
  firstName: string;
26
  lastName: string;
27
  email: string;
28
  password: string;
29
  role: string;
30
  isVerified: boolean;
31
  isSeller?: boolean;
32
}
33

34
export default class UserController {
10✔
35
  static async getUsers(req: Request, res: Response): Promise<Response> {
36
    try {
4✔
37
      const page = parseInt(req.query.page as string) || 1;
4!
38
      const rowsPerPage = parseInt(req.query.rowsPerPage as string) || 10;
2!
39
      const offset = (page - 1) * rowsPerPage;
2✔
40
      const users = await db.User.findAll({
2✔
41
        offset: offset,
42
        limit: rowsPerPage,
43
      });
44

45
      const userCount = await db.User.count();
2✔
46

47
      return res.status(200).json({
2✔
48
        data: {
49
          users: users,
50
          pagination: {
51
            currentPage: page,
52
            rowsPerPage: rowsPerPage,
53
            pageCount: Math.ceil(userCount / rowsPerPage),
54
            totalUsers: userCount,
55
          },
56
        },
57
      });
58
    } catch (error) {
59
      return res.status(500).json({ message: 'Failed to fetch users' });
2✔
60
    }
61
  }
62

63
  static async registerUser(req: Request, res: Response): Promise<Response> {
64
    try {
6✔
65
      const { firstName, lastName, email, password } = req.body as User;
6✔
66

67
      if (!firstName || !lastName || !email || !password) {
6✔
68
        return res.status(400).json({ message: 'All fields are required' });
1✔
69
      }
70

71
      const existingUser = await db.User.findOne({ where: { email } });
5✔
72
      if (existingUser) {
4✔
73
        return res.status(400).json({ message: 'Email already exists' });
1✔
74
      }
75

76
      if (!validateEmail(email)) {
3✔
77
        return res.status(400).json({ message: 'Invalid email' });
1✔
78
      }
79

80
      if (!validatePassword(password)) {
2✔
81
        return res.status(400).json({ message: 'Password should be strong' });
1✔
82
      }
83

84
      const hashedPassword = bcrypt.hashSync(password, 10);
1✔
85

86
      const isSeller: boolean = req.body.isSeller;
1✔
87

88
      const newUser = await db.User.create({
1✔
89
        firstName,
90
        lastName,
91
        email,
92
        role: isSeller ? 'seller' : 'buyer',
1!
93
        password: hashedPassword,
94
      });
95

96
      const token = generateToken(
1✔
97
        newUser.userId,
98

99
        email,
100

101
        firstName,
102

103
        lastName,
104
        newUser.passwordLastChanged,
105

106
        newUser?.role,
3!
107
        newUser?.isVerified,
3!
108
      );
109

110
      await nodeMail(
1✔
111
        email,
112
        'You are required to Verify your email',
113
        registerMessageTemplate(firstName, token),
114
      );
115

116
      return res
1✔
117
        .status(200)
118
        .json({ message: 'Account created!', data: newUser, token });
119
    } catch (error: any) {
120
      return res.status(500).json({ message: 'Failed to register user' });
1✔
121
    }
122
  }
123

124
  // get single profile/user controller
125
  static async getSingleUser(req: Request, res: Response) {
126
    try {
2✔
127
      const singleUser = await db.User.findOne({
2✔
128
        where: {
129
          userId: req.params.id,
130
        },
131
      });
132
      if (singleUser) {
1✔
133
        return res.status(200).json({
1✔
134
          status: 'User Profile',
135
          data: singleUser,
136
          billing: singleUser.billingAddress,
137
        });
138
      }
139
    } catch (error: any) {
140
      return res.status(500).json({
1✔
141
        message: "provided ID doen't exist!",
142
        error: error.message,
143
      });
144
    }
145
  }
146
  //update single profile/user
147
  static async updateSingleUser(req: Request, res: Response) {
148
    try {
4✔
149
      const singleUser = await db.User.findOne({
4✔
150
        where: {
151
          userId: req.params.id,
152
        },
153
      });
154

155
      if (!singleUser) {
3✔
156
        return res.status(404).json({
1✔
157
          status: 'Not Found',
158
          error: 'User not found',
159
        });
160
      }
161

162
      const {
163
        firstName,
164
        lastName,
165
        gender,
166
        birthdate,
167
        preferredLanguage,
168
        preferredCurrency,
169
        billingAddress,
170
      } = req.body;
2✔
171

172
      if (firstName) {
2✔
173
        singleUser.firstName = firstName;
2✔
174
      }
175
      if (lastName) {
2✔
176
        singleUser.lastName = lastName;
2✔
177
      }
178
      if (gender) {
2✔
179
        singleUser.gender = gender;
2✔
180
      }
181
      if (birthdate) {
2✔
182
        singleUser.birthdate = birthdate;
2✔
183
      }
184
      if (preferredLanguage) {
2✔
185
        singleUser.preferredLanguage = preferredLanguage;
2✔
186
      }
187
      if (preferredCurrency) {
2✔
188
        singleUser.preferredCurrency = preferredCurrency;
2✔
189
      }
190
      if (billingAddress) {
2✔
191
        singleUser.billingAddress = billingAddress;
2✔
192
      }
193

194
      singleUser.updatedAt = new Date();
2✔
195

196
      if (req.body.email) {
2✔
197
        return res.status(400).json({
1✔
198
          status: 'Bad Request',
199
          error: 'Email cannot be updated',
200
        });
201
      }
202

203
      await singleUser.save();
1✔
204

205
      return res.status(200).json({
1✔
206
        status: 'Profile updated successfully',
207
        data: singleUser,
208
      });
209
    } catch (err: any) {
210
      return res.status(500).json({
1✔
211
        status: 'Internal Server Error',
212
        error: err.message,
213
      });
214
    }
215
  }
216

217
  static async registerUserGoogle(req: any, res: any) {
218
    const data = req.body;
2✔
219
    let firstName = data.given_name;
2✔
220
    let lastName = data.family_name;
2✔
221
    let email = data.email;
2✔
222
    const newUser = {
2✔
223
      firstName,
224
      lastName,
225
      email,
226
      isActive: true,
227
      isGoogle: true,
228
      password: 'google',
229
    };
230
    try {
2✔
231
      const emailExist = await db.User.findOne({
2✔
232
        where: { email: email, isGoogle: false },
233
      });
234
      if (emailExist) {
1!
235
        ('Email already exists');
×
236
        return res.status(401).json({
×
237
          message:
238
            'Email has registered using normal way, Go and login using email and password',
239
        });
240
      }
241
      const alreadyRegistered = await db.User.findOne({
1✔
242
        where: { email: email, isGoogle: true },
243
      });
244
      if (alreadyRegistered) {
1!
UNCOV
245
        const payLoad = {
×
246
          userId: alreadyRegistered.userId,
247
          firstName: alreadyRegistered.firstName,
248
          lastName: alreadyRegistered.lastName,
249
          role: alreadyRegistered.role,
250
        };
UNCOV
251
        const userToken = await registerToken(payLoad);
×
UNCOV
252
        return res.status(201).json({ message: 'User signed in!', userToken });
×
253
      }
254
      const createdUser = await db.User.create(newUser);
1✔
255
      const payLoad = {
1✔
256
        userId: createdUser.userId,
257
        firstName: createdUser.firstName,
258
        lastName: createdUser.lastName,
259
        role: createdUser.role,
260
      };
261
      const userToken = await registerToken(payLoad);
1✔
262
      return res.status(201).json({
1✔
263
        message: 'User registered Successful, Please Sign in!',
264
        token: userToken,
265
      });
266
    } catch (err) {
267
      return res.status(500).json({ message: 'Internal Serveral error!' });
1✔
268
    }
269
  }
270

271
  static async loginUserPage(req: any, res: any) {
272
    return res.send('User login <a href="/auth/google">google</a>');
1✔
273
  }
274

275
  static async googleAuth(req: any, res: any) {
276
    passport.authenticate('google', { scope: ['profile', 'email'] });
1✔
277
  }
278

279
  static async login(req: Request, res: Response) {
280
    try {
6✔
281
      const { email, password } = req.body;
6✔
282
      if (!email || !password) {
6✔
283
        return res
1✔
284
          .status(400)
285
          .json({ message: 'Email and password are required' });
286
      }
287

288
      const user = await db.User.findOne({ where: { email } });
5✔
289
      if (!user) {
4✔
290
        return res.status(404).json({ message: 'User not found' });
1✔
291
      }
292

293
      const isPasswordMatch = await bcrypt.compare(password, user.password);
3✔
294
      if (!isPasswordMatch) {
3✔
295
        return res.status(401).json({ message: 'Incorrect credentials' });
1✔
296
      }
297

298
      if (!user.isVerified) {
2✔
299
        return res.status(401).send({ message: 'Email not verified' });
1✔
300
      }
301

302
      if (user.role === 'seller') {
1!
UNCOV
303
        if (!user.use2FA) {
×
304
          // If 2FA is not enabled, return a JWT token without 2FA verification
UNCOV
305
          const token = generateToken(
×
306
            user.userId,
307
            user.email,
308
            user.firstName,
309
            user.lastName,
310
            user.role,
311
            user.passwordLastChanged,
312
            user.isVerified,
313
          );
UNCOV
314
          return res
×
315
            .status(200)
316
            .json({ message: 'User authenticated without 2FA', token });
317
        }
318

319
        // If use2FA is true, proceed with sending the 2FA token
UNCOV
320
        const token = speakeasy.totp({
×
321
          secret: user.secret,
322
          encoding: 'base32',
323
          step: 120, // Token is valid for 2 minutes
324
        });
325

326
        // Send the email with the token
UNCOV
327
        const name = user.firstName;
×
UNCOV
328
        await nodeMail(
×
329
          email,
330
          '2FA Token for One and Zero E-commerce',
331
          twoFAMessageTemplate(name, token),
332
        );
333

334
        // Send response with message to check email for the 2FA token
NEW
335
        return res.status(200).json({
×
336
          message: 'Check your email for the 2FA token',
337
          userId: user.userId,
338
        });
339
      } else {
340
        const token = generateToken(
1✔
341
          user.userId,
342
          user.email,
343
          user.firstName,
344
          user.lastName,
345
          user.role,
346
          user.passwordLastChanged,
347
          user.isVerified,
348
        );
349
        res.status(200).json({ message: 'User authenticated', token });
1✔
350
      }
351
    } catch (error: any) {
352
      res.status(500).json({ message: 'Error during login' });
1✔
353
    }
354
  }
355

356
  static async disableUser(req: Request, res: Response) {
357
    try {
6✔
358
      const { reason } = req.body;
6✔
359
      const existingUser = await db.User.findOne({
6✔
360
        where: { userId: req.params.id },
361
      });
362
      const user = (req as any).user;
6✔
363
      if (!existingUser) {
6✔
364
        return res.status(404).json({ message: 'No such User found' });
1✔
365
      }
366
      if (existingUser.dataValues.userId === user.userId) {
5✔
367
        return res.status(403).json({ message: 'User cannot self-disable' });
1✔
368
      }
369

370
      if (!existingUser.dataValues.isActive) {
3✔
371
        await db.User.update(
1✔
372
          { isActive: true },
373
          {
374
            where: {
375
              userId: req.params.id,
376
            },
377
          },
378
        );
379
        const restoredMessage: string = successfullyRestoredAccountTemplate(
1✔
380
          existingUser.dataValues.firstName,
381
        );
382
        await nodeMail(
1✔
383
          existingUser.dataValues.email,
384
          'Your account was restored',
385
          restoredMessage,
386
        );
387
        return res
1✔
388
          .status(200)
389
          .json({ message: 'User account was successfully restored' });
390
      }
391
      if (!reason) {
2✔
392
        return res
1✔
393
          .status(400)
394
          .json({ message: 'Missing reason for disabling account' });
395
      }
396
      await db.User.update(
1✔
397
        { isActive: false },
398
        {
399
          where: {
400
            userId: req.params.id,
401
          },
402
        },
403
      );
404
      const disabledMessage: string = successfullyDisabledAccountTemplate(
1✔
405
        existingUser.dataValues.firstName,
406
        reason,
407
      );
408

409
      await nodeMail(
1✔
410
        existingUser.dataValues.email,
411
        'Your account was disabled',
412
        disabledMessage,
413
      );
414
      return res
1✔
415
        .status(200)
416
        .json({ message: 'User account was successfully disabled' });
417
    } catch (err) {
418
      return res
1✔
419
        .status(500)
420
        .json({ message: 'Failed to disable user account' });
421
    }
422
  }
423

424
  static async isVerified(req: Request, res: Response) {
425
    try {
4✔
426
      const token = req.params.token;
4✔
427
      if (!token) {
4✔
428
        return res.status(400).json({ error: 'No token provided' });
1✔
429
      }
430

431
      let decoded: any;
432
      decoded = jwt.verify(token, secret!);
3✔
433
      const { userId } = decoded;
2✔
434
      const [updated] = await db.User.update(
2✔
435
        { isVerified: true },
436
        { where: { userId } },
437
      );
438

439
      if (updated === 0) {
2✔
440
        throw new Error('No user updated');
1✔
441
      }
442

443
      const email = decoded.email;
1✔
444
      const name = decoded.firstName;
1✔
445

446
      await nodeMail(
1✔
447
        email,
448
        'Welcome to One and Zero E-commerce',
449
        successfullyverifiedTemplate(name),
450
      );
451

452
      return res
1✔
453
        .status(200)
454
        .redirect(`${process.env.CLIENT_URL}/users/isVerified`);
455
    } catch (error) {
456
      if (error instanceof jwt.JsonWebTokenError) {
2✔
457
        return res
1✔
458
          .status(400)
459
          .redirect(`${process.env.CLIENT_URL}/users/isVerified`);
460
      } else {
461
        return res
1✔
462
          .status(500)
463
          .redirect(`${process.env.CLIENT_URL}/users/isVerified`);
464
      }
465
    }
466
  }
467
  static async updatePassword(req: any, res: Response) {
468
    const { token } = req;
5✔
469
    const { password, newPassword, verifyNewPassword } = req.body;
5✔
470

471
    try {
5✔
472
      const getDecodedToken = jwt.verify(token, secret);
5✔
473
      const userId = getDecodedToken.userId;
5✔
474
      const userData = await db.User.findOne({
4✔
475
        where: { userId: userId },
476
      });
477

478
      if (!userData) {
4✔
479
        return res.status(404).json({
1✔
480
          status: 'fail',
481
          message: 'User not found',
482
        });
483
      }
484

485
      //extract Hash Password from user detail
486
      const currentHash = userData.dataValues.password;
3✔
487

488
      try {
3✔
489
        const result = await bcrypt.compare(password, currentHash);
3✔
490
        if (result == false) {
3✔
491
          return res.status(401).json({
1✔
492
            status: 'fail',
493
            message: 'Wrong credentials',
494
          });
495
        }
496

497
        //hashNewPassword
498
        const saltRounds = 10;
2✔
499
        const salt: any = await bcrypt.genSalt(saltRounds);
2✔
500
        const newHashPassword = await bcrypt.hash(newPassword, salt);
2✔
501

502
        const [updatePassword] = await db.User.update(
2✔
503
          { password: newHashPassword },
504
          { where: { userId: userId } },
505
        );
506

507
        if (updatePassword > 0) {
1✔
508
          return res.status(200).json({
1✔
509
            status: 'OK',
510
            message: 'Password updated successfully',
511
          });
512
        }
513
      } catch (e) {
514
        return res.status(500).json({
1✔
515
          status: 'error',
516
          message: 'Server error',
517
        });
518
      }
519
    } catch (e) {
520
      res.status(500).json({
1✔
521
        status: 'fail',
522
        message: 'something went wrong: ' + e,
523
      });
524
    }
525
  }
526
  static async setUserRoles(req: Request, res: Response) {
527
    try {
4✔
528
      const { role } = req.body;
4✔
529
      if (!role)
3✔
530
        return res.status(400).json({
1✔
531
          message: 'role can not be empty',
532
        });
533
      const user = await db.User.findOne({ where: { userId: req.params.id } });
2✔
534
      if (!user)
2✔
535
        return res.status(404).json({
1✔
536
          message: 'user not found',
537
        });
538

539
      const updatedUser = await db.User.update(
1✔
540
        { role: role },
541
        { where: { userId: req.params.id } },
542
      );
543
      return res.status(200).json({
1✔
544
        message: 'user role updated',
545
      });
546
    } catch (error: any) {
547
      return res.status(500).json({
1✔
548
        status: 'error',
549
        message: error.message,
550
      });
551
    }
552
  }
553

554
  static async getNotifications(req: any, res: Response) {
555
    const { token } = req;
3✔
556

557
    try {
3✔
558
      const getDecodedToken = jwt.verify(token, secret);
3✔
559
      const userId = getDecodedToken.userId;
3✔
560
      const allNotifications = await db.Notifications.findAll({
3✔
561
        where: { userId: userId },
562
      });
563

564
      if (!allNotifications) {
2✔
565
        return res.status(404).json({
1✔
566
          status: 'fail',
567
          message: 'No notification found',
568
        });
569
      }
570

571
      return res.status(200).json({
1✔
572
        status: 'Success',
573
        data: allNotifications,
574
      });
575
    } catch (e) {
576
      res.status(500).json({
1✔
577
        status: 'fail',
578
        message: 'something went wrong: ' + e,
579
      });
580
    }
581
  }
582
  static async getSingleNotification(req: any, res: Response) {
583
    const { token } = req;
3✔
584
    const { notificationId } = req.body;
3✔
585

586
    try {
3✔
587
      const getDecodedToken = jwt.verify(token, secret);
3✔
588
      const userId = getDecodedToken.userId;
2✔
589
      const singleNotification = await db.Notifications.findOne({
2✔
590
        where: {
591
          userId: userId,
592
          notificationId: notificationId,
593
        },
594
      });
595

596
      if (singleNotification) {
2✔
597
        singleNotification.isRead = true;
1✔
598
        await singleNotification.save();
1✔
599
        return res.status(200).json({
1✔
600
          status: 'Success',
601
          data: singleNotification,
602
        });
603
      } else {
604
        return res.status(404).json({
1✔
605
          status: 'fail',
606
          message: 'No notification found',
607
        });
608
      }
609
    } catch (e) {
610
      res.status(500).json({
1✔
611
        status: 'fail',
612
        message: 'something went wrong: ' + e,
613
      });
614
    }
615
  }
616
}
617

618
dotenv.config();
10✔
619

620
export async function handlePasswordResetRequest(
10✔
621
  req: Request,
622
  res: Response,
623
): Promise<void> {
624
  try {
4✔
625
    const { email } = req.body;
4✔
626

627
    if (!email) {
4✔
628
      res.status(400).json({ error: 'Email is required' });
1✔
629
      return;
1✔
630
    }
631

632
    const user = await db.User.findOne({ where: { email: email } });
3✔
633
    if (!user) {
2✔
634
      res.status(404).json({ message: 'User not found' });
1✔
635
      return;
1✔
636
    }
637

638
    const token = generateToken(
1✔
639
      user.userId,
640
      user.email,
641
      user.firstName,
642
      user.lastName,
643
      user.role,
644
      user.passwordLastChanged,
645
      user.isVerified,
646
    );
647

648
    // Store the token in the user's record
649
    user.resetPasswordToken = token;
1✔
650
    user.resetPasswordExpires = new Date(Date.now() + 3600000); // 1 hour expiration
1✔
651

652
    await user.save();
1✔
653

654
    await nodeMail(
1✔
655
      email,
656
      'Reset password request',
657
      resetPasswordEmail(token, user.firstName),
658
    );
659

660
    res.status(200).json({ message: 'Password reset email sent successfully' });
1✔
661
  } catch (error) {
662
    res.status(500).json({ error: 'Internal server error' });
1✔
663
  }
664
}
665

666
export async function resetPassword(
10✔
667
  req: Request,
668
  res: Response,
669
): Promise<void> {
670
  try {
4✔
671
    const { newPassword } = req.body;
4✔
672
    const isValid = validatePassword(newPassword);
4✔
673
    if (!isValid) {
4✔
674
      res.status(404).json({ message: 'Password must be strong' });
4✔
675
      return;
4✔
676
    }
UNCOV
677
    const token = req.params.token;
×
678

679
    // Hash the new password
UNCOV
680
    const hashedPassword = await bcrypt.hash(newPassword, 10);
×
681

682
    // Verify the token and decode the payload
UNCOV
683
    const decodedToken = jwt.verify(token, secret) as { email: string };
×
684

685
    // Find user by decoded email from token
UNCOV
686
    const user = await db.User.findOne({
×
687
      where: {
688
        email: decodedToken.email,
689
      },
690
    });
691

UNCOV
692
    if (!user) {
×
UNCOV
693
      res.status(400).json({ error: 'Invalid token or user not found' });
×
UNCOV
694
      return;
×
695
    }
UNCOV
696
    user.password = hashedPassword;
×
UNCOV
697
    user.resetPasswordToken = undefined;
×
UNCOV
698
    user.resetPasswordExpires = undefined;
×
699

UNCOV
700
    await user.save();
×
701

UNCOV
702
    res.status(200).json({ message: 'Password reset successfully' });
×
703
  } catch (error) {
UNCOV
704
    res.status(500).json({ error: 'Internal server error' });
×
705
  }
706
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc