diff --git a/server/email.ts b/server/email.ts new file mode 100644 index 0000000..f1ed81a --- /dev/null +++ b/server/email.ts @@ -0,0 +1,122 @@ +/** + * Email service powered by Resend. + * + * Required env vars: + * RESEND_API_KEY + * STORE_EMAIL_FROM (e.g. "orders@yourstore.com") + * STORE_NAME (e.g. "My Store") + */ + +import { Resend } from "resend"; + +let _resend: Resend | null = null; + +function getResend(): Resend { + if (!_resend) { + if (!process.env.RESEND_API_KEY) { + throw new Error("RESEND_API_KEY is not configured"); + } + _resend = new Resend(process.env.RESEND_API_KEY); + } + return _resend; +} + +const FROM = process.env.STORE_EMAIL_FROM || "noreply@yourstore.com"; +const STORE_NAME = process.env.STORE_NAME || "My Store"; + +export async function sendMagicLink(email: string, token: string, baseUrl: string) { + const link = `${baseUrl}/account/verify?token=${token}`; + try { + await getResend().emails.send({ + from: FROM, + to: email, + subject: `Sign in to ${STORE_NAME}`, + html: ` +
Click the link below to sign in to ${STORE_NAME}. This link expires in 15 minutes.
+ +If you didn't request this, you can safely ignore this email.
+ `, + }); + } catch (err) { + console.error("[email] sendMagicLink failed:", err); + } +} + +export async function sendOrderConfirmation(params: { + email: string; + orderNumber: string; + total: string; + items: Array<{ title: string; variantTitle?: string; quantity: number; price: string }>; + shippingAddress?: any; +}) { + const itemsHtml = params.items + .map( + (item) => `Ship to:
${addr.firstName} ${addr.lastName}
${addr.address1}${addr.address2 ? `, ${addr.address2}` : ""}
${addr.city}, ${addr.state} ${addr.zip}
Order #${params.orderNumber} has been received and is being processed.
+| Item | +Qty | +Price | +
|---|---|---|
| Total | +$${params.total} | +|
Thank you for shopping with ${STORE_NAME}.
+Great news! Your ${STORE_NAME} order #${params.orderNumber} is on its way.
+Tracking number: ${params.trackingNumber}${params.trackingCarrier ? ` (${params.trackingCarrier})` : ""}
+Thank you for your order!
+ `, + }); + } catch (err) { + console.error("[email] sendShippingNotification failed:", err); + } +}