@flink-app/email-plugin 0.12.1-alpha.9 → 0.13.1
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/CHANGELOG.md +13 -0
- package/dist/flowmailerClient.js +2 -2
- package/dist/schemas/email.d.ts +0 -4
- package/dist/sendgridClient.js +2 -2
- package/dist/smtpClient.js +2 -2
- package/package.json +28 -29
- package/readme.md +352 -45
- package/tsconfig.json +1 -1
package/CHANGELOG.md
ADDED
package/dist/flowmailerClient.js
CHANGED
|
@@ -20,8 +20,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
20
20
|
});
|
|
21
21
|
};
|
|
22
22
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
-
return g =
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
24
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
25
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
26
|
function step(op) {
|
|
27
27
|
if (f) throw new TypeError("Generator is already executing.");
|
package/dist/schemas/email.d.ts
CHANGED
package/dist/sendgridClient.js
CHANGED
|
@@ -9,8 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
-
return g =
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
14
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
15
|
function step(op) {
|
|
16
16
|
if (f) throw new TypeError("Generator is already executing.");
|
package/dist/smtpClient.js
CHANGED
|
@@ -9,8 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
-
return g =
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
14
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
15
|
function step(op) {
|
|
16
16
|
if (f) throw new TypeError("Generator is already executing.");
|
package/package.json
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"types": "
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
2
|
+
"name": "@flink-app/email-plugin",
|
|
3
|
+
"version": "0.13.1",
|
|
4
|
+
"description": "Flink plugin that makes it possible to send email",
|
|
5
|
+
"author": "johan@frost.se",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@sendgrid/mail": "^7.4.5",
|
|
14
|
+
"@types/nodemailer": "^6.4.4",
|
|
15
|
+
"axios": "^0.27.2",
|
|
16
|
+
"nodemailer": "^6.6.2",
|
|
17
|
+
"querystring": "^0.2.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "22.13.10",
|
|
21
|
+
"@flink-app/flink": "0.13.1"
|
|
22
|
+
},
|
|
23
|
+
"gitHead": "4243e3b3cd6d4e1ca001a61baa8436bf2bbe4113",
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "echo \"Error: no test specified\"",
|
|
26
|
+
"build": "tsc",
|
|
27
|
+
"clean": "rimraf dist .flink"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/readme.md
CHANGED
|
@@ -1,103 +1,410 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Email Plugin
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A Flink plugin that provides multi-provider email sending capabilities with support for SendGrid, SMTP, and Flowmailer.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
Install plugin to your
|
|
7
|
+
Install the plugin to your Flink app project:
|
|
8
8
|
|
|
9
|
-
```
|
|
10
|
-
npm
|
|
9
|
+
```bash
|
|
10
|
+
npm install @flink-app/email-plugin
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Configuration
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
The email plugin supports three different email providers through a unified client interface:
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
### Option 1: SendGrid
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Configure the plugin with SendGrid for API-based email sending:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { FlinkApp, FlinkContext } from "@flink-app/flink";
|
|
20
23
|
import { emailPlugin, sendgridClient } from "@flink-app/email-plugin";
|
|
21
24
|
|
|
22
25
|
function start() {
|
|
23
26
|
new FlinkApp<AppContext>({
|
|
24
27
|
name: "My app",
|
|
25
28
|
plugins: [
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
apiKey : process.env.SENDGRID_API_KEY
|
|
30
|
-
})
|
|
29
|
+
emailPlugin({
|
|
30
|
+
client: new sendgridClient({
|
|
31
|
+
apiKey: process.env.SENDGRID_API_KEY!
|
|
31
32
|
})
|
|
33
|
+
})
|
|
32
34
|
],
|
|
33
35
|
}).start();
|
|
34
36
|
}
|
|
35
|
-
|
|
36
37
|
```
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
**SendGrid Client Options:**
|
|
39
40
|
|
|
41
|
+
```typescript
|
|
42
|
+
interface sendgridClientOptions {
|
|
43
|
+
apiKey: string; // Your SendGrid API key
|
|
44
|
+
}
|
|
40
45
|
```
|
|
41
|
-
import { emailPluginContext } from "@flink-app/email-plugin";
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
### Option 2: SMTP
|
|
48
|
+
|
|
49
|
+
Configure the plugin with any SMTP server:
|
|
44
50
|
|
|
51
|
+
```typescript
|
|
52
|
+
import { emailPlugin, smtpClient } from "@flink-app/email-plugin";
|
|
53
|
+
|
|
54
|
+
function start() {
|
|
55
|
+
new FlinkApp<AppContext>({
|
|
56
|
+
name: "My app",
|
|
57
|
+
plugins: [
|
|
58
|
+
emailPlugin({
|
|
59
|
+
client: new smtpClient({
|
|
60
|
+
host: "smtp.example.com",
|
|
61
|
+
port: 587,
|
|
62
|
+
secure: false,
|
|
63
|
+
auth: {
|
|
64
|
+
user: "username",
|
|
65
|
+
pass: "password"
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
],
|
|
70
|
+
}).start();
|
|
45
71
|
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**SMTP Client Options:**
|
|
46
75
|
|
|
76
|
+
```typescript
|
|
77
|
+
interface smtpClientOptions {
|
|
78
|
+
host: string; // SMTP server hostname
|
|
79
|
+
port: number; // SMTP server port (usually 587 or 465)
|
|
80
|
+
secure: boolean; // true for port 465, false for other ports
|
|
81
|
+
auth?: {
|
|
82
|
+
user: string; // SMTP username
|
|
83
|
+
pass: string; // SMTP password
|
|
84
|
+
};
|
|
85
|
+
}
|
|
47
86
|
```
|
|
48
87
|
|
|
49
|
-
###
|
|
88
|
+
### Option 3: Flowmailer
|
|
50
89
|
|
|
51
|
-
|
|
90
|
+
Configure the plugin with Flowmailer:
|
|
52
91
|
|
|
53
|
-
```
|
|
54
|
-
import { emailPlugin,
|
|
92
|
+
```typescript
|
|
93
|
+
import { emailPlugin, flowMailerClient } from "@flink-app/email-plugin";
|
|
55
94
|
|
|
56
95
|
function start() {
|
|
57
96
|
new FlinkApp<AppContext>({
|
|
58
97
|
name: "My app",
|
|
59
98
|
plugins: [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
secure: false
|
|
66
|
-
auth: {
|
|
67
|
-
user: "user"
|
|
68
|
-
pass: "pass"
|
|
69
|
-
},
|
|
70
|
-
})
|
|
99
|
+
emailPlugin({
|
|
100
|
+
client: new flowMailerClient({
|
|
101
|
+
client_id: process.env.FLOWMAILER_CLIENT_ID!,
|
|
102
|
+
client_secrect: process.env.FLOWMAILER_CLIENT_SECRET!,
|
|
103
|
+
account_id: process.env.FLOWMAILER_ACCOUNT_ID!
|
|
71
104
|
})
|
|
105
|
+
})
|
|
72
106
|
],
|
|
73
107
|
}).start();
|
|
74
108
|
}
|
|
75
|
-
|
|
76
109
|
```
|
|
77
110
|
|
|
78
|
-
|
|
111
|
+
**Flowmailer Client Options:**
|
|
79
112
|
|
|
113
|
+
```typescript
|
|
114
|
+
interface flowmailerClientOptions {
|
|
115
|
+
client_id: string; // Flowmailer OAuth client ID
|
|
116
|
+
client_secrect: string; // Flowmailer OAuth client secret
|
|
117
|
+
account_id: string; // Flowmailer account ID
|
|
118
|
+
}
|
|
80
119
|
```
|
|
120
|
+
|
|
121
|
+
## TypeScript Setup
|
|
122
|
+
|
|
123
|
+
Add the plugin context to your app's context type (usually in `Ctx.ts`):
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { FlinkContext } from "@flink-app/flink";
|
|
81
127
|
import { emailPluginContext } from "@flink-app/email-plugin";
|
|
82
128
|
|
|
83
129
|
export interface Ctx extends FlinkContext<emailPluginContext> {
|
|
130
|
+
// Your other context properties
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Plugin Context Interface:**
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
interface emailPluginContext {
|
|
138
|
+
emailPlugin: {
|
|
139
|
+
client: client; // Unified email client interface
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Usage in Handlers
|
|
145
|
+
|
|
146
|
+
### Basic Email
|
|
147
|
+
|
|
148
|
+
Send a simple text email:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { Handler } from "@flink-app/flink";
|
|
152
|
+
import { Ctx } from "../Ctx";
|
|
153
|
+
|
|
154
|
+
const SendWelcomeEmail: Handler<Ctx, any, any> = async ({ ctx, req }) => {
|
|
155
|
+
await ctx.plugins.emailPlugin.client.send({
|
|
156
|
+
from: "noreply@example.com",
|
|
157
|
+
to: ["user@example.com"],
|
|
158
|
+
subject: "Welcome!",
|
|
159
|
+
text: "Welcome to our service!"
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return { data: { success: true } };
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export default SendWelcomeEmail;
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### HTML Email
|
|
169
|
+
|
|
170
|
+
Send an HTML email:
|
|
84
171
|
|
|
172
|
+
```typescript
|
|
173
|
+
await ctx.plugins.emailPlugin.client.send({
|
|
174
|
+
from: "noreply@example.com",
|
|
175
|
+
to: ["user@example.com"],
|
|
176
|
+
subject: "Newsletter",
|
|
177
|
+
html: "<h1>Hello</h1><p>This is an HTML email</p>"
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Email with Multiple Recipients
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
await ctx.plugins.emailPlugin.client.send({
|
|
185
|
+
from: "noreply@example.com",
|
|
186
|
+
to: ["user1@example.com", "user2@example.com", "user3@example.com"],
|
|
187
|
+
subject: "Team Update",
|
|
188
|
+
text: "Important team announcement"
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Email with BCC and Reply-To
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
await ctx.plugins.emailPlugin.client.send({
|
|
196
|
+
from: "noreply@example.com",
|
|
197
|
+
to: ["user@example.com"],
|
|
198
|
+
replyTo: "support@example.com",
|
|
199
|
+
bcc: ["admin@example.com"],
|
|
200
|
+
subject: "Support Ticket",
|
|
201
|
+
html: "<p>Your support ticket has been received.</p>"
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Email with Attachments
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
await ctx.plugins.emailPlugin.client.send({
|
|
209
|
+
from: "noreply@example.com",
|
|
210
|
+
to: ["user@example.com"],
|
|
211
|
+
subject: "Invoice",
|
|
212
|
+
html: "<p>Please find your invoice attached.</p>",
|
|
213
|
+
attachments: [
|
|
214
|
+
{
|
|
215
|
+
filename: "invoice.pdf",
|
|
216
|
+
content: Buffer.from("..."), // File content as Buffer or string
|
|
217
|
+
contentType: "application/pdf"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
filename: "logo.png",
|
|
221
|
+
path: "/path/to/logo.png" // Or provide a file path
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## API Reference
|
|
228
|
+
|
|
229
|
+
### Email Type (Generic)
|
|
230
|
+
|
|
231
|
+
The generic email type used by SMTP and basic implementations:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
type email = {
|
|
235
|
+
from: string; // Sender email address
|
|
236
|
+
to: string[]; // Array of recipient email addresses
|
|
237
|
+
replyTo?: string; // Optional reply-to address
|
|
238
|
+
bcc?: string[]; // Optional blind carbon copy recipients
|
|
239
|
+
subject: string; // Email subject line
|
|
240
|
+
attachments?: Attachment[]; // Optional file attachments
|
|
241
|
+
} & ({ text: string } | { html: string }); // Either text or html is required
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Attachment Type
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
interface Attachment {
|
|
248
|
+
content?: string | Buffer | Readable; // File content
|
|
249
|
+
filename?: string | false; // Filename to display
|
|
250
|
+
path?: string | Url; // Path to file on disk
|
|
251
|
+
href?: string; // URL to fetch file from
|
|
252
|
+
httpHeaders?: string; // HTTP headers for href requests
|
|
253
|
+
contentType?: string; // MIME type
|
|
254
|
+
contentDisposition?: 'attachment' | 'inline'; // How to display
|
|
255
|
+
cid?: string; // Content ID for inline images
|
|
256
|
+
encoding?: string; // Encoding (e.g., 'base64')
|
|
257
|
+
headers?: { [key: string]: string | string[] }; // Custom headers
|
|
258
|
+
raw?: string | Buffer; // Raw MIME node content
|
|
85
259
|
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### SendGrid Email Type
|
|
263
|
+
|
|
264
|
+
SendGrid-specific email format (similar to generic but with SendGrid-specific attachment structure):
|
|
86
265
|
|
|
266
|
+
```typescript
|
|
267
|
+
type emailSendgrid = {
|
|
268
|
+
from: string;
|
|
269
|
+
to: string[];
|
|
270
|
+
replyTo?: string;
|
|
271
|
+
bcc?: string[];
|
|
272
|
+
subject: string;
|
|
273
|
+
attachments?: {
|
|
274
|
+
content: string; // Base64 encoded string
|
|
275
|
+
filename: string;
|
|
276
|
+
type?: string;
|
|
277
|
+
disposition?: string;
|
|
278
|
+
}[];
|
|
279
|
+
} & ({ text: string } | { html: string });
|
|
87
280
|
```
|
|
88
281
|
|
|
89
|
-
|
|
282
|
+
### Flowmailer Email Type
|
|
90
283
|
|
|
91
|
-
|
|
284
|
+
Flowmailer-specific email format:
|
|
92
285
|
|
|
286
|
+
```typescript
|
|
287
|
+
type emailFlowmailer = {
|
|
288
|
+
from: string;
|
|
289
|
+
to: string[];
|
|
290
|
+
replyTo?: string;
|
|
291
|
+
bcc?: string[];
|
|
292
|
+
subject: string;
|
|
293
|
+
text?: string;
|
|
294
|
+
html?: string;
|
|
295
|
+
attachments?: {
|
|
296
|
+
content: string; // Base64 encoded content
|
|
297
|
+
contentType: string; // MIME type
|
|
298
|
+
filename: string;
|
|
299
|
+
}[];
|
|
300
|
+
};
|
|
93
301
|
```
|
|
94
302
|
|
|
95
|
-
|
|
96
|
-
from : "from@xxx.com",
|
|
97
|
-
to : ["to@yyy.com"],
|
|
98
|
-
subject : "Hello world",
|
|
99
|
-
text : "Hello world", //Eiter text or html must be specified
|
|
100
|
-
html : "Hello <b>world</b>"
|
|
101
|
-
})
|
|
303
|
+
### Client Interface
|
|
102
304
|
|
|
305
|
+
All email clients implement this unified interface:
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
interface client {
|
|
309
|
+
send(email: email | emailSendgrid | emailFlowmailer): Promise<boolean>;
|
|
310
|
+
}
|
|
103
311
|
```
|
|
312
|
+
|
|
313
|
+
The `send` method returns:
|
|
314
|
+
- `true` - Email sent successfully
|
|
315
|
+
- `false` - Email sending failed (errors are logged to console)
|
|
316
|
+
|
|
317
|
+
## Complete Example
|
|
318
|
+
|
|
319
|
+
Here's a complete example of a handler that sends different types of emails:
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
import { Handler } from "@flink-app/flink";
|
|
323
|
+
import { Ctx } from "../Ctx";
|
|
324
|
+
|
|
325
|
+
interface SendEmailRequest {
|
|
326
|
+
type: "welcome" | "invoice" | "notification";
|
|
327
|
+
recipientEmail: string;
|
|
328
|
+
userName: string;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const SendEmail: Handler<Ctx, SendEmailRequest, { success: boolean }> = async ({ ctx, req }) => {
|
|
332
|
+
const { type, recipientEmail, userName } = req.body;
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
switch (type) {
|
|
336
|
+
case "welcome":
|
|
337
|
+
await ctx.plugins.emailPlugin.client.send({
|
|
338
|
+
from: "welcome@example.com",
|
|
339
|
+
to: [recipientEmail],
|
|
340
|
+
subject: `Welcome ${userName}!`,
|
|
341
|
+
html: `
|
|
342
|
+
<h1>Welcome to our platform, ${userName}!</h1>
|
|
343
|
+
<p>We're excited to have you on board.</p>
|
|
344
|
+
`
|
|
345
|
+
});
|
|
346
|
+
break;
|
|
347
|
+
|
|
348
|
+
case "invoice":
|
|
349
|
+
await ctx.plugins.emailPlugin.client.send({
|
|
350
|
+
from: "billing@example.com",
|
|
351
|
+
to: [recipientEmail],
|
|
352
|
+
replyTo: "support@example.com",
|
|
353
|
+
subject: "Your Invoice",
|
|
354
|
+
html: `<p>Dear ${userName}, your invoice is attached.</p>`,
|
|
355
|
+
attachments: [
|
|
356
|
+
{
|
|
357
|
+
filename: "invoice.pdf",
|
|
358
|
+
content: Buffer.from("..."), // Your PDF content
|
|
359
|
+
contentType: "application/pdf"
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
});
|
|
363
|
+
break;
|
|
364
|
+
|
|
365
|
+
case "notification":
|
|
366
|
+
await ctx.plugins.emailPlugin.client.send({
|
|
367
|
+
from: "notifications@example.com",
|
|
368
|
+
to: [recipientEmail],
|
|
369
|
+
subject: "New Notification",
|
|
370
|
+
text: `Hi ${userName}, you have a new notification.`
|
|
371
|
+
});
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return { data: { success: true } };
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.error("Failed to send email:", error);
|
|
378
|
+
return { data: { success: false } };
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
export default SendEmail;
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Error Handling
|
|
386
|
+
|
|
387
|
+
All email clients handle errors internally and return `false` on failure. Errors are logged to the console with `JSON.stringify(ex)`. For production use, you may want to implement additional error logging or monitoring.
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
const success = await ctx.plugins.emailPlugin.client.send({
|
|
391
|
+
from: "noreply@example.com",
|
|
392
|
+
to: ["user@example.com"],
|
|
393
|
+
subject: "Test",
|
|
394
|
+
text: "Test email"
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
if (!success) {
|
|
398
|
+
// Handle email sending failure
|
|
399
|
+
console.log("Failed to send email");
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## Notes
|
|
404
|
+
|
|
405
|
+
- Either `text` or `html` must be provided (not both required, but at least one)
|
|
406
|
+
- All clients return a boolean indicating success/failure
|
|
407
|
+
- Flowmailer automatically handles OAuth token refresh
|
|
408
|
+
- SendGrid attachments must be base64 encoded
|
|
409
|
+
- SMTP client uses nodemailer internally
|
|
410
|
+
- Multiple recipients are supported by all clients
|