mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 18:11:27 -07:00
update skills
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
# Common Patterns
|
||||
|
||||
## 1. Allowlist/Blocklist
|
||||
|
||||
```typescript
|
||||
// Allowlist
|
||||
const allowed = ["user@example.com", "trusted@corp.com"];
|
||||
if (!allowed.includes(message.from)) {
|
||||
message.setReject("Not allowed");
|
||||
return;
|
||||
}
|
||||
await message.forward("inbox@corp.com");
|
||||
```
|
||||
|
||||
## 2. Parse Email Body
|
||||
|
||||
```typescript
|
||||
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("inbox@corp.com");
|
||||
}
|
||||
} satisfies ExportedHandler;
|
||||
```
|
||||
|
||||
## 3. Spam Filter
|
||||
|
||||
```typescript
|
||||
const score = parseFloat(message.headers.get("x-cf-spamh-score") || "0");
|
||||
if (score > 5) {
|
||||
message.setReject("Spam detected");
|
||||
return;
|
||||
}
|
||||
await message.forward("inbox@corp.com");
|
||||
```
|
||||
|
||||
## 4. Archive to R2
|
||||
|
||||
```typescript
|
||||
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("inbox@corp.com");
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
|
||||
## 5. Store Metadata in KV
|
||||
|
||||
```typescript
|
||||
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("inbox@corp.com");
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
|
||||
## 6. Subject-Based Routing
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async email(message, env, ctx) {
|
||||
const subject = message.headers.get("subject")?.toLowerCase() || "";
|
||||
|
||||
if (subject.includes("[urgent]")) {
|
||||
await message.forward("oncall@corp.com");
|
||||
} else if (subject.includes("[billing]")) {
|
||||
await message.forward("billing@corp.com");
|
||||
} else if (subject.includes("[support]")) {
|
||||
await message.forward("support@corp.com");
|
||||
} else {
|
||||
await message.forward("general@corp.com");
|
||||
}
|
||||
}
|
||||
} satisfies ExportedHandler;
|
||||
```
|
||||
|
||||
## 7. Auto-Reply
|
||||
|
||||
```typescript
|
||||
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("archive@corp.com");
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.waitUntil((async () => {
|
||||
await env.EMAIL.send({
|
||||
from: "noreply@yourdomain.com",
|
||||
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("support@corp.com");
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
|
||||
## 8. Extract Attachments
|
||||
|
||||
```typescript
|
||||
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("inbox@corp.com");
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
|
||||
## 9. Log to D1
|
||||
|
||||
```typescript
|
||||
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("inbox@corp.com");
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
|
||||
## 10. Multi-Tenant
|
||||
|
||||
```typescript
|
||||
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 |
|
||||
Reference in New Issue
Block a user