n8n-nodes-jmap 0.2.0 → 0.2.2
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[]>;
|
|
@@ -1,40 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
3
|
exports.JMAP_CAPABILITIES = void 0;
|
|
40
4
|
exports.getJmapSession = getJmapSession;
|
|
@@ -58,8 +22,6 @@ exports.getThreads = getThreads;
|
|
|
58
22
|
exports.downloadBlob = downloadBlob;
|
|
59
23
|
exports.getAttachments = getAttachments;
|
|
60
24
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
61
|
-
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
62
|
-
const tar = __importStar(require("tar"));
|
|
63
25
|
// Standard JMAP capabilities
|
|
64
26
|
exports.JMAP_CAPABILITIES = {
|
|
65
27
|
CORE: 'urn:ietf:params:jmap:core',
|
|
@@ -511,147 +473,12 @@ function matchesMimeType(mimeType, filter) {
|
|
|
511
473
|
return normalizedMime === normalizedFilter;
|
|
512
474
|
}
|
|
513
475
|
/**
|
|
514
|
-
*
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
const zipTypes = [
|
|
518
|
-
'application/zip',
|
|
519
|
-
'application/x-zip-compressed',
|
|
520
|
-
'application/x-zip',
|
|
521
|
-
];
|
|
522
|
-
return zipTypes.includes(mimeType.toLowerCase());
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* Check if a MIME type is a tar.gz archive
|
|
526
|
-
*/
|
|
527
|
-
function isTarGzMimeType(mimeType) {
|
|
528
|
-
const tarGzTypes = [
|
|
529
|
-
'application/gzip',
|
|
530
|
-
'application/x-gzip',
|
|
531
|
-
'application/x-tar',
|
|
532
|
-
'application/x-compressed-tar',
|
|
533
|
-
];
|
|
534
|
-
return tarGzTypes.includes(mimeType.toLowerCase());
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Check if filename suggests tar.gz
|
|
538
|
-
*/
|
|
539
|
-
function isTarGzFilename(filename) {
|
|
540
|
-
const lower = filename.toLowerCase();
|
|
541
|
-
return lower.endsWith('.tar.gz') || lower.endsWith('.tgz');
|
|
542
|
-
}
|
|
543
|
-
/**
|
|
544
|
-
* Get file extension from filename
|
|
545
|
-
*/
|
|
546
|
-
function getFileExtension(filename) {
|
|
547
|
-
const parts = filename.split('.');
|
|
548
|
-
if (parts.length > 1) {
|
|
549
|
-
return parts[parts.length - 1].toLowerCase();
|
|
550
|
-
}
|
|
551
|
-
return '';
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* Guess MIME type from filename extension
|
|
555
|
-
*/
|
|
556
|
-
function guessMimeType(filename) {
|
|
557
|
-
const ext = getFileExtension(filename).toLowerCase();
|
|
558
|
-
const mimeTypes = {
|
|
559
|
-
pdf: 'application/pdf',
|
|
560
|
-
doc: 'application/msword',
|
|
561
|
-
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
562
|
-
xls: 'application/vnd.ms-excel',
|
|
563
|
-
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
564
|
-
ppt: 'application/vnd.ms-powerpoint',
|
|
565
|
-
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
566
|
-
txt: 'text/plain',
|
|
567
|
-
csv: 'text/csv',
|
|
568
|
-
json: 'application/json',
|
|
569
|
-
xml: 'application/xml',
|
|
570
|
-
html: 'text/html',
|
|
571
|
-
htm: 'text/html',
|
|
572
|
-
jpg: 'image/jpeg',
|
|
573
|
-
jpeg: 'image/jpeg',
|
|
574
|
-
png: 'image/png',
|
|
575
|
-
gif: 'image/gif',
|
|
576
|
-
svg: 'image/svg+xml',
|
|
577
|
-
webp: 'image/webp',
|
|
578
|
-
mp3: 'audio/mpeg',
|
|
579
|
-
wav: 'audio/wav',
|
|
580
|
-
mp4: 'video/mp4',
|
|
581
|
-
avi: 'video/x-msvideo',
|
|
582
|
-
zip: 'application/zip',
|
|
583
|
-
tar: 'application/x-tar',
|
|
584
|
-
gz: 'application/gzip',
|
|
585
|
-
};
|
|
586
|
-
return mimeTypes[ext] || 'application/octet-stream';
|
|
587
|
-
}
|
|
588
|
-
/**
|
|
589
|
-
* Extract files from a ZIP archive
|
|
590
|
-
*/
|
|
591
|
-
function extractZip(buffer) {
|
|
592
|
-
const files = [];
|
|
593
|
-
const zip = new adm_zip_1.default(buffer);
|
|
594
|
-
const entries = zip.getEntries();
|
|
595
|
-
for (const entry of entries) {
|
|
596
|
-
if (!entry.isDirectory) {
|
|
597
|
-
const data = entry.getData();
|
|
598
|
-
const name = entry.entryName.split('/').pop() || entry.entryName;
|
|
599
|
-
files.push({
|
|
600
|
-
name,
|
|
601
|
-
data,
|
|
602
|
-
mimeType: guessMimeType(name),
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
return files;
|
|
607
|
-
}
|
|
608
|
-
/**
|
|
609
|
-
* Extract files from a tar.gz archive
|
|
610
|
-
*/
|
|
611
|
-
async function extractTarGz(buffer) {
|
|
612
|
-
const files = [];
|
|
613
|
-
return new Promise((resolve, reject) => {
|
|
614
|
-
const chunks = new Map();
|
|
615
|
-
const parser = new tar.Parser();
|
|
616
|
-
parser.on('entry', (entry) => {
|
|
617
|
-
if (entry.type === 'File') {
|
|
618
|
-
const entryChunks = [];
|
|
619
|
-
entry.on('data', (chunk) => {
|
|
620
|
-
entryChunks.push(chunk);
|
|
621
|
-
});
|
|
622
|
-
entry.on('end', () => {
|
|
623
|
-
const name = entry.path.split('/').pop() || entry.path;
|
|
624
|
-
chunks.set(name, entryChunks);
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
else {
|
|
628
|
-
entry.resume();
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
parser.on('end', () => {
|
|
632
|
-
for (const [name, entryChunks] of chunks) {
|
|
633
|
-
const data = Buffer.concat(entryChunks);
|
|
634
|
-
files.push({
|
|
635
|
-
name,
|
|
636
|
-
data,
|
|
637
|
-
mimeType: guessMimeType(name),
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
resolve(files);
|
|
641
|
-
});
|
|
642
|
-
parser.on('error', reject);
|
|
643
|
-
const { Gunzip } = require('zlib');
|
|
644
|
-
const gunzip = new Gunzip();
|
|
645
|
-
gunzip.pipe(parser);
|
|
646
|
-
gunzip.write(buffer);
|
|
647
|
-
gunzip.end();
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
/**
|
|
651
|
-
* 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.
|
|
652
479
|
*/
|
|
653
480
|
async function getAttachments(accountId, emailId, options = {}) {
|
|
654
|
-
const {
|
|
481
|
+
const { includeInline = false, mimeTypeFilter = '' } = options;
|
|
655
482
|
// Get email with attachments metadata
|
|
656
483
|
const emails = await getEmails.call(this, accountId, [emailId], [
|
|
657
484
|
'id',
|
|
@@ -686,63 +513,17 @@ async function getAttachments(accountId, emailId, options = {}) {
|
|
|
686
513
|
}
|
|
687
514
|
// Download the attachment
|
|
688
515
|
const buffer = await downloadBlob.call(this, accountId, attachment.blobId, attachment.name, attachment.type);
|
|
689
|
-
//
|
|
690
|
-
const isZip = isZipMimeType(attachment.type);
|
|
691
|
-
const isTarGz = isTarGzMimeType(attachment.type) || isTarGzFilename(attachment.name);
|
|
692
|
-
if (extractArchives && (isZip || isTarGz)) {
|
|
693
|
-
// Extract archive contents
|
|
694
|
-
let extractedFiles = [];
|
|
695
|
-
try {
|
|
696
|
-
if (isZip) {
|
|
697
|
-
extractedFiles = extractZip(buffer);
|
|
698
|
-
}
|
|
699
|
-
else if (isTarGz) {
|
|
700
|
-
extractedFiles = await extractTarGz(buffer);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
catch (error) {
|
|
704
|
-
// If extraction fails, return the archive as-is
|
|
705
|
-
extractedFiles = [];
|
|
706
|
-
}
|
|
707
|
-
if (extractedFiles.length > 0) {
|
|
708
|
-
// Return each extracted file as a separate item
|
|
709
|
-
for (const file of extractedFiles) {
|
|
710
|
-
const binaryData = await this.helpers.prepareBinaryData(file.data, file.name, file.mimeType);
|
|
711
|
-
results.push({
|
|
712
|
-
json: {
|
|
713
|
-
emailId: email.id,
|
|
714
|
-
emailSubject: email.subject,
|
|
715
|
-
attachmentIndex,
|
|
716
|
-
originalFileName: attachment.name,
|
|
717
|
-
fileName: file.name,
|
|
718
|
-
mimeType: file.mimeType,
|
|
719
|
-
fileSize: file.data.length,
|
|
720
|
-
wasExtractedFromArchive: true,
|
|
721
|
-
sourceArchiveName: attachment.name,
|
|
722
|
-
},
|
|
723
|
-
binary: {
|
|
724
|
-
file: binaryData,
|
|
725
|
-
},
|
|
726
|
-
});
|
|
727
|
-
attachmentIndex++;
|
|
728
|
-
}
|
|
729
|
-
continue;
|
|
730
|
-
}
|
|
731
|
-
// If extraction failed or no files, fall through to return the archive as-is
|
|
732
|
-
}
|
|
733
|
-
// Return the attachment as-is (not an archive, or extraction disabled/failed)
|
|
516
|
+
// Prepare binary data for n8n
|
|
734
517
|
const binaryData = await this.helpers.prepareBinaryData(buffer, attachment.name, attachment.type);
|
|
735
518
|
results.push({
|
|
736
519
|
json: {
|
|
737
520
|
emailId: email.id,
|
|
738
521
|
emailSubject: email.subject,
|
|
739
522
|
attachmentIndex,
|
|
740
|
-
originalFileName: attachment.name,
|
|
741
523
|
fileName: attachment.name,
|
|
742
524
|
mimeType: attachment.type,
|
|
743
525
|
fileSize: attachment.size,
|
|
744
|
-
|
|
745
|
-
sourceArchiveName: null,
|
|
526
|
+
isInline: attachment.isInline || false,
|
|
746
527
|
},
|
|
747
528
|
binary: {
|
|
748
529
|
file: binaryData,
|
|
@@ -429,13 +429,6 @@ class Jmap {
|
|
|
429
429
|
},
|
|
430
430
|
},
|
|
431
431
|
options: [
|
|
432
|
-
{
|
|
433
|
-
displayName: 'Extract Archives',
|
|
434
|
-
name: 'extractArchives',
|
|
435
|
-
type: 'boolean',
|
|
436
|
-
default: false,
|
|
437
|
-
description: 'Whether to extract ZIP and tar.gz archives and return their contents as individual files',
|
|
438
|
-
},
|
|
439
432
|
{
|
|
440
433
|
displayName: 'Include Inline Images',
|
|
441
434
|
name: 'includeInline',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-jmap",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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",
|
|
@@ -46,9 +46,7 @@
|
|
|
46
46
|
]
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@types/adm-zip": "^0.5.7",
|
|
50
49
|
"@types/node": "^20.0.0",
|
|
51
|
-
"@types/tar": "^6.1.13",
|
|
52
50
|
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
53
51
|
"@typescript-eslint/parser": "^7.0.0",
|
|
54
52
|
"eslint": "^8.56.0",
|
|
@@ -59,9 +57,5 @@
|
|
|
59
57
|
},
|
|
60
58
|
"peerDependencies": {
|
|
61
59
|
"n8n-workflow": "*"
|
|
62
|
-
},
|
|
63
|
-
"dependencies": {
|
|
64
|
-
"adm-zip": "^0.5.16",
|
|
65
|
-
"tar": "^7.5.2"
|
|
66
60
|
}
|
|
67
61
|
}
|