eletrotupi / tcc/ api/src/models/user.tsf217e8c7216589be03ffe264c1931b4c2369d5b1
3.6 KB | 176 lines Raw
1
import bcrypt from "bcryptjs";
2
import { prisma } from '../lib/prisma'
3
import {
4
  generateToken as createJWT,
5
  generatePasswordResetToken,
6
  getPasswordResetExpiration,
7
  isTokenExpired
8
} from '@app/lib/jwt';
9

10
import {
11
  isValidEmail,
12
  isValidPassword,
13
  sanitizeString,
14
  validateRequiredFields
15
} from "@app/utils/validators";
16

17
import { InvalidEmailError, ShortPasswordError } from '@app/lib/errors/UserErrors'
18

19
type UserOptions = {
20
  email: string;
21
  firstName: string;
22
  lastName: string;
23
  password?: string;
24
  encryptedPassword?: string;
25
};
26

27
const createUser = async (email: string, firstName: string, lastName: string, password: string) => {
28
  // Validate email format
29
  if (!isValidEmail(email)) {
30
    throw new InvalidEmailError()
31
  }
32

33
  // Validate password strength
34
  if (!isValidPassword(password)) {
35
    throw new ShortPasswordError()
36
  }
37

38
  const encryptedPassword = bcrypt.hashSync(password, 10);
39

40
  try {
41
    const user = await prisma.user.create({
42
      data: {
43
        email,
44
        firstName,
45
        lastName,
46
        encryptedPassword
47
      }
48
    });
49

50
    return user;
51
  } catch (err: any) {
52
    throw err;
53
  }
54
}
55

56
const generateToken = (userId: number, email: string) => {
57
  return createJWT(userId, email);
58
}
59

60
const findUserById = async (id: number) => {
61
  return await prisma.user.findUnique({
62
    where: { id }
63
  });
64
};
65

66
const findUserByEmail = async (email: string) => {
67
  return await prisma.user.findUnique({
68
    where: { email }
69
  });
70
};
71

72
const authenticateUser = async (email: string, password: string) => {
73
  const user = await findUserByEmail(email);
74

75
  if (!user) {
76
    throw new Error('Invalid credentials');
77
  }
78

79
  const isPasswordValid = bcrypt.compareSync(password, user.encryptedPassword);
80

81
  if (!isPasswordValid) {
82
    throw new Error('Invalid credentials');
83
  }
84

85
  return user;
86
};
87

88
const initiatePasswordReset = async (email: string) => {
89
  const user = await findUserByEmail(email);
90

91
  if (!user) {
92
    // Don't reveal whether email exists
93
    return { success: true };
94
  }
95

96
  const resetToken = generatePasswordResetToken();
97
  const resetExpires = getPasswordResetExpiration();
98

99
  await prisma.user.update({
100
    where: { id: user.id },
101
    data: {
102
      passwordResetToken: resetToken,
103
      passwordResetExpires: resetExpires
104
    }
105
  });
106

107
  // In production, send email with reset link
108
  // For now, return token for testing
109
  return {
110
    success: true,
111
    token: resetToken,
112
    email: user.email,
113
    id: user.id
114
  };
115
};
116

117
const resetPassword = async (token: string, newPassword: string) => {
118
  const user = await prisma.user.findFirst({
119
    where: {
120
      passwordResetToken: token,
121
      passwordResetExpires: { not: null }
122
    }
123
  });
124

125
  if (!user || !user.passwordResetExpires) {
126
    throw new Error('Invalid or expired reset token');
127
  }
128

129
  if (isTokenExpired(user.passwordResetExpires)) {
130
    throw new Error('Reset token has expired');
131
  }
132

133
  if (!isValidPassword(newPassword)) {
134
    throw new ShortPasswordError();
135
  }
136

137
  const encryptedPassword = bcrypt.hashSync(newPassword, 10);
138

139
  await prisma.user.update({
140
    where: { id: user.id },
141
    data: {
142
      encryptedPassword,
143
      passwordResetToken: null,
144
      passwordResetExpires: null
145
    }
146
  });
147

148
  return { success: true };
149
};
150

151
const updateUser = async (user: any, data: UserOptions) => {
152
  const { password } = data;
153

154
  if (password) {
155
    const encryptedPassword = bcrypt.hashSync(password, 10);
156

157
    delete data.password;
158
    data = { ...data, encryptedPassword }
159
  }
160

161
  return await prisma.user.update({
162
    where: { id: user.id },
163
    data
164
  });
165
}
166

167
export {
168
  createUser,
169
  findUserById,
170
  generateToken,
171
  findUserByEmail,
172
  authenticateUser,
173
  initiatePasswordReset,
174
  resetPassword,
175
  updateUser
176
}