diff options
author | Puyodead1 <puyodead@proton.me> | 2023-01-20 10:43:06 -0500 |
---|---|---|
committer | Puyodead1 <puyodead@protonmail.com> | 2023-02-23 21:35:54 -0500 |
commit | 689b710c9e61646e62d3aea21d616402665d2f66 (patch) | |
tree | 6a63cf20ae97601bd2ee90067c47f358a0dd2ad0 /src | |
parent | Add other email templates (diff) | |
download | server-689b710c9e61646e62d3aea21d616402665d2f66.tar.xz |
Fix template rendering and use verify email template
email html is weird, some stuff isn't supported.
Diffstat (limited to 'src')
-rw-r--r-- | src/api/routes/auth/verify/resend.ts | 2 | ||||
-rw-r--r-- | src/util/entities/User.ts | 2 | ||||
-rw-r--r-- | src/util/util/Email.ts | 121 |
3 files changed, 113 insertions, 12 deletions
diff --git a/src/api/routes/auth/verify/resend.ts b/src/api/routes/auth/verify/resend.ts index 0c8c4ed9..d9a9cda5 100644 --- a/src/api/routes/auth/verify/resend.ts +++ b/src/api/routes/auth/verify/resend.ts @@ -33,7 +33,7 @@ router.post("/", route({}), async (req: Request, res: Response) => { throw new HTTPError("User does not have an email address", 400); } - await Email.sendVerificationEmail(req.user_id, user.email) + await Email.sendVerificationEmail(user, user.email) .then((info) => { console.log("Message sent: %s", info.messageId); return res.sendStatus(204); diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index 66e10297..4a399ed9 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -386,7 +386,7 @@ export class User extends BaseClass { // send verification email if users aren't verified by default and we have an email if (!Config.get().defaults.user.verified && email) { - await Email.sendVerificationEmail(user.id, email) + await Email.sendVerificationEmail(user, email) .then((info) => { console.log("Message sent: %s", info.messageId); }) diff --git a/src/util/util/Email.ts b/src/util/util/Email.ts index 371ba827..9688c3c5 100644 --- a/src/util/util/Email.ts +++ b/src/util/util/Email.ts @@ -16,10 +16,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ +import fs from "node:fs"; +import path from "node:path"; import nodemailer, { Transporter } from "nodemailer"; +import { User } from "../entities"; import { Config } from "./Config"; import { generateToken } from "./Token"; +const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "assets"); export const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; @@ -51,7 +55,20 @@ export function adjustEmail(email?: string): string | undefined { export const Email: { transporter: Transporter | null; init: () => Promise<void>; - sendVerificationEmail: (id: string, email: string) => Promise<any>; + generateVerificationLink: (id: string, email: string) => Promise<string>; + sendVerificationEmail: (user: User, email: string) => Promise<any>; + doReplacements: ( + template: string, + user: User, + emailVerificationUrl?: string, + passwordResetUrl?: string, + ipInfo?: { + ip: string; + city: string; + region: string; + country_name: string; + }, + ) => string; } = { transporter: null, init: async function () { @@ -78,25 +95,109 @@ export const Email: { console.log(`[SMTP] Ready`); }); }, - sendVerificationEmail: async function ( - id: string, - email: string, - ): Promise<any> { - if (!this.transporter) return; + /** + * Replaces all placeholders in an email template with the correct values + */ + doReplacements: function ( + template: string, + user: User, + emailVerificationUrl?: string, + passwordResetUrl?: string, + ipInfo?: { + ip: string; + city: string; + region: string; + country_name: string; + }, + ) { + const { instanceName } = Config.get().general; + template = template.replaceAll("{instanceName}", instanceName); + template = template.replaceAll("{userUsername}", user.username); + template = template.replaceAll( + "{userDiscriminator}", + user.discriminator, + ); + template = template.replaceAll("{userId}", user.id); + if (user.phone) + template = template.replaceAll( + "{phoneNumber}", + user.phone.slice(-4), + ); + if (user.email) + template = template.replaceAll("{userEmail}", user.email); + + // template specific replacements + if (emailVerificationUrl) + template = template.replaceAll( + "{emailVerificationUrl}", + emailVerificationUrl, + ); + if (passwordResetUrl) + template = template.replaceAll( + "{passwordResetUrl}", + passwordResetUrl, + ); + if (ipInfo) { + template = template.replaceAll("{ipAddress}", ipInfo.ip); + template = template.replaceAll("{locationCity}", ipInfo.city); + template = template.replaceAll("{locationRegion}", ipInfo.region); + template = template.replaceAll( + "{locationCountryName}", + ipInfo.country_name, + ); + } + + return template; + }, + /** + * + * @param id user id + * @param email user email + * @returns a verification link for the user + */ + generateVerificationLink: async function (id: string, email: string) { const token = (await generateToken(id, email)) as string; const instanceUrl = Config.get().general.frontPage || "http://localhost:3001"; const link = `${instanceUrl}/verify#token=${token}`; + return link; + }, + sendVerificationEmail: async function ( + user: User, + email: string, + ): Promise<any> { + if (!this.transporter) return; + + // generate a verification link for the user + const verificationLink = await this.generateVerificationLink( + user.id, + email, + ); + // load the email template + const rawTemplate = fs.readFileSync( + path.join( + ASSET_FOLDER_PATH, + "email_templates", + "verify_email.html", + ), + { encoding: "utf-8" }, + ); + // replace email template placeholders + const html = this.doReplacements(rawTemplate, user, verificationLink); + + // extract the title from the email template to use as the email subject + const subject = html.match(/<title>(.*)<\/title>/)?.[1] || ""; + + // // construct the email const message = { from: Config.get().general.correspondenceEmail || "noreply@localhost", to: email, - subject: `Verify Email Address for ${ - Config.get().general.instanceName - }`, - html: `Please verify your email address by clicking the following link: <a href="${link}">Verify Email</a>`, + subject, + html, }; + // // send the email return this.transporter.sendMail(message); }, }; |