import type { BrandingShell } from '@/lib/email/shell'; import { consumePendingWelcome } from './pending-welcome'; interface AuthBranding { appName?: string | null; logoUrl?: string | null; backgroundUrl?: string | null; } /** * Builds the email body for better-auth's `sendResetPassword` callback, * choosing between two framings of the same set-password link: * * - a unique **welcome** email when the recipient was flagged by the admin * "create user" flow (a brand-new user has nothing to reset), or * - the standard **password-reset** email for genuine self-service resets. * * Pure aside from rendering — no SMTP, no DB — so the welcome-vs-reset routing * is directly unit-testable. */ export async function buildAccountPasswordEmail(opts: { email: string; name?: string | null; url: string; appName: string; authBranding: AuthBranding | null; }): Promise<{ subject: string; html: string; text: string }> { const emailBranding: BrandingShell | null = opts.authBranding ? { logoUrl: opts.authBranding.logoUrl ?? null, backgroundUrl: opts.authBranding.backgroundUrl ?? null, primaryColor: null, emailHeaderHtml: null, emailFooterHtml: null, } : null; if (consumePendingWelcome(opts.email)) { const { crmWelcomeEmail } = await import('@/lib/email/templates/crm-welcome'); return crmWelcomeEmail( { link: opts.url, recipientName: opts.name ?? undefined, appName: opts.appName }, { branding: emailBranding }, ); } const { renderShell, safeUrl } = await import('@/lib/email/shell'); const subject = `Reset your ${opts.appName} password`; const safeName = (opts.name || 'there').replace(/[<>&]/g, ''); const body = `
Hi ${safeName},
You requested a password reset for your ${opts.appName} account.
Click here to set a new password - the link expires in 1 hour.
If you didn't request this, you can safely ignore this email.
`; const html = renderShell({ title: subject, body, branding: emailBranding }); const text = `Reset your password: ${opts.url}`; return { subject, html, text }; }