emailengine-app 2.63.2 → 2.63.4
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 +21 -0
- package/data/google-crawlers.json +1 -1
- package/lib/email-client/base-client.js +22 -0
- package/lib/email-client/gmail/gmail-api.js +35 -0
- package/lib/email-client/imap/mailbox.js +24 -6
- package/lib/email-client/imap/sync-operations.js +17 -5
- package/lib/email-client/imap-client.js +20 -13
- package/lib/email-client/outlook/graph-api.js +31 -0
- package/lib/oauth/pubsub/google.js +12 -1
- package/lib/oauth2-apps.js +35 -0
- package/lib/tools.js +6 -0
- package/package.json +9 -9
- package/sbom.json +1 -1
- package/static/licenses.html +35 -35
- package/translations/messages.pot +17 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.63.4](https://github.com/postalsys/emailengine/compare/v2.63.3...v2.63.4) (2026-03-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* guard null imapClient dereferences during async operations ([671bcee](https://github.com/postalsys/emailengine/commit/671bcee4e81c7c0d6421617c3e25ec6906cb1abb))
|
|
9
|
+
* prevent null dereference crash in getImapConnection during connection drops ([6a356fd](https://github.com/postalsys/emailengine/commit/6a356fd20e1412a070c26c093108c4ea2a722a5c))
|
|
10
|
+
* throw instead of silent return in select() null guard, revert redundant optional chaining ([cb7db50](https://github.com/postalsys/emailengine/commit/cb7db5024e0b00f8a773328dd5e0009067bed0b6))
|
|
11
|
+
* update tests for getCurrentListing null guard changes ([a644f9c](https://github.com/postalsys/emailengine/commit/a644f9c1c3f3e4cbce388e43bfa319f11f4a469c))
|
|
12
|
+
|
|
13
|
+
## [2.63.3](https://github.com/postalsys/emailengine/compare/v2.63.2...v2.63.3) (2026-03-05)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* handle undici HeadersTimeoutError as transient in Pub/Sub and OAuth2 paths ([8b3b698](https://github.com/postalsys/emailengine/commit/8b3b69880ab09119df4ca377d085db261eae2763))
|
|
19
|
+
* prevent IMAP worker crash on ImapFlow unhandled rejection during IDLE recovery ([86ebb02](https://github.com/postalsys/emailengine/commit/86ebb02c321ffa758966af1957509c10e20a35fc))
|
|
20
|
+
* prevent transient network errors during OAuth2 token refresh from being misclassified as auth failures ([38fa212](https://github.com/postalsys/emailengine/commit/38fa212092847b727ac1d02541103c032a983dc7))
|
|
21
|
+
* retry transient network errors in Gmail and Outlook API request functions ([08ea0da](https://github.com/postalsys/emailengine/commit/08ea0daecaec73c58a91ac07df5d0f0ffc083a2f))
|
|
22
|
+
* treat DNS errors as transient in Google Pub/Sub polling loop ([ec33673](https://github.com/postalsys/emailengine/commit/ec33673b47242fd7156b4641c9c4924a33eaf9e5))
|
|
23
|
+
|
|
3
24
|
## [2.63.2](https://github.com/postalsys/emailengine/compare/v2.63.1...v2.63.2) (2026-03-03)
|
|
4
25
|
|
|
5
26
|
|
|
@@ -538,6 +538,28 @@ class BaseClient {
|
|
|
538
538
|
});
|
|
539
539
|
accessToken = accountData.oauth2.accessToken;
|
|
540
540
|
} catch (err) {
|
|
541
|
+
// Transient network errors are not authentication failures
|
|
542
|
+
let isNetworkError =
|
|
543
|
+
err.code === 'ENOTFOUND' ||
|
|
544
|
+
err.code === 'ETIMEDOUT' ||
|
|
545
|
+
err.code === 'ECONNREFUSED' ||
|
|
546
|
+
err.code === 'ECONNRESET' ||
|
|
547
|
+
err.code === 'EAI_AGAIN' ||
|
|
548
|
+
err.code === 'UND_ERR_CONNECT_TIMEOUT' ||
|
|
549
|
+
err.code === 'UND_ERR_SOCKET' ||
|
|
550
|
+
err.code === 'UND_ERR_HEADERS_TIMEOUT';
|
|
551
|
+
|
|
552
|
+
if (isNetworkError) {
|
|
553
|
+
ctx.logger.error({
|
|
554
|
+
msg: 'Network error during OAuth2 token refresh',
|
|
555
|
+
account: accountObject.account,
|
|
556
|
+
code: err.code,
|
|
557
|
+
err
|
|
558
|
+
});
|
|
559
|
+
// Do not mark as authenticationFailed, let the caller handle as a connection error
|
|
560
|
+
throw err;
|
|
561
|
+
}
|
|
562
|
+
|
|
541
563
|
err.authenticationFailed = true;
|
|
542
564
|
let notifyData = {
|
|
543
565
|
response: err.message,
|
|
@@ -12,6 +12,18 @@ const LIST_BATCH_SIZE = 10;
|
|
|
12
12
|
const MAX_RETRY_ATTEMPTS = 3;
|
|
13
13
|
const RETRY_BASE_DELAY = 1000; // 1 second base delay
|
|
14
14
|
|
|
15
|
+
// Network-level errors that are transient and should be retried
|
|
16
|
+
const TRANSIENT_NETWORK_CODES = new Set([
|
|
17
|
+
'ENOTFOUND',
|
|
18
|
+
'EAI_AGAIN',
|
|
19
|
+
'ETIMEDOUT',
|
|
20
|
+
'ECONNRESET',
|
|
21
|
+
'ECONNREFUSED',
|
|
22
|
+
'UND_ERR_SOCKET',
|
|
23
|
+
'UND_ERR_CONNECT_TIMEOUT',
|
|
24
|
+
'UND_ERR_HEADERS_TIMEOUT'
|
|
25
|
+
]);
|
|
26
|
+
|
|
15
27
|
// Gmail API error code mapping to internal error codes
|
|
16
28
|
// https://developers.google.com/gmail/api/reference/rest#error-codes
|
|
17
29
|
const GMAIL_ERROR_MAP = {
|
|
@@ -117,6 +129,29 @@ async function request(context, url, method, payload, options = {}) {
|
|
|
117
129
|
} catch (err) {
|
|
118
130
|
lastError = err;
|
|
119
131
|
|
|
132
|
+
// Check if this is a transient network error
|
|
133
|
+
if (TRANSIENT_NETWORK_CODES.has(err.code) && attempt < maxRetries) {
|
|
134
|
+
const delay = RETRY_BASE_DELAY * Math.pow(2, attempt) + Math.random() * 500;
|
|
135
|
+
|
|
136
|
+
context.logger.warn({
|
|
137
|
+
msg: 'Transient network error during Gmail API request, retrying',
|
|
138
|
+
account: context.account,
|
|
139
|
+
attempt: attempt + 1,
|
|
140
|
+
maxRetries,
|
|
141
|
+
delay,
|
|
142
|
+
code: err.code
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
metricsMeta({ account: context.account }, context.logger, 'oauth2ApiRequest', 'inc', {
|
|
146
|
+
status: 'network_error',
|
|
147
|
+
provider: 'gmail',
|
|
148
|
+
statusCode: '0'
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
120
155
|
// Check if this is a rate limit error
|
|
121
156
|
if (isRateLimitError(err) && attempt < maxRetries) {
|
|
122
157
|
const delay = calculateRetryDelay(err, attempt);
|
|
@@ -94,7 +94,10 @@ class Mailbox {
|
|
|
94
94
|
getMailboxStatus(connectionClient) {
|
|
95
95
|
connectionClient = connectionClient || this.connection.imapClient;
|
|
96
96
|
if (!connectionClient) {
|
|
97
|
-
|
|
97
|
+
let err = new Error('IMAP connection not available');
|
|
98
|
+
err.code = 'IMAPConnectionClosing';
|
|
99
|
+
err.statusCode = 503;
|
|
100
|
+
throw err;
|
|
98
101
|
}
|
|
99
102
|
|
|
100
103
|
let mailboxInfo = connectionClient.mailbox;
|
|
@@ -490,6 +493,12 @@ class Mailbox {
|
|
|
490
493
|
* @param {Boolean} skipIdle - Don't start IDLE after selecting
|
|
491
494
|
*/
|
|
492
495
|
async select(skipIdle) {
|
|
496
|
+
if (!this.connection.imapClient) {
|
|
497
|
+
let err = new Error('IMAP connection not available');
|
|
498
|
+
err.code = 'IMAPConnectionClosing';
|
|
499
|
+
err.statusCode = 503;
|
|
500
|
+
throw err;
|
|
501
|
+
}
|
|
493
502
|
const currentLock = this.connection.imapClient.currentLock;
|
|
494
503
|
// Avoid interfering with any active operations
|
|
495
504
|
if (currentLock) {
|
|
@@ -520,7 +529,7 @@ class Mailbox {
|
|
|
520
529
|
|
|
521
530
|
// Check if we still need to select after getting the lock
|
|
522
531
|
// Another operation might have already selected this mailbox while we were waiting
|
|
523
|
-
if (this.connection.imapClient
|
|
532
|
+
if (this.connection.imapClient?.mailbox && this.connection.imapClient.mailbox.path === this.path) {
|
|
524
533
|
this.logger.trace({
|
|
525
534
|
msg: 'Mailbox already selected after lock acquired',
|
|
526
535
|
path: this.path
|
|
@@ -551,7 +560,10 @@ class Mailbox {
|
|
|
551
560
|
connectionClient = connectionClient || this.connection.imapClient;
|
|
552
561
|
|
|
553
562
|
if (!connectionClient) {
|
|
554
|
-
|
|
563
|
+
let err = new Error('IMAP connection not available');
|
|
564
|
+
err.code = 'IMAPConnectionClosing';
|
|
565
|
+
err.statusCode = 503;
|
|
566
|
+
throw err;
|
|
555
567
|
}
|
|
556
568
|
|
|
557
569
|
let lock = await connectionClient.getMailboxLock(this.path, options || {});
|
|
@@ -613,7 +625,13 @@ class Mailbox {
|
|
|
613
625
|
return false;
|
|
614
626
|
})
|
|
615
627
|
.then(() => this.select())
|
|
616
|
-
.catch(err =>
|
|
628
|
+
.catch(err => {
|
|
629
|
+
if (err.code === 'IMAPConnectionClosing') {
|
|
630
|
+
this.logger.debug({ msg: 'Sync skipped, connection closing', err });
|
|
631
|
+
} else {
|
|
632
|
+
this.logger.error({ msg: 'Sync error', err });
|
|
633
|
+
}
|
|
634
|
+
});
|
|
617
635
|
}, 1000);
|
|
618
636
|
}
|
|
619
637
|
|
|
@@ -1090,7 +1108,7 @@ class Mailbox {
|
|
|
1090
1108
|
|
|
1091
1109
|
// Resolve Gmail category for inbox messages
|
|
1092
1110
|
if (
|
|
1093
|
-
this.connection.imapClient
|
|
1111
|
+
this.connection.imapClient?.capabilities?.has('X-GM-EXT-1') &&
|
|
1094
1112
|
this.isAllMail &&
|
|
1095
1113
|
messageInfo.labels &&
|
|
1096
1114
|
messageInfo.labels.includes('\\Inbox') &&
|
|
@@ -2069,7 +2087,7 @@ class Mailbox {
|
|
|
2069
2087
|
}
|
|
2070
2088
|
|
|
2071
2089
|
// Partial sync if CONDSTORE indicates only new messages or flag changes
|
|
2072
|
-
if (canUseCondstorePartialSync(this.connection.imapClient, storedStatus, mailboxStatus)) {
|
|
2090
|
+
if (this.connection.imapClient && canUseCondstorePartialSync(this.connection.imapClient, storedStatus, mailboxStatus)) {
|
|
2073
2091
|
return await this.partialSync(storedStatus);
|
|
2074
2092
|
}
|
|
2075
2093
|
|
|
@@ -197,7 +197,10 @@ class SyncOperations {
|
|
|
197
197
|
this.mailbox.syncing = true;
|
|
198
198
|
try {
|
|
199
199
|
if (!this.connection.imapClient) {
|
|
200
|
-
|
|
200
|
+
let err = new Error('IMAP connection not available');
|
|
201
|
+
err.code = 'IMAPConnectionClosing';
|
|
202
|
+
err.statusCode = 503;
|
|
203
|
+
throw err;
|
|
201
204
|
}
|
|
202
205
|
|
|
203
206
|
let knownUidNext = typeof storedStatus.uidNext === 'number' ? storedStatus.uidNext || 1 : 1;
|
|
@@ -351,7 +354,10 @@ class SyncOperations {
|
|
|
351
354
|
this.mailbox.syncing = true;
|
|
352
355
|
try {
|
|
353
356
|
if (!this.connection.imapClient) {
|
|
354
|
-
|
|
357
|
+
let err = new Error('IMAP connection not available');
|
|
358
|
+
err.code = 'IMAPConnectionClosing';
|
|
359
|
+
err.statusCode = 503;
|
|
360
|
+
throw err;
|
|
355
361
|
}
|
|
356
362
|
|
|
357
363
|
let fields = { uid: true, flags: true, modseq: true, emailId: true, labels: true, internalDate: true };
|
|
@@ -378,7 +384,10 @@ class SyncOperations {
|
|
|
378
384
|
let imapClient = this.connection.imapClient;
|
|
379
385
|
if (!imapClient || !imapClient.usable) {
|
|
380
386
|
this.logger.error({ msg: 'IMAP client not available for partial sync' });
|
|
381
|
-
|
|
387
|
+
let err = new Error('IMAP connection not available');
|
|
388
|
+
err.code = 'IMAPConnectionClosing';
|
|
389
|
+
err.statusCode = 503;
|
|
390
|
+
throw err;
|
|
382
391
|
}
|
|
383
392
|
|
|
384
393
|
try {
|
|
@@ -556,7 +565,10 @@ class SyncOperations {
|
|
|
556
565
|
const imapClient = this.connection.imapClient;
|
|
557
566
|
if (!imapClient || !imapClient.usable) {
|
|
558
567
|
this.logger.error({ msg: 'IMAP client not available for FETCH' });
|
|
559
|
-
|
|
568
|
+
let err = new Error('IMAP connection not available');
|
|
569
|
+
err.code = 'IMAPConnectionClosing';
|
|
570
|
+
err.statusCode = 503;
|
|
571
|
+
throw err;
|
|
560
572
|
}
|
|
561
573
|
|
|
562
574
|
try {
|
|
@@ -728,7 +740,7 @@ class SyncOperations {
|
|
|
728
740
|
|
|
729
741
|
// Verify we're still on the correct mailbox after the delay
|
|
730
742
|
// Another operation might have changed the mailbox while we were waiting
|
|
731
|
-
const currentMailbox = this.connection.imapClient
|
|
743
|
+
const currentMailbox = this.connection.imapClient?.mailbox;
|
|
732
744
|
if (!currentMailbox || currentMailbox.path !== this.mailbox.path) {
|
|
733
745
|
this.logger.error({
|
|
734
746
|
msg: 'Mailbox changed during retry delay, aborting sync',
|
|
@@ -191,6 +191,12 @@ class IMAPClient extends BaseClient {
|
|
|
191
191
|
let syncing = this.syncing || ['init', 'connecting', 'syncing'].includes(this.state);
|
|
192
192
|
if (!noPool && (!syncing || !allowSecondary)) {
|
|
193
193
|
// Return the primary connection for most operations
|
|
194
|
+
if (!this.imapClient) {
|
|
195
|
+
let err = new Error('IMAP connection not available');
|
|
196
|
+
err.code = 'IMAPConnectionClosing';
|
|
197
|
+
err.statusCode = 503;
|
|
198
|
+
throw err;
|
|
199
|
+
}
|
|
194
200
|
return this.imapClient;
|
|
195
201
|
}
|
|
196
202
|
|
|
@@ -202,14 +208,19 @@ class IMAPClient extends BaseClient {
|
|
|
202
208
|
if (connectionClient && connectionClient.usable) {
|
|
203
209
|
connectionOptions.connectionClient = connectionClient;
|
|
204
210
|
return connectionClient;
|
|
205
|
-
} else {
|
|
206
|
-
// fall back to default connection
|
|
207
|
-
return this.imapClient;
|
|
208
211
|
}
|
|
209
212
|
} catch (err) {
|
|
210
213
|
this.logger.error({ msg: 'Failed to acquire command connection', reason, err });
|
|
211
|
-
return this.imapClient;
|
|
212
214
|
}
|
|
215
|
+
|
|
216
|
+
// Fall back to primary connection
|
|
217
|
+
if (!this.imapClient) {
|
|
218
|
+
let err = new Error('IMAP connection not available');
|
|
219
|
+
err.code = 'IMAPConnectionClosing';
|
|
220
|
+
err.statusCode = 503;
|
|
221
|
+
throw err;
|
|
222
|
+
}
|
|
223
|
+
return this.imapClient;
|
|
213
224
|
}
|
|
214
225
|
|
|
215
226
|
/**
|
|
@@ -535,14 +546,6 @@ class IMAPClient extends BaseClient {
|
|
|
535
546
|
this.checkIMAPConnection(connectionOptions);
|
|
536
547
|
|
|
537
548
|
const connectionClient = await this.getImapConnection(connectionOptions, 'getCurrentListing');
|
|
538
|
-
if (!connectionClient) {
|
|
539
|
-
if (this.imapClient) {
|
|
540
|
-
this.imapClient.close();
|
|
541
|
-
}
|
|
542
|
-
let error = new Error('Failed to get connection');
|
|
543
|
-
error.code = 'ConnectionError';
|
|
544
|
-
throw error;
|
|
545
|
-
}
|
|
546
549
|
|
|
547
550
|
let accountData = await this.accountObject.loadAccountData();
|
|
548
551
|
|
|
@@ -802,7 +805,11 @@ class IMAPClient extends BaseClient {
|
|
|
802
805
|
try {
|
|
803
806
|
await mailbox.onOpen(event);
|
|
804
807
|
} catch (err) {
|
|
805
|
-
|
|
808
|
+
if (err.code === 'IMAPConnectionClosing') {
|
|
809
|
+
imapClient.log.debug({ msg: 'Open skipped, connection closing', err });
|
|
810
|
+
} else {
|
|
811
|
+
imapClient.log.error({ msg: 'Open error', err });
|
|
812
|
+
}
|
|
806
813
|
}
|
|
807
814
|
});
|
|
808
815
|
|
|
@@ -7,6 +7,18 @@ const { OUTLOOK_MAX_BATCH_SIZE, OUTLOOK_MAX_RETRY_ATTEMPTS, OUTLOOK_RETRY_BASE_D
|
|
|
7
7
|
// Maximum number of operations in a single batch request to Microsoft Graph API
|
|
8
8
|
const MAX_BATCH_SIZE = OUTLOOK_MAX_BATCH_SIZE;
|
|
9
9
|
|
|
10
|
+
// Network-level errors that are transient and should be retried
|
|
11
|
+
const TRANSIENT_NETWORK_CODES = new Set([
|
|
12
|
+
'ENOTFOUND',
|
|
13
|
+
'EAI_AGAIN',
|
|
14
|
+
'ETIMEDOUT',
|
|
15
|
+
'ECONNRESET',
|
|
16
|
+
'ECONNREFUSED',
|
|
17
|
+
'UND_ERR_SOCKET',
|
|
18
|
+
'UND_ERR_CONNECT_TIMEOUT',
|
|
19
|
+
'UND_ERR_HEADERS_TIMEOUT'
|
|
20
|
+
]);
|
|
21
|
+
|
|
10
22
|
// MS Graph API error code mapping to internal error codes
|
|
11
23
|
// https://learn.microsoft.com/en-us/graph/errors
|
|
12
24
|
const GRAPH_ERROR_MAP = {
|
|
@@ -163,6 +175,25 @@ async function requestWithRetry(context, url, method, payload, options = {}) {
|
|
|
163
175
|
try {
|
|
164
176
|
return await request(context, url, method, payload, options);
|
|
165
177
|
} catch (err) {
|
|
178
|
+
// Retry on transient network errors
|
|
179
|
+
if (TRANSIENT_NETWORK_CODES.has(err.code) && attempt < maxRetries) {
|
|
180
|
+
lastError = err;
|
|
181
|
+
const delay = Math.min(OUTLOOK_RETRY_BASE_DELAY * Math.pow(2, attempt), OUTLOOK_RETRY_MAX_DELAY);
|
|
182
|
+
|
|
183
|
+
context.logger.warn({
|
|
184
|
+
msg: 'Transient network error during Graph API request, retrying',
|
|
185
|
+
account: context.account,
|
|
186
|
+
attempt: attempt + 1,
|
|
187
|
+
maxRetries,
|
|
188
|
+
delay,
|
|
189
|
+
code: err.code,
|
|
190
|
+
url
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
await new Promise(resolve => setTimeout(resolve, delay * 1000));
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
166
197
|
// Only retry on 429 (rate limit) errors
|
|
167
198
|
if (err.oauthRequest?.status !== 429 || attempt === maxRetries) {
|
|
168
199
|
throw err;
|
|
@@ -203,7 +203,18 @@ class PubSubInstance {
|
|
|
203
203
|
await oauth2Apps.setMeta(this.app, { pubSubFlag: null });
|
|
204
204
|
} catch (err) {
|
|
205
205
|
// Transient network errors are expected for long-polling connections
|
|
206
|
-
if (
|
|
206
|
+
if (
|
|
207
|
+
[
|
|
208
|
+
'ENOTFOUND',
|
|
209
|
+
'EAI_AGAIN',
|
|
210
|
+
'ETIMEDOUT',
|
|
211
|
+
'ECONNRESET',
|
|
212
|
+
'ECONNREFUSED',
|
|
213
|
+
'UND_ERR_SOCKET',
|
|
214
|
+
'UND_ERR_CONNECT_TIMEOUT',
|
|
215
|
+
'UND_ERR_HEADERS_TIMEOUT'
|
|
216
|
+
].includes(err.code)
|
|
217
|
+
) {
|
|
207
218
|
logger.warn({ msg: 'Transient error pulling subscription messages', app: this.app, code: err.code });
|
|
208
219
|
return;
|
|
209
220
|
}
|
package/lib/oauth2-apps.js
CHANGED
|
@@ -11,6 +11,17 @@ const Lock = require('ioredfour');
|
|
|
11
11
|
const getSecret = require('./get-secret');
|
|
12
12
|
const { parentPort } = require('worker_threads');
|
|
13
13
|
|
|
14
|
+
const TRANSIENT_NETWORK_CODES = new Set([
|
|
15
|
+
'ENOTFOUND',
|
|
16
|
+
'EAI_AGAIN',
|
|
17
|
+
'ETIMEDOUT',
|
|
18
|
+
'ECONNRESET',
|
|
19
|
+
'ECONNREFUSED',
|
|
20
|
+
'UND_ERR_SOCKET',
|
|
21
|
+
'UND_ERR_CONNECT_TIMEOUT',
|
|
22
|
+
'UND_ERR_HEADERS_TIMEOUT'
|
|
23
|
+
]);
|
|
24
|
+
|
|
14
25
|
/**
|
|
15
26
|
* Record metrics for OAuth2 token operations
|
|
16
27
|
* Works in both main thread and worker threads
|
|
@@ -922,6 +933,10 @@ class OAuth2AppsHandler {
|
|
|
922
933
|
{name: 'projects/...'}
|
|
923
934
|
*/
|
|
924
935
|
} catch (err) {
|
|
936
|
+
if (TRANSIENT_NETWORK_CODES.has(err.code)) {
|
|
937
|
+
logger.warn({ msg: 'Network error checking Pub/Sub topic', app: appData.id, code: err.code });
|
|
938
|
+
throw err;
|
|
939
|
+
}
|
|
925
940
|
switch (err?.oauthRequest?.response?.error?.code) {
|
|
926
941
|
case 403:
|
|
927
942
|
// no permissions
|
|
@@ -964,6 +979,10 @@ class OAuth2AppsHandler {
|
|
|
964
979
|
|
|
965
980
|
results.pubSubTopic = topicName;
|
|
966
981
|
} catch (err) {
|
|
982
|
+
if (TRANSIENT_NETWORK_CODES.has(err.code)) {
|
|
983
|
+
logger.warn({ msg: 'Network error creating Pub/Sub topic', app: appData.id, code: err.code });
|
|
984
|
+
throw err;
|
|
985
|
+
}
|
|
967
986
|
switch (err?.oauthRequest?.response?.error?.code) {
|
|
968
987
|
case 403:
|
|
969
988
|
// no permissions
|
|
@@ -999,6 +1018,10 @@ class OAuth2AppsHandler {
|
|
|
999
1018
|
{name: 'projects/...'}
|
|
1000
1019
|
*/
|
|
1001
1020
|
} catch (err) {
|
|
1021
|
+
if (TRANSIENT_NETWORK_CODES.has(err.code)) {
|
|
1022
|
+
logger.warn({ msg: 'Network error checking Pub/Sub subscription', app: appData.id, code: err.code });
|
|
1023
|
+
throw err;
|
|
1024
|
+
}
|
|
1002
1025
|
switch (err?.oauthRequest?.response?.error?.code) {
|
|
1003
1026
|
case 403:
|
|
1004
1027
|
// no permissions
|
|
@@ -1042,6 +1065,10 @@ class OAuth2AppsHandler {
|
|
|
1042
1065
|
|
|
1043
1066
|
results.pubSubSubscription = subscriptionName;
|
|
1044
1067
|
} catch (err) {
|
|
1068
|
+
if (TRANSIENT_NETWORK_CODES.has(err.code)) {
|
|
1069
|
+
logger.warn({ msg: 'Network error creating Pub/Sub subscription', app: appData.id, code: err.code });
|
|
1070
|
+
throw err;
|
|
1071
|
+
}
|
|
1045
1072
|
switch (err?.oauthRequest?.response?.error?.code) {
|
|
1046
1073
|
case 403:
|
|
1047
1074
|
// no permissions
|
|
@@ -1087,6 +1114,10 @@ class OAuth2AppsHandler {
|
|
|
1087
1114
|
}
|
|
1088
1115
|
*/
|
|
1089
1116
|
} catch (err) {
|
|
1117
|
+
if (TRANSIENT_NETWORK_CODES.has(err.code)) {
|
|
1118
|
+
logger.warn({ msg: 'Network error checking Pub/Sub IAM policy', app: appData.id, code: err.code });
|
|
1119
|
+
throw err;
|
|
1120
|
+
}
|
|
1090
1121
|
switch (err?.oauthRequest?.response?.error?.code) {
|
|
1091
1122
|
case 403:
|
|
1092
1123
|
// no permissions
|
|
@@ -1130,6 +1161,10 @@ class OAuth2AppsHandler {
|
|
|
1130
1161
|
{ partial: true }
|
|
1131
1162
|
);
|
|
1132
1163
|
} catch (err) {
|
|
1164
|
+
if (TRANSIENT_NETWORK_CODES.has(err.code)) {
|
|
1165
|
+
logger.warn({ msg: 'Network error setting Pub/Sub IAM policy', app: appData.id, code: err.code });
|
|
1166
|
+
throw err;
|
|
1167
|
+
}
|
|
1133
1168
|
switch (err?.oauthRequest?.response?.error?.code) {
|
|
1134
1169
|
case 403:
|
|
1135
1170
|
// no permissions
|
package/lib/tools.js
CHANGED
|
@@ -2069,6 +2069,7 @@ RHH2IL/f3lipzTFk
|
|
|
2069
2069
|
5q3TdjzMplq0+yI7
|
|
2070
2070
|
nL6uRkZxgug/JFyl
|
|
2071
2071
|
uSY0M+yfff4Op/HP
|
|
2072
|
+
tpADMZ5Ir6bmwexH
|
|
2072
2073
|
h7Squx/sKZNkDat0
|
|
2073
2074
|
elb9jWFuO3UHnphx
|
|
2074
2075
|
//m57pmYUuiv84pZ
|
|
@@ -2102,6 +2103,10 @@ r/CVn8qB2xdyVXCg
|
|
|
2102
2103
|
iSeRSmpWBZGpkTZi
|
|
2103
2104
|
ZZkXc6t+ZDyuhEoO
|
|
2104
2105
|
vPHQ3BYeEr+9DSlW
|
|
2106
|
+
5ChBAv/YX3PWk/Wz
|
|
2107
|
+
YjuOzxqQEGdKLC0Q
|
|
2108
|
+
nmu7gC+IxPl4AgjV
|
|
2109
|
+
43Du3vhZaEGambkT
|
|
2105
2110
|
vIwyKmyMuykMUPeA
|
|
2106
2111
|
RTIy3POEF2gZADpU
|
|
2107
2112
|
pqvSQnQ/jx8sYGi4
|
|
@@ -2144,6 +2149,7 @@ AW6pdfk8U1/EXPdY
|
|
|
2144
2149
|
Q/6B3a679QVyRrsE
|
|
2145
2150
|
EsbWnuADOq9qe/EZ
|
|
2146
2151
|
1xyU3RkTVo/iQd3J
|
|
2152
|
+
+HW4BuGQxaXdahTi
|
|
2147
2153
|
YfUSxQtq1+kpwW/W
|
|
2148
2154
|
QodzxvoJ6NPGhCgW
|
|
2149
2155
|
5/VK+O950efBio0t
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emailengine-app",
|
|
3
|
-
"version": "2.63.
|
|
3
|
+
"version": "2.63.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"productTitle": "EmailEngine",
|
|
6
6
|
"description": "Email Sync Engine",
|
|
@@ -52,15 +52,15 @@
|
|
|
52
52
|
"@hapi/boom": "10.0.1",
|
|
53
53
|
"@hapi/cookie": "12.0.1",
|
|
54
54
|
"@hapi/crumb": "9.0.1",
|
|
55
|
-
"@hapi/hapi": "21.4.
|
|
55
|
+
"@hapi/hapi": "21.4.7",
|
|
56
56
|
"@hapi/inert": "7.1.0",
|
|
57
57
|
"@hapi/vision": "7.0.3",
|
|
58
58
|
"@phc/pbkdf2": "1.1.14",
|
|
59
59
|
"@postalsys/bounce-classifier": "^2.0.0",
|
|
60
60
|
"@postalsys/certs": "1.0.12",
|
|
61
61
|
"@postalsys/ee-client": "1.3.0",
|
|
62
|
-
"@postalsys/email-ai-tools": "1.11.
|
|
63
|
-
"@postalsys/email-text-tools": "2.4.
|
|
62
|
+
"@postalsys/email-ai-tools": "1.11.4",
|
|
63
|
+
"@postalsys/email-text-tools": "2.4.2",
|
|
64
64
|
"@postalsys/gettext": "4.1.1",
|
|
65
65
|
"@postalsys/joi-messages": "1.0.5",
|
|
66
66
|
"@postalsys/templates": "2.0.0",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"@zone-eu/wild-config": "1.7.3",
|
|
69
69
|
"ace-builds": "1.43.6",
|
|
70
70
|
"base32.js": "0.1.0",
|
|
71
|
-
"bullmq": "5.70.
|
|
71
|
+
"bullmq": "5.70.4",
|
|
72
72
|
"compare-versions": "6.1.1",
|
|
73
73
|
"dotenv": "17.3.1",
|
|
74
74
|
"encoding-japanese": "2.2.0",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"html-to-text": "9.0.5",
|
|
83
83
|
"ical.js": "1.5.0",
|
|
84
84
|
"iconv-lite": "0.7.2",
|
|
85
|
-
"imapflow": "1.2.
|
|
85
|
+
"imapflow": "1.2.13",
|
|
86
86
|
"ioredfour": "1.4.0",
|
|
87
87
|
"ioredis": "5.10.0",
|
|
88
88
|
"ipaddr.js": "2.3.0",
|
|
@@ -92,13 +92,13 @@
|
|
|
92
92
|
"libmime": "5.3.7",
|
|
93
93
|
"libqp": "2.1.1",
|
|
94
94
|
"license-checker": "25.0.1",
|
|
95
|
-
"mailparser": "3.9.
|
|
95
|
+
"mailparser": "3.9.4",
|
|
96
96
|
"marked": "9.1.6",
|
|
97
97
|
"minimist": "1.2.8",
|
|
98
98
|
"msgpack5": "6.0.2",
|
|
99
99
|
"murmurhash": "2.0.1",
|
|
100
100
|
"nanoid": "3.3.8",
|
|
101
|
-
"nodemailer": "8.0.
|
|
101
|
+
"nodemailer": "8.0.2",
|
|
102
102
|
"pino": "10.3.1",
|
|
103
103
|
"popper.js": "1.16.1",
|
|
104
104
|
"prom-client": "15.1.3",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"@eslint/js": "10.0.1",
|
|
119
119
|
"chai": "4.3.10",
|
|
120
120
|
"eerawlog": "1.5.1",
|
|
121
|
-
"eslint": "10.0.
|
|
121
|
+
"eslint": "10.0.3",
|
|
122
122
|
"grunt": "1.6.1",
|
|
123
123
|
"grunt-cli": "1.5.0",
|
|
124
124
|
"grunt-shell-spawn": "0.5.0",
|