emailengine-app 2.68.1 → 2.70.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/.github/workflows/deploy.yml +8 -3
- package/.github/workflows/release.yaml +6 -0
- package/CHANGELOG.md +59 -0
- package/Gruntfile.js +3 -1
- package/config/default.toml +2 -0
- package/data/google-crawlers.json +7 -1
- package/getswagger.sh +40 -4
- package/gettext-extract.js +163 -0
- package/lib/account.js +135 -72
- package/lib/api-routes/account-routes.js +684 -106
- package/lib/api-routes/blocklist-routes.js +344 -0
- package/lib/api-routes/chat-routes.js +32 -14
- package/lib/api-routes/delivery-test-routes.js +346 -0
- package/lib/api-routes/export-routes.js +28 -14
- package/lib/api-routes/gateway-routes.js +427 -0
- package/lib/api-routes/license-routes.js +156 -0
- package/lib/api-routes/mailbox-routes.js +344 -0
- package/lib/api-routes/message-routes.js +221 -187
- package/lib/api-routes/oauth2-app-routes.js +697 -0
- package/lib/api-routes/outbox-routes.js +185 -0
- package/lib/api-routes/pubsub-routes.js +102 -0
- package/lib/api-routes/route-helpers.js +58 -0
- package/lib/api-routes/settings-routes.js +357 -0
- package/lib/api-routes/stats-routes.js +111 -0
- package/lib/api-routes/submit-routes.js +461 -0
- package/lib/api-routes/template-routes.js +60 -75
- package/lib/api-routes/token-routes.js +297 -0
- package/lib/api-routes/webhook-route-routes.js +181 -0
- package/lib/autodetect-imap-settings.js +0 -2
- package/lib/consts.js +5 -0
- package/lib/email-client/base-client.js +28 -6
- package/lib/email-client/gmail-client.js +133 -112
- package/lib/email-client/imap/mailbox.js +34 -11
- package/lib/email-client/imap/subconnection.js +20 -13
- package/lib/email-client/imap/sync-operations.js +131 -3
- package/lib/email-client/imap-client.js +152 -75
- package/lib/email-client/notification-handler.js +1 -4
- package/lib/email-client/outlook-client.js +134 -75
- package/lib/export.js +97 -20
- package/lib/feature-flags.js +2 -2
- package/lib/gateway.js +4 -9
- package/lib/get-raw-email.js +5 -5
- package/lib/imapproxy/imap-core/lib/commands/starttls.js +18 -0
- package/lib/imapproxy/imap-core/lib/imap-command.js +6 -1
- package/lib/imapproxy/imap-core/lib/imap-connection.js +106 -24
- package/lib/imapproxy/imap-core/lib/imap-server.js +24 -0
- package/lib/imapproxy/imap-core/lib/imap-stream.js +26 -0
- package/lib/logger.js +24 -21
- package/lib/message-port-stream.js +113 -16
- package/lib/metrics-collector.js +0 -2
- package/lib/oauth2-apps.js +13 -4
- package/lib/outbox.js +24 -40
- package/lib/redis-operations.js +1 -1
- package/lib/reject-worker-calls.js +42 -0
- package/lib/routes-ui.js +37 -8778
- package/lib/schemas.js +429 -84
- package/lib/sentry.js +139 -0
- package/lib/settings.js +9 -3
- package/lib/stream-encrypt.js +1 -1
- package/lib/templates.js +1 -1
- package/lib/tokens.js +5 -3
- package/lib/tools.js +70 -4
- package/lib/ui-routes/account-routes.js +45 -212
- package/lib/ui-routes/admin-config-routes.js +928 -489
- package/lib/ui-routes/admin-entities-routes.js +1 -0
- package/lib/ui-routes/auth-routes.js +1339 -0
- package/lib/ui-routes/dashboard-routes.js +188 -0
- package/lib/ui-routes/document-store-routes.js +800 -0
- package/lib/ui-routes/export-routes.js +217 -0
- package/lib/ui-routes/internals-routes.js +354 -0
- package/lib/ui-routes/network-config-routes.js +759 -0
- package/lib/ui-routes/{oauth-routes.js → oauth-config-routes.js} +369 -91
- package/lib/ui-routes/route-helpers.js +314 -0
- package/lib/ui-routes/smtp-test-routes.js +236 -0
- package/lib/ui-routes/unsubscribe-routes.js +232 -0
- package/lib/webhook-request.js +36 -0
- package/lib/webhooks.js +8 -4
- package/package.json +13 -12
- package/sbom.json +1 -1
- package/server.js +222 -39
- package/static/licenses.html +160 -300
- package/translations/messages.pot +112 -132
- package/update-info.sh +19 -1
- package/views/config/logging.hbs +48 -0
- package/views/dashboard.hbs +7 -26
- package/views/internals/index.hbs +15 -0
- package/views/tokens/index.hbs +9 -0
- package/workers/api.js +200 -4424
- package/workers/documents.js +2 -22
- package/workers/export.js +103 -104
- package/workers/imap-proxy.js +3 -23
- package/workers/imap.js +32 -36
- package/workers/smtp.js +2 -22
- package/workers/submit.js +26 -35
- package/workers/webhooks.js +9 -43
|
@@ -4,25 +4,47 @@ const { redis } = require('../db');
|
|
|
4
4
|
const { Account } = require('../account');
|
|
5
5
|
const getSecret = require('../get-secret');
|
|
6
6
|
const settings = require('../settings');
|
|
7
|
-
const Boom = require('@hapi/boom');
|
|
8
7
|
const Joi = require('joi');
|
|
9
8
|
const { failAction } = require('../tools');
|
|
9
|
+
const { handleError } = require('./route-helpers');
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
12
|
accountIdSchema,
|
|
13
13
|
messageDetailsSchema,
|
|
14
14
|
messageListSchema,
|
|
15
|
+
messageEntrySchema,
|
|
15
16
|
documentStoreSchema,
|
|
17
|
+
documentStoreQuerySchema,
|
|
16
18
|
searchSchema,
|
|
17
19
|
messageUpdateSchema,
|
|
18
20
|
addressSchema,
|
|
19
21
|
fromAddressSchema,
|
|
20
|
-
messageReferenceSchema
|
|
22
|
+
messageReferenceSchema,
|
|
23
|
+
emailIdSchema,
|
|
24
|
+
threadIdSchema,
|
|
25
|
+
errorResponses
|
|
21
26
|
} = require('../schemas');
|
|
22
27
|
|
|
23
28
|
const listMessageFolderPathDescription =
|
|
24
29
|
'Mailbox folder path. Can use special use labels like "\\Sent". Special value "\\All" is available for Gmail IMAP, Gmail API, MS Graph API accounts.';
|
|
25
30
|
|
|
31
|
+
// Response field for a flag or label update operation. IMAP accounts return a boolean (whether
|
|
32
|
+
// the operation succeeded), API-based accounts echo the requested values as an array.
|
|
33
|
+
const updateOpResultSchema = (op, noun, entryLabel, example, label) =>
|
|
34
|
+
Joi.alternatives()
|
|
35
|
+
.try(Joi.boolean(), Joi.array().items(Joi.string().label(entryLabel)))
|
|
36
|
+
.example(example)
|
|
37
|
+
.description(`Did the ${op} operation succeed (boolean, IMAP accounts) or the requested ${noun} (array, API-based accounts)`)
|
|
38
|
+
.label(label);
|
|
39
|
+
|
|
40
|
+
// Source-to-target ID map for messages that were moved to another folder. Only the IMAP backend
|
|
41
|
+
// provides this info.
|
|
42
|
+
const idMapSchema = Joi.array()
|
|
43
|
+
.items(Joi.array().length(2).items(Joi.string().max(256).required().description('Message ID')).label('IdMapTuple'))
|
|
44
|
+
.example([['AAAAAQAACnA', 'AAAAAwAAAD4']])
|
|
45
|
+
.description('An optional map of source and target ID values, if the server provided this info (IMAP accounts only)')
|
|
46
|
+
.label('IdMapArray');
|
|
47
|
+
|
|
26
48
|
async function init(args) {
|
|
27
49
|
const { server, call, CORS_CONFIG, MAX_ATTACHMENT_SIZE, MAX_BODY_SIZE, MAX_PAYLOAD_TIMEOUT } = args;
|
|
28
50
|
|
|
@@ -44,15 +66,7 @@ async function init(args) {
|
|
|
44
66
|
const response = await accountObject.getRawMessage(request.params.message);
|
|
45
67
|
return h.response(response);
|
|
46
68
|
} catch (err) {
|
|
47
|
-
|
|
48
|
-
if (Boom.isBoom(err)) {
|
|
49
|
-
throw err;
|
|
50
|
-
}
|
|
51
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
52
|
-
if (err.code) {
|
|
53
|
-
error.output.payload.code = err.code;
|
|
54
|
-
}
|
|
55
|
-
throw error;
|
|
69
|
+
handleError(request, err);
|
|
56
70
|
}
|
|
57
71
|
},
|
|
58
72
|
options: {
|
|
@@ -68,7 +82,8 @@ async function init(args) {
|
|
|
68
82
|
|
|
69
83
|
plugins: {
|
|
70
84
|
'hapi-swagger': {
|
|
71
|
-
produces: ['message/rfc822']
|
|
85
|
+
produces: ['message/rfc822'],
|
|
86
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
72
87
|
}
|
|
73
88
|
},
|
|
74
89
|
|
|
@@ -106,15 +121,7 @@ async function init(args) {
|
|
|
106
121
|
try {
|
|
107
122
|
return await accountObject.getMessage(request.params.message, request.query);
|
|
108
123
|
} catch (err) {
|
|
109
|
-
|
|
110
|
-
if (Boom.isBoom(err)) {
|
|
111
|
-
throw err;
|
|
112
|
-
}
|
|
113
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
114
|
-
if (err.code) {
|
|
115
|
-
error.output.payload.code = err.code;
|
|
116
|
-
}
|
|
117
|
-
throw error;
|
|
124
|
+
handleError(request, err);
|
|
118
125
|
}
|
|
119
126
|
},
|
|
120
127
|
options: {
|
|
@@ -122,6 +129,12 @@ async function init(args) {
|
|
|
122
129
|
notes: 'Returns details of a specific message. By default text content is not included, use textType value to force retrieving text',
|
|
123
130
|
tags: ['api', 'Message'],
|
|
124
131
|
|
|
132
|
+
plugins: {
|
|
133
|
+
'hapi-swagger': {
|
|
134
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
125
138
|
auth: {
|
|
126
139
|
strategy: 'api-token',
|
|
127
140
|
mode: 'required'
|
|
@@ -213,15 +226,7 @@ async function init(args) {
|
|
|
213
226
|
try {
|
|
214
227
|
return await accountObject.uploadMessage(request.payload);
|
|
215
228
|
} catch (err) {
|
|
216
|
-
|
|
217
|
-
if (Boom.isBoom(err)) {
|
|
218
|
-
throw err;
|
|
219
|
-
}
|
|
220
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
221
|
-
if (err.code) {
|
|
222
|
-
error.output.payload.code = err.code;
|
|
223
|
-
}
|
|
224
|
-
throw error;
|
|
229
|
+
handleError(request, err);
|
|
225
230
|
}
|
|
226
231
|
},
|
|
227
232
|
options: {
|
|
@@ -234,7 +239,11 @@ async function init(args) {
|
|
|
234
239
|
notes: 'Upload a message structure, compile it into an EML file and store it into selected mailbox.',
|
|
235
240
|
tags: ['api', 'Message'],
|
|
236
241
|
|
|
237
|
-
plugins: {
|
|
242
|
+
plugins: {
|
|
243
|
+
'hapi-swagger': {
|
|
244
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
245
|
+
}
|
|
246
|
+
},
|
|
238
247
|
|
|
239
248
|
auth: {
|
|
240
249
|
strategy: 'api-token',
|
|
@@ -348,9 +357,11 @@ async function init(args) {
|
|
|
348
357
|
)
|
|
349
358
|
.label('MessageAppendId'),
|
|
350
359
|
path: Joi.string().example('INBOX').description('Folder this message was uploaded to').label('MessageAppendPath'),
|
|
351
|
-
uid: Joi.number().integer().example(12345).description('UID of uploaded message'),
|
|
352
|
-
uidValidity: Joi.string()
|
|
353
|
-
|
|
360
|
+
uid: Joi.number().integer().example(12345).description('UID of uploaded message (IMAP accounts only)'),
|
|
361
|
+
uidValidity: Joi.string()
|
|
362
|
+
.example('12345')
|
|
363
|
+
.description('UIDVALIDITY of the target folder. Numeric value cast as string. IMAP accounts only.'),
|
|
364
|
+
seq: Joi.number().integer().example(12345).description('Sequence number of uploaded message (IMAP accounts only)'),
|
|
354
365
|
|
|
355
366
|
messageId: Joi.string().max(996).example('<test123@example.com>').description('Message ID'),
|
|
356
367
|
|
|
@@ -358,9 +369,9 @@ async function init(args) {
|
|
|
358
369
|
message: Joi.string()
|
|
359
370
|
.base64({ paddingRequired: false, urlSafe: true })
|
|
360
371
|
.max(256)
|
|
361
|
-
.required()
|
|
362
372
|
.example('AAAAAQAACnA')
|
|
363
|
-
.description('Referenced message ID'),
|
|
373
|
+
.description('Referenced message ID. Not present when only a thread ID was referenced'),
|
|
374
|
+
threadId: threadIdSchema.description('Referenced thread ID (Gmail API accounts only)').label('ResponseReferenceThreadId'),
|
|
364
375
|
success: Joi.boolean().example(true).description('Was the referenced message processed').label('ResponseReferenceSuccess'),
|
|
365
376
|
documentStore: documentStoreSchema.default(false),
|
|
366
377
|
error: Joi.string().example('Referenced message was not found').description('An error message if referenced message processing failed')
|
|
@@ -390,15 +401,7 @@ async function init(args) {
|
|
|
390
401
|
try {
|
|
391
402
|
return await accountObject.updateMessage(request.params.message, request.payload);
|
|
392
403
|
} catch (err) {
|
|
393
|
-
|
|
394
|
-
if (Boom.isBoom(err)) {
|
|
395
|
-
throw err;
|
|
396
|
-
}
|
|
397
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
398
|
-
if (err.code) {
|
|
399
|
-
error.output.payload.code = err.code;
|
|
400
|
-
}
|
|
401
|
-
throw error;
|
|
404
|
+
handleError(request, err);
|
|
402
405
|
}
|
|
403
406
|
},
|
|
404
407
|
options: {
|
|
@@ -406,7 +409,11 @@ async function init(args) {
|
|
|
406
409
|
notes: 'Update message information. Mainly this means changing message flag values',
|
|
407
410
|
tags: ['api', 'Message'],
|
|
408
411
|
|
|
409
|
-
plugins: {
|
|
412
|
+
plugins: {
|
|
413
|
+
'hapi-swagger': {
|
|
414
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
415
|
+
}
|
|
416
|
+
},
|
|
410
417
|
|
|
411
418
|
auth: {
|
|
412
419
|
strategy: 'api-token',
|
|
@@ -432,14 +439,24 @@ async function init(args) {
|
|
|
432
439
|
response: {
|
|
433
440
|
schema: Joi.object({
|
|
434
441
|
flags: Joi.object({
|
|
435
|
-
add:
|
|
436
|
-
delete:
|
|
437
|
-
set:
|
|
442
|
+
add: updateOpResultSchema('add', 'flags', 'FlagEntry', true, 'FlagAddResult'),
|
|
443
|
+
delete: updateOpResultSchema('delete', 'flags', 'FlagEntry', false, 'FlagDeleteResult'),
|
|
444
|
+
set: updateOpResultSchema('set', 'flags', 'FlagEntry', true, 'FlagSetResult'),
|
|
445
|
+
result: Joi.array()
|
|
446
|
+
.items(Joi.string().label('FlagEntry'))
|
|
447
|
+
.example(['\\Seen'])
|
|
448
|
+
.description('Resulting flag set after the update (Gmail API and MS Graph API accounts only)')
|
|
449
|
+
.label('FlagResultList')
|
|
438
450
|
}).label('FlagUpdateResponse'),
|
|
439
451
|
labels: Joi.object({
|
|
440
|
-
add:
|
|
441
|
-
delete:
|
|
442
|
-
set:
|
|
452
|
+
add: updateOpResultSchema('add', 'labels', 'LabelEntry', ['Label1', 'Label2'], 'LabelAddResult'),
|
|
453
|
+
delete: updateOpResultSchema('delete', 'labels', 'LabelEntry', ['Label3'], 'LabelDeleteResult'),
|
|
454
|
+
set: updateOpResultSchema('set', 'labels', 'LabelEntry', ['Label1'], 'LabelSetResult'),
|
|
455
|
+
result: Joi.array()
|
|
456
|
+
.items(Joi.string().label('LabelEntry'))
|
|
457
|
+
.example(['Label1'])
|
|
458
|
+
.description('Resulting label set after the update (Gmail API and MS Graph API accounts only)')
|
|
459
|
+
.label('LabelResultList')
|
|
443
460
|
}).label('LabelUpdateResponse')
|
|
444
461
|
}).label('MessageUpdateResponse'),
|
|
445
462
|
failAction: 'log'
|
|
@@ -464,15 +481,7 @@ async function init(args) {
|
|
|
464
481
|
try {
|
|
465
482
|
return await accountObject.updateMessages(request.query.path, request.payload.search, request.payload.update);
|
|
466
483
|
} catch (err) {
|
|
467
|
-
|
|
468
|
-
if (Boom.isBoom(err)) {
|
|
469
|
-
throw err;
|
|
470
|
-
}
|
|
471
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
472
|
-
if (err.code) {
|
|
473
|
-
error.output.payload.code = err.code;
|
|
474
|
-
}
|
|
475
|
-
throw error;
|
|
484
|
+
handleError(request, err);
|
|
476
485
|
}
|
|
477
486
|
},
|
|
478
487
|
options: {
|
|
@@ -480,7 +489,11 @@ async function init(args) {
|
|
|
480
489
|
notes: 'Update message information for matching emails',
|
|
481
490
|
tags: ['api', 'Multi Message Actions'],
|
|
482
491
|
|
|
483
|
-
plugins: {
|
|
492
|
+
plugins: {
|
|
493
|
+
'hapi-swagger': {
|
|
494
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
495
|
+
}
|
|
496
|
+
},
|
|
484
497
|
|
|
485
498
|
auth: {
|
|
486
499
|
strategy: 'api-token',
|
|
@@ -512,15 +525,19 @@ async function init(args) {
|
|
|
512
525
|
response: {
|
|
513
526
|
schema: Joi.object({
|
|
514
527
|
flags: Joi.object({
|
|
515
|
-
add:
|
|
516
|
-
delete:
|
|
517
|
-
set:
|
|
528
|
+
add: updateOpResultSchema('add', 'flags', 'BulkFlagEntry', true, 'BulkFlagAddResult'),
|
|
529
|
+
delete: updateOpResultSchema('delete', 'flags', 'BulkFlagEntry', false, 'BulkFlagDeleteResult'),
|
|
530
|
+
set: updateOpResultSchema('set', 'flags', 'BulkFlagEntry', true, 'BulkFlagSetResult')
|
|
518
531
|
}).label('BulkFlagUpdateResponse'),
|
|
519
532
|
labels: Joi.object({
|
|
520
|
-
add:
|
|
521
|
-
delete:
|
|
522
|
-
set:
|
|
523
|
-
}).label('BulkLabelUpdateResponse')
|
|
533
|
+
add: updateOpResultSchema('add', 'labels', 'BulkLabelEntry', ['Label1', 'Label2'], 'BulkLabelAddResult'),
|
|
534
|
+
delete: updateOpResultSchema('delete', 'labels', 'BulkLabelEntry', ['Label3'], 'BulkLabelDeleteResult'),
|
|
535
|
+
set: updateOpResultSchema('set', 'labels', 'BulkLabelEntry', ['Label1'], 'BulkLabelSetResult')
|
|
536
|
+
}).label('BulkLabelUpdateResponse'),
|
|
537
|
+
emailIds: Joi.array()
|
|
538
|
+
.items(emailIdSchema)
|
|
539
|
+
.description('List of updated email IDs (Gmail API and MS Graph API accounts only)')
|
|
540
|
+
.label('BulkUpdatedEmailIds')
|
|
524
541
|
}).label('BulkMessageUpdateResponse'),
|
|
525
542
|
failAction: 'log'
|
|
526
543
|
}
|
|
@@ -548,15 +565,7 @@ async function init(args) {
|
|
|
548
565
|
}
|
|
549
566
|
return await accountObject.moveMessage(request.params.message, { path: request.payload.path }, { source: sourceOption });
|
|
550
567
|
} catch (err) {
|
|
551
|
-
|
|
552
|
-
if (Boom.isBoom(err)) {
|
|
553
|
-
throw err;
|
|
554
|
-
}
|
|
555
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
556
|
-
if (err.code) {
|
|
557
|
-
error.output.payload.code = err.code;
|
|
558
|
-
}
|
|
559
|
-
throw error;
|
|
568
|
+
handleError(request, err);
|
|
560
569
|
}
|
|
561
570
|
},
|
|
562
571
|
options: {
|
|
@@ -564,7 +573,11 @@ async function init(args) {
|
|
|
564
573
|
notes: 'Moves a message to a target folder',
|
|
565
574
|
tags: ['api', 'Message'],
|
|
566
575
|
|
|
567
|
-
plugins: {
|
|
576
|
+
plugins: {
|
|
577
|
+
'hapi-swagger': {
|
|
578
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
579
|
+
}
|
|
580
|
+
},
|
|
568
581
|
|
|
569
582
|
auth: {
|
|
570
583
|
strategy: 'api-token',
|
|
@@ -626,15 +639,7 @@ async function init(args) {
|
|
|
626
639
|
try {
|
|
627
640
|
return await accountObject.moveMessages(request.query.path, request.payload.search, { path: request.payload.path });
|
|
628
641
|
} catch (err) {
|
|
629
|
-
|
|
630
|
-
if (Boom.isBoom(err)) {
|
|
631
|
-
throw err;
|
|
632
|
-
}
|
|
633
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
634
|
-
if (err.code) {
|
|
635
|
-
error.output.payload.code = err.code;
|
|
636
|
-
}
|
|
637
|
-
throw error;
|
|
642
|
+
handleError(request, err);
|
|
638
643
|
}
|
|
639
644
|
},
|
|
640
645
|
options: {
|
|
@@ -642,7 +647,11 @@ async function init(args) {
|
|
|
642
647
|
notes: 'Move messages matching to a search query to another folder',
|
|
643
648
|
tags: ['api', 'Multi Message Actions'],
|
|
644
649
|
|
|
645
|
-
plugins: {
|
|
650
|
+
plugins: {
|
|
651
|
+
'hapi-swagger': {
|
|
652
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
653
|
+
}
|
|
654
|
+
},
|
|
646
655
|
|
|
647
656
|
auth: {
|
|
648
657
|
strategy: 'api-token',
|
|
@@ -676,16 +685,15 @@ async function init(args) {
|
|
|
676
685
|
schema: Joi.object({
|
|
677
686
|
path: Joi.string().required().example('INBOX').description('Target mailbox folder path'),
|
|
678
687
|
|
|
679
|
-
idMap:
|
|
680
|
-
.items(Joi.array().length(2).items(Joi.string().max(256).required().description('Message ID')).label('IdMapTuple'))
|
|
681
|
-
.example([['AAAAAQAACnA', 'AAAAAwAAAD4']])
|
|
682
|
-
.description('An optional map of source and target ID values, if the server provided this info')
|
|
683
|
-
.label('IdMapArray'),
|
|
688
|
+
idMap: idMapSchema,
|
|
684
689
|
|
|
685
690
|
emailIds: Joi.array()
|
|
686
|
-
.items(
|
|
687
|
-
.
|
|
688
|
-
.
|
|
691
|
+
.items(emailIdSchema)
|
|
692
|
+
.allow(null)
|
|
693
|
+
.description(
|
|
694
|
+
'An optional list of emailId values, if the server supports unique email IDs (Gmail API and MS Graph API accounts only). Null when no messages matched the search (Gmail API)'
|
|
695
|
+
)
|
|
696
|
+
.label('MovedEmailIdsArray')
|
|
689
697
|
}).label('MessagesMoveResponse'),
|
|
690
698
|
failAction: 'log'
|
|
691
699
|
}
|
|
@@ -709,15 +717,7 @@ async function init(args) {
|
|
|
709
717
|
try {
|
|
710
718
|
return await accountObject.deleteMessage(request.params.message, request.query.force);
|
|
711
719
|
} catch (err) {
|
|
712
|
-
|
|
713
|
-
if (Boom.isBoom(err)) {
|
|
714
|
-
throw err;
|
|
715
|
-
}
|
|
716
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
717
|
-
if (err.code) {
|
|
718
|
-
error.output.payload.code = err.code;
|
|
719
|
-
}
|
|
720
|
-
throw error;
|
|
720
|
+
handleError(request, err);
|
|
721
721
|
}
|
|
722
722
|
},
|
|
723
723
|
options: {
|
|
@@ -725,7 +725,11 @@ async function init(args) {
|
|
|
725
725
|
notes: 'Move message to Trash or delete it if already in Trash',
|
|
726
726
|
tags: ['api', 'Message'],
|
|
727
727
|
|
|
728
|
-
plugins: {
|
|
728
|
+
plugins: {
|
|
729
|
+
'hapi-swagger': {
|
|
730
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
731
|
+
}
|
|
732
|
+
},
|
|
729
733
|
|
|
730
734
|
auth: {
|
|
731
735
|
strategy: 'api-token',
|
|
@@ -757,10 +761,20 @@ async function init(args) {
|
|
|
757
761
|
},
|
|
758
762
|
response: {
|
|
759
763
|
schema: Joi.object({
|
|
760
|
-
deleted: Joi.boolean()
|
|
764
|
+
deleted: Joi.boolean()
|
|
765
|
+
.example(false)
|
|
766
|
+
.description(
|
|
767
|
+
'Was the message deleted permanently. IMAP accounts return false when the message was moved to Trash, Gmail API and MS Graph API accounts return true also for moves to Trash'
|
|
768
|
+
),
|
|
761
769
|
moved: Joi.object({
|
|
762
|
-
destination: Joi.string()
|
|
763
|
-
|
|
770
|
+
destination: Joi.string()
|
|
771
|
+
.example('Trash')
|
|
772
|
+
.description('Trash folder path. Can be missing if the server did not provide the destination folder')
|
|
773
|
+
.label('TrashPath'),
|
|
774
|
+
message: Joi.string()
|
|
775
|
+
.example('AAAAAwAAAWg')
|
|
776
|
+
.description('Message ID in Trash. Can be missing if the server did not provide the new message ID')
|
|
777
|
+
.label('TrashMessageId')
|
|
764
778
|
})
|
|
765
779
|
.description('Present if message was moved to Trash')
|
|
766
780
|
.label('MessageMovedToTrash')
|
|
@@ -787,15 +801,7 @@ async function init(args) {
|
|
|
787
801
|
try {
|
|
788
802
|
return await accountObject.deleteMessages(request.query.path, request.payload.search, request.query.force);
|
|
789
803
|
} catch (err) {
|
|
790
|
-
|
|
791
|
-
if (Boom.isBoom(err)) {
|
|
792
|
-
throw err;
|
|
793
|
-
}
|
|
794
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
795
|
-
if (err.code) {
|
|
796
|
-
error.output.payload.code = err.code;
|
|
797
|
-
}
|
|
798
|
-
throw error;
|
|
804
|
+
handleError(request, err);
|
|
799
805
|
}
|
|
800
806
|
},
|
|
801
807
|
options: {
|
|
@@ -803,7 +809,11 @@ async function init(args) {
|
|
|
803
809
|
notes: 'Move messages to Trash or delete these if already in Trash',
|
|
804
810
|
tags: ['api', 'Multi Message Actions'],
|
|
805
811
|
|
|
806
|
-
plugins: {
|
|
812
|
+
plugins: {
|
|
813
|
+
'hapi-swagger': {
|
|
814
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
815
|
+
}
|
|
816
|
+
},
|
|
807
817
|
|
|
808
818
|
auth: {
|
|
809
819
|
strategy: 'api-token',
|
|
@@ -829,7 +839,7 @@ async function init(args) {
|
|
|
829
839
|
.truthy('Y', 'true', '1')
|
|
830
840
|
.falsy('N', 'false', 0)
|
|
831
841
|
.default(false)
|
|
832
|
-
.description('Delete messages even if not in Trash')
|
|
842
|
+
.description('Delete messages even if not in Trash. Not supported for Gmail API accounts (messages are always moved to Trash)')
|
|
833
843
|
.label('ForceDelete')
|
|
834
844
|
}).label('MessagesDeleteQuery'),
|
|
835
845
|
|
|
@@ -840,23 +850,33 @@ async function init(args) {
|
|
|
840
850
|
|
|
841
851
|
response: {
|
|
842
852
|
schema: Joi.object({
|
|
843
|
-
deleted: Joi.boolean()
|
|
853
|
+
deleted: Joi.boolean()
|
|
854
|
+
.example(false)
|
|
855
|
+
.description(
|
|
856
|
+
'Was the delete action executed. IMAP accounts return false when messages were moved to Trash, Gmail API and MS Graph API accounts return true also for moves to Trash'
|
|
857
|
+
),
|
|
844
858
|
moved: Joi.object({
|
|
845
859
|
destination: Joi.string().required().example('Trash').description('Trash folder path').label('TrashPath'),
|
|
846
860
|
|
|
847
|
-
idMap:
|
|
848
|
-
.items(Joi.array().length(2).items(Joi.string().max(256).required().description('Message ID')).label('IdMapTuple'))
|
|
849
|
-
.example([['AAAAAQAACnA', 'AAAAAwAAAD4']])
|
|
850
|
-
.description('An optional map of source and target ID values, if the server provided this info')
|
|
851
|
-
.label('IdMapArray'),
|
|
861
|
+
idMap: idMapSchema,
|
|
852
862
|
|
|
853
863
|
emailIds: Joi.array()
|
|
854
|
-
.items(
|
|
855
|
-
.description(
|
|
856
|
-
|
|
864
|
+
.items(emailIdSchema)
|
|
865
|
+
.description(
|
|
866
|
+
'An optional list of emailId values, if the server supports unique email IDs (Gmail API and MS Graph API accounts only)'
|
|
867
|
+
)
|
|
868
|
+
.label('TrashedEmailIdsArray')
|
|
857
869
|
})
|
|
858
870
|
.label('MessagesMovedToTrash')
|
|
859
|
-
.description('Value is present if messages were moved to Trash')
|
|
871
|
+
.description('Value is present if messages were moved to Trash'),
|
|
872
|
+
deletedMessages: Joi.object({
|
|
873
|
+
emailIds: Joi.array()
|
|
874
|
+
.items(emailIdSchema)
|
|
875
|
+
.description('List of emailId values of the permanently deleted messages')
|
|
876
|
+
.label('DeletedEmailIdsArray')
|
|
877
|
+
})
|
|
878
|
+
.label('MessagesDeleted')
|
|
879
|
+
.description('Value is present if messages were deleted permanently (MS Graph API accounts only)')
|
|
860
880
|
}).label('MessagesDeleteResponse'),
|
|
861
881
|
failAction: 'log'
|
|
862
882
|
}
|
|
@@ -881,15 +901,7 @@ async function init(args) {
|
|
|
881
901
|
try {
|
|
882
902
|
return await accountObject.listMessages(request.query);
|
|
883
903
|
} catch (err) {
|
|
884
|
-
|
|
885
|
-
if (Boom.isBoom(err)) {
|
|
886
|
-
throw err;
|
|
887
|
-
}
|
|
888
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
889
|
-
if (err.code) {
|
|
890
|
-
error.output.payload.code = err.code;
|
|
891
|
-
}
|
|
892
|
-
throw error;
|
|
904
|
+
handleError(request, err);
|
|
893
905
|
}
|
|
894
906
|
},
|
|
895
907
|
options: {
|
|
@@ -897,6 +909,12 @@ async function init(args) {
|
|
|
897
909
|
notes: 'Lists messages in a mailbox folder',
|
|
898
910
|
tags: ['api', 'Message'],
|
|
899
911
|
|
|
912
|
+
plugins: {
|
|
913
|
+
'hapi-swagger': {
|
|
914
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
915
|
+
}
|
|
916
|
+
},
|
|
917
|
+
|
|
900
918
|
auth: {
|
|
901
919
|
strategy: 'api-token',
|
|
902
920
|
mode: 'required'
|
|
@@ -988,15 +1006,7 @@ async function init(args) {
|
|
|
988
1006
|
try {
|
|
989
1007
|
return await accountObject.searchMessages(Object.assign(request.query, request.payload));
|
|
990
1008
|
} catch (err) {
|
|
991
|
-
|
|
992
|
-
if (Boom.isBoom(err)) {
|
|
993
|
-
throw err;
|
|
994
|
-
}
|
|
995
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
996
|
-
if (err.code) {
|
|
997
|
-
error.output.payload.code = err.code;
|
|
998
|
-
}
|
|
999
|
-
throw error;
|
|
1009
|
+
handleError(request, err);
|
|
1000
1010
|
}
|
|
1001
1011
|
},
|
|
1002
1012
|
options: {
|
|
@@ -1004,7 +1014,11 @@ async function init(args) {
|
|
|
1004
1014
|
notes: 'Filter messages from a mailbox folder by search options. Search is performed against a specific folder and not for the entire account.',
|
|
1005
1015
|
tags: ['api', 'Message'],
|
|
1006
1016
|
|
|
1007
|
-
plugins: {
|
|
1017
|
+
plugins: {
|
|
1018
|
+
'hapi-swagger': {
|
|
1019
|
+
responses: errorResponses(400, 401, 403, 404, 422, 429, 500, 503)
|
|
1020
|
+
}
|
|
1021
|
+
},
|
|
1008
1022
|
|
|
1009
1023
|
auth: {
|
|
1010
1024
|
strategy: 'api-token',
|
|
@@ -1059,7 +1073,7 @@ async function init(args) {
|
|
|
1059
1073
|
.truthy('Y', 'true', '1')
|
|
1060
1074
|
.falsy('N', 'false', 0)
|
|
1061
1075
|
.description(
|
|
1062
|
-
'MS Graph only. If enabled, uses the $search parameter for MS Graph search queries instead of $filter. This allows searching the "to", "cc", "bcc", "larger", "smaller", "body", "before", "sentBefore", "since", and the "sentSince" fields. Note that $search returns up to 1,000 results, does not indicate the total number of matching results or pages, and returns results sorted by relevance rather than date.'
|
|
1076
|
+
'MS Graph only. If enabled, uses the $search parameter for MS Graph search queries instead of $filter. This allows searching the "to", "cc", "bcc", "larger", "smaller", "body", "before", "sentBefore", "since", and the "sentSince" fields. Note that $search returns up to 1,000 results, does not indicate the total number of matching results or pages, and returns results sorted by relevance rather than date. The "labels" filter is not available in this mode - leave this option disabled to filter by category.'
|
|
1063
1077
|
)
|
|
1064
1078
|
.label('useOutlookSearch')
|
|
1065
1079
|
.optional(),
|
|
@@ -1068,7 +1082,7 @@ async function init(args) {
|
|
|
1068
1082
|
exposeQuery: Joi.boolean()
|
|
1069
1083
|
.truthy('Y', 'true', '1')
|
|
1070
1084
|
.falsy('N', 'false', 0)
|
|
1071
|
-
.description('If enabled then
|
|
1085
|
+
.description('If enabled then includes the combined query (as the documentStoreQuery field) in the response for debugging')
|
|
1072
1086
|
.label('exposeQuery')
|
|
1073
1087
|
.when('documentStore', {
|
|
1074
1088
|
is: true,
|
|
@@ -1102,7 +1116,11 @@ async function init(args) {
|
|
|
1102
1116
|
header: {
|
|
1103
1117
|
'Message-ID': '<12345@example.com>'
|
|
1104
1118
|
},
|
|
1105
|
-
gmailRaw: 'has:attachment in:unread'
|
|
1119
|
+
gmailRaw: 'has:attachment in:unread',
|
|
1120
|
+
labels: {
|
|
1121
|
+
has: ['Important'],
|
|
1122
|
+
not: ['Horizon']
|
|
1123
|
+
}
|
|
1106
1124
|
}
|
|
1107
1125
|
})
|
|
1108
1126
|
},
|
|
@@ -1152,23 +1170,19 @@ async function init(args) {
|
|
|
1152
1170
|
try {
|
|
1153
1171
|
return await accountObject.searchMessages(Object.assign({ documentStore: true }, request.query, request.payload), { unified: true });
|
|
1154
1172
|
} catch (err) {
|
|
1155
|
-
|
|
1156
|
-
if (Boom.isBoom(err)) {
|
|
1157
|
-
throw err;
|
|
1158
|
-
}
|
|
1159
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
1160
|
-
if (err.code) {
|
|
1161
|
-
error.output.payload.code = err.code;
|
|
1162
|
-
}
|
|
1163
|
-
throw error;
|
|
1173
|
+
handleError(request, err);
|
|
1164
1174
|
}
|
|
1165
1175
|
},
|
|
1166
1176
|
options: {
|
|
1167
1177
|
description: 'Unified search for messages',
|
|
1168
1178
|
notes: 'Filter messages from the Document Store for multiple accounts or paths. Document Store must be enabled for the unified search to work.',
|
|
1169
|
-
tags: ['Deprecated endpoints (Document Store)'],
|
|
1179
|
+
tags: ['api', 'Deprecated endpoints (Document Store)'],
|
|
1170
1180
|
|
|
1171
|
-
plugins: {
|
|
1181
|
+
plugins: {
|
|
1182
|
+
'hapi-swagger': {
|
|
1183
|
+
responses: errorResponses(400, 401, 403, 429, 500)
|
|
1184
|
+
}
|
|
1185
|
+
},
|
|
1172
1186
|
|
|
1173
1187
|
auth: {
|
|
1174
1188
|
strategy: 'api-token',
|
|
@@ -1196,7 +1210,7 @@ async function init(args) {
|
|
|
1196
1210
|
exposeQuery: Joi.boolean()
|
|
1197
1211
|
.truthy('Y', 'true', '1')
|
|
1198
1212
|
.falsy('N', 'false', 0)
|
|
1199
|
-
.description('If enabled then
|
|
1213
|
+
.description('If enabled then includes the combined query (as the documentStoreQuery field) in the response for debugging')
|
|
1200
1214
|
.label('exposeQuery')
|
|
1201
1215
|
.optional()
|
|
1202
1216
|
.meta({ swaggerHidden: true })
|
|
@@ -1219,7 +1233,34 @@ async function init(args) {
|
|
|
1219
1233
|
},
|
|
1220
1234
|
|
|
1221
1235
|
response: {
|
|
1222
|
-
schema:
|
|
1236
|
+
schema: Joi.object({
|
|
1237
|
+
total: Joi.number()
|
|
1238
|
+
.integer()
|
|
1239
|
+
.example(120)
|
|
1240
|
+
.description('Total number of matching messages (capped at 10000 by the Document Store)')
|
|
1241
|
+
.label('UnifiedTotalNumber'),
|
|
1242
|
+
page: Joi.number().integer().example(0).description('Current page number (zero-based)').label('UnifiedPageNumber'),
|
|
1243
|
+
pages: Joi.number().integer().example(24).description('Total number of pages available').label('UnifiedPagesNumber'),
|
|
1244
|
+
accounts: Joi.array()
|
|
1245
|
+
.items(Joi.string().example('example'))
|
|
1246
|
+
.description('Account filter used for the search, if provided')
|
|
1247
|
+
.label('UnifiedSearchAccountsEcho'),
|
|
1248
|
+
paths: Joi.array()
|
|
1249
|
+
.items(Joi.string().example('INBOX'))
|
|
1250
|
+
.description('Path filter used for the search, if provided')
|
|
1251
|
+
.label('UnifiedSearchPathsEcho'),
|
|
1252
|
+
messages: Joi.array()
|
|
1253
|
+
.items(
|
|
1254
|
+
messageEntrySchema
|
|
1255
|
+
.keys({
|
|
1256
|
+
account: accountIdSchema.description('Account ID this message belongs to')
|
|
1257
|
+
})
|
|
1258
|
+
.unknown()
|
|
1259
|
+
.label('UnifiedMessageListEntry')
|
|
1260
|
+
)
|
|
1261
|
+
.label('UnifiedPageMessages'),
|
|
1262
|
+
documentStoreQuery: documentStoreQuerySchema
|
|
1263
|
+
}).label('UnifiedSearchResponse'),
|
|
1223
1264
|
failAction: 'log'
|
|
1224
1265
|
}
|
|
1225
1266
|
}
|
|
@@ -1243,15 +1284,7 @@ async function init(args) {
|
|
|
1243
1284
|
try {
|
|
1244
1285
|
return await accountObject.getText(request.params.text, request.query);
|
|
1245
1286
|
} catch (err) {
|
|
1246
|
-
|
|
1247
|
-
if (Boom.isBoom(err)) {
|
|
1248
|
-
throw err;
|
|
1249
|
-
}
|
|
1250
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
1251
|
-
if (err.code) {
|
|
1252
|
-
error.output.payload.code = err.code;
|
|
1253
|
-
}
|
|
1254
|
-
throw error;
|
|
1287
|
+
handleError(request, err);
|
|
1255
1288
|
}
|
|
1256
1289
|
},
|
|
1257
1290
|
options: {
|
|
@@ -1259,6 +1292,12 @@ async function init(args) {
|
|
|
1259
1292
|
notes: 'Retrieves message text',
|
|
1260
1293
|
tags: ['api', 'Message'],
|
|
1261
1294
|
|
|
1295
|
+
plugins: {
|
|
1296
|
+
'hapi-swagger': {
|
|
1297
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
1298
|
+
}
|
|
1299
|
+
},
|
|
1300
|
+
|
|
1262
1301
|
auth: {
|
|
1263
1302
|
strategy: 'api-token',
|
|
1264
1303
|
mode: 'required'
|
|
@@ -1279,7 +1318,7 @@ async function init(args) {
|
|
|
1279
1318
|
.min(0)
|
|
1280
1319
|
.max(1024 * 1024 * 1024)
|
|
1281
1320
|
.example(MAX_ATTACHMENT_SIZE)
|
|
1282
|
-
.description('Max length of text content'),
|
|
1321
|
+
.description('Max length of text content. Ignored for Gmail API and MS Graph API accounts (full content is always returned)'),
|
|
1283
1322
|
textType: Joi.string()
|
|
1284
1323
|
.lowercase()
|
|
1285
1324
|
.valid('html', 'plain', '*')
|
|
@@ -1305,7 +1344,9 @@ async function init(args) {
|
|
|
1305
1344
|
schema: Joi.object({
|
|
1306
1345
|
plain: Joi.string().example('Hello world').description('Plaintext content'),
|
|
1307
1346
|
html: Joi.string().example('<p>Hello world</p>').description('HTML content'),
|
|
1308
|
-
hasMore: Joi.boolean()
|
|
1347
|
+
hasMore: Joi.boolean()
|
|
1348
|
+
.example(false)
|
|
1349
|
+
.description('Is the current text output capped or not. Always false for Gmail API and MS Graph API accounts')
|
|
1309
1350
|
}).label('TextResponse'),
|
|
1310
1351
|
failAction: 'log'
|
|
1311
1352
|
}
|
|
@@ -1329,15 +1370,7 @@ async function init(args) {
|
|
|
1329
1370
|
try {
|
|
1330
1371
|
return await accountObject.getAttachment(request.params.attachment);
|
|
1331
1372
|
} catch (err) {
|
|
1332
|
-
|
|
1333
|
-
if (Boom.isBoom(err)) {
|
|
1334
|
-
throw err;
|
|
1335
|
-
}
|
|
1336
|
-
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
|
|
1337
|
-
if (err.code) {
|
|
1338
|
-
error.output.payload.code = err.code;
|
|
1339
|
-
}
|
|
1340
|
-
throw error;
|
|
1373
|
+
handleError(request, err);
|
|
1341
1374
|
}
|
|
1342
1375
|
},
|
|
1343
1376
|
options: {
|
|
@@ -1353,7 +1386,8 @@ async function init(args) {
|
|
|
1353
1386
|
|
|
1354
1387
|
plugins: {
|
|
1355
1388
|
'hapi-swagger': {
|
|
1356
|
-
produces: ['application/octet-stream']
|
|
1389
|
+
produces: ['application/octet-stream'],
|
|
1390
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
1357
1391
|
}
|
|
1358
1392
|
},
|
|
1359
1393
|
|