n8n-nodes-jmap 0.2.6 → 0.3.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/dist/credentials/JmapBasicAuthApi.credentials.d.ts +13 -0
- package/dist/credentials/JmapBasicAuthApi.credentials.js +66 -0
- package/dist/credentials/JmapBearerTokenApi.credentials.d.ts +13 -0
- package/dist/credentials/JmapBearerTokenApi.credentials.js +58 -0
- package/dist/credentials/JmapOAuth2Api.credentials.d.ts +0 -13
- package/dist/credentials/JmapOAuth2Api.credentials.js +0 -10
- package/dist/nodes/Jmap/GenericFunctions.js +35 -83
- package/dist/nodes/Jmap/Jmap.node.js +25 -7
- package/dist/nodes/Jmap/JmapTrigger.node.js +19 -5
- package/package.json +3 -2
- package/dist/credentials/JmapApi.credentials.d.ts +0 -13
- package/dist/credentials/JmapApi.credentials.js +0 -105
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, Icon } from 'n8n-workflow';
|
|
2
|
+
/**
|
|
3
|
+
* JMAP API Credentials for Basic Auth authentication
|
|
4
|
+
*/
|
|
5
|
+
export declare class JmapBasicAuthApi implements ICredentialType {
|
|
6
|
+
name: string;
|
|
7
|
+
displayName: string;
|
|
8
|
+
documentationUrl: string;
|
|
9
|
+
icon: Icon;
|
|
10
|
+
properties: INodeProperties[];
|
|
11
|
+
authenticate: IAuthenticateGeneric;
|
|
12
|
+
test: ICredentialTestRequest;
|
|
13
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JmapBasicAuthApi = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* JMAP API Credentials for Basic Auth authentication
|
|
6
|
+
*/
|
|
7
|
+
class JmapBasicAuthApi {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.name = 'jmapBasicAuthApi';
|
|
10
|
+
this.displayName = 'JMAP Basic Auth API';
|
|
11
|
+
this.documentationUrl = 'https://jmap.io/spec-core.html';
|
|
12
|
+
this.icon = {
|
|
13
|
+
light: 'file:../nodes/Jmap/jmap.svg',
|
|
14
|
+
dark: 'file:../nodes/Jmap/jmap.svg',
|
|
15
|
+
};
|
|
16
|
+
this.properties = [
|
|
17
|
+
{
|
|
18
|
+
displayName: 'JMAP Server URL',
|
|
19
|
+
name: 'serverUrl',
|
|
20
|
+
type: 'string',
|
|
21
|
+
default: 'https://jmap.example.com/jmap',
|
|
22
|
+
placeholder: 'https://jmap.example.com/jmap',
|
|
23
|
+
description: 'The base URL of the JMAP server',
|
|
24
|
+
required: true,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
displayName: 'Email',
|
|
28
|
+
name: 'email',
|
|
29
|
+
type: 'string',
|
|
30
|
+
placeholder: 'user@example.com',
|
|
31
|
+
default: '',
|
|
32
|
+
required: true,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
displayName: 'Password',
|
|
36
|
+
name: 'password',
|
|
37
|
+
type: 'string',
|
|
38
|
+
typeOptions: {
|
|
39
|
+
password: true,
|
|
40
|
+
},
|
|
41
|
+
default: '',
|
|
42
|
+
required: true,
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
this.authenticate = {
|
|
46
|
+
type: 'generic',
|
|
47
|
+
properties: {
|
|
48
|
+
auth: {
|
|
49
|
+
username: '={{$credentials.email}}',
|
|
50
|
+
password: '={{$credentials.password}}',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
this.test = {
|
|
55
|
+
request: {
|
|
56
|
+
baseURL: '={{$credentials.serverUrl}}',
|
|
57
|
+
url: '/session',
|
|
58
|
+
method: 'GET',
|
|
59
|
+
headers: {
|
|
60
|
+
Accept: 'application/json',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.JmapBasicAuthApi = JmapBasicAuthApi;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, Icon } from 'n8n-workflow';
|
|
2
|
+
/**
|
|
3
|
+
* JMAP API Credentials for Bearer Token authentication
|
|
4
|
+
*/
|
|
5
|
+
export declare class JmapBearerTokenApi implements ICredentialType {
|
|
6
|
+
name: string;
|
|
7
|
+
displayName: string;
|
|
8
|
+
documentationUrl: string;
|
|
9
|
+
icon: Icon;
|
|
10
|
+
properties: INodeProperties[];
|
|
11
|
+
authenticate: IAuthenticateGeneric;
|
|
12
|
+
test: ICredentialTestRequest;
|
|
13
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JmapBearerTokenApi = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* JMAP API Credentials for Bearer Token authentication
|
|
6
|
+
*/
|
|
7
|
+
class JmapBearerTokenApi {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.name = 'jmapBearerTokenApi';
|
|
10
|
+
this.displayName = 'JMAP Bearer Token API';
|
|
11
|
+
this.documentationUrl = 'https://jmap.io/spec-core.html';
|
|
12
|
+
this.icon = {
|
|
13
|
+
light: 'file:../nodes/Jmap/jmap.svg',
|
|
14
|
+
dark: 'file:../nodes/Jmap/jmap.svg',
|
|
15
|
+
};
|
|
16
|
+
this.properties = [
|
|
17
|
+
{
|
|
18
|
+
displayName: 'JMAP Server URL',
|
|
19
|
+
name: 'serverUrl',
|
|
20
|
+
type: 'string',
|
|
21
|
+
default: 'https://jmap.example.com/jmap',
|
|
22
|
+
placeholder: 'https://jmap.example.com/jmap',
|
|
23
|
+
description: 'The base URL of the JMAP server',
|
|
24
|
+
required: true,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
displayName: 'Access Token',
|
|
28
|
+
name: 'accessToken',
|
|
29
|
+
type: 'string',
|
|
30
|
+
typeOptions: {
|
|
31
|
+
password: true,
|
|
32
|
+
},
|
|
33
|
+
default: '',
|
|
34
|
+
required: true,
|
|
35
|
+
description: 'The access token (e.g., from OAuth2/OIDC)',
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
this.authenticate = {
|
|
39
|
+
type: 'generic',
|
|
40
|
+
properties: {
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: '=Bearer {{$credentials.accessToken}}',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
this.test = {
|
|
47
|
+
request: {
|
|
48
|
+
baseURL: '={{$credentials.serverUrl}}',
|
|
49
|
+
url: '/session',
|
|
50
|
+
method: 'GET',
|
|
51
|
+
headers: {
|
|
52
|
+
Accept: 'application/json',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.JmapBearerTokenApi = JmapBearerTokenApi;
|
|
@@ -11,17 +11,4 @@ export declare class JmapOAuth2Api implements ICredentialType {
|
|
|
11
11
|
extends: string[];
|
|
12
12
|
icon: Icon;
|
|
13
13
|
properties: INodeProperties[];
|
|
14
|
-
oauth2: {
|
|
15
|
-
tokenExpiredStatusCode: number;
|
|
16
|
-
grantType: string;
|
|
17
|
-
scope: string;
|
|
18
|
-
tokenRequestMethod: "POST";
|
|
19
|
-
authorizeUrl: {
|
|
20
|
-
url: string;
|
|
21
|
-
};
|
|
22
|
-
accessTokenUrl: {
|
|
23
|
-
url: string;
|
|
24
|
-
};
|
|
25
|
-
clientAuthentication: "body";
|
|
26
|
-
};
|
|
27
14
|
}
|
|
@@ -88,16 +88,6 @@ class JmapOAuth2Api {
|
|
|
88
88
|
default: 'body',
|
|
89
89
|
},
|
|
90
90
|
];
|
|
91
|
-
// OAuth2 flow configuration
|
|
92
|
-
this.oauth2 = {
|
|
93
|
-
tokenExpiredStatusCode: 401,
|
|
94
|
-
grantType: 'pkce',
|
|
95
|
-
scope: 'openid email profile offline_access',
|
|
96
|
-
tokenRequestMethod: 'POST',
|
|
97
|
-
authorizeUrl: { url: '={{ $credentials.authUrl }}' },
|
|
98
|
-
accessTokenUrl: { url: '={{ $credentials.accessTokenUrl }}' },
|
|
99
|
-
clientAuthentication: 'body',
|
|
100
|
-
};
|
|
101
91
|
}
|
|
102
92
|
}
|
|
103
93
|
exports.JmapOAuth2Api = JmapOAuth2Api;
|
|
@@ -52,7 +52,8 @@ async function getServerUrl(context) {
|
|
|
52
52
|
return credentials.jmapServerUrl.replace(/\/$/, '');
|
|
53
53
|
}
|
|
54
54
|
else {
|
|
55
|
-
|
|
55
|
+
// Both jmapBasicAuthApi and jmapBearerTokenApi use 'serverUrl'
|
|
56
|
+
const credentials = await context.getCredentials(authType);
|
|
56
57
|
return credentials.serverUrl.replace(/\/$/, '');
|
|
57
58
|
}
|
|
58
59
|
}
|
|
@@ -63,45 +64,18 @@ async function makeJmapRequest(context, method, endpoint, body) {
|
|
|
63
64
|
const authType = getAuthType(context);
|
|
64
65
|
const serverUrl = await getServerUrl(context);
|
|
65
66
|
const url = endpoint.startsWith('http') ? endpoint : `${serverUrl}${endpoint}`;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
url,
|
|
71
|
-
headers: {
|
|
72
|
-
'Content-Type': 'application/json',
|
|
73
|
-
Accept: 'application/json',
|
|
74
|
-
},
|
|
75
|
-
body,
|
|
76
|
-
json: true,
|
|
77
|
-
});
|
|
78
|
-
return response;
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
// Use Basic Auth or Bearer Token
|
|
82
|
-
const credentials = await context.getCredentials('jmapApi');
|
|
83
|
-
const authMethod = credentials.authMethod || 'basicAuth';
|
|
84
|
-
const headers = {
|
|
67
|
+
const requestOptions = {
|
|
68
|
+
method,
|
|
69
|
+
url,
|
|
70
|
+
headers: {
|
|
85
71
|
'Content-Type': 'application/json',
|
|
86
72
|
Accept: 'application/json',
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
headers.Authorization = `Bearer ${credentials.accessToken}`;
|
|
94
|
-
}
|
|
95
|
-
const options = {
|
|
96
|
-
method,
|
|
97
|
-
url,
|
|
98
|
-
headers,
|
|
99
|
-
body,
|
|
100
|
-
json: true,
|
|
101
|
-
};
|
|
102
|
-
const response = await context.helpers.httpRequest(options);
|
|
103
|
-
return response;
|
|
104
|
-
}
|
|
73
|
+
},
|
|
74
|
+
body,
|
|
75
|
+
json: true,
|
|
76
|
+
};
|
|
77
|
+
const response = await context.helpers.httpRequestWithAuthentication.call(context, authType, requestOptions);
|
|
78
|
+
return response;
|
|
105
79
|
}
|
|
106
80
|
/**
|
|
107
81
|
* Get JMAP session from the server
|
|
@@ -148,7 +122,7 @@ async function getPrimaryAccountId() {
|
|
|
148
122
|
if (accountIds.length > 0) {
|
|
149
123
|
return accountIds[0];
|
|
150
124
|
}
|
|
151
|
-
throw new
|
|
125
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No JMAP account found');
|
|
152
126
|
}
|
|
153
127
|
/**
|
|
154
128
|
* Get all mailboxes for an account
|
|
@@ -159,7 +133,7 @@ async function getMailboxes(accountId) {
|
|
|
159
133
|
if (methodResponse[0] === 'Mailbox/get') {
|
|
160
134
|
return methodResponse[1].list;
|
|
161
135
|
}
|
|
162
|
-
throw new
|
|
136
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to get mailboxes');
|
|
163
137
|
}
|
|
164
138
|
/**
|
|
165
139
|
* Find a mailbox by name
|
|
@@ -194,7 +168,7 @@ async function queryEmails(accountId, filter = {}, sort = [{ property: 'received
|
|
|
194
168
|
total: result.total,
|
|
195
169
|
};
|
|
196
170
|
}
|
|
197
|
-
throw new
|
|
171
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to query emails');
|
|
198
172
|
}
|
|
199
173
|
/**
|
|
200
174
|
* Get emails by IDs
|
|
@@ -223,7 +197,7 @@ async function getEmails(accountId, ids, properties = [
|
|
|
223
197
|
if (methodResponse[0] === 'Email/get') {
|
|
224
198
|
return methodResponse[1].list;
|
|
225
199
|
}
|
|
226
|
-
throw new
|
|
200
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to get emails');
|
|
227
201
|
}
|
|
228
202
|
/**
|
|
229
203
|
* Create and send an email
|
|
@@ -231,7 +205,7 @@ async function getEmails(accountId, ids, properties = [
|
|
|
231
205
|
async function sendEmail(accountId, email, identityId) {
|
|
232
206
|
const draftsMailbox = await findMailboxByRole.call(this, accountId, 'drafts');
|
|
233
207
|
if (!draftsMailbox) {
|
|
234
|
-
throw new
|
|
208
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Drafts mailbox not found');
|
|
235
209
|
}
|
|
236
210
|
const emailCreate = {
|
|
237
211
|
...email,
|
|
@@ -252,7 +226,7 @@ async function sendEmail(accountId, email, identityId) {
|
|
|
252
226
|
], [exports.JMAP_CAPABILITIES.CORE, exports.JMAP_CAPABILITIES.MAIL, exports.JMAP_CAPABILITIES.SUBMISSION]);
|
|
253
227
|
for (const methodResponse of response.methodResponses) {
|
|
254
228
|
if (methodResponse[0] === 'error') {
|
|
255
|
-
throw new
|
|
229
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `JMAP error: ${JSON.stringify(methodResponse[1])}`);
|
|
256
230
|
}
|
|
257
231
|
}
|
|
258
232
|
return response.methodResponses[1][1];
|
|
@@ -263,7 +237,7 @@ async function sendEmail(accountId, email, identityId) {
|
|
|
263
237
|
async function createDraft(accountId, email) {
|
|
264
238
|
const draftsMailbox = await findMailboxByRole.call(this, accountId, 'drafts');
|
|
265
239
|
if (!draftsMailbox) {
|
|
266
|
-
throw new
|
|
240
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Drafts mailbox not found');
|
|
267
241
|
}
|
|
268
242
|
const emailCreate = {
|
|
269
243
|
...email,
|
|
@@ -273,7 +247,7 @@ async function createDraft(accountId, email) {
|
|
|
273
247
|
const response = await jmapApiRequest.call(this, [['Email/set', { accountId, create: { draft: emailCreate } }, 'c1']]);
|
|
274
248
|
const methodResponse = response.methodResponses[0];
|
|
275
249
|
if (methodResponse[0] === 'error') {
|
|
276
|
-
throw new
|
|
250
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `JMAP error: ${JSON.stringify(methodResponse[1])}`);
|
|
277
251
|
}
|
|
278
252
|
if (methodResponse[0] === 'Email/set') {
|
|
279
253
|
const result = methodResponse[1];
|
|
@@ -293,7 +267,7 @@ async function getIdentities(accountId) {
|
|
|
293
267
|
if (methodResponse[0] === 'Identity/get') {
|
|
294
268
|
return methodResponse[1].list;
|
|
295
269
|
}
|
|
296
|
-
throw new
|
|
270
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to get identities');
|
|
297
271
|
}
|
|
298
272
|
/**
|
|
299
273
|
* Update email keywords
|
|
@@ -304,7 +278,7 @@ async function updateEmailKeywords(accountId, emailId, keywords) {
|
|
|
304
278
|
if (methodResponse[0] === 'Email/set') {
|
|
305
279
|
return methodResponse[1];
|
|
306
280
|
}
|
|
307
|
-
throw new
|
|
281
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to update email');
|
|
308
282
|
}
|
|
309
283
|
/**
|
|
310
284
|
* Move email to a different mailbox
|
|
@@ -324,7 +298,7 @@ async function moveEmail(accountId, emailId, targetMailboxId) {
|
|
|
324
298
|
if (methodResponse[0] === 'Email/set') {
|
|
325
299
|
return methodResponse[1];
|
|
326
300
|
}
|
|
327
|
-
throw new
|
|
301
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to move email');
|
|
328
302
|
}
|
|
329
303
|
/**
|
|
330
304
|
* Add a label (mailbox) to an email
|
|
@@ -344,7 +318,7 @@ async function addLabel(accountId, emailId, mailboxId) {
|
|
|
344
318
|
if (methodResponse[0] === 'Email/set') {
|
|
345
319
|
return methodResponse[1];
|
|
346
320
|
}
|
|
347
|
-
throw new
|
|
321
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to add label');
|
|
348
322
|
}
|
|
349
323
|
/**
|
|
350
324
|
* Remove a label (mailbox) from an email
|
|
@@ -364,7 +338,7 @@ async function removeLabel(accountId, emailId, mailboxId) {
|
|
|
364
338
|
if (methodResponse[0] === 'Email/set') {
|
|
365
339
|
return methodResponse[1];
|
|
366
340
|
}
|
|
367
|
-
throw new
|
|
341
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to remove label');
|
|
368
342
|
}
|
|
369
343
|
/**
|
|
370
344
|
* Get labels (mailboxes) for an email with their names
|
|
@@ -372,7 +346,7 @@ async function removeLabel(accountId, emailId, mailboxId) {
|
|
|
372
346
|
async function getLabels(accountId, emailId) {
|
|
373
347
|
const emails = await getEmails.call(this, accountId, [emailId], ['id', 'mailboxIds']);
|
|
374
348
|
if (emails.length === 0) {
|
|
375
|
-
throw new
|
|
349
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Email not found');
|
|
376
350
|
}
|
|
377
351
|
const email = emails[0];
|
|
378
352
|
const mailboxIds = email.mailboxIds;
|
|
@@ -407,7 +381,7 @@ async function deleteEmails(accountId, emailIds) {
|
|
|
407
381
|
if (methodResponse[0] === 'Email/set') {
|
|
408
382
|
return methodResponse[1];
|
|
409
383
|
}
|
|
410
|
-
throw new
|
|
384
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to delete emails');
|
|
411
385
|
}
|
|
412
386
|
/**
|
|
413
387
|
* Get threads
|
|
@@ -418,7 +392,7 @@ async function getThreads(accountId, ids) {
|
|
|
418
392
|
if (methodResponse[0] === 'Thread/get') {
|
|
419
393
|
return methodResponse[1].list;
|
|
420
394
|
}
|
|
421
|
-
throw new
|
|
395
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to get threads');
|
|
422
396
|
}
|
|
423
397
|
/**
|
|
424
398
|
* Download an attachment blob
|
|
@@ -431,34 +405,12 @@ async function downloadBlob(accountId, blobId, name, type) {
|
|
|
431
405
|
.replace('{blobId}', blobId)
|
|
432
406
|
.replace('{name}', encodeURIComponent(name))
|
|
433
407
|
.replace('{type}', encodeURIComponent(type));
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
return Buffer.from(response);
|
|
441
|
-
}
|
|
442
|
-
else {
|
|
443
|
-
const credentials = await this.getCredentials('jmapApi');
|
|
444
|
-
const authMethod = credentials.authMethod || 'basicAuth';
|
|
445
|
-
const headers = {};
|
|
446
|
-
if (authMethod === 'basicAuth') {
|
|
447
|
-
const authString = Buffer.from(`${credentials.email}:${credentials.password}`).toString('base64');
|
|
448
|
-
headers.Authorization = `Basic ${authString}`;
|
|
449
|
-
}
|
|
450
|
-
else if (authMethod === 'bearerToken') {
|
|
451
|
-
headers.Authorization = `Bearer ${credentials.accessToken}`;
|
|
452
|
-
}
|
|
453
|
-
const options = {
|
|
454
|
-
method: 'GET',
|
|
455
|
-
url: downloadUrl,
|
|
456
|
-
headers,
|
|
457
|
-
encoding: 'arraybuffer',
|
|
458
|
-
};
|
|
459
|
-
const response = await this.helpers.httpRequest(options);
|
|
460
|
-
return Buffer.from(response);
|
|
461
|
-
}
|
|
408
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, authType, {
|
|
409
|
+
method: 'GET',
|
|
410
|
+
url: downloadUrl,
|
|
411
|
+
encoding: 'arraybuffer',
|
|
412
|
+
});
|
|
413
|
+
return Buffer.from(response);
|
|
462
414
|
}
|
|
463
415
|
/**
|
|
464
416
|
* Check if a MIME type matches a filter pattern
|
|
@@ -486,7 +438,7 @@ async function getAttachments(accountId, emailId, options = {}) {
|
|
|
486
438
|
'attachments',
|
|
487
439
|
]);
|
|
488
440
|
if (emails.length === 0) {
|
|
489
|
-
throw new
|
|
441
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Email with ID ${emailId} not found`);
|
|
490
442
|
}
|
|
491
443
|
const email = emails[0];
|
|
492
444
|
const attachments = email.attachments || [];
|
|
@@ -18,15 +18,24 @@ class Jmap {
|
|
|
18
18
|
defaults: {
|
|
19
19
|
name: 'JMAP',
|
|
20
20
|
},
|
|
21
|
-
inputs: [
|
|
22
|
-
outputs: [
|
|
21
|
+
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
22
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
23
23
|
credentials: [
|
|
24
24
|
{
|
|
25
|
-
name: '
|
|
25
|
+
name: 'jmapBasicAuthApi',
|
|
26
26
|
required: true,
|
|
27
27
|
displayOptions: {
|
|
28
28
|
show: {
|
|
29
|
-
authentication: ['
|
|
29
|
+
authentication: ['jmapBasicAuthApi'],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'jmapBearerTokenApi',
|
|
35
|
+
required: true,
|
|
36
|
+
displayOptions: {
|
|
37
|
+
show: {
|
|
38
|
+
authentication: ['jmapBearerTokenApi'],
|
|
30
39
|
},
|
|
31
40
|
},
|
|
32
41
|
},
|
|
@@ -52,8 +61,12 @@ class Jmap {
|
|
|
52
61
|
value: 'jmapOAuth2Api',
|
|
53
62
|
},
|
|
54
63
|
{
|
|
55
|
-
name: 'Basic Auth
|
|
56
|
-
value: '
|
|
64
|
+
name: 'Basic Auth',
|
|
65
|
+
value: 'jmapBasicAuthApi',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'Bearer Token',
|
|
69
|
+
value: 'jmapBearerTokenApi',
|
|
57
70
|
},
|
|
58
71
|
],
|
|
59
72
|
default: 'jmapOAuth2Api',
|
|
@@ -608,7 +621,12 @@ class Jmap {
|
|
|
608
621
|
const resource = this.getNodeParameter('resource', 0);
|
|
609
622
|
const operation = this.getNodeParameter('operation', 0);
|
|
610
623
|
const accountId = await GenericFunctions_1.getPrimaryAccountId.call(this);
|
|
611
|
-
|
|
624
|
+
// Operations that should only run once, regardless of input items
|
|
625
|
+
const singleExecutionOperations = [
|
|
626
|
+
'getMany', // Email/Mailbox/Thread getMany - query with filters, not per-item
|
|
627
|
+
];
|
|
628
|
+
const itemsToProcess = singleExecutionOperations.includes(operation) ? 1 : items.length;
|
|
629
|
+
for (let i = 0; i < itemsToProcess; i++) {
|
|
612
630
|
try {
|
|
613
631
|
let responseData = {};
|
|
614
632
|
// ==================== EMAIL ====================
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.JmapTrigger = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
4
5
|
const GenericFunctions_1 = require("./GenericFunctions");
|
|
5
6
|
class JmapTrigger {
|
|
6
7
|
constructor() {
|
|
@@ -16,14 +17,23 @@ class JmapTrigger {
|
|
|
16
17
|
name: 'JMAP Trigger',
|
|
17
18
|
},
|
|
18
19
|
inputs: [],
|
|
19
|
-
outputs: [
|
|
20
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
20
21
|
credentials: [
|
|
21
22
|
{
|
|
22
|
-
name: '
|
|
23
|
+
name: 'jmapBasicAuthApi',
|
|
23
24
|
required: true,
|
|
24
25
|
displayOptions: {
|
|
25
26
|
show: {
|
|
26
|
-
authentication: ['
|
|
27
|
+
authentication: ['jmapBasicAuthApi'],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'jmapBearerTokenApi',
|
|
33
|
+
required: true,
|
|
34
|
+
displayOptions: {
|
|
35
|
+
show: {
|
|
36
|
+
authentication: ['jmapBearerTokenApi'],
|
|
27
37
|
},
|
|
28
38
|
},
|
|
29
39
|
},
|
|
@@ -50,8 +60,12 @@ class JmapTrigger {
|
|
|
50
60
|
value: 'jmapOAuth2Api',
|
|
51
61
|
},
|
|
52
62
|
{
|
|
53
|
-
name: 'Basic Auth
|
|
54
|
-
value: '
|
|
63
|
+
name: 'Basic Auth',
|
|
64
|
+
value: 'jmapBasicAuthApi',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'Bearer Token',
|
|
68
|
+
value: 'jmapBearerTokenApi',
|
|
55
69
|
},
|
|
56
70
|
],
|
|
57
71
|
default: 'jmapOAuth2Api',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-jmap",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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",
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
"n8n": {
|
|
39
39
|
"n8nNodesApiVersion": 1,
|
|
40
40
|
"credentials": [
|
|
41
|
-
"dist/credentials/
|
|
41
|
+
"dist/credentials/JmapBasicAuthApi.credentials.js",
|
|
42
|
+
"dist/credentials/JmapBearerTokenApi.credentials.js",
|
|
42
43
|
"dist/credentials/JmapOAuth2Api.credentials.js"
|
|
43
44
|
],
|
|
44
45
|
"nodes": [
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
-
/**
|
|
3
|
-
* JMAP API Credentials for Basic Auth or Bearer Token authentication
|
|
4
|
-
*
|
|
5
|
-
* For OAuth2/OIDC with LemonLDAP, use the JmapOAuth2Api credentials instead.
|
|
6
|
-
*/
|
|
7
|
-
export declare class JmapApi implements ICredentialType {
|
|
8
|
-
name: string;
|
|
9
|
-
displayName: string;
|
|
10
|
-
documentationUrl: string;
|
|
11
|
-
properties: INodeProperties[];
|
|
12
|
-
test: ICredentialTestRequest;
|
|
13
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.JmapApi = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* JMAP API Credentials for Basic Auth or Bearer Token authentication
|
|
6
|
-
*
|
|
7
|
-
* For OAuth2/OIDC with LemonLDAP, use the JmapOAuth2Api credentials instead.
|
|
8
|
-
*/
|
|
9
|
-
class JmapApi {
|
|
10
|
-
constructor() {
|
|
11
|
-
this.name = 'jmapApi';
|
|
12
|
-
this.displayName = 'JMAP API';
|
|
13
|
-
this.documentationUrl = 'https://jmap.io/spec-core.html';
|
|
14
|
-
this.properties = [
|
|
15
|
-
{
|
|
16
|
-
displayName: 'JMAP Server URL',
|
|
17
|
-
name: 'serverUrl',
|
|
18
|
-
type: 'string',
|
|
19
|
-
default: 'https://jmap.example.com/jmap',
|
|
20
|
-
placeholder: 'https://jmap.example.com/jmap',
|
|
21
|
-
description: 'The base URL of the JMAP server',
|
|
22
|
-
required: true,
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
displayName: 'Authentication Method',
|
|
26
|
-
name: 'authMethod',
|
|
27
|
-
type: 'options',
|
|
28
|
-
options: [
|
|
29
|
-
{
|
|
30
|
-
name: 'Basic Auth',
|
|
31
|
-
value: 'basicAuth',
|
|
32
|
-
description: 'Authenticate with email and password',
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: 'Bearer Token',
|
|
36
|
-
value: 'bearerToken',
|
|
37
|
-
description: 'Authenticate with an existing access token',
|
|
38
|
-
},
|
|
39
|
-
],
|
|
40
|
-
default: 'basicAuth',
|
|
41
|
-
},
|
|
42
|
-
// Basic Auth fields
|
|
43
|
-
{
|
|
44
|
-
displayName: 'Email',
|
|
45
|
-
name: 'email',
|
|
46
|
-
type: 'string',
|
|
47
|
-
placeholder: 'user@example.com',
|
|
48
|
-
default: '',
|
|
49
|
-
required: true,
|
|
50
|
-
displayOptions: {
|
|
51
|
-
show: {
|
|
52
|
-
authMethod: ['basicAuth'],
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
displayName: 'Password',
|
|
58
|
-
name: 'password',
|
|
59
|
-
type: 'string',
|
|
60
|
-
typeOptions: {
|
|
61
|
-
password: true,
|
|
62
|
-
},
|
|
63
|
-
default: '',
|
|
64
|
-
required: true,
|
|
65
|
-
displayOptions: {
|
|
66
|
-
show: {
|
|
67
|
-
authMethod: ['basicAuth'],
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
// Bearer Token field
|
|
72
|
-
{
|
|
73
|
-
displayName: 'Access Token',
|
|
74
|
-
name: 'accessToken',
|
|
75
|
-
type: 'string',
|
|
76
|
-
typeOptions: {
|
|
77
|
-
password: true,
|
|
78
|
-
},
|
|
79
|
-
default: '',
|
|
80
|
-
required: true,
|
|
81
|
-
description: 'The access token (e.g., from OAuth2/OIDC)',
|
|
82
|
-
displayOptions: {
|
|
83
|
-
show: {
|
|
84
|
-
authMethod: ['bearerToken'],
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
];
|
|
89
|
-
this.test = {
|
|
90
|
-
request: {
|
|
91
|
-
baseURL: '={{$credentials.serverUrl}}',
|
|
92
|
-
url: '/session',
|
|
93
|
-
method: 'GET',
|
|
94
|
-
headers: {
|
|
95
|
-
Accept: 'application/json',
|
|
96
|
-
},
|
|
97
|
-
auth: {
|
|
98
|
-
username: '={{$credentials.email}}',
|
|
99
|
-
password: '={{$credentials.password}}',
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
exports.JmapApi = JmapApi;
|