eletrotupi / tcc/ api/src/models/user.tsdf2ccb016884f7c1bce5bf430fe10cdbd900895c
3.0 KB | 146 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 generateToken = (userId: number, email: string) => {
28
  return createJWT(userId, email);
29
}
30

31
const findUserById = async (id: number) => {
32
  return await prisma.user.findUnique({
33
    where: { id }
34
  });
35
};
36

37
const findUserByEmail = async (email: string) => {
38
  return await prisma.user.findUnique({
39
    where: { email }
40
  });
41
};
42

43
const authenticateUser = async (email: string, password: string) => {
44
  const user = await findUserByEmail(email);
45

46
  if (!user) {
47
    throw new Error('Invalid credentials');
48
  }
49

50
  const isPasswordValid = bcrypt.compareSync(password, user.encryptedPassword);
51

52
  if (!isPasswordValid) {
53
    throw new Error('Invalid credentials');
54
  }
55

56
  return user;
57
};
58

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

62
  if (!user) {
63
    // Don't reveal whether email exists
64
    return { success: true };
65
  }
66

67
  const resetToken = generatePasswordResetToken();
68
  const resetExpires = getPasswordResetExpiration();
69

70
  await prisma.user.update({
71
    where: { id: user.id },
72
    data: {
73
      passwordResetToken: resetToken,
74
      passwordResetExpires: resetExpires
75
    }
76
  });
77

78
  // In production, send email with reset link
79
  // For now, return token for testing
80
  return {
81
    success: true,
82
    token: resetToken,
83
    email: user.email,
84
    id: user.id
85
  };
86
};
87

88
const resetPassword = async (token: string, newPassword: string) => {
89
  const user = await prisma.user.findFirst({
90
    where: {
91
      passwordResetToken: token,
92
      passwordResetExpires: { not: null }
93
    }
94
  });
95

96
  if (!user || !user.passwordResetExpires) {
97
    throw new Error('Invalid or expired reset token');
98
  }
99

100
  if (isTokenExpired(user.passwordResetExpires)) {
101
    throw new Error('Reset token has expired');
102
  }
103

104
  if (!isValidPassword(newPassword)) {
105
    throw new ShortPasswordError();
106
  }
107

108
  const encryptedPassword = bcrypt.hashSync(newPassword, 10);
109

110
  await prisma.user.update({
111
    where: { id: user.id },
112
    data: {
113
      encryptedPassword,
114
      passwordResetToken: null,
115
      passwordResetExpires: null
116
    }
117
  });
118

119
  return { success: true };
120
};
121

122
const updateUser = async (user: any, data: UserOptions) => {
123
  const { password } = data;
124

125
  if (password) {
126
    const encryptedPassword = bcrypt.hashSync(password, 10);
127

128
    delete data.password;
129
    data = { ...data, encryptedPassword }
130
  }
131

132
  return await prisma.user.update({
133
    where: { id: user.id },
134
    data
135
  });
136
}
137

138
export {
139
  findUserById,
140
  generateToken,
141
  findUserByEmail,
142
  authenticateUser,
143
  initiatePasswordReset,
144
  resetPassword,
145
  updateUser
146
}