n8n-nodes-jmap 0.2.1 → 0.2.3
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.
|
@@ -123,11 +123,12 @@ export declare function downloadBlob(this: IExecuteFunctions, accountId: string,
|
|
|
123
123
|
* Interface for attachment options
|
|
124
124
|
*/
|
|
125
125
|
export interface IAttachmentOptions {
|
|
126
|
-
extractArchives?: boolean;
|
|
127
126
|
includeInline?: boolean;
|
|
128
127
|
mimeTypeFilter?: string;
|
|
129
128
|
}
|
|
130
129
|
/**
|
|
131
|
-
* Get attachments from an email and return them as binary data
|
|
130
|
+
* Get attachments from an email and return them as binary data.
|
|
131
|
+
* Each attachment is returned as a separate item with binary data in the 'file' field.
|
|
132
|
+
* To extract archives (ZIP, tar.gz), chain with the n8n Compression node.
|
|
132
133
|
*/
|
|
133
134
|
export declare function getAttachments(this: IExecuteFunctions, accountId: string, emailId: string, options?: IAttachmentOptions): Promise<INodeExecutionData[]>;
|
|
@@ -473,60 +473,12 @@ function matchesMimeType(mimeType, filter) {
|
|
|
473
473
|
return normalizedMime === normalizedFilter;
|
|
474
474
|
}
|
|
475
475
|
/**
|
|
476
|
-
*
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
const zipTypes = [
|
|
480
|
-
'application/zip',
|
|
481
|
-
'application/x-zip-compressed',
|
|
482
|
-
'application/x-zip',
|
|
483
|
-
];
|
|
484
|
-
return zipTypes.includes(mimeType.toLowerCase());
|
|
485
|
-
}
|
|
486
|
-
/**
|
|
487
|
-
* Check if a MIME type is a tar.gz archive
|
|
488
|
-
*/
|
|
489
|
-
function isTarGzMimeType(mimeType) {
|
|
490
|
-
const tarGzTypes = [
|
|
491
|
-
'application/gzip',
|
|
492
|
-
'application/x-gzip',
|
|
493
|
-
'application/x-tar',
|
|
494
|
-
'application/x-compressed-tar',
|
|
495
|
-
];
|
|
496
|
-
return tarGzTypes.includes(mimeType.toLowerCase());
|
|
497
|
-
}
|
|
498
|
-
/**
|
|
499
|
-
* Check if filename suggests tar.gz
|
|
500
|
-
*/
|
|
501
|
-
function isTarGzFilename(filename) {
|
|
502
|
-
const lower = filename.toLowerCase();
|
|
503
|
-
return lower.endsWith('.tar.gz') || lower.endsWith('.tgz');
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* Extract files from a ZIP archive
|
|
507
|
-
* Note: Archive extraction is not yet available (planned for future release with self-hosted support)
|
|
508
|
-
* Returns empty array, causing the archive to be returned as-is
|
|
509
|
-
*/
|
|
510
|
-
function extractZip(_buffer) {
|
|
511
|
-
// Archive extraction not yet implemented
|
|
512
|
-
// Will be available in a future release for self-hosted n8n
|
|
513
|
-
return [];
|
|
514
|
-
}
|
|
515
|
-
/**
|
|
516
|
-
* Extract files from a tar.gz archive
|
|
517
|
-
* Note: Archive extraction is not yet available (planned for future release with self-hosted support)
|
|
518
|
-
* Returns empty array, causing the archive to be returned as-is
|
|
519
|
-
*/
|
|
520
|
-
async function extractTarGz(_buffer) {
|
|
521
|
-
// Archive extraction not yet implemented
|
|
522
|
-
// Will be available in a future release for self-hosted n8n
|
|
523
|
-
return [];
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Get attachments from an email and return them as binary data
|
|
476
|
+
* Get attachments from an email and return them as binary data.
|
|
477
|
+
* Each attachment is returned as a separate item with binary data in the 'file' field.
|
|
478
|
+
* To extract archives (ZIP, tar.gz), chain with the n8n Compression node.
|
|
527
479
|
*/
|
|
528
480
|
async function getAttachments(accountId, emailId, options = {}) {
|
|
529
|
-
const {
|
|
481
|
+
const { includeInline = false, mimeTypeFilter = '' } = options;
|
|
530
482
|
// Get email with attachments metadata
|
|
531
483
|
const emails = await getEmails.call(this, accountId, [emailId], [
|
|
532
484
|
'id',
|
|
@@ -561,63 +513,17 @@ async function getAttachments(accountId, emailId, options = {}) {
|
|
|
561
513
|
}
|
|
562
514
|
// Download the attachment
|
|
563
515
|
const buffer = await downloadBlob.call(this, accountId, attachment.blobId, attachment.name, attachment.type);
|
|
564
|
-
//
|
|
565
|
-
const isZip = isZipMimeType(attachment.type);
|
|
566
|
-
const isTarGz = isTarGzMimeType(attachment.type) || isTarGzFilename(attachment.name);
|
|
567
|
-
if (extractArchives && (isZip || isTarGz)) {
|
|
568
|
-
// Extract archive contents
|
|
569
|
-
let extractedFiles = [];
|
|
570
|
-
try {
|
|
571
|
-
if (isZip) {
|
|
572
|
-
extractedFiles = extractZip(buffer);
|
|
573
|
-
}
|
|
574
|
-
else if (isTarGz) {
|
|
575
|
-
extractedFiles = await extractTarGz(buffer);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
catch (error) {
|
|
579
|
-
// If extraction fails, return the archive as-is
|
|
580
|
-
extractedFiles = [];
|
|
581
|
-
}
|
|
582
|
-
if (extractedFiles.length > 0) {
|
|
583
|
-
// Return each extracted file as a separate item
|
|
584
|
-
for (const file of extractedFiles) {
|
|
585
|
-
const binaryData = await this.helpers.prepareBinaryData(file.data, file.name, file.mimeType);
|
|
586
|
-
results.push({
|
|
587
|
-
json: {
|
|
588
|
-
emailId: email.id,
|
|
589
|
-
emailSubject: email.subject,
|
|
590
|
-
attachmentIndex,
|
|
591
|
-
originalFileName: attachment.name,
|
|
592
|
-
fileName: file.name,
|
|
593
|
-
mimeType: file.mimeType,
|
|
594
|
-
fileSize: file.data.length,
|
|
595
|
-
wasExtractedFromArchive: true,
|
|
596
|
-
sourceArchiveName: attachment.name,
|
|
597
|
-
},
|
|
598
|
-
binary: {
|
|
599
|
-
file: binaryData,
|
|
600
|
-
},
|
|
601
|
-
});
|
|
602
|
-
attachmentIndex++;
|
|
603
|
-
}
|
|
604
|
-
continue;
|
|
605
|
-
}
|
|
606
|
-
// If extraction failed or no files, fall through to return the archive as-is
|
|
607
|
-
}
|
|
608
|
-
// Return the attachment as-is (not an archive, or extraction disabled/failed)
|
|
516
|
+
// Prepare binary data for n8n
|
|
609
517
|
const binaryData = await this.helpers.prepareBinaryData(buffer, attachment.name, attachment.type);
|
|
610
518
|
results.push({
|
|
611
519
|
json: {
|
|
612
520
|
emailId: email.id,
|
|
613
521
|
emailSubject: email.subject,
|
|
614
522
|
attachmentIndex,
|
|
615
|
-
originalFileName: attachment.name,
|
|
616
523
|
fileName: attachment.name,
|
|
617
524
|
mimeType: attachment.type,
|
|
618
525
|
fileSize: attachment.size,
|
|
619
|
-
|
|
620
|
-
sourceArchiveName: null,
|
|
526
|
+
isInline: attachment.isInline || false,
|
|
621
527
|
},
|
|
622
528
|
binary: {
|
|
623
529
|
file: binaryData,
|
|
@@ -429,13 +429,6 @@ class Jmap {
|
|
|
429
429
|
},
|
|
430
430
|
},
|
|
431
431
|
options: [
|
|
432
|
-
{
|
|
433
|
-
displayName: 'Extract Archives (Coming Soon)',
|
|
434
|
-
name: 'extractArchives',
|
|
435
|
-
type: 'boolean',
|
|
436
|
-
default: false,
|
|
437
|
-
description: 'Extract ZIP and tar.gz archives. Note: This feature is planned for a future release and currently returns archives as-is.',
|
|
438
|
-
},
|
|
439
432
|
{
|
|
440
433
|
displayName: 'Include Inline Images',
|
|
441
434
|
name: 'includeInline',
|
|
@@ -471,6 +464,89 @@ class Jmap {
|
|
|
471
464
|
default: 50,
|
|
472
465
|
description: 'Max number of results to return',
|
|
473
466
|
},
|
|
467
|
+
// Options for getMany (search filters)
|
|
468
|
+
{
|
|
469
|
+
displayName: 'Options',
|
|
470
|
+
name: 'getManyOptions',
|
|
471
|
+
type: 'collection',
|
|
472
|
+
placeholder: 'Add Option',
|
|
473
|
+
default: {},
|
|
474
|
+
displayOptions: {
|
|
475
|
+
show: {
|
|
476
|
+
resource: ['email'],
|
|
477
|
+
operation: ['getMany'],
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
options: [
|
|
481
|
+
{
|
|
482
|
+
displayName: 'Flagged Only',
|
|
483
|
+
name: 'flaggedOnly',
|
|
484
|
+
type: 'boolean',
|
|
485
|
+
default: false,
|
|
486
|
+
description: 'Whether to return only flagged/starred emails',
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
displayName: 'From Contains',
|
|
490
|
+
name: 'from',
|
|
491
|
+
type: 'string',
|
|
492
|
+
default: '',
|
|
493
|
+
placeholder: 'sender@example.com',
|
|
494
|
+
description: 'Filter emails where the From address contains this text',
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
displayName: 'Full Text Search',
|
|
498
|
+
name: 'text',
|
|
499
|
+
type: 'string',
|
|
500
|
+
default: '',
|
|
501
|
+
placeholder: 'search terms',
|
|
502
|
+
description: 'Search in subject, body, and addresses',
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
displayName: 'Has Attachment',
|
|
506
|
+
name: 'hasAttachment',
|
|
507
|
+
type: 'boolean',
|
|
508
|
+
default: false,
|
|
509
|
+
description: 'Whether to return only emails with attachments',
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
displayName: 'Received After',
|
|
513
|
+
name: 'after',
|
|
514
|
+
type: 'dateTime',
|
|
515
|
+
default: '',
|
|
516
|
+
description: 'Filter emails received after this date',
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
displayName: 'Received Before',
|
|
520
|
+
name: 'before',
|
|
521
|
+
type: 'dateTime',
|
|
522
|
+
default: '',
|
|
523
|
+
description: 'Filter emails received before this date',
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
displayName: 'Subject Contains',
|
|
527
|
+
name: 'subject',
|
|
528
|
+
type: 'string',
|
|
529
|
+
default: '',
|
|
530
|
+
placeholder: 'Invoice',
|
|
531
|
+
description: 'Filter emails where the subject contains this text',
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
displayName: 'To Contains',
|
|
535
|
+
name: 'to',
|
|
536
|
+
type: 'string',
|
|
537
|
+
default: '',
|
|
538
|
+
placeholder: 'recipient@example.com',
|
|
539
|
+
description: 'Filter emails where the To address contains this text',
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
displayName: 'Unread Only',
|
|
543
|
+
name: 'unreadOnly',
|
|
544
|
+
type: 'boolean',
|
|
545
|
+
default: false,
|
|
546
|
+
description: 'Whether to return only unread emails',
|
|
547
|
+
},
|
|
548
|
+
],
|
|
549
|
+
},
|
|
474
550
|
// ==================== MAILBOX PARAMETERS ====================
|
|
475
551
|
{
|
|
476
552
|
displayName: 'Mailbox ID',
|
|
@@ -637,10 +713,42 @@ class Jmap {
|
|
|
637
713
|
if (operation === 'getMany') {
|
|
638
714
|
const mailbox = this.getNodeParameter('mailbox', i);
|
|
639
715
|
const limit = this.getNodeParameter('limit', i);
|
|
716
|
+
const options = this.getNodeParameter('getManyOptions', i);
|
|
640
717
|
const filter = {};
|
|
718
|
+
// Mailbox filter
|
|
641
719
|
if (mailbox) {
|
|
642
720
|
filter.inMailbox = mailbox;
|
|
643
721
|
}
|
|
722
|
+
// Date filters
|
|
723
|
+
if (options.after) {
|
|
724
|
+
filter.after = new Date(options.after).toISOString();
|
|
725
|
+
}
|
|
726
|
+
if (options.before) {
|
|
727
|
+
filter.before = new Date(options.before).toISOString();
|
|
728
|
+
}
|
|
729
|
+
// Content filters
|
|
730
|
+
if (options.from) {
|
|
731
|
+
filter.from = options.from;
|
|
732
|
+
}
|
|
733
|
+
if (options.to) {
|
|
734
|
+
filter.to = options.to;
|
|
735
|
+
}
|
|
736
|
+
if (options.subject) {
|
|
737
|
+
filter.subject = options.subject;
|
|
738
|
+
}
|
|
739
|
+
if (options.text) {
|
|
740
|
+
filter.text = options.text;
|
|
741
|
+
}
|
|
742
|
+
// Boolean filters
|
|
743
|
+
if (options.hasAttachment) {
|
|
744
|
+
filter.hasAttachment = true;
|
|
745
|
+
}
|
|
746
|
+
if (options.unreadOnly) {
|
|
747
|
+
filter.notKeyword = '$seen';
|
|
748
|
+
}
|
|
749
|
+
if (options.flaggedOnly) {
|
|
750
|
+
filter.hasKeyword = '$flagged';
|
|
751
|
+
}
|
|
644
752
|
const { ids } = await GenericFunctions_1.queryEmails.call(this, accountId, filter, [{ property: 'receivedAt', isAscending: false }], limit);
|
|
645
753
|
if (ids.length > 0) {
|
|
646
754
|
responseData = await GenericFunctions_1.getEmails.call(this, accountId, ids);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-jmap",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "n8n community node for JMAP email protocol (RFC 8620/8621) - Works with Apache James, Twake Mail, Fastmail, and other JMAP-compatible servers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|