From 282345b983c4e07020d058578f95621848eba259 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Mon, 2 Jun 2025 18:03:46 -0700 Subject: [PATCH] fix(emails): fix base URL --- apps/sim/app/api/help/route.ts | 13 ++++++------- apps/sim/app/api/workspaces/invitations/route.ts | 4 ++-- apps/sim/lib/auth.ts | 8 ++++---- apps/sim/lib/mailer.ts | 10 +++------- apps/sim/lib/urls/utils.ts | 14 ++++++++++++++ 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/apps/sim/app/api/help/route.ts b/apps/sim/app/api/help/route.ts index 34f96fce37..425a6afceb 100644 --- a/apps/sim/app/api/help/route.ts +++ b/apps/sim/app/api/help/route.ts @@ -3,12 +3,11 @@ import { Resend } from 'resend' import { z } from 'zod' import { env } from '@/lib/env' import { createLogger } from '@/lib/logs/console-logger' -import { getBaseDomain } from '@/lib/urls/utils' +import { getEmailDomain } from '@/lib/urls/utils' const resend = env.RESEND_API_KEY ? new Resend(env.RESEND_API_KEY) : null const logger = createLogger('HelpAPI') -// Define schema for validation const helpFormSchema = z.object({ email: z.string().email('Invalid email address'), subject: z.string().min(1, 'Subject is required'), @@ -99,8 +98,8 @@ ${message} // Send email using Resend const { data, error } = await resend.emails.send({ - from: `Sim Studio `, - to: [`help@${getBaseDomain()}`], + from: `Sim Studio `, + to: [`help@${getEmailDomain()}`], subject: `[${type.toUpperCase()}] ${subject}`, replyTo: email, text: emailText, @@ -122,7 +121,7 @@ ${message} // Send confirmation email to the user await resend.emails .send({ - from: `Sim Studio `, + from: `Sim Studio `, to: [email], subject: `Your ${type} request has been received: ${subject}`, text: ` @@ -137,8 +136,8 @@ ${images.length > 0 ? `You attached ${images.length} image(s).` : ''} Best regards, The Sim Studio Team - `, - replyTo: `help@${getBaseDomain()}`, + `, + replyTo: `help@${getEmailDomain()}`, }) .catch((err) => { logger.warn(`[${requestId}] Failed to send confirmation email`, err) diff --git a/apps/sim/app/api/workspaces/invitations/route.ts b/apps/sim/app/api/workspaces/invitations/route.ts index 8576341a84..1e9ab928e8 100644 --- a/apps/sim/app/api/workspaces/invitations/route.ts +++ b/apps/sim/app/api/workspaces/invitations/route.ts @@ -7,7 +7,7 @@ import { WorkspaceInvitationEmail } from '@/components/emails/workspace-invitati import { getSession } from '@/lib/auth' import { env } from '@/lib/env' import { createLogger } from '@/lib/logs/console-logger' -import { getBaseDomain } from '@/lib/urls/utils' +import { getEmailDomain } from '@/lib/urls/utils' import { db } from '@/db' import { user, workspace, workspaceInvitation, workspaceMember } from '@/db/schema' @@ -229,7 +229,7 @@ async function sendInvitationEmail({ } await resend.emails.send({ - from: `noreply@${getBaseDomain()}`, + from: `noreply@${getEmailDomain()}`, to, subject: `You've been invited to join "${workspaceName}" on Sim Studio`, html: emailHtml, diff --git a/apps/sim/lib/auth.ts b/apps/sim/lib/auth.ts index 99cbc1b317..fb744ade8b 100644 --- a/apps/sim/lib/auth.ts +++ b/apps/sim/lib/auth.ts @@ -17,7 +17,7 @@ import { createLogger } from '@/lib/logs/console-logger' import { db } from '@/db' import * as schema from '@/db/schema' import { env } from './env' -import { getBaseDomain } from './urls/utils' +import { getEmailDomain } from './urls/utils' const logger = createLogger('Auth') @@ -147,7 +147,7 @@ export const auth = betterAuth({ const html = await renderPasswordResetEmail(username, url) const result = await resend.emails.send({ - from: `Sim Studio `, + from: `Sim Studio `, to: user.email, subject: getEmailSubject('reset-password'), html, @@ -189,7 +189,7 @@ export const auth = betterAuth({ // In production, send an actual email const result = await resend.emails.send({ - from: `Sim Studio `, + from: `Sim Studio `, to: data.email, subject: getEmailSubject(data.type), html, @@ -1144,7 +1144,7 @@ export const auth = betterAuth({ ) await resend.emails.send({ - from: `Sim Studio `, + from: `Sim Studio `, to: invitation.email, subject: `${inviterName} has invited you to join ${organization.name} on Sim Studio`, html, diff --git a/apps/sim/lib/mailer.ts b/apps/sim/lib/mailer.ts index 8958e735b5..f52a305959 100644 --- a/apps/sim/lib/mailer.ts +++ b/apps/sim/lib/mailer.ts @@ -1,7 +1,7 @@ import { Resend } from 'resend' import { createLogger } from '@/lib/logs/console-logger' import { env } from './env' -import { getBaseDomain } from './urls/utils' +import { getEmailDomain } from './urls/utils' interface EmailOptions { to: string @@ -42,7 +42,7 @@ export async function sendEmail({ from, }: EmailOptions): Promise { try { - const senderEmail = from || `noreply@${getBaseDomain()}` + const senderEmail = from || `noreply@${getEmailDomain()}` if (!resend) { logger.info('Email not sent (Resend not configured):', { @@ -90,7 +90,7 @@ export async function sendBatchEmails({ emails, }: BatchEmailOptions): Promise { try { - const senderEmail = `noreply@${getBaseDomain()}` + const senderEmail = `noreply@${getEmailDomain()}` const results: SendEmailResult[] = [] if (!resend) { @@ -98,7 +98,6 @@ export async function sendBatchEmails({ emailCount: emails.length, }) - // Create mock results for each email emails.forEach(() => { results.push({ success: true, @@ -115,7 +114,6 @@ export async function sendBatchEmails({ } } - // Prepare emails for batch sending const batchEmails = emails.map((email) => ({ from: `Sim Studio <${email.from || senderEmail}>`, to: email.to, @@ -123,8 +121,6 @@ export async function sendBatchEmails({ html: email.html, })) - // Send batch emails (maximum 100 per batch as per Resend API limits) - // Process in chunks of 50 to be safe const BATCH_SIZE = 50 let allSuccessful = true diff --git a/apps/sim/lib/urls/utils.ts b/apps/sim/lib/urls/utils.ts index 92a89d7a6d..c38c32a139 100644 --- a/apps/sim/lib/urls/utils.ts +++ b/apps/sim/lib/urls/utils.ts @@ -39,3 +39,17 @@ export function getBaseDomain(): string { } } } + +/** + * Returns the domain for email addresses, stripping www subdomain for Resend compatibility + * @returns The email domain (e.g., 'simstudio.ai' instead of 'www.simstudio.ai') + */ +export function getEmailDomain(): string { + try { + const baseDomain = getBaseDomain() + return baseDomain.startsWith('www.') ? baseDomain.substring(4) : baseDomain + } catch (_e) { + const isProd = process.env.NODE_ENV === 'production' + return isProd ? 'simstudio.ai' : 'localhost:3000' + } +}