emailengine-app 2.70.0 → 2.72.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/codeql.yml +3 -0
- package/.github/workflows/e2e.yml +56 -0
- package/.github/workflows/test.yml +81 -12
- package/.ncurc.js +20 -20
- package/CHANGELOG.md +25 -0
- package/Gruntfile.js +19 -23
- package/bin/emailengine.js +8 -1
- package/config/default.toml +5 -0
- package/config/e2e.toml +35 -0
- package/config/test.toml +5 -0
- package/data/google-crawlers.json +1 -1
- package/getswagger.sh +4 -0
- package/lib/account.js +31 -25
- package/lib/api-routes/message-routes.js +125 -121
- package/lib/auth-token.js +83 -0
- package/lib/delivery-error.js +62 -0
- package/lib/document-store.js +22 -1
- package/lib/email-client/base-client.js +3 -2
- package/lib/email-client/gmail-client.js +33 -1
- package/lib/email-client/imap/mailbox.js +2 -2
- package/lib/email-client/notification-handler.js +2 -2
- package/lib/export.js +12 -0
- package/lib/feature-flags.js +6 -0
- package/lib/imap-proxy-auth.js +81 -0
- package/lib/imapproxy/imap-server.js +8 -103
- package/lib/license-beacon.js +367 -0
- package/lib/logger.js +11 -1
- package/lib/oauth/gmail.js +3 -0
- package/lib/oauth/outlook.js +3 -0
- package/lib/oauth2-apps.js +100 -11
- package/lib/routes-ui.js +2 -1
- package/lib/smtp-auth.js +70 -0
- package/lib/sub-script.js +8 -2
- package/lib/tools.js +26 -2
- package/lib/ui-routes/admin-config-routes.js +4 -3
- package/lib/ui-routes/document-store-routes.js +7 -1
- package/package.json +30 -24
- package/playwright.config.js +45 -0
- package/sbom.json +1 -1
- package/server.js +30 -8
- package/static/licenses.html +108 -128
- package/test-coverage-plan.md +233 -0
- package/translations/de.mo +0 -0
- package/translations/de.po +154 -142
- package/translations/et.mo +0 -0
- package/translations/et.po +129 -131
- package/translations/fr.mo +0 -0
- package/translations/fr.po +133 -136
- package/translations/ja.mo +0 -0
- package/translations/ja.po +126 -129
- package/translations/messages.pot +37 -37
- package/translations/nl.mo +0 -0
- package/translations/nl.po +128 -130
- package/translations/pl.mo +0 -0
- package/translations/pl.po +125 -128
- package/views/dashboard.hbs +22 -0
- package/workers/api.js +22 -5
- package/workers/export.js +58 -43
- package/workers/smtp.js +5 -85
- package/workers/submit.js +2 -12
|
@@ -22,6 +22,9 @@ on:
|
|
|
22
22
|
jobs:
|
|
23
23
|
analyze:
|
|
24
24
|
name: Analyze (${{ matrix.language }})
|
|
25
|
+
# Skip release-please's auto-generated release PR: it only changes CHANGELOG/version,
|
|
26
|
+
# so there is no new code to scan. (Matches the guard in test.yml.)
|
|
27
|
+
if: ${{ !startsWith(github.ref_name, 'release-please--') && !startsWith(github.head_ref, 'release-please--') }}
|
|
25
28
|
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
|
26
29
|
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
|
27
30
|
# - https://gh.io/supported-runners-and-hardware-resources
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: E2E Tests
|
|
2
|
+
|
|
3
|
+
# Browser-driven happy-path end-to-end suite (test/e2e), kept separate from the unit and
|
|
4
|
+
# integration tiers so it is easy to re-run on its own and has its own requirements: it drives a
|
|
5
|
+
# real browser (Playwright/Chromium) and reaches external services (Ethereal for the test mailbox,
|
|
6
|
+
# postalsys.com for the 14-day trial). The trial rate limit is bypassed for the e2e serviceUrl
|
|
7
|
+
# (https://e2e.emailengine.app/) - see the postalsys-web trial allowlist.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
push:
|
|
12
|
+
branches: [master]
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: e2e-${{ github.ref }}
|
|
19
|
+
cancel-in-progress: true
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
e2e:
|
|
23
|
+
name: E2E (Playwright)
|
|
24
|
+
timeout-minutes: 20
|
|
25
|
+
runs-on: ubuntu-24.04
|
|
26
|
+
services:
|
|
27
|
+
redis:
|
|
28
|
+
image: redis
|
|
29
|
+
options: >-
|
|
30
|
+
--health-cmd "redis-cli ping"
|
|
31
|
+
--health-interval 10s
|
|
32
|
+
--health-timeout 5s
|
|
33
|
+
--health-retries 5
|
|
34
|
+
ports:
|
|
35
|
+
- 6379:6379
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v6
|
|
38
|
+
- name: Use Node.js 24
|
|
39
|
+
uses: actions/setup-node@v6
|
|
40
|
+
with:
|
|
41
|
+
node-version: 24
|
|
42
|
+
cache: npm
|
|
43
|
+
- run: npm install
|
|
44
|
+
- name: Install Playwright browser
|
|
45
|
+
run: npx playwright install --with-deps chromium
|
|
46
|
+
- name: Run e2e tests
|
|
47
|
+
run: npm run test:e2e
|
|
48
|
+
env:
|
|
49
|
+
NODE_ENV: e2e
|
|
50
|
+
- name: Upload Playwright report
|
|
51
|
+
uses: actions/upload-artifact@v4
|
|
52
|
+
if: ${{ !cancelled() }}
|
|
53
|
+
with:
|
|
54
|
+
name: playwright-report
|
|
55
|
+
path: playwright-report/
|
|
56
|
+
retention-days: 7
|
|
@@ -12,8 +12,13 @@ concurrency:
|
|
|
12
12
|
cancel-in-progress: true
|
|
13
13
|
|
|
14
14
|
jobs:
|
|
15
|
+
# Skip CI for release-please's auto-generated release PR / branch: it only bumps the
|
|
16
|
+
# version + CHANGELOG (no code change), so re-running the suite is wasted work. This is
|
|
17
|
+
# gated per job rather than with [skip ci] in the release commit, because that text would
|
|
18
|
+
# ride the squash-merge into master and skip the Manage Release run, breaking releases.
|
|
15
19
|
license_check:
|
|
16
20
|
name: License Compliance Check
|
|
21
|
+
if: ${{ !startsWith(github.ref_name, 'release-please--') && !startsWith(github.head_ref, 'release-please--') }}
|
|
17
22
|
runs-on: ubuntu-24.04
|
|
18
23
|
# Service containers to run with `container-job`
|
|
19
24
|
steps:
|
|
@@ -23,20 +28,83 @@ jobs:
|
|
|
23
28
|
uses: actions/setup-node@v6
|
|
24
29
|
with:
|
|
25
30
|
node-version: 24
|
|
31
|
+
cache: npm
|
|
26
32
|
- run: npm install
|
|
27
33
|
|
|
28
34
|
- name: Run License Checks
|
|
29
35
|
run: |
|
|
30
36
|
npm run licenses
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
name:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
lint:
|
|
39
|
+
name: Lint
|
|
40
|
+
if: ${{ !startsWith(github.ref_name, 'release-please--') && !startsWith(github.head_ref, 'release-please--') }}
|
|
41
|
+
runs-on: ubuntu-24.04
|
|
42
|
+
timeout-minutes: 10
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v6
|
|
45
|
+
- name: Use Node.js 24
|
|
46
|
+
uses: actions/setup-node@v6
|
|
47
|
+
with:
|
|
48
|
+
node-version: 24
|
|
49
|
+
cache: npm
|
|
50
|
+
- run: npm install
|
|
51
|
+
- name: Run ESLint
|
|
52
|
+
run: |
|
|
53
|
+
npm run lint
|
|
54
|
+
|
|
55
|
+
unit:
|
|
56
|
+
name: Unit Tests
|
|
57
|
+
if: ${{ !startsWith(github.ref_name, 'release-please--') && !startsWith(github.head_ref, 'release-please--') }}
|
|
58
|
+
timeout-minutes: 10
|
|
59
|
+
runs-on: ubuntu-24.04
|
|
60
|
+
# Service containers to run with `container-job`
|
|
61
|
+
services:
|
|
62
|
+
# Label used to access the service container
|
|
63
|
+
redis:
|
|
64
|
+
# Docker Hub image
|
|
65
|
+
image: redis
|
|
66
|
+
# Set health checks to wait until redis has started
|
|
67
|
+
options: >-
|
|
68
|
+
--health-cmd "redis-cli ping"
|
|
69
|
+
--health-interval 10s
|
|
70
|
+
--health-timeout 5s
|
|
71
|
+
--health-retries 5
|
|
72
|
+
ports:
|
|
73
|
+
- 6379:6379
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v6
|
|
76
|
+
- name: Use Node.js 24
|
|
77
|
+
uses: actions/setup-node@v6
|
|
78
|
+
with:
|
|
79
|
+
node-version: 24
|
|
80
|
+
cache: npm
|
|
81
|
+
- name: Setup Redis CLI
|
|
82
|
+
uses: shogo82148/actions-setup-redis@v1
|
|
83
|
+
with:
|
|
84
|
+
redis-version: '7.x'
|
|
85
|
+
auto-start: 'false'
|
|
86
|
+
- run: npm install
|
|
87
|
+
- name: Run unit tests
|
|
88
|
+
run: |
|
|
89
|
+
npm run test:unit
|
|
90
|
+
env:
|
|
91
|
+
NODE_ENV: test
|
|
92
|
+
# account-revoke-on-delete-test.js runs against live Gmail when
|
|
93
|
+
# these are present and skips itself when they are not
|
|
94
|
+
GMAIL_API_PROJECT_ID: ${{ secrets.TEST_GMAIL_API_PROJECT_ID }}
|
|
95
|
+
GMAIL_API_CLIENT_ID: ${{ secrets.TEST_GMAIL_API_CLIENT_ID }}
|
|
96
|
+
GMAIL_API_CLIENT_SECRET: ${{ secrets.TEST_GMAIL_API_CLIENT_SECRET }}
|
|
97
|
+
GMAIL_API_SERVICE_EMAIL: ${{ secrets.TEST_GMAIL_API_SERVICE_EMAIL }}
|
|
98
|
+
GMAIL_API_SERVICE_CLIENT: ${{ secrets.TEST_GMAIL_API_SERVICE_CLIENT }}
|
|
99
|
+
GMAIL_API_SERVICE_KEY: ${{ secrets.TEST_GMAIL_API_SERVICE_KEY }}
|
|
100
|
+
GMAIL_API_ACCOUNT_EMAIL_1: ${{ secrets.TEST_GMAIL_API_ACCOUNT_EMAIL_1 }}
|
|
101
|
+
GMAIL_API_ACCOUNT_REFRESH_1: ${{ secrets.TEST_GMAIL_API_ACCOUNT_REFRESH_1 }}
|
|
102
|
+
|
|
103
|
+
integration:
|
|
104
|
+
name: Integration Tests
|
|
105
|
+
if: ${{ !startsWith(github.ref_name, 'release-please--') && !startsWith(github.head_ref, 'release-please--') }}
|
|
106
|
+
timeout-minutes: 15 # Increased timeout for Gmail API tests
|
|
107
|
+
runs-on: ubuntu-24.04
|
|
40
108
|
# Service containers to run with `container-job`
|
|
41
109
|
services:
|
|
42
110
|
# Label used to access the service container
|
|
@@ -53,19 +121,20 @@ jobs:
|
|
|
53
121
|
- 6379:6379
|
|
54
122
|
steps:
|
|
55
123
|
- uses: actions/checkout@v6
|
|
56
|
-
- name: Use Node.js
|
|
124
|
+
- name: Use Node.js 24
|
|
57
125
|
uses: actions/setup-node@v6
|
|
58
126
|
with:
|
|
59
|
-
node-version:
|
|
127
|
+
node-version: 24
|
|
128
|
+
cache: npm
|
|
60
129
|
- name: Setup Redis CLI
|
|
61
130
|
uses: shogo82148/actions-setup-redis@v1
|
|
62
131
|
with:
|
|
63
132
|
redis-version: '7.x'
|
|
64
133
|
auto-start: 'false'
|
|
65
134
|
- run: npm install
|
|
66
|
-
- name: Run tests
|
|
135
|
+
- name: Run integration tests
|
|
67
136
|
run: |
|
|
68
|
-
npm test
|
|
137
|
+
npm run test:integration
|
|
69
138
|
env:
|
|
70
139
|
NODE_ENV: test
|
|
71
140
|
GMAIL_API_PROJECT_ID: ${{ secrets.TEST_GMAIL_API_PROJECT_ID }}
|
package/.ncurc.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
upgrade: true,
|
|
3
|
+
// Packages capped inside their current major: the next major is ESM-only (this codebase is
|
|
4
|
+
// CommonJS and is bundled into a binary with pkg) or it breaks a peer dependency. Using 'minor'
|
|
5
|
+
// instead of a blanket reject so these still receive security/patch updates within the safe
|
|
6
|
+
// major instead of being frozen at one exact version. Verified against Node 20 (Docker) 2026-06-17.
|
|
7
|
+
target: name => (['joi', 'nanoid', 'ical.js', 'gettext-parser', 'xgettext-template', 'chai', 'undici', 'marked'].includes(name) ? 'minor' : 'latest'),
|
|
8
|
+
// joi - hapi-swagger (repo archived 2026-02-04) peer-requires joi 17.x; permanent ceiling
|
|
9
|
+
// nanoid - 4.x dropped the CommonJS require export (ESM-only)
|
|
10
|
+
// ical.js - 2.x is ESM-only
|
|
11
|
+
// gettext-parser - 8.x is ESM-only
|
|
12
|
+
// xgettext-template - 6.x is ESM-only (translation build tool)
|
|
13
|
+
// chai - 5.x is ESM-only (only used by the vendored imap-core tests)
|
|
14
|
+
// undici - 8.x requires Node >=22.19 and crashes at require() on Node 20; EmailEngine supports Node 20+
|
|
15
|
+
// marked - 16.x dropped the CommonJS build (ESM-only, needs require(esm)/Node >=20.19); 15.x is the
|
|
16
|
+
// last require()-compatible line. 15.0.12 verified on Node 20-24 and in a yao/pkg node24 build.
|
|
3
17
|
reject: [
|
|
4
|
-
//
|
|
5
|
-
|
|
6
|
-
'gettext-parser',
|
|
7
|
-
'xgettext-template',
|
|
8
|
-
'chai',
|
|
9
|
-
'js-beautify',
|
|
10
|
-
'ical.js',
|
|
18
|
+
// 8.16+ pulls apache-arrow (ESM) into the pkg bundle; 9.x drops it but is a major bump for
|
|
19
|
+
// the deprecated, default-off Document Store. Even the 8.19 minor is unsafe to bundle.
|
|
11
20
|
'@elastic/elasticsearch',
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// no support for Node 16
|
|
16
|
-
'marked',
|
|
17
|
-
|
|
18
|
-
// some kind of CVE in later versions. Only needed for license reference, so the actual version does not matter anyway
|
|
22
|
+
// v4.x adds vulnerable jquery + bootstrap runtime dependencies; v3.3.7 has none. Used only
|
|
23
|
+
// for the generated software-license listing, never executed at runtime.
|
|
19
24
|
'startbootstrap-sb-admin-2',
|
|
20
25
|
|
|
21
|
-
// Keep joi at version 17.x for hapi-swagger compatibility
|
|
22
|
-
'joi',
|
|
23
|
-
|
|
24
26
|
// @asamuzakjp/css-color >=4.1.2 pulls in @csstools/* v4 which are pure ESM and break pkg bundling
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// undici >=8.0.0 requires Node >=22.19.0; pin to last Node 20-compatible release
|
|
28
|
-
'undici'
|
|
27
|
+
// (transitive via @postalsys/email-text-tools -> jsdom -> cssstyle; also pinned in package.json "overrides").
|
|
28
|
+
'@asamuzakjp/css-color'
|
|
29
29
|
]
|
|
30
30
|
};
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.72.0](https://github.com/postalsys/emailengine/compare/v2.71.0...v2.72.0) (2026-06-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* adopt pre-existing Gmail Pub/Sub resources with ownership tracking ([fe43fad](https://github.com/postalsys/emailengine/commit/fe43fadad624025a56139382428256eb810f9e08))
|
|
9
|
+
|
|
10
|
+
## [2.71.0](https://github.com/postalsys/emailengine/compare/v2.70.0...v2.71.0) (2026-06-15)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* add anonymized feature beacon to license validation ([954d92a](https://github.com/postalsys/emailengine/commit/954d92a9d8e7c5fff6f1b8c711dfd228f556387a))
|
|
16
|
+
* disable deprecated Document Store by default behind a feature gate ([8790f75](https://github.com/postalsys/emailengine/commit/8790f7538ba2ee8d6ec73d1f3bbc221c2f54f0fe))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* defer export worker job consumption until startup recovery completes ([a21b0a5](https://github.com/postalsys/emailengine/commit/a21b0a50fdb98d13a2227a020069129d23fe225b))
|
|
22
|
+
* fail export when a folder cannot be indexed ([36c9c9d](https://github.com/postalsys/emailengine/commit/36c9c9d536540cd5a61b16b69af5e8e84c194ee6))
|
|
23
|
+
* filter transient fetch failures from Sentry and retry DNS errors ([883b9b4](https://github.com/postalsys/emailengine/commit/883b9b487e3580c5b1240d28cd519d190c402b47))
|
|
24
|
+
* retry transient errors in API-account batch export path ([6cba7c7](https://github.com/postalsys/emailengine/commit/6cba7c72658a037f68867cf2464f7d3420a79507))
|
|
25
|
+
* translate OAuth scope error page across 6 languages ([4ae1919](https://github.com/postalsys/emailengine/commit/4ae19199fe628f8a4f7177d53db1f05167077e3e))
|
|
26
|
+
* upgrade joi to 17.13.4 and @postalsys/certs to 1.0.15 (GHSA-q7cg-457f-vx79) ([6ec77e5](https://github.com/postalsys/emailengine/commit/6ec77e50b288b14550cd09b724f5aab59d17ced4))
|
|
27
|
+
|
|
3
28
|
## [2.70.0](https://github.com/postalsys/emailengine/compare/v2.69.0...v2.70.0) (2026-06-11)
|
|
4
29
|
|
|
5
30
|
|
package/Gruntfile.js
CHANGED
|
@@ -5,22 +5,11 @@ const config = require('@zone-eu/wild-config');
|
|
|
5
5
|
module.exports = function (grunt) {
|
|
6
6
|
// Project configuration.
|
|
7
7
|
grunt.initConfig({
|
|
8
|
-
wait: {
|
|
9
|
-
server: {
|
|
10
|
-
options: {
|
|
11
|
-
delay: 20 * 1000 // Increased from 12s to 20s for Gmail API operations
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
|
|
16
8
|
shell: {
|
|
17
9
|
eslint: {
|
|
18
10
|
// Globs are quoted so eslint expands them itself - unquoted, sh (no globstar)
|
|
19
11
|
// expands lib/**/*.js to depth-2 files only and skips most of lib/
|
|
20
|
-
command: "npx eslint 'lib/**/*.js' 'workers/**/*.js' server.js Gruntfile.js"
|
|
21
|
-
options: {
|
|
22
|
-
async: false
|
|
23
|
-
}
|
|
12
|
+
command: "npx eslint 'lib/**/*.js' 'workers/**/*.js' server.js Gruntfile.js"
|
|
24
13
|
},
|
|
25
14
|
server: {
|
|
26
15
|
command: 'node server.js',
|
|
@@ -29,16 +18,22 @@ module.exports = function (grunt) {
|
|
|
29
18
|
}
|
|
30
19
|
},
|
|
31
20
|
flush: {
|
|
32
|
-
command: `redis-cli -u "${config.dbs.redis}" flushdb
|
|
33
|
-
options: {
|
|
34
|
-
async: false
|
|
35
|
-
}
|
|
21
|
+
command: `redis-cli -u "${config.dbs.redis}" flushdb`
|
|
36
22
|
},
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
23
|
+
waitServer: {
|
|
24
|
+
// Polls /health until the server reports ready (all IMAP workers up,
|
|
25
|
+
// Redis responding) instead of sleeping for a fixed delay
|
|
26
|
+
command: 'node test/helpers/wait-for-server.js'
|
|
27
|
+
},
|
|
28
|
+
testUnit: {
|
|
29
|
+
// Self-contained tests - need Redis but not the live server.
|
|
30
|
+
// Run with default --test concurrency; the suite is verified to pass in parallel.
|
|
31
|
+
// The *-test.js pattern keeps helper modules out of the test runner
|
|
32
|
+
command: 'node --test --test-timeout=180000 test/*-test.js'
|
|
33
|
+
},
|
|
34
|
+
testIntegration: {
|
|
35
|
+
// Tests that run against the live server started by shell:server
|
|
36
|
+
command: 'node --test --test-concurrency=1 --test-timeout=180000 test/integration/*-test.js'
|
|
42
37
|
},
|
|
43
38
|
options: {
|
|
44
39
|
stdout: data => console.log(data.toString().trim()),
|
|
@@ -50,10 +45,11 @@ module.exports = function (grunt) {
|
|
|
50
45
|
|
|
51
46
|
// Load the plugin(s)
|
|
52
47
|
grunt.loadNpmTasks('grunt-shell-spawn');
|
|
53
|
-
grunt.loadNpmTasks('grunt-wait');
|
|
54
48
|
|
|
55
49
|
// Tasks
|
|
56
|
-
grunt.registerTask('test', ['shell:flush', 'shell:
|
|
50
|
+
grunt.registerTask('test-unit', ['shell:flush', 'shell:testUnit']);
|
|
51
|
+
grunt.registerTask('test-integration', ['shell:flush', 'shell:server', 'shell:waitServer', 'shell:testIntegration', 'shell:server:kill']);
|
|
52
|
+
grunt.registerTask('test', ['test-unit', 'test-integration']);
|
|
57
53
|
|
|
58
54
|
grunt.registerTask('default', ['shell:eslint', 'test']);
|
|
59
55
|
};
|
package/bin/emailengine.js
CHANGED
|
@@ -105,7 +105,14 @@ const GLOBAL_OPTIONS = [
|
|
|
105
105
|
{ name: '--smtp.host', description: 'SMTP server bind address', type: 'string', default: '127.0.0.1', group: 'SMTP server' },
|
|
106
106
|
{ name: '--smtp.port', description: 'SMTP server port', type: 'number', default: 2525, group: 'SMTP server' },
|
|
107
107
|
{ name: '--smtp.proxy', description: 'Enable HAProxy PROXY protocol', type: 'boolean', default: false, group: 'SMTP server' },
|
|
108
|
-
{ name: '--smtp.maxMessageSize', description: 'Maximum email size', type: 'number/string', default: '25M', group: 'SMTP server' }
|
|
108
|
+
{ name: '--smtp.maxMessageSize', description: 'Maximum email size', type: 'number/string', default: '25M', group: 'SMTP server' },
|
|
109
|
+
{
|
|
110
|
+
name: '--documentStore.enabled',
|
|
111
|
+
description: 'Enable the deprecated Document Store (ElasticSearch) feature',
|
|
112
|
+
type: 'boolean',
|
|
113
|
+
default: false,
|
|
114
|
+
group: 'Document Store (deprecated)'
|
|
115
|
+
}
|
|
109
116
|
];
|
|
110
117
|
|
|
111
118
|
// Help formatting functions
|
package/config/default.toml
CHANGED
|
@@ -31,6 +31,11 @@ secret = "" # client password, if not set allows any password
|
|
|
31
31
|
proxy = false # Set to true if using HAProxy with send-proxy option
|
|
32
32
|
#maxMessageSize = "25M" # maximum message size accepted by SMTP server (default: 25MB)
|
|
33
33
|
|
|
34
|
+
[documentStore]
|
|
35
|
+
# Deprecated Document Store (ElasticSearch) feature. Disabled by default; the worker and
|
|
36
|
+
# all document-store API/UI endpoints are only available when this is enabled.
|
|
37
|
+
enabled = false
|
|
38
|
+
|
|
34
39
|
[dbs]
|
|
35
40
|
# redis connection
|
|
36
41
|
redis = "redis://127.0.0.1:6379/8"
|
package/config/e2e.toml
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# End-to-end overrides, merged over default.toml by @zone-eu/wild-config when NODE_ENV=e2e.
|
|
2
|
+
# Boots a fresh EmailEngine for the Playwright happy-path suite (test/e2e): enable auth,
|
|
3
|
+
# activate a 14-day trial (hits postalsys.com), register an Ethereal account, send + read back.
|
|
4
|
+
#
|
|
5
|
+
# Uses an isolated Redis DB so the suite can flush it for a clean fresh-instance run without
|
|
6
|
+
# touching dev (db 9), test (db 13), or default (db 8) data. No preparedToken / preparedPassword /
|
|
7
|
+
# preparedLicense here on purpose - the suite enables auth and activates the trial through the UI.
|
|
8
|
+
|
|
9
|
+
# JSON formatted settings, seeded on startup.
|
|
10
|
+
# serviceUrl is the recognizable e2e URL that postalsys-web allowlists to skip the trial rate
|
|
11
|
+
# limit. It is only used as the trial request's `url`; the browser and API talk to 127.0.0.1:7099.
|
|
12
|
+
settings = '''
|
|
13
|
+
{
|
|
14
|
+
"serviceUrl": "https://e2e.emailengine.app/",
|
|
15
|
+
"ignoreMailCertErrors": true
|
|
16
|
+
}
|
|
17
|
+
'''
|
|
18
|
+
|
|
19
|
+
[service]
|
|
20
|
+
# Fixed encryption secret for the throwaway e2e instance (Redis is flushed on every run).
|
|
21
|
+
secret = "e2e encryption secret"
|
|
22
|
+
|
|
23
|
+
[workers]
|
|
24
|
+
imap = 1
|
|
25
|
+
|
|
26
|
+
[log]
|
|
27
|
+
level = "warn"
|
|
28
|
+
|
|
29
|
+
[dbs]
|
|
30
|
+
# Isolated Redis DB for e2e; flushed before each run by test/e2e/flush-redis.js
|
|
31
|
+
redis = "redis://127.0.0.1:6379/14"
|
|
32
|
+
|
|
33
|
+
[api]
|
|
34
|
+
host = "127.0.0.1"
|
|
35
|
+
port = 7099
|
package/config/test.toml
CHANGED
package/getswagger.sh
CHANGED
|
@@ -4,6 +4,10 @@ set -e
|
|
|
4
4
|
|
|
5
5
|
export EENGINE_PORT=5678
|
|
6
6
|
|
|
7
|
+
# Keep the deprecated Document Store endpoints in the generated spec; they are only
|
|
8
|
+
# registered when the feature is enabled.
|
|
9
|
+
export EENGINE_DOCUMENT_STORE_ENABLED=true
|
|
10
|
+
|
|
7
11
|
# refuse to run if something is already listening on the port, otherwise the
|
|
8
12
|
# polling loop below would silently fetch swagger.json from a stale instance
|
|
9
13
|
if (exec 3<>"/dev/tcp/127.0.0.1/${EENGINE_PORT}") 2>/dev/null; then
|
package/lib/account.js
CHANGED
|
@@ -20,6 +20,7 @@ const { deepStrictEqual, strictEqual } = require('assert');
|
|
|
20
20
|
const { encrypt, decrypt } = require('./encrypt');
|
|
21
21
|
const { oauth2Apps, LEGACY_KEYS, isApiBasedApp } = require('./oauth2-apps');
|
|
22
22
|
const settings = require('./settings');
|
|
23
|
+
const { isDocumentStoreEnabled, documentStoreFeatureEnabled } = require('./document-store');
|
|
23
24
|
const redisScanDelete = require('./redis-scan-delete');
|
|
24
25
|
const { customAlphabet } = require('nanoid');
|
|
25
26
|
const Lock = require('ioredfour');
|
|
@@ -1048,28 +1049,33 @@ class Account {
|
|
|
1048
1049
|
};
|
|
1049
1050
|
}
|
|
1050
1051
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1052
|
+
// Only notify the documents queue when the deprecated Document Store feature is enabled.
|
|
1053
|
+
// When it is off the documents worker is not running, so an enqueued job would never be
|
|
1054
|
+
// consumed and would pile up in Redis.
|
|
1055
|
+
if (documentStoreFeatureEnabled) {
|
|
1056
|
+
try {
|
|
1057
|
+
let queueKeep = (await settings.get('queueKeep')) || true;
|
|
1058
|
+
let serviceUrl = (await settings.get('serviceUrl')) || null;
|
|
1054
1059
|
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1060
|
+
let payload = {
|
|
1061
|
+
serviceUrl,
|
|
1062
|
+
account: this.account,
|
|
1063
|
+
date: new Date().toISOString(),
|
|
1064
|
+
event: ACCOUNT_DELETED_NOTIFY
|
|
1065
|
+
};
|
|
1061
1066
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1067
|
+
await this.documentsQueue.add(ACCOUNT_DELETED_NOTIFY, payload, {
|
|
1068
|
+
removeOnComplete: queueKeep,
|
|
1069
|
+
removeOnFail: queueKeep,
|
|
1070
|
+
attempts: 10,
|
|
1071
|
+
backoff: {
|
|
1072
|
+
type: 'exponential',
|
|
1073
|
+
delay: 5000
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
} catch (err) {
|
|
1077
|
+
this.logger.error({ msg: 'Failed to add entry to documents queue', err });
|
|
1078
|
+
}
|
|
1073
1079
|
}
|
|
1074
1080
|
|
|
1075
1081
|
await this.call({
|
|
@@ -1458,7 +1464,7 @@ class Account {
|
|
|
1458
1464
|
}
|
|
1459
1465
|
|
|
1460
1466
|
async getText(text, options) {
|
|
1461
|
-
if (options.documentStore && (await
|
|
1467
|
+
if (options.documentStore && (await isDocumentStoreEnabled())) {
|
|
1462
1468
|
await this.loadAccountData(this.account, false);
|
|
1463
1469
|
|
|
1464
1470
|
const { index, client } = this.esClient;
|
|
@@ -1516,7 +1522,7 @@ class Account {
|
|
|
1516
1522
|
options.preProcessHtml = true;
|
|
1517
1523
|
}
|
|
1518
1524
|
|
|
1519
|
-
if (options.documentStore && (await
|
|
1525
|
+
if (options.documentStore && (await isDocumentStoreEnabled())) {
|
|
1520
1526
|
await this.loadAccountData(this.account, false);
|
|
1521
1527
|
|
|
1522
1528
|
const { index, client } = this.esClient;
|
|
@@ -1700,7 +1706,7 @@ class Account {
|
|
|
1700
1706
|
}
|
|
1701
1707
|
|
|
1702
1708
|
async listMessages(query) {
|
|
1703
|
-
if (query.documentStore && (await
|
|
1709
|
+
if (query.documentStore && (await isDocumentStoreEnabled())) {
|
|
1704
1710
|
await this.loadAccountData(this.account, false);
|
|
1705
1711
|
|
|
1706
1712
|
const { index, client } = this.esClient;
|
|
@@ -1836,7 +1842,7 @@ class Account {
|
|
|
1836
1842
|
|
|
1837
1843
|
async searchMessages(query, searchOpts) {
|
|
1838
1844
|
searchOpts = searchOpts || {};
|
|
1839
|
-
if (query.documentStore && (await
|
|
1845
|
+
if (query.documentStore && (await isDocumentStoreEnabled())) {
|
|
1840
1846
|
if (!searchOpts.unified) {
|
|
1841
1847
|
await this.loadAccountData(this.account, false);
|
|
1842
1848
|
}
|
|
@@ -2401,7 +2407,7 @@ class Account {
|
|
|
2401
2407
|
// scan and delete keys
|
|
2402
2408
|
await redisScanDelete(this.redis, this.logger, `${REDIS_PREFIX}iam:${this.account}:*`);
|
|
2403
2409
|
|
|
2404
|
-
if (await
|
|
2410
|
+
if (await isDocumentStoreEnabled()) {
|
|
2405
2411
|
// Flush ElasticSearch index for this account
|
|
2406
2412
|
const { index, client } = this.esClient;
|
|
2407
2413
|
if (!client) {
|