update
This commit is contained in:
12
package.json
12
package.json
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "@serve.zone/platformservice",
|
||||
"name": "@serve.zone/dcrouter",
|
||||
"private": false,
|
||||
"version": "2.12.0",
|
||||
"description": "A multifaceted platform service handling mail, SMS, letter delivery, and AI services.",
|
||||
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
"type": "module",
|
||||
@ -16,11 +16,11 @@
|
||||
"localPublish": ""
|
||||
},
|
||||
"devDependencies": {
|
||||
"@git.zone/tsbuild": "^2.5.1",
|
||||
"@git.zone/tsbuild": "^2.5.2",
|
||||
"@git.zone/tsrun": "^1.2.8",
|
||||
"@git.zone/tstest": "^1.9.0",
|
||||
"@git.zone/tswatch": "^2.0.1",
|
||||
"@types/node": "^22.15.20"
|
||||
"@types/node": "^22.15.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"@api.global/typedrequest": "^3.0.19",
|
||||
@ -33,7 +33,7 @@
|
||||
"@push.rocks/smartdata": "^5.15.1",
|
||||
"@push.rocks/smartdns": "^6.2.2",
|
||||
"@push.rocks/smartfile": "^11.0.4",
|
||||
"@push.rocks/smartlog": "^3.1.7",
|
||||
"@push.rocks/smartlog": "^3.1.8",
|
||||
"@push.rocks/smartmail": "^2.1.0",
|
||||
"@push.rocks/smartpath": "^5.0.5",
|
||||
"@push.rocks/smartpromise": "^4.0.3",
|
||||
@ -59,7 +59,7 @@
|
||||
"SMTP server",
|
||||
"mail parsing",
|
||||
"DKIM",
|
||||
"platform service",
|
||||
"mail router",
|
||||
"letterXpress",
|
||||
"OpenAI",
|
||||
"Anthropic AI",
|
||||
|
120
pnpm-lock.yaml
generated
120
pnpm-lock.yaml
generated
@ -39,8 +39,8 @@ importers:
|
||||
specifier: ^11.0.4
|
||||
version: 11.2.0
|
||||
'@push.rocks/smartlog':
|
||||
specifier: ^3.1.7
|
||||
version: 3.1.7
|
||||
specifier: ^3.1.8
|
||||
version: 3.1.8
|
||||
'@push.rocks/smartmail':
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0
|
||||
@ -91,8 +91,8 @@ importers:
|
||||
version: 11.1.0
|
||||
devDependencies:
|
||||
'@git.zone/tsbuild':
|
||||
specifier: ^2.5.1
|
||||
version: 2.5.1
|
||||
specifier: ^2.5.2
|
||||
version: 2.5.2
|
||||
'@git.zone/tsrun':
|
||||
specifier: ^1.2.8
|
||||
version: 1.3.3
|
||||
@ -103,8 +103,8 @@ importers:
|
||||
specifier: ^2.0.1
|
||||
version: 2.1.0
|
||||
'@types/node':
|
||||
specifier: ^22.15.20
|
||||
version: 22.15.20
|
||||
specifier: ^22.15.21
|
||||
version: 22.15.21
|
||||
|
||||
packages:
|
||||
|
||||
@ -623,8 +623,8 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@git.zone/tsbuild@2.5.1':
|
||||
resolution: {integrity: sha512-b1TyaNnaPCD3dvdRZ2da0MkZbH9liCrhzg57pwFIB2Gx4g8UMv8ZLN2cA1NRaNE0o8NCybf3gV1L+V0FO0DrMQ==}
|
||||
'@git.zone/tsbuild@2.5.2':
|
||||
resolution: {integrity: sha512-GoZ2vNgMe6OeGcejwhx7Sem8YCbwybEuU4r2/wWnrNrozw+HuT5UTROVGW7rTAxcxr2Hi4jWHSsuoCz9/6ZzrA==}
|
||||
hasBin: true
|
||||
|
||||
'@git.zone/tsbundle@2.2.5':
|
||||
@ -869,8 +869,8 @@ packages:
|
||||
'@push.rocks/smartlog-interfaces@3.0.2':
|
||||
resolution: {integrity: sha512-8hGRTJehbsFSJxLhCQkA018mZtXVPxPTblbg9VaE/EqISRzUw+eosJ2EJV7M4Qu0eiTJZjnWnNLn8CkD77ziWw==}
|
||||
|
||||
'@push.rocks/smartlog@3.1.7':
|
||||
resolution: {integrity: sha512-ltg4oALFBrAflWMTOhN7lcK6OJ50MvrDx46wyXVGZZ+zRzYUUjBqyBzSXLQrOohOt/r6vQH+KYPgt6D0tYJA9A==}
|
||||
'@push.rocks/smartlog@3.1.8':
|
||||
resolution: {integrity: sha512-j4H5x4/hEmiIO7q+/LKyX3N+AhRIOj1jDE4TvZDvujZkbT/9wEWfpO1bqeMe/EQbg1eOQMlAuyrcLXUcDICpQg==}
|
||||
|
||||
'@push.rocks/smartmail@2.1.0':
|
||||
resolution: {integrity: sha512-PUzxHuDQRdUZGKb5CejjkFr4JyRAZjUmEyX0jL/kTr9BEX+Ba+/t0cAOFC4v/xFbIxvFAlF3VvFtULCbgAvwaA==}
|
||||
@ -1494,11 +1494,11 @@ packages:
|
||||
'@types/node-forge@1.3.11':
|
||||
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
|
||||
|
||||
'@types/node@18.19.102':
|
||||
resolution: {integrity: sha512-8+SHxopyHeI9xdylfts948LTTr7ZOCSQWMEEDS1qmFIv1kdl03YoMcy3H2NhmxeozCxJiTw6al1h5PAp9h0a5w==}
|
||||
'@types/node@18.19.103':
|
||||
resolution: {integrity: sha512-hHTHp+sEz6SxFsp+SA+Tqrua3AbmlAw+Y//aEwdHrdZkYVRWdvWD3y5uPZ0flYOkgskaFWqZ/YGFm3FaFQ0pRw==}
|
||||
|
||||
'@types/node@22.15.20':
|
||||
resolution: {integrity: sha512-A6BohGFRGHAscJsTslDCA9JG7qSJr/DWUvrvY8yi9IgnGtMxCyat7vvQ//MFa0DnLsyuS3wYTpLdw4Hf+Q5JXw==}
|
||||
'@types/node@22.15.21':
|
||||
resolution: {integrity: sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==}
|
||||
|
||||
'@types/ping@0.4.4':
|
||||
resolution: {integrity: sha512-ifvo6w2f5eJYlXm+HiVx67iJe8WZp87sfa683nlqED5Vnt9Z93onkokNoWqOG21EaE8fMxyKPobE+mkPEyxsdw==}
|
||||
@ -4178,7 +4178,7 @@ snapshots:
|
||||
'@push.rocks/smartfeed': 1.0.11
|
||||
'@push.rocks/smartfile': 11.2.0
|
||||
'@push.rocks/smartjson': 5.0.20
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartlog-destination-devtools': 1.0.12
|
||||
'@push.rocks/smartlog-interfaces': 3.0.2
|
||||
'@push.rocks/smartmanifest': 2.0.2
|
||||
@ -4232,7 +4232,7 @@ snapshots:
|
||||
'@apiclient.xyz/cloudflare@6.4.1':
|
||||
dependencies:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrequest': 2.1.0
|
||||
'@push.rocks/smartstring': 4.0.15
|
||||
@ -5001,14 +5001,14 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@git.zone/tsbuild@2.5.1':
|
||||
'@git.zone/tsbuild@2.5.2':
|
||||
dependencies:
|
||||
'@git.zone/tspublish': 1.9.1
|
||||
'@push.rocks/early': 4.0.4
|
||||
'@push.rocks/smartcli': 4.0.11
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartfile': 11.2.0
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartpath': 5.0.18
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
typescript: 5.7.3
|
||||
@ -5021,7 +5021,7 @@ snapshots:
|
||||
'@push.rocks/smartcli': 4.0.11
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartfile': 11.2.0
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartlog-destination-local': 9.0.2
|
||||
'@push.rocks/smartpath': 5.0.18
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
@ -5038,7 +5038,7 @@ snapshots:
|
||||
'@push.rocks/smartcli': 4.0.11
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartfile': 11.2.0
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartnpm': 2.0.4
|
||||
'@push.rocks/smartpath': 5.0.18
|
||||
'@push.rocks/smartrequest': 2.1.0
|
||||
@ -5066,7 +5066,7 @@ snapshots:
|
||||
'@push.rocks/smartexpect': 2.4.2
|
||||
'@push.rocks/smartfile': 11.2.0
|
||||
'@push.rocks/smartjson': 5.0.20
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartmongo': 2.0.12(@aws-sdk/credential-providers@3.812.0)(socks@2.8.4)
|
||||
'@push.rocks/smartpath': 5.0.18
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
@ -5106,7 +5106,7 @@ snapshots:
|
||||
'@push.rocks/smartcli': 4.0.11
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartfile': 11.2.0
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartlog-destination-local': 9.0.2
|
||||
'@push.rocks/smartshell': 3.2.3
|
||||
'@push.rocks/taskbuffer': 3.1.7
|
||||
@ -5384,7 +5384,7 @@ snapshots:
|
||||
'@api.global/typedrequest': 3.1.10
|
||||
'@configvault.io/interfaces': 1.0.17
|
||||
'@push.rocks/smartfile': 11.2.0
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartpath': 5.0.18
|
||||
|
||||
'@push.rocks/smartacme@8.0.0(@aws-sdk/credential-providers@3.812.0)(socks@2.8.4)':
|
||||
@ -5396,7 +5396,7 @@ snapshots:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartdns': 6.2.2
|
||||
'@push.rocks/smartfile': 11.2.0
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartnetwork': 4.0.2
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrequest': 2.1.0
|
||||
@ -5410,7 +5410,6 @@ snapshots:
|
||||
- '@mongodb-js/zstd'
|
||||
- '@nuxt/kit'
|
||||
- aws-crt
|
||||
- bufferutil
|
||||
- encoding
|
||||
- gcp-metadata
|
||||
- kerberos
|
||||
@ -5419,7 +5418,6 @@ snapshots:
|
||||
- snappy
|
||||
- socks
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
- vue
|
||||
|
||||
'@push.rocks/smartarchive@3.0.8':
|
||||
@ -5486,7 +5484,7 @@ snapshots:
|
||||
'@push.rocks/smartcli@4.0.11':
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartobject': 1.0.12
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
@ -5511,7 +5509,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartmongo': 2.0.12(@aws-sdk/credential-providers@3.812.0)(socks@2.8.4)
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
@ -5649,7 +5647,7 @@ snapshots:
|
||||
'@api.global/typedrequest-interfaces': 2.0.2
|
||||
'@tsclass/tsclass': 4.4.4
|
||||
|
||||
'@push.rocks/smartlog@3.1.7':
|
||||
'@push.rocks/smartlog@3.1.8':
|
||||
dependencies:
|
||||
'@api.global/typedrequest-interfaces': 3.0.19
|
||||
'@push.rocks/consolecolor': 2.0.2
|
||||
@ -5820,7 +5818,7 @@ snapshots:
|
||||
'@push.rocks/smartcrypto': 2.0.4
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartfile': 11.2.0
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartnetwork': 4.0.2
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrequest': 2.1.0
|
||||
@ -5922,7 +5920,7 @@ snapshots:
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartenv': 5.0.12
|
||||
'@push.rocks/smartjson': 5.0.20
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
'@push.rocks/smarttime': 4.1.1
|
||||
@ -6020,7 +6018,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartdelay': 3.0.5
|
||||
'@push.rocks/smartlog': 3.1.7
|
||||
'@push.rocks/smartlog': 3.1.8
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrx': 3.0.10
|
||||
'@push.rocks/smarttime': 4.1.1
|
||||
@ -6616,27 +6614,27 @@ snapshots:
|
||||
|
||||
'@types/bn.js@5.1.6':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/body-parser@1.19.5':
|
||||
dependencies:
|
||||
'@types/connect': 3.4.38
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/buffer-json@2.0.3': {}
|
||||
|
||||
'@types/clean-css@4.2.11':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
source-map: 0.6.1
|
||||
|
||||
'@types/connect@3.4.38':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/cors@2.8.18':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
dependencies:
|
||||
@ -6648,7 +6646,7 @@ snapshots:
|
||||
|
||||
'@types/dns-packet@5.6.5':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/elliptic@6.4.18':
|
||||
dependencies:
|
||||
@ -6656,7 +6654,7 @@ snapshots:
|
||||
|
||||
'@types/express-serve-static-core@5.0.6':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
'@types/qs': 6.14.0
|
||||
'@types/range-parser': 1.2.7
|
||||
'@types/send': 0.17.4
|
||||
@ -6673,30 +6671,30 @@ snapshots:
|
||||
|
||||
'@types/from2@2.3.5':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/fs-extra@11.0.4':
|
||||
dependencies:
|
||||
'@types/jsonfile': 6.1.4
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/fs-extra@9.0.13':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/glob@7.2.0':
|
||||
dependencies:
|
||||
'@types/minimatch': 5.1.2
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/glob@8.1.0':
|
||||
dependencies:
|
||||
'@types/minimatch': 5.1.2
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/gunzip-maybe@1.4.2':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/hast@3.0.4':
|
||||
dependencies:
|
||||
@ -6718,11 +6716,11 @@ snapshots:
|
||||
|
||||
'@types/jsonfile@6.1.4':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/mailparser@3.4.6':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
iconv-lite: 0.6.3
|
||||
|
||||
'@types/mdast@4.0.4':
|
||||
@ -6741,18 +6739,18 @@ snapshots:
|
||||
|
||||
'@types/node-fetch@2.6.12':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
form-data: 4.0.2
|
||||
|
||||
'@types/node-forge@1.3.11':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/node@18.19.102':
|
||||
'@types/node@18.19.103':
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
|
||||
'@types/node@22.15.20':
|
||||
'@types/node@22.15.21':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
@ -6768,30 +6766,30 @@ snapshots:
|
||||
|
||||
'@types/s3rver@3.7.4':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/semver@7.7.0': {}
|
||||
|
||||
'@types/send@0.17.4':
|
||||
dependencies:
|
||||
'@types/mime': 1.3.5
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/serve-static@1.15.7':
|
||||
dependencies:
|
||||
'@types/http-errors': 2.0.4
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
'@types/send': 0.17.4
|
||||
|
||||
'@types/symbol-tree@3.2.5': {}
|
||||
|
||||
'@types/tar-stream@2.2.3':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/through2@2.0.41':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/triple-beam@1.3.5': {}
|
||||
|
||||
@ -6815,18 +6813,18 @@ snapshots:
|
||||
|
||||
'@types/whatwg-url@8.2.2':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
'@types/webidl-conversions': 7.0.3
|
||||
|
||||
'@types/which@3.0.4': {}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
|
||||
'@types/yauzl@2.10.3':
|
||||
dependencies:
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
optional: true
|
||||
|
||||
'@ungap/structured-clone@1.3.0': {}
|
||||
@ -7106,7 +7104,7 @@ snapshots:
|
||||
|
||||
cloudflare@4.2.0:
|
||||
dependencies:
|
||||
'@types/node': 18.19.102
|
||||
'@types/node': 18.19.103
|
||||
'@types/node-fetch': 2.6.12
|
||||
abort-controller: 3.0.0
|
||||
agentkeepalive: 4.6.0
|
||||
@ -7377,7 +7375,7 @@ snapshots:
|
||||
engine.io@6.6.4:
|
||||
dependencies:
|
||||
'@types/cors': 2.8.18
|
||||
'@types/node': 22.15.20
|
||||
'@types/node': 22.15.21
|
||||
accepts: 1.3.8
|
||||
base64id: 2.0.0
|
||||
cookie: 0.7.2
|
||||
|
@ -0,0 +1,76 @@
|
||||
# DCRouter Project Improvement Plan
|
||||
|
||||
## Type Safety Enhancement Plan
|
||||
|
||||
Our goal is to improve type safety across the DCRouter codebase to reduce runtime errors, improve developer experience, and make the code more maintainable. This document outlines the specific changes we'll implement.
|
||||
|
||||
### 1. Platform Service Interface Improvements
|
||||
|
||||
- [ ] Create a comprehensive `IPlatformService` interface to replace `any` type usage
|
||||
- [ ] Define specific methods and properties that platform services should implement
|
||||
- [ ] Update all references to use the new interface
|
||||
|
||||
### 2. SMTP Session Type Safety
|
||||
|
||||
- [ ] Define a proper `ISmtpSession` interface with all required properties
|
||||
- [ ] Ensure consistent usage across SMTPServer implementation
|
||||
- [ ] Add proper validation for session properties
|
||||
|
||||
### 3. Configuration Type Enhancements
|
||||
|
||||
- [ ] Replace loose config objects with strictly typed interfaces
|
||||
- [ ] Add validation functions for all configuration objects
|
||||
- [ ] Create union types for configuration options with specific values
|
||||
|
||||
### 4. Function Return Types
|
||||
|
||||
- [ ] Audit all async functions to ensure they have explicit return types
|
||||
- [ ] Replace `Promise<any>` with specific return type interfaces
|
||||
- [ ] Add proper error types for rejected promises
|
||||
|
||||
### 5. String Literal Types and Enums
|
||||
|
||||
- [ ] Replace string constants with proper TypeScript enums or string literal unions
|
||||
- [ ] Create specific types for email status values, priorities, etc.
|
||||
- [ ] Ensure consistent usage throughout the codebase
|
||||
|
||||
### 6. Event System Types
|
||||
|
||||
- [ ] Create typed event emitters for all event-based components
|
||||
- [ ] Define specific event payload interfaces for each event type
|
||||
- [ ] Ensure type safety for event handlers
|
||||
|
||||
### 7. Authentication Data Types
|
||||
|
||||
- [ ] Create proper interfaces for authentication data
|
||||
- [ ] Replace generic Record types with specific property interfaces
|
||||
- [ ] Add validation for auth data objects
|
||||
|
||||
### 8. Email Attachment Type Safety
|
||||
|
||||
- [ ] Define comprehensive interfaces for email attachments
|
||||
- [ ] Ensure consistent usage between different email handling components
|
||||
- [ ] Add validation for attachment properties
|
||||
|
||||
### 9. Processing Mode Type Safety
|
||||
|
||||
- [ ] Create discriminated unions for different email processing modes
|
||||
- [ ] Add type guards to ensure safe handling of mode-specific properties
|
||||
- [ ] Ensure proper validation of processing mode values
|
||||
|
||||
### 10. Third-Party Integration Types
|
||||
|
||||
- [ ] Review and update type definitions for third-party dependencies
|
||||
- [ ] Create additional type definitions where missing
|
||||
- [ ] Ensure consistent usage of external libraries
|
||||
|
||||
## Implementation Order
|
||||
|
||||
We'll implement these improvements in the following order:
|
||||
|
||||
1. First, focus on the core interfaces (Platform Service, SMTP Session)
|
||||
2. Next, improve configuration types as they affect multiple components
|
||||
3. Then, address function return types and string literals
|
||||
4. Finally, handle the remaining specialized types (auth, attachments, etc.)
|
||||
|
||||
This approach allows us to tackle the most widely-used types first, providing the greatest immediate benefit while establishing patterns for the rest of the implementation.
|
258
test/test.mta.ts
Normal file
258
test/test.mta.ts
Normal file
@ -0,0 +1,258 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import * as plugins from '../ts/plugins.js';
|
||||
import * as paths from '../ts/paths.js';
|
||||
import { SmtpClient } from '../ts/mail/delivery/classes.smtp.client.js';
|
||||
import type { ISmtpClientOptions } from '../ts/mail/delivery/classes.smtp.client.js';
|
||||
import { Email } from '../ts/mail/core/classes.email.js';
|
||||
|
||||
/**
|
||||
* Tests for the SMTP client class
|
||||
*/
|
||||
tap.test('verify SMTP client initialization', async () => {
|
||||
// Create test configuration
|
||||
const options: ISmtpClientOptions = {
|
||||
host: 'smtp.example.com',
|
||||
port: 587,
|
||||
secure: false,
|
||||
connectionTimeout: 10000,
|
||||
domain: 'test.example.com'
|
||||
};
|
||||
|
||||
// Create MTA instance
|
||||
const mta = new MailTransferAgent(options);
|
||||
|
||||
// Verify instance was created correctly
|
||||
expect(mta).toBeTruthy();
|
||||
expect(mta.isConnected()).toBeFalsy(); // Should start disconnected
|
||||
});
|
||||
|
||||
tap.test('test MTA configuration update', async () => {
|
||||
// Create test configuration
|
||||
const options: IMtaConnectionOptions = {
|
||||
host: 'smtp.example.com',
|
||||
port: 587,
|
||||
secure: false
|
||||
};
|
||||
|
||||
// Create MTA instance
|
||||
const mta = new MailTransferAgent(options);
|
||||
|
||||
// Update configuration
|
||||
mta.updateOptions({
|
||||
host: 'new-smtp.example.com',
|
||||
port: 465,
|
||||
secure: true
|
||||
});
|
||||
|
||||
// Can't directly test private fields, but we can verify it doesn't throw
|
||||
expect(() => mta.updateOptions({
|
||||
tls: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
})).not.toThrow();
|
||||
});
|
||||
|
||||
// Mocked SMTP server for testing
|
||||
class MockSmtpServer {
|
||||
private responses: Map<string, string>;
|
||||
|
||||
constructor() {
|
||||
this.responses = new Map();
|
||||
|
||||
// Default responses
|
||||
this.responses.set('connect', '220 smtp.example.com ESMTP ready');
|
||||
this.responses.set('EHLO', '250-smtp.example.com\r\n250-PIPELINING\r\n250-SIZE 10240000\r\n250-STARTTLS\r\n250-AUTH PLAIN LOGIN\r\n250 HELP');
|
||||
this.responses.set('MAIL FROM', '250 OK');
|
||||
this.responses.set('RCPT TO', '250 OK');
|
||||
this.responses.set('DATA', '354 Start mail input; end with <CRLF>.<CRLF>');
|
||||
this.responses.set('data content', '250 OK: message accepted');
|
||||
this.responses.set('QUIT', '221 Bye');
|
||||
}
|
||||
|
||||
public setResponse(command: string, response: string): void {
|
||||
this.responses.set(command, response);
|
||||
}
|
||||
|
||||
public getResponse(command: string): string {
|
||||
if (command.startsWith('MAIL FROM')) {
|
||||
return this.responses.get('MAIL FROM') || '250 OK';
|
||||
} else if (command.startsWith('RCPT TO')) {
|
||||
return this.responses.get('RCPT TO') || '250 OK';
|
||||
} else if (command.startsWith('EHLO') || command.startsWith('HELO')) {
|
||||
return this.responses.get('EHLO') || '250 OK';
|
||||
} else if (command === 'DATA') {
|
||||
return this.responses.get('DATA') || '354 Start mail input; end with <CRLF>.<CRLF>';
|
||||
} else if (command.includes('Content-Type')) {
|
||||
return this.responses.get('data content') || '250 OK: message accepted';
|
||||
} else if (command === 'QUIT') {
|
||||
return this.responses.get('QUIT') || '221 Bye';
|
||||
}
|
||||
|
||||
return this.responses.get(command) || '250 OK';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test validates the MTA capabilities without connecting to a real server
|
||||
* It uses a simple mock that only checks method signatures and properties
|
||||
*/
|
||||
tap.test('verify MTA email delivery functionality with mock', async () => {
|
||||
// Create a mock SMTP server
|
||||
const mockServer = new MockSmtpServer();
|
||||
|
||||
// Create a test email
|
||||
const testEmail = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Test Email',
|
||||
text: 'This is a test email'
|
||||
});
|
||||
|
||||
// Create MTA options
|
||||
const options: IMtaConnectionOptions = {
|
||||
host: 'smtp.example.com',
|
||||
port: 587,
|
||||
secure: false,
|
||||
domain: 'test.example.com',
|
||||
auth: {
|
||||
user: 'testuser',
|
||||
pass: 'testpass'
|
||||
}
|
||||
};
|
||||
|
||||
// Create MTA instance
|
||||
const mta = new MailTransferAgent(options);
|
||||
|
||||
// Mock the connect method
|
||||
mta['connect'] = async function() {
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.connected = true;
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.socket = {
|
||||
write: (data: string, callback: () => void) => {
|
||||
callback();
|
||||
},
|
||||
on: () => {},
|
||||
once: () => {},
|
||||
removeListener: () => {},
|
||||
destroy: () => {},
|
||||
setTimeout: () => {}
|
||||
};
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.supportedExtensions = new Set(['PIPELINING', 'SIZE', 'STARTTLS', 'AUTH']);
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
// Mock the sendCommand method
|
||||
mta['sendCommand'] = async function(command: string) {
|
||||
return Promise.resolve(mockServer.getResponse(command));
|
||||
};
|
||||
|
||||
// Mock the readResponse method
|
||||
mta['readResponse'] = async function() {
|
||||
return Promise.resolve(mockServer.getResponse('connect'));
|
||||
};
|
||||
|
||||
// Test sending an email
|
||||
try {
|
||||
const result = await mta.sendMail(testEmail);
|
||||
|
||||
// Verify the result
|
||||
expect(result).toBeTruthy();
|
||||
expect(result.success).toEqual(true);
|
||||
expect(result.acceptedRecipients).toEqual(['recipient@example.com']);
|
||||
expect(result.rejectedRecipients).toEqual([]);
|
||||
|
||||
} catch (error) {
|
||||
// This should not happen
|
||||
expect(error).toBeUndefined();
|
||||
}
|
||||
|
||||
// Test closing the connection
|
||||
await mta.close();
|
||||
expect(mta.isConnected()).toBeFalsy();
|
||||
});
|
||||
|
||||
tap.test('test MTA error handling with mock', async () => {
|
||||
// Create a mock SMTP server
|
||||
const mockServer = new MockSmtpServer();
|
||||
|
||||
// Set error response for RCPT TO
|
||||
mockServer.setResponse('RCPT TO', '550 No such user here');
|
||||
|
||||
// Create a test email
|
||||
const testEmail = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['unknown@example.com'],
|
||||
subject: 'Test Email',
|
||||
text: 'This is a test email'
|
||||
});
|
||||
|
||||
// Create MTA instance
|
||||
const mta = new MailTransferAgent({
|
||||
host: 'smtp.example.com',
|
||||
port: 587,
|
||||
secure: false
|
||||
});
|
||||
|
||||
// Mock the connect method
|
||||
mta['connect'] = async function() {
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.connected = true;
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.socket = {
|
||||
write: (data: string, callback: () => void) => {
|
||||
callback();
|
||||
},
|
||||
on: () => {},
|
||||
once: () => {},
|
||||
removeListener: () => {},
|
||||
destroy: () => {},
|
||||
setTimeout: () => {}
|
||||
};
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.supportedExtensions = new Set(['PIPELINING', 'SIZE', 'STARTTLS', 'AUTH']);
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
// Mock the sendCommand method
|
||||
mta['sendCommand'] = async function(command: string) {
|
||||
const response = mockServer.getResponse(command);
|
||||
|
||||
// Simulate an error response for RCPT TO
|
||||
if (command.startsWith('RCPT TO') && response.startsWith('550')) {
|
||||
const error = new Error(response);
|
||||
error['context'] = {
|
||||
data: {
|
||||
statusCode: '550'
|
||||
}
|
||||
};
|
||||
throw error;
|
||||
}
|
||||
|
||||
return Promise.resolve(response);
|
||||
};
|
||||
|
||||
// Test sending an email that will fail
|
||||
const result = await mta.sendMail(testEmail);
|
||||
|
||||
// Verify the result shows failure
|
||||
expect(result).toBeTruthy();
|
||||
expect(result.success).toEqual(false);
|
||||
expect(result.acceptedRecipients).toEqual([]);
|
||||
expect(result.rejectedRecipients).toEqual(['unknown@example.com']);
|
||||
expect(result.error).toBeTruthy();
|
||||
});
|
||||
|
||||
// Final clean-up test
|
||||
tap.test('clean up after tests', async () => {
|
||||
// No-op - just to make sure everything is cleaned up properly
|
||||
});
|
||||
|
||||
tap.test('stop', async () => {
|
||||
await tap.stopForcefully();
|
||||
});
|
||||
|
||||
export default tap.start();
|
258
test/test.smtp.client.ts
Normal file
258
test/test.smtp.client.ts
Normal file
@ -0,0 +1,258 @@
|
||||
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
||||
import * as plugins from '../ts/plugins.js';
|
||||
import * as paths from '../ts/paths.js';
|
||||
import { SmtpClient } from '../ts/mail/delivery/classes.smtp.client.js';
|
||||
import type { ISmtpClientOptions } from '../ts/mail/delivery/classes.smtp.client.js';
|
||||
import { Email } from '../ts/mail/core/classes.email.js';
|
||||
|
||||
/**
|
||||
* Tests for the SMTP client class
|
||||
*/
|
||||
tap.test('verify SMTP client initialization', async () => {
|
||||
// Create test configuration
|
||||
const options: ISmtpClientOptions = {
|
||||
host: 'smtp.example.com',
|
||||
port: 587,
|
||||
secure: false,
|
||||
connectionTimeout: 10000,
|
||||
domain: 'test.example.com'
|
||||
};
|
||||
|
||||
// Create SMTP client instance
|
||||
const smtpClient = new SmtpClient(options);
|
||||
|
||||
// Verify instance was created correctly
|
||||
expect(smtpClient).toBeTruthy();
|
||||
expect(smtpClient.isConnected()).toBeFalsy(); // Should start disconnected
|
||||
});
|
||||
|
||||
tap.test('test SMTP client configuration update', async () => {
|
||||
// Create test configuration
|
||||
const options: ISmtpClientOptions = {
|
||||
host: 'smtp.example.com',
|
||||
port: 587,
|
||||
secure: false
|
||||
};
|
||||
|
||||
// Create SMTP client instance
|
||||
const smtpClient = new SmtpClient(options);
|
||||
|
||||
// Update configuration
|
||||
smtpClient.updateOptions({
|
||||
host: 'new-smtp.example.com',
|
||||
port: 465,
|
||||
secure: true
|
||||
});
|
||||
|
||||
// Can't directly test private fields, but we can verify it doesn't throw
|
||||
expect(() => smtpClient.updateOptions({
|
||||
tls: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
})).not.toThrow();
|
||||
});
|
||||
|
||||
// Mocked SMTP server for testing
|
||||
class MockSmtpServer {
|
||||
private responses: Map<string, string>;
|
||||
|
||||
constructor() {
|
||||
this.responses = new Map();
|
||||
|
||||
// Default responses
|
||||
this.responses.set('connect', '220 smtp.example.com ESMTP ready');
|
||||
this.responses.set('EHLO', '250-smtp.example.com\r\n250-PIPELINING\r\n250-SIZE 10240000\r\n250-STARTTLS\r\n250-AUTH PLAIN LOGIN\r\n250 HELP');
|
||||
this.responses.set('MAIL FROM', '250 OK');
|
||||
this.responses.set('RCPT TO', '250 OK');
|
||||
this.responses.set('DATA', '354 Start mail input; end with <CRLF>.<CRLF>');
|
||||
this.responses.set('data content', '250 OK: message accepted');
|
||||
this.responses.set('QUIT', '221 Bye');
|
||||
}
|
||||
|
||||
public setResponse(command: string, response: string): void {
|
||||
this.responses.set(command, response);
|
||||
}
|
||||
|
||||
public getResponse(command: string): string {
|
||||
if (command.startsWith('MAIL FROM')) {
|
||||
return this.responses.get('MAIL FROM') || '250 OK';
|
||||
} else if (command.startsWith('RCPT TO')) {
|
||||
return this.responses.get('RCPT TO') || '250 OK';
|
||||
} else if (command.startsWith('EHLO') || command.startsWith('HELO')) {
|
||||
return this.responses.get('EHLO') || '250 OK';
|
||||
} else if (command === 'DATA') {
|
||||
return this.responses.get('DATA') || '354 Start mail input; end with <CRLF>.<CRLF>';
|
||||
} else if (command.includes('Content-Type')) {
|
||||
return this.responses.get('data content') || '250 OK: message accepted';
|
||||
} else if (command === 'QUIT') {
|
||||
return this.responses.get('QUIT') || '221 Bye';
|
||||
}
|
||||
|
||||
return this.responses.get(command) || '250 OK';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test validates the SMTP client capabilities without connecting to a real server
|
||||
* It uses a simple mock that only checks method signatures and properties
|
||||
*/
|
||||
tap.test('verify SMTP client email delivery functionality with mock', async () => {
|
||||
// Create a mock SMTP server
|
||||
const mockServer = new MockSmtpServer();
|
||||
|
||||
// Create a test email
|
||||
const testEmail = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Test Email',
|
||||
text: 'This is a test email'
|
||||
});
|
||||
|
||||
// Create SMTP client options
|
||||
const options: ISmtpClientOptions = {
|
||||
host: 'smtp.example.com',
|
||||
port: 587,
|
||||
secure: false,
|
||||
domain: 'test.example.com',
|
||||
auth: {
|
||||
user: 'testuser',
|
||||
pass: 'testpass'
|
||||
}
|
||||
};
|
||||
|
||||
// Create SMTP client instance
|
||||
const smtpClient = new SmtpClient(options);
|
||||
|
||||
// Mock the connect method
|
||||
smtpClient['connect'] = async function() {
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.connected = true;
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.socket = {
|
||||
write: (data: string, callback: () => void) => {
|
||||
callback();
|
||||
},
|
||||
on: () => {},
|
||||
once: () => {},
|
||||
removeListener: () => {},
|
||||
destroy: () => {},
|
||||
setTimeout: () => {}
|
||||
};
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.supportedExtensions = new Set(['PIPELINING', 'SIZE', 'STARTTLS', 'AUTH']);
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
// Mock the sendCommand method
|
||||
smtpClient['sendCommand'] = async function(command: string) {
|
||||
return Promise.resolve(mockServer.getResponse(command));
|
||||
};
|
||||
|
||||
// Mock the readResponse method
|
||||
smtpClient['readResponse'] = async function() {
|
||||
return Promise.resolve(mockServer.getResponse('connect'));
|
||||
};
|
||||
|
||||
// Test sending an email
|
||||
try {
|
||||
const result = await smtpClient.sendMail(testEmail);
|
||||
|
||||
// Verify the result
|
||||
expect(result).toBeTruthy();
|
||||
expect(result.success).toEqual(true);
|
||||
expect(result.acceptedRecipients).toEqual(['recipient@example.com']);
|
||||
expect(result.rejectedRecipients).toEqual([]);
|
||||
|
||||
} catch (error) {
|
||||
// This should not happen
|
||||
expect(error).toBeUndefined();
|
||||
}
|
||||
|
||||
// Test closing the connection
|
||||
await smtpClient.close();
|
||||
expect(smtpClient.isConnected()).toBeFalsy();
|
||||
});
|
||||
|
||||
tap.test('test SMTP client error handling with mock', async () => {
|
||||
// Create a mock SMTP server
|
||||
const mockServer = new MockSmtpServer();
|
||||
|
||||
// Set error response for RCPT TO
|
||||
mockServer.setResponse('RCPT TO', '550 No such user here');
|
||||
|
||||
// Create a test email
|
||||
const testEmail = new Email({
|
||||
from: 'sender@example.com',
|
||||
to: ['unknown@example.com'],
|
||||
subject: 'Test Email',
|
||||
text: 'This is a test email'
|
||||
});
|
||||
|
||||
// Create SMTP client instance
|
||||
const smtpClient = new SmtpClient({
|
||||
host: 'smtp.example.com',
|
||||
port: 587,
|
||||
secure: false
|
||||
});
|
||||
|
||||
// Mock the connect method
|
||||
smtpClient['connect'] = async function() {
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.connected = true;
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.socket = {
|
||||
write: (data: string, callback: () => void) => {
|
||||
callback();
|
||||
},
|
||||
on: () => {},
|
||||
once: () => {},
|
||||
removeListener: () => {},
|
||||
destroy: () => {},
|
||||
setTimeout: () => {}
|
||||
};
|
||||
// @ts-ignore: setting private property for testing
|
||||
this.supportedExtensions = new Set(['PIPELINING', 'SIZE', 'STARTTLS', 'AUTH']);
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
// Mock the sendCommand method
|
||||
smtpClient['sendCommand'] = async function(command: string) {
|
||||
const response = mockServer.getResponse(command);
|
||||
|
||||
// Simulate an error response for RCPT TO
|
||||
if (command.startsWith('RCPT TO') && response.startsWith('550')) {
|
||||
const error = new Error(response);
|
||||
error['context'] = {
|
||||
data: {
|
||||
statusCode: '550'
|
||||
}
|
||||
};
|
||||
throw error;
|
||||
}
|
||||
|
||||
return Promise.resolve(response);
|
||||
};
|
||||
|
||||
// Test sending an email that will fail
|
||||
const result = await smtpClient.sendMail(testEmail);
|
||||
|
||||
// Verify the result shows failure
|
||||
expect(result).toBeTruthy();
|
||||
expect(result.success).toEqual(false);
|
||||
expect(result.acceptedRecipients).toEqual([]);
|
||||
expect(result.rejectedRecipients).toEqual(['unknown@example.com']);
|
||||
expect(result.error).toBeTruthy();
|
||||
});
|
||||
|
||||
// Final clean-up test
|
||||
tap.test('clean up after tests', async () => {
|
||||
// No-op - just to make sure everything is cleaned up properly
|
||||
});
|
||||
|
||||
tap.test('stop', async () => {
|
||||
await tap.stopForcefully();
|
||||
});
|
||||
|
||||
export default tap.start();
|
@ -94,20 +94,16 @@ export class DcRouter {
|
||||
public deliverySystem?: MultiModeDeliverySystem;
|
||||
public rateLimiter?: UnifiedRateLimiter;
|
||||
|
||||
// Reference to the platform service for accessing MTA and other services
|
||||
public platformServiceRef?: any;
|
||||
|
||||
// Environment access
|
||||
private qenv = new plugins.qenv.Qenv('./', '.nogit/');
|
||||
|
||||
constructor(optionsArg: IDcRouterOptions, platformServiceRef?: any) {
|
||||
constructor(optionsArg: IDcRouterOptions) {
|
||||
// Set defaults in options
|
||||
this.options = {
|
||||
...optionsArg
|
||||
};
|
||||
|
||||
// Store reference to platform service if provided
|
||||
this.platformServiceRef = platformServiceRef;
|
||||
}
|
||||
|
||||
public async start() {
|
||||
@ -681,9 +677,6 @@ export class DcRouter {
|
||||
}): Promise<boolean> {
|
||||
logger.log('info', 'Configuring MTA service with custom settings');
|
||||
|
||||
if (!this.platformServiceRef) {
|
||||
throw new Error('Platform service reference is required for MTA configuration');
|
||||
}
|
||||
|
||||
// Update email port configuration
|
||||
if (!this.options.emailPortConfig) {
|
||||
|
1201
ts/mail/delivery/classes.smtp.client.ts
Normal file
1201
ts/mail/delivery/classes.smtp.client.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,55 +11,18 @@ import {
|
||||
ReputationThreshold
|
||||
} from '../../security/index.js';
|
||||
|
||||
export interface ISmtpServerOptions {
|
||||
port: number;
|
||||
key: string;
|
||||
cert: string;
|
||||
hostname?: string;
|
||||
}
|
||||
|
||||
// SMTP Session States
|
||||
enum SmtpState {
|
||||
GREETING,
|
||||
AFTER_EHLO,
|
||||
MAIL_FROM,
|
||||
RCPT_TO,
|
||||
DATA,
|
||||
DATA_RECEIVING,
|
||||
FINISHED
|
||||
}
|
||||
|
||||
// Structure to store session information
|
||||
interface SmtpSession {
|
||||
id: string;
|
||||
state: SmtpState;
|
||||
clientHostname: string;
|
||||
mailFrom: string;
|
||||
rcptTo: string[];
|
||||
emailData: string;
|
||||
useTLS: boolean;
|
||||
connectionEnded: boolean;
|
||||
remoteAddress: string;
|
||||
secure: boolean;
|
||||
authenticated: boolean;
|
||||
envelope: {
|
||||
mailFrom: {
|
||||
address: string;
|
||||
args: any;
|
||||
};
|
||||
rcptTo: Array<{
|
||||
address: string;
|
||||
args: any;
|
||||
}>;
|
||||
};
|
||||
processingMode?: 'forward' | 'mta' | 'process';
|
||||
}
|
||||
import type {
|
||||
ISmtpServerOptions,
|
||||
ISmtpSession,
|
||||
EmailProcessingMode
|
||||
} from './interfaces.js';
|
||||
import { SmtpState } from './interfaces.js';
|
||||
|
||||
export class SMTPServer {
|
||||
public emailServerRef: UnifiedEmailServer;
|
||||
private smtpServerOptions: ISmtpServerOptions;
|
||||
private server: plugins.net.Server;
|
||||
private sessions: Map<plugins.net.Socket | plugins.tls.TLSSocket, SmtpSession>;
|
||||
private sessions: Map<plugins.net.Socket | plugins.tls.TLSSocket, ISmtpSession>;
|
||||
private hostname: string;
|
||||
|
||||
constructor(emailServerRefArg: UnifiedEmailServer, optionsArg: ISmtpServerOptions) {
|
||||
@ -722,6 +685,12 @@ export class SMTPServer {
|
||||
try {
|
||||
await this.emailServerRef.processEmailByMode(email, {
|
||||
id: session.id,
|
||||
state: session.state,
|
||||
mailFrom: session.mailFrom,
|
||||
rcptTo: session.rcptTo,
|
||||
emailData: session.emailData,
|
||||
useTLS: session.useTLS,
|
||||
connectionEnded: session.connectionEnded,
|
||||
remoteAddress: session.remoteAddress,
|
||||
clientHostname: session.clientHostname,
|
||||
secure: session.useTLS,
|
||||
|
@ -15,5 +15,6 @@ export type { IRateLimitConfig } from './classes.ratelimiter.js';
|
||||
// Unified rate limiter
|
||||
export * from './classes.unified.rate.limiter.js';
|
||||
|
||||
// MTA configuration helpers
|
||||
export * from './classes.mta.config.js';
|
||||
// SMTP client and configuration
|
||||
export * from './classes.mta.config.js';
|
||||
export * from './classes.smtp.client.js';
|
223
ts/mail/delivery/interfaces.ts
Normal file
223
ts/mail/delivery/interfaces.ts
Normal file
@ -0,0 +1,223 @@
|
||||
/**
|
||||
* SMTP and email delivery interface definitions
|
||||
*/
|
||||
|
||||
import type { Email } from '../core/classes.email.js';
|
||||
|
||||
/**
|
||||
* SMTP session state enumeration
|
||||
*/
|
||||
export enum SmtpState {
|
||||
GREETING = 'GREETING',
|
||||
AFTER_EHLO = 'AFTER_EHLO',
|
||||
MAIL_FROM = 'MAIL_FROM',
|
||||
RCPT_TO = 'RCPT_TO',
|
||||
DATA = 'DATA',
|
||||
DATA_RECEIVING = 'DATA_RECEIVING',
|
||||
FINISHED = 'FINISHED'
|
||||
}
|
||||
|
||||
/**
|
||||
* Email processing mode type
|
||||
*/
|
||||
export type EmailProcessingMode = 'forward' | 'mta' | 'process';
|
||||
|
||||
/**
|
||||
* Envelope recipient information
|
||||
*/
|
||||
export interface IEnvelopeRecipient {
|
||||
/**
|
||||
* Email address of the recipient
|
||||
*/
|
||||
address: string;
|
||||
|
||||
/**
|
||||
* Additional SMTP command arguments
|
||||
*/
|
||||
args: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP session envelope information
|
||||
*/
|
||||
export interface ISmtpEnvelope {
|
||||
/**
|
||||
* Envelope sender (MAIL FROM) information
|
||||
*/
|
||||
mailFrom: {
|
||||
/**
|
||||
* Email address of the sender
|
||||
*/
|
||||
address: string;
|
||||
|
||||
/**
|
||||
* Additional SMTP command arguments
|
||||
*/
|
||||
args: Record<string, string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Envelope recipients (RCPT TO) information
|
||||
*/
|
||||
rcptTo: IEnvelopeRecipient[];
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP Session interface - represents an active SMTP connection
|
||||
*/
|
||||
export interface ISmtpSession {
|
||||
/**
|
||||
* Unique session identifier
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Current session state in the SMTP conversation
|
||||
*/
|
||||
state: SmtpState;
|
||||
|
||||
/**
|
||||
* Hostname provided by the client in EHLO/HELO command
|
||||
*/
|
||||
clientHostname: string;
|
||||
|
||||
/**
|
||||
* MAIL FROM email address (legacy format)
|
||||
*/
|
||||
mailFrom: string;
|
||||
|
||||
/**
|
||||
* RCPT TO email addresses (legacy format)
|
||||
*/
|
||||
rcptTo: string[];
|
||||
|
||||
/**
|
||||
* Raw email data being received
|
||||
*/
|
||||
emailData: string;
|
||||
|
||||
/**
|
||||
* Whether the connection is using TLS
|
||||
*/
|
||||
useTLS: boolean;
|
||||
|
||||
/**
|
||||
* Whether the connection has ended
|
||||
*/
|
||||
connectionEnded: boolean;
|
||||
|
||||
/**
|
||||
* Remote IP address of the client
|
||||
*/
|
||||
remoteAddress: string;
|
||||
|
||||
/**
|
||||
* Whether the connection is secure (TLS)
|
||||
*/
|
||||
secure: boolean;
|
||||
|
||||
/**
|
||||
* Whether the client has been authenticated
|
||||
*/
|
||||
authenticated: boolean;
|
||||
|
||||
/**
|
||||
* SMTP envelope information (structured format)
|
||||
*/
|
||||
envelope: ISmtpEnvelope;
|
||||
|
||||
/**
|
||||
* Email processing mode to use for this session
|
||||
*/
|
||||
processingMode?: EmailProcessingMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP authentication data
|
||||
*/
|
||||
export interface ISmtpAuth {
|
||||
/**
|
||||
* Authentication method used
|
||||
*/
|
||||
method: 'PLAIN' | 'LOGIN' | 'OAUTH2' | string;
|
||||
|
||||
/**
|
||||
* Username for authentication
|
||||
*/
|
||||
username: string;
|
||||
|
||||
/**
|
||||
* Password or token for authentication
|
||||
*/
|
||||
password: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP server options
|
||||
*/
|
||||
export interface ISmtpServerOptions {
|
||||
/**
|
||||
* Port to listen on
|
||||
*/
|
||||
port: number;
|
||||
|
||||
/**
|
||||
* TLS private key (PEM format)
|
||||
*/
|
||||
key: string;
|
||||
|
||||
/**
|
||||
* TLS certificate (PEM format)
|
||||
*/
|
||||
cert: string;
|
||||
|
||||
/**
|
||||
* Server hostname for SMTP banner
|
||||
*/
|
||||
hostname?: string;
|
||||
|
||||
/**
|
||||
* Maximum size of messages in bytes
|
||||
*/
|
||||
maxSize?: number;
|
||||
|
||||
/**
|
||||
* Authentication options
|
||||
*/
|
||||
auth?: {
|
||||
/**
|
||||
* Whether authentication is required
|
||||
*/
|
||||
required: boolean;
|
||||
|
||||
/**
|
||||
* Allowed authentication methods
|
||||
*/
|
||||
methods: ('PLAIN' | 'LOGIN' | 'OAUTH2')[];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of SMTP transaction
|
||||
*/
|
||||
export interface ISmtpTransactionResult {
|
||||
/**
|
||||
* Whether the transaction was successful
|
||||
*/
|
||||
success: boolean;
|
||||
|
||||
/**
|
||||
* Error message if failed
|
||||
*/
|
||||
error?: string;
|
||||
|
||||
/**
|
||||
* Message ID if successful
|
||||
*/
|
||||
messageId?: string;
|
||||
|
||||
/**
|
||||
* Resulting email if successful
|
||||
*/
|
||||
email?: Email;
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
|
||||
/**
|
||||
* Email processing modes
|
||||
*/
|
||||
export type EmailProcessingMode = 'forward' | 'mta' | 'process';
|
||||
import type { EmailProcessingMode } from '../delivery/interfaces.js';
|
||||
|
||||
// Re-export EmailProcessingMode type
|
||||
export type { EmailProcessingMode };
|
||||
|
||||
/**
|
||||
* Consolidated email configuration interface
|
||||
|
@ -18,7 +18,6 @@ import {
|
||||
import { DomainRouter } from './classes.domain.router.js';
|
||||
import type {
|
||||
IEmailConfig,
|
||||
EmailProcessingMode,
|
||||
IDomainRule
|
||||
} from './classes.email.config.js';
|
||||
import { Email } from '../core/classes.email.js';
|
||||
@ -29,6 +28,18 @@ import * as stream from 'node:stream';
|
||||
import { SMTPServer as MtaSmtpServer } from '../delivery/classes.smtpserver.js';
|
||||
import { MultiModeDeliverySystem, type IMultiModeDeliveryOptions } from '../delivery/classes.delivery.system.js';
|
||||
import { UnifiedDeliveryQueue, type IQueueOptions } from '../delivery/classes.delivery.queue.js';
|
||||
import { SmtpState } from '../delivery/interfaces.js';
|
||||
import type { EmailProcessingMode, ISmtpSession as IBaseSmtpSession } from '../delivery/interfaces.js';
|
||||
|
||||
/**
|
||||
* Extended SMTP session interface with domain rule information
|
||||
*/
|
||||
export interface IExtendedSmtpSession extends ISmtpSession {
|
||||
/**
|
||||
* Matched domain rule for this session
|
||||
*/
|
||||
matchedRule?: IDomainRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for the unified email server
|
||||
@ -78,41 +89,30 @@ export interface IUnifiedEmailServerOptions {
|
||||
reputationMonitorConfig?: IReputationMonitorConfig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interface describing SMTP session data
|
||||
* Extended SMTP session interface for UnifiedEmailServer
|
||||
*/
|
||||
export interface ISmtpSession {
|
||||
id: string;
|
||||
remoteAddress: string;
|
||||
clientHostname: string;
|
||||
secure: boolean;
|
||||
authenticated: boolean;
|
||||
export interface ISmtpSession extends IBaseSmtpSession {
|
||||
/**
|
||||
* User information if authenticated
|
||||
*/
|
||||
user?: {
|
||||
username: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
envelope: {
|
||||
mailFrom: {
|
||||
address: string;
|
||||
args: any;
|
||||
};
|
||||
rcptTo: Array<{
|
||||
address: string;
|
||||
args: any;
|
||||
}>;
|
||||
};
|
||||
processingMode?: EmailProcessingMode;
|
||||
|
||||
/**
|
||||
* Matched domain rule for this session
|
||||
*/
|
||||
matchedRule?: IDomainRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication data for SMTP
|
||||
*/
|
||||
export interface IAuthData {
|
||||
method: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
import type { ISmtpAuth } from '../delivery/interfaces.js';
|
||||
export type IAuthData = ISmtpAuth;
|
||||
|
||||
/**
|
||||
* Server statistics
|
||||
@ -330,6 +330,12 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
// Process based on the mode
|
||||
await this.processEmailByMode(email, {
|
||||
id: 'session-' + Math.random().toString(36).substring(2),
|
||||
state: SmtpState.FINISHED,
|
||||
mailFrom: email.from,
|
||||
rcptTo: email.to,
|
||||
emailData: email.toRFC822String(), // Use the proper method to get the full email content
|
||||
useTLS: false,
|
||||
connectionEnded: true,
|
||||
remoteAddress: '127.0.0.1',
|
||||
clientHostname: '',
|
||||
secure: false,
|
||||
@ -431,7 +437,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle new SMTP connection with IP reputation checking
|
||||
*/
|
||||
private async onConnect(session: ISmtpSession, callback: (err?: Error) => void): Promise<void> {
|
||||
private async onConnect(session: IExtendedSmtpSession, callback: (err?: Error) => void): Promise<void> {
|
||||
logger.log('info', `New connection from ${session.remoteAddress}`);
|
||||
|
||||
// Update connection statistics
|
||||
@ -498,7 +504,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle authentication (stub implementation)
|
||||
*/
|
||||
private onAuth(auth: IAuthData, session: ISmtpSession, callback: (err?: Error, user?: any) => void): void {
|
||||
private onAuth(auth: IAuthData, session: IExtendedSmtpSession, callback: (err?: Error, user?: any) => void): void {
|
||||
if (!this.options.auth || !this.options.auth.users || this.options.auth.users.length === 0) {
|
||||
// No authentication configured, reject
|
||||
const error = new Error('Authentication not supported');
|
||||
@ -564,7 +570,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle MAIL FROM command (stub implementation)
|
||||
*/
|
||||
private onMailFrom(address: {address: string}, session: ISmtpSession, callback: (err?: Error) => void): void {
|
||||
private onMailFrom(address: {address: string}, session: IExtendedSmtpSession, callback: (err?: Error) => void): void {
|
||||
logger.log('info', `MAIL FROM: ${address.address}`);
|
||||
|
||||
// Validate the email address
|
||||
@ -614,7 +620,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle RCPT TO command (stub implementation)
|
||||
*/
|
||||
private onRcptTo(address: {address: string}, session: ISmtpSession, callback: (err?: Error) => void): void {
|
||||
private onRcptTo(address: {address: string}, session: IExtendedSmtpSession, callback: (err?: Error) => void): void {
|
||||
logger.log('info', `RCPT TO: ${address.address}`);
|
||||
|
||||
// Validate the email address
|
||||
@ -658,7 +664,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle incoming email data (stub implementation)
|
||||
*/
|
||||
private onData(stream: stream.Readable, session: ISmtpSession, callback: (err?: Error) => void): void {
|
||||
private onData(stream: stream.Readable, session: IExtendedSmtpSession, callback: (err?: Error) => void): void {
|
||||
logger.log('info', `Processing email data for session ${session.id}`);
|
||||
|
||||
const startTime = Date.now();
|
||||
@ -762,7 +768,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Process email based on the determined mode
|
||||
*/
|
||||
public async processEmailByMode(emailData: Email | Buffer, session: ISmtpSession, mode: EmailProcessingMode): Promise<Email> {
|
||||
public async processEmailByMode(emailData: Email | Buffer, session: IExtendedSmtpSession, mode: EmailProcessingMode): Promise<Email> {
|
||||
// Convert Buffer to Email if needed
|
||||
let email: Email;
|
||||
if (Buffer.isBuffer(emailData)) {
|
||||
@ -833,7 +839,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle email in forward mode (SMTP proxy)
|
||||
*/
|
||||
private async handleForwardMode(email: Email, session: ISmtpSession): Promise<void> {
|
||||
private async handleForwardMode(email: Email, session: IExtendedSmtpSession): Promise<void> {
|
||||
logger.log('info', `Handling email in forward mode for session ${session.id}`);
|
||||
|
||||
// Get target server information
|
||||
@ -927,7 +933,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle email in MTA mode (programmatic processing)
|
||||
*/
|
||||
private async handleMtaMode(email: Email, session: ISmtpSession): Promise<void> {
|
||||
private async handleMtaMode(email: Email, session: IExtendedSmtpSession): Promise<void> {
|
||||
logger.log('info', `Handling email in MTA mode for session ${session.id}`);
|
||||
|
||||
try {
|
||||
@ -1022,7 +1028,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle email in process mode (store-and-forward with scanning)
|
||||
*/
|
||||
private async handleProcessMode(email: Email, session: ISmtpSession): Promise<void> {
|
||||
private async handleProcessMode(email: Email, session: IExtendedSmtpSession): Promise<void> {
|
||||
logger.log('info', `Handling email in process mode for session ${session.id}`);
|
||||
|
||||
try {
|
||||
|
@ -6,8 +6,7 @@ import { TemplateManager } from '../core/classes.templatemanager.js';
|
||||
import { EmailValidator } from '../core/classes.emailvalidator.js';
|
||||
import { BounceManager } from '../core/classes.bouncemanager.js';
|
||||
import { logger } from '../../logger.js';
|
||||
// Import types from platform interfaces
|
||||
import type { default as platformInterfaces } from '../../types/platform.interfaces.js';
|
||||
// Import types from router interfaces
|
||||
import { UnifiedEmailServer } from '../routing/classes.unified.email.server.js';
|
||||
import { DomainRouter } from '../routing/classes.domain.router.js';
|
||||
import { Email } from '../core/classes.email.js';
|
||||
@ -144,7 +143,6 @@ export interface IEmailServiceStats {
|
||||
* Email service with MTA support
|
||||
*/
|
||||
export class EmailService {
|
||||
public platformServiceRef: any; // Reference to platform service
|
||||
|
||||
// typedrouter
|
||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||
@ -166,9 +164,7 @@ export class EmailService {
|
||||
// configuration
|
||||
private config: IEmailConfig;
|
||||
|
||||
constructor(platformServiceRefArg: any, options: IEmailConfig = {}) {
|
||||
this.platformServiceRef = platformServiceRefArg;
|
||||
this.platformServiceRef.typedrouter.addTypedRouter(this.typedrouter);
|
||||
constructor(options: IEmailConfig = {}) {
|
||||
|
||||
// Validate and apply defaults to configuration
|
||||
const validationResult = ConfigValidator.validate(options, emailConfigSchema);
|
||||
|
@ -1,21 +1,16 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import * as paths from '../paths.js';
|
||||
import { logger } from '../logger.js';
|
||||
// Import types from platform interfaces
|
||||
import type { default as platformInterfaces } from '../types/platform.interfaces.js';
|
||||
|
||||
import type { ISmsConfig } from '../config/sms.config.js';
|
||||
import { ConfigValidator, smsConfigSchema } from '../config/index.js';
|
||||
|
||||
export class SmsService {
|
||||
public platformServiceRef: any; // Platform service reference, using any to avoid dependency
|
||||
public projectinfo: plugins.projectinfo.ProjectInfo;
|
||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||
public config: ISmsConfig;
|
||||
|
||||
constructor(platformServiceRefArg: any, options: ISmsConfig) {
|
||||
this.platformServiceRef = platformServiceRefArg;
|
||||
|
||||
constructor(options: ISmsConfig) {
|
||||
// Validate and apply defaults to configuration
|
||||
const validationResult = ConfigValidator.validate(options, smsConfigSchema);
|
||||
|
||||
@ -27,7 +22,6 @@ export class SmsService {
|
||||
this.config = validationResult.config;
|
||||
|
||||
// Add router to platform service
|
||||
this.platformServiceRef.typedrouter.addTypedRouter(this.typedrouter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Minimal platform interfaces to support transition
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dummy placeholder for SzPlatformService interface
|
||||
*/
|
||||
export interface SzPlatformService {
|
||||
// Empty interface for now
|
||||
typedrouter?: any;
|
||||
}
|
||||
|
||||
// Create a default export with an object that has the SzPlatformService property
|
||||
const interfaces = {
|
||||
// Add a dummy constructor function that returns an empty object
|
||||
SzPlatformService: function() { return {}; }
|
||||
};
|
||||
|
||||
export default interfaces;
|
Reference in New Issue
Block a user