n8n-nodes-cakemail 1.0.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.
@@ -0,0 +1,8 @@
1
+ import { ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class CakemailApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ properties: INodeProperties[];
7
+ test: ICredentialTestRequest;
8
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CakemailApi = void 0;
4
+ class CakemailApi {
5
+ name = 'cakemailApi';
6
+ displayName = 'Cakemail API';
7
+ documentationUrl = 'https://docs.cakemail.com';
8
+ properties = [
9
+ {
10
+ displayName: 'Email',
11
+ name: 'email',
12
+ type: 'string',
13
+ typeOptions: {
14
+ password: false,
15
+ },
16
+ default: '',
17
+ required: true,
18
+ placeholder: 'your-email@example.com',
19
+ description: 'Your Cakemail account email address',
20
+ },
21
+ {
22
+ displayName: 'Password',
23
+ name: 'password',
24
+ type: 'string',
25
+ typeOptions: {
26
+ password: true,
27
+ },
28
+ default: '',
29
+ required: true,
30
+ description: 'Your Cakemail account password',
31
+ },
32
+ {
33
+ displayName: 'API Base URL',
34
+ name: 'baseURL',
35
+ type: 'string',
36
+ default: 'https://api.cakemail.dev',
37
+ required: false,
38
+ description: 'Cakemail API base URL (advanced users only)',
39
+ },
40
+ ];
41
+ test = {
42
+ request: {
43
+ baseURL: '={{$credentials.baseURL || "https://api.cakemail.dev"}}',
44
+ url: '/accounts/self',
45
+ method: 'GET',
46
+ },
47
+ };
48
+ }
49
+ exports.CakemailApi = CakemailApi;
@@ -0,0 +1,2 @@
1
+ // Type definitions entry point
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ // Export nothing - n8n will auto-discover nodes from package.json
2
+ // This file exists to satisfy n8n's requirement for an entry point
@@ -0,0 +1,9 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class Cakemail implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ private executeAccountOperation;
6
+ private executeContactOperation;
7
+ private executeListOperation;
8
+ private executeCampaignOperation;
9
+ }
@@ -0,0 +1,1221 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Cakemail = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const sdk_1 = require("@cakemail/sdk");
6
+ class Cakemail {
7
+ description = {
8
+ displayName: 'Cakemail',
9
+ name: 'cakemail',
10
+ icon: 'file:cakemail.svg',
11
+ group: ['transform'],
12
+ version: 1,
13
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
14
+ description: 'Interact with Cakemail API',
15
+ defaults: {
16
+ name: 'Cakemail',
17
+ },
18
+ inputs: ['main'],
19
+ outputs: ['main'],
20
+ credentials: [
21
+ {
22
+ name: 'cakemailApi',
23
+ required: true,
24
+ },
25
+ ],
26
+ properties: [
27
+ {
28
+ displayName: 'Resource',
29
+ name: 'resource',
30
+ type: 'options',
31
+ noDataExpression: true,
32
+ options: [
33
+ {
34
+ name: 'Account',
35
+ value: 'account',
36
+ },
37
+ {
38
+ name: 'Contact',
39
+ value: 'contact',
40
+ },
41
+ {
42
+ name: 'List',
43
+ value: 'list',
44
+ },
45
+ {
46
+ name: 'Campaign',
47
+ value: 'campaign',
48
+ },
49
+ ],
50
+ default: 'account',
51
+ required: true,
52
+ description: 'The resource to operate on',
53
+ },
54
+ {
55
+ displayName: 'Operation',
56
+ name: 'operation',
57
+ type: 'options',
58
+ noDataExpression: true,
59
+ displayOptions: {
60
+ show: {
61
+ resource: ['account'],
62
+ },
63
+ },
64
+ options: [
65
+ {
66
+ name: 'Get',
67
+ value: 'get',
68
+ description: 'Get current account information',
69
+ action: 'Get current account',
70
+ },
71
+ {
72
+ name: 'Get By ID',
73
+ value: 'getById',
74
+ description: 'Get account by ID',
75
+ action: 'Get account by ID',
76
+ },
77
+ {
78
+ name: 'List',
79
+ value: 'list',
80
+ description: 'List accounts',
81
+ action: 'List accounts',
82
+ },
83
+ {
84
+ name: 'Create',
85
+ value: 'create',
86
+ description: 'Create a new account',
87
+ action: 'Create account',
88
+ },
89
+ {
90
+ name: 'Update',
91
+ value: 'update',
92
+ description: 'Update an account',
93
+ action: 'Update account',
94
+ },
95
+ {
96
+ name: 'Delete',
97
+ value: 'delete',
98
+ description: 'Delete an account',
99
+ action: 'Delete account',
100
+ },
101
+ {
102
+ name: 'Get Children',
103
+ value: 'getChildren',
104
+ description: 'Get child accounts of a parent account',
105
+ action: 'Get child accounts',
106
+ },
107
+ ],
108
+ default: 'get',
109
+ required: true,
110
+ },
111
+ {
112
+ displayName: 'Operation',
113
+ name: 'operation',
114
+ type: 'options',
115
+ noDataExpression: true,
116
+ displayOptions: {
117
+ show: {
118
+ resource: ['contact'],
119
+ },
120
+ },
121
+ options: [
122
+ {
123
+ name: 'Get',
124
+ value: 'get',
125
+ description: 'Get a contact by ID',
126
+ action: 'Get contact',
127
+ },
128
+ {
129
+ name: 'Get By Email',
130
+ value: 'getByEmail',
131
+ description: 'Get a contact by email',
132
+ action: 'Get contact by email',
133
+ },
134
+ {
135
+ name: 'List',
136
+ value: 'list',
137
+ description: 'List contacts',
138
+ action: 'List contacts',
139
+ },
140
+ {
141
+ name: 'Create',
142
+ value: 'create',
143
+ description: 'Create a new contact',
144
+ action: 'Create contact',
145
+ },
146
+ {
147
+ name: 'Update',
148
+ value: 'update',
149
+ description: 'Update a contact',
150
+ action: 'Update contact',
151
+ },
152
+ {
153
+ name: 'Delete',
154
+ value: 'delete',
155
+ description: 'Delete a contact',
156
+ action: 'Delete contact',
157
+ },
158
+ {
159
+ name: 'Subscribe',
160
+ value: 'subscribe',
161
+ description: 'Subscribe a contact to a list',
162
+ action: 'Subscribe contact to list',
163
+ },
164
+ {
165
+ name: 'Unsubscribe',
166
+ value: 'unsubscribe',
167
+ description: 'Unsubscribe a contact from a list',
168
+ action: 'Unsubscribe contact from list',
169
+ },
170
+ {
171
+ name: 'Bulk Import',
172
+ value: 'bulkImport',
173
+ description: 'Import multiple contacts at once',
174
+ action: 'Bulk import contacts',
175
+ },
176
+ {
177
+ name: 'Search',
178
+ value: 'search',
179
+ description: 'Advanced search with custom field filtering',
180
+ action: 'Search contacts',
181
+ },
182
+ {
183
+ name: 'Add Tags',
184
+ value: 'addTags',
185
+ description: 'Add tags to a contact',
186
+ action: 'Add tags to contact',
187
+ },
188
+ {
189
+ name: 'Remove Tags',
190
+ value: 'removeTags',
191
+ description: 'Remove tags from a contact',
192
+ action: 'Remove tags from contact',
193
+ },
194
+ ],
195
+ default: 'get',
196
+ required: true,
197
+ },
198
+ {
199
+ displayName: 'Operation',
200
+ name: 'operation',
201
+ type: 'options',
202
+ noDataExpression: true,
203
+ displayOptions: {
204
+ show: {
205
+ resource: ['list'],
206
+ },
207
+ },
208
+ options: [
209
+ {
210
+ name: 'Get',
211
+ value: 'get',
212
+ description: 'Get a list by ID',
213
+ action: 'Get list',
214
+ },
215
+ {
216
+ name: 'List',
217
+ value: 'list',
218
+ description: 'List all lists',
219
+ action: 'List lists',
220
+ },
221
+ {
222
+ name: 'Create',
223
+ value: 'create',
224
+ description: 'Create a new list',
225
+ action: 'Create list',
226
+ },
227
+ {
228
+ name: 'Update',
229
+ value: 'update',
230
+ description: 'Update a list',
231
+ action: 'Update list',
232
+ },
233
+ {
234
+ name: 'Delete',
235
+ value: 'delete',
236
+ description: 'Delete a list',
237
+ action: 'Delete list',
238
+ },
239
+ {
240
+ name: 'Get Statistics',
241
+ value: 'getStatistics',
242
+ description: 'Get list statistics',
243
+ action: 'Get list statistics',
244
+ },
245
+ ],
246
+ default: 'get',
247
+ required: true,
248
+ },
249
+ {
250
+ displayName: 'Operation',
251
+ name: 'operation',
252
+ type: 'options',
253
+ noDataExpression: true,
254
+ displayOptions: {
255
+ show: {
256
+ resource: ['campaign'],
257
+ },
258
+ },
259
+ options: [
260
+ {
261
+ name: 'Get',
262
+ value: 'get',
263
+ description: 'Get a campaign by ID',
264
+ action: 'Get campaign',
265
+ },
266
+ {
267
+ name: 'List',
268
+ value: 'list',
269
+ description: 'List campaigns',
270
+ action: 'List campaigns',
271
+ },
272
+ {
273
+ name: 'Create',
274
+ value: 'create',
275
+ description: 'Create a new campaign',
276
+ action: 'Create campaign',
277
+ },
278
+ {
279
+ name: 'Update',
280
+ value: 'update',
281
+ description: 'Update a campaign',
282
+ action: 'Update campaign',
283
+ },
284
+ {
285
+ name: 'Delete',
286
+ value: 'delete',
287
+ description: 'Delete a campaign',
288
+ action: 'Delete campaign',
289
+ },
290
+ {
291
+ name: 'Send',
292
+ value: 'send',
293
+ description: 'Send a campaign',
294
+ action: 'Send campaign',
295
+ },
296
+ ],
297
+ default: 'get',
298
+ required: true,
299
+ },
300
+ {
301
+ displayName: 'Account ID',
302
+ name: 'accountId',
303
+ type: 'number',
304
+ default: undefined,
305
+ required: false,
306
+ description: 'Optional: Specify an account ID for multi-tenant operations. If not provided, uses the authenticated account.',
307
+ },
308
+ {
309
+ displayName: 'Target Account ID',
310
+ name: 'targetAccountId',
311
+ type: 'number',
312
+ required: true,
313
+ displayOptions: {
314
+ show: {
315
+ resource: ['account'],
316
+ operation: ['getById', 'update', 'delete', 'getChildren'],
317
+ },
318
+ },
319
+ default: 0,
320
+ description: 'The ID of the account to operate on',
321
+ },
322
+ {
323
+ displayName: 'Name',
324
+ name: 'accountName',
325
+ type: 'string',
326
+ required: true,
327
+ displayOptions: {
328
+ show: {
329
+ resource: ['account'],
330
+ operation: ['create'],
331
+ },
332
+ },
333
+ default: '',
334
+ description: 'Account name',
335
+ },
336
+ {
337
+ displayName: 'Email',
338
+ name: 'accountEmail',
339
+ type: 'string',
340
+ required: true,
341
+ displayOptions: {
342
+ show: {
343
+ resource: ['account'],
344
+ operation: ['create'],
345
+ },
346
+ },
347
+ default: '',
348
+ description: 'Primary email for the account',
349
+ },
350
+ {
351
+ displayName: 'Additional Fields',
352
+ name: 'accountAdditionalFields',
353
+ type: 'collection',
354
+ placeholder: 'Add Field',
355
+ default: {},
356
+ displayOptions: {
357
+ show: {
358
+ resource: ['account'],
359
+ operation: ['create', 'update'],
360
+ },
361
+ },
362
+ options: [
363
+ {
364
+ displayName: 'Name',
365
+ name: 'name',
366
+ type: 'string',
367
+ default: '',
368
+ description: 'Account name (for update)',
369
+ },
370
+ {
371
+ displayName: 'Primary Email',
372
+ name: 'primary_email',
373
+ type: 'string',
374
+ default: '',
375
+ description: 'Primary email (for update)',
376
+ },
377
+ {
378
+ displayName: 'Account Type',
379
+ name: 'account_type',
380
+ type: 'options',
381
+ options: [
382
+ { name: 'Parent', value: 'parent' },
383
+ { name: 'Child', value: 'child' },
384
+ { name: 'Standalone', value: 'standalone' },
385
+ ],
386
+ default: 'standalone',
387
+ description: 'Type of account',
388
+ },
389
+ {
390
+ displayName: 'Parent Account ID',
391
+ name: 'parent_account_id',
392
+ type: 'number',
393
+ default: 0,
394
+ description: 'Parent account ID (for child accounts)',
395
+ },
396
+ {
397
+ displayName: 'Status',
398
+ name: 'status',
399
+ type: 'options',
400
+ options: [
401
+ { name: 'Active', value: 'active' },
402
+ { name: 'Suspended', value: 'suspended' },
403
+ { name: 'Deleted', value: 'deleted' },
404
+ ],
405
+ default: 'active',
406
+ description: 'Account status (for update)',
407
+ },
408
+ {
409
+ displayName: 'Language',
410
+ name: 'language',
411
+ type: 'string',
412
+ default: 'en',
413
+ description: 'Account language (e.g., en, fr)',
414
+ },
415
+ {
416
+ displayName: 'Timezone',
417
+ name: 'timezone',
418
+ type: 'string',
419
+ default: 'America/New_York',
420
+ description: 'Account timezone',
421
+ },
422
+ {
423
+ displayName: 'Currency',
424
+ name: 'currency',
425
+ type: 'string',
426
+ default: 'USD',
427
+ description: 'Account currency',
428
+ },
429
+ {
430
+ displayName: 'Settings',
431
+ name: 'settings',
432
+ type: 'json',
433
+ default: '{}',
434
+ description: 'Account settings as JSON object',
435
+ },
436
+ ],
437
+ },
438
+ {
439
+ displayName: 'Options',
440
+ name: 'getChildrenOptions',
441
+ type: 'collection',
442
+ placeholder: 'Add Option',
443
+ default: {},
444
+ displayOptions: {
445
+ show: {
446
+ resource: ['account'],
447
+ operation: ['getChildren'],
448
+ },
449
+ },
450
+ options: [
451
+ {
452
+ displayName: 'Page',
453
+ name: 'page',
454
+ type: 'number',
455
+ default: 1,
456
+ description: 'Page number',
457
+ },
458
+ {
459
+ displayName: 'Per Page',
460
+ name: 'per_page',
461
+ type: 'number',
462
+ default: 20,
463
+ description: 'Results per page',
464
+ },
465
+ ],
466
+ },
467
+ {
468
+ displayName: 'Filters',
469
+ name: 'accountFilters',
470
+ type: 'collection',
471
+ placeholder: 'Add Filter',
472
+ default: {},
473
+ displayOptions: {
474
+ show: {
475
+ resource: ['account'],
476
+ operation: ['list'],
477
+ },
478
+ },
479
+ options: [
480
+ {
481
+ displayName: 'Status',
482
+ name: 'status',
483
+ type: 'options',
484
+ options: [
485
+ { name: 'Active', value: 'active' },
486
+ { name: 'Suspended', value: 'suspended' },
487
+ { name: 'Deleted', value: 'deleted' },
488
+ ],
489
+ default: 'active',
490
+ description: 'Filter by account status',
491
+ },
492
+ {
493
+ displayName: 'Account Type',
494
+ name: 'account_type',
495
+ type: 'options',
496
+ options: [
497
+ { name: 'Parent', value: 'parent' },
498
+ { name: 'Child', value: 'child' },
499
+ { name: 'Standalone', value: 'standalone' },
500
+ ],
501
+ default: 'standalone',
502
+ description: 'Filter by account type',
503
+ },
504
+ {
505
+ displayName: 'Parent Account ID',
506
+ name: 'parent_account_id',
507
+ type: 'number',
508
+ default: 0,
509
+ description: 'Filter by parent account ID',
510
+ },
511
+ {
512
+ displayName: 'Search',
513
+ name: 'search',
514
+ type: 'string',
515
+ default: '',
516
+ description: 'Search by name or email',
517
+ },
518
+ {
519
+ displayName: 'Page',
520
+ name: 'page',
521
+ type: 'number',
522
+ default: 1,
523
+ description: 'Page number',
524
+ },
525
+ {
526
+ displayName: 'Per Page',
527
+ name: 'per_page',
528
+ type: 'number',
529
+ default: 20,
530
+ description: 'Results per page',
531
+ },
532
+ ],
533
+ },
534
+ {
535
+ displayName: 'List ID',
536
+ name: 'listIdParam',
537
+ type: 'number',
538
+ required: true,
539
+ displayOptions: {
540
+ show: {
541
+ resource: ['list'],
542
+ operation: ['get', 'update', 'delete', 'getStatistics'],
543
+ },
544
+ },
545
+ default: 0,
546
+ description: 'The ID of the list',
547
+ },
548
+ {
549
+ displayName: 'Name',
550
+ name: 'listName',
551
+ type: 'string',
552
+ required: true,
553
+ displayOptions: {
554
+ show: {
555
+ resource: ['list'],
556
+ operation: ['create'],
557
+ },
558
+ },
559
+ default: '',
560
+ description: 'The name of the list',
561
+ },
562
+ {
563
+ displayName: 'Additional Fields',
564
+ name: 'listAdditionalFields',
565
+ type: 'collection',
566
+ placeholder: 'Add Field',
567
+ default: {},
568
+ displayOptions: {
569
+ show: {
570
+ resource: ['list'],
571
+ operation: ['create', 'update'],
572
+ },
573
+ },
574
+ options: [
575
+ {
576
+ displayName: 'Description',
577
+ name: 'description',
578
+ type: 'string',
579
+ default: '',
580
+ description: 'List description',
581
+ },
582
+ {
583
+ displayName: 'Sender Name',
584
+ name: 'sender_name',
585
+ type: 'string',
586
+ default: '',
587
+ description: 'Default sender name for campaigns',
588
+ },
589
+ {
590
+ displayName: 'Sender Email',
591
+ name: 'sender_email',
592
+ type: 'string',
593
+ default: '',
594
+ description: 'Default sender email for campaigns',
595
+ },
596
+ {
597
+ displayName: 'Language',
598
+ name: 'language',
599
+ type: 'string',
600
+ default: 'en',
601
+ description: 'Default language',
602
+ },
603
+ ],
604
+ },
605
+ {
606
+ displayName: 'Limit',
607
+ name: 'listLimit',
608
+ type: 'number',
609
+ displayOptions: {
610
+ show: {
611
+ resource: ['list'],
612
+ operation: ['list'],
613
+ },
614
+ },
615
+ default: 20,
616
+ description: 'Number of results to return',
617
+ },
618
+ {
619
+ displayName: 'Contact ID',
620
+ name: 'contactId',
621
+ type: 'number',
622
+ required: true,
623
+ displayOptions: {
624
+ show: {
625
+ resource: ['contact'],
626
+ operation: ['get', 'update', 'delete', 'subscribe', 'unsubscribe', 'addTags', 'removeTags'],
627
+ },
628
+ },
629
+ default: 0,
630
+ description: 'The ID of the contact',
631
+ },
632
+ {
633
+ displayName: 'Tags',
634
+ name: 'tagsToManage',
635
+ type: 'string',
636
+ required: true,
637
+ displayOptions: {
638
+ show: {
639
+ resource: ['contact'],
640
+ operation: ['addTags', 'removeTags'],
641
+ },
642
+ },
643
+ default: '',
644
+ placeholder: 'vip, newsletter, customer',
645
+ description: 'Comma-separated list of tags',
646
+ },
647
+ {
648
+ displayName: 'Email',
649
+ name: 'email',
650
+ type: 'string',
651
+ required: true,
652
+ displayOptions: {
653
+ show: {
654
+ resource: ['contact'],
655
+ operation: ['getByEmail', 'create'],
656
+ },
657
+ },
658
+ default: '',
659
+ placeholder: 'user@example.com',
660
+ description: 'The email address of the contact',
661
+ },
662
+ {
663
+ displayName: 'Additional Fields',
664
+ name: 'additionalFields',
665
+ type: 'collection',
666
+ placeholder: 'Add Field',
667
+ default: {},
668
+ displayOptions: {
669
+ show: {
670
+ resource: ['contact'],
671
+ operation: ['create', 'update'],
672
+ },
673
+ },
674
+ options: [
675
+ {
676
+ displayName: 'First Name',
677
+ name: 'first_name',
678
+ type: 'string',
679
+ default: '',
680
+ description: 'First name of the contact',
681
+ },
682
+ {
683
+ displayName: 'Last Name',
684
+ name: 'last_name',
685
+ type: 'string',
686
+ default: '',
687
+ description: 'Last name of the contact',
688
+ },
689
+ {
690
+ displayName: 'Company',
691
+ name: 'company',
692
+ type: 'string',
693
+ default: '',
694
+ description: 'Company name',
695
+ },
696
+ {
697
+ displayName: 'Phone',
698
+ name: 'phone',
699
+ type: 'string',
700
+ default: '',
701
+ description: 'Phone number',
702
+ },
703
+ {
704
+ displayName: 'Mobile',
705
+ name: 'mobile',
706
+ type: 'string',
707
+ default: '',
708
+ description: 'Mobile phone number',
709
+ },
710
+ {
711
+ displayName: 'Language',
712
+ name: 'language',
713
+ type: 'string',
714
+ default: 'en',
715
+ description: 'Preferred language (e.g., en, fr)',
716
+ },
717
+ {
718
+ displayName: 'Timezone',
719
+ name: 'timezone',
720
+ type: 'string',
721
+ default: 'UTC',
722
+ description: 'Timezone (e.g., America/New_York, Europe/Paris)',
723
+ },
724
+ {
725
+ displayName: 'Tags',
726
+ name: 'tags',
727
+ type: 'string',
728
+ default: '',
729
+ description: 'Comma-separated list of tags',
730
+ },
731
+ {
732
+ displayName: 'Custom Attributes',
733
+ name: 'custom_attributes',
734
+ type: 'json',
735
+ default: '{}',
736
+ description: 'JSON object with custom attribute key-value pairs',
737
+ },
738
+ {
739
+ displayName: 'List ID',
740
+ name: 'list_id',
741
+ type: 'number',
742
+ default: 0,
743
+ description: 'Subscribe contact to this list immediately (create only)',
744
+ },
745
+ ],
746
+ },
747
+ {
748
+ displayName: 'List ID',
749
+ name: 'listId',
750
+ type: 'number',
751
+ required: true,
752
+ displayOptions: {
753
+ show: {
754
+ resource: ['contact'],
755
+ operation: ['subscribe', 'unsubscribe'],
756
+ },
757
+ },
758
+ default: 0,
759
+ description: 'The ID of the list',
760
+ },
761
+ {
762
+ displayName: 'Contacts',
763
+ name: 'contacts',
764
+ type: 'json',
765
+ required: true,
766
+ displayOptions: {
767
+ show: {
768
+ resource: ['contact'],
769
+ operation: ['bulkImport'],
770
+ },
771
+ },
772
+ default: '[]',
773
+ description: 'Array of contact objects to import. Each contact should have at least an email field.',
774
+ placeholder: '[{"email": "user1@example.com", "first_name": "John"}, {"email": "user2@example.com"}]',
775
+ },
776
+ {
777
+ displayName: 'Search Query',
778
+ name: 'searchQuery',
779
+ type: 'json',
780
+ required: true,
781
+ displayOptions: {
782
+ show: {
783
+ resource: ['contact'],
784
+ operation: ['search'],
785
+ },
786
+ },
787
+ default: '{}',
788
+ description: 'Advanced search query with filters and operators',
789
+ placeholder: '{"custom_attributes.company": "Acme", "tags": ["vip"], "status": "active"}',
790
+ },
791
+ {
792
+ displayName: 'Search Options',
793
+ name: 'searchOptions',
794
+ type: 'collection',
795
+ placeholder: 'Add Option',
796
+ default: {},
797
+ displayOptions: {
798
+ show: {
799
+ resource: ['contact'],
800
+ operation: ['search'],
801
+ },
802
+ },
803
+ options: [
804
+ {
805
+ displayName: 'Page',
806
+ name: 'page',
807
+ type: 'number',
808
+ default: 1,
809
+ description: 'Page number',
810
+ },
811
+ {
812
+ displayName: 'Per Page',
813
+ name: 'per_page',
814
+ type: 'number',
815
+ default: 20,
816
+ description: 'Results per page',
817
+ },
818
+ {
819
+ displayName: 'Sort By',
820
+ name: 'sort',
821
+ type: 'string',
822
+ default: 'created_on',
823
+ description: 'Field to sort by (e.g., created_on, email, first_name)',
824
+ },
825
+ {
826
+ displayName: 'Sort Order',
827
+ name: 'sort_order',
828
+ type: 'options',
829
+ options: [
830
+ { name: 'Ascending', value: 'asc' },
831
+ { name: 'Descending', value: 'desc' },
832
+ ],
833
+ default: 'desc',
834
+ description: 'Sort order',
835
+ },
836
+ ],
837
+ },
838
+ {
839
+ displayName: 'Filters',
840
+ name: 'filters',
841
+ type: 'collection',
842
+ placeholder: 'Add Filter',
843
+ default: {},
844
+ displayOptions: {
845
+ show: {
846
+ resource: ['contact'],
847
+ operation: ['list'],
848
+ },
849
+ },
850
+ options: [
851
+ {
852
+ displayName: 'Status',
853
+ name: 'status',
854
+ type: 'options',
855
+ options: [
856
+ {
857
+ name: 'Active',
858
+ value: 'active',
859
+ },
860
+ {
861
+ name: 'Unsubscribed',
862
+ value: 'unsubscribed',
863
+ },
864
+ {
865
+ name: 'Bounced',
866
+ value: 'bounced',
867
+ },
868
+ {
869
+ name: 'Complained',
870
+ value: 'complained',
871
+ },
872
+ ],
873
+ default: 'active',
874
+ description: 'Filter by contact status',
875
+ },
876
+ {
877
+ displayName: 'List ID',
878
+ name: 'list_id',
879
+ type: 'number',
880
+ default: 0,
881
+ description: 'Filter by list membership',
882
+ },
883
+ {
884
+ displayName: 'Search',
885
+ name: 'search',
886
+ type: 'string',
887
+ default: '',
888
+ description: 'Search by email, name, etc.',
889
+ },
890
+ {
891
+ displayName: 'Tags',
892
+ name: 'tags',
893
+ type: 'string',
894
+ default: '',
895
+ description: 'Comma-separated list of tags to filter by',
896
+ },
897
+ {
898
+ displayName: 'Page',
899
+ name: 'page',
900
+ type: 'number',
901
+ default: 1,
902
+ description: 'Page number for pagination',
903
+ },
904
+ {
905
+ displayName: 'Per Page',
906
+ name: 'per_page',
907
+ type: 'number',
908
+ default: 20,
909
+ description: 'Number of results per page',
910
+ },
911
+ ],
912
+ },
913
+ ],
914
+ };
915
+ async execute() {
916
+ const items = this.getInputData();
917
+ const returnData = [];
918
+ const credentials = await this.getCredentials('cakemailApi');
919
+ const client = new sdk_1.CakemailClient({
920
+ email: credentials.email,
921
+ password: credentials.password,
922
+ baseURL: credentials.baseURL || undefined,
923
+ });
924
+ for (let i = 0; i < items.length; i++) {
925
+ try {
926
+ const resource = this.getNodeParameter('resource', i);
927
+ const operation = this.getNodeParameter('operation', i);
928
+ const accountId = this.getNodeParameter('accountId', i, undefined);
929
+ const options = accountId ? { accountId } : undefined;
930
+ let responseData;
931
+ const node = new Cakemail();
932
+ if (resource === 'account') {
933
+ responseData = await node.executeAccountOperation.call(this, client, operation, i, options);
934
+ }
935
+ else if (resource === 'contact') {
936
+ responseData = await node.executeContactOperation.call(this, client, operation, i, options);
937
+ }
938
+ else if (resource === 'list') {
939
+ responseData = await node.executeListOperation.call(this, client, operation, i, options);
940
+ }
941
+ else if (resource === 'campaign') {
942
+ responseData = await node.executeCampaignOperation.call(this, client, operation, i, options);
943
+ }
944
+ returnData.push({
945
+ json: responseData,
946
+ pairedItem: { item: i },
947
+ });
948
+ }
949
+ catch (error) {
950
+ if (this.continueOnFail()) {
951
+ returnData.push({
952
+ json: {
953
+ error: error?.message || 'Unknown error',
954
+ },
955
+ pairedItem: { item: i },
956
+ });
957
+ continue;
958
+ }
959
+ if (error instanceof sdk_1.CakemailError) {
960
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error.message, {
961
+ itemIndex: i,
962
+ description: error.cause?.message,
963
+ });
964
+ }
965
+ throw error;
966
+ }
967
+ }
968
+ return [returnData];
969
+ }
970
+ async executeAccountOperation(client, operation, itemIndex, options) {
971
+ if (operation === 'get') {
972
+ return await client.accounts.getSelf();
973
+ }
974
+ if (operation === 'getById') {
975
+ const targetAccountId = this.getNodeParameter('targetAccountId', itemIndex);
976
+ return await client.accounts.get(targetAccountId, options);
977
+ }
978
+ if (operation === 'list') {
979
+ const filters = this.getNodeParameter('accountFilters', itemIndex, {});
980
+ return await client.accounts.list(filters, options);
981
+ }
982
+ if (operation === 'create') {
983
+ const name = this.getNodeParameter('accountName', itemIndex);
984
+ const email = this.getNodeParameter('accountEmail', itemIndex);
985
+ const additionalFields = this.getNodeParameter('accountAdditionalFields', itemIndex, {});
986
+ if (additionalFields.settings) {
987
+ try {
988
+ additionalFields.settings =
989
+ typeof additionalFields.settings === 'string'
990
+ ? JSON.parse(additionalFields.settings)
991
+ : additionalFields.settings;
992
+ }
993
+ catch (error) {
994
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid settings JSON: ${error.message}`, { itemIndex });
995
+ }
996
+ }
997
+ const accountData = {
998
+ name,
999
+ primary_email: email,
1000
+ ...additionalFields,
1001
+ };
1002
+ return await client.accounts.create(accountData, options);
1003
+ }
1004
+ if (operation === 'update') {
1005
+ const targetAccountId = this.getNodeParameter('targetAccountId', itemIndex);
1006
+ const additionalFields = this.getNodeParameter('accountAdditionalFields', itemIndex, {});
1007
+ if (additionalFields.settings) {
1008
+ try {
1009
+ additionalFields.settings =
1010
+ typeof additionalFields.settings === 'string'
1011
+ ? JSON.parse(additionalFields.settings)
1012
+ : additionalFields.settings;
1013
+ }
1014
+ catch (error) {
1015
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid settings JSON: ${error.message}`, { itemIndex });
1016
+ }
1017
+ }
1018
+ return await client.accounts.update(targetAccountId, additionalFields, options);
1019
+ }
1020
+ if (operation === 'delete') {
1021
+ const targetAccountId = this.getNodeParameter('targetAccountId', itemIndex);
1022
+ await client.accounts.delete(targetAccountId, options);
1023
+ return { success: true, accountId: targetAccountId };
1024
+ }
1025
+ if (operation === 'getChildren') {
1026
+ const targetAccountId = this.getNodeParameter('targetAccountId', itemIndex);
1027
+ const childrenOptions = this.getNodeParameter('getChildrenOptions', itemIndex, {});
1028
+ return await client.accounts.getChildren(targetAccountId, childrenOptions, options);
1029
+ }
1030
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The operation "${operation}" is not yet implemented for account resource`, { itemIndex });
1031
+ }
1032
+ async executeContactOperation(client, operation, itemIndex, options) {
1033
+ if (operation === 'get') {
1034
+ const contactId = this.getNodeParameter('contactId', itemIndex);
1035
+ return await client.contacts.get(contactId, options);
1036
+ }
1037
+ if (operation === 'getByEmail') {
1038
+ const email = this.getNodeParameter('email', itemIndex);
1039
+ return await client.contacts.getByEmail(email, options);
1040
+ }
1041
+ if (operation === 'list') {
1042
+ const filters = this.getNodeParameter('filters', itemIndex, {});
1043
+ if (filters.tags && typeof filters.tags === 'string') {
1044
+ filters.tags = filters.tags.split(',').map((t) => t.trim());
1045
+ }
1046
+ return await client.contacts.list(filters, options);
1047
+ }
1048
+ if (operation === 'create') {
1049
+ const email = this.getNodeParameter('email', itemIndex);
1050
+ const additionalFields = this.getNodeParameter('additionalFields', itemIndex, {});
1051
+ const createData = {
1052
+ email,
1053
+ ...additionalFields,
1054
+ };
1055
+ if (createData.tags && typeof createData.tags === 'string') {
1056
+ createData.tags = createData.tags.split(',').map((t) => t.trim());
1057
+ }
1058
+ if (createData.custom_attributes && typeof createData.custom_attributes === 'string') {
1059
+ try {
1060
+ createData.custom_attributes = JSON.parse(createData.custom_attributes);
1061
+ }
1062
+ catch (error) {
1063
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid JSON in custom_attributes field', { itemIndex });
1064
+ }
1065
+ }
1066
+ return await client.contacts.create(createData, options);
1067
+ }
1068
+ if (operation === 'update') {
1069
+ const contactId = this.getNodeParameter('contactId', itemIndex);
1070
+ const additionalFields = this.getNodeParameter('additionalFields', itemIndex, {});
1071
+ if (additionalFields.tags && typeof additionalFields.tags === 'string') {
1072
+ additionalFields.tags = additionalFields.tags
1073
+ .split(',')
1074
+ .map((t) => t.trim());
1075
+ }
1076
+ if (additionalFields.custom_attributes && typeof additionalFields.custom_attributes === 'string') {
1077
+ try {
1078
+ additionalFields.custom_attributes = JSON.parse(additionalFields.custom_attributes);
1079
+ }
1080
+ catch (error) {
1081
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid JSON in custom_attributes field', { itemIndex });
1082
+ }
1083
+ }
1084
+ return await client.contacts.update(contactId, additionalFields, options);
1085
+ }
1086
+ if (operation === 'delete') {
1087
+ const contactId = this.getNodeParameter('contactId', itemIndex);
1088
+ await client.contacts.delete(contactId, options);
1089
+ return { success: true, contactId };
1090
+ }
1091
+ if (operation === 'subscribe') {
1092
+ const contactId = this.getNodeParameter('contactId', itemIndex);
1093
+ const listId = this.getNodeParameter('listId', itemIndex);
1094
+ return await client.contacts.subscribe(contactId, listId, options);
1095
+ }
1096
+ if (operation === 'unsubscribe') {
1097
+ const contactId = this.getNodeParameter('contactId', itemIndex);
1098
+ const listId = this.getNodeParameter('listId', itemIndex);
1099
+ return await client.contacts.unsubscribe(contactId, listId, options);
1100
+ }
1101
+ if (operation === 'bulkImport') {
1102
+ const contactsParam = this.getNodeParameter('contacts', itemIndex);
1103
+ let contacts;
1104
+ try {
1105
+ contacts = typeof contactsParam === 'string'
1106
+ ? JSON.parse(contactsParam)
1107
+ : contactsParam;
1108
+ if (!Array.isArray(contacts)) {
1109
+ throw new Error('Contacts must be an array');
1110
+ }
1111
+ }
1112
+ catch (error) {
1113
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid contacts JSON: ${error.message}`, { itemIndex });
1114
+ }
1115
+ const invalidContacts = contacts.filter((c, i) => !c.email);
1116
+ if (invalidContacts.length > 0) {
1117
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `All contacts must have an email field. Found ${invalidContacts.length} contacts without email.`, { itemIndex });
1118
+ }
1119
+ const results = {
1120
+ total: contacts.length,
1121
+ successful: 0,
1122
+ failed: 0,
1123
+ errors: [],
1124
+ };
1125
+ for (let i = 0; i < contacts.length; i++) {
1126
+ try {
1127
+ await client.contacts.create(contacts[i], options);
1128
+ results.successful++;
1129
+ }
1130
+ catch (error) {
1131
+ results.failed++;
1132
+ results.errors.push({
1133
+ index: i,
1134
+ email: contacts[i].email,
1135
+ error: error.message || 'Unknown error',
1136
+ });
1137
+ }
1138
+ }
1139
+ return results;
1140
+ }
1141
+ if (operation === 'search') {
1142
+ const searchQueryParam = this.getNodeParameter('searchQuery', itemIndex);
1143
+ const searchOptions = this.getNodeParameter('searchOptions', itemIndex, {});
1144
+ let searchQuery;
1145
+ try {
1146
+ searchQuery = typeof searchQueryParam === 'string'
1147
+ ? JSON.parse(searchQueryParam)
1148
+ : searchQueryParam;
1149
+ }
1150
+ catch (error) {
1151
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid search query JSON: ${error.message}`, { itemIndex });
1152
+ }
1153
+ const searchParams = {
1154
+ ...searchOptions,
1155
+ filter: searchQuery,
1156
+ };
1157
+ if (searchOptions.sort && searchOptions.sort_order) {
1158
+ searchParams.sort = `${searchOptions.sort}:${searchOptions.sort_order}`;
1159
+ delete searchParams.sort_order;
1160
+ }
1161
+ return await client.contacts.list(searchParams, options);
1162
+ }
1163
+ if (operation === 'addTags') {
1164
+ const contactId = this.getNodeParameter('contactId', itemIndex);
1165
+ const tagsString = this.getNodeParameter('tagsToManage', itemIndex);
1166
+ const tags = tagsString.split(',').map((t) => t.trim()).filter((t) => t);
1167
+ if (tags.length === 0) {
1168
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'At least one tag must be provided', { itemIndex });
1169
+ }
1170
+ return await client.contacts.addTags(contactId, tags, options);
1171
+ }
1172
+ if (operation === 'removeTags') {
1173
+ const contactId = this.getNodeParameter('contactId', itemIndex);
1174
+ const tagsString = this.getNodeParameter('tagsToManage', itemIndex);
1175
+ const tags = tagsString.split(',').map((t) => t.trim()).filter((t) => t);
1176
+ if (tags.length === 0) {
1177
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'At least one tag must be provided', { itemIndex });
1178
+ }
1179
+ return await client.contacts.removeTags(contactId, tags, options);
1180
+ }
1181
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The operation "${operation}" is not yet implemented for contact resource`, { itemIndex });
1182
+ }
1183
+ async executeListOperation(client, operation, itemIndex, options) {
1184
+ if (operation === 'get') {
1185
+ const listId = this.getNodeParameter('listIdParam', itemIndex);
1186
+ return await client.lists.get(listId, options);
1187
+ }
1188
+ if (operation === 'list') {
1189
+ const limit = this.getNodeParameter('listLimit', itemIndex, 20);
1190
+ return await client.lists.list({ per_page: limit }, options);
1191
+ }
1192
+ if (operation === 'create') {
1193
+ const name = this.getNodeParameter('listName', itemIndex);
1194
+ const additionalFields = this.getNodeParameter('listAdditionalFields', itemIndex, {});
1195
+ const createData = {
1196
+ name,
1197
+ ...additionalFields,
1198
+ };
1199
+ return await client.lists.create(createData, options);
1200
+ }
1201
+ if (operation === 'update') {
1202
+ const listId = this.getNodeParameter('listIdParam', itemIndex);
1203
+ const additionalFields = this.getNodeParameter('listAdditionalFields', itemIndex, {});
1204
+ return await client.lists.update(listId, additionalFields, options);
1205
+ }
1206
+ if (operation === 'delete') {
1207
+ const listId = this.getNodeParameter('listIdParam', itemIndex);
1208
+ await client.lists.delete(listId, options);
1209
+ return { success: true, listId };
1210
+ }
1211
+ if (operation === 'getStatistics') {
1212
+ const listId = this.getNodeParameter('listIdParam', itemIndex);
1213
+ return await client.lists.getStatistics(listId, options);
1214
+ }
1215
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The operation "${operation}" is not yet implemented for list resource`, { itemIndex });
1216
+ }
1217
+ async executeCampaignOperation(client, operation, itemIndex, options) {
1218
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Campaign operations are not yet implemented`, { itemIndex });
1219
+ }
1220
+ }
1221
+ exports.Cakemail = Cakemail;
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "n8n-nodes-cakemail",
3
+ "version": "1.0.0",
4
+ "description": "n8n node for Cakemail - workflow-native email platform",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "test": "jest",
13
+ "lint": "eslint src --ext .ts",
14
+ "clean": "rm -rf dist"
15
+ },
16
+ "dependencies": {
17
+ "cakemail-sdk": "^1.0.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/jest": "^29.5.10",
21
+ "@types/node": "^20.10.0",
22
+ "jest": "^29.7.0",
23
+ "n8n-workflow": "^1.0.0",
24
+ "ts-jest": "^29.1.1",
25
+ "typescript": "^5.3.2"
26
+ },
27
+ "n8n": {
28
+ "n8nNodesApiVersion": 1,
29
+ "credentials": [
30
+ "dist/credentials/CakemailApi.credentials.js"
31
+ ],
32
+ "nodes": [
33
+ "dist/nodes/Cakemail/Cakemail.node.js"
34
+ ]
35
+ },
36
+ "keywords": [
37
+ "n8n",
38
+ "n8n-community-node-package",
39
+ "cakemail",
40
+ "email",
41
+ "marketing",
42
+ "automation",
43
+ "multi-tenant"
44
+ ],
45
+ "author": "Cakemail",
46
+ "license": "MIT",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "https://github.com/cakemail/cakemail-n8n-nodes.git",
50
+ "directory": "packages/n8n-nodes-cakemail"
51
+ }
52
+ }