emailengine-app 2.61.5 → 2.62.1
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/CHANGELOG.md +88 -0
- package/data/google-crawlers.json +1 -1
- package/lib/account.js +20 -7
- package/lib/api-routes/account-routes.js +28 -5
- package/lib/api-routes/chat-routes.js +1 -1
- package/lib/api-routes/export-routes.js +316 -0
- package/lib/api-routes/message-routes.js +28 -23
- package/lib/api-routes/template-routes.js +28 -7
- package/lib/arf-detect.js +1 -1
- package/lib/autodetect-imap-settings.js +5 -5
- package/lib/consts.js +16 -0
- package/lib/db.js +3 -0
- package/lib/email-client/base-client.js +6 -4
- package/lib/email-client/gmail-client.js +205 -35
- package/lib/email-client/imap/mailbox.js +99 -8
- package/lib/email-client/imap/subconnection.js +5 -5
- package/lib/email-client/imap-client.js +76 -19
- package/lib/email-client/message-builder.js +3 -1
- package/lib/email-client/notification-handler.js +12 -9
- package/lib/email-client/outlook-client.js +364 -73
- package/lib/email-client/smtp-pool-manager.js +1 -1
- package/lib/export.js +528 -0
- package/lib/oauth/gmail.js +24 -16
- package/lib/oauth/mail-ru.js +26 -13
- package/lib/oauth/outlook.js +29 -19
- package/lib/oauth/pubsub/google.js +5 -0
- package/lib/routes-ui.js +268 -9
- package/lib/schemas.js +274 -81
- package/lib/stream-encrypt.js +263 -0
- package/lib/sub-script.js +2 -2
- package/lib/tools.js +194 -12
- package/lib/ui-routes/account-routes.js +23 -0
- package/lib/ui-routes/admin-config-routes.js +13 -6
- package/lib/ui-routes/admin-entities-routes.js +18 -0
- package/lib/webhooks.js +16 -20
- package/package.json +20 -20
- package/sbom.json +1 -1
- package/server.js +66 -7
- package/static/js/ace/ace.js +1 -1
- package/static/js/ace/ext-language_tools.js +1 -1
- package/static/licenses.html +118 -149
- package/translations/de.mo +0 -0
- package/translations/de.po +63 -36
- package/translations/en.mo +0 -0
- package/translations/en.po +64 -37
- package/translations/et.mo +0 -0
- package/translations/et.po +63 -36
- package/translations/fr.mo +0 -0
- package/translations/fr.po +63 -36
- package/translations/ja.mo +0 -0
- package/translations/ja.po +63 -36
- package/translations/messages.pot +84 -51
- package/translations/nl.mo +0 -0
- package/translations/nl.po +63 -36
- package/translations/pl.mo +0 -0
- package/translations/pl.po +63 -36
- package/views/accounts/account.hbs +375 -2
- package/views/config/network.hbs +45 -0
- package/views/config/service.hbs +35 -0
- package/workers/api.js +130 -47
- package/workers/documents.js +3 -2
- package/workers/export.js +933 -0
- package/workers/imap.js +34 -1
- package/workers/submit.js +33 -6
- package/workers/webhooks.js +20 -4
|
@@ -147,7 +147,8 @@ async function init(args) {
|
|
|
147
147
|
.lowercase()
|
|
148
148
|
.valid('html', 'plain', '*')
|
|
149
149
|
.example('*')
|
|
150
|
-
.description('Which text content to return, use * for all. By default text content is not returned.')
|
|
150
|
+
.description('Which text content to return, use * for all. By default text content is not returned.')
|
|
151
|
+
.label('MessageTextType'),
|
|
151
152
|
|
|
152
153
|
webSafeHtml: Joi.boolean()
|
|
153
154
|
.truthy('Y', 'true', '1')
|
|
@@ -310,9 +311,9 @@ async function init(args) {
|
|
|
310
311
|
}),
|
|
311
312
|
|
|
312
313
|
contentType: Joi.string().lowercase().max(256).example('image/gif'),
|
|
313
|
-
contentDisposition: Joi.string().lowercase().valid('inline', 'attachment'),
|
|
314
|
+
contentDisposition: Joi.string().lowercase().valid('inline', 'attachment').label('MsgUploadContentDisposition'),
|
|
314
315
|
cid: Joi.string().max(256).example('unique-image-id@localhost').description('Content-ID value for embedded images'),
|
|
315
|
-
encoding: Joi.string().valid('base64').default('base64'),
|
|
316
|
+
encoding: Joi.string().valid('base64').default('base64').label('MsgUploadEncoding'),
|
|
316
317
|
|
|
317
318
|
reference: Joi.string()
|
|
318
319
|
.base64({ paddingRequired: false, urlSafe: true })
|
|
@@ -322,6 +323,7 @@ async function init(args) {
|
|
|
322
323
|
.description(
|
|
323
324
|
'References an existing attachment by its ID instead of providing new attachment content. If this field is set, the `content` field must not be included. If not set, the `content` field is required.'
|
|
324
325
|
)
|
|
326
|
+
.label('MsgUploadReference')
|
|
325
327
|
}).label('UploadAttachment')
|
|
326
328
|
)
|
|
327
329
|
.description('List of attachments')
|
|
@@ -430,15 +432,15 @@ async function init(args) {
|
|
|
430
432
|
response: {
|
|
431
433
|
schema: Joi.object({
|
|
432
434
|
flags: Joi.object({
|
|
433
|
-
add: Joi.array().items(Joi.string()).example(['\\Seen', '\\Flagged']),
|
|
434
|
-
delete: Joi.array().items(Joi.string()).example(['\\Draft']),
|
|
435
|
-
set: Joi.array().items(Joi.string()).example(['\\Seen'])
|
|
436
|
-
}).label('
|
|
435
|
+
add: Joi.array().items(Joi.string().label('FlagEntry')).example(['\\Seen', '\\Flagged']).label('FlagAddList'),
|
|
436
|
+
delete: Joi.array().items(Joi.string().label('FlagEntry')).example(['\\Draft']).label('FlagDeleteList'),
|
|
437
|
+
set: Joi.array().items(Joi.string().label('FlagEntry')).example(['\\Seen']).label('FlagSetList')
|
|
438
|
+
}).label('FlagUpdateResponse'),
|
|
437
439
|
labels: Joi.object({
|
|
438
|
-
add: Joi.array().items(Joi.string()).example(['Label1', 'Label2']),
|
|
439
|
-
delete: Joi.array().items(Joi.string()).example(['Label3']),
|
|
440
|
-
set: Joi.array().items(Joi.string()).example(['Label1'])
|
|
441
|
-
}).label('
|
|
440
|
+
add: Joi.array().items(Joi.string().label('LabelEntry')).example(['Label1', 'Label2']).label('LabelAddList'),
|
|
441
|
+
delete: Joi.array().items(Joi.string().label('LabelEntry')).example(['Label3']).label('LabelDeleteList'),
|
|
442
|
+
set: Joi.array().items(Joi.string().label('LabelEntry')).example(['Label1']).label('LabelSetList')
|
|
443
|
+
}).label('LabelUpdateResponse')
|
|
442
444
|
}).label('MessageUpdateResponse'),
|
|
443
445
|
failAction: 'log'
|
|
444
446
|
}
|
|
@@ -510,16 +512,16 @@ async function init(args) {
|
|
|
510
512
|
response: {
|
|
511
513
|
schema: Joi.object({
|
|
512
514
|
flags: Joi.object({
|
|
513
|
-
add: Joi.array().items(Joi.string()).example(['\\Seen', '\\Flagged']),
|
|
514
|
-
delete: Joi.array().items(Joi.string()).example(['\\Draft']),
|
|
515
|
-
set: Joi.array().items(Joi.string()).example(['\\Seen'])
|
|
516
|
-
}).label('
|
|
515
|
+
add: Joi.array().items(Joi.string().label('BulkFlagEntry')).example(['\\Seen', '\\Flagged']).label('BulkFlagAddList'),
|
|
516
|
+
delete: Joi.array().items(Joi.string().label('BulkFlagEntry')).example(['\\Draft']).label('BulkFlagDeleteList'),
|
|
517
|
+
set: Joi.array().items(Joi.string().label('BulkFlagEntry')).example(['\\Seen']).label('BulkFlagSetList')
|
|
518
|
+
}).label('BulkFlagUpdateResponse'),
|
|
517
519
|
labels: Joi.object({
|
|
518
|
-
add: Joi.array().items(Joi.string()).example(['Label1', 'Label2']),
|
|
519
|
-
delete: Joi.array().items(Joi.string()).example(['Label3']),
|
|
520
|
-
set: Joi.array().items(Joi.string()).example(['Label1'])
|
|
521
|
-
}).label('
|
|
522
|
-
}).label('
|
|
520
|
+
add: Joi.array().items(Joi.string().label('BulkLabelEntry')).example(['Label1', 'Label2']).label('BulkLabelAddList'),
|
|
521
|
+
delete: Joi.array().items(Joi.string().label('BulkLabelEntry')).example(['Label3']).label('BulkLabelDeleteList'),
|
|
522
|
+
set: Joi.array().items(Joi.string().label('BulkLabelEntry')).example(['Label1']).label('BulkLabelSetList')
|
|
523
|
+
}).label('BulkLabelUpdateResponse')
|
|
524
|
+
}).label('BulkMessageUpdateResponse'),
|
|
523
525
|
failAction: 'log'
|
|
524
526
|
}
|
|
525
527
|
}
|
|
@@ -759,7 +761,9 @@ async function init(args) {
|
|
|
759
761
|
moved: Joi.object({
|
|
760
762
|
destination: Joi.string().required().example('Trash').description('Trash folder path').label('TrashPath'),
|
|
761
763
|
message: Joi.string().required().example('AAAAAwAAAWg').description('Message ID in Trash').label('TrashMessageId')
|
|
762
|
-
})
|
|
764
|
+
})
|
|
765
|
+
.description('Present if message was moved to Trash')
|
|
766
|
+
.label('MessageMovedToTrash')
|
|
763
767
|
}).label('MessageDeleteResponse'),
|
|
764
768
|
failAction: 'log'
|
|
765
769
|
}
|
|
@@ -1083,7 +1087,7 @@ async function init(args) {
|
|
|
1083
1087
|
.unknown()
|
|
1084
1088
|
.meta({ swaggerHidden: true })
|
|
1085
1089
|
})
|
|
1086
|
-
.label('
|
|
1090
|
+
.label('MessageSearchPayload')
|
|
1087
1091
|
.example({
|
|
1088
1092
|
search: {
|
|
1089
1093
|
unseen: true,
|
|
@@ -1281,7 +1285,8 @@ async function init(args) {
|
|
|
1281
1285
|
.valid('html', 'plain', '*')
|
|
1282
1286
|
.default('*')
|
|
1283
1287
|
.example('*')
|
|
1284
|
-
.description('Which text content to return, use * for all. By default all contents are returned.')
|
|
1288
|
+
.description('Which text content to return, use * for all. By default all contents are returned.')
|
|
1289
|
+
.label('TextSearchTextType'),
|
|
1285
1290
|
documentStore: documentStoreSchema.default(false)
|
|
1286
1291
|
}),
|
|
1287
1292
|
|
|
@@ -93,7 +93,11 @@ async function init(args) {
|
|
|
93
93
|
.example('Something about the template')
|
|
94
94
|
.description('Optional description of the template')
|
|
95
95
|
.label('TemplateDescription'),
|
|
96
|
-
format: Joi.string()
|
|
96
|
+
format: Joi.string()
|
|
97
|
+
.valid('html', 'markdown')
|
|
98
|
+
.default('html')
|
|
99
|
+
.description('Markup language for HTML ("html" or "markdown")')
|
|
100
|
+
.label('TemplateFormat'),
|
|
97
101
|
content: Joi.object({
|
|
98
102
|
subject: templateSchemas.subject,
|
|
99
103
|
text: templateSchemas.text,
|
|
@@ -176,7 +180,12 @@ async function init(args) {
|
|
|
176
180
|
.example('Something about the template')
|
|
177
181
|
.description('Optional description of the template')
|
|
178
182
|
.label('TemplateDescription'),
|
|
179
|
-
format: Joi.string()
|
|
183
|
+
format: Joi.string()
|
|
184
|
+
.empty('')
|
|
185
|
+
.valid('html', 'markdown')
|
|
186
|
+
.default('html')
|
|
187
|
+
.description('Markup language for HTML ("html" or "markdown")')
|
|
188
|
+
.label('TemplateUpdateFormat'),
|
|
180
189
|
content: Joi.object({
|
|
181
190
|
subject: templateSchemas.subject,
|
|
182
191
|
text: templateSchemas.text,
|
|
@@ -277,7 +286,11 @@ async function init(args) {
|
|
|
277
286
|
.example('Something about the template')
|
|
278
287
|
.description('Optional description of the template')
|
|
279
288
|
.label('TemplateDescription'),
|
|
280
|
-
format: Joi.string()
|
|
289
|
+
format: Joi.string()
|
|
290
|
+
.valid('html', 'markdown')
|
|
291
|
+
.default('html')
|
|
292
|
+
.description('Markup language for HTML ("html" or "markdown")')
|
|
293
|
+
.label('TemplateListFormat'),
|
|
281
294
|
created: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('The time this template was created'),
|
|
282
295
|
updated: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('The time this template was last updated')
|
|
283
296
|
}).label('AccountTemplate')
|
|
@@ -345,7 +358,11 @@ async function init(args) {
|
|
|
345
358
|
.example('Something about the template')
|
|
346
359
|
.description('Optional description of the template')
|
|
347
360
|
.label('TemplateDescription'),
|
|
348
|
-
format: Joi.string()
|
|
361
|
+
format: Joi.string()
|
|
362
|
+
.valid('html', 'markdown')
|
|
363
|
+
.default('html')
|
|
364
|
+
.description('Markup language for HTML ("html" or "markdown")')
|
|
365
|
+
.label('TemplateResponseFormat'),
|
|
349
366
|
created: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('The time this template was created'),
|
|
350
367
|
updated: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('The time this template was last updated'),
|
|
351
368
|
content: Joi.object({
|
|
@@ -353,7 +370,11 @@ async function init(args) {
|
|
|
353
370
|
text: templateSchemas.text,
|
|
354
371
|
html: templateSchemas.html,
|
|
355
372
|
previewText: templateSchemas.previewText,
|
|
356
|
-
format: Joi.string()
|
|
373
|
+
format: Joi.string()
|
|
374
|
+
.valid('html', 'markdown')
|
|
375
|
+
.default('html')
|
|
376
|
+
.description('Markup language for HTML ("html" or "markdown")')
|
|
377
|
+
.label('TemplateContentFormat')
|
|
357
378
|
}).label('RequestTemplateContent')
|
|
358
379
|
}).label('AccountTemplateResponse'),
|
|
359
380
|
failAction: 'log'
|
|
@@ -411,7 +432,7 @@ async function init(args) {
|
|
|
411
432
|
deleted: Joi.boolean().truthy('Y', 'true', '1').falsy('N', 'false', 0).default(true).description('Was the template deleted'),
|
|
412
433
|
account: accountIdSchema.required(),
|
|
413
434
|
id: Joi.string().max(256).required().example('AAABgS-UcAYAAAABAA').description('Template ID')
|
|
414
|
-
}).label('
|
|
435
|
+
}).label('DeleteTemplateResponse'),
|
|
415
436
|
failAction: 'log'
|
|
416
437
|
}
|
|
417
438
|
}
|
|
@@ -480,7 +501,7 @@ async function init(args) {
|
|
|
480
501
|
schema: Joi.object({
|
|
481
502
|
deleted: Joi.boolean().truthy('Y', 'true', '1').falsy('N', 'false', 0).default(true).description('Were the templates flushed'),
|
|
482
503
|
account: accountIdSchema.required()
|
|
483
|
-
}).label('
|
|
504
|
+
}).label('FlushTemplatesResponse'),
|
|
484
505
|
failAction: 'log'
|
|
485
506
|
}
|
|
486
507
|
}
|
package/lib/arf-detect.js
CHANGED
|
@@ -27,7 +27,7 @@ const arfDetect = async messageInfo => {
|
|
|
27
27
|
|
|
28
28
|
let returnPath;
|
|
29
29
|
|
|
30
|
-
for (let attachment of messageInfo.attachments) {
|
|
30
|
+
for (let attachment of messageInfo.attachments || []) {
|
|
31
31
|
switch (attachment.contentType.toLowerCase()) {
|
|
32
32
|
case 'message/feedback-report': {
|
|
33
33
|
// found a feedback report
|
|
@@ -20,7 +20,7 @@ const parseXml = util.promisify((xml, cb) => {
|
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
const { fetch: fetchCmd } = require('undici');
|
|
23
|
-
const {
|
|
23
|
+
const { httpAgent } = require('./tools');
|
|
24
24
|
|
|
25
25
|
const RESOLV_TIMEOUT = 5 * 1000;
|
|
26
26
|
|
|
@@ -191,7 +191,7 @@ async function resolveUsingMozillaDirectory(email, domain, source) {
|
|
|
191
191
|
headers: {
|
|
192
192
|
'User-Agent': `${packageData.name}/${packageData.version} (+${packageData.homepage})`
|
|
193
193
|
},
|
|
194
|
-
dispatcher:
|
|
194
|
+
dispatcher: httpAgent.retry
|
|
195
195
|
});
|
|
196
196
|
|
|
197
197
|
if (!res.ok) {
|
|
@@ -216,7 +216,7 @@ async function resolveUsingAutoconfig(email, domain, source) {
|
|
|
216
216
|
headers: {
|
|
217
217
|
'User-Agent': `${packageData.name}/${packageData.version} (+${packageData.homepage})`
|
|
218
218
|
},
|
|
219
|
-
dispatcher:
|
|
219
|
+
dispatcher: httpAgent.retry
|
|
220
220
|
});
|
|
221
221
|
|
|
222
222
|
if (!res.ok) {
|
|
@@ -241,7 +241,7 @@ async function resolveUsingWellKnown(email, domain, source) {
|
|
|
241
241
|
headers: {
|
|
242
242
|
'User-Agent': `${packageData.name}/${packageData.version} (+${packageData.homepage})`
|
|
243
243
|
},
|
|
244
|
-
dispatcher:
|
|
244
|
+
dispatcher: httpAgent.retry
|
|
245
245
|
});
|
|
246
246
|
|
|
247
247
|
if (!res.ok) {
|
|
@@ -640,7 +640,7 @@ async function resolveUsingAutodiscovery(email, domain, source) {
|
|
|
640
640
|
'Content-type': 'application/xml'
|
|
641
641
|
},
|
|
642
642
|
body,
|
|
643
|
-
dispatcher:
|
|
643
|
+
dispatcher: httpAgent.retry
|
|
644
644
|
});
|
|
645
645
|
|
|
646
646
|
if (!res.ok) {
|
package/lib/consts.js
CHANGED
|
@@ -87,6 +87,22 @@ module.exports = {
|
|
|
87
87
|
LIST_SUBSCRIBE_NOTIFY: 'listSubscribe',
|
|
88
88
|
LIST_SUBSCRIBE_DESCRIPTION: 'Recipient Subscribed - A recipient re-subscribed to a list',
|
|
89
89
|
|
|
90
|
+
EXPORT_COMPLETED_NOTIFY: 'exportCompleted',
|
|
91
|
+
EXPORT_COMPLETED_DESCRIPTION: 'Export Completed - A bulk message export has completed successfully',
|
|
92
|
+
|
|
93
|
+
EXPORT_FAILED_NOTIFY: 'exportFailed',
|
|
94
|
+
EXPORT_FAILED_DESCRIPTION: 'Export Failed - A bulk message export has failed',
|
|
95
|
+
|
|
96
|
+
// Export defaults
|
|
97
|
+
DEFAULT_EXPORT_MAX_AGE: 24 * 60 * 60 * 1000, // 24 hours
|
|
98
|
+
DEFAULT_EXPORT_MAX_SIZE: 10 * 1024 * 1024 * 1024, // 10GB
|
|
99
|
+
DEFAULT_EXPORT_MAX_MESSAGES: 500000,
|
|
100
|
+
DEFAULT_EXPORT_MAX_CONCURRENT: 2,
|
|
101
|
+
DEFAULT_EXPORT_MAX_GLOBAL_CONCURRENT: 8,
|
|
102
|
+
DEFAULT_EXPORT_MAX_MESSAGE_SIZE: 50 * 1024 * 1024, // 50MB
|
|
103
|
+
DEFAULT_GMAIL_EXPORT_BATCH_SIZE: 10,
|
|
104
|
+
DEFAULT_OUTLOOK_EXPORT_BATCH_SIZE: 20,
|
|
105
|
+
|
|
90
106
|
MAX_DAYS_STATS: 7,
|
|
91
107
|
|
|
92
108
|
DEFAULT_MAX_LOG_LINES: 10000,
|
package/lib/db.js
CHANGED
|
@@ -107,12 +107,14 @@ const reqisQueue = new Redis(REDIS_CONF);
|
|
|
107
107
|
|
|
108
108
|
module.exports.queueConf = {
|
|
109
109
|
connection: reqisQueue,
|
|
110
|
+
sharedConnection: true, // Prevent BullMQ from closing shared connection
|
|
110
111
|
prefix: `${REDIS_PREFIX}bull`
|
|
111
112
|
};
|
|
112
113
|
|
|
113
114
|
const notifyQueue = new Queue('notify', module.exports.queueConf);
|
|
114
115
|
const submitQueue = new Queue('submit', module.exports.queueConf);
|
|
115
116
|
const documentsQueue = new Queue('documents', module.exports.queueConf);
|
|
117
|
+
const exportQueue = new Queue('export', module.exports.queueConf);
|
|
116
118
|
|
|
117
119
|
const zExpungeScript = fs.readFileSync(pathlib.join(__dirname, '/lua/z-expunge.lua'), 'utf-8');
|
|
118
120
|
const zSetScript = fs.readFileSync(pathlib.join(__dirname, 'lua/z-set.lua'), 'utf-8');
|
|
@@ -215,6 +217,7 @@ module.exports.redis = redis;
|
|
|
215
217
|
module.exports.notifyQueue = notifyQueue;
|
|
216
218
|
module.exports.submitQueue = submitQueue;
|
|
217
219
|
module.exports.documentsQueue = documentsQueue;
|
|
220
|
+
module.exports.exportQueue = exportQueue;
|
|
218
221
|
|
|
219
222
|
// do not set up the flow producer by default
|
|
220
223
|
module.exports.getFlowProducer = () => new FlowProducer(module.exports.queueConf /*, Redis*/);
|
|
@@ -1984,7 +1984,7 @@ class BaseClient {
|
|
|
1984
1984
|
// Store message in Redis
|
|
1985
1985
|
await this.redis.hsetBuffer(`${REDIS_PREFIX}iaq:${this.account}`, queueId, msgEntry);
|
|
1986
1986
|
|
|
1987
|
-
let queueKeep = (await settings.get('queueKeep'))
|
|
1987
|
+
let queueKeep = (await settings.get('queueKeep')) ?? true;
|
|
1988
1988
|
|
|
1989
1989
|
// Configure delivery retry settings
|
|
1990
1990
|
let defaultDeliveryAttempts = await settings.get('deliveryAttempts');
|
|
@@ -2007,14 +2007,16 @@ class BaseClient {
|
|
|
2007
2007
|
|
|
2008
2008
|
// Configure queue job options
|
|
2009
2009
|
let queueName = 'queued';
|
|
2010
|
+
let retention = typeof queueKeep === 'number' ? { age: 24 * 3600, count: queueKeep } : queueKeep;
|
|
2010
2011
|
let jobOpts = {
|
|
2011
2012
|
jobId: queueId,
|
|
2012
|
-
removeOnComplete:
|
|
2013
|
-
removeOnFail:
|
|
2013
|
+
removeOnComplete: retention,
|
|
2014
|
+
removeOnFail: retention,
|
|
2014
2015
|
attempts: typeof deliveryAttempts === 'number' ? deliveryAttempts : defaultDeliveryAttempts,
|
|
2015
2016
|
backoff: {
|
|
2016
2017
|
type: 'exponential',
|
|
2017
|
-
delay: 5000
|
|
2018
|
+
delay: 5000,
|
|
2019
|
+
jitter: 0.2 // 20% randomization to prevent thundering herd
|
|
2018
2020
|
}
|
|
2019
2021
|
};
|
|
2020
2022
|
|