emailengine-app 2.63.4 → 2.64.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/data/google-crawlers.json +1 -1
  3. package/eslint.config.js +2 -0
  4. package/lib/account.js +6 -2
  5. package/lib/consts.js +17 -1
  6. package/lib/email-client/gmail/gmail-api.js +1 -12
  7. package/lib/email-client/imap-client.js +5 -3
  8. package/lib/email-client/outlook/graph-api.js +7 -13
  9. package/lib/email-client/outlook-client.js +363 -167
  10. package/lib/imapproxy/imap-server.js +1 -0
  11. package/lib/oauth/gmail.js +12 -1
  12. package/lib/oauth/pubsub/google.js +253 -85
  13. package/lib/oauth2-apps.js +554 -377
  14. package/lib/routes-ui.js +186 -91
  15. package/lib/schemas.js +18 -1
  16. package/lib/ui-routes/account-routes.js +1 -1
  17. package/lib/ui-routes/admin-entities-routes.js +3 -3
  18. package/lib/ui-routes/oauth-routes.js +9 -3
  19. package/package.json +9 -9
  20. package/sbom.json +1 -1
  21. package/server.js +54 -22
  22. package/static/licenses.html +27 -27
  23. package/translations/de.mo +0 -0
  24. package/translations/de.po +54 -42
  25. package/translations/en.mo +0 -0
  26. package/translations/en.po +55 -43
  27. package/translations/et.mo +0 -0
  28. package/translations/et.po +54 -42
  29. package/translations/fr.mo +0 -0
  30. package/translations/fr.po +54 -42
  31. package/translations/ja.mo +0 -0
  32. package/translations/ja.po +54 -42
  33. package/translations/messages.pot +74 -52
  34. package/translations/nl.mo +0 -0
  35. package/translations/nl.po +54 -42
  36. package/translations/pl.mo +0 -0
  37. package/translations/pl.po +54 -42
  38. package/views/config/oauth/app.hbs +12 -0
  39. package/views/config/oauth/index.hbs +2 -0
  40. package/views/config/oauth/subscriptions.hbs +175 -0
  41. package/views/error.hbs +4 -4
  42. package/views/partials/oauth_tabs.hbs +8 -0
  43. package/workers/api.js +174 -96
  44. package/workers/documents.js +1 -0
  45. package/workers/imap.js +30 -47
  46. package/workers/smtp.js +1 -0
  47. package/workers/submit.js +1 -0
  48. package/workers/webhooks.js +42 -30
package/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.64.0](https://github.com/postalsys/emailengine/compare/v2.63.4...v2.64.0) (2026-03-16)
4
+
5
+
6
+ ### Features
7
+
8
+ * add configurable Gmail Pub/Sub subscription TTL setting ([ca33e7f](https://github.com/postalsys/emailengine/commit/ca33e7f9101b3375d7e0001cdbe1d7a41a9442d3))
9
+ * add Gmail Subscriptions tab to OAuth config page ([3bd30bf](https://github.com/postalsys/emailengine/commit/3bd30bf0bbc26600eae40d42e95ef7976257e4f2))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * add .catch() to fire-and-forget setMeta and track error fingerprints ([672a03e](https://github.com/postalsys/emailengine/commit/672a03e2524bcaa26c4d3c908fa828f4e636999b))
15
+ * add lock to del() to prevent race with ensurePubsub, fix pubSubApp cleanup ([27f2bdd](https://github.com/postalsys/emailengine/commit/27f2bdd4a9e3d7e913e7f4bed96084a278bb6312))
16
+ * always notify webhook workers when Pub/Sub app config changes ([e9f276a](https://github.com/postalsys/emailengine/commit/e9f276ab528ad384bcbf5c370a0d0cb22e084cc5))
17
+ * auto-recover expired Gmail Pub/Sub subscriptions and expose status via API ([7961ceb](https://github.com/postalsys/emailengine/commit/7961ceb8ce81688e5c3bb435a6c33ed3882afd7b))
18
+ * avoid redundant Redis call and concurrent backfill races in Pub/Sub setup ([05b02af](https://github.com/postalsys/emailengine/commit/05b02afc1c14de1b8fa4e272d26da2056280731a))
19
+ * broaden Pub/Sub notification condition in oauth-routes.js ([cbe1380](https://github.com/postalsys/emailengine/commit/cbe13800ab9460c41f5dcdcd4d7ae50f8e5e75e3))
20
+ * clean up Pub/Sub Redis keys on deletion, refresh stale instances, and localize UI labels ([3b35fa1](https://github.com/postalsys/emailengine/commit/3b35fa19a6a8024a60d2ed21e9fcd69fda349481))
21
+ * clear stale pubSubFlag after restart and consolidate backfill push ([f67961f](https://github.com/postalsys/emailengine/commit/f67961fbc590aa586ef40b65c2189b5dab4d89c0))
22
+ * correct pubSubApp property name in del() and add cleanup tests ([016ae0a](https://github.com/postalsys/emailengine/commit/016ae0a98bc52eeee2bd1ec4f4d0ed58e92c507d))
23
+ * correct typo in Pub/Sub schema version log message ([2cc8e08](https://github.com/postalsys/emailengine/commit/2cc8e085463ed550e341b027e592b5351784182e))
24
+ * eliminate redundant Redis ops in Pub/Sub pull loop and backfill ([c8ebe39](https://github.com/postalsys/emailengine/commit/c8ebe393744eee46c3d19b5be413badd8ab315d6))
25
+ * fix lifecycle event races, lock TTL, and stale clearExisting in MS Graph subscriptions ([3f3cbac](https://github.com/postalsys/emailengine/commit/3f3cbac843c4c221644492855d7c1beb41cd1da0))
26
+ * fix missing renewal retry, clock-skew gap, and incomplete pipeline error check ([8a8b5b6](https://github.com/postalsys/emailengine/commit/8a8b5b68feb3edd7c984707a85ddb6d146739020))
27
+ * fix off-by-one retry cap and simplify MS Graph subscription code ([aa9afbc](https://github.com/postalsys/emailengine/commit/aa9afbc5aea20d1bf775e59dbfd82842c4f8b701))
28
+ * fix Pub/Sub deletion race, add 429 handling, batch ACKs, and lock ensurePubsub ([744c354](https://github.com/postalsys/emailengine/commit/744c354a062f63eedcec8de1a324a5d13519b9a7))
29
+ * fix retry boundary, lock races, and dropped lifecycle events in MS Graph subscriptions ([d62741d](https://github.com/postalsys/emailengine/commit/d62741d2a2aafc47d831482b71ff24fa4c9d1883))
30
+ * fix silent 401/403 error suppression, floating promise, and recovery loop in Pub/Sub ([406211c](https://github.com/postalsys/emailengine/commit/406211cbd62f7d3f1b811fbec81767f319219c63))
31
+ * fix stale subscription state blocking recovery and retry count persisting across reconnects ([60b77d5](https://github.com/postalsys/emailengine/commit/60b77d5d7574abf728c5e1f015c0cecddd5d9fdd))
32
+ * fix subscription loss on subscriptionRemoved, lifecycle webhook timeout, and retry gaps ([62988ab](https://github.com/postalsys/emailengine/commit/62988ab3b690ed98583fa01557a9c4145427e3ae))
33
+ * guard fire-and-forget setMeta calls and add Pub/Sub graceful shutdown ([45f7b64](https://github.com/postalsys/emailengine/commit/45f7b64d7692b220ff14ec0e1c0e9deea84731de))
34
+ * guard releaseLock against undefined, add 429 handling to Pub/Sub deletion ([d67ebe1](https://github.com/postalsys/emailengine/commit/d67ebe138ca9663796eacc5a1c6b5b73e721e6c4))
35
+ * handle TimeoutError from AbortSignal.timeout in Pub/Sub pull loop ([c0e3036](https://github.com/postalsys/emailengine/commit/c0e303641e4eb9f6cdca3c584861052b2bd91148))
36
+ * harden MS Graph subscription lifecycle, locking, cleanup, and error recovery ([8210056](https://github.com/postalsys/emailengine/commit/8210056f7e7b00977fb5487323ff1b8f9f4b7a99))
37
+ * harden Pub/Sub deletion, IAM policy, and worker timeout cleanup ([0be7c3e](https://github.com/postalsys/emailengine/commit/0be7c3eb04f3381306284e1e3790f7c3cc28cb5a))
38
+ * harden Pub/Sub pull loop resilience and fix projectId typo ([8124328](https://github.com/postalsys/emailengine/commit/81243287f339b5fe40cddd0e1d127514d2e11f4a))
39
+ * harden Pub/Sub pull loop, message ACK, list pairing, and shutdown cleanup ([9e9775f](https://github.com/postalsys/emailengine/commit/9e9775f6df97af42bdb8b799c2345a4564f8a77a))
40
+ * harden Pub/Sub pull loop, worker coordination, and OAuth app lifecycle ([529b705](https://github.com/postalsys/emailengine/commit/529b7055dd4aeb0b1d80fabff3f139cf23f00252))
41
+ * harden Pub/Sub shutdown, loop scheduling, and abort lifecycle ([71b3ab4](https://github.com/postalsys/emailengine/commit/71b3ab4625fa3304ae7a4413a3b0a64276948d7f))
42
+ * harden Pub/Sub shutdown, transient error handling, and input validation ([cfc6e0b](https://github.com/postalsys/emailengine/commit/cfc6e0bb73902600b6eecc6bc955af85f06d9b0f))
43
+ * localize error page strings using translation helper ([609ebb9](https://github.com/postalsys/emailengine/commit/609ebb94d1e662ac661f7efe22f6608d7823fb85))
44
+ * move pubsub status from unauthenticated /health to GET /v1/pubsub/status ([f6597ad](https://github.com/postalsys/emailengine/commit/f6597addff4de1c23fbd88964702bc2425f14d0c))
45
+ * prevent oscillating recovery loop for Pub/Sub apps missing googleProjectId ([8bdbc39](https://github.com/postalsys/emailengine/commit/8bdbc39c790b7097b02459b558328c18584585a2))
46
+ * reduce Pub/Sub recovery log noise and respect backoff delay ([63cd78f](https://github.com/postalsys/emailengine/commit/63cd78fcfd0c12934e488f6936109653371f0899))
47
+ * remove dead circuit breaker code from IMAP and webhooks workers ([da9295b](https://github.com/postalsys/emailengine/commit/da9295b951d20a5817116195689dd3c964b3d996))
48
+ * remove Pub/Sub circuit breaker, fix 401/403 recovery handling ([b7017f3](https://github.com/postalsys/emailengine/commit/b7017f34478dbb3bcedfb9a1e098a8a45cb0f83d))
49
+ * reorder OAuth app deletion to prevent pull-loop gap, add startLoop tests ([129f96c](https://github.com/postalsys/emailengine/commit/129f96c99ff9ff2552fa1c1962b1e0a652b8837e))
50
+ * replace custom Redis locks with ioredfour in Outlook subscription code ([019eaa1](https://github.com/postalsys/emailengine/commit/019eaa1f26be504d127cb6c8a05dfe50d8a47b71))
51
+ * resolve lint errors, fix TTL null guard, and extract Pub/Sub constants ([4949431](https://github.com/postalsys/emailengine/commit/4949431c8e05f06c5aafc3dfe93876ea8a2875b9))
52
+ * resolve livelock, timeout, and state corruption in MS Graph subscription lifecycle ([745334e](https://github.com/postalsys/emailengine/commit/745334e8c55fcf4b0b3fc6adef2b37f6cf9aea61))
53
+ * retry transient errors during Pub/Sub resource deletion and log dropped messages ([b9d9de5](https://github.com/postalsys/emailengine/commit/b9d9de56a50b37e0163ea4e9a954d65bc305004c))
54
+ * show OAuth apps with failed Pub/Sub setup in subscriptions list ([14124cd](https://github.com/postalsys/emailengine/commit/14124cd29baf107f1f82dff48695bc51cae4fd97))
55
+ * stop Pub/Sub instances on OAuth2 app deletion and harden lifecycle ([bc0ff75](https://github.com/postalsys/emailengine/commit/bc0ff758cea8d8713485e3a28e81b16ee56d1dc7))
56
+ * surface TTL reconciliation failures to operators via ttlWarning meta flag ([6e5d5c5](https://github.com/postalsys/emailengine/commit/6e5d5c5a97fb9aaff2662f3fe2f90c8226359b4a))
57
+ * update imapflow to 1.2.15 to fix unhandled rejection crashes ([494a3f8](https://github.com/postalsys/emailengine/commit/494a3f8cd9e71bc1fc1683bea2fc92474cb33206))
58
+
3
59
  ## [2.63.4](https://github.com/postalsys/emailengine/compare/v2.63.3...v2.63.4) (2026-03-09)
4
60
 
5
61
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "creationTime": "2026-03-06T15:46:26.000000",
2
+ "creationTime": "2026-03-13T15:46:23.000000",
3
3
  "prefixes": [
4
4
  {
5
5
  "ipv6Prefix": "2001:4860:4801:2008::/64"
package/eslint.config.js CHANGED
@@ -27,6 +27,8 @@ module.exports = [
27
27
  clearTimeout: 'readonly',
28
28
  setInterval: 'readonly',
29
29
  clearInterval: 'readonly',
30
+ AbortController: 'readonly',
31
+ AbortSignal: 'readonly',
30
32
  URL: 'readonly',
31
33
  URLSearchParams: 'readonly',
32
34
  structuredClone: 'readonly',
package/lib/account.js CHANGED
@@ -2374,7 +2374,9 @@ class Account {
2374
2374
  });
2375
2375
  }
2376
2376
  } finally {
2377
- await lock.releaseLock(flushLock);
2377
+ if (flushLock?.success) {
2378
+ await lock.releaseLock(flushLock);
2379
+ }
2378
2380
  }
2379
2381
  }
2380
2382
 
@@ -2490,7 +2492,9 @@ class Account {
2490
2492
 
2491
2493
  throw err;
2492
2494
  } finally {
2493
- await lock.releaseLock(renewLock);
2495
+ if (renewLock?.success) {
2496
+ await lock.releaseLock(renewLock);
2497
+ }
2494
2498
  }
2495
2499
  }
2496
2500
 
package/lib/consts.js CHANGED
@@ -199,11 +199,27 @@ module.exports = {
199
199
  OUTLOOK_EXPIRATION_RENEW_TIME: 24 * 60 * 60 * 1000, // Renew when less than 24 hours remain
200
200
 
201
201
  // MS Graph API retry and rate limiting settings
202
- OUTLOOK_SUBSCRIPTION_LOCK_TTL: 60, // seconds - lock TTL for subscription renewal
202
+ OUTLOOK_SUBSCRIPTION_LOCK_TTL: 4 * 60 * 1000, // ms - lock TTL for subscription operations (ioredfour uses ms)
203
203
  OUTLOOK_MAX_BATCH_SIZE: 20, // MS Graph batch request limit
204
204
  OUTLOOK_MAX_RETRY_ATTEMPTS: 3, // Maximum retry attempts for rate-limited requests
205
205
  OUTLOOK_RETRY_BASE_DELAY: 30, // seconds - base delay for exponential backoff
206
206
  OUTLOOK_RETRY_MAX_DELAY: 120, // seconds - maximum delay between retries
207
+ OUTLOOK_CLOCK_SKEW_BUFFER: 60 * 1000, // ms - buffer for clock skew between EmailEngine and MS Graph servers
208
+
209
+ // Google Pub/Sub subscription defaults
210
+ GMAIL_PUBSUB_DEFAULT_EXPIRATION_TTL: '2678400s', // 31 days in seconds (Google's default)
211
+ GMAIL_PUBSUB_ACK_DEADLINE_SECONDS: 30,
212
+ // Transient network error codes that indicate a retry-worthy connection failure
213
+ TRANSIENT_NETWORK_CODES: new Set([
214
+ 'ENOTFOUND',
215
+ 'EAI_AGAIN',
216
+ 'ETIMEDOUT',
217
+ 'ECONNRESET',
218
+ 'ECONNREFUSED',
219
+ 'UND_ERR_SOCKET',
220
+ 'UND_ERR_CONNECT_TIMEOUT',
221
+ 'UND_ERR_HEADERS_TIMEOUT'
222
+ ]),
207
223
 
208
224
  generateWebhookTable() {
209
225
  let entries = [];
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { metricsMeta } = require('../base-client');
4
+ const { TRANSIENT_NETWORK_CODES } = require('../../consts');
4
5
 
5
6
  // Gmail API configuration
6
7
  const GMAIL_API_BASE = 'https://gmail.googleapis.com';
@@ -12,18 +13,6 @@ const LIST_BATCH_SIZE = 10;
12
13
  const MAX_RETRY_ATTEMPTS = 3;
13
14
  const RETRY_BASE_DELAY = 1000; // 1 second base delay
14
15
 
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
-
27
16
  // Gmail API error code mapping to internal error codes
28
17
  // https://developers.google.com/gmail/api/reference/rest#error-codes
29
18
  const GMAIL_ERROR_MAP = {
@@ -335,9 +335,11 @@ class IMAPClient extends BaseClient {
335
335
  return commandClient;
336
336
  } finally {
337
337
  // Always release the lock
338
- this.logger.debug({ msg: 'Releasing connection lock', lockKey, index: connectLock.index });
339
- await lock.releaseLock(connectLock);
340
- this.logger.debug({ msg: 'Released connection lock', lockKey, index: connectLock.index });
338
+ if (connectLock?.success) {
339
+ this.logger.debug({ msg: 'Releasing connection lock', lockKey, index: connectLock.index });
340
+ await lock.releaseLock(connectLock);
341
+ this.logger.debug({ msg: 'Released connection lock', lockKey, index: connectLock.index });
342
+ }
341
343
  }
342
344
  }
343
345
 
@@ -2,23 +2,17 @@
2
2
 
3
3
  const { metricsMeta } = require('../base-client');
4
4
 
5
- const { OUTLOOK_MAX_BATCH_SIZE, OUTLOOK_MAX_RETRY_ATTEMPTS, OUTLOOK_RETRY_BASE_DELAY, OUTLOOK_RETRY_MAX_DELAY } = require('../../consts');
5
+ const {
6
+ OUTLOOK_MAX_BATCH_SIZE,
7
+ OUTLOOK_MAX_RETRY_ATTEMPTS,
8
+ OUTLOOK_RETRY_BASE_DELAY,
9
+ OUTLOOK_RETRY_MAX_DELAY,
10
+ TRANSIENT_NETWORK_CODES
11
+ } = require('../../consts');
6
12
 
7
13
  // Maximum number of operations in a single batch request to Microsoft Graph API
8
14
  const MAX_BATCH_SIZE = OUTLOOK_MAX_BATCH_SIZE;
9
15
 
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
-
22
16
  // MS Graph API error code mapping to internal error codes
23
17
  // https://learn.microsoft.com/en-us/graph/errors
24
18
  const GRAPH_ERROR_MAP = {