emdash-aws-ses 1.0.0 → 1.0.2
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/.gitattributes +2 -0
- package/package.json +1 -4
- package/{dist/index.js → src/index.ts} +6 -7
- package/{dist/sandbox-entry.js → src/sandbox-entry.ts} +128 -79
- package/tsconfig.json +14 -0
- package/dist/index.cjs +0 -43
- package/dist/sandbox-entry.cjs +0 -391
package/.gitattributes
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emdash-aws-ses",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Amazon SES SMTP email transport plugin for EmDash CMS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -14,9 +14,6 @@
|
|
|
14
14
|
"main": "./dist/index.cjs",
|
|
15
15
|
"module": "./dist/index.js",
|
|
16
16
|
"types": "./dist/index.d.ts",
|
|
17
|
-
"files": [
|
|
18
|
-
"dist"
|
|
19
|
-
],
|
|
20
17
|
"exports": {
|
|
21
18
|
".": {
|
|
22
19
|
"types": "./dist/index.d.ts",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { PluginDescriptor } from "emdash";
|
|
2
|
+
|
|
3
|
+
export function awsSesPlugin(): PluginDescriptor {
|
|
3
4
|
return {
|
|
4
5
|
id: "emdash-aws-ses",
|
|
5
6
|
version: "1.0.0",
|
|
@@ -7,12 +8,10 @@ function awsSesPlugin() {
|
|
|
7
8
|
entrypoint: "emdash-aws-ses/sandbox",
|
|
8
9
|
options: {},
|
|
9
10
|
capabilities: ["hooks.email-transport:register"],
|
|
11
|
+
allowedHosts: [],
|
|
10
12
|
adminPages: [
|
|
11
13
|
{ path: "/settings", label: "AWS SES Settings", icon: "mail" },
|
|
12
|
-
{ path: "/test", label: "Test & Status", icon: "send" }
|
|
13
|
-
]
|
|
14
|
+
{ path: "/test", label: "AWS SES Test & Status", icon: "send" },
|
|
15
|
+
],
|
|
14
16
|
};
|
|
15
17
|
}
|
|
16
|
-
export {
|
|
17
|
-
awsSesPlugin
|
|
18
|
-
};
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
import { definePlugin } from "emdash";
|
|
1
|
+
import type { SandboxedPlugin, SandboxedRouteContext, PluginContext, EmailDeliverEvent, LifecycleEvent } from "emdash/plugin";
|
|
3
2
|
import nodemailer from "nodemailer";
|
|
4
|
-
|
|
3
|
+
import type { Transporter } from "nodemailer";
|
|
4
|
+
import type { BlockInteraction } from "@emdash-cms/blocks";
|
|
5
|
+
|
|
6
|
+
interface SesSettings {
|
|
7
|
+
smtpHost: string;
|
|
8
|
+
smtpPort: number;
|
|
9
|
+
smtpUser: string;
|
|
10
|
+
smtpPassword: string;
|
|
11
|
+
region: string;
|
|
12
|
+
defaultFrom: string;
|
|
13
|
+
fromName: string;
|
|
14
|
+
replyTo: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function getSettings(ctx: PluginContext): Promise<SesSettings> {
|
|
5
18
|
const defaults = {
|
|
6
19
|
smtpHost: "email-smtp.us-east-1.amazonaws.com",
|
|
7
20
|
smtpPort: 587,
|
|
@@ -10,18 +23,20 @@ async function getSettings(ctx) {
|
|
|
10
23
|
fromName: "",
|
|
11
24
|
replyTo: "",
|
|
12
25
|
smtpUser: "",
|
|
13
|
-
smtpPassword: ""
|
|
26
|
+
smtpPassword: "",
|
|
14
27
|
};
|
|
28
|
+
|
|
15
29
|
const [smtpHost, smtpPortRaw, smtpUser, smtpPassword, region, defaultFrom, fromName, replyTo] = await Promise.all([
|
|
16
|
-
ctx.kv.get("settings:smtpHost"),
|
|
17
|
-
ctx.kv.get("settings:smtpPort"),
|
|
18
|
-
ctx.kv.get("settings:smtpUser"),
|
|
19
|
-
ctx.kv.get("settings:smtpPassword"),
|
|
20
|
-
ctx.kv.get("settings:region"),
|
|
21
|
-
ctx.kv.get("settings:defaultFrom"),
|
|
22
|
-
ctx.kv.get("settings:fromName"),
|
|
23
|
-
ctx.kv.get("settings:replyTo")
|
|
30
|
+
ctx.kv.get<string>("settings:smtpHost"),
|
|
31
|
+
ctx.kv.get<number>("settings:smtpPort"),
|
|
32
|
+
ctx.kv.get<string>("settings:smtpUser"),
|
|
33
|
+
ctx.kv.get<string>("settings:smtpPassword"),
|
|
34
|
+
ctx.kv.get<string>("settings:region"),
|
|
35
|
+
ctx.kv.get<string>("settings:defaultFrom"),
|
|
36
|
+
ctx.kv.get<string>("settings:fromName"),
|
|
37
|
+
ctx.kv.get<string>("settings:replyTo"),
|
|
24
38
|
]);
|
|
39
|
+
|
|
25
40
|
return {
|
|
26
41
|
smtpHost: smtpHost ?? defaults.smtpHost,
|
|
27
42
|
smtpPort: Number(smtpPortRaw ?? defaults.smtpPort),
|
|
@@ -30,30 +45,33 @@ async function getSettings(ctx) {
|
|
|
30
45
|
region: region ?? defaults.region,
|
|
31
46
|
defaultFrom: defaultFrom ?? defaults.defaultFrom,
|
|
32
47
|
fromName: fromName ?? defaults.fromName,
|
|
33
|
-
replyTo: replyTo ?? defaults.replyTo
|
|
48
|
+
replyTo: replyTo ?? defaults.replyTo,
|
|
34
49
|
};
|
|
35
50
|
}
|
|
36
|
-
|
|
51
|
+
|
|
52
|
+
function createTransporter(settings: SesSettings): Transporter {
|
|
37
53
|
const secure = settings.smtpPort === 465;
|
|
38
54
|
const requireTLS = settings.smtpPort === 587 || settings.smtpPort === 25 || settings.smtpPort === 2587;
|
|
55
|
+
|
|
39
56
|
return nodemailer.createTransport({
|
|
40
57
|
host: settings.smtpHost,
|
|
41
58
|
port: settings.smtpPort,
|
|
42
59
|
secure,
|
|
43
60
|
auth: {
|
|
44
61
|
user: settings.smtpUser,
|
|
45
|
-
pass: settings.smtpPassword
|
|
62
|
+
pass: settings.smtpPassword,
|
|
46
63
|
},
|
|
47
64
|
tls: {
|
|
48
|
-
rejectUnauthorized: true
|
|
65
|
+
rejectUnauthorized: true,
|
|
49
66
|
},
|
|
50
67
|
requireTLS,
|
|
51
|
-
connectionTimeout:
|
|
52
|
-
greetingTimeout:
|
|
53
|
-
socketTimeout:
|
|
68
|
+
connectionTimeout: 10000,
|
|
69
|
+
greetingTimeout: 10000,
|
|
70
|
+
socketTimeout: 10000,
|
|
54
71
|
});
|
|
55
72
|
}
|
|
56
|
-
|
|
73
|
+
|
|
74
|
+
async function validateSMTP(ctx: PluginContext): Promise<{ ok: boolean; error?: string }> {
|
|
57
75
|
try {
|
|
58
76
|
const settings = await getSettings(ctx);
|
|
59
77
|
if (!settings.smtpHost || !settings.smtpUser || !settings.smtpPassword) {
|
|
@@ -67,7 +85,8 @@ async function validateSMTP(ctx) {
|
|
|
67
85
|
return { ok: false, error: `Connection failed: ${message}` };
|
|
68
86
|
}
|
|
69
87
|
}
|
|
70
|
-
|
|
88
|
+
|
|
89
|
+
async function sendTestEmail(ctx: PluginContext, recipient: string): Promise<{ ok: boolean; error?: string }> {
|
|
71
90
|
try {
|
|
72
91
|
const settings = await getSettings(ctx);
|
|
73
92
|
if (!settings.defaultFrom) {
|
|
@@ -80,7 +99,7 @@ async function sendTestEmail(ctx, recipient) {
|
|
|
80
99
|
subject: "Test email from EmDash + AWS SES",
|
|
81
100
|
text: "This is a test email sent via Amazon SES from your EmDash CMS.",
|
|
82
101
|
html: "<p>This is a test email sent via <strong>Amazon SES</strong> from your EmDash CMS.</p>",
|
|
83
|
-
replyTo: settings.replyTo ||
|
|
102
|
+
replyTo: settings.replyTo || undefined,
|
|
84
103
|
});
|
|
85
104
|
return { ok: true };
|
|
86
105
|
} catch (err) {
|
|
@@ -88,8 +107,10 @@ async function sendTestEmail(ctx, recipient) {
|
|
|
88
107
|
return { ok: false, error: `Send failed: ${message}` };
|
|
89
108
|
}
|
|
90
109
|
}
|
|
91
|
-
|
|
110
|
+
|
|
111
|
+
async function renderSettingsPage(ctx: PluginContext) {
|
|
92
112
|
const settings = await getSettings(ctx);
|
|
113
|
+
|
|
93
114
|
return {
|
|
94
115
|
blocks: [
|
|
95
116
|
{ type: "header", text: "AWS SES Settings" },
|
|
@@ -104,7 +125,7 @@ async function renderSettingsPage(ctx) {
|
|
|
104
125
|
action_id: "smtpHost",
|
|
105
126
|
label: "SMTP Host",
|
|
106
127
|
initial_value: settings.smtpHost,
|
|
107
|
-
placeholder: "email-smtp.us-east-1.amazonaws.com"
|
|
128
|
+
placeholder: "email-smtp.us-east-1.amazonaws.com",
|
|
108
129
|
},
|
|
109
130
|
{
|
|
110
131
|
type: "number_input",
|
|
@@ -112,66 +133,73 @@ async function renderSettingsPage(ctx) {
|
|
|
112
133
|
label: "SMTP Port",
|
|
113
134
|
initial_value: settings.smtpPort,
|
|
114
135
|
min: 1,
|
|
115
|
-
max: 65535
|
|
136
|
+
max: 65535,
|
|
116
137
|
},
|
|
117
138
|
{
|
|
118
139
|
type: "text_input",
|
|
119
140
|
action_id: "smtpUser",
|
|
120
141
|
label: "SMTP Username",
|
|
121
142
|
initial_value: settings.smtpUser,
|
|
122
|
-
placeholder: "AWS SES SMTP username (IAM Access Key ID)"
|
|
143
|
+
placeholder: "AWS SES SMTP username (IAM Access Key ID)",
|
|
123
144
|
},
|
|
124
145
|
{
|
|
125
146
|
type: "secret_input",
|
|
126
147
|
action_id: "smtpPassword",
|
|
127
148
|
label: "SMTP Password",
|
|
128
|
-
initial_value: ""
|
|
149
|
+
initial_value: "",
|
|
129
150
|
},
|
|
130
151
|
{
|
|
131
152
|
type: "text_input",
|
|
132
153
|
action_id: "region",
|
|
133
154
|
label: "AWS Region",
|
|
134
155
|
initial_value: settings.region,
|
|
135
|
-
placeholder: "us-east-1"
|
|
156
|
+
placeholder: "us-east-1",
|
|
136
157
|
},
|
|
137
158
|
{
|
|
138
159
|
type: "text_input",
|
|
139
160
|
action_id: "defaultFrom",
|
|
140
161
|
label: "Default From Address",
|
|
141
162
|
initial_value: settings.defaultFrom,
|
|
142
|
-
placeholder: "noreply@example.com"
|
|
163
|
+
placeholder: "noreply@example.com",
|
|
143
164
|
},
|
|
144
165
|
{
|
|
145
166
|
type: "text_input",
|
|
146
167
|
action_id: "fromName",
|
|
147
168
|
label: "From Name",
|
|
148
169
|
initial_value: settings.fromName,
|
|
149
|
-
placeholder: "My Site"
|
|
170
|
+
placeholder: "My Site",
|
|
150
171
|
},
|
|
151
172
|
{
|
|
152
173
|
type: "text_input",
|
|
153
174
|
action_id: "replyTo",
|
|
154
175
|
label: "Reply-To Address",
|
|
155
176
|
initial_value: settings.replyTo,
|
|
156
|
-
placeholder: "support@example.com"
|
|
157
|
-
}
|
|
177
|
+
placeholder: "support@example.com",
|
|
178
|
+
},
|
|
158
179
|
],
|
|
159
|
-
submit: { label: "Save Settings", action_id: "save_settings" }
|
|
160
|
-
}
|
|
161
|
-
|
|
180
|
+
submit: { label: "Save Settings", action_id: "save_settings" },
|
|
181
|
+
},
|
|
182
|
+
{ type: "divider" },
|
|
183
|
+
{ type: "header", text: "Email Provider Activation" },
|
|
184
|
+
{ type: "section", text: "To activate AWS SES as your active email provider, go to Settings > Email in the EmDash admin and select emdash-aws-ses from the list. This routes all EmDash email (magic links, invites, notifications) through AWS SES." },
|
|
185
|
+
],
|
|
162
186
|
};
|
|
163
187
|
}
|
|
164
|
-
|
|
188
|
+
|
|
189
|
+
function getDefaultEndpoint(region: string): string {
|
|
165
190
|
return `email-smtp.${region}.amazonaws.com`;
|
|
166
191
|
}
|
|
167
|
-
|
|
192
|
+
|
|
193
|
+
async function saveSettings(ctx: PluginContext, values: Record<string, unknown>) {
|
|
168
194
|
const currentSettings = await getSettings(ctx);
|
|
195
|
+
|
|
169
196
|
if (typeof values.region === "string" && values.region.length > 0 && values.region !== currentSettings.region) {
|
|
170
197
|
const oldEndpoint = getDefaultEndpoint(currentSettings.region);
|
|
171
198
|
if (!currentSettings.smtpHost || currentSettings.smtpHost === oldEndpoint) {
|
|
172
199
|
values.smtpHost = getDefaultEndpoint(values.region);
|
|
173
200
|
}
|
|
174
201
|
}
|
|
202
|
+
|
|
175
203
|
if (typeof values.smtpHost === "string" && values.smtpHost.length > 0) {
|
|
176
204
|
await ctx.kv.set("settings:smtpHost", values.smtpHost);
|
|
177
205
|
}
|
|
@@ -197,30 +225,38 @@ async function saveSettings(ctx, values) {
|
|
|
197
225
|
await ctx.kv.set("settings:replyTo", values.replyTo);
|
|
198
226
|
}
|
|
199
227
|
}
|
|
200
|
-
|
|
228
|
+
|
|
229
|
+
async function renderTestPage(
|
|
230
|
+
ctx: PluginContext,
|
|
231
|
+
validation?: { ok: boolean; error?: string } | null,
|
|
232
|
+
sendResult?: { ok: boolean; error?: string; recipient?: string } | null,
|
|
233
|
+
) {
|
|
201
234
|
const settings = await getSettings(ctx);
|
|
202
|
-
|
|
235
|
+
|
|
236
|
+
const blocks: Array<Record<string, unknown>> = [
|
|
203
237
|
{ type: "header", text: "AWS SES Test & Status" },
|
|
204
238
|
{ type: "section", text: `Current endpoint: ${settings.smtpHost}:${settings.smtpPort} (${settings.region})` },
|
|
205
|
-
{ type: "divider" }
|
|
239
|
+
{ type: "divider" },
|
|
206
240
|
];
|
|
241
|
+
|
|
207
242
|
if (validation) {
|
|
208
243
|
if (validation.ok) {
|
|
209
244
|
blocks.push({
|
|
210
245
|
type: "banner",
|
|
211
246
|
title: "Connection verified",
|
|
212
247
|
description: "SMTP credentials are valid.",
|
|
213
|
-
variant: "default"
|
|
248
|
+
variant: "default",
|
|
214
249
|
});
|
|
215
250
|
} else {
|
|
216
251
|
blocks.push({
|
|
217
252
|
type: "banner",
|
|
218
253
|
title: "Connection failed",
|
|
219
254
|
description: validation.error,
|
|
220
|
-
variant: "error"
|
|
255
|
+
variant: "error",
|
|
221
256
|
});
|
|
222
257
|
}
|
|
223
258
|
}
|
|
259
|
+
|
|
224
260
|
blocks.push(
|
|
225
261
|
{ type: "section", text: "Test your SMTP connection and verify your credentials are correct." },
|
|
226
262
|
{
|
|
@@ -229,31 +265,33 @@ async function renderTestPage(ctx, validation, sendResult) {
|
|
|
229
265
|
{
|
|
230
266
|
type: "button",
|
|
231
267
|
label: "Test Connection",
|
|
232
|
-
action_id: "validate"
|
|
233
|
-
}
|
|
234
|
-
]
|
|
268
|
+
action_id: "validate",
|
|
269
|
+
},
|
|
270
|
+
],
|
|
235
271
|
},
|
|
236
272
|
{ type: "divider" },
|
|
237
273
|
{ type: "header", text: "Send Test Email" },
|
|
238
|
-
{ type: "section", text: "Send a test email to verify the full transport pipeline. Enter a recipient address below." }
|
|
274
|
+
{ type: "section", text: "Send a test email to verify the full transport pipeline. Enter a recipient address below." },
|
|
239
275
|
);
|
|
276
|
+
|
|
240
277
|
if (sendResult) {
|
|
241
278
|
if (sendResult.ok) {
|
|
242
279
|
blocks.push({
|
|
243
280
|
type: "banner",
|
|
244
281
|
title: "Email sent",
|
|
245
282
|
description: `Test email sent successfully to ${sendResult.recipient}.`,
|
|
246
|
-
variant: "default"
|
|
283
|
+
variant: "default",
|
|
247
284
|
});
|
|
248
285
|
} else {
|
|
249
286
|
blocks.push({
|
|
250
287
|
type: "banner",
|
|
251
288
|
title: "Email send failed",
|
|
252
289
|
description: sendResult.error,
|
|
253
|
-
variant: "error"
|
|
290
|
+
variant: "error",
|
|
254
291
|
});
|
|
255
292
|
}
|
|
256
293
|
}
|
|
294
|
+
|
|
257
295
|
blocks.push({
|
|
258
296
|
type: "form",
|
|
259
297
|
block_id: "test_form",
|
|
@@ -263,51 +301,57 @@ async function renderTestPage(ctx, validation, sendResult) {
|
|
|
263
301
|
action_id: "recipient",
|
|
264
302
|
label: "Recipient Email",
|
|
265
303
|
placeholder: "you@example.com",
|
|
266
|
-
initial_value: sendResult?.ok ? "" :
|
|
267
|
-
}
|
|
304
|
+
initial_value: sendResult?.ok ? "" : undefined,
|
|
305
|
+
},
|
|
268
306
|
],
|
|
269
|
-
submit: { label: "Send Test", action_id: "send_test" }
|
|
307
|
+
submit: { label: "Send Test", action_id: "send_test" },
|
|
270
308
|
});
|
|
309
|
+
|
|
271
310
|
return { blocks };
|
|
272
311
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
version: "1.0.0",
|
|
276
|
-
capabilities: ["hooks.email-transport:register"],
|
|
312
|
+
|
|
313
|
+
export default {
|
|
277
314
|
hooks: {
|
|
278
|
-
"plugin:install": async (_event, ctx) => {
|
|
315
|
+
"plugin:install": async (_event: LifecycleEvent, ctx: PluginContext) => {
|
|
279
316
|
await ctx.kv.set("settings:smtpHost", "email-smtp.us-east-1.amazonaws.com");
|
|
280
317
|
await ctx.kv.set("settings:smtpPort", 587);
|
|
281
318
|
await ctx.kv.set("settings:region", "us-east-1");
|
|
282
319
|
ctx.log.info("AWS SES plugin defaults seeded");
|
|
283
320
|
},
|
|
321
|
+
|
|
284
322
|
"email:deliver": {
|
|
285
323
|
exclusive: true,
|
|
286
|
-
handler: async ({ message, source }, ctx) => {
|
|
324
|
+
handler: async ({ message, source }: EmailDeliverEvent, ctx: PluginContext) => {
|
|
287
325
|
const settings = await getSettings(ctx);
|
|
288
326
|
const transporter = createTransporter(settings);
|
|
327
|
+
|
|
289
328
|
await transporter.sendMail({
|
|
290
329
|
from: settings.fromName ? `"${settings.fromName}" <${settings.defaultFrom}>` : settings.defaultFrom,
|
|
291
330
|
to: message.to,
|
|
292
331
|
subject: message.subject,
|
|
293
332
|
text: message.text,
|
|
294
333
|
html: message.html,
|
|
295
|
-
replyTo: settings.replyTo ||
|
|
334
|
+
replyTo: settings.replyTo || undefined,
|
|
296
335
|
});
|
|
336
|
+
|
|
297
337
|
ctx.log.info(`Email sent via AWS SES to ${message.to}`, { source });
|
|
298
|
-
}
|
|
299
|
-
}
|
|
338
|
+
},
|
|
339
|
+
},
|
|
300
340
|
},
|
|
341
|
+
|
|
301
342
|
routes: {
|
|
302
343
|
admin: {
|
|
303
|
-
handler: async (routeCtx, ctx) => {
|
|
304
|
-
const interaction = routeCtx.input;
|
|
344
|
+
handler: async (routeCtx: SandboxedRouteContext, ctx: PluginContext) => {
|
|
345
|
+
const interaction = routeCtx.input as BlockInteraction;
|
|
346
|
+
|
|
305
347
|
if (interaction.type === "page_load" && (!interaction.page || interaction.page === "/")) {
|
|
306
348
|
return renderSettingsPage(ctx);
|
|
307
349
|
}
|
|
350
|
+
|
|
308
351
|
if (interaction.type === "page_load" && interaction.page === "/settings") {
|
|
309
352
|
return renderSettingsPage(ctx);
|
|
310
353
|
}
|
|
354
|
+
|
|
311
355
|
if (interaction.type === "form_submit" && interaction.action_id === "save_settings") {
|
|
312
356
|
await saveSettings(ctx, interaction.values ?? {});
|
|
313
357
|
const settingsPage = await renderSettingsPage(ctx);
|
|
@@ -319,42 +363,47 @@ var sandbox_entry_default = definePlugin({
|
|
|
319
363
|
type: "banner",
|
|
320
364
|
title: "Settings saved",
|
|
321
365
|
description: "Your AWS SES configuration has been updated successfully.",
|
|
322
|
-
variant: "default"
|
|
366
|
+
variant: "default",
|
|
323
367
|
},
|
|
324
|
-
...settingsBlocks.slice(1)
|
|
368
|
+
...settingsBlocks.slice(1),
|
|
325
369
|
],
|
|
326
|
-
toast: { message: "Settings saved successfully", type: "success" }
|
|
370
|
+
toast: { message: "Settings saved successfully", type: "success" },
|
|
327
371
|
};
|
|
328
372
|
}
|
|
373
|
+
|
|
329
374
|
if (interaction.type === "page_load" && interaction.page === "/test") {
|
|
330
375
|
return renderTestPage(ctx);
|
|
331
376
|
}
|
|
377
|
+
|
|
332
378
|
if (interaction.type === "block_action" && interaction.action_id === "validate") {
|
|
333
379
|
const result = await validateSMTP(ctx);
|
|
334
380
|
return {
|
|
335
|
-
...await renderTestPage(ctx, result),
|
|
336
|
-
toast: result.ok
|
|
381
|
+
...(await renderTestPage(ctx, result)),
|
|
382
|
+
toast: result.ok
|
|
383
|
+
? { message: "Connection verified successfully", type: "success" }
|
|
384
|
+
: { message: result.error ?? "Validation failed", type: "error" },
|
|
337
385
|
};
|
|
338
386
|
}
|
|
387
|
+
|
|
339
388
|
if (interaction.type === "form_submit" && interaction.action_id === "send_test") {
|
|
340
|
-
const recipient = interaction.values?.recipient?.trim();
|
|
389
|
+
const recipient = (interaction.values?.recipient as string)?.trim();
|
|
341
390
|
if (!recipient) {
|
|
342
391
|
return {
|
|
343
|
-
...await renderTestPage(ctx),
|
|
344
|
-
toast: { message: "Please enter a recipient email", type: "error" }
|
|
392
|
+
...(await renderTestPage(ctx)),
|
|
393
|
+
toast: { message: "Please enter a recipient email", type: "error" },
|
|
345
394
|
};
|
|
346
395
|
}
|
|
347
396
|
const result = await sendTestEmail(ctx, recipient);
|
|
348
397
|
return {
|
|
349
|
-
...await renderTestPage(ctx, null, { ...result, recipient }),
|
|
350
|
-
toast: result.ok
|
|
398
|
+
...(await renderTestPage(ctx, null, { ...result, recipient })),
|
|
399
|
+
toast: result.ok
|
|
400
|
+
? { message: `Test email sent to ${recipient}`, type: "success" }
|
|
401
|
+
: { message: result.error ?? "Send failed", type: "error" },
|
|
351
402
|
};
|
|
352
403
|
}
|
|
404
|
+
|
|
353
405
|
return { blocks: [] };
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
export {
|
|
359
|
-
sandbox_entry_default as default
|
|
360
|
-
};
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
} satisfies SandboxedPlugin;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noImplicitAny": false, // <--- Añade esto para silenciar los 'any' implícitos
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"outDir": "./dist"
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|
package/dist/index.cjs
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
awsSesPlugin: () => awsSesPlugin
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(index_exports);
|
|
26
|
-
function awsSesPlugin() {
|
|
27
|
-
return {
|
|
28
|
-
id: "emdash-aws-ses",
|
|
29
|
-
version: "1.0.0",
|
|
30
|
-
format: "standard",
|
|
31
|
-
entrypoint: "emdash-aws-ses/sandbox",
|
|
32
|
-
options: {},
|
|
33
|
-
capabilities: ["hooks.email-transport:register"],
|
|
34
|
-
adminPages: [
|
|
35
|
-
{ path: "/settings", label: "AWS SES Settings", icon: "mail" },
|
|
36
|
-
{ path: "/test", label: "Test & Status", icon: "send" }
|
|
37
|
-
]
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
41
|
-
0 && (module.exports = {
|
|
42
|
-
awsSesPlugin
|
|
43
|
-
});
|
package/dist/sandbox-entry.cjs
DELETED
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/sandbox-entry.ts
|
|
31
|
-
var sandbox_entry_exports = {};
|
|
32
|
-
__export(sandbox_entry_exports, {
|
|
33
|
-
default: () => sandbox_entry_default
|
|
34
|
-
});
|
|
35
|
-
module.exports = __toCommonJS(sandbox_entry_exports);
|
|
36
|
-
var import_emdash = require("emdash");
|
|
37
|
-
var import_nodemailer = __toESM(require("nodemailer"), 1);
|
|
38
|
-
async function getSettings(ctx) {
|
|
39
|
-
const defaults = {
|
|
40
|
-
smtpHost: "email-smtp.us-east-1.amazonaws.com",
|
|
41
|
-
smtpPort: 587,
|
|
42
|
-
region: "us-east-1",
|
|
43
|
-
defaultFrom: "",
|
|
44
|
-
fromName: "",
|
|
45
|
-
replyTo: "",
|
|
46
|
-
smtpUser: "",
|
|
47
|
-
smtpPassword: ""
|
|
48
|
-
};
|
|
49
|
-
const [smtpHost, smtpPortRaw, smtpUser, smtpPassword, region, defaultFrom, fromName, replyTo] = await Promise.all([
|
|
50
|
-
ctx.kv.get("settings:smtpHost"),
|
|
51
|
-
ctx.kv.get("settings:smtpPort"),
|
|
52
|
-
ctx.kv.get("settings:smtpUser"),
|
|
53
|
-
ctx.kv.get("settings:smtpPassword"),
|
|
54
|
-
ctx.kv.get("settings:region"),
|
|
55
|
-
ctx.kv.get("settings:defaultFrom"),
|
|
56
|
-
ctx.kv.get("settings:fromName"),
|
|
57
|
-
ctx.kv.get("settings:replyTo")
|
|
58
|
-
]);
|
|
59
|
-
return {
|
|
60
|
-
smtpHost: smtpHost ?? defaults.smtpHost,
|
|
61
|
-
smtpPort: Number(smtpPortRaw ?? defaults.smtpPort),
|
|
62
|
-
smtpUser: smtpUser ?? defaults.smtpUser,
|
|
63
|
-
smtpPassword: smtpPassword ?? defaults.smtpPassword,
|
|
64
|
-
region: region ?? defaults.region,
|
|
65
|
-
defaultFrom: defaultFrom ?? defaults.defaultFrom,
|
|
66
|
-
fromName: fromName ?? defaults.fromName,
|
|
67
|
-
replyTo: replyTo ?? defaults.replyTo
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
function createTransporter(settings) {
|
|
71
|
-
const secure = settings.smtpPort === 465;
|
|
72
|
-
const requireTLS = settings.smtpPort === 587 || settings.smtpPort === 25 || settings.smtpPort === 2587;
|
|
73
|
-
return import_nodemailer.default.createTransport({
|
|
74
|
-
host: settings.smtpHost,
|
|
75
|
-
port: settings.smtpPort,
|
|
76
|
-
secure,
|
|
77
|
-
auth: {
|
|
78
|
-
user: settings.smtpUser,
|
|
79
|
-
pass: settings.smtpPassword
|
|
80
|
-
},
|
|
81
|
-
tls: {
|
|
82
|
-
rejectUnauthorized: true
|
|
83
|
-
},
|
|
84
|
-
requireTLS,
|
|
85
|
-
connectionTimeout: 1e4,
|
|
86
|
-
greetingTimeout: 1e4,
|
|
87
|
-
socketTimeout: 1e4
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
async function validateSMTP(ctx) {
|
|
91
|
-
try {
|
|
92
|
-
const settings = await getSettings(ctx);
|
|
93
|
-
if (!settings.smtpHost || !settings.smtpUser || !settings.smtpPassword) {
|
|
94
|
-
return { ok: false, error: "SMTP settings are incomplete. Please configure host, user, and password." };
|
|
95
|
-
}
|
|
96
|
-
const transporter = createTransporter(settings);
|
|
97
|
-
await transporter.verify();
|
|
98
|
-
return { ok: true };
|
|
99
|
-
} catch (err) {
|
|
100
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
101
|
-
return { ok: false, error: `Connection failed: ${message}` };
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
async function sendTestEmail(ctx, recipient) {
|
|
105
|
-
try {
|
|
106
|
-
const settings = await getSettings(ctx);
|
|
107
|
-
if (!settings.defaultFrom) {
|
|
108
|
-
return { ok: false, error: "Default From address is not configured." };
|
|
109
|
-
}
|
|
110
|
-
const transporter = createTransporter(settings);
|
|
111
|
-
await transporter.sendMail({
|
|
112
|
-
from: settings.fromName ? `"${settings.fromName}" <${settings.defaultFrom}>` : settings.defaultFrom,
|
|
113
|
-
to: recipient,
|
|
114
|
-
subject: "Test email from EmDash + AWS SES",
|
|
115
|
-
text: "This is a test email sent via Amazon SES from your EmDash CMS.",
|
|
116
|
-
html: "<p>This is a test email sent via <strong>Amazon SES</strong> from your EmDash CMS.</p>",
|
|
117
|
-
replyTo: settings.replyTo || void 0
|
|
118
|
-
});
|
|
119
|
-
return { ok: true };
|
|
120
|
-
} catch (err) {
|
|
121
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
122
|
-
return { ok: false, error: `Send failed: ${message}` };
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
async function renderSettingsPage(ctx) {
|
|
126
|
-
const settings = await getSettings(ctx);
|
|
127
|
-
return {
|
|
128
|
-
blocks: [
|
|
129
|
-
{ type: "header", text: "AWS SES Settings" },
|
|
130
|
-
{ type: "section", text: "Configure your Amazon SES SMTP transport. All settings are stored in the plugin KV store." },
|
|
131
|
-
{ type: "divider" },
|
|
132
|
-
{
|
|
133
|
-
type: "form",
|
|
134
|
-
block_id: "settings",
|
|
135
|
-
fields: [
|
|
136
|
-
{
|
|
137
|
-
type: "text_input",
|
|
138
|
-
action_id: "smtpHost",
|
|
139
|
-
label: "SMTP Host",
|
|
140
|
-
initial_value: settings.smtpHost,
|
|
141
|
-
placeholder: "email-smtp.us-east-1.amazonaws.com"
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
type: "number_input",
|
|
145
|
-
action_id: "smtpPort",
|
|
146
|
-
label: "SMTP Port",
|
|
147
|
-
initial_value: settings.smtpPort,
|
|
148
|
-
min: 1,
|
|
149
|
-
max: 65535
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
type: "text_input",
|
|
153
|
-
action_id: "smtpUser",
|
|
154
|
-
label: "SMTP Username",
|
|
155
|
-
initial_value: settings.smtpUser,
|
|
156
|
-
placeholder: "AWS SES SMTP username (IAM Access Key ID)"
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
type: "secret_input",
|
|
160
|
-
action_id: "smtpPassword",
|
|
161
|
-
label: "SMTP Password",
|
|
162
|
-
initial_value: ""
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
type: "text_input",
|
|
166
|
-
action_id: "region",
|
|
167
|
-
label: "AWS Region",
|
|
168
|
-
initial_value: settings.region,
|
|
169
|
-
placeholder: "us-east-1"
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
type: "text_input",
|
|
173
|
-
action_id: "defaultFrom",
|
|
174
|
-
label: "Default From Address",
|
|
175
|
-
initial_value: settings.defaultFrom,
|
|
176
|
-
placeholder: "noreply@example.com"
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
type: "text_input",
|
|
180
|
-
action_id: "fromName",
|
|
181
|
-
label: "From Name",
|
|
182
|
-
initial_value: settings.fromName,
|
|
183
|
-
placeholder: "My Site"
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
type: "text_input",
|
|
187
|
-
action_id: "replyTo",
|
|
188
|
-
label: "Reply-To Address",
|
|
189
|
-
initial_value: settings.replyTo,
|
|
190
|
-
placeholder: "support@example.com"
|
|
191
|
-
}
|
|
192
|
-
],
|
|
193
|
-
submit: { label: "Save Settings", action_id: "save_settings" }
|
|
194
|
-
}
|
|
195
|
-
]
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
function getDefaultEndpoint(region) {
|
|
199
|
-
return `email-smtp.${region}.amazonaws.com`;
|
|
200
|
-
}
|
|
201
|
-
async function saveSettings(ctx, values) {
|
|
202
|
-
const currentSettings = await getSettings(ctx);
|
|
203
|
-
if (typeof values.region === "string" && values.region.length > 0 && values.region !== currentSettings.region) {
|
|
204
|
-
const oldEndpoint = getDefaultEndpoint(currentSettings.region);
|
|
205
|
-
if (!currentSettings.smtpHost || currentSettings.smtpHost === oldEndpoint) {
|
|
206
|
-
values.smtpHost = getDefaultEndpoint(values.region);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
if (typeof values.smtpHost === "string" && values.smtpHost.length > 0) {
|
|
210
|
-
await ctx.kv.set("settings:smtpHost", values.smtpHost);
|
|
211
|
-
}
|
|
212
|
-
if (typeof values.smtpPort === "number" && values.smtpPort >= 1 && values.smtpPort <= 65535) {
|
|
213
|
-
await ctx.kv.set("settings:smtpPort", values.smtpPort);
|
|
214
|
-
}
|
|
215
|
-
if (typeof values.smtpUser === "string" && values.smtpUser.length > 0) {
|
|
216
|
-
await ctx.kv.set("settings:smtpUser", values.smtpUser);
|
|
217
|
-
}
|
|
218
|
-
if (typeof values.smtpPassword === "string" && values.smtpPassword.length > 0) {
|
|
219
|
-
await ctx.kv.set("settings:smtpPassword", values.smtpPassword);
|
|
220
|
-
}
|
|
221
|
-
if (typeof values.region === "string" && values.region.length > 0) {
|
|
222
|
-
await ctx.kv.set("settings:region", values.region);
|
|
223
|
-
}
|
|
224
|
-
if (typeof values.defaultFrom === "string") {
|
|
225
|
-
await ctx.kv.set("settings:defaultFrom", values.defaultFrom);
|
|
226
|
-
}
|
|
227
|
-
if (typeof values.fromName === "string") {
|
|
228
|
-
await ctx.kv.set("settings:fromName", values.fromName);
|
|
229
|
-
}
|
|
230
|
-
if (typeof values.replyTo === "string") {
|
|
231
|
-
await ctx.kv.set("settings:replyTo", values.replyTo);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
async function renderTestPage(ctx, validation, sendResult) {
|
|
235
|
-
const settings = await getSettings(ctx);
|
|
236
|
-
const blocks = [
|
|
237
|
-
{ type: "header", text: "AWS SES Test & Status" },
|
|
238
|
-
{ type: "section", text: `Current endpoint: ${settings.smtpHost}:${settings.smtpPort} (${settings.region})` },
|
|
239
|
-
{ type: "divider" }
|
|
240
|
-
];
|
|
241
|
-
if (validation) {
|
|
242
|
-
if (validation.ok) {
|
|
243
|
-
blocks.push({
|
|
244
|
-
type: "banner",
|
|
245
|
-
title: "Connection verified",
|
|
246
|
-
description: "SMTP credentials are valid.",
|
|
247
|
-
variant: "default"
|
|
248
|
-
});
|
|
249
|
-
} else {
|
|
250
|
-
blocks.push({
|
|
251
|
-
type: "banner",
|
|
252
|
-
title: "Connection failed",
|
|
253
|
-
description: validation.error,
|
|
254
|
-
variant: "error"
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
blocks.push(
|
|
259
|
-
{ type: "section", text: "Test your SMTP connection and verify your credentials are correct." },
|
|
260
|
-
{
|
|
261
|
-
type: "actions",
|
|
262
|
-
elements: [
|
|
263
|
-
{
|
|
264
|
-
type: "button",
|
|
265
|
-
label: "Test Connection",
|
|
266
|
-
action_id: "validate"
|
|
267
|
-
}
|
|
268
|
-
]
|
|
269
|
-
},
|
|
270
|
-
{ type: "divider" },
|
|
271
|
-
{ type: "header", text: "Send Test Email" },
|
|
272
|
-
{ type: "section", text: "Send a test email to verify the full transport pipeline. Enter a recipient address below." }
|
|
273
|
-
);
|
|
274
|
-
if (sendResult) {
|
|
275
|
-
if (sendResult.ok) {
|
|
276
|
-
blocks.push({
|
|
277
|
-
type: "banner",
|
|
278
|
-
title: "Email sent",
|
|
279
|
-
description: `Test email sent successfully to ${sendResult.recipient}.`,
|
|
280
|
-
variant: "default"
|
|
281
|
-
});
|
|
282
|
-
} else {
|
|
283
|
-
blocks.push({
|
|
284
|
-
type: "banner",
|
|
285
|
-
title: "Email send failed",
|
|
286
|
-
description: sendResult.error,
|
|
287
|
-
variant: "error"
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
blocks.push({
|
|
292
|
-
type: "form",
|
|
293
|
-
block_id: "test_form",
|
|
294
|
-
fields: [
|
|
295
|
-
{
|
|
296
|
-
type: "text_input",
|
|
297
|
-
action_id: "recipient",
|
|
298
|
-
label: "Recipient Email",
|
|
299
|
-
placeholder: "you@example.com",
|
|
300
|
-
initial_value: sendResult?.ok ? "" : void 0
|
|
301
|
-
}
|
|
302
|
-
],
|
|
303
|
-
submit: { label: "Send Test", action_id: "send_test" }
|
|
304
|
-
});
|
|
305
|
-
return { blocks };
|
|
306
|
-
}
|
|
307
|
-
var sandbox_entry_default = (0, import_emdash.definePlugin)({
|
|
308
|
-
id: "emdash-aws-ses",
|
|
309
|
-
version: "1.0.0",
|
|
310
|
-
capabilities: ["hooks.email-transport:register"],
|
|
311
|
-
hooks: {
|
|
312
|
-
"plugin:install": async (_event, ctx) => {
|
|
313
|
-
await ctx.kv.set("settings:smtpHost", "email-smtp.us-east-1.amazonaws.com");
|
|
314
|
-
await ctx.kv.set("settings:smtpPort", 587);
|
|
315
|
-
await ctx.kv.set("settings:region", "us-east-1");
|
|
316
|
-
ctx.log.info("AWS SES plugin defaults seeded");
|
|
317
|
-
},
|
|
318
|
-
"email:deliver": {
|
|
319
|
-
exclusive: true,
|
|
320
|
-
handler: async ({ message, source }, ctx) => {
|
|
321
|
-
const settings = await getSettings(ctx);
|
|
322
|
-
const transporter = createTransporter(settings);
|
|
323
|
-
await transporter.sendMail({
|
|
324
|
-
from: settings.fromName ? `"${settings.fromName}" <${settings.defaultFrom}>` : settings.defaultFrom,
|
|
325
|
-
to: message.to,
|
|
326
|
-
subject: message.subject,
|
|
327
|
-
text: message.text,
|
|
328
|
-
html: message.html,
|
|
329
|
-
replyTo: settings.replyTo || void 0
|
|
330
|
-
});
|
|
331
|
-
ctx.log.info(`Email sent via AWS SES to ${message.to}`, { source });
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
routes: {
|
|
336
|
-
admin: {
|
|
337
|
-
handler: async (routeCtx, ctx) => {
|
|
338
|
-
const interaction = routeCtx.input;
|
|
339
|
-
if (interaction.type === "page_load" && (!interaction.page || interaction.page === "/")) {
|
|
340
|
-
return renderSettingsPage(ctx);
|
|
341
|
-
}
|
|
342
|
-
if (interaction.type === "page_load" && interaction.page === "/settings") {
|
|
343
|
-
return renderSettingsPage(ctx);
|
|
344
|
-
}
|
|
345
|
-
if (interaction.type === "form_submit" && interaction.action_id === "save_settings") {
|
|
346
|
-
await saveSettings(ctx, interaction.values ?? {});
|
|
347
|
-
const settingsPage = await renderSettingsPage(ctx);
|
|
348
|
-
const settingsBlocks = settingsPage.blocks;
|
|
349
|
-
return {
|
|
350
|
-
blocks: [
|
|
351
|
-
settingsBlocks[0],
|
|
352
|
-
{
|
|
353
|
-
type: "banner",
|
|
354
|
-
title: "Settings saved",
|
|
355
|
-
description: "Your AWS SES configuration has been updated successfully.",
|
|
356
|
-
variant: "default"
|
|
357
|
-
},
|
|
358
|
-
...settingsBlocks.slice(1)
|
|
359
|
-
],
|
|
360
|
-
toast: { message: "Settings saved successfully", type: "success" }
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
if (interaction.type === "page_load" && interaction.page === "/test") {
|
|
364
|
-
return renderTestPage(ctx);
|
|
365
|
-
}
|
|
366
|
-
if (interaction.type === "block_action" && interaction.action_id === "validate") {
|
|
367
|
-
const result = await validateSMTP(ctx);
|
|
368
|
-
return {
|
|
369
|
-
...await renderTestPage(ctx, result),
|
|
370
|
-
toast: result.ok ? { message: "Connection verified successfully", type: "success" } : { message: result.error ?? "Validation failed", type: "error" }
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
if (interaction.type === "form_submit" && interaction.action_id === "send_test") {
|
|
374
|
-
const recipient = interaction.values?.recipient?.trim();
|
|
375
|
-
if (!recipient) {
|
|
376
|
-
return {
|
|
377
|
-
...await renderTestPage(ctx),
|
|
378
|
-
toast: { message: "Please enter a recipient email", type: "error" }
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
const result = await sendTestEmail(ctx, recipient);
|
|
382
|
-
return {
|
|
383
|
-
...await renderTestPage(ctx, null, { ...result, recipient }),
|
|
384
|
-
toast: result.ok ? { message: `Test email sent to ${recipient}`, type: "success" } : { message: result.error ?? "Send failed", type: "error" }
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
return { blocks: [] };
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
});
|