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

atlp-rwanda / e-commerce-bitcrafters-bn / e30611b2-12ea-49e6-80b9-28b039d2ecb3

25 Jun 2024 08:59AM UTC coverage: 92.635% (-0.08%) from 92.718%
e30611b2-12ea-49e6-80b9-28b039d2ecb3

push

circleci

web-flow
fix:redirection on login of frontend (#112)

478 of 535 branches covered (89.35%)

Branch coverage included in aggregate %.

2 of 4 new or added lines in 1 file covered. (50.0%)

1 existing line in 1 file now uncovered.

1522 of 1624 relevant lines covered (93.72%)

5.18 hits per line

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

73.38
/src/controllers/LoginController.ts
1
import { Request, Response, NextFunction } from 'express'
2
import { verificationsEmailTemplate } from '../utils/emailTemplates'
2✔
3
import User, { UserRole, UserAttributes } from '../database/models/userModel'
2✔
4
import loginSchema from '../validations/userLogin'
5
import { generateToken } from '../utils/jwt'
2✔
6
import { createUserProfile } from '../services/userServices'
2✔
7
import Token from '../database/models/tokenModel'
2✔
8
import { comparePassword,hashPassword } from '../utils/passwords'
2✔
9
import userEvents from '../utils/passwordUpdateEvent'
10
import otpEmailTemplate from '../utils/otpEmailTemplate'
2✔
11
import redisClient from '../utils/redisConfiguration'
2✔
12
import sendMail from '../utils/sendEmail'
2✔
13
import passport from '../config/passport'
2✔
14
import { getTokenByTokenValue } from '../services/tokenServices'
15
import { FRONTEND_URL} from '../config/index'
2✔
16

17
/**
18
 * Controller class for managing user-related operations.
19
 */
20
export default class LoginController {
2✔
21
  /**
22
   * Handles user login.
23
   * @param {Request} req - Express request object
24
   * @param {Response} res - Express response object
25
   * @returns {Promise<Response>} Promise that resolves to an Express response
26
   */
27
  static async login(req: Request, res: Response): Promise<Response> {
28
    try {
22✔
29
      const { email, password } = req.body
22✔
30

31
      const existingUser = await User.findOne({ where: { email } })
22✔
32

33
      if (!existingUser) {
18✔
34
        return res.status(404).send({ message: 'User not found' })
2✔
35
      }
36
      if (existingUser.status === 'inactive') {
16!
37
        return res.status(401).json({ message: 'User is disabled' })
×
38
      }
39

40
      const isPasswordValid = await comparePassword(
16✔
41
        password,
42
        existingUser.password,
43
      )
44
      if (!isPasswordValid) {
14✔
45
        return res.status(401).json({ message: 'Invalid email or password' })
4✔
46
      }
47

48
      if (existingUser.isExpired === true) {
10!
49
        return res
×
50
          .status(401)
51
          .json({ message: 'Your password has expired please update it' })
52
      }
53

54
      createUserProfile(existingUser.id)
10✔
55

56
      if (existingUser.dataValues.verified === false) {
10✔
57
        // Handle pending verification
58
        const pendingVerification = await Token.findOne({
6✔
59
          where: { userId: existingUser.id },
60
        })
61

62
        // Generate and send new verification email
63
        const newVerificationToken = generateToken({
6✔
64
          id: existingUser.id,
65
          email: existingUser.email,
66
        })
67
        const baseUrl = `${process.env.NODEMAILER_BASE_URL}users/verify/${newVerificationToken}`
6✔
68
        const html = verificationsEmailTemplate(existingUser.username, baseUrl)
6✔
69

70
        if (!pendingVerification) {
4✔
71
          await Token.create({
2✔
72
            userId: existingUser.id,
73
            token: newVerificationToken,
74
          })
75
        } else {
76
          await pendingVerification.update({ token: newVerificationToken })
2✔
77
        }
78

79
        try {
4✔
80
          await sendMail(email, 'verify Your Account', html)
4✔
81
        } catch (error) {
82
          return res.status(500).json({ message: 'Internal server error' })
×
83
        }
84

85
        return res
4✔
86
          .status(200)
87
          .send({ message: 'A verification email has been sent' })
88
      }
89

90
      const tokenPayload = {
4✔
91
        id: existingUser.id,
92
        userRole: existingUser.userRole,
93
        username: existingUser.username,
94
        email: existingUser.email,
95
        otp: '',
96
      }
97

98
      if (existingUser.userRole === UserRole.SELLER) {
4!
99
        const otp = `${Math.floor(1000 + Math.random() * 9000)}`
×
100
        const html = otpEmailTemplate(existingUser.username, otp)
×
101
        const otptoken = generateToken({
×
102
          id: existingUser.id,
103
          email: existingUser.email,
104
          username: existingUser.username,
105
          otp,
106
        })
107
        await redisClient
×
108
          .setEx(existingUser.email, 300, `${otp}=${otptoken}`)
109
          .then(async () => {
×
110
            await sendMail(email, 'OTP VERIFICATION CODE', html)
×
111
          })
112

113
        tokenPayload.otp = otp
×
114
        const sellerTokenMessage =
115
          'Email sent to your email. Please check your inbox messages and enter the OTP for verification'
×
116
        return res.status(200).json({ message: sellerTokenMessage })
×
117
      }
118

119
      // Generate and return JWT token for authenticated user
120
      const authToken = generateToken(tokenPayload)
4✔
121
      redisClient.setEx(`user:${existingUser.id}`, 86400, authToken)
4✔
122
       res.setHeader('Authorization', `Bearer ${authToken}`)
4✔
123
      return res.status(200).send({ message: 'Login successful', authToken })
4✔
124
    } catch (error) {
125
      return res
8✔
126
        .status(500)
127
        .send({ message: 'Internal server error', error: error.message })
128
    }
129
  }
130

131
  /**
132
   * Handles user logout
133
   * @param {AuthenticatedRequest} req - Express request object with user property
134
   * @param {Response} res - Express response object
135
   * @returns {Promise<Response>} Promise that resolves to an Express response
136
   */
137
  static async logOut(req: Request, res: Response): Promise<Response> {
138
    try{
4✔
139
    const userId = req.user.id
4✔
140
    const tokenKey = `user:${userId}`
4✔
141
    const tokenExists = await redisClient.exists(tokenKey)
4✔
142

143
    if (tokenExists) {
4✔
144
      await redisClient.del(tokenKey)
2✔
145
      res.removeHeader('authorization')
2✔
146
      return res.status(200).json({ message: 'Logout successfully' })
2✔
147
    }
148
    return res.status(401).json({ message: 'Already logged out' })
2✔
149
  }catch (error) {
150
      return res
×
151
        .status(500)
152
        .send({ message: 'Internal server error', error: error.message })
153
    }
154
  }
155
   /**
156
   * Login method via google
157
   * @param {Request} req - Express request object
158
   * @param {Response} res - Express response object
159
   * @param {NextFunction} next - Express next middleware function
160
   * @returns {void}
161
   */
162
  static async loginWithGoogle(
163
    req: Request,
164
    res: Response,
165
    next: NextFunction,
166
  ): Promise<void> {
167
    try {
12✔
168
      passport.authenticate(
12✔
169
        'google',
170
        async (err: unknown, user: UserAttributes | null) => {
12✔
171
          if (err) {
12✔
172
            return res.status(500).json({ error: 'Internal Server Error' })
2✔
173
          }
174
          if (!user) {
10✔
175
            return res.status(401).json({ error: 'Authentication failed' })
2✔
176
          }
177
          if (user.status === 'inactive') {
8✔
178
            return res.status(401).json({ message: 'User is disabled' })
2✔
179
          }
180
          createUserProfile(user.id)
6✔
181

182
          if (!user.verified) {
6!
183
            await handlePendingVerification(user, res)
×
184
          }
185
          if (user.userRole === UserRole.SELLER) {
6✔
186
            await handleSellerOTP(user, res)
2✔
187
            return
2✔
188
          }
189
          const token = generateUserToken(user)
4✔
190
  
191
          if(user.userRole === UserRole.ADMIN){
4!
NEW
192
            res.redirect(`${FRONTEND_URL}/admin?token=${token}`)
×
193
          }
194
          res.redirect(`${FRONTEND_URL}/login?token=${token}`)
4✔
195
        },
196
      )(req, res, next)
197
    } catch (error) {
198
      next(error)
12✔
199
    }
200
  }
201
}
202

203
const handlePendingVerification = async (
2✔
204
  user: UserAttributes,
205
  res: Response,
206
) => {
×
207
  const pendingVerification = await Token.findOne({
×
208
    where: { userId: user.id },
209
  })
210
  const newVerificationToken = generateToken({ id: user.id, email: user.email })
×
211
  const baseUrl = `${process.env.NODEMAILER_BASE_URL}users/verify/${newVerificationToken}`
×
212
  const html = verificationsEmailTemplate(user.username, baseUrl)
×
213

214
  if (!pendingVerification) {
×
215
    await Token.create({ userId: user.id, token: newVerificationToken })
×
216
  } else {
217
    await pendingVerification.update({ token: newVerificationToken })
×
218
  }
219

220
  try {
×
221
    await sendMail(user.email, 'Verify Your Account', html)
×
222
    res.status(200).send({ message: 'A verification email has been sent' })
×
223
  } catch (error) {
224
    res.status(500).json({ message: 'Internal server error' })
×
225
  }
226
}
227

228
const handleSellerOTP = async (user: UserAttributes, res: Response) => {
2✔
229
  const otp = `${Math.floor(1000 + Math.random() * 9000)}`
2✔
230
  const html = otpEmailTemplate(user.username, otp)
2✔
231
  const otpToken = generateToken({
2✔
232
    id: user.id,
233
    email: user.email,
234
    username: user.username,
235
    otp,
236
  })
237

238
  try {
2✔
239
    await redisClient.setEx(user.email, 300, `${otp}=${otpToken}`)
2✔
240
    await sendMail(user.email, 'OTP Verification Code', html)
×
NEW
241
    res.redirect(`${FRONTEND_URL}/verify-otp`)
×
UNCOV
242
    res.status(200).json({
×
243
      message:
244
        'Email sent to your email. Please check your inbox messages and enter the OTP for verification',
245
    })
246
  } catch (error) {
247
    res.status(500).json({ message: 'Internal server error' })
2✔
248
  }
249
}
250

251
const generateUserToken = (user: UserAttributes) => {
2✔
252
  const plainUser = {
4✔
253
    id: user.id,
254
    username: user.username,
255
    email: user.email,
256
    userRole: user.userRole,
257
    verified: user.verified,
258
  }
259
  return generateToken(plainUser)
4✔
260
}
261

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