api: dispatch a reset password email
Pedro Lucas Porcellis porcellis@eletrotupi.com · 5 months ago
f217e8c7216589be03ffe264c1931b4c2369d5b1 Browse filesParents
c387557Changed files
6
Additions
+52
Deletions
-7
api/src/controllers/auth.ts
1 1
import { Request, Response, NextFunction } from 'express';
2 2
import {
3 3
authenticateUser,
4 4
generateToken,
5 5
initiatePasswordReset,
6 6
resetPassword,
7 7
findUserByEmail
8 8
} from '@app/models/user';
9 +
9 10
import {
10 11
validateRequiredFields
11 12
} from '@app/utils/validators';
13 +
12 14
import { verifyToken } from '@app/lib/jwt';
15 +
import { getQueue, MailJobName } from '@app/lib/queue';
13 16
14 17
export const AuthController = {
15 18
login: async (req: Request, res: Response, next: NextFunction) => {
16 19
try {
17 20
const { email, password } = req.body;
59 62
missing: requiredValidation.missing
60 63
});
61 64
}
62 65
63 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 +
});
64 72
65 73
// In production, don't send token in response
66 74
// Instead, send email with reset link
67 75
res.json({
68 76
message: 'Password reset instructions sent to your email',
128 136
}
129 137
next(err);
130 138
}
131 139
}
132 140
};
api/src/lib/queue/processors/mail.ts
1 1
import { Job } from 'bullmq';
2 2
import {
3 3
MailJobName, MailJobData,
4 4
WelcomeEmailPayload,
5 5
PasswordResetPayload,
6 6
} from '@app/lib/queue/types';
7 7
8 8
import {
9 -
sendWelcomeEmail
9 +
sendWelcomeEmail,
10 +
sendResetPasswordEmail
10 11
} from '@app/services/mail'
11 12
12 13
export async function mailProcessor(job: Job<MailJobData['data']>): Promise<void> {
13 14
// job.name is the discriminant
14 15
switch (job.name as MailJobName) {
15 16
case MailJobName.WelcomeEmail: {
16 17
const data = job.data as WelcomeEmailPayload;
17 -
// await sendWelcomeEmail(data.userId);
18 -
19 18
console.log("Dispatching an welcome email", data);
20 19
21 20
sendWelcomeEmail(Number(data.userId));
22 21
23 22
break;
24 23
}
25 24
26 25
case MailJobName.PasswordReset: {
27 26
const data = job.data as PasswordResetPayload;
28 -
// await sendPasswordResetEmail(data.userId, data.token);
27 +
console.log("Dispatching an welcome email", data);
28 +
29 +
await sendResetPasswordEmail(data.userId, data.token);
30 +
29 31
break;
30 32
}
31 33
32 34
default:
33 35
throw new Error(`Unknown mail job: ${job.name}`);
34 36
}
35 37
}
api/src/lib/queue/types.ts
1 1
// Mail queue
2 2
export enum MailJobName {
3 3
WelcomeEmail = 'mail:welcome',
4 4
PasswordReset = 'mail:password-reset',
5 5
}
6 6
7 -
export type WelcomeEmailPayload = { userId: string };
8 -
export type PasswordResetPayload = { userId: string; token: string };
7 +
export type WelcomeEmailPayload = { userId: number };
8 +
export type PasswordResetPayload = { userId: number; token: string };
9 9
10 10
export type MailJobData =
11 11
| { name: MailJobName.WelcomeEmail; data: WelcomeEmailPayload }
12 12
| { name: MailJobName.PasswordReset; data: PasswordResetPayload };
api/src/models/user.ts
1 1
import bcrypt from "bcryptjs";
2 2
import { prisma } from '../lib/prisma'
3 3
import {
4 4
generateToken as createJWT,
5 5
generatePasswordResetToken,
107 107
// In production, send email with reset link
108 108
// For now, return token for testing
109 109
return {
110 110
success: true,
111 111
token: resetToken,
112 -
email: user.email
112 +
email: user.email,
113 +
id: user.id
113 114
};
114 115
};
115 116
116 117
const resetPassword = async (token: string, newPassword: string) => {
117 118
const user = await prisma.user.findFirst({
171 172
authenticateUser,
172 173
initiatePasswordReset,
173 174
resetPassword,
174 175
updateUser
175 176
}
api/src/services/mail/index.ts
1 1
export { sendWelcomeEmail } from '@app/services/mail/welcome';
2 +
export { sendResetPasswordEmail } from '@app/services/mail/sendPasswordReset'
/dev/null → api/src/services/mail/sendPasswordReset.ts
1 +
import { findUserById } from '@app/models/user';
2 +
import { sendEmail } from '@app/lib/mail';
3 +
4 +
const email = (firstName: string, token: string) => {
5 +
return (`
6 +
Oi, ${firstName}.
7 +
8 +
Ouvimos por aí que você precisa trocar a senha?
9 +
10 +
Segue o código para você digitar no app: ${token}
11 +
12 +
Qualquer coisa prende o grito!
13 +
`)
14 +
}
15 +
16 +
export async function sendResetPasswordEmail(userId: number, token: string): Promise<void> {
17 +
const user = await findUserById(userId)!;
18 +
19 +
console.log("Email/passwdReset: ", user);
20 +
21 +
const { data, error } = await sendEmail({
22 +
to: user!.email,
23 +
subject: "Esqueceu a senha do orbit?",
24 +
text: email(user!.firstName, token)
25 +
});
26 +
27 +
if (error) {
28 +
console.error("Error sending email: ", error);
29 +
}
30 +
31 +
console.log("Email sent successfully!");
32 +
console.log("Email ID:", data?.id);
33 +
}