@open-workspace/email 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +29 -2
- package/alchemy.ts +123 -0
- package/containers/office-preview/Dockerfile +10 -0
- package/index.ts +3 -0
- package/migrations/0000_email.sql +53 -0
- package/migrations/meta/0000_snapshot.json +408 -0
- package/migrations/meta/_journal.json +13 -0
- package/package.json +91 -4
- package/shared/attachment-preview.ts +67 -0
- package/shared/mail-authentication.ts +145 -0
- package/shared/mail-feedback.ts +90 -0
- package/shared/mail-headers.ts +250 -0
- package/shared/mail-received.ts +298 -0
- package/shared/mail-remote.ts +17 -0
- package/shared/mail-rules.ts +156 -0
- package/shared/mail-sender-intel.ts +125 -0
- package/shared/mail-translation.ts +2 -0
- package/shared/mail.ts +179 -0
- package/shared/office-preview.ts +26 -0
- package/worker/admin/report-routes.ts +164 -0
- package/worker/bindings.d.ts +11 -0
- package/worker/db/schema.ts +97 -0
- package/worker/mail/access.ts +30 -0
- package/worker/mail/ai-classification.ts +119 -0
- package/worker/mail/ai-translation.ts +31 -0
- package/worker/mail/auth-headers.ts +557 -0
- package/worker/mail/bimi.ts +172 -0
- package/worker/mail/classification-signals.ts +116 -0
- package/worker/mail/conversation-cursor.ts +43 -0
- package/worker/mail/delivery-events.ts +99 -0
- package/worker/mail/delivery-facts.ts +122 -0
- package/worker/mail/demo-routes.ts +33 -0
- package/worker/mail/doh.ts +74 -0
- package/worker/mail/downloads.ts +47 -0
- package/worker/mail/header-metadata.ts +77 -0
- package/worker/mail/impersonation.ts +175 -0
- package/worker/mail/inbound-delivery.ts +22 -0
- package/worker/mail/inbound-threading.ts +48 -0
- package/worker/mail/inbound.ts +255 -0
- package/worker/mail/ip-intel.ts +135 -0
- package/worker/mail/link-domains.ts +57 -0
- package/worker/mail/mailbox-deletion.ts +18 -0
- package/worker/mail/mailbox-directory.ts +73 -0
- package/worker/mail/mailing-list.ts +120 -0
- package/worker/mail/message-security.ts +190 -0
- package/worker/mail/message-view.ts +63 -0
- package/worker/mail/office-conversion.ts +38 -0
- package/worker/mail/office-preview-container.ts +57 -0
- package/worker/mail/office-preview-queue.ts +49 -0
- package/worker/mail/org-domain.ts +15 -0
- package/worker/mail/outbound-content.ts +208 -0
- package/worker/mail/outbound-delivery.ts +83 -0
- package/worker/mail/outbound-service.ts +145 -0
- package/worker/mail/outbound-threading.ts +70 -0
- package/worker/mail/outbound.ts +446 -0
- package/worker/mail/outgoing-context.ts +122 -0
- package/worker/mail/participants.ts +115 -0
- package/worker/mail/push-notifications.ts +484 -0
- package/worker/mail/quote-parser.ts +50 -0
- package/worker/mail/r2-presigned-urls.ts +69 -0
- package/worker/mail/receive.ts +59 -0
- package/worker/mail/recipients.ts +34 -0
- package/worker/mail/remote-proxy.ts +140 -0
- package/worker/mail/reply-plan.ts +136 -0
- package/worker/mail/reported-messages.ts +68 -0
- package/worker/mail/rfc.ts +73 -0
- package/worker/mail/routes.ts +1434 -0
- package/worker/mail/safety-routes.ts +350 -0
- package/worker/mail/schemas.ts +339 -0
- package/worker/mail/search.ts +14 -0
- package/worker/mail/security-settings.ts +38 -0
- package/worker/mail/sender-intel.ts +189 -0
- package/worker/mail/spamhaus.ts +97 -0
- package/worker/mail/unsubscribe.ts +118 -0
- package/worker/mail/uploads.ts +537 -0
- package/worker/mail/webhook-source.ts +71 -0
- package/worker/mail/workspace-safety.ts +92 -0
- package/worker/mailbox/demo-html.ts +136 -0
- package/worker/mailbox/demo-seed.ts +79 -0
- package/worker/mailbox/demo.ts +978 -0
- package/worker/mailbox/folder-model.ts +72 -0
- package/worker/mailbox/index.ts +2508 -0
- package/worker/mailbox/legacy-migrations.ts +41 -0
- package/worker/mailbox/migrations.ts +10 -0
- package/worker/mailbox/model.ts +65 -0
- package/worker/mailbox/office-preview-jobs.ts +73 -0
- package/worker/mailbox/rules.ts +110 -0
- package/worker/mailbox/schema.ts +353 -0
- package/worker/mailbox/senders.ts +240 -0
- package/worker/mcp/mail-action-tools.ts +210 -0
- package/worker/mcp/mail-compose-tools.ts +196 -0
- package/worker/mcp/mail-query-tools.ts +196 -0
- package/worker/mcp/mail-safety-tools.ts +452 -0
- package/worker/mcp/tools.ts +15 -0
- package/worker/module.ts +59 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// HTML bodies for demo messages. Written in the style of real newsletters and
|
|
2
|
+
// service mail (fixed-width tables, inline styles, bulletproof buttons) so the
|
|
3
|
+
// reader's HTML path is exercised locally; every name, link and number is made up.
|
|
4
|
+
|
|
5
|
+
export const denseDiscoveryHtml = `<!doctype html>
|
|
6
|
+
<html><head><meta charset="utf-8"><title>Dense Discovery 341</title></head>
|
|
7
|
+
<body style="margin:0;padding:0;background:#f4f1ea">
|
|
8
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f4f1ea">
|
|
9
|
+
<tr><td align="center" style="padding:32px 12px">
|
|
10
|
+
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:600px;background:#ffffff;border:1px solid #e4dfd3">
|
|
11
|
+
<tr><td style="padding:28px 40px 0;font:600 13px/1.4 Helvetica,Arial,sans-serif;letter-spacing:.12em;text-transform:uppercase;color:#8a8170">Dense Discovery · Issue 341</td></tr>
|
|
12
|
+
<tr><td style="padding:12px 40px 0;font:400 34px/1.2 Georgia,'Times New Roman',serif;color:#1d1b16">Tools for thought</td></tr>
|
|
13
|
+
<tr><td style="padding:16px 40px 0"><hr style="border:0;border-top:1px solid #e4dfd3;margin:0"></td></tr>
|
|
14
|
+
<tr><td style="padding:20px 40px 0;font:400 16px/1.65 Helvetica,Arial,sans-serif;color:#3b372e">
|
|
15
|
+
<p style="margin:0 0 16px">A small collection of thoughtful products, strange links, and ideas worth carrying into the weekend.</p>
|
|
16
|
+
<p style="margin:0 0 16px">This week: humane inboxes, local-first databases, and why fast tools feel calm.</p>
|
|
17
|
+
</td></tr>
|
|
18
|
+
<tr><td style="padding:8px 40px 0">
|
|
19
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
|
20
|
+
<tr><td style="padding:16px 0;border-top:1px solid #efe9dc;font:400 15px/1.6 Helvetica,Arial,sans-serif;color:#3b372e">
|
|
21
|
+
<div style="font:700 12px/1.4 Helvetica,Arial,sans-serif;letter-spacing:.08em;text-transform:uppercase;color:#b4532a">Read</div>
|
|
22
|
+
<a href="https://example.com/dd/humane-inbox" style="font:600 18px/1.4 Georgia,serif;color:#1d1b16;text-decoration:none">The humane inbox</a>
|
|
23
|
+
<div>Why the calmest mail clients hide almost everything until you ask for it.</div>
|
|
24
|
+
</td></tr>
|
|
25
|
+
<tr><td style="padding:16px 0;border-top:1px solid #efe9dc;font:400 15px/1.6 Helvetica,Arial,sans-serif;color:#3b372e">
|
|
26
|
+
<div style="font:700 12px/1.4 Helvetica,Arial,sans-serif;letter-spacing:.08em;text-transform:uppercase;color:#b4532a">Build</div>
|
|
27
|
+
<a href="https://example.com/dd/local-first" style="font:600 18px/1.4 Georgia,serif;color:#1d1b16;text-decoration:none">Local-first, five years in</a>
|
|
28
|
+
<div>Notes from teams that moved their data back onto the device — and what they kept on the server.</div>
|
|
29
|
+
</td></tr>
|
|
30
|
+
<tr><td style="padding:16px 0;border-top:1px solid #efe9dc;font:400 15px/1.6 Helvetica,Arial,sans-serif;color:#3b372e">
|
|
31
|
+
<div style="font:700 12px/1.4 Helvetica,Arial,sans-serif;letter-spacing:.08em;text-transform:uppercase;color:#b4532a">Think</div>
|
|
32
|
+
<a href="https://example.com/dd/fast-calm" style="font:600 18px/1.4 Georgia,serif;color:#1d1b16;text-decoration:none">Fast tools feel calm</a>
|
|
33
|
+
<div>Latency is a feeling before it is a number. A short essay on perceived speed.</div>
|
|
34
|
+
</td></tr>
|
|
35
|
+
</table>
|
|
36
|
+
</td></tr>
|
|
37
|
+
<tr><td align="center" style="padding:24px 40px 8px">
|
|
38
|
+
<table role="presentation" cellpadding="0" cellspacing="0"><tr><td style="background:#1d1b16;border-radius:4px">
|
|
39
|
+
<a href="https://example.com/dd/341" style="display:inline-block;padding:12px 24px;font:600 14px/1 Helvetica,Arial,sans-serif;color:#ffffff;text-decoration:none">Read the full issue</a>
|
|
40
|
+
</td></tr></table>
|
|
41
|
+
</td></tr>
|
|
42
|
+
<tr><td style="padding:24px 40px 32px;font:400 12px/1.6 Helvetica,Arial,sans-serif;color:#8a8170">
|
|
43
|
+
You are receiving this because you subscribed to Dense Discovery.<br>
|
|
44
|
+
<a href="https://example.com/dd/unsubscribe" style="color:#8a8170">Unsubscribe</a> · <a href="https://example.com/dd/archive" style="color:#8a8170">Past issues</a>
|
|
45
|
+
</td></tr>
|
|
46
|
+
</table>
|
|
47
|
+
</td></tr>
|
|
48
|
+
</table>
|
|
49
|
+
</body></html>`;
|
|
50
|
+
|
|
51
|
+
export const figmaInvoiceHtml = `<!doctype html>
|
|
52
|
+
<html><head><meta charset="utf-8"><title>Your July invoice</title></head>
|
|
53
|
+
<body style="margin:0;padding:0;background:#f5f5f5">
|
|
54
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f5f5f5">
|
|
55
|
+
<tr><td align="center" style="padding:28px 12px">
|
|
56
|
+
<table role="presentation" width="640" cellpadding="0" cellspacing="0" style="width:640px;background:#ffffff;border-radius:8px">
|
|
57
|
+
<tr><td style="padding:24px 36px;background:#1e1e1e;border-radius:8px 8px 0 0">
|
|
58
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr>
|
|
59
|
+
<td style="font:700 20px/1 Helvetica,Arial,sans-serif;color:#ffffff">Figma</td>
|
|
60
|
+
<td align="right" style="font:400 13px/1 Helvetica,Arial,sans-serif;color:#bdbdbd">Invoice INV-2026-0714</td>
|
|
61
|
+
</tr></table>
|
|
62
|
+
</td></tr>
|
|
63
|
+
<tr><td style="padding:32px 36px 8px;font:600 26px/1.3 Helvetica,Arial,sans-serif;color:#1e1e1e">Your July invoice is ready</td></tr>
|
|
64
|
+
<tr><td style="padding:0 36px 24px;font:400 15px/1.6 Helvetica,Arial,sans-serif;color:#4d4d4d">Hi Ilya, thanks for working with us. No action is required — the card on file was charged automatically.</td></tr>
|
|
65
|
+
<tr><td style="padding:0 36px">
|
|
66
|
+
<table role="presentation" width="568" cellpadding="0" cellspacing="0" style="width:568px;border:1px solid #e6e6e6;border-collapse:collapse;font:400 14px/1.4 Helvetica,Arial,sans-serif;color:#1e1e1e">
|
|
67
|
+
<tr style="background:#fafafa">
|
|
68
|
+
<th align="left" style="padding:12px 16px;border-bottom:1px solid #e6e6e6;font-weight:600">Item</th>
|
|
69
|
+
<th align="center" style="padding:12px 16px;border-bottom:1px solid #e6e6e6;font-weight:600">Seats</th>
|
|
70
|
+
<th align="right" style="padding:12px 16px;border-bottom:1px solid #e6e6e6;font-weight:600">Amount</th>
|
|
71
|
+
</tr>
|
|
72
|
+
<tr>
|
|
73
|
+
<td style="padding:12px 16px;border-bottom:1px solid #f0f0f0">Professional plan · Jul 1 – Jul 31</td>
|
|
74
|
+
<td align="center" style="padding:12px 16px;border-bottom:1px solid #f0f0f0">4</td>
|
|
75
|
+
<td align="right" style="padding:12px 16px;border-bottom:1px solid #f0f0f0">$60.00</td>
|
|
76
|
+
</tr>
|
|
77
|
+
<tr>
|
|
78
|
+
<td style="padding:12px 16px;border-bottom:1px solid #f0f0f0">Dev Mode add-on</td>
|
|
79
|
+
<td align="center" style="padding:12px 16px;border-bottom:1px solid #f0f0f0">2</td>
|
|
80
|
+
<td align="right" style="padding:12px 16px;border-bottom:1px solid #f0f0f0">$24.00</td>
|
|
81
|
+
</tr>
|
|
82
|
+
<tr>
|
|
83
|
+
<td colspan="2" align="right" style="padding:12px 16px;color:#4d4d4d">Tax</td>
|
|
84
|
+
<td align="right" style="padding:12px 16px">$0.00</td>
|
|
85
|
+
</tr>
|
|
86
|
+
<tr style="background:#fafafa">
|
|
87
|
+
<td colspan="2" align="right" style="padding:14px 16px;font-weight:700">Total paid</td>
|
|
88
|
+
<td align="right" style="padding:14px 16px;font-weight:700">$84.00</td>
|
|
89
|
+
</tr>
|
|
90
|
+
</table>
|
|
91
|
+
</td></tr>
|
|
92
|
+
<tr><td style="padding:28px 36px 8px">
|
|
93
|
+
<table role="presentation" cellpadding="0" cellspacing="0"><tr><td style="background:#0d99ff;border-radius:6px">
|
|
94
|
+
<a href="https://example.com/billing/INV-2026-0714" style="display:inline-block;padding:12px 22px;font:600 14px/1 Helvetica,Arial,sans-serif;color:#ffffff;text-decoration:none">View invoice</a>
|
|
95
|
+
</td></tr></table>
|
|
96
|
+
</td></tr>
|
|
97
|
+
<tr><td style="padding:20px 36px 32px;font:400 12px/1.6 Helvetica,Arial,sans-serif;color:#8c8c8c">
|
|
98
|
+
Paid with Visa ending in 4242 on July 31. Billing details are in your workspace settings.<br>
|
|
99
|
+
Questions? <a href="https://example.com/help/billing" style="color:#8c8c8c">Contact billing support</a>.
|
|
100
|
+
</td></tr>
|
|
101
|
+
</table>
|
|
102
|
+
</td></tr>
|
|
103
|
+
</table>
|
|
104
|
+
</body></html>`;
|
|
105
|
+
|
|
106
|
+
export const hirelineHtml = `<!doctype html>
|
|
107
|
+
<html lang="en"><head><meta charset="utf-8"><title>Position closed</title></head>
|
|
108
|
+
<body style="margin:0;padding:0;background:#ffffff">
|
|
109
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
|
110
|
+
<tr><td align="center" style="padding:24px 12px">
|
|
111
|
+
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="width:600px">
|
|
112
|
+
<tr><td style="padding:8px 24px 28px">
|
|
113
|
+
<table role="presentation" cellpadding="0" cellspacing="0"><tr>
|
|
114
|
+
<td width="40" height="40" align="center" style="width:40px;height:40px;border-radius:20px;background:#e8452c;font:700 15px/40px Arial,sans-serif;color:#ffffff">H</td>
|
|
115
|
+
<td style="padding-left:10px;font:700 17px/1 Arial,sans-serif;color:#1f1f1f">Hireline</td>
|
|
116
|
+
</tr></table>
|
|
117
|
+
</td></tr>
|
|
118
|
+
<tr><td style="padding:0 24px 20px;font:700 32px/1.2 Arial,sans-serif;color:#000000">This position is closed</td></tr>
|
|
119
|
+
<tr><td style="padding:0 24px 16px;font:400 16px/1.5 Arial,sans-serif;color:#1f1f1f">You applied for <b>Senior Frontend Engineer</b> at Northwind Labs. The employer has closed this position.</td></tr>
|
|
120
|
+
<tr><td style="padding:0 24px 24px;font:400 16px/1.5 Arial,sans-serif;color:#1f1f1f">We picked a few similar roles that match your profile. Good luck with your search!</td></tr>
|
|
121
|
+
<tr><td style="padding:0 24px 32px">
|
|
122
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td align="center" style="background:#0070ff;border-radius:12px">
|
|
123
|
+
<a href="https://example.com/hireline/similar" style="display:block;padding:14px 20px;font:600 16px/1.2 Arial,sans-serif;color:#ffffff;text-decoration:none">See similar roles</a>
|
|
124
|
+
</td></tr></table>
|
|
125
|
+
</td></tr>
|
|
126
|
+
<tr><td style="padding:0 24px"><hr style="border:0;border-top:1px solid #e6e6e6;margin:0"></td></tr>
|
|
127
|
+
<tr><td style="padding:24px 24px 4px;font:400 15px/1.5 Arial,sans-serif;color:#1f1f1f">Need help?</td></tr>
|
|
128
|
+
<tr><td style="padding:0 24px 20px"><a href="https://example.com/hireline/support" style="font:400 15px/1.5 Arial,sans-serif;color:#5f6b7a;text-decoration:none">Contact support</a></td></tr>
|
|
129
|
+
<tr><td style="padding:0 24px 32px;font:400 12px/1.6 Arial,sans-serif;color:#8a8a8a">
|
|
130
|
+
You received this email because you applied for a role on Hireline.<br>
|
|
131
|
+
<a href="https://example.com/hireline/settings" style="color:#8a8a8a">Notification settings</a>
|
|
132
|
+
</td></tr>
|
|
133
|
+
</table>
|
|
134
|
+
</td></tr>
|
|
135
|
+
</table>
|
|
136
|
+
</body></html>`;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createDb } from "@open-workspace/core/worker/db/client.ts";
|
|
2
|
+
import { reportedMessages } from "../db/schema";
|
|
3
|
+
import { emailSecurity } from "../mail/message-view";
|
|
4
|
+
import { reportSnapshot } from "../mail/reported-messages";
|
|
5
|
+
import { workspaceIdentity } from "../mail/security-settings";
|
|
6
|
+
import { storedSenderIntel } from "../mail/sender-intel";
|
|
7
|
+
import {
|
|
8
|
+
demoMailboxConversationState,
|
|
9
|
+
demoMailboxEmails,
|
|
10
|
+
demoMailboxObjects,
|
|
11
|
+
demoMailboxFolders,
|
|
12
|
+
demoMailboxEmailIds,
|
|
13
|
+
demoMailboxRules,
|
|
14
|
+
demoMailboxSenders,
|
|
15
|
+
demoReportedMessage,
|
|
16
|
+
} from "./demo";
|
|
17
|
+
import { mailboxStub } from "./index";
|
|
18
|
+
|
|
19
|
+
async function seedDemoMailbox(env: Env, mailboxId: string) {
|
|
20
|
+
const mailbox = mailboxStub(env, mailboxId);
|
|
21
|
+
const senders = demoMailboxSenders(mailboxId);
|
|
22
|
+
const rules = demoMailboxRules(mailboxId);
|
|
23
|
+
await mailbox.seedMailbox(
|
|
24
|
+
demoMailboxFolders(mailboxId),
|
|
25
|
+
demoMailboxEmails(mailboxId),
|
|
26
|
+
demoMailboxConversationState(mailboxId),
|
|
27
|
+
senders,
|
|
28
|
+
rules,
|
|
29
|
+
);
|
|
30
|
+
await mailbox.resetDemoSafetyState({
|
|
31
|
+
emailIds: demoMailboxEmailIds(mailboxId),
|
|
32
|
+
reportLabels: demoReportedMessage.mailboxId === mailboxId
|
|
33
|
+
? { [demoReportedMessage.emailId]: demoReportedMessage.reportLabel }
|
|
34
|
+
: {},
|
|
35
|
+
blockedPatterns: senders.flatMap((sender) =>
|
|
36
|
+
"blockedAt" in sender && sender.blockedAt ? [{ pattern: sender.address, at: sender.blockedAt.getTime() }] : []
|
|
37
|
+
),
|
|
38
|
+
ruleIds: rules.map((rule) => rule.id),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The pre-seeded phishing report in Administration; kept once created. */
|
|
43
|
+
async function seedDemoReport(env: Env, reporterUserId: string) {
|
|
44
|
+
const { mailboxId, emailId } = demoReportedMessage;
|
|
45
|
+
const email = await mailboxStub(env, mailboxId).getEmail(emailId);
|
|
46
|
+
if (!email) return;
|
|
47
|
+
const security = emailSecurity(email, await workspaceIdentity(env.DB), {});
|
|
48
|
+
await createDb(env.DB).insert(reportedMessages).values({
|
|
49
|
+
id: demoReportedMessage.id,
|
|
50
|
+
mailboxId,
|
|
51
|
+
emailId,
|
|
52
|
+
reporterUserId,
|
|
53
|
+
senderAddress: email.fromJson[0]?.address ?? "unknown@invalid",
|
|
54
|
+
senderDomain: email.fromJson[0]?.address.split("@")[1] ?? "invalid",
|
|
55
|
+
subject: email.subject,
|
|
56
|
+
receivedAt: email.timelineAt,
|
|
57
|
+
snapshotJson: reportSnapshot({ email, security, intel: storedSenderIntel(email.senderIntelJson), bodyHtml: null }),
|
|
58
|
+
status: "open",
|
|
59
|
+
createdAt: new Date(email.timelineAt.getTime() + 600_000),
|
|
60
|
+
}).onConflictDoNothing();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Seeds the demo administrator's mailboxes and the phishing report. */
|
|
64
|
+
export async function seedEmailDemo(env: Env, input: { userId: string }) {
|
|
65
|
+
const { userId } = input;
|
|
66
|
+
await Promise.all(
|
|
67
|
+
// Rows only reference the R2 keys, so objects and rows seed concurrently.
|
|
68
|
+
["mbx_demo_personal", "mbx_demo_support"].flatMap((mailboxId) => [
|
|
69
|
+
...demoMailboxObjects(mailboxId).map((object) =>
|
|
70
|
+
env.MAIL_STORAGE.put(object.key, object.body, {
|
|
71
|
+
httpMetadata: { contentType: object.contentType },
|
|
72
|
+
customMetadata: "customMetadata" in object ? object.customMetadata : undefined,
|
|
73
|
+
}),
|
|
74
|
+
),
|
|
75
|
+
seedDemoMailbox(env, mailboxId),
|
|
76
|
+
]),
|
|
77
|
+
);
|
|
78
|
+
await seedDemoReport(env, userId);
|
|
79
|
+
}
|