@coastweb/emdash-plugin-cloudflare-email 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 +21 -0
- package/README.md +60 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +15 -0
- package/dist/sandbox-entry.mjs +105 -0
- package/package.json +22 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 immber (Jessie Rushing) & CoastWeb, LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @coastweb/emdash-plugin-cloudflare-email
|
|
2
|
+
|
|
3
|
+
An [EmDash CMS](https://docs.emdashcms.com/introduction/) plugin that adds email sending through [Cloudflare Email Service](https://developers.cloudflare.com/email-service/) as an email provider.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
**Cloudflare Workers only.** This plugin uses Cloudflare's `cloudflare:email` runtime API, which is only available on Cloudflare Workers. This plugin will not work on Node.js deployments.
|
|
8
|
+
|
|
9
|
+
You must have [Cloudflare Email Service](https://developers.cloudflare.com/email-service/) (available in public beta) enabled on the same Cloudflare account where you are deploying your emdash worker. You will need to configure a sending domain in Cloudflare before you can use this plugin.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @coastweb/@coastweb/emdash-plugin-cloudflare-email
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### 1. Add the `SEND_EMAIL` binding in `wrangler.jsonc` on your emdashcms worker
|
|
18
|
+
|
|
19
|
+
```jsonc
|
|
20
|
+
{
|
|
21
|
+
"send_email": [
|
|
22
|
+
{
|
|
23
|
+
"name": "SEND_EMAIL"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 2. Register the plugin in `astro.config.mjs`
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import { cloudflareEmailPlugin } from "@coastweb/emdash-plugin-cloudflare-email";
|
|
33
|
+
|
|
34
|
+
export default defineConfig({
|
|
35
|
+
integrations: [
|
|
36
|
+
emdash({
|
|
37
|
+
plugins: [cloudflareEmailPlugin()],
|
|
38
|
+
// ...
|
|
39
|
+
}),
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The plugin must be in `plugins: []` (trusted mode), not `sandboxed: []`, because it requires access to the `SEND_EMAIL` Cloudflare binding.
|
|
45
|
+
|
|
46
|
+
### 3. Set the from address
|
|
47
|
+
|
|
48
|
+
After deploying, go to your EmDash admin and navigate to **Plugins → Cloudflare Email → Settings**. Enter a sender from address — this must be an address for a domain you have already configured in Cloudflare Email Service.
|
|
49
|
+
|
|
50
|
+
## How it works
|
|
51
|
+
|
|
52
|
+
This plugin registers as the `email:deliver` transport for EmDash's email pipeline. When another plugin (such as a forms plugin) calls `ctx.email.send()`, this plugin handles delivery by:
|
|
53
|
+
|
|
54
|
+
1. Reading the configured `from` address from plugin settings
|
|
55
|
+
2. Building a MIME message (plain text, or multipart `text/plain` + `text/html` if HTML is provided)
|
|
56
|
+
3. Sending via the Cloudflare `SEND_EMAIL` Workers binding
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
function cloudflareEmailPlugin() {
|
|
3
|
+
return {
|
|
4
|
+
id: "cloudflare-email",
|
|
5
|
+
version: "0.1.0",
|
|
6
|
+
format: "standard",
|
|
7
|
+
entrypoint: "emdash-plugin-cloudflare-email/sandbox",
|
|
8
|
+
options: {},
|
|
9
|
+
capabilities: ["email:provide"],
|
|
10
|
+
adminPages: [{ path: "/settings", label: "Cloudflare Email Settings", icon: "settings" }]
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
cloudflareEmailPlugin
|
|
15
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// src/sandbox-entry.ts
|
|
2
|
+
import { definePlugin } from "emdash";
|
|
3
|
+
function buildMimeMessage(from, to, subject, text, html) {
|
|
4
|
+
const boundary = `----=_Part_${Date.now()}`;
|
|
5
|
+
const lines = [
|
|
6
|
+
`From: ${from}`,
|
|
7
|
+
`To: ${to}`,
|
|
8
|
+
`Subject: ${subject}`,
|
|
9
|
+
`MIME-Version: 1.0`
|
|
10
|
+
];
|
|
11
|
+
if (html) {
|
|
12
|
+
lines.push(`Content-Type: multipart/alternative; boundary="${boundary}"`, ``);
|
|
13
|
+
lines.push(`--${boundary}`);
|
|
14
|
+
lines.push(`Content-Type: text/plain; charset=UTF-8`, ``, text, ``);
|
|
15
|
+
lines.push(`--${boundary}`);
|
|
16
|
+
lines.push(`Content-Type: text/html; charset=UTF-8`, ``, html, ``);
|
|
17
|
+
lines.push(`--${boundary}--`);
|
|
18
|
+
} else {
|
|
19
|
+
lines.push(`Content-Type: text/plain; charset=UTF-8`, ``, text);
|
|
20
|
+
}
|
|
21
|
+
return lines.join("\r\n");
|
|
22
|
+
}
|
|
23
|
+
var sandbox_entry_default = definePlugin({
|
|
24
|
+
hooks: {
|
|
25
|
+
"email:deliver": {
|
|
26
|
+
exclusive: true,
|
|
27
|
+
handler: async (event, ctx) => {
|
|
28
|
+
const from = await ctx.kv.get("settings:from");
|
|
29
|
+
if (!from) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"From address not configured. Set it in the Cloudflare Email plugin settings."
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const { env } = await import("cloudflare:workers");
|
|
35
|
+
const binding = env.SEND_EMAIL;
|
|
36
|
+
if (!binding) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
"SEND_EMAIL binding not found. Ensure send_email is configured in wrangler.jsonc."
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
const { message } = event;
|
|
42
|
+
const { EmailMessage } = await import("cloudflare:email");
|
|
43
|
+
const raw = buildMimeMessage(from, message.to, message.subject, message.text, message.html);
|
|
44
|
+
const emailMessage = new EmailMessage(from, message.to, raw);
|
|
45
|
+
await binding.send(emailMessage);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
routes: {
|
|
50
|
+
admin: {
|
|
51
|
+
handler: async (routeCtx, ctx) => {
|
|
52
|
+
const interaction = routeCtx.input;
|
|
53
|
+
if (interaction.type === "form_submit" && interaction.action_id === "save_settings") {
|
|
54
|
+
const from2 = interaction.values?.from ?? "";
|
|
55
|
+
await ctx.kv.set("settings:from", from2);
|
|
56
|
+
return {
|
|
57
|
+
blocks: [
|
|
58
|
+
{ type: "header", text: "Cloudflare Email Settings" },
|
|
59
|
+
{
|
|
60
|
+
type: "form",
|
|
61
|
+
block_id: "settings",
|
|
62
|
+
fields: [
|
|
63
|
+
{
|
|
64
|
+
type: "text_input",
|
|
65
|
+
action_id: "from",
|
|
66
|
+
label: "From address",
|
|
67
|
+
initial_value: from2
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
submit: { label: "Save", action_id: "save_settings" }
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
toast: { message: "Settings saved", type: "success" }
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const from = await ctx.kv.get("settings:from") ?? "";
|
|
77
|
+
return {
|
|
78
|
+
blocks: [
|
|
79
|
+
{ type: "header", text: "Cloudflare Email Settings" },
|
|
80
|
+
{
|
|
81
|
+
type: "context",
|
|
82
|
+
text: "Must be an address for a domain you have already configured in Cloudflare Email Service"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: "form",
|
|
86
|
+
block_id: "settings",
|
|
87
|
+
fields: [
|
|
88
|
+
{
|
|
89
|
+
type: "text_input",
|
|
90
|
+
action_id: "from",
|
|
91
|
+
label: "From address",
|
|
92
|
+
initial_value: from
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
submit: { label: "Save", action_id: "save_settings" }
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
export {
|
|
104
|
+
sandbox_entry_default as default
|
|
105
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@coastweb/emdash-plugin-cloudflare-email",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": { "import": "./dist/index.mjs", "types": "./dist/index.d.ts" },
|
|
7
|
+
"./sandbox": { "import": "./dist/sandbox-entry.mjs" }
|
|
8
|
+
},
|
|
9
|
+
"files": ["dist"],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup",
|
|
12
|
+
"dev": "tsup --watch"
|
|
13
|
+
},
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"emdash": "*"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@cloudflare/workers-types": "^4.20260426.1",
|
|
19
|
+
"tsup": "^8.5.1",
|
|
20
|
+
"typescript": "^5.9.3"
|
|
21
|
+
}
|
|
22
|
+
}
|