diff --git a/src/app/contact/actions.ts b/src/app/contact/actions.ts index 683b67a..83908db 100644 --- a/src/app/contact/actions.ts +++ b/src/app/contact/actions.ts @@ -1,8 +1,8 @@ "use server"; -import nodemailer from "nodemailer"; import { APIEmbed } from "discord-api-types/v10"; import { sendDiscordMessageEmbed } from "@/utils/discord"; +import { createMailTransporter, sendTransactionalEmail } from "@/utils/email"; export interface ContactActionState { error: string | null; @@ -47,16 +47,6 @@ export async function sendContact(prev: ContactActionState, formData: FormData): return { error: "Email is required." }; } - const transporter = nodemailer.createTransport({ - host: process.env.SMTP_HOST!, - port: Number(process.env.SMTP_PORT!), - requireTLS: true, - auth: { - user: process.env.SMTP_USER!, - pass: process.env.SMTP_PASS!, - }, - }); - const ticketId = generateTicketId(); const noreply = process.env.EMAIL_NOREPLY!; @@ -77,10 +67,8 @@ export async function sendContact(prev: ContactActionState, formData: FormData): .filter(Boolean) .join("\n"); - await transporter.sendMail({ - from: `Hackdex <${noreply}>`, + await sendTransactionalEmail({ to: process.env.EMAIL_CONTACT!, - replyTo: email, subject, text: body, }); @@ -97,8 +85,7 @@ export async function sendContact(prev: ContactActionState, formData: FormData): .filter(Boolean) .join("\n"); - await transporter.sendMail({ - from: `Hackdex <${noreply}>`, + await sendTransactionalEmail({ to: email, subject: `[#${ticketId}] Support request confirmation`, text: confirmationMessage, diff --git a/src/utils/email.ts b/src/utils/email.ts new file mode 100644 index 0000000..7a68af1 --- /dev/null +++ b/src/utils/email.ts @@ -0,0 +1,43 @@ +import nodemailer from "nodemailer"; + +export const createMailTransporter = () => { + return nodemailer.createTransport({ + host: process.env.SMTP_HOST!, + port: Number(process.env.SMTP_PORT!), + requireTLS: true, + auth: { + user: process.env.SMTP_USER!, + pass: process.env.SMTP_PASS!, + }, + }); +}; + +// At least one of html or text must be provided +// If html is provided, text can optionally be provided as well + +interface SendTransactionalEmailBase { + to: string; + subject: string; +} + +interface SendTransactionalEmailWithHtml extends SendTransactionalEmailBase { + html: string; + text?: string; +} + +interface SendTransactionalEmailWithText extends SendTransactionalEmailBase { + text: string; +} + +export const sendTransactionalEmail = async (args: SendTransactionalEmailWithHtml | SendTransactionalEmailWithText) => { + const noreply = process.env.EMAIL_NOREPLY!; + const transporter = createMailTransporter(); + + await transporter.sendMail({ + from: `Hackdex <${noreply}>`, + to: args.to, + subject: args.subject, + html: "html" in args ? args.html : undefined, + text: "text" in args ? args.text : undefined, + }); +};