emailengine-app 2.61.3 → 2.61.5

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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.61.5](https://github.com/postalsys/emailengine/compare/v2.61.4...v2.61.5) (2026-01-15)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * use base64url encoding for OAuth state nonce in /v1/authentication/form ([1f2cecf](https://github.com/postalsys/emailengine/commit/1f2cecf9efbee8a12c3a0d27c9879bfbbf7dfa39))
9
+ * use base64url encoding for OAuth state nonce in /v1/authentication/form ([dead38c](https://github.com/postalsys/emailengine/commit/dead38c348f1c0204f2bfba09997090cb86c348b))
10
+ * use base64url encoding for OAuth state nonce in remaining locations ([961b710](https://github.com/postalsys/emailengine/commit/961b710357d836782c9bbce329178aa96e08ee20))
11
+
12
+ ## [2.61.4](https://github.com/postalsys/emailengine/compare/v2.61.3...v2.61.4) (2026-01-14)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * handle delegation errors in loadAccountData, isApiClient, and UI listing ([c306618](https://github.com/postalsys/emailengine/commit/c306618a37428bbf088ed1289bf832e778f14b9c))
18
+ * show Failed status for accounts with IMAP disabled due to auth errors ([e6b687d](https://github.com/postalsys/emailengine/commit/e6b687d458cfd080795f7176f862109e2c299fff))
19
+
3
20
  ## [2.61.3](https://github.com/postalsys/emailengine/compare/v2.61.2...v2.61.3) (2026-01-14)
4
21
 
5
22
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "creationTime": "2026-01-13T15:46:00.000000",
2
+ "creationTime": "2026-01-14T15:45:48.000000",
3
3
  "prefixes": [
4
4
  {
5
5
  "ipv6Prefix": "2001:4860:4801:2008::/64"
package/lib/account.js CHANGED
@@ -697,20 +697,30 @@ class Account {
697
697
  accountData._app = app;
698
698
  }
699
699
  } else if (accountData.oauth2 && accountData.oauth2.auth && accountData.oauth2.auth.delegatedAccount) {
700
- let delegatedAccount = await resolveDelegatedAccount(this.redis, accountData.account);
701
- if (delegatedAccount) {
702
- accountData.delegatedAccount = delegatedAccount;
703
- let delegatedAccountRow = await this.redis.hgetall(`${REDIS_PREFIX}iad:${delegatedAccount}`);
704
- let delegatedAccountData = this.unserializeAccountData(delegatedAccountRow);
705
- if (delegatedAccountData.oauth2 && delegatedAccountData.oauth2.provider) {
706
- let app = await oauth2Apps.get(delegatedAccountData.oauth2.provider);
707
- if (app) {
708
- accountData._app = app;
709
- if (app.baseScopes === 'api') {
710
- accountData.isApi = true;
700
+ try {
701
+ let delegatedAccount = await resolveDelegatedAccount(this.redis, accountData.account);
702
+ if (delegatedAccount) {
703
+ accountData.delegatedAccount = delegatedAccount;
704
+ let delegatedAccountRow = await this.redis.hgetall(`${REDIS_PREFIX}iad:${delegatedAccount}`);
705
+ let delegatedAccountData = this.unserializeAccountData(delegatedAccountRow);
706
+ if (delegatedAccountData.oauth2 && delegatedAccountData.oauth2.provider) {
707
+ let app = await oauth2Apps.get(delegatedAccountData.oauth2.provider);
708
+ if (app) {
709
+ accountData._app = app;
710
+ if (app.baseScopes === 'api') {
711
+ accountData.isApi = true;
712
+ }
711
713
  }
712
714
  }
713
715
  }
716
+ } catch (err) {
717
+ this.logger.warn({
718
+ msg: 'Failed to resolve delegated account',
719
+ account: accountData.account,
720
+ delegatedAccount: accountData.oauth2.auth.delegatedAccount,
721
+ err
722
+ });
723
+ accountData.delegationError = err.message;
714
724
  }
715
725
  }
716
726
 
@@ -2545,17 +2555,22 @@ class Account {
2545
2555
 
2546
2556
  async isApiClient(accountData) {
2547
2557
  if (accountData.oauth2?.auth?.delegatedAccount) {
2548
- let delegatedAccount = await resolveDelegatedAccount(this.redis, accountData.account);
2549
- if (delegatedAccount) {
2550
- accountData.delegatedAccount = delegatedAccount;
2551
- let delegatedAccountRow = await this.redis.hgetall(`${REDIS_PREFIX}iad:${delegatedAccount}`);
2552
- let delegatedAccountData = this.unserializeAccountData(delegatedAccountRow);
2553
- if (delegatedAccountData?.oauth2?.provider) {
2554
- let app = await oauth2Apps.get(delegatedAccountData.oauth2.provider);
2555
- return app?.baseScopes === 'api';
2556
- } else {
2557
- return false;
2558
+ try {
2559
+ let delegatedAccount = await resolveDelegatedAccount(this.redis, accountData.account);
2560
+ if (delegatedAccount) {
2561
+ accountData.delegatedAccount = delegatedAccount;
2562
+ let delegatedAccountRow = await this.redis.hgetall(`${REDIS_PREFIX}iad:${delegatedAccount}`);
2563
+ let delegatedAccountData = this.unserializeAccountData(delegatedAccountRow);
2564
+ if (delegatedAccountData?.oauth2?.provider) {
2565
+ let app = await oauth2Apps.get(delegatedAccountData.oauth2.provider);
2566
+ return app?.baseScopes === 'api';
2567
+ } else {
2568
+ return false;
2569
+ }
2558
2570
  }
2571
+ } catch (err) {
2572
+ // Invalid delegation config - treat as non-API client
2573
+ return false;
2559
2574
  }
2560
2575
  }
2561
2576
 
package/lib/routes-ui.js CHANGED
@@ -618,6 +618,15 @@ function formatAccountData(account, gt) {
618
618
  break;
619
619
  }
620
620
 
621
+ // Check if IMAP was disabled due to errors - override state label to show error
622
+ if (account.imap && account.imap.disabled && account.lastErrorState) {
623
+ account.stateLabel = {
624
+ type: 'danger',
625
+ name: 'Failed',
626
+ error: account.lastErrorState.description || account.lastErrorState.response
627
+ };
628
+ }
629
+
621
630
  if (account.oauth2) {
622
631
  account.oauth2.scopes = []
623
632
  .concat(account.oauth2.scope || [])
@@ -4157,13 +4166,20 @@ ${Buffer.from(data.content, 'base64url').toString('base64')}
4157
4166
 
4158
4167
  for (let account of accounts.accounts) {
4159
4168
  let accountObject = new Account({ redis, account: account.account });
4160
- account.data = await accountObject.loadAccountData(null, null, runIndex);
4169
+ try {
4170
+ account.data = await accountObject.loadAccountData(null, null, runIndex);
4161
4171
 
4162
- if (account.data && account.data.oauth2 && account.data.oauth2.provider) {
4163
- let oauth2App = await oauth2Apps.get(account.data.oauth2.provider);
4164
- if (oauth2App) {
4165
- account.data.oauth2.app = oauth2App;
4172
+ if (account.data && account.data.oauth2 && account.data.oauth2.provider) {
4173
+ let oauth2App = await oauth2Apps.get(account.data.oauth2.provider);
4174
+ if (oauth2App) {
4175
+ account.data.oauth2.app = oauth2App;
4176
+ }
4166
4177
  }
4178
+ } catch (err) {
4179
+ // Account has invalid config (e.g., broken delegation)
4180
+ account.data = {
4181
+ delegationError: err.message
4182
+ };
4167
4183
  }
4168
4184
  }
4169
4185
 
@@ -4325,7 +4341,7 @@ ${Buffer.from(data.content, 'base64url').toString('base64')}
4325
4341
  name: request.payload.name,
4326
4342
 
4327
4343
  // identify request
4328
- n: crypto.randomBytes(NONCE_BYTES).toString('base64'),
4344
+ n: crypto.randomBytes(NONCE_BYTES).toString('base64url'),
4329
4345
  t: Date.now()
4330
4346
  });
4331
4347
 
@@ -159,6 +159,15 @@ function formatAccountData(account, gt) {
159
159
  break;
160
160
  }
161
161
 
162
+ // Check if IMAP was disabled due to errors - override state label to show error
163
+ if (account.imap && account.imap.disabled && account.lastErrorState) {
164
+ account.stateLabel = {
165
+ type: 'danger',
166
+ name: 'Failed',
167
+ error: account.lastErrorState.description || account.lastErrorState.response
168
+ };
169
+ }
170
+
162
171
  if (account.oauth2) {
163
172
  account.oauth2.scopes = []
164
173
  .concat(account.oauth2.scope || [])
@@ -417,7 +426,7 @@ function init(args) {
417
426
  name: request.payload.name,
418
427
 
419
428
  // identify request
420
- n: crypto.randomBytes(NONCE_BYTES).toString('base64'),
429
+ n: crypto.randomBytes(NONCE_BYTES).toString('base64url'),
421
430
  t: Date.now()
422
431
  });
423
432
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emailengine-app",
3
- "version": "2.61.3",
3
+ "version": "2.61.5",
4
4
  "private": false,
5
5
  "productTitle": "EmailEngine",
6
6
  "description": "Email Sync Engine",
@@ -59,8 +59,8 @@
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.1",
63
- "@postalsys/email-text-tools": "2.4.0",
62
+ "@postalsys/email-ai-tools": "1.11.2",
63
+ "@postalsys/email-text-tools": "2.4.1",
64
64
  "@postalsys/gettext": "4.1.1",
65
65
  "@postalsys/joi-messages": "1.0.5",
66
66
  "@postalsys/templates": "2.0.0",
@@ -99,7 +99,7 @@
99
99
  "murmurhash": "2.0.1",
100
100
  "nanoid": "3.3.8",
101
101
  "nodemailer": "7.0.12",
102
- "pino": "10.1.1",
102
+ "pino": "10.2.0",
103
103
  "popper.js": "1.16.1",
104
104
  "prom-client": "15.1.3",
105
105
  "psl": "1.15.0",
@@ -125,7 +125,7 @@
125
125
  "grunt-wait": "0.3.0",
126
126
  "jsxgettext": "0.11.0",
127
127
  "pino-pretty": "13.0.0",
128
- "prettier": "3.7.4",
128
+ "prettier": "3.8.0",
129
129
  "resedit": "3.0.1",
130
130
  "spdx-satisfies": "6.0.0",
131
131
  "supertest": "7.2.2",