Add Resend mailer as primary with SMTP fallback for reliable email delivery
This commit is contained in:
1 parent
f386ae2b25
commit
bf4075233d
4 files changed
+75
-15
No files matched your search
@@ -11,6 +11,8 @@ const schema = z.object({
|
||||
DATABASE_CONNECT_TIMEOUT_MS: z.coerce.number().int().positive().default(10_000),
|
||||
HOTEL_NAME: z.string().default("Atom"),
|
||||
APP_URL: z.string().url().default("http://localhost:3000"),
|
||||
// Resend API key (preferred — simpler HTTP API, always works).
|
||||
RESEND_API_KEY: z.string().optional(),
|
||||
// SMTP (password reset / notifications). Email features no-op if unset.
|
||||
SMTP_HOST: z.string().optional(),
|
||||
SMTP_PORT: z.coerce.number().int().positive().optional(),
|
||||
|
||||
+32
-15
@@ -1,7 +1,9 @@
|
||||
import nodemailer, { type Transporter } from "nodemailer";
|
||||
import { Resend } from "resend";
|
||||
import { env } from "@/env";
|
||||
|
||||
let transporter: Transporter | null = null;
|
||||
let resend: Resend | null = null;
|
||||
|
||||
function getTransport(): Transporter | null {
|
||||
if (!env.SMTP_HOST) return null;
|
||||
@@ -16,23 +18,38 @@ function getTransport(): Transporter | null {
|
||||
return transporter;
|
||||
}
|
||||
|
||||
/** Send an HTML email. No-ops (returns false) when SMTP isn't configured. */
|
||||
function getResend(): Resend | null {
|
||||
if (!env.RESEND_API_KEY) return null;
|
||||
if (!resend) resend = new Resend(env.RESEND_API_KEY);
|
||||
return resend;
|
||||
}
|
||||
|
||||
/** Send an HTML email. Tries Resend first, then SMTP. Returns false if both are unconfigured. */
|
||||
export async function sendMail(to: string, subject: string, html: string): Promise<boolean> {
|
||||
const from = env.SMTP_FROM ?? `no-reply@${env.HOTEL_NAME}`;
|
||||
|
||||
const r = getResend();
|
||||
if (r) {
|
||||
try {
|
||||
await r.emails.send({ from, to, subject, html });
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("[email] Resend failed:", (e as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
const t = getTransport();
|
||||
if (!t) {
|
||||
console.warn("[email] SMTP not configured — skipping mail to", to);
|
||||
return false;
|
||||
if (t) {
|
||||
try {
|
||||
await t.sendMail({ from, to, subject, html });
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("[email] SMTP failed:", (e as Error).message);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await t.sendMail({
|
||||
from: env.SMTP_FROM ?? `no-reply@${env.HOTEL_NAME}`,
|
||||
to,
|
||||
subject,
|
||||
html,
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("[email] send failed:", (e as Error).message);
|
||||
return false;
|
||||
|
||||
if (!r && !t) {
|
||||
console.warn("[email] No mailer configured — set RESEND_API_KEY or SMTP_HOST");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Reference in new issue
Block a user