n8n-nodes-mailhooks 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/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # n8n-nodes-mailhooks
2
+
3
+ This is an n8n community node. It lets you use _app/service name_ in your n8n workflows.
4
+
5
+ _App/service name_ is _one or two sentences describing the service this node integrates with_.
6
+
7
+ [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/sustainable-use-license/) workflow automation platform.
8
+
9
+ [Installation](#installation)
10
+ [Operations](#operations)
11
+ [Credentials](#credentials)
12
+ [Compatibility](#compatibility)
13
+ [Usage](#usage)
14
+ [Resources](#resources)
15
+ [Version history](#version-history)
16
+
17
+ ## Installation
18
+
19
+ Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.
20
+
21
+ ## Operations
22
+
23
+ _List the operations supported by your node._
24
+
25
+ ## Credentials
26
+
27
+ _If users need to authenticate with the app/service, provide details here. You should include prerequisites (such as signing up with the service), available authentication methods, and how to set them up._
28
+
29
+ ## Compatibility
30
+
31
+ _State the minimum n8n version, as well as which versions you test against. You can also include any known version incompatibility issues._
32
+
33
+ ## Usage
34
+
35
+ _This is an optional section. Use it to help users with any difficult or confusing aspects of the node._
36
+
37
+ _By the time users are looking for community nodes, they probably already know n8n basics. But if you expect new users, you can link to the [Try it out](https://docs.n8n.io/try-it-out/) documentation to help them get started._
38
+
39
+ ## Resources
40
+
41
+ * [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
42
+ * _Link to app/service documentation._
43
+
44
+ ## Version history
45
+
46
+ _This is another optional section. If your node has multiple versions, include a short description of available versions and what changed, as well as any compatibility impact._
@@ -0,0 +1,10 @@
1
+ import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class MailhooksApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ icon: "file:mailhooks-logo.png";
7
+ properties: INodeProperties[];
8
+ authenticate: IAuthenticateGeneric;
9
+ test: ICredentialTestRequest;
10
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MailhooksApi = void 0;
4
+ class MailhooksApi {
5
+ constructor() {
6
+ this.name = 'mailhooksApi';
7
+ this.displayName = 'Mailhooks API';
8
+ this.documentationUrl = 'https://mailhooks.dev/docs';
9
+ this.icon = 'file:mailhooks-logo.png';
10
+ this.properties = [
11
+ {
12
+ displayName: 'API Key',
13
+ name: 'apiKey',
14
+ type: 'string',
15
+ typeOptions: { password: true },
16
+ default: '',
17
+ required: true,
18
+ description: 'Your Mailhooks API key',
19
+ },
20
+ {
21
+ displayName: 'Base URL',
22
+ name: 'baseUrl',
23
+ type: 'string',
24
+ default: 'https://mailhooks.dev/api',
25
+ description: 'The base URL for the Mailhooks API',
26
+ },
27
+ ];
28
+ this.authenticate = {
29
+ type: 'generic',
30
+ properties: {
31
+ headers: {
32
+ 'x-api-key': '={{$credentials.apiKey}}',
33
+ },
34
+ },
35
+ };
36
+ this.test = {
37
+ request: {
38
+ baseURL: '={{$credentials.baseUrl}}',
39
+ url: '/v1/emails?perPage=1',
40
+ method: 'GET',
41
+ },
42
+ };
43
+ }
44
+ }
45
+ exports.MailhooksApi = MailhooksApi;
46
+ //# sourceMappingURL=MailhooksApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MailhooksApi.credentials.js","sourceRoot":"","sources":["../../credentials/MailhooksApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,YAAY;IAAzB;QACC,SAAI,GAAG,cAAc,CAAC;QACtB,gBAAW,GAAG,eAAe,CAAC;QAC9B,qBAAgB,GAAG,4BAA4B,CAAC;QAChD,SAAI,GAAG,yBAAkC,CAAC;QAC1C,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,wBAAwB;aACrC;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,2BAA2B;gBACpC,WAAW,EAAE,oCAAoC;aACjD;SACD,CAAC;QAEF,iBAAY,GAAyB;YACpC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACX,OAAO,EAAE;oBACR,WAAW,EAAE,0BAA0B;iBACvC;aACD;SACD,CAAC;QAEF,SAAI,GAA2B;YAC9B,OAAO,EAAE;gBACR,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,sBAAsB;gBAC3B,MAAM,EAAE,KAAK;aACb;SACD,CAAC;IACH,CAAC;CAAA;AAxCD,oCAwCC"}
@@ -0,0 +1,5 @@
1
+ import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class Mailhooks implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,554 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Mailhooks = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const sdk_1 = require("@mailhooks/sdk");
6
+ function toDataObject(obj) {
7
+ return JSON.parse(JSON.stringify(obj));
8
+ }
9
+ class Mailhooks {
10
+ constructor() {
11
+ this.description = {
12
+ displayName: 'Mailhooks',
13
+ name: 'mailhooks',
14
+ icon: 'file:mailhooks-logo.png',
15
+ group: ['transform'],
16
+ version: 1,
17
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
18
+ description: 'Interact with the Mailhooks API',
19
+ defaults: {
20
+ name: 'Mailhooks',
21
+ },
22
+ inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
23
+ outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
24
+ usableAsTool: true,
25
+ credentials: [
26
+ {
27
+ name: 'mailhooksApi',
28
+ required: true,
29
+ },
30
+ ],
31
+ properties: [
32
+ {
33
+ displayName: 'Resource',
34
+ name: 'resource',
35
+ type: 'options',
36
+ noDataExpression: true,
37
+ options: [
38
+ {
39
+ name: 'Email',
40
+ value: 'email',
41
+ },
42
+ {
43
+ name: 'Utility',
44
+ value: 'utility',
45
+ },
46
+ ],
47
+ default: 'email',
48
+ },
49
+ {
50
+ displayName: 'Operation',
51
+ name: 'operation',
52
+ type: 'options',
53
+ noDataExpression: true,
54
+ displayOptions: {
55
+ show: {
56
+ resource: ['email'],
57
+ },
58
+ },
59
+ options: [
60
+ {
61
+ name: 'Download Attachment',
62
+ value: 'downloadAttachment',
63
+ description: 'Download a specific attachment',
64
+ action: 'Download attachment',
65
+ },
66
+ {
67
+ name: 'Download EML',
68
+ value: 'downloadEml',
69
+ description: 'Download email in EML format',
70
+ action: 'Download email as EML',
71
+ },
72
+ {
73
+ name: 'Get',
74
+ value: 'get',
75
+ description: 'Get a specific email by ID',
76
+ action: 'Get email',
77
+ },
78
+ {
79
+ name: 'Get Content',
80
+ value: 'getContent',
81
+ description: 'Get the HTML and text content of an email',
82
+ action: 'Get email content',
83
+ },
84
+ {
85
+ name: 'List',
86
+ value: 'list',
87
+ description: 'List emails with optional filters',
88
+ action: 'List emails',
89
+ },
90
+ {
91
+ name: 'Mark as Read',
92
+ value: 'markAsRead',
93
+ description: 'Mark an email as read',
94
+ action: 'Mark email as read',
95
+ },
96
+ {
97
+ name: 'Mark as Unread',
98
+ value: 'markAsUnread',
99
+ description: 'Mark an email as unread',
100
+ action: 'Mark email as unread',
101
+ },
102
+ {
103
+ name: 'Wait For',
104
+ value: 'waitFor',
105
+ description: 'Wait for an email matching filters',
106
+ action: 'Wait for email',
107
+ },
108
+ ],
109
+ default: 'list',
110
+ },
111
+ {
112
+ displayName: 'Operation',
113
+ name: 'operation',
114
+ type: 'options',
115
+ noDataExpression: true,
116
+ displayOptions: {
117
+ show: {
118
+ resource: ['utility'],
119
+ },
120
+ },
121
+ options: [
122
+ {
123
+ name: 'Parse EML',
124
+ value: 'parseEml',
125
+ description: 'Parse raw EML content into structured data',
126
+ action: 'Parse EML file',
127
+ },
128
+ {
129
+ name: 'Verify Webhook',
130
+ value: 'verifyWebhook',
131
+ description: 'Verify a webhook signature',
132
+ action: 'Verify webhook signature',
133
+ },
134
+ ],
135
+ default: 'parseEml',
136
+ },
137
+ {
138
+ displayName: 'Email ID',
139
+ name: 'emailId',
140
+ type: 'string',
141
+ required: true,
142
+ default: '',
143
+ displayOptions: {
144
+ show: {
145
+ resource: ['email'],
146
+ operation: ['get', 'getContent', 'markAsRead', 'markAsUnread', 'downloadEml', 'downloadAttachment'],
147
+ },
148
+ },
149
+ description: 'The ID of the email',
150
+ },
151
+ {
152
+ displayName: 'Attachment ID',
153
+ name: 'attachmentId',
154
+ type: 'string',
155
+ required: true,
156
+ default: '',
157
+ displayOptions: {
158
+ show: {
159
+ resource: ['email'],
160
+ operation: ['downloadAttachment'],
161
+ },
162
+ },
163
+ description: 'The ID of the attachment to download',
164
+ },
165
+ {
166
+ displayName: 'Mark as Read',
167
+ name: 'markAsReadOnGet',
168
+ type: 'boolean',
169
+ default: false,
170
+ displayOptions: {
171
+ show: {
172
+ resource: ['email'],
173
+ operation: ['get'],
174
+ },
175
+ },
176
+ description: 'Whether to mark the email as read when fetching',
177
+ },
178
+ {
179
+ displayName: 'Filters',
180
+ name: 'filters',
181
+ type: 'collection',
182
+ placeholder: 'Add Filter',
183
+ default: {},
184
+ displayOptions: {
185
+ show: {
186
+ resource: ['email'],
187
+ operation: ['list'],
188
+ },
189
+ },
190
+ options: [
191
+ {
192
+ displayName: 'End Date',
193
+ name: 'endDate',
194
+ type: 'dateTime',
195
+ default: '',
196
+ description: 'Filter emails received before this date',
197
+ },
198
+ {
199
+ displayName: 'From',
200
+ name: 'from',
201
+ type: 'string',
202
+ default: '',
203
+ description: 'Filter by sender email address',
204
+ },
205
+ {
206
+ displayName: 'Read Status',
207
+ name: 'read',
208
+ type: 'options',
209
+ options: [
210
+ { name: 'All', value: '' },
211
+ { name: 'Read', value: 'true' },
212
+ { name: 'Unread', value: 'false' },
213
+ ],
214
+ default: '',
215
+ description: 'Filter by read status',
216
+ },
217
+ {
218
+ displayName: 'Start Date',
219
+ name: 'startDate',
220
+ type: 'dateTime',
221
+ default: '',
222
+ description: 'Filter emails received after this date',
223
+ },
224
+ {
225
+ displayName: 'Subject',
226
+ name: 'subject',
227
+ type: 'string',
228
+ default: '',
229
+ description: 'Filter by subject',
230
+ },
231
+ {
232
+ displayName: 'To',
233
+ name: 'to',
234
+ type: 'string',
235
+ default: '',
236
+ description: 'Filter by recipient email address',
237
+ },
238
+ ],
239
+ },
240
+ {
241
+ displayName: 'Options',
242
+ name: 'options',
243
+ type: 'collection',
244
+ placeholder: 'Add Option',
245
+ default: {},
246
+ displayOptions: {
247
+ show: {
248
+ resource: ['email'],
249
+ operation: ['list'],
250
+ },
251
+ },
252
+ options: [
253
+ {
254
+ displayName: 'Page',
255
+ name: 'page',
256
+ type: 'number',
257
+ default: 1,
258
+ description: 'Page number',
259
+ },
260
+ {
261
+ displayName: 'Per Page',
262
+ name: 'perPage',
263
+ type: 'number',
264
+ default: 20,
265
+ description: 'Number of results per page',
266
+ },
267
+ {
268
+ displayName: 'Sort Field',
269
+ name: 'sortField',
270
+ type: 'options',
271
+ options: [
272
+ { name: 'Created At', value: 'createdAt' },
273
+ { name: 'From', value: 'from' },
274
+ { name: 'Subject', value: 'subject' },
275
+ ],
276
+ default: 'createdAt',
277
+ description: 'Field to sort by',
278
+ },
279
+ {
280
+ displayName: 'Sort Order',
281
+ name: 'sortOrder',
282
+ type: 'options',
283
+ options: [
284
+ { name: 'Ascending', value: 'asc' },
285
+ { name: 'Descending', value: 'desc' },
286
+ ],
287
+ default: 'desc',
288
+ },
289
+ ],
290
+ },
291
+ {
292
+ displayName: 'Wait For Filters',
293
+ name: 'waitForFilters',
294
+ type: 'collection',
295
+ placeholder: 'Add Filter',
296
+ default: {},
297
+ displayOptions: {
298
+ show: {
299
+ resource: ['email'],
300
+ operation: ['waitFor'],
301
+ },
302
+ },
303
+ options: [
304
+ {
305
+ displayName: 'From',
306
+ name: 'from',
307
+ type: 'string',
308
+ default: '',
309
+ description: 'Filter by sender email address',
310
+ },
311
+ {
312
+ displayName: 'To',
313
+ name: 'to',
314
+ type: 'string',
315
+ default: '',
316
+ description: 'Filter by recipient email address',
317
+ },
318
+ {
319
+ displayName: 'Subject',
320
+ name: 'subject',
321
+ type: 'string',
322
+ default: '',
323
+ description: 'Filter by subject',
324
+ },
325
+ ],
326
+ },
327
+ {
328
+ displayName: 'Wait For Options',
329
+ name: 'waitForOptions',
330
+ type: 'collection',
331
+ placeholder: 'Add Option',
332
+ default: {},
333
+ displayOptions: {
334
+ show: {
335
+ resource: ['email'],
336
+ operation: ['waitFor'],
337
+ },
338
+ },
339
+ options: [
340
+ {
341
+ displayName: 'Timeout (Ms)',
342
+ name: 'timeout',
343
+ type: 'number',
344
+ default: 30000,
345
+ description: 'Maximum time to wait for email (in milliseconds)',
346
+ },
347
+ {
348
+ displayName: 'Poll Interval (Ms)',
349
+ name: 'pollInterval',
350
+ type: 'number',
351
+ default: 1000,
352
+ description: 'How often to check for new emails (in milliseconds)',
353
+ },
354
+ {
355
+ displayName: 'Lookback Window (Ms)',
356
+ name: 'lookbackWindow',
357
+ type: 'number',
358
+ default: 10000,
359
+ description: 'Only consider emails from this time window (in milliseconds)',
360
+ },
361
+ ],
362
+ },
363
+ {
364
+ displayName: 'EML Content',
365
+ name: 'emlContent',
366
+ type: 'string',
367
+ typeOptions: {
368
+ rows: 10,
369
+ },
370
+ required: true,
371
+ default: '',
372
+ displayOptions: {
373
+ show: {
374
+ resource: ['utility'],
375
+ operation: ['parseEml'],
376
+ },
377
+ },
378
+ description: 'The raw EML content to parse',
379
+ },
380
+ {
381
+ displayName: 'Payload',
382
+ name: 'webhookPayload',
383
+ type: 'string',
384
+ typeOptions: {
385
+ rows: 5,
386
+ },
387
+ required: true,
388
+ default: '',
389
+ displayOptions: {
390
+ show: {
391
+ resource: ['utility'],
392
+ operation: ['verifyWebhook'],
393
+ },
394
+ },
395
+ description: 'The raw webhook payload body',
396
+ },
397
+ {
398
+ displayName: 'Signature',
399
+ name: 'webhookSignature',
400
+ type: 'string',
401
+ required: true,
402
+ default: '',
403
+ displayOptions: {
404
+ show: {
405
+ resource: ['utility'],
406
+ operation: ['verifyWebhook'],
407
+ },
408
+ },
409
+ description: 'The X-Webhook-Signature header value',
410
+ },
411
+ {
412
+ displayName: 'Webhook Secret',
413
+ name: 'webhookSecret',
414
+ type: 'string',
415
+ typeOptions: { password: true },
416
+ required: true,
417
+ default: '',
418
+ displayOptions: {
419
+ show: {
420
+ resource: ['utility'],
421
+ operation: ['verifyWebhook'],
422
+ },
423
+ },
424
+ description: 'Your webhook secret (starts with whsec_)',
425
+ },
426
+ ],
427
+ };
428
+ }
429
+ async execute() {
430
+ const items = this.getInputData();
431
+ const returnData = [];
432
+ const resource = this.getNodeParameter('resource', 0);
433
+ const operation = this.getNodeParameter('operation', 0);
434
+ const credentials = await this.getCredentials('mailhooksApi');
435
+ const mailhooks = new sdk_1.Mailhooks({
436
+ apiKey: credentials.apiKey,
437
+ baseUrl: credentials.baseUrl,
438
+ });
439
+ for (let i = 0; i < items.length; i++) {
440
+ try {
441
+ if (resource === 'email') {
442
+ if (operation === 'list') {
443
+ const filters = this.getNodeParameter('filters', i, {});
444
+ const options = this.getNodeParameter('options', i, {});
445
+ const response = await mailhooks.emails.list({
446
+ filter: {
447
+ from: filters.from,
448
+ to: filters.to,
449
+ subject: filters.subject,
450
+ read: filters.read ? filters.read === 'true' : undefined,
451
+ startDate: filters.startDate,
452
+ endDate: filters.endDate,
453
+ },
454
+ page: options.page,
455
+ perPage: options.perPage,
456
+ sort: options.sortField
457
+ ? { field: options.sortField, order: options.sortOrder }
458
+ : undefined,
459
+ });
460
+ returnData.push({
461
+ json: toDataObject(response),
462
+ });
463
+ }
464
+ else if (operation === 'get') {
465
+ const emailId = this.getNodeParameter('emailId', i);
466
+ const markAsRead = this.getNodeParameter('markAsReadOnGet', i);
467
+ const email = await mailhooks.emails.getEmail(emailId, markAsRead);
468
+ returnData.push({ json: toDataObject(email) });
469
+ }
470
+ else if (operation === 'getContent') {
471
+ const emailId = this.getNodeParameter('emailId', i);
472
+ const content = await mailhooks.emails.getContent(emailId);
473
+ returnData.push({ json: toDataObject(content) });
474
+ }
475
+ else if (operation === 'markAsRead') {
476
+ const emailId = this.getNodeParameter('emailId', i);
477
+ const email = await mailhooks.emails.markAsRead(emailId);
478
+ returnData.push({ json: toDataObject(email) });
479
+ }
480
+ else if (operation === 'markAsUnread') {
481
+ const emailId = this.getNodeParameter('emailId', i);
482
+ const email = await mailhooks.emails.markAsUnread(emailId);
483
+ returnData.push({ json: toDataObject(email) });
484
+ }
485
+ else if (operation === 'downloadEml') {
486
+ const emailId = this.getNodeParameter('emailId', i);
487
+ const response = await mailhooks.emails.downloadEml(emailId);
488
+ returnData.push({
489
+ json: {
490
+ filename: response.filename,
491
+ contentType: response.contentType,
492
+ },
493
+ binary: {
494
+ data: await this.helpers.prepareBinaryData(Buffer.from(response.data), response.filename || `email-${emailId}.eml`, response.contentType || 'message/rfc822'),
495
+ },
496
+ });
497
+ }
498
+ else if (operation === 'downloadAttachment') {
499
+ const emailId = this.getNodeParameter('emailId', i);
500
+ const attachmentId = this.getNodeParameter('attachmentId', i);
501
+ const response = await mailhooks.emails.downloadAttachment(emailId, attachmentId);
502
+ returnData.push({
503
+ json: {
504
+ filename: response.filename,
505
+ contentType: response.contentType,
506
+ },
507
+ binary: {
508
+ data: await this.helpers.prepareBinaryData(Buffer.from(response.data), response.filename || `attachment-${attachmentId}`, response.contentType || 'application/octet-stream'),
509
+ },
510
+ });
511
+ }
512
+ else if (operation === 'waitFor') {
513
+ const filters = this.getNodeParameter('waitForFilters', i, {});
514
+ const options = this.getNodeParameter('waitForOptions', i, {});
515
+ const email = await mailhooks.emails.waitFor({
516
+ filter: filters,
517
+ timeout: options.timeout,
518
+ pollInterval: options.pollInterval,
519
+ lookbackWindow: options.lookbackWindow,
520
+ });
521
+ returnData.push({ json: toDataObject(email) });
522
+ }
523
+ }
524
+ else if (resource === 'utility') {
525
+ if (operation === 'parseEml') {
526
+ const emlContent = this.getNodeParameter('emlContent', i);
527
+ const parsed = await (0, sdk_1.parseEml)(emlContent);
528
+ returnData.push({ json: toDataObject(parsed) });
529
+ }
530
+ else if (operation === 'verifyWebhook') {
531
+ const payload = this.getNodeParameter('webhookPayload', i);
532
+ const signature = this.getNodeParameter('webhookSignature', i);
533
+ const secret = this.getNodeParameter('webhookSecret', i);
534
+ const isValid = (0, sdk_1.verifyWebhookSignature)(payload, signature, secret);
535
+ returnData.push({ json: { valid: isValid } });
536
+ }
537
+ }
538
+ }
539
+ catch (error) {
540
+ if (this.continueOnFail()) {
541
+ returnData.push({
542
+ json: { error: error.message },
543
+ pairedItem: { item: i },
544
+ });
545
+ continue;
546
+ }
547
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
548
+ }
549
+ }
550
+ return [returnData];
551
+ }
552
+ }
553
+ exports.Mailhooks = Mailhooks;
554
+ //# sourceMappingURL=Mailhooks.node.js.map