Files
pn-new-crm/src/lib/auth/account-setup-email.ts

63 lines
2.3 KiB
TypeScript
Raw Normal View History

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 = `
<p style="margin-bottom:16px;">Hi ${safeName},</p>
<p style="margin-bottom:16px;">You requested a password reset for your ${opts.appName} account.</p>
<p style="margin-bottom:16px;">
<a href="${safeUrl(opts.url)}" style="color:#2563eb;font-weight:600;">Click here to set a new password</a>
- the link expires in 1 hour.
</p>
<p style="color:#64748b;">If you didn't request this, you can safely ignore this email.</p>
`;
const html = renderShell({ title: subject, body, branding: emailBranding });
const text = `Reset your password: ${opts.url}`;
return { subject, html, text };
}