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
package/lib/outbox.js
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
const { submitQueue, redis } = require('./db');
|
|
4
4
|
const { REDIS_PREFIX } = require('../lib/consts');
|
|
5
5
|
|
|
6
|
+
function formatQueueEntry(job) {
|
|
7
|
+
let scheduled = job.timestamp + (Number(job.opts.delay) || 0);
|
|
8
|
+
|
|
9
|
+
let backoffDelay = Number(job.opts.backoff && job.opts.backoff.delay) || 0;
|
|
10
|
+
// attemptsMade already includes the failed attempt, so the retry delay is 2^(attemptsMade-1) * base
|
|
11
|
+
let nextAttempt = job.attemptsMade ? Math.round(job.processedOn + Math.pow(2, job.attemptsMade - 1) * backoffDelay) : scheduled;
|
|
12
|
+
|
|
13
|
+
if (job.opts.attempts <= job.attemptsMade) {
|
|
14
|
+
nextAttempt = false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return Object.assign(job.data, {
|
|
18
|
+
created: new Date(Number(job.data.created || job.timestamp)).toISOString(),
|
|
19
|
+
//status: job.name,
|
|
20
|
+
progress: job.progress,
|
|
21
|
+
attemptsMade: job.attemptsMade,
|
|
22
|
+
attempts: job.opts.attempts,
|
|
23
|
+
scheduled: new Date(scheduled).toISOString(),
|
|
24
|
+
nextAttempt: nextAttempt ? new Date(nextAttempt).toISOString() : false
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
6
28
|
async function list(options) {
|
|
7
29
|
options = options || {};
|
|
8
30
|
let page = Number(options.page) || 0;
|
|
@@ -23,26 +45,7 @@ async function list(options) {
|
|
|
23
45
|
try {
|
|
24
46
|
let job = await submitQueue.getJob(jobId);
|
|
25
47
|
if (job) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
let backoffDelay = Number(job.opts.backoff && job.opts.backoff.delay) || 0;
|
|
29
|
-
let nextAttempt = job.attemptsMade ? Math.round(job.processedOn + Math.pow(2, job.attemptsMade) * backoffDelay) : scheduled;
|
|
30
|
-
|
|
31
|
-
if (job.opts.attempts <= job.attemptsMade) {
|
|
32
|
-
nextAttempt = false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
messages.push(
|
|
36
|
-
Object.assign(job.data, {
|
|
37
|
-
created: new Date(Number(job.created || job.timestamp)).toISOString(),
|
|
38
|
-
//status: job.name,
|
|
39
|
-
progress: job.progress,
|
|
40
|
-
attemptsMade: job.attemptsMade,
|
|
41
|
-
attempts: job.opts.attempts,
|
|
42
|
-
scheduled: new Date(scheduled).toISOString(),
|
|
43
|
-
nextAttempt: nextAttempt ? new Date(nextAttempt).toISOString() : false
|
|
44
|
-
})
|
|
45
|
-
);
|
|
48
|
+
messages.push(formatQueueEntry(job));
|
|
46
49
|
}
|
|
47
50
|
} catch (err) {
|
|
48
51
|
logger.error({ msg: 'Failed to retrieve message info from outbox', jobId, err });
|
|
@@ -105,15 +108,6 @@ async function get(options) {
|
|
|
105
108
|
return false;
|
|
106
109
|
}
|
|
107
110
|
|
|
108
|
-
let scheduled = job.timestamp + (Number(job.opts.delay) || 0);
|
|
109
|
-
|
|
110
|
-
let backoffDelay = Number(job.opts.backoff && job.opts.backoff.delay) || 0;
|
|
111
|
-
let nextAttempt = job.attemptsMade ? Math.round(job.processedOn + Math.pow(2, job.attemptsMade) * backoffDelay) : scheduled;
|
|
112
|
-
|
|
113
|
-
if (job.opts.attempts <= job.attemptsMade) {
|
|
114
|
-
nextAttempt = false;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
111
|
try {
|
|
118
112
|
let queueEntryBuf = await redis.hgetBuffer(`${REDIS_PREFIX}iaq:${job.data.account}`, job.data.queueId);
|
|
119
113
|
if (!queueEntryBuf) {
|
|
@@ -124,17 +118,7 @@ async function get(options) {
|
|
|
124
118
|
throw err;
|
|
125
119
|
}
|
|
126
120
|
|
|
127
|
-
|
|
128
|
-
created: new Date(Number(job.created || job.timestamp)).toISOString(),
|
|
129
|
-
//status: job.name,
|
|
130
|
-
progress: job.progress,
|
|
131
|
-
attemptsMade: job.attemptsMade,
|
|
132
|
-
attempts: job.opts.attempts,
|
|
133
|
-
scheduled: new Date(scheduled).toISOString(),
|
|
134
|
-
nextAttempt: nextAttempt ? new Date(nextAttempt).toISOString() : false
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
return response;
|
|
121
|
+
return formatQueueEntry(job);
|
|
138
122
|
}
|
|
139
123
|
|
|
140
124
|
module.exports = { list, del, get };
|
package/lib/redis-operations.js
CHANGED
|
@@ -228,7 +228,7 @@ class RedisTransaction {
|
|
|
228
228
|
* @returns {Promise<Array>} Array of values from successful commands
|
|
229
229
|
*/
|
|
230
230
|
async execOrThrow() {
|
|
231
|
-
const {
|
|
231
|
+
const { values, error } = await this.exec();
|
|
232
232
|
|
|
233
233
|
if (error) {
|
|
234
234
|
throw error;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reject every pending cross-thread call routed to a worker thread that has
|
|
5
|
+
* terminated. Without this, a call placed against a worker that then crashes or
|
|
6
|
+
* is force-restarted keeps waiting until its own timeout fires - which can be
|
|
7
|
+
* minutes (the submitMessage floor) or hours (a large X-EE-Timeout). Rejecting
|
|
8
|
+
* here lets the caller fail fast with a retryable error the instant the worker
|
|
9
|
+
* is known to be gone.
|
|
10
|
+
*
|
|
11
|
+
* The stored `reject` wrapper clears the entry's timeout, so we only need to
|
|
12
|
+
* drop the entry from the queue and invoke it.
|
|
13
|
+
*
|
|
14
|
+
* @param {Map} callQueue - mid -> { resolve, reject, timer, worker }
|
|
15
|
+
* @param {Worker} worker - The terminated worker thread
|
|
16
|
+
* @param {Error} err - Rejection reason. May be a shared instance reused across
|
|
17
|
+
* concurrent rejections, so callers must not attach per-call fields to it.
|
|
18
|
+
* @returns {number} Number of pending calls that were rejected
|
|
19
|
+
*/
|
|
20
|
+
function rejectWorkerCalls(callQueue, worker, err) {
|
|
21
|
+
let rejected = 0;
|
|
22
|
+
|
|
23
|
+
// Deleting the current entry while iterating a Map is safe in JS.
|
|
24
|
+
for (let [mid, entry] of callQueue) {
|
|
25
|
+
if (entry.worker !== worker) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
callQueue.delete(mid);
|
|
30
|
+
try {
|
|
31
|
+
entry.reject(err);
|
|
32
|
+
} catch (rejectErr) {
|
|
33
|
+
// A consumer that throws synchronously from its rejection path must
|
|
34
|
+
// not stop us from cleaning up the remaining pending calls.
|
|
35
|
+
}
|
|
36
|
+
rejected++;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return rejected;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { rejectWorkerCalls };
|