Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/email-workers/patterns.md
1# Email Workers Patterns23## Parse Email45```typescript6import PostalMime from 'postal-mime';78export default {9async email(message, env, ctx) {10const buffer = await new Response(message.raw).arrayBuffer();11const email = await PostalMime.parse(buffer);12console.log(email.from, email.subject, email.text, email.attachments.length);13await message.forward('[email protected]');14}15};16```1718## Filtering1920```typescript21// Allowlist from KV22const allowList = await env.ALLOWED_SENDERS.get('list', 'json') || [];23if (!allowList.includes(message.from)) {24message.setReject('Not allowed');25return;26}2728// Size check (avoid parsing large emails)29if (message.rawSize > 5_000_000) {30await message.forward('[email protected]'); // Forward without parsing31return;32}33```3435## Auto-Reply with Threading3637```typescript38import { EmailMessage } from 'cloudflare:email';39import { createMimeMessage } from 'mimetext';4041const msg = createMimeMessage();42msg.setSender({ addr: '[email protected]' });43msg.setRecipient(message.from);44msg.setSubject(`Re: ${message.headers.get('Subject')}`);45msg.setHeader('In-Reply-To', message.headers.get('Message-ID') || '');46msg.addMessage({ contentType: 'text/plain', data: 'Thank you. We will respond.' });4748await message.reply(new EmailMessage('[email protected]', message.from, msg.asRaw()));49```5051## Rate-Limited Auto-Reply5253```typescript54const rateKey = `rate:${message.from}`;55if (!await env.RATE_LIMIT.get(rateKey)) {56// Send reply...57ctx.waitUntil(env.RATE_LIMIT.put(rateKey, '1', { expirationTtl: 3600 }));58}59```6061## Subject-Based Routing6263```typescript64const subject = (message.headers.get('Subject') || '').toLowerCase();65if (subject.includes('billing')) await message.forward('[email protected]');66else if (subject.includes('support')) await message.forward('[email protected]');67else await message.forward('[email protected]');68```6970## Multi-Tenant Routing7172```typescript73// [email protected] → tenant12374const tenantId = message.to.split('@')[0].match(/\+(.+)$/)?.[1] || 'default';75const config = await env.TENANT_CONFIG.get(tenantId, 'json');76config?.forwardTo ? await message.forward(config.forwardTo) : message.setReject('Unknown');77```7879## Archive & Extract Attachments8081```typescript82// Archive to KV83ctx.waitUntil(env.ARCHIVE.put(`email:${Date.now()}`, JSON.stringify({84from: message.from, subject: email.subject85})));8687// Attachments to R288for (const att of email.attachments) {89ctx.waitUntil(env.R2.put(`${Date.now()}-${att.filename}`, att.content));90}91```9293## Webhook Integration9495```typescript96ctx.waitUntil(97fetch(env.WEBHOOK_URL, {98method: 'POST',99body: JSON.stringify({ from: message.from, subject: message.headers.get('Subject') })100}).catch(err => console.error(err))101);102```103