eletrotupi / tcc/ api/src/models/user.ts90f9ece8c22148d981fe8468069be72a39c17c1a
3.1 KB | 144 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
const createUser = async (email: string, firstName: string, lastName: string, password: string) => {
20
  // Validate email format
21
  if (!isValidEmail(email)) {
22
    throw new InvalidEmailError()
23
  }
24

25
  // Validate password strength
26
  if (!isValidPassword(password)) {
27
    throw new ShortPasswordError()
28
  }
29
  
30
  const encryptedPassword = bcrypt.hashSync(password, 10);
31

32
  try {
33
    const user = await prisma.user.create({
34
      data: {
35
        email,
36
        firstName,
37
        lastName,
38
        encryptedPassword
39
      }
40
    });
41

42
    return user;
43
  } catch (err: any) {
44
    console.log(err)
45
    throw new Error("Something was off when creating user")
46
  }
47
}
48

49
const generateToken = (userId: number, email: string) => {
50
  return createJWT(userId, email);
51
}
52

53
const findUserByEmail = async (email: string) => {
54
  return await prisma.user.findUnique({
55
    where: { email }
56
  });
57
};
58

59
const authenticateUser = async (email: string, password: string) => {
60
  const user = await findUserByEmail(email);
61

62
  if (!user) {
63
    throw new Error('Invalid credentials');
64
  }
65

66
  const isPasswordValid = bcrypt.compareSync(password, user.encryptedPassword);
67

68
  if (!isPasswordValid) {
69
    throw new Error('Invalid credentials');
70
  }
71

72
  return user;
73
};
74

75
const initiatePasswordReset = async (email: string) => {
76
  const user = await findUserByEmail(email);
77

78
  if (!user) {
79
    // Don't reveal whether email exists
80
    return { success: true };
81
  }
82

83
  const resetToken = generatePasswordResetToken();
84
  const resetExpires = getPasswordResetExpiration();
85

86
  await prisma.user.update({
87
    where: { id: user.id },
88
    data: {
89
      passwordResetToken: resetToken,
90
      passwordResetExpires: resetExpires
91
    }
92
  });
93

94
  // In production, send email with reset link
95
  // For now, return token for testing
96
  return {
97
    success: true,
98
    token: resetToken,
99
    email: user.email
100
  };
101
};
102

103
const resetPassword = async (token: string, newPassword: string) => {
104
  const user = await prisma.user.findFirst({
105
    where: {
106
      passwordResetToken: token,
107
      passwordResetExpires: { not: null }
108
    }
109
  });
110

111
  if (!user || !user.passwordResetExpires) {
112
    throw new Error('Invalid or expired reset token');
113
  }
114

115
  if (isTokenExpired(user.passwordResetExpires)) {
116
    throw new Error('Reset token has expired');
117
  }
118

119
  if (!isValidPassword(newPassword)) {
120
    throw new ShortPasswordError();
121
  }
122

123
  const encryptedPassword = bcrypt.hashSync(newPassword, 10);
124

125
  await prisma.user.update({
126
    where: { id: user.id },
127
    data: {
128
      encryptedPassword,
129
      passwordResetToken: null,
130
      passwordResetExpires: null
131
    }
132
  });
133

134
  return { success: true };
135
};
136

137
export {
138
  createUser,
139
  generateToken,
140
  findUserByEmail,
141
  authenticateUser,
142
  initiatePasswordReset,
143
  resetPassword
144
}