emailengine-app 2.69.0 → 2.71.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 +6 -3
- package/.github/workflows/release.yaml +2 -0
- package/.github/workflows/test.yml +73 -12
- package/.ncurc.js +3 -3
- package/CHANGELOG.md +37 -0
- package/Gruntfile.js +21 -23
- package/bin/emailengine.js +8 -1
- package/config/default.toml +5 -0
- package/config/test.toml +5 -0
- package/data/google-crawlers.json +1 -1
- package/getswagger.sh +44 -4
- package/gettext-extract.js +163 -0
- package/lib/account.js +104 -72
- package/lib/api-routes/account-routes.js +231 -71
- package/lib/api-routes/blocklist-routes.js +25 -18
- package/lib/api-routes/chat-routes.js +32 -14
- package/lib/api-routes/delivery-test-routes.js +30 -5
- package/lib/api-routes/export-routes.js +27 -2
- package/lib/api-routes/gateway-routes.js +63 -12
- package/lib/api-routes/license-routes.js +18 -4
- package/lib/api-routes/mailbox-routes.js +33 -7
- package/lib/api-routes/message-routes.js +291 -145
- package/lib/api-routes/oauth2-app-routes.js +90 -24
- package/lib/api-routes/outbox-routes.js +16 -4
- package/lib/api-routes/pubsub-routes.js +8 -4
- package/lib/api-routes/route-helpers.js +14 -1
- package/lib/api-routes/settings-routes.js +51 -25
- package/lib/api-routes/stats-routes.js +37 -3
- package/lib/api-routes/submit-routes.js +31 -42
- package/lib/api-routes/template-routes.js +54 -21
- package/lib/api-routes/token-routes.js +67 -67
- package/lib/api-routes/webhook-route-routes.js +37 -8
- package/lib/autodetect-imap-settings.js +0 -2
- package/lib/consts.js +5 -0
- package/lib/document-store.js +22 -1
- package/lib/email-client/base-client.js +31 -8
- package/lib/email-client/gmail-client.js +119 -112
- package/lib/email-client/imap/mailbox.js +2 -2
- package/lib/email-client/imap/subconnection.js +0 -1
- package/lib/email-client/imap/sync-operations.js +1 -1
- package/lib/email-client/imap-client.js +36 -17
- package/lib/email-client/notification-handler.js +3 -6
- package/lib/email-client/outlook-client.js +49 -62
- package/lib/export.js +49 -1
- package/lib/feature-flags.js +8 -2
- package/lib/gateway.js +4 -9
- package/lib/get-raw-email.js +5 -5
- package/lib/imapproxy/imap-core/lib/imap-connection.js +0 -1
- package/lib/license-beacon.js +367 -0
- package/lib/logger.js +35 -22
- 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/routes-ui.js +2 -1
- package/lib/schemas.js +403 -83
- 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 +28 -6
- package/lib/ui-routes/account-routes.js +7 -4
- package/lib/ui-routes/admin-config-routes.js +20 -6
- package/lib/ui-routes/document-store-routes.js +7 -1
- package/lib/ui-routes/oauth-config-routes.js +0 -2
- package/lib/ui-routes/route-helpers.js +0 -2
- package/lib/ui-routes/unsubscribe-routes.js +0 -2
- package/lib/webhooks.js +8 -4
- package/package.json +23 -19
- package/sbom.json +1 -1
- package/server.js +38 -31
- package/static/licenses.html +171 -391
- package/translations/de.mo +0 -0
- package/translations/de.po +154 -142
- package/translations/et.mo +0 -0
- package/translations/et.po +129 -131
- package/translations/fr.mo +0 -0
- package/translations/fr.po +133 -136
- package/translations/ja.mo +0 -0
- package/translations/ja.po +126 -129
- package/translations/messages.pot +107 -107
- package/translations/nl.mo +0 -0
- package/translations/nl.po +128 -130
- package/translations/pl.mo +0 -0
- package/translations/pl.po +125 -128
- package/update-info.sh +19 -1
- package/views/config/logging.hbs +48 -0
- package/views/dashboard.hbs +22 -0
- package/workers/api.js +33 -37
- package/workers/documents.js +2 -22
- package/workers/export.js +73 -92
- package/workers/imap-proxy.js +3 -23
- package/workers/imap.js +2 -22
- package/workers/smtp.js +2 -22
- package/workers/submit.js +6 -24
- package/workers/webhooks.js +2 -22
|
@@ -12,19 +12,41 @@ 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
|
-
const { server, call, CORS_CONFIG, MAX_ATTACHMENT_SIZE, MAX_BODY_SIZE, MAX_PAYLOAD_TIMEOUT } = args;
|
|
49
|
+
const { server, call, CORS_CONFIG, MAX_ATTACHMENT_SIZE, MAX_BODY_SIZE, MAX_PAYLOAD_TIMEOUT, documentStoreFeatureEnabled } = args;
|
|
28
50
|
|
|
29
51
|
// GET /v1/account/{account}/message/{message}/source - Download raw message
|
|
30
52
|
server.route({
|
|
@@ -60,7 +82,8 @@ async function init(args) {
|
|
|
60
82
|
|
|
61
83
|
plugins: {
|
|
62
84
|
'hapi-swagger': {
|
|
63
|
-
produces: ['message/rfc822']
|
|
85
|
+
produces: ['message/rfc822'],
|
|
86
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
64
87
|
}
|
|
65
88
|
},
|
|
66
89
|
|
|
@@ -106,6 +129,12 @@ async function init(args) {
|
|
|
106
129
|
notes: 'Returns details of a specific message. By default text content is not included, use textType value to force retrieving text',
|
|
107
130
|
tags: ['api', 'Message'],
|
|
108
131
|
|
|
132
|
+
plugins: {
|
|
133
|
+
'hapi-swagger': {
|
|
134
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
109
138
|
auth: {
|
|
110
139
|
strategy: 'api-token',
|
|
111
140
|
mode: 'required'
|
|
@@ -210,7 +239,11 @@ async function init(args) {
|
|
|
210
239
|
notes: 'Upload a message structure, compile it into an EML file and store it into selected mailbox.',
|
|
211
240
|
tags: ['api', 'Message'],
|
|
212
241
|
|
|
213
|
-
plugins: {
|
|
242
|
+
plugins: {
|
|
243
|
+
'hapi-swagger': {
|
|
244
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
245
|
+
}
|
|
246
|
+
},
|
|
214
247
|
|
|
215
248
|
auth: {
|
|
216
249
|
strategy: 'api-token',
|
|
@@ -324,9 +357,11 @@ async function init(args) {
|
|
|
324
357
|
)
|
|
325
358
|
.label('MessageAppendId'),
|
|
326
359
|
path: Joi.string().example('INBOX').description('Folder this message was uploaded to').label('MessageAppendPath'),
|
|
327
|
-
uid: Joi.number().integer().example(12345).description('UID of uploaded message'),
|
|
328
|
-
uidValidity: Joi.string()
|
|
329
|
-
|
|
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)'),
|
|
330
365
|
|
|
331
366
|
messageId: Joi.string().max(996).example('<test123@example.com>').description('Message ID'),
|
|
332
367
|
|
|
@@ -334,9 +369,9 @@ async function init(args) {
|
|
|
334
369
|
message: Joi.string()
|
|
335
370
|
.base64({ paddingRequired: false, urlSafe: true })
|
|
336
371
|
.max(256)
|
|
337
|
-
.required()
|
|
338
372
|
.example('AAAAAQAACnA')
|
|
339
|
-
.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'),
|
|
340
375
|
success: Joi.boolean().example(true).description('Was the referenced message processed').label('ResponseReferenceSuccess'),
|
|
341
376
|
documentStore: documentStoreSchema.default(false),
|
|
342
377
|
error: Joi.string().example('Referenced message was not found').description('An error message if referenced message processing failed')
|
|
@@ -374,7 +409,11 @@ async function init(args) {
|
|
|
374
409
|
notes: 'Update message information. Mainly this means changing message flag values',
|
|
375
410
|
tags: ['api', 'Message'],
|
|
376
411
|
|
|
377
|
-
plugins: {
|
|
412
|
+
plugins: {
|
|
413
|
+
'hapi-swagger': {
|
|
414
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
415
|
+
}
|
|
416
|
+
},
|
|
378
417
|
|
|
379
418
|
auth: {
|
|
380
419
|
strategy: 'api-token',
|
|
@@ -400,14 +439,24 @@ async function init(args) {
|
|
|
400
439
|
response: {
|
|
401
440
|
schema: Joi.object({
|
|
402
441
|
flags: Joi.object({
|
|
403
|
-
add:
|
|
404
|
-
delete:
|
|
405
|
-
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')
|
|
406
450
|
}).label('FlagUpdateResponse'),
|
|
407
451
|
labels: Joi.object({
|
|
408
|
-
add:
|
|
409
|
-
delete:
|
|
410
|
-
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')
|
|
411
460
|
}).label('LabelUpdateResponse')
|
|
412
461
|
}).label('MessageUpdateResponse'),
|
|
413
462
|
failAction: 'log'
|
|
@@ -440,7 +489,11 @@ async function init(args) {
|
|
|
440
489
|
notes: 'Update message information for matching emails',
|
|
441
490
|
tags: ['api', 'Multi Message Actions'],
|
|
442
491
|
|
|
443
|
-
plugins: {
|
|
492
|
+
plugins: {
|
|
493
|
+
'hapi-swagger': {
|
|
494
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
495
|
+
}
|
|
496
|
+
},
|
|
444
497
|
|
|
445
498
|
auth: {
|
|
446
499
|
strategy: 'api-token',
|
|
@@ -472,15 +525,19 @@ async function init(args) {
|
|
|
472
525
|
response: {
|
|
473
526
|
schema: Joi.object({
|
|
474
527
|
flags: Joi.object({
|
|
475
|
-
add:
|
|
476
|
-
delete:
|
|
477
|
-
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')
|
|
478
531
|
}).label('BulkFlagUpdateResponse'),
|
|
479
532
|
labels: Joi.object({
|
|
480
|
-
add:
|
|
481
|
-
delete:
|
|
482
|
-
set:
|
|
483
|
-
}).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')
|
|
484
541
|
}).label('BulkMessageUpdateResponse'),
|
|
485
542
|
failAction: 'log'
|
|
486
543
|
}
|
|
@@ -516,7 +573,11 @@ async function init(args) {
|
|
|
516
573
|
notes: 'Moves a message to a target folder',
|
|
517
574
|
tags: ['api', 'Message'],
|
|
518
575
|
|
|
519
|
-
plugins: {
|
|
576
|
+
plugins: {
|
|
577
|
+
'hapi-swagger': {
|
|
578
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
579
|
+
}
|
|
580
|
+
},
|
|
520
581
|
|
|
521
582
|
auth: {
|
|
522
583
|
strategy: 'api-token',
|
|
@@ -586,7 +647,11 @@ async function init(args) {
|
|
|
586
647
|
notes: 'Move messages matching to a search query to another folder',
|
|
587
648
|
tags: ['api', 'Multi Message Actions'],
|
|
588
649
|
|
|
589
|
-
plugins: {
|
|
650
|
+
plugins: {
|
|
651
|
+
'hapi-swagger': {
|
|
652
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
653
|
+
}
|
|
654
|
+
},
|
|
590
655
|
|
|
591
656
|
auth: {
|
|
592
657
|
strategy: 'api-token',
|
|
@@ -620,16 +685,15 @@ async function init(args) {
|
|
|
620
685
|
schema: Joi.object({
|
|
621
686
|
path: Joi.string().required().example('INBOX').description('Target mailbox folder path'),
|
|
622
687
|
|
|
623
|
-
idMap:
|
|
624
|
-
.items(Joi.array().length(2).items(Joi.string().max(256).required().description('Message ID')).label('IdMapTuple'))
|
|
625
|
-
.example([['AAAAAQAACnA', 'AAAAAwAAAD4']])
|
|
626
|
-
.description('An optional map of source and target ID values, if the server provided this info')
|
|
627
|
-
.label('IdMapArray'),
|
|
688
|
+
idMap: idMapSchema,
|
|
628
689
|
|
|
629
690
|
emailIds: Joi.array()
|
|
630
|
-
.items(
|
|
631
|
-
.
|
|
632
|
-
.
|
|
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')
|
|
633
697
|
}).label('MessagesMoveResponse'),
|
|
634
698
|
failAction: 'log'
|
|
635
699
|
}
|
|
@@ -661,7 +725,11 @@ async function init(args) {
|
|
|
661
725
|
notes: 'Move message to Trash or delete it if already in Trash',
|
|
662
726
|
tags: ['api', 'Message'],
|
|
663
727
|
|
|
664
|
-
plugins: {
|
|
728
|
+
plugins: {
|
|
729
|
+
'hapi-swagger': {
|
|
730
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
731
|
+
}
|
|
732
|
+
},
|
|
665
733
|
|
|
666
734
|
auth: {
|
|
667
735
|
strategy: 'api-token',
|
|
@@ -693,10 +761,20 @@ async function init(args) {
|
|
|
693
761
|
},
|
|
694
762
|
response: {
|
|
695
763
|
schema: Joi.object({
|
|
696
|
-
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
|
+
),
|
|
697
769
|
moved: Joi.object({
|
|
698
|
-
destination: Joi.string()
|
|
699
|
-
|
|
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')
|
|
700
778
|
})
|
|
701
779
|
.description('Present if message was moved to Trash')
|
|
702
780
|
.label('MessageMovedToTrash')
|
|
@@ -731,7 +809,11 @@ async function init(args) {
|
|
|
731
809
|
notes: 'Move messages to Trash or delete these if already in Trash',
|
|
732
810
|
tags: ['api', 'Multi Message Actions'],
|
|
733
811
|
|
|
734
|
-
plugins: {
|
|
812
|
+
plugins: {
|
|
813
|
+
'hapi-swagger': {
|
|
814
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
815
|
+
}
|
|
816
|
+
},
|
|
735
817
|
|
|
736
818
|
auth: {
|
|
737
819
|
strategy: 'api-token',
|
|
@@ -757,7 +839,7 @@ async function init(args) {
|
|
|
757
839
|
.truthy('Y', 'true', '1')
|
|
758
840
|
.falsy('N', 'false', 0)
|
|
759
841
|
.default(false)
|
|
760
|
-
.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)')
|
|
761
843
|
.label('ForceDelete')
|
|
762
844
|
}).label('MessagesDeleteQuery'),
|
|
763
845
|
|
|
@@ -768,23 +850,33 @@ async function init(args) {
|
|
|
768
850
|
|
|
769
851
|
response: {
|
|
770
852
|
schema: Joi.object({
|
|
771
|
-
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
|
+
),
|
|
772
858
|
moved: Joi.object({
|
|
773
859
|
destination: Joi.string().required().example('Trash').description('Trash folder path').label('TrashPath'),
|
|
774
860
|
|
|
775
|
-
idMap:
|
|
776
|
-
.items(Joi.array().length(2).items(Joi.string().max(256).required().description('Message ID')).label('IdMapTuple'))
|
|
777
|
-
.example([['AAAAAQAACnA', 'AAAAAwAAAD4']])
|
|
778
|
-
.description('An optional map of source and target ID values, if the server provided this info')
|
|
779
|
-
.label('IdMapArray'),
|
|
861
|
+
idMap: idMapSchema,
|
|
780
862
|
|
|
781
863
|
emailIds: Joi.array()
|
|
782
|
-
.items(
|
|
783
|
-
.description(
|
|
784
|
-
|
|
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')
|
|
785
869
|
})
|
|
786
870
|
.label('MessagesMovedToTrash')
|
|
787
|
-
.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)')
|
|
788
880
|
}).label('MessagesDeleteResponse'),
|
|
789
881
|
failAction: 'log'
|
|
790
882
|
}
|
|
@@ -817,6 +909,12 @@ async function init(args) {
|
|
|
817
909
|
notes: 'Lists messages in a mailbox folder',
|
|
818
910
|
tags: ['api', 'Message'],
|
|
819
911
|
|
|
912
|
+
plugins: {
|
|
913
|
+
'hapi-swagger': {
|
|
914
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
915
|
+
}
|
|
916
|
+
},
|
|
917
|
+
|
|
820
918
|
auth: {
|
|
821
919
|
strategy: 'api-token',
|
|
822
920
|
mode: 'required'
|
|
@@ -916,7 +1014,11 @@ async function init(args) {
|
|
|
916
1014
|
notes: 'Filter messages from a mailbox folder by search options. Search is performed against a specific folder and not for the entire account.',
|
|
917
1015
|
tags: ['api', 'Message'],
|
|
918
1016
|
|
|
919
|
-
plugins: {
|
|
1017
|
+
plugins: {
|
|
1018
|
+
'hapi-swagger': {
|
|
1019
|
+
responses: errorResponses(400, 401, 403, 404, 422, 429, 500, 503)
|
|
1020
|
+
}
|
|
1021
|
+
},
|
|
920
1022
|
|
|
921
1023
|
auth: {
|
|
922
1024
|
strategy: 'api-token',
|
|
@@ -980,7 +1082,7 @@ async function init(args) {
|
|
|
980
1082
|
exposeQuery: Joi.boolean()
|
|
981
1083
|
.truthy('Y', 'true', '1')
|
|
982
1084
|
.falsy('N', 'false', 0)
|
|
983
|
-
.description('If enabled then
|
|
1085
|
+
.description('If enabled then includes the combined query (as the documentStoreQuery field) in the response for debugging')
|
|
984
1086
|
.label('exposeQuery')
|
|
985
1087
|
.when('documentStore', {
|
|
986
1088
|
is: true,
|
|
@@ -1031,107 +1133,142 @@ async function init(args) {
|
|
|
1031
1133
|
});
|
|
1032
1134
|
|
|
1033
1135
|
// POST /v1/unified/search - Unified search for messages
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1136
|
+
// Deprecated Document Store feature: only register this endpoint when the feature is enabled,
|
|
1137
|
+
// otherwise it behaves like a regular 404.
|
|
1138
|
+
if (documentStoreFeatureEnabled) {
|
|
1139
|
+
server.route({
|
|
1140
|
+
method: 'POST',
|
|
1141
|
+
path: '/v1/unified/search',
|
|
1142
|
+
|
|
1143
|
+
async handler(request, h) {
|
|
1144
|
+
let accountObject = new Account({
|
|
1145
|
+
redis,
|
|
1146
|
+
call,
|
|
1147
|
+
secret: await getSecret(),
|
|
1148
|
+
esClient: await h.getESClient(request.logger),
|
|
1149
|
+
timeout: request.headers['x-ee-timeout']
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
let extraValidationErrors = [];
|
|
1037
1153
|
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
secret: await getSecret(),
|
|
1043
|
-
esClient: await h.getESClient(request.logger),
|
|
1044
|
-
timeout: request.headers['x-ee-timeout']
|
|
1045
|
-
});
|
|
1046
|
-
|
|
1047
|
-
let extraValidationErrors = [];
|
|
1048
|
-
|
|
1049
|
-
for (let key of ['seq', 'modseq']) {
|
|
1050
|
-
if (request.payload.search && key in request.payload.search) {
|
|
1051
|
-
extraValidationErrors.push({ message: 'Not available when using Document Store', context: { key } });
|
|
1154
|
+
for (let key of ['seq', 'modseq']) {
|
|
1155
|
+
if (request.payload.search && key in request.payload.search) {
|
|
1156
|
+
extraValidationErrors.push({ message: 'Not available when using Document Store', context: { key } });
|
|
1157
|
+
}
|
|
1052
1158
|
}
|
|
1053
|
-
}
|
|
1054
1159
|
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
let documentStoreEnabled = await settings.get('documentStoreEnabled');
|
|
1062
|
-
if (!documentStoreEnabled) {
|
|
1063
|
-
let error = new Error('Document store not enabled');
|
|
1064
|
-
error.details = extraValidationErrors;
|
|
1065
|
-
return failAction(request, h, error);
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
try {
|
|
1069
|
-
return await accountObject.searchMessages(Object.assign({ documentStore: true }, request.query, request.payload), { unified: true });
|
|
1070
|
-
} catch (err) {
|
|
1071
|
-
handleError(request, err);
|
|
1072
|
-
}
|
|
1073
|
-
},
|
|
1074
|
-
options: {
|
|
1075
|
-
description: 'Unified search for messages',
|
|
1076
|
-
notes: 'Filter messages from the Document Store for multiple accounts or paths. Document Store must be enabled for the unified search to work.',
|
|
1077
|
-
tags: ['Deprecated endpoints (Document Store)'],
|
|
1160
|
+
if (extraValidationErrors.length) {
|
|
1161
|
+
let error = new Error('Input validation failed');
|
|
1162
|
+
error.details = extraValidationErrors;
|
|
1163
|
+
return failAction(request, h, error);
|
|
1164
|
+
}
|
|
1078
1165
|
|
|
1079
|
-
|
|
1166
|
+
let documentStoreEnabled = await settings.get('documentStoreEnabled');
|
|
1167
|
+
if (!documentStoreEnabled) {
|
|
1168
|
+
let error = new Error('Document store not enabled');
|
|
1169
|
+
error.details = extraValidationErrors;
|
|
1170
|
+
return failAction(request, h, error);
|
|
1171
|
+
}
|
|
1080
1172
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1173
|
+
try {
|
|
1174
|
+
return await accountObject.searchMessages(Object.assign({ documentStore: true }, request.query, request.payload), { unified: true });
|
|
1175
|
+
} catch (err) {
|
|
1176
|
+
handleError(request, err);
|
|
1177
|
+
}
|
|
1084
1178
|
},
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1179
|
+
options: {
|
|
1180
|
+
description: 'Unified search for messages',
|
|
1181
|
+
notes: 'Filter messages from the Document Store for multiple accounts or paths. Document Store must be enabled for the unified search to work.',
|
|
1182
|
+
tags: ['api', 'Deprecated endpoints (Document Store)'],
|
|
1183
|
+
|
|
1184
|
+
plugins: {
|
|
1185
|
+
'hapi-swagger': {
|
|
1186
|
+
responses: errorResponses(400, 401, 403, 429, 500)
|
|
1187
|
+
}
|
|
1092
1188
|
},
|
|
1093
|
-
failAction,
|
|
1094
1189
|
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
.
|
|
1111
|
-
|
|
1190
|
+
auth: {
|
|
1191
|
+
strategy: 'api-token',
|
|
1192
|
+
mode: 'required'
|
|
1193
|
+
},
|
|
1194
|
+
cors: CORS_CONFIG,
|
|
1195
|
+
|
|
1196
|
+
validate: {
|
|
1197
|
+
options: {
|
|
1198
|
+
stripUnknown: false,
|
|
1199
|
+
abortEarly: false,
|
|
1200
|
+
convert: true
|
|
1201
|
+
},
|
|
1202
|
+
failAction,
|
|
1203
|
+
|
|
1204
|
+
query: Joi.object({
|
|
1205
|
+
page: Joi.number()
|
|
1206
|
+
.integer()
|
|
1207
|
+
.min(0)
|
|
1208
|
+
.max(1024 * 1024)
|
|
1209
|
+
.default(0)
|
|
1210
|
+
.example(0)
|
|
1211
|
+
.description('Page number (zero indexed, so use 0 for first page)'),
|
|
1212
|
+
pageSize: Joi.number().integer().min(1).max(1000).default(20).example(20).description('How many entries per page'),
|
|
1213
|
+
exposeQuery: Joi.boolean()
|
|
1214
|
+
.truthy('Y', 'true', '1')
|
|
1215
|
+
.falsy('N', 'false', 0)
|
|
1216
|
+
.description('If enabled then includes the combined query (as the documentStoreQuery field) in the response for debugging')
|
|
1217
|
+
.label('exposeQuery')
|
|
1218
|
+
.optional()
|
|
1219
|
+
.meta({ swaggerHidden: true })
|
|
1220
|
+
}),
|
|
1112
1221
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1222
|
+
payload: Joi.object({
|
|
1223
|
+
accounts: Joi.array()
|
|
1224
|
+
.items(Joi.string().empty('').trim().max(256).example('example'))
|
|
1225
|
+
.single()
|
|
1226
|
+
.description('Optional list of account ID values')
|
|
1227
|
+
.label('UnifiedSearchAccounts'),
|
|
1228
|
+
paths: Joi.array()
|
|
1229
|
+
.items(Joi.string().optional().example('INBOX'))
|
|
1230
|
+
.single()
|
|
1231
|
+
.description('Optional list of mailbox folder paths or specialUse flags')
|
|
1232
|
+
.label('UnifiedSearchPaths'),
|
|
1233
|
+
search: searchSchema,
|
|
1234
|
+
documentQuery: Joi.object().min(1).description('Document Store query').label('DocumentQuery').unknown().meta({ swaggerHidden: true })
|
|
1235
|
+
}).label('UnifiedSearchQuery')
|
|
1236
|
+
},
|
|
1128
1237
|
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1238
|
+
response: {
|
|
1239
|
+
schema: Joi.object({
|
|
1240
|
+
total: Joi.number()
|
|
1241
|
+
.integer()
|
|
1242
|
+
.example(120)
|
|
1243
|
+
.description('Total number of matching messages (capped at 10000 by the Document Store)')
|
|
1244
|
+
.label('UnifiedTotalNumber'),
|
|
1245
|
+
page: Joi.number().integer().example(0).description('Current page number (zero-based)').label('UnifiedPageNumber'),
|
|
1246
|
+
pages: Joi.number().integer().example(24).description('Total number of pages available').label('UnifiedPagesNumber'),
|
|
1247
|
+
accounts: Joi.array()
|
|
1248
|
+
.items(Joi.string().example('example'))
|
|
1249
|
+
.description('Account filter used for the search, if provided')
|
|
1250
|
+
.label('UnifiedSearchAccountsEcho'),
|
|
1251
|
+
paths: Joi.array()
|
|
1252
|
+
.items(Joi.string().example('INBOX'))
|
|
1253
|
+
.description('Path filter used for the search, if provided')
|
|
1254
|
+
.label('UnifiedSearchPathsEcho'),
|
|
1255
|
+
messages: Joi.array()
|
|
1256
|
+
.items(
|
|
1257
|
+
messageEntrySchema
|
|
1258
|
+
.keys({
|
|
1259
|
+
account: accountIdSchema.description('Account ID this message belongs to')
|
|
1260
|
+
})
|
|
1261
|
+
.unknown()
|
|
1262
|
+
.label('UnifiedMessageListEntry')
|
|
1263
|
+
)
|
|
1264
|
+
.label('UnifiedPageMessages'),
|
|
1265
|
+
documentStoreQuery: documentStoreQuerySchema
|
|
1266
|
+
}).label('UnifiedSearchResponse'),
|
|
1267
|
+
failAction: 'log'
|
|
1268
|
+
}
|
|
1132
1269
|
}
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1135
1272
|
|
|
1136
1273
|
// GET /v1/account/{account}/text/{text} - Retrieve message text
|
|
1137
1274
|
server.route({
|
|
@@ -1159,6 +1296,12 @@ async function init(args) {
|
|
|
1159
1296
|
notes: 'Retrieves message text',
|
|
1160
1297
|
tags: ['api', 'Message'],
|
|
1161
1298
|
|
|
1299
|
+
plugins: {
|
|
1300
|
+
'hapi-swagger': {
|
|
1301
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
|
|
1162
1305
|
auth: {
|
|
1163
1306
|
strategy: 'api-token',
|
|
1164
1307
|
mode: 'required'
|
|
@@ -1179,7 +1322,7 @@ async function init(args) {
|
|
|
1179
1322
|
.min(0)
|
|
1180
1323
|
.max(1024 * 1024 * 1024)
|
|
1181
1324
|
.example(MAX_ATTACHMENT_SIZE)
|
|
1182
|
-
.description('Max length of text content'),
|
|
1325
|
+
.description('Max length of text content. Ignored for Gmail API and MS Graph API accounts (full content is always returned)'),
|
|
1183
1326
|
textType: Joi.string()
|
|
1184
1327
|
.lowercase()
|
|
1185
1328
|
.valid('html', 'plain', '*')
|
|
@@ -1205,7 +1348,9 @@ async function init(args) {
|
|
|
1205
1348
|
schema: Joi.object({
|
|
1206
1349
|
plain: Joi.string().example('Hello world').description('Plaintext content'),
|
|
1207
1350
|
html: Joi.string().example('<p>Hello world</p>').description('HTML content'),
|
|
1208
|
-
hasMore: Joi.boolean()
|
|
1351
|
+
hasMore: Joi.boolean()
|
|
1352
|
+
.example(false)
|
|
1353
|
+
.description('Is the current text output capped or not. Always false for Gmail API and MS Graph API accounts')
|
|
1209
1354
|
}).label('TextResponse'),
|
|
1210
1355
|
failAction: 'log'
|
|
1211
1356
|
}
|
|
@@ -1245,7 +1390,8 @@ async function init(args) {
|
|
|
1245
1390
|
|
|
1246
1391
|
plugins: {
|
|
1247
1392
|
'hapi-swagger': {
|
|
1248
|
-
produces: ['application/octet-stream']
|
|
1393
|
+
produces: ['application/octet-stream'],
|
|
1394
|
+
responses: errorResponses(400, 401, 403, 404, 429, 500, 503)
|
|
1249
1395
|
}
|
|
1250
1396
|
},
|
|
1251
1397
|
|