fix(email): gate resend wrappers behind ALLOW_DEV_TEST_ENDPOINTS

All five resend.js send wrappers (registration, cancellation, waitlist,
series pass, welcome) dispatched live in dev. Add a skipEmailInDev guard
mirroring the gate in pre-registrants/invite.post.js so dev runs and e2e
don't fire real Resend sends. Also add the monthly-onboarding Slack-timing
line to the welcome email. Unit-tested.
This commit is contained in:
Jennie Robinson Faber 2026-05-24 22:17:24 +01:00
parent a9312c423b
commit dac423afcd
2 changed files with 118 additions and 0 deletions

View file

@ -2,6 +2,17 @@ import { Resend } from "resend";
const resend = new Resend(useRuntimeConfig().resendApiKey);
// In dev/test runs (ALLOW_DEV_TEST_ENDPOINTS=true) skip live email dispatch so
// local flows and e2e don't fire real Resend sends. Mirrors the gate in
// server/api/admin/pre-registrants/invite.post.js.
const skipEmailInDev = (label, to) => {
if (process.env.ALLOW_DEV_TEST_ENDPOINTS === "true") {
console.log(`[resend] DEV MODE — skipping ${label}`, { to });
return true;
}
return false;
};
const formatEventDate = (dateString, timeZone = "America/Toronto") => {
const date = new Date(dateString);
return new Intl.DateTimeFormat("en-US", {
@ -58,6 +69,10 @@ Paid: $${registration.amountPaid.toFixed(2)} CAD`;
ticketSection = "\nThis event is free for Ghost Guild members.\n";
}
if (skipEmailInDev("registration email", registration.email)) {
return { success: true, skipped: true };
}
try {
const { data, error } = await resend.emails.send({
from: "Ghost Guild <events@babyghosts.org>",
@ -96,6 +111,10 @@ We look forward to seeing you there!`,
export async function sendEventCancellationEmail(registration, eventData) {
const baseUrl = process.env.BASE_URL || "https://ghostguild.org";
if (skipEmailInDev("cancellation email", registration.email)) {
return { success: true, skipped: true };
}
try {
const { data, error } = await resend.emails.send({
from: "Ghost Guild <events@babyghosts.org>",
@ -129,6 +148,10 @@ export async function sendWaitlistNotificationEmail(waitlistEntry, eventData) {
const baseUrl = process.env.BASE_URL || "https://ghostguild.org";
const eventUrl = `${baseUrl}/events/${eventData.slug || eventData._id}`;
if (skipEmailInDev("waitlist notification email", waitlistEntry.email)) {
return { success: true, skipped: true };
}
try {
const { data, error } = await resend.emails.send({
from: "Ghost Guild <events@babyghosts.org>",
@ -188,6 +211,10 @@ export async function sendSeriesPassConfirmation(options) {
})
.join("\n\n");
if (skipEmailInDev("series pass confirmation email", to)) {
return { success: true, skipped: true };
}
try {
const { data, error } = await resend.emails.send({
from: "Ghost Guild <events@babyghosts.org>",
@ -226,6 +253,10 @@ ${eventList}`,
export async function sendWelcomeEmail(member) {
const baseUrl = process.env.BASE_URL || "https://ghostguild.org";
if (skipEmailInDev("welcome email", member.email)) {
return { success: true, skipped: true };
}
try {
const { data, error } = await resend.emails.send({
from: "Ghost Guild <ghostguild@babyghosts.org>",
@ -238,6 +269,8 @@ Welcome to Ghost Guild! You're now part of the ${member.circle} circle.
Sign in to your dashboard to get started:
${baseUrl}/member/dashboard
Your Slack invitation arrives in our monthly onboarding waves there may be a short wait.
If you have questions, just reply to this email.`,
});