@flink-app/email-plugin 0.12.1-alpha.4 → 0.12.1-alpha.44

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.
Files changed (2) hide show
  1. package/package.json +4 -4
  2. package/readme.md +352 -45
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@flink-app/email-plugin",
3
- "version": "0.12.1-alpha.4",
3
+ "version": "0.12.1-alpha.44",
4
4
  "description": "Flink plugin that makes it possible to send email",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\"",
7
- "prepublish": "tsc"
7
+ "prepare": "tsc"
8
8
  },
9
9
  "author": "johan@frost.se",
10
10
  "publishConfig": {
@@ -21,10 +21,10 @@
21
21
  "querystring": "^0.2.1"
22
22
  },
23
23
  "devDependencies": {
24
- "@flink-app/flink": "^0.12.1-alpha.4",
24
+ "@flink-app/flink": "^0.12.1-alpha.44",
25
25
  "@types/node": "22.13.10",
26
26
  "ts-node": "^9.1.1",
27
27
  "typescript": "5.4.5"
28
28
  },
29
- "gitHead": "8f06a4ab98e7179322d36546756d4305fd196f5a"
29
+ "gitHead": "4243e3b3cd6d4e1ca001a61baa8436bf2bbe4113"
30
30
  }
package/readme.md CHANGED
@@ -1,103 +1,410 @@
1
- # Flink API Docs
1
+ # Email Plugin
2
2
 
3
- A FLINK plugin that makes it possible to send email.
3
+ A Flink plugin that provides multi-provider email sending capabilities with support for SendGrid, SMTP, and Flowmailer.
4
4
 
5
- ## Usage
5
+ ## Installation
6
6
 
7
- Install plugin to your flink app project:
7
+ Install the plugin to your Flink app project:
8
8
 
9
- ```
10
- npm i -S @flink-app/email-plugin
9
+ ```bash
10
+ npm install @flink-app/email-plugin
11
11
  ```
12
12
 
13
- ## Setup
13
+ ## Configuration
14
14
 
15
- ### With Sendgrid
15
+ The email plugin supports three different email providers through a unified client interface:
16
16
 
17
- Add and configure plugin in your app startup (probable the `index.ts` in root project):
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
- // Register plugin
27
- emailPlugin({
28
- client : new sendgridClient({
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
- Add plugin ctx to `Ctx.ts` in root project
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
- export interface Ctx extends FlinkContext<emailPluginContext> {
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
- ### With SMTP
88
+ ### Option 3: Flowmailer
50
89
 
51
- Add and configure plugin in your app startup (probable the `index.ts` in root project):
90
+ Configure the plugin with Flowmailer:
52
91
 
53
- ```
54
- import { emailPlugin, smtpClient } from "@flink-app/email-plugin";
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
- // Register plugin
61
- emailPlugin({
62
- client : new smtpClient({
63
- host: "XYZ",
64
- port: 587,
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
- Add plugin ctx to `Ctx.ts` in root project
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
- ## Send email
282
+ ### Flowmailer Email Type
90
283
 
91
- Send email from your handlers by using the the context
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
- await ctx.plugins.emailPlugin.client.send({
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