READ-ONLY PACKAGE PREVIEW

openai-cloudflare-deploy/references/email-routing/patterns.md

Version 49f948fa.bb1 · Apache-2.0. This preview displays packaged text and does not execute code. Treat the contents as untrusted instructions.

← Return to resource and package checksum

Common Patterns

1. Allowlist/Blocklist

// Allowlist
const allowed = ["[email protected]", "[email protected]"];
if (!allowed.includes(message.from)) {
  message.setReject("Not allowed");
  return;
}
await message.forward("[email protected]");

2. Parse Email Body

import PostalMime from 'postal-mime';

export default {
  async email(message, env, ctx) {
    // CRITICAL: Consume stream immediately
    const raw = await message.raw.arrayBuffer();

    const parser = new PostalMime();
    const email = await parser.parse(raw);

    console.log({
      subject: email.subject,
      text: email.text,
      html: email.html,
      from: email.from.address,
      attachments: email.attachments.length
    });

    await message.forward("[email protected]");
  }
} satisfies ExportedHandler;

3. Spam Filter

const score = parseFloat(message.headers.get("x-cf-spamh-score") || "0");
if (score > 5) {
  message.setReject("Spam detected");
  return;
}
await message.forward("[email protected]");

4. Archive to R2

interface Env { R2: R2Bucket; }

export default {
  async email(message, env, ctx) {
    const raw = await message.raw.arrayBuffer();

    const key = `${new Date().toISOString()}-${message.from}.eml`;
    await env.R2.put(key, raw, { 
      httpMetadata: { contentType: "message/rfc822" }
    });

    await message.forward("[email protected]");
  }
} satisfies ExportedHandler<Env>;

5. Store Metadata in KV

import PostalMime from 'postal-mime';

interface Env { KV: KVNamespace; }

export default {
  async email(message, env, ctx) {
    const raw = await message.raw.arrayBuffer();
    const parser = new PostalMime();
    const email = await parser.parse(raw);

    const metadata = {
      from: email.from.address,
      subject: email.subject,
      timestamp: new Date().toISOString(),
      size: raw.byteLength
    };

    await env.KV.put(`email:${Date.now()}`, JSON.stringify(metadata));
    await message.forward("[email protected]");
  }
} satisfies ExportedHandler<Env>;

6. Subject-Based Routing

export default {
  async email(message, env, ctx) {
    const subject = message.headers.get("subject")?.toLowerCase() || "";

    if (subject.includes("[urgent]")) {
      await message.forward("[email protected]");
    } else if (subject.includes("[billing]")) {
      await message.forward("[email protected]");
    } else if (subject.includes("[support]")) {
      await message.forward("[email protected]");
    } else {
      await message.forward("[email protected]");
    }
  }
} satisfies ExportedHandler;

7. Auto-Reply

interface Env {
  EMAIL: SendEmail;
  REPLIED: KVNamespace;
}

export default {
  async email(message, env, ctx) {
    const msgId = message.headers.get("message-id");

    if (msgId && await env.REPLIED.get(msgId)) {
      await message.forward("[email protected]");
      return;
    }

    ctx.waitUntil((async () => {
      await env.EMAIL.send({
        from: "[email protected]",
        to: message.from,
        subject: "Re: " + (message.headers.get("subject") || ""),
        text: "Thank you. We'll respond within 24h."
      });
      if (msgId) await env.REPLIED.put(msgId, "1", { expirationTtl: 604800 });
    })());

    await message.forward("[email protected]");
  }
} satisfies ExportedHandler<Env>;

8. Extract Attachments

import PostalMime from 'postal-mime';

interface Env { ATTACHMENTS: R2Bucket; }

export default {
  async email(message, env, ctx) {
    const parser = new PostalMime();
    const email = await parser.parse(await message.raw.arrayBuffer());

    for (const att of email.attachments) {
      const key = `${Date.now()}-${att.filename}`;
      await env.ATTACHMENTS.put(key, att.content, {
        httpMetadata: { contentType: att.mimeType }
      });
    }

    await message.forward("[email protected]");
  }
} satisfies ExportedHandler<Env>;

9. Log to D1

import PostalMime from 'postal-mime';

interface Env { DB: D1Database; }

export default {
  async email(message, env, ctx) {
    const parser = new PostalMime();
    const email = await parser.parse(await message.raw.arrayBuffer());

    ctx.waitUntil(
      env.DB.prepare("INSERT INTO log (ts, from_addr, subj) VALUES (?, ?, ?)")
        .bind(new Date().toISOString(), email.from.address, email.subject || "")
        .run()
    );

    await message.forward("[email protected]");
  }
} satisfies ExportedHandler<Env>;

10. Multi-Tenant

interface Env { TENANTS: KVNamespace; }

export default {
  async email(message, env, ctx) {
    const subdomain = message.to.split("@")[1].split(".")[0];
    const config = await env.TENANTS.get(subdomain, "json") as { forward: string } | null;

    if (!config) {
      message.setReject("Unknown tenant");
      return;
    }

    await message.forward(config.forward);
  }
} satisfies ExportedHandler<Env>;

Summary

Pattern Use Case Storage
Allowlist Security None
Parse Body/attachments None
Spam Filter Reduce spam None
R2 Archive Email storage R2
KV Meta Analytics KV
Subject Route Dept routing None
Auto-Reply Support KV
Attachments Doc mgmt R2
D1 Log Audit trail D1
Multi-Tenant SaaS KV