@premium-cms/plugin-cloudflare-email-byo 0.1.0 → 0.2.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/README.md +7 -6
- package/dist/index.mjs +29 -2
- package/dist/manifest.json +29 -3
- package/dist/plugin.mjs +2 -2
- package/emdash-plugin.jsonc +21 -1
- package/package.json +41 -37
package/README.md
CHANGED
|
@@ -16,9 +16,9 @@ verified domain. The platform never holds the credential.
|
|
|
16
16
|
|
|
17
17
|
1. In your own Cloudflare dashboard, onboard a domain for **Email Sending**.
|
|
18
18
|
2. Create an **API token** with email sending permission.
|
|
19
|
-
3.
|
|
20
|
-
|
|
21
|
-
4.
|
|
19
|
+
3. Under **Plugins → Email (own Cloudflare) → Settings** fill in the account ID,
|
|
20
|
+
token and sender address (the plugin manager's own settings form).
|
|
21
|
+
4. On the plugin's **Email (own Cloudflare)** page, click **Send test email**.
|
|
22
22
|
5. Under **Settings → Email**, select `cloudflare-email-byo` as the provider.
|
|
23
23
|
|
|
24
24
|
Step 5 matters: EmDash auto-selects a provider only when exactly one is active.
|
|
@@ -34,9 +34,10 @@ With both this and the built-in provider installed, the choice must be explicit.
|
|
|
34
34
|
| Sender name | Optional |
|
|
35
35
|
| Reply-To | Optional |
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
The settings form is the plugin manager's auto-generated one (declared as
|
|
38
|
+
`admin.settingsSchema` in `emdash-plugin.jsonc`); the token is a write-only
|
|
39
|
+
secret there — it is never shown again, and saving other fields leaves it alone.
|
|
40
|
+
The plugin's own page only shows the status and sends a test email.
|
|
40
41
|
|
|
41
42
|
## Trust contract
|
|
42
43
|
|
package/dist/index.mjs
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
/** @type {import("@premium-cms/emdash").PluginDescriptor} */
|
|
8
8
|
const descriptor = Object.freeze({
|
|
9
9
|
"id": "cloudflare-email-byo",
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.2.0",
|
|
11
11
|
"format": "standard",
|
|
12
12
|
"entrypoint": "@premium-cms/plugin-cloudflare-email-byo/sandbox",
|
|
13
13
|
"capabilities": [
|
|
@@ -18,9 +18,36 @@ const descriptor = Object.freeze({
|
|
|
18
18
|
"api.cloudflare.com"
|
|
19
19
|
],
|
|
20
20
|
"storage": {},
|
|
21
|
+
"settingsSchema": {
|
|
22
|
+
"accountId": {
|
|
23
|
+
"label": "Cloudflare account ID",
|
|
24
|
+
"description": "32 hex characters, from the dashboard sidebar of the account that sends the mail.",
|
|
25
|
+
"type": "string"
|
|
26
|
+
},
|
|
27
|
+
"apiToken": {
|
|
28
|
+
"label": "API token",
|
|
29
|
+
"description": "A token of that account with the Email Sending permission.",
|
|
30
|
+
"type": "secret"
|
|
31
|
+
},
|
|
32
|
+
"fromAddress": {
|
|
33
|
+
"label": "Send from",
|
|
34
|
+
"description": "An address on a domain onboarded to Email Sending in that account.",
|
|
35
|
+
"type": "email",
|
|
36
|
+
"placeholder": "cms@mail.yourdomain.com"
|
|
37
|
+
},
|
|
38
|
+
"fromName": {
|
|
39
|
+
"label": "Sender name",
|
|
40
|
+
"description": "Optional, e.g. the site name.",
|
|
41
|
+
"type": "string"
|
|
42
|
+
},
|
|
43
|
+
"replyTo": {
|
|
44
|
+
"label": "Reply-To (optional)",
|
|
45
|
+
"type": "email"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
21
48
|
"adminPages": [
|
|
22
49
|
{
|
|
23
|
-
"path": "/
|
|
50
|
+
"path": "/status",
|
|
24
51
|
"label": "Email (own Cloudflare)",
|
|
25
52
|
"icon": "mail"
|
|
26
53
|
}
|
package/dist/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "cloudflare-email-byo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"declaredAccess": {
|
|
5
5
|
"network": {
|
|
6
6
|
"request": {
|
|
@@ -31,13 +31,39 @@
|
|
|
31
31
|
"routes": [
|
|
32
32
|
"admin",
|
|
33
33
|
"settings",
|
|
34
|
-
"settings/save",
|
|
35
34
|
"test"
|
|
36
35
|
],
|
|
37
36
|
"admin": {
|
|
37
|
+
"settingsSchema": {
|
|
38
|
+
"accountId": {
|
|
39
|
+
"label": "Cloudflare account ID",
|
|
40
|
+
"description": "32 hex characters, from the dashboard sidebar of the account that sends the mail.",
|
|
41
|
+
"type": "string"
|
|
42
|
+
},
|
|
43
|
+
"apiToken": {
|
|
44
|
+
"label": "API token",
|
|
45
|
+
"description": "A token of that account with the Email Sending permission.",
|
|
46
|
+
"type": "secret"
|
|
47
|
+
},
|
|
48
|
+
"fromAddress": {
|
|
49
|
+
"label": "Send from",
|
|
50
|
+
"description": "An address on a domain onboarded to Email Sending in that account.",
|
|
51
|
+
"type": "email",
|
|
52
|
+
"placeholder": "cms@mail.yourdomain.com"
|
|
53
|
+
},
|
|
54
|
+
"fromName": {
|
|
55
|
+
"label": "Sender name",
|
|
56
|
+
"description": "Optional, e.g. the site name.",
|
|
57
|
+
"type": "string"
|
|
58
|
+
},
|
|
59
|
+
"replyTo": {
|
|
60
|
+
"label": "Reply-To (optional)",
|
|
61
|
+
"type": "email"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
38
64
|
"pages": [
|
|
39
65
|
{
|
|
40
|
-
"path": "/
|
|
66
|
+
"path": "/status",
|
|
41
67
|
"label": "Email (own Cloudflare)",
|
|
42
68
|
"icon": "mail"
|
|
43
69
|
}
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const e=`settings:`,t=/^[0-9a-f]{32}$/;function n(e
|
|
1
|
+
const e=`settings:`,t=/^[0-9a-f]{32}$/;function n(e,t=``){return typeof e==`string`?e:t}function r(e){let t=e.indexOf(`@`);return t>0&&t<e.length-1&&!/\s/.test(e)}async function i(t){let r={accountId:``,fromAddress:``,fromName:``,replyTo:``,apiToken:``};try{let r=await t.kv.list(e),i={};for(let t of r)i[t.key.replace(e,``)]=t.value;return{accountId:n(i.accountId).trim().toLowerCase(),fromAddress:n(i.fromAddress).trim(),fromName:n(i.fromName).trim(),replyTo:n(i.replyTo).trim(),apiToken:n(i.apiToken)}}catch(e){return t.log.error(`Failed to read settings`,e),r}}function a(e){let n=[];return e.accountId?t.test(e.accountId)||n.push(`a valid account ID (32 hex characters)`):n.push(`account ID`),e.apiToken||n.push(`API token`),e.fromAddress?r(e.fromAddress)||n.push(`a valid sender address`):n.push(`sender address`),n.length===0?{ok:!0}:{ok:!1,missing:n}}function o(e){return e?e.length<=8?`••••••••`:`••••••••${e.slice(-4)}`:``}function s(e,t){if(e===200&&t?.success!==!1){let e=t?.result?.permanent_bounces??[];return e.length>0?`permanently bounced: ${e.join(`, `)}`:null}let n=(t?.errors??[]).map(e=>e.code?`${e.code}: ${e.message??``}`:e.message??``).filter(Boolean).join(`; `)||`HTTP ${e}`;switch(e){case 401:case 403:return`authentication rejected (${n}) — check the API token has email sending permission`;case 400:return`request rejected (${n}) — check the sender domain is onboarded for Email Sending`;case 429:return`rate limited (${n}) — retryable`;default:return e>=500?`Cloudflare error (${n}) — retryable`:n}}async function c(e,t,n){if(!e.http)throw Error("[cloudflare-email-byo] ctx.http is unavailable — the plugin needs the `network:request` capability.");let r={to:n.to,from:t.fromName?{address:t.fromAddress,name:t.fromName}:{address:t.fromAddress},subject:n.subject,text:n.text};n.html&&(r.html=n.html),t.replyTo&&(r.reply_to=t.replyTo);let i=await e.http.fetch(`https://api.cloudflare.com/client/v4/accounts/${t.accountId}/email/sending/send`,{method:`POST`,headers:{Authorization:`Bearer ${t.apiToken}`,"Content-Type":`application/json`},body:JSON.stringify(r)}),a=null;try{a=await i.json()}catch{}let o=s(i.status,a);if(o)throw Error(`[cloudflare-email-byo] send failed for ${n.to} — ${o}`);e.log.info(`email delivered via the site's own Cloudflare account`,{to:n.to,subject:n.subject,accountId:t.accountId})}const l={hooks:{"plugin:install":async(e,t)=>{t.log.info(`Cloudflare Email (own account) installed — add credentials on its settings page, then select it under Settings → Email`)},"email:deliver":{exclusive:!0,handler:async(e,t)=>{let n=await i(t),r=a(n);if(!r.ok)throw Error(`[cloudflare-email-byo] not configured — missing ${r.missing.join(`, `)}. Add them on the plugin's settings page.`);await c(t,n,e.message)}}},routes:{admin:{handler:async(e,t)=>{let n=e.input;return n.type===`page_load`&&n.page===`/status`?d(t):n.type===`block_action`&&n.action_id===`send_test`?d(t,await u(t)):{blocks:[]}}},settings:{handler:async(e,t)=>{let n=await i(t),r=a(n);return{accountId:n.accountId,fromAddress:n.fromAddress,fromName:n.fromName,replyTo:n.replyTo,apiToken:o(n.apiToken??``),configured:r.ok,missing:r.ok?[]:r.missing}}},test:{handler:async(e,t)=>({message:await u(t)})}}};async function u(e){let t=await i(e),n=a(t);if(!n.ok)return`Cannot send a test — missing ${n.missing.join(`, `)}.`;try{return await c(e,t,{to:t.fromAddress,subject:`Test email from your site`,text:`This is a test from the Cloudflare Email (own account) provider.
|
|
2
2
|
|
|
3
|
-
If you received it, transactional email is sending through your own Cloudflare account.`}),`Test sent to ${t.fromAddress}.`}catch(e){return String(e instanceof Error?e.message:e)}}async function
|
|
3
|
+
If you received it, transactional email is sending through your own Cloudflare account.`}),`Test sent to ${t.fromAddress}.`}catch(e){return String(e instanceof Error?e.message:e)}}async function d(e,t){try{let n=await i(e),r=a(n),o=[{type:`header`,text:`Email from your own Cloudflare account`},{type:`context`,text:`Transactional email (sign-in links, invites, notifications) is sent through your Cloudflare account instead of the platform's.`},{type:`divider`},{type:`section`,text:r.ok?`Ready — sending as ${n.fromAddress} from account ${n.accountId}.`:`Not sending yet — missing ${r.missing.join(`, `)}.`}];return t&&o.push({type:`context`,text:t}),o.push({type:`context`,text:`Account, token and sender are set under Plugins → Email (own Cloudflare) → Settings.`},{type:`divider`},{type:`actions`,elements:[{type:`button`,text:`Send test email`,action_id:`send_test`,style:`primary`}]},{type:`divider`},{type:`context`,text:`The sending domain must be onboarded for Email Sending in your own Cloudflare account, and the token needs email sending permission. Once saved, choose this provider under Settings → Email.`}),{blocks:o}}catch(t){return e.log.error(`Failed to build settings page`,t),{blocks:[{type:`context`,text:`Failed to load email settings.`}]}}}export{l as default};
|
|
4
4
|
//# sourceMappingURL=plugin.mjs.map
|
package/emdash-plugin.jsonc
CHANGED
|
@@ -28,6 +28,26 @@
|
|
|
28
28
|
"storage": {},
|
|
29
29
|
|
|
30
30
|
"admin": {
|
|
31
|
-
"
|
|
31
|
+
"settingsSchema": {
|
|
32
|
+
"accountId": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"label": "Cloudflare account ID",
|
|
35
|
+
"description": "32 hex characters, from the dashboard sidebar of the account that sends the mail."
|
|
36
|
+
},
|
|
37
|
+
"apiToken": {
|
|
38
|
+
"type": "secret",
|
|
39
|
+
"label": "API token",
|
|
40
|
+
"description": "A token of that account with the Email Sending permission."
|
|
41
|
+
},
|
|
42
|
+
"fromAddress": {
|
|
43
|
+
"type": "email",
|
|
44
|
+
"label": "Send from",
|
|
45
|
+
"placeholder": "cms@mail.yourdomain.com",
|
|
46
|
+
"description": "An address on a domain onboarded to Email Sending in that account."
|
|
47
|
+
},
|
|
48
|
+
"fromName": { "type": "string", "label": "Sender name", "description": "Optional, e.g. the site name." },
|
|
49
|
+
"replyTo": { "type": "email", "label": "Reply-To (optional)" }
|
|
50
|
+
},
|
|
51
|
+
"pages": [{ "path": "/status", "label": "Email (own Cloudflare)", "icon": "mail" }],
|
|
32
52
|
},
|
|
33
53
|
}
|
package/package.json
CHANGED
|
@@ -1,39 +1,43 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
2
|
+
"name": "@premium-cms/plugin-cloudflare-email-byo",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Send email through the site owner's own Cloudflare account",
|
|
6
|
+
"main": "dist/index.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.mjs",
|
|
10
|
+
"types": "./dist/index.d.mts"
|
|
11
|
+
},
|
|
12
|
+
"./sandbox": "./dist/plugin.mjs"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"emdash-plugin.jsonc"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "emdash-plugin build",
|
|
20
|
+
"dev": "emdash-plugin dev",
|
|
21
|
+
"bundle": "emdash-plugin bundle",
|
|
22
|
+
"validate": "emdash-plugin validate .",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"test": "vitest run"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@premium-cms/emdash": ">=0.35.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@premium-cms/plugin-cli": "^0.8.5",
|
|
31
|
+
"@premium-cms/plugin-types": "^0.3.0",
|
|
32
|
+
"@premium-cms/emdash": "^0.35.0",
|
|
33
|
+
"typescript": "^6.0.3",
|
|
34
|
+
"vitest": "^4.1.5"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/MahmoodKhalil57/premium-cms-plugin-cloudflare-email-byo"
|
|
42
|
+
}
|
|
39
43
|
}
|