-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathemail.ts
More file actions
189 lines (169 loc) · 6.08 KB
/
email.ts
File metadata and controls
189 lines (169 loc) · 6.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
"use server";
import { v4 as uuidv4 } from "uuid";
import SignUpToken, { ISignUpToken } from "../models/SignUpToken";
import nodemailer from "nodemailer";
import dbConnect from "../dbConnect";
import { requireAdmin } from "../auth/auth";
import * as crypto from "crypto";
import { adminAuth } from "../firebase/admin/firebaseAdmin";
import { getUser, getUserByEmail, updateUser } from "./User";
import { IUser } from "../models/User";
export async function sendVolunteerSignupEmail(userId: string) {
await requireAdmin();
await dbConnect();
const user = (await getUser(userId)) as IUser;
if (!user) {
throw new Error("User with that id " + userId + " does not exist");
}
const { firstName, email } = user;
let firebaseUserId;
let randomPassword;
const activationToken = uuidv4();
try {
randomPassword = crypto.randomBytes(12).toString("base64");
const firebaseUser = await adminAuth.createUser({
email,
password: randomPassword,
});
firebaseUserId = firebaseUser.uid;
} catch (err) {
console.error("Failed to create firebase user for ", email, err);
throw new Error("Failed to create firebase user");
}
try {
const transporter = nodemailer.createTransport({
host: "smtp.resend.com",
secure: true,
port: 465,
auth: {
user: "resend",
pass: process.env.RESEND_API_KEY,
},
});
const loginLink = `${process.env.BASE_URL}/activate?token=${activationToken}`;
const info = await transporter.sendMail({
from: process.env.BAGELS_EMAIL,
to: email,
subject: "Bagel Rescue Volunteer Sign Up Invite",
html: `
<div style="font-family: Arial, sans-serif; line-height: 1.6;">
<p>Hello ${firstName}, </p>
<p>Bagel Rescue has invited you to join their organization as a volunteer! Please use the link below to set up your account and password:</p>
<br/>
<p>
<strong>Account activation link: </strong>
<a href="${loginLink}" style="color: #1a73e8; text-decoration: none;">${loginLink}</a>
</p>
<p>
<strong>Email:</strong> ${email}
</p>
<p>
<strong>Password:</strong> ${randomPassword}
</p>
<br/>
<p>If you have any questions, feel free to reach out to BagelRescueTeam@gmail.com.</p>
<br/>
<p>Best,</p>
<p>Bagel Rescue Team</p>
</div>
`,
});
} catch (error) {
console.error("Error sending email:", error);
if (firebaseUserId) {
try {
await adminAuth.deleteUser(firebaseUserId);
} catch (delErr) {
console.error("Failed to delete firebase user during rollback", delErr);
}
}
throw new Error("Failed to send email");
}
try {
const mongoUser = await getUserByEmail(email);
await updateUser(mongoUser?._id!.toString() ?? "", {
status: "INVITE_SENT",
activationToken: activationToken,
firebaseUid: firebaseUserId,
});
} catch (err) {
console.error(
"Failed to update user status/activation token in Mongo",
err
);
throw new Error("Failed to update user in MongoDB");
}
return true;
}
export async function sendVolunteerSignupEmails(emails: string[]) {
await requireAdmin(); // Only admins should be able to send invite emails
try {
await dbConnect();
const expirationDate = new Date(Date.now() + 24 * 60 * 60 * 1000);
for (const email of emails) {
const token = uuidv4();
await SignUpToken.create({ email, token, expiration: expirationDate });
const transporter = nodemailer.createTransport({
host: "smtp.resend.com",
secure: true,
port: 465,
auth: {
user: "resend",
pass: process.env.RESEND_API_KEY,
},
});
const bagelsimg =
"https://static.wixstatic.com/media/c95ffe_7302ff5526b34e8c81cd07b4b6eef796~mv2.png/v1/fill/w_230,h_236,al_c,q_85,usm_0.66_1.00_0.01,enc_avif,quality_auto/Bagel%20Rescue%20no%20background.png";
const info = await transporter.sendMail({
from: process.env.BAGELS_EMAIL,
to: email,
subject: "Bagel Rescue Volunteer Sign Up Invite",
html: `
<div style="font-family: Arial, sans-serif; line-height: 1.6;">
<img src="${bagelsimg}" alt="Bagel Rescue" style="width: 150px; height: auto;"/>
<h2>Hello from Bagel Rescue! </h2>
<p>You have been invited to be a volunteer. We are excited to have you be apart of our team</p>
<p>Please contact your administrator for account setup instructions.</p>
<hr/>
<p style="line-height: 14px; font-size: 12px;">This is email was sent to <a href ="${
"mailto:" + email
}">${email}</a> by Bits Of Good.</p>
</div>
`,
});
}
return true;
} catch (error) {
console.error("Error sending emails:", error);
throw new Error("Failed to send emails");
}
}
export async function validateSignUpToken(token: string) {
try {
await dbConnect();
const signUpToken: ISignUpToken | null = await SignUpToken.findOne({
token,
});
if (!signUpToken) {
throw new Error("Invalid token");
}
const currentDate = new Date();
if (currentDate > signUpToken.expiration) {
throw new Error("Token expired");
}
return signUpToken.email;
} catch (error) {
console.error("Error validating token:", error);
throw new Error("Failed to validate token");
}
}
export async function deleteSignUpToken(token: string) {
try {
await dbConnect();
await SignUpToken.deleteOne({ token });
return true;
} catch (error) {
console.error("Error deleting token:", error);
throw new Error("Failed to delete token");
}
}