eletrotupi / tcc/ api/src/controllers/auth.ts88d7ba76dc54469a8a0bf8d1f5da7ba67d8b612a
3.8 KB | 140 lines Raw
1
import { Request, Response, NextFunction } from 'express';
2
import {
3
  authenticateUser,
4
  generateToken,
5
  initiatePasswordReset,
6
  resetPassword,
7
  findUserByEmail
8
} from '@app/models/user';
9

10
import {
11
  validateRequiredFields
12
} from '@app/utils/validators';
13

14
import { verifyToken } from '@app/lib/jwt';
15
import { getQueue, MailJobName } from '@app/lib/queue';
16

17
export const AuthController = {
18
  login: async (req: Request, res: Response, next: NextFunction) => {
19
    try {
20
      const { email, password } = req.body;
21

22
      const requiredValidation = validateRequiredFields(req.body, [
23
        'email', 'password'
24
      ]);
25

26
      if (!requiredValidation.valid) {
27
        return res.status(400).json({
28
          error: 'Missing required fields',
29
          missing: requiredValidation.missing
30
        });
31
      }
32

33
      const user = await authenticateUser(email, password);
34
      const jwtToken = generateToken(user.id, user.email);
35

36
      res.json({
37
        token: jwtToken,
38
        user: {
39
          id: user.id,
40
          email: user.email,
41
          firstName: user.firstName,
42
          lastName: user.lastName
43
        }
44
      });
45
    } catch (err: any) {
46
      if (err.message === 'Invalid credentials') {
47
        return res.status(401).json({ error: 'Invalid credentials' });
48
      }
49
      next(err);
50
    }
51
  },
52

53
  forgotPassword: async (req: Request, res: Response, next: NextFunction) => {
54
    try {
55
      const { email } = req.body;
56

57
      const requiredValidation = validateRequiredFields(req.body, ['email']);
58

59
      if (!requiredValidation.valid) {
60
        return res.status(400).json({
61
          error: 'Email is required',
62
          missing: requiredValidation.missing
63
        });
64
      }
65

66
      const result = await initiatePasswordReset(email);
67
      const mailQueue = getQueue('mail');
68
      const job = await mailQueue.add(MailJobName.PasswordReset, {
69
        userId: result.id,
70
        token: result.token
71
      });
72

73
      // In production, don't send token in response
74
      // Instead, send email with reset link
75
      res.json({
76
        message: 'Password reset instructions sent to your email',
77
        ...(!process.env.NODE_ENV || process.env.NODE_ENV === 'development' ? { token: result.token } : {})
78
      });
79
    } catch (err) {
80
      next(err);
81
    }
82
  },
83

84
  resetPassword: async (req: Request, res: Response, next: NextFunction) => {
85
    try {
86
      const { token, password } = req.body;
87

88
      const requiredValidation = validateRequiredFields(req.body, [
89
        'token', 'password'
90
      ]);
91

92
      if (!requiredValidation.valid) {
93
        return res.status(400).json({
94
          error: 'Missing required fields',
95
          missing: requiredValidation.missing
96
        });
97
      }
98

99
      await resetPassword(token, password);
100

101
      res.json({ message: 'Password successfully reset' });
102
    } catch (err: any) {
103
      if (err.message.includes('token')) {
104
        return res.status(400).json({ error: err.message });
105
      }
106
      next(err);
107
    }
108
  },
109

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

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

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

122
      res.json({
123
        valid: true,
124
        userId: payload.userId,
125
        email: payload.email,
126
        user: {
127
          firstName: user?.firstName,
128
          email: user?.email,
129
          lastName: user?.lastName,
130
          updatedAt: user?.updatedAt
131
        }
132
      });
133
    } catch (err: any) {
134
      if (err.name === 'JsonWebTokenError' || err.name === 'TokenExpiredError') {
135
        return res.status(401).json({ error: 'Invalid or expired token' });
136
      }
137
      next(err);
138
    }
139
  }
140
};