From 243eec0cce9f82fb79205bac8b1ddabfd57644bd Mon Sep 17 00:00:00 2001 From: Julien Herr Date: Wed, 20 May 2026 23:13:43 +0200 Subject: [PATCH] refactor: extract ForwardEmail adapter to src/lib/forwardemail.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the same pattern as cloudflare-email.ts — each provider has its own adapter that translates provider-specific input into ProcessEmailInput and delegates to processEmail(). inbound.ts is now a thin HTTP handler. Co-Authored-By: Claude Sonnet 4.6 --- src/lib/forwardemail.ts | 62 +++++++++++++++++++++++++++++++++++++++++ src/routes/inbound.ts | 58 ++++---------------------------------- 2 files changed, 67 insertions(+), 53 deletions(-) create mode 100644 src/lib/forwardemail.ts diff --git a/src/lib/forwardemail.ts b/src/lib/forwardemail.ts new file mode 100644 index 0000000..87e40a2 --- /dev/null +++ b/src/lib/forwardemail.ts @@ -0,0 +1,62 @@ +import { EmailParser } from "../utils/email-parser"; +import { Env } from "../types"; +import { processEmail } from "./email-processor"; + +export interface ForwardEmailPayload { + recipients?: string[]; + from?: { + value?: Array<{ address?: string; name?: string }>; + text?: string; + html?: string; + }; + subject?: string; + text?: string; + html?: string; + date?: string; + messageId?: string; + headerLines?: Array<{ key: string; line: string }>; + headers?: string; + raw?: string; + attachments?: Array; +} + +function normalizeEmail(value: string): string { + return value.trim().toLowerCase(); +} + +function extractSenderAddresses(payload: ForwardEmailPayload): string[] { + const valueEntries = payload.from?.value || []; + const structuredAddresses = valueEntries + .map((entry) => entry.address || "") + .map(normalizeEmail) + .filter(Boolean); + + if (structuredAddresses.length > 0) { + return Array.from(new Set(structuredAddresses)); + } + + const fromText = payload.from?.text || ""; + const matches = + fromText.match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi) || []; + return Array.from(new Set(matches.map(normalizeEmail))); +} + +export async function handleForwardEmail( + payload: ForwardEmailPayload, + env: Env, +): Promise { + const emailData = EmailParser.parseForwardEmailPayload(payload); + + return processEmail( + { + toAddress: payload.recipients?.[0] || "", + from: emailData.from, + senders: extractSenderAddresses(payload), + subject: emailData.subject, + content: emailData.content, + receivedAt: emailData.receivedAt, + headers: emailData.headers, + }, + env, + ); +} diff --git a/src/routes/inbound.ts b/src/routes/inbound.ts index 7f2e3ba..e62d7ea 100644 --- a/src/routes/inbound.ts +++ b/src/routes/inbound.ts @@ -1,44 +1,9 @@ import { Context } from "hono"; -import { EmailParser } from "../utils/email-parser"; import { Env } from "../types"; -import { processEmail } from "../lib/email-processor"; - -interface ForwardEmailPayload { - recipients?: string[]; - from?: { - value?: Array<{ address?: string; name?: string }>; - text?: string; - html?: string; - }; - subject?: string; - text?: string; - html?: string; - date?: string; - messageId?: string; - headerLines?: Array<{ key: string; line: string }>; - headers?: string; - raw?: string; - attachments?: Array; -} - -function extractIncomingSenderAddresses( - payload: ForwardEmailPayload, -): string[] { - const valueEntries = payload.from?.value || []; - const structuredAddresses = valueEntries - .map((entry) => entry.address || "") - .map((v) => v.trim().toLowerCase()) - .filter(Boolean); - - if (structuredAddresses.length > 0) { - return Array.from(new Set(structuredAddresses)); - } - - const fromText = payload.from?.text || ""; - const matches = - fromText.match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi) || []; - return Array.from(new Set(matches.map((v) => v.trim().toLowerCase()))); -} +import { + ForwardEmailPayload, + handleForwardEmail, +} from "../lib/forwardemail"; export async function handle(c: Context): Promise { try { @@ -52,20 +17,7 @@ export async function handle(c: Context): Promise { contentType: payload.html ? "HTML" : "Text", }); - const emailData = EmailParser.parseForwardEmailPayload(payload); - - return processEmail( - { - toAddress: payload.recipients?.[0] || "", - from: emailData.from, - senders: extractIncomingSenderAddresses(payload), - subject: emailData.subject, - content: emailData.content, - receivedAt: emailData.receivedAt, - headers: emailData.headers, - }, - env, - ); + return handleForwardEmail(payload, env); } catch (error) { console.error("Error processing email:", error); return new Response("Error processing email", { status: 500 });