eletrotupi / tcc/ api/src/controllers/auth.ts16add07e21e5fc0ec3a8a6e9eb2720080a785255
5.4 KB | 211 lines Raw
1
import { Request, Response, NextFunction } from 'express';
2
import { AuthenticatedRequest } from '@app/middleware/auth';
3

4
import {
5
  login,
6
  generateToken,
7
  requestPasswordReset,
8
  resetPassword,
9
  activateUser,
10
  storeActivationCode
11
} from '@app/services/auth.service';
12

13
import {
14
  verifyToken
15
} from '@app/lib/jwt';
16

17
import {
18
  findUserByEmail,
19
  findUserById,
20
  findUserByActivationCode,
21
} from '@app/services/user.service'
22

23
import {
24
  LoginSchema,
25
  PasswordResetSchema,
26
  PasswordResetRequestSchema,
27
  ActivateUserSchema
28
} from '@app/schemas';
29

30
import { addMinutes } from 'date-fns';
31
import { getQueue, MailJobName } from '@app/lib/queue';
32
import crypto from 'crypto';
33

34
export const AuthController = {
35
  login: async (req: Request, res: Response, next: NextFunction) => {
36
    try {
37
      const parsed = LoginSchema.safeParse(req.body);
38

39
      if (!parsed.success) {
40
        return res.status(400).json({
41
          errors: parsed.error!.issues
42
        });
43
      }
44

45
      const { user, token } = await login(parsed.data);
46

47
      if (!user.active && (new Date() > new Date(user.activationCodeExpiresAt!))) {
48
        const code = [...Array(6)].map(() => crypto.randomInt(9))
49
        .join("");
50

51
        const expiresAt = addMinutes(new Date(), 5);
52
        await storeActivationCode(user.id, code, expiresAt);
53

54
        const mailQueue = getQueue('mail');
55
        const job = await mailQueue.add(MailJobName.ActivateAccountEmail, {
56
          userId: user.id,
57
          code: code
58
        });
59
      }
60

61
      return res.json({
62
        token,
63
        user
64
      });
65
    } catch (err: any) {
66
      if (err.message === 'Invalid credentials') {
67
        return res.status(401).json({ error: 'Invalid credentials' });
68
      }
69
      next(err);
70
    }
71
  },
72

73
  forgotPassword: async (req: Request, res: Response, next: NextFunction) => {
74
    try {
75

76
      const parsed = PasswordResetRequestSchema.safeParse(req.body);
77

78
      if (!parsed.success) {
79
        return res.status(400).json({
80
          errors: parsed.error!.issues
81
        });
82
      }
83

84
      const result = await requestPasswordReset(parsed.data);
85
      return res.status(200).json(result)
86
    } catch (err) {
87
      next(err);
88
    }
89
  },
90

91
  resetPassword: async (req: Request, res: Response, next: NextFunction) => {
92
    try {
93
      const parsed = PasswordResetSchema.safeParse(req.body);
94

95
      if (!parsed.success) {
96
        return res.status(400).json({
97
          errors: parsed.error!.issues
98
        });
99
      }
100

101
      const result = await resetPassword(parsed.data);
102
      return res.status(200).json(result);
103
    } catch (err: any) {
104
      if (err.message.includes('token')) {
105
        return res.status(400).json({ error: err.message });
106
      }
107
      next(err);
108
    }
109
  },
110

111
  verify: async (req: Request, res: Response, next: NextFunction) => {
112
    try {
113
      const authHeader = req.headers.authorization;
114

115
      if (!authHeader || !authHeader.startsWith('Bearer ')) {
116
        return res.status(401).json({ error: 'No token provided' });
117
      }
118

119
      const token = authHeader.substring(7);
120
      const payload = verifyToken(token);
121
      const user = await findUserByEmail(payload.email);
122

123
      if (!user) {
124
        // XXX: Investigate which actual cases this would happen
125
        // But the possible reason is that a token is living inside
126
        // the user's phone, but somehow he was banned/deleted
127
        return res.status(404).json({
128
          errors: "User does not exist anymore"
129
        });
130
      }
131

132
      res.json({
133
        valid: true,
134
        userId: payload.userId,
135
        email: payload.email,
136
        user: {
137
          firstName: user.firstName,
138
          email: user.email,
139
          lastName: user.lastName,
140
          updatedAt: user.updatedAt,
141
          active: user.active,
142
          avatarKey: user.avatarKey,
143
          avatarURL: user.avatarURL
144
        }
145
      });
146
    } catch (err: any) {
147
      if (err.name === 'JsonWebTokenError' || err.name === 'TokenExpiredError') {
148
        return res.status(401).json({ error: 'Invalid or expired token' });
149
      }
150
      next(err);
151
    }
152
  },
153

154
  activate: async (req: Request, res: Response, next: NextFunction) => {
155
    try {
156
      const parsed = ActivateUserSchema.safeParse(req.body);
157

158
      if (!parsed.success) {
159
        return res.status(400).json({
160
          errors: parsed.error!.issues
161
        });
162
      }
163

164
      const user = await findUserByActivationCode(String(parsed.data.code));
165

166
      if (!user) {
167
        return res.status(422).json({
168
          error: "Código incorreto. Verifique o código e tente novamente"
169
        })
170
      }
171

172
      const result = await activateUser(user);
173

174
      return res.status(200).json({
175
        user: result
176
      });
177
    } catch (err: any) {
178
      next(err);
179
    }
180
  },
181

182
  resendActivationCode: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
183
    try {
184
      const user = await findUserById(req.userId!);
185

186
      if (!user) {
187
        return res.status(400).json({
188
          errors: "Usuário não encontrado"
189
        })
190
      }
191

192
      const code = [...Array(6)].map(() => crypto.randomInt(9))
193
      .join("");
194

195
      const expiresAt = addMinutes(new Date(), 5);
196
      await storeActivationCode(user.id, code, expiresAt);
197

198
      const mailQueue = getQueue('mail');
199
      const job = await mailQueue.add(MailJobName.ActivateAccountEmail, {
200
        userId: user.id,
201
        code: code
202
      });
203

204
      res.json({
205
        user
206
      })
207
    } catch (err: any) {
208
      next(err);
209
    }
210
  }
211
};