Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
018b499010 | |||
a4d79c2d01 | |||
90d3e75963 | |||
4887ec9d93 | |||
983e6cb623 | |||
e9b2ec0f59 | |||
c084de9c78 |
30
changelog.md
30
changelog.md
@ -1,5 +1,35 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-03-15 - 2.0.0 - BREAKING CHANGE(platformservice)
|
||||||
|
Remove deprecated AIBridge module and update email service to use the MTA connector; update dependency versions and adjust build scripts in package.json.
|
||||||
|
|
||||||
|
- Completely remove the aibridge module files (aibridge.classes.aibridge.ts, aibridge.classes.aibridgedb.ts, aibridge.classes.openaibridge.ts, aibridge.paths.ts, aibridge.plugins.ts, and index.ts) as they are no longer needed.
|
||||||
|
- Switch the email service from using MailgunConnector to the new MTA connector for sending emails.
|
||||||
|
- Update dependency versions for @serve.zone/interfaces, @tsclass/tsclass, letterxpress, and uuid in package.json.
|
||||||
|
- Enhance the build script in package.json and add pnpm configuration.
|
||||||
|
|
||||||
|
## 2025-03-15 - 1.1.2 - fix(mta)
|
||||||
|
Expose HttpResponse.statusCode and add explicit generic type annotations in DNSManager cache retrieval
|
||||||
|
|
||||||
|
- Changed HttpResponse.statusCode from private to public to allow external access and inspection
|
||||||
|
- Added explicit generic type parameters in getFromCache calls for lookupMx and lookupTxt to enhance type safety
|
||||||
|
|
||||||
|
## 2025-03-15 - 1.1.1 - fix(paths)
|
||||||
|
Update directory paths to use a dedicated 'data' directory and add ensureDirectories function for proper directory creation.
|
||||||
|
|
||||||
|
- Refactored ts/paths.ts to define a base data directory using process.cwd().
|
||||||
|
- Reorganized MTA directories (keys, dns, emails sent/received/failed, logs) under the data directory.
|
||||||
|
- Added ensureDirectories function to create missing directories at runtime.
|
||||||
|
|
||||||
|
## 2025-03-15 - 1.1.1 - fix(mta)
|
||||||
|
Refactor API Manager and DKIMCreator: remove Express dependency in favor of Node's native HTTP server, add an HttpResponse helper to improve request handling, update path and authentication logic, and expose previously private DKIMCreator methods for API access.
|
||||||
|
|
||||||
|
- Replaced Express-based middleware with native HTTP server handling, including request body parsing and CORS headers.
|
||||||
|
- Introduced an HttpResponse helper class to standardize response writing.
|
||||||
|
- Updated route matching, parameter extraction, and error handling within the API Manager.
|
||||||
|
- Modified DKIMCreator methods (createDKIMKeys, storeDKIMKeys, createAndStoreDKIMKeys, and getDNSRecordForDomain) from private to public for better API accessibility.
|
||||||
|
- Updated plugin imports to include the native HTTP module.
|
||||||
|
|
||||||
## 2025-03-15 - 1.1.0 - feat(mta)
|
## 2025-03-15 - 1.1.0 - feat(mta)
|
||||||
Enhance MTA service and SMTP server with robust session management, advanced email handling, and integrated API routes
|
Enhance MTA service and SMTP server with robust session management, advanced email handling, and integrated API routes
|
||||||
|
|
||||||
|
24
package.json
24
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/platformservice",
|
"name": "@serve.zone/platformservice",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.1.0",
|
"version": "2.0.0",
|
||||||
"description": "A multifaceted platform service handling mail, SMS, letter delivery, and AI services.",
|
"description": "A multifaceted platform service handling mail, SMS, letter delivery, and AI services.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
"typings": "dist_ts/index.d.ts",
|
"typings": "dist_ts/index.d.ts",
|
||||||
@ -12,6 +12,7 @@
|
|||||||
"test": "(tstest test/)",
|
"test": "(tstest test/)",
|
||||||
"start": "(node --max_old_space_size=250 ./cli.js)",
|
"start": "(node --max_old_space_size=250 ./cli.js)",
|
||||||
"startTs": "(node cli.ts.js)",
|
"startTs": "(node cli.ts.js)",
|
||||||
|
"build": "(tsbuild tsfolders --allowimplicitany)",
|
||||||
"localPublish": ""
|
"localPublish": ""
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -22,12 +23,11 @@
|
|||||||
"@push.rocks/tapbundle": "^5.0.22"
|
"@push.rocks/tapbundle": "^5.0.22"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@anthropic-ai/sdk": "^0.18.0",
|
|
||||||
"@api.global/typedrequest": "^3.0.19",
|
"@api.global/typedrequest": "^3.0.19",
|
||||||
"@api.global/typedserver": "^3.0.27",
|
"@api.global/typedserver": "^3.0.27",
|
||||||
"@api.global/typedsocket": "^3.0.0",
|
"@api.global/typedsocket": "^3.0.0",
|
||||||
"@apiclient.xyz/cloudflare": "^6.0.3",
|
"@apiclient.xyz/cloudflare": "^6.0.3",
|
||||||
"@apiclient.xyz/letterxpress": "^1.0.17",
|
"@apiclient.xyz/letterxpress": "^1.0.20",
|
||||||
"@push.rocks/projectinfo": "^5.0.1",
|
"@push.rocks/projectinfo": "^5.0.1",
|
||||||
"@push.rocks/qenv": "^6.0.5",
|
"@push.rocks/qenv": "^6.0.5",
|
||||||
"@push.rocks/smartdata": "^5.0.7",
|
"@push.rocks/smartdata": "^5.0.7",
|
||||||
@ -37,14 +37,15 @@
|
|||||||
"@push.rocks/smartpath": "^5.0.5",
|
"@push.rocks/smartpath": "^5.0.5",
|
||||||
"@push.rocks/smartpromise": "^4.0.3",
|
"@push.rocks/smartpromise": "^4.0.3",
|
||||||
"@push.rocks/smartrequest": "^2.0.21",
|
"@push.rocks/smartrequest": "^2.0.21",
|
||||||
|
"@push.rocks/smartrule": "^2.0.1",
|
||||||
"@push.rocks/smartrx": "^3.0.7",
|
"@push.rocks/smartrx": "^3.0.7",
|
||||||
"@push.rocks/smartstate": "^2.0.0",
|
"@push.rocks/smartstate": "^2.0.0",
|
||||||
"@serve.zone/interfaces": "^1.0.47",
|
"@serve.zone/interfaces": "^4.12.1",
|
||||||
"@tsclass/tsclass": "^4.0.52",
|
"@tsclass/tsclass": "^5.0.0",
|
||||||
|
"@types/mailparser": "^3.4.5",
|
||||||
"mailauth": "^4.6.5",
|
"mailauth": "^4.6.5",
|
||||||
"mailparser": "^3.6.9",
|
"mailparser": "^3.6.9",
|
||||||
"openai": "^4.29.2",
|
"uuid": "^11.1.0"
|
||||||
"uuid": "^9.0.1"
|
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"mail service",
|
"mail service",
|
||||||
@ -67,5 +68,12 @@
|
|||||||
"rule management",
|
"rule management",
|
||||||
"SMTP STARTTLS",
|
"SMTP STARTTLS",
|
||||||
"DNS management"
|
"DNS management"
|
||||||
]
|
],
|
||||||
|
"pnpm": {
|
||||||
|
"onlyBuiltDependencies": [
|
||||||
|
"esbuild",
|
||||||
|
"mongodb-memory-server",
|
||||||
|
"puppeteer"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
257
pnpm-lock.yaml
generated
257
pnpm-lock.yaml
generated
@ -8,9 +8,6 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@anthropic-ai/sdk':
|
|
||||||
specifier: ^0.18.0
|
|
||||||
version: 0.18.0
|
|
||||||
'@api.global/typedrequest':
|
'@api.global/typedrequest':
|
||||||
specifier: ^3.0.19
|
specifier: ^3.0.19
|
||||||
version: 3.1.10
|
version: 3.1.10
|
||||||
@ -24,8 +21,8 @@ importers:
|
|||||||
specifier: ^6.0.3
|
specifier: ^6.0.3
|
||||||
version: 6.0.3
|
version: 6.0.3
|
||||||
'@apiclient.xyz/letterxpress':
|
'@apiclient.xyz/letterxpress':
|
||||||
specifier: ^1.0.17
|
specifier: ^1.0.20
|
||||||
version: 1.0.19(typescript@5.7.3)
|
version: 1.0.20(typescript@5.7.3)
|
||||||
'@push.rocks/projectinfo':
|
'@push.rocks/projectinfo':
|
||||||
specifier: ^5.0.1
|
specifier: ^5.0.1
|
||||||
version: 5.0.2
|
version: 5.0.2
|
||||||
@ -53,6 +50,9 @@ importers:
|
|||||||
'@push.rocks/smartrequest':
|
'@push.rocks/smartrequest':
|
||||||
specifier: ^2.0.21
|
specifier: ^2.0.21
|
||||||
version: 2.0.23
|
version: 2.0.23
|
||||||
|
'@push.rocks/smartrule':
|
||||||
|
specifier: ^2.0.1
|
||||||
|
version: 2.0.1
|
||||||
'@push.rocks/smartrx':
|
'@push.rocks/smartrx':
|
||||||
specifier: ^3.0.7
|
specifier: ^3.0.7
|
||||||
version: 3.0.7
|
version: 3.0.7
|
||||||
@ -60,23 +60,23 @@ importers:
|
|||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.19
|
version: 2.0.19
|
||||||
'@serve.zone/interfaces':
|
'@serve.zone/interfaces':
|
||||||
specifier: ^1.0.47
|
specifier: ^4.12.1
|
||||||
version: 1.1.2
|
version: 4.12.1
|
||||||
'@tsclass/tsclass':
|
'@tsclass/tsclass':
|
||||||
specifier: ^4.0.52
|
specifier: ^5.0.0
|
||||||
version: 4.4.4
|
version: 5.0.0
|
||||||
|
'@types/mailparser':
|
||||||
|
specifier: ^3.4.5
|
||||||
|
version: 3.4.5
|
||||||
mailauth:
|
mailauth:
|
||||||
specifier: ^4.6.5
|
specifier: ^4.6.5
|
||||||
version: 4.8.2
|
version: 4.8.2
|
||||||
mailparser:
|
mailparser:
|
||||||
specifier: ^3.6.9
|
specifier: ^3.6.9
|
||||||
version: 3.7.2
|
version: 3.7.2
|
||||||
openai:
|
|
||||||
specifier: ^4.29.2
|
|
||||||
version: 4.87.3(ws@8.18.1)(zod@3.24.2)
|
|
||||||
uuid:
|
uuid:
|
||||||
specifier: ^9.0.1
|
specifier: ^11.1.0
|
||||||
version: 9.0.1
|
version: 11.1.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@git.zone/tsbuild':
|
'@git.zone/tsbuild':
|
||||||
specifier: ^2.1.17
|
specifier: ^2.1.17
|
||||||
@ -96,9 +96,6 @@ importers:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
'@anthropic-ai/sdk@0.18.0':
|
|
||||||
resolution: {integrity: sha512-3XsWEn/4nPGRd4AdSguugbSDFy6Z2AWTNOeI3iK+aV22+w23+vY9CEb3Hiy0kvKIQuxSmZz/+5WKC8nPWy8gVg==}
|
|
||||||
|
|
||||||
'@api.global/typedrequest-interfaces@2.0.2':
|
'@api.global/typedrequest-interfaces@2.0.2':
|
||||||
resolution: {integrity: sha512-D+mkr4IiUZ/eUgrdp5jXjBKOW/iuMcl0z2ZLQsLLypKX/psFGD3viZJ58FNRa+/1OSM38JS5wFyoWl8oPEFLrw==}
|
resolution: {integrity: sha512-D+mkr4IiUZ/eUgrdp5jXjBKOW/iuMcl0z2ZLQsLLypKX/psFGD3viZJ58FNRa+/1OSM38JS5wFyoWl8oPEFLrw==}
|
||||||
|
|
||||||
@ -117,8 +114,8 @@ packages:
|
|||||||
'@apiclient.xyz/cloudflare@6.0.3':
|
'@apiclient.xyz/cloudflare@6.0.3':
|
||||||
resolution: {integrity: sha512-NOPHFrKVxfkrRn9lVBLCdo0Ibm08PuaJe3A1K6TmXWxgbeJSqGkgVMGRwLilnol/062v6/1w2ZFO6f3fAxk5Gw==}
|
resolution: {integrity: sha512-NOPHFrKVxfkrRn9lVBLCdo0Ibm08PuaJe3A1K6TmXWxgbeJSqGkgVMGRwLilnol/062v6/1w2ZFO6f3fAxk5Gw==}
|
||||||
|
|
||||||
'@apiclient.xyz/letterxpress@1.0.19':
|
'@apiclient.xyz/letterxpress@1.0.20':
|
||||||
resolution: {integrity: sha512-+TGKZ0Xq3yoxXxLNn+WLvI0zKSRZPN0t9hpgNcqehVZ7srcRdDvVewsXOL6NJVXdUByy3yfP99yMaW+9rXYnkA==}
|
resolution: {integrity: sha512-dPkuUa+wi0IG/NmIuCfdz0VMYYFKNQyZSDVMJXAHZPu3YzHf2xHr45n3VxwP5wZur+NG/+6pneoOnyzUosIKmw==}
|
||||||
|
|
||||||
'@apiglobal/typedrequest-interfaces@2.0.1':
|
'@apiglobal/typedrequest-interfaces@2.0.1':
|
||||||
resolution: {integrity: sha512-Oi7pNU4vKo5UvcCJmqkH43Us237Ws/Pp/WDYnwnonRnTmIMd+6QjNfN/gXcPnP6tbamk8r8Xzcz9mgnSDM2ysw==}
|
resolution: {integrity: sha512-Oi7pNU4vKo5UvcCJmqkH43Us237Ws/Pp/WDYnwnonRnTmIMd+6QjNfN/gXcPnP6tbamk8r8Xzcz9mgnSDM2ysw==}
|
||||||
@ -823,9 +820,6 @@ packages:
|
|||||||
'@push.rocks/smartbucket@3.3.7':
|
'@push.rocks/smartbucket@3.3.7':
|
||||||
resolution: {integrity: sha512-RiOuEtwHJ+HFbV1nlZgh5VuMvP6PXElX6rVe7OSQsyNCBybRQa/d1qDic92+2Ejx852DGeHlyREELQCxd/a/7w==}
|
resolution: {integrity: sha512-RiOuEtwHJ+HFbV1nlZgh5VuMvP6PXElX6rVe7OSQsyNCBybRQa/d1qDic92+2Ejx852DGeHlyREELQCxd/a/7w==}
|
||||||
|
|
||||||
'@push.rocks/smartbuffer@2.0.3':
|
|
||||||
resolution: {integrity: sha512-5AcGbnuRSaF2Iyn3a1JNbNtDw0/xEaS6AOL2uTTtWqBJQQf088GFKS0/8Gn7TJiCnX+N+KOEGwi+5BAQKvcZeQ==}
|
|
||||||
|
|
||||||
'@push.rocks/smartbuffer@3.0.4':
|
'@push.rocks/smartbuffer@3.0.4':
|
||||||
resolution: {integrity: sha512-TLfhx/JD61YC8XGO9TI6Ux6US38R14HaIM84QT8hZZod8axfXrg+h8xA8tMUBpSV8PXsQy9LzxmOq0Il1fmDXw==}
|
resolution: {integrity: sha512-TLfhx/JD61YC8XGO9TI6Ux6US38R14HaIM84QT8hZZod8axfXrg+h8xA8tMUBpSV8PXsQy9LzxmOq0Il1fmDXw==}
|
||||||
|
|
||||||
@ -949,6 +943,9 @@ packages:
|
|||||||
'@push.rocks/smartrouter@1.3.2':
|
'@push.rocks/smartrouter@1.3.2':
|
||||||
resolution: {integrity: sha512-JtkxClN4CaHXMSeLDNvfWPwiVEPdEoQVSX2ee3gLgbXNO9dt9hvXdIhFrnFeLwyeA6M8nJdb9SqjrjZroYJsxw==}
|
resolution: {integrity: sha512-JtkxClN4CaHXMSeLDNvfWPwiVEPdEoQVSX2ee3gLgbXNO9dt9hvXdIhFrnFeLwyeA6M8nJdb9SqjrjZroYJsxw==}
|
||||||
|
|
||||||
|
'@push.rocks/smartrule@2.0.1':
|
||||||
|
resolution: {integrity: sha512-8oYEnS9z+NgCAcUtXPMguYyZpHqA/ROp0bxVQwUaHDwa3YzzA8jHIXvA94hk3sxvkk0xmIpp4UhBEelzIwwJow==}
|
||||||
|
|
||||||
'@push.rocks/smartrx@3.0.7':
|
'@push.rocks/smartrx@3.0.7':
|
||||||
resolution: {integrity: sha512-qCWy0s3RLAgGSnaw/Gu0BNaJ59CsI6RK5OJDCCqxc7P2X/S755vuLtnAR5/0dEjdhCHXHX9ytPZx+o9g/CNiyA==}
|
resolution: {integrity: sha512-qCWy0s3RLAgGSnaw/Gu0BNaJ59CsI6RK5OJDCCqxc7P2X/S755vuLtnAR5/0dEjdhCHXHX9ytPZx+o9g/CNiyA==}
|
||||||
|
|
||||||
@ -1149,8 +1146,8 @@ packages:
|
|||||||
'@selderee/plugin-htmlparser2@0.11.0':
|
'@selderee/plugin-htmlparser2@0.11.0':
|
||||||
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
|
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
|
||||||
|
|
||||||
'@serve.zone/interfaces@1.1.2':
|
'@serve.zone/interfaces@4.12.1':
|
||||||
resolution: {integrity: sha512-ajHArBX7iJcUPYjYoO1rx78Mz5IzHIKzxt34kWKk5fSDrOkAoNwk0P/zpm0JZ6ClQy7/maOHTiSsNpdbBNvhQg==}
|
resolution: {integrity: sha512-M1ZfXmhlo3OyTtmhYWmQIVzd/AwTM5ANMHSuoZDuddq7K0UwmdYJNjRUK7nR1g4PucwFzpbMgUuXQSkhBJfvAw==}
|
||||||
|
|
||||||
'@sideway/address@4.1.5':
|
'@sideway/address@4.1.5':
|
||||||
resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
|
resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
|
||||||
@ -1405,6 +1402,9 @@ packages:
|
|||||||
'@tsclass/tsclass@4.4.4':
|
'@tsclass/tsclass@4.4.4':
|
||||||
resolution: {integrity: sha512-YZOAF+u+r4u5rCev2uUd1KBTBdfyFdtDmcv4wuN+864lMccbdfRICR3SlJwCfYS1lbeV3QNLYGD30wjRXgvCJA==}
|
resolution: {integrity: sha512-YZOAF+u+r4u5rCev2uUd1KBTBdfyFdtDmcv4wuN+864lMccbdfRICR3SlJwCfYS1lbeV3QNLYGD30wjRXgvCJA==}
|
||||||
|
|
||||||
|
'@tsclass/tsclass@5.0.0':
|
||||||
|
resolution: {integrity: sha512-2X66VCk0Oe1L01j6GQHC6F9Gj7lpZPPSUTDNax7e29lm4OqBTyAzTR3ePR8coSbWBwsmRV8awLRSrSI+swlqWA==}
|
||||||
|
|
||||||
'@types/accepts@1.3.7':
|
'@types/accepts@1.3.7':
|
||||||
resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
|
resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
|
||||||
|
|
||||||
@ -1535,6 +1535,9 @@ packages:
|
|||||||
'@types/koa@2.15.0':
|
'@types/koa@2.15.0':
|
||||||
resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==}
|
resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==}
|
||||||
|
|
||||||
|
'@types/mailparser@3.4.5':
|
||||||
|
resolution: {integrity: sha512-EPERBp7fLeFZh7tS2X36MF7jawUx3Y6/0rXciZah3CTYgwLi3e0kpGUJ6FOmUabgzis/U1g+3/JzrVWbWIOGjg==}
|
||||||
|
|
||||||
'@types/mdast@4.0.4':
|
'@types/mdast@4.0.4':
|
||||||
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
|
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
|
||||||
|
|
||||||
@ -1553,15 +1556,9 @@ packages:
|
|||||||
'@types/ms@2.1.0':
|
'@types/ms@2.1.0':
|
||||||
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
|
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
|
||||||
|
|
||||||
'@types/node-fetch@2.6.12':
|
|
||||||
resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==}
|
|
||||||
|
|
||||||
'@types/node-forge@1.3.11':
|
'@types/node-forge@1.3.11':
|
||||||
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
|
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
|
||||||
|
|
||||||
'@types/node@18.19.80':
|
|
||||||
resolution: {integrity: sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==}
|
|
||||||
|
|
||||||
'@types/node@22.13.10':
|
'@types/node@22.13.10':
|
||||||
resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==}
|
resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==}
|
||||||
|
|
||||||
@ -1687,10 +1684,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-84E1025aUSjvZU1j17eCTwV7m5Zg3cZHErV3+CaJM9JPCesZwLraIa0ONIQ9w4KLgcDgJFw9UnJ0LbFf42h6tg==}
|
resolution: {integrity: sha512-84E1025aUSjvZU1j17eCTwV7m5Zg3cZHErV3+CaJM9JPCesZwLraIa0ONIQ9w4KLgcDgJFw9UnJ0LbFf42h6tg==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
abort-controller@3.0.0:
|
|
||||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
|
||||||
engines: {node: '>=6.5'}
|
|
||||||
|
|
||||||
accepts@1.3.8:
|
accepts@1.3.8:
|
||||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@ -1815,9 +1808,6 @@ packages:
|
|||||||
bare-events:
|
bare-events:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
base-64@0.1.0:
|
|
||||||
resolution: {integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==}
|
|
||||||
|
|
||||||
base64-js@1.5.1:
|
base64-js@1.5.1:
|
||||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||||
|
|
||||||
@ -1948,9 +1938,6 @@ packages:
|
|||||||
character-entities@2.0.2:
|
character-entities@2.0.2:
|
||||||
resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
|
resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
|
||||||
|
|
||||||
charenc@0.0.2:
|
|
||||||
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
|
|
||||||
|
|
||||||
chokidar@4.0.3:
|
chokidar@4.0.3:
|
||||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||||
engines: {node: '>= 14.16.0'}
|
engines: {node: '>= 14.16.0'}
|
||||||
@ -2105,9 +2092,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
crypt@0.0.2:
|
|
||||||
resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
|
|
||||||
|
|
||||||
crypto-random-string@5.0.0:
|
crypto-random-string@5.0.0:
|
||||||
resolution: {integrity: sha512-KWjTXWwxFd6a94m5CdRGW/t82Tr8DoBc9dNnPCAbFI1EBweN6v1tv8y4Y1m7ndkp/nkIBRxUxAzpaBnR2k3bcQ==}
|
resolution: {integrity: sha512-KWjTXWwxFd6a94m5CdRGW/t82Tr8DoBc9dNnPCAbFI1EBweN6v1tv8y4Y1m7ndkp/nkIBRxUxAzpaBnR2k3bcQ==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
@ -2246,9 +2230,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
|
resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
|
||||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
digest-fetch@1.3.0:
|
|
||||||
resolution: {integrity: sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==}
|
|
||||||
|
|
||||||
dijkstrajs@1.0.3:
|
dijkstrajs@1.0.3:
|
||||||
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
|
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
|
||||||
|
|
||||||
@ -2423,10 +2404,6 @@ packages:
|
|||||||
resolution: {integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=}
|
resolution: {integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
event-target-shim@5.0.1:
|
|
||||||
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
|
|
||||||
engines: {node: '>=6'}
|
|
||||||
|
|
||||||
eventemitter3@4.0.7:
|
eventemitter3@4.0.7:
|
||||||
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
||||||
|
|
||||||
@ -2551,9 +2528,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
form-data-encoder@1.7.2:
|
|
||||||
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
|
|
||||||
|
|
||||||
form-data-encoder@2.1.4:
|
form-data-encoder@2.1.4:
|
||||||
resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==}
|
resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==}
|
||||||
engines: {node: '>= 14.17'}
|
engines: {node: '>= 14.17'}
|
||||||
@ -2566,10 +2540,6 @@ packages:
|
|||||||
resolution: {integrity: sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=}
|
resolution: {integrity: sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=}
|
||||||
engines: {node: '>=0.4.x'}
|
engines: {node: '>=0.4.x'}
|
||||||
|
|
||||||
formdata-node@4.4.1:
|
|
||||||
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
|
|
||||||
engines: {node: '>= 12.20'}
|
|
||||||
|
|
||||||
forwarded@0.2.0:
|
forwarded@0.2.0:
|
||||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@ -2861,9 +2831,6 @@ packages:
|
|||||||
is-arrayish@0.3.2:
|
is-arrayish@0.3.2:
|
||||||
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
|
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
|
||||||
|
|
||||||
is-buffer@1.1.6:
|
|
||||||
resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
|
|
||||||
|
|
||||||
is-deflate@1.0.0:
|
is-deflate@1.0.0:
|
||||||
resolution: {integrity: sha1-yGKQHDwWH7CdrHzcfnhPgOmPLxQ=}
|
resolution: {integrity: sha1-yGKQHDwWH7CdrHzcfnhPgOmPLxQ=}
|
||||||
|
|
||||||
@ -3237,9 +3204,6 @@ packages:
|
|||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
md5@2.3.0:
|
|
||||||
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
|
|
||||||
|
|
||||||
mdast-util-find-and-replace@3.0.2:
|
mdast-util-find-and-replace@3.0.2:
|
||||||
resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
|
resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
|
||||||
|
|
||||||
@ -3545,19 +3509,6 @@ packages:
|
|||||||
no-case@2.3.2:
|
no-case@2.3.2:
|
||||||
resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==}
|
resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==}
|
||||||
|
|
||||||
node-domexception@1.0.0:
|
|
||||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
|
||||||
engines: {node: '>=10.5.0'}
|
|
||||||
|
|
||||||
node-fetch@2.7.0:
|
|
||||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
|
||||||
engines: {node: 4.x || >=6.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
encoding: ^0.1.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
encoding:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
node-forge@1.3.1:
|
node-forge@1.3.1:
|
||||||
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
|
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
|
||||||
engines: {node: '>= 6.13.0'}
|
engines: {node: '>= 6.13.0'}
|
||||||
@ -3618,18 +3569,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
|
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
openai@4.87.3:
|
|
||||||
resolution: {integrity: sha512-d2D54fzMuBYTxMW8wcNmhT1rYKcTfMJ8t+4KjH2KtvYenygITiGBgHoIrzHwnDQWW+C5oCA+ikIR2jgPCFqcKQ==}
|
|
||||||
hasBin: true
|
|
||||||
peerDependencies:
|
|
||||||
ws: ^8.18.0
|
|
||||||
zod: ^3.23.8
|
|
||||||
peerDependenciesMeta:
|
|
||||||
ws:
|
|
||||||
optional: true
|
|
||||||
zod:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
p-cancelable@3.0.0:
|
p-cancelable@3.0.0:
|
||||||
resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
|
resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
|
||||||
engines: {node: '>=12.20'}
|
engines: {node: '>=12.20'}
|
||||||
@ -4303,9 +4242,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==}
|
resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
|
|
||||||
tr46@0.0.3:
|
|
||||||
resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=}
|
|
||||||
|
|
||||||
tr46@3.0.0:
|
tr46@3.0.0:
|
||||||
resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==}
|
resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -4385,9 +4321,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==}
|
resolution: {integrity: sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
undici-types@5.26.5:
|
|
||||||
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
|
||||||
|
|
||||||
undici-types@6.20.0:
|
undici-types@6.20.0:
|
||||||
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
|
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
|
||||||
|
|
||||||
@ -4442,6 +4375,10 @@ packages:
|
|||||||
resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=}
|
resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=}
|
||||||
engines: {node: '>= 0.4.0'}
|
engines: {node: '>= 0.4.0'}
|
||||||
|
|
||||||
|
uuid@11.1.0:
|
||||||
|
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
uuid@9.0.1:
|
uuid@9.0.1:
|
||||||
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@ -4456,17 +4393,6 @@ packages:
|
|||||||
vfile@6.0.3:
|
vfile@6.0.3:
|
||||||
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
|
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
|
||||||
|
|
||||||
web-streams-polyfill@3.3.3:
|
|
||||||
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
|
|
||||||
engines: {node: '>= 8'}
|
|
||||||
|
|
||||||
web-streams-polyfill@4.0.0-beta.3:
|
|
||||||
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
|
|
||||||
engines: {node: '>= 14'}
|
|
||||||
|
|
||||||
webidl-conversions@3.0.1:
|
|
||||||
resolution: {integrity: sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=}
|
|
||||||
|
|
||||||
webidl-conversions@7.0.0:
|
webidl-conversions@7.0.0:
|
||||||
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -4483,9 +4409,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
|
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
|
||||||
resolution: {integrity: sha1-lmRU6HZUYuN2RNNib2dCzotwll0=}
|
|
||||||
|
|
||||||
which-module@2.0.1:
|
which-module@2.0.1:
|
||||||
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
||||||
|
|
||||||
@ -4626,20 +4549,6 @@ packages:
|
|||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
'@anthropic-ai/sdk@0.18.0':
|
|
||||||
dependencies:
|
|
||||||
'@types/node': 18.19.80
|
|
||||||
'@types/node-fetch': 2.6.12
|
|
||||||
abort-controller: 3.0.0
|
|
||||||
agentkeepalive: 4.6.0
|
|
||||||
digest-fetch: 1.3.0
|
|
||||||
form-data-encoder: 1.7.2
|
|
||||||
formdata-node: 4.4.1
|
|
||||||
node-fetch: 2.7.0
|
|
||||||
web-streams-polyfill: 3.3.3
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- encoding
|
|
||||||
|
|
||||||
'@api.global/typedrequest-interfaces@2.0.2': {}
|
'@api.global/typedrequest-interfaces@2.0.2': {}
|
||||||
|
|
||||||
'@api.global/typedrequest-interfaces@3.0.19': {}
|
'@api.global/typedrequest-interfaces@3.0.19': {}
|
||||||
@ -4730,15 +4639,15 @@ snapshots:
|
|||||||
'@pushrocks/smartstring': 4.0.7
|
'@pushrocks/smartstring': 4.0.7
|
||||||
'@tsclass/tsclass': 4.4.4
|
'@tsclass/tsclass': 4.4.4
|
||||||
|
|
||||||
'@apiclient.xyz/letterxpress@1.0.19(typescript@5.7.3)':
|
'@apiclient.xyz/letterxpress@1.0.20(typescript@5.7.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@design.estate/dees-document': 1.6.9(typescript@5.7.3)
|
'@design.estate/dees-document': 1.6.9(typescript@5.7.3)
|
||||||
'@push.rocks/smartbuffer': 2.0.3
|
'@push.rocks/smartbuffer': 3.0.4
|
||||||
'@push.rocks/smarthash': 3.0.4
|
'@push.rocks/smarthash': 3.0.4
|
||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
'@push.rocks/smartrequest': 2.0.23
|
'@push.rocks/smartrequest': 2.0.23
|
||||||
'@push.rocks/smartrx': 3.0.7
|
'@push.rocks/smartrx': 3.0.7
|
||||||
'@tsclass/tsclass': 4.4.4
|
'@tsclass/tsclass': 5.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@nuxt/kit'
|
- '@nuxt/kit'
|
||||||
- bare-buffer
|
- bare-buffer
|
||||||
@ -5654,8 +5563,10 @@ snapshots:
|
|||||||
'@push.rocks/taskbuffer': 3.1.7
|
'@push.rocks/taskbuffer': 3.1.7
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@nuxt/kit'
|
- '@nuxt/kit'
|
||||||
|
- bufferutil
|
||||||
- react
|
- react
|
||||||
- supports-color
|
- supports-color
|
||||||
|
- utf-8-validate
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
'@hapi/bourne@3.0.0': {}
|
'@hapi/bourne@3.0.0': {}
|
||||||
@ -5961,10 +5872,6 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- aws-crt
|
- aws-crt
|
||||||
|
|
||||||
'@push.rocks/smartbuffer@2.0.3':
|
|
||||||
dependencies:
|
|
||||||
uint8array-extras: 1.4.0
|
|
||||||
|
|
||||||
'@push.rocks/smartbuffer@3.0.4':
|
'@push.rocks/smartbuffer@3.0.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
uint8array-extras: 1.4.0
|
uint8array-extras: 1.4.0
|
||||||
@ -6299,6 +6206,8 @@ snapshots:
|
|||||||
'@push.rocks/smartrx': 3.0.7
|
'@push.rocks/smartrx': 3.0.7
|
||||||
path-to-regexp: 8.2.0
|
path-to-regexp: 8.2.0
|
||||||
|
|
||||||
|
'@push.rocks/smartrule@2.0.1': {}
|
||||||
|
|
||||||
'@push.rocks/smartrx@3.0.7':
|
'@push.rocks/smartrx@3.0.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
@ -6702,7 +6611,7 @@ snapshots:
|
|||||||
domhandler: 5.0.3
|
domhandler: 5.0.3
|
||||||
selderee: 0.11.0
|
selderee: 0.11.0
|
||||||
|
|
||||||
'@serve.zone/interfaces@1.1.2':
|
'@serve.zone/interfaces@4.12.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@api.global/typedrequest-interfaces': 3.0.19
|
'@api.global/typedrequest-interfaces': 3.0.19
|
||||||
'@push.rocks/smartlog-interfaces': 3.0.2
|
'@push.rocks/smartlog-interfaces': 3.0.2
|
||||||
@ -7075,6 +6984,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
type-fest: 4.37.0
|
type-fest: 4.37.0
|
||||||
|
|
||||||
|
'@tsclass/tsclass@5.0.0':
|
||||||
|
dependencies:
|
||||||
|
type-fest: 4.37.0
|
||||||
|
|
||||||
'@types/accepts@1.3.7':
|
'@types/accepts@1.3.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.13.10
|
'@types/node': 22.13.10
|
||||||
@ -7247,6 +7160,11 @@ snapshots:
|
|||||||
'@types/koa-compose': 3.2.8
|
'@types/koa-compose': 3.2.8
|
||||||
'@types/node': 22.13.10
|
'@types/node': 22.13.10
|
||||||
|
|
||||||
|
'@types/mailparser@3.4.5':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.13.10
|
||||||
|
iconv-lite: 0.6.3
|
||||||
|
|
||||||
'@types/mdast@4.0.4':
|
'@types/mdast@4.0.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 3.0.3
|
'@types/unist': 3.0.3
|
||||||
@ -7261,19 +7179,10 @@ snapshots:
|
|||||||
|
|
||||||
'@types/ms@2.1.0': {}
|
'@types/ms@2.1.0': {}
|
||||||
|
|
||||||
'@types/node-fetch@2.6.12':
|
|
||||||
dependencies:
|
|
||||||
'@types/node': 18.19.80
|
|
||||||
form-data: 4.0.2
|
|
||||||
|
|
||||||
'@types/node-forge@1.3.11':
|
'@types/node-forge@1.3.11':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.13.10
|
'@types/node': 22.13.10
|
||||||
|
|
||||||
'@types/node@18.19.80':
|
|
||||||
dependencies:
|
|
||||||
undici-types: 5.26.5
|
|
||||||
|
|
||||||
'@types/node@22.13.10':
|
'@types/node@22.13.10':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.20.0
|
undici-types: 6.20.0
|
||||||
@ -7458,10 +7367,6 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
|
||||||
abort-controller@3.0.0:
|
|
||||||
dependencies:
|
|
||||||
event-target-shim: 5.0.1
|
|
||||||
|
|
||||||
accepts@1.3.8:
|
accepts@1.3.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
@ -7571,8 +7476,6 @@ snapshots:
|
|||||||
bare-events: 2.5.4
|
bare-events: 2.5.4
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
base-64@0.1.0: {}
|
|
||||||
|
|
||||||
base64-js@1.5.1: {}
|
base64-js@1.5.1: {}
|
||||||
|
|
||||||
base64id@2.0.0: {}
|
base64id@2.0.0: {}
|
||||||
@ -7724,8 +7627,6 @@ snapshots:
|
|||||||
|
|
||||||
character-entities@2.0.2: {}
|
character-entities@2.0.2: {}
|
||||||
|
|
||||||
charenc@0.0.2: {}
|
|
||||||
|
|
||||||
chokidar@4.0.3:
|
chokidar@4.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
readdirp: 4.1.2
|
readdirp: 4.1.2
|
||||||
@ -7871,8 +7772,6 @@ snapshots:
|
|||||||
shebang-command: 2.0.0
|
shebang-command: 2.0.0
|
||||||
which: 2.0.2
|
which: 2.0.2
|
||||||
|
|
||||||
crypt@0.0.2: {}
|
|
||||||
|
|
||||||
crypto-random-string@5.0.0:
|
crypto-random-string@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
type-fest: 2.19.0
|
type-fest: 2.19.0
|
||||||
@ -7969,11 +7868,6 @@ snapshots:
|
|||||||
|
|
||||||
diff-sequences@29.6.3: {}
|
diff-sequences@29.6.3: {}
|
||||||
|
|
||||||
digest-fetch@1.3.0:
|
|
||||||
dependencies:
|
|
||||||
base-64: 0.1.0
|
|
||||||
md5: 2.3.0
|
|
||||||
|
|
||||||
dijkstrajs@1.0.3: {}
|
dijkstrajs@1.0.3: {}
|
||||||
|
|
||||||
dir-glob@3.0.1:
|
dir-glob@3.0.1:
|
||||||
@ -8186,8 +8080,6 @@ snapshots:
|
|||||||
|
|
||||||
etag@1.8.1: {}
|
etag@1.8.1: {}
|
||||||
|
|
||||||
event-target-shim@5.0.1: {}
|
|
||||||
|
|
||||||
eventemitter3@4.0.7: {}
|
eventemitter3@4.0.7: {}
|
||||||
|
|
||||||
execa@5.1.1:
|
execa@5.1.1:
|
||||||
@ -8368,8 +8260,6 @@ snapshots:
|
|||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
signal-exit: 4.1.0
|
signal-exit: 4.1.0
|
||||||
|
|
||||||
form-data-encoder@1.7.2: {}
|
|
||||||
|
|
||||||
form-data-encoder@2.1.4: {}
|
form-data-encoder@2.1.4: {}
|
||||||
|
|
||||||
form-data@4.0.2:
|
form-data@4.0.2:
|
||||||
@ -8381,11 +8271,6 @@ snapshots:
|
|||||||
|
|
||||||
format@0.2.2: {}
|
format@0.2.2: {}
|
||||||
|
|
||||||
formdata-node@4.4.1:
|
|
||||||
dependencies:
|
|
||||||
node-domexception: 1.0.0
|
|
||||||
web-streams-polyfill: 4.0.0-beta.3
|
|
||||||
|
|
||||||
forwarded@0.2.0: {}
|
forwarded@0.2.0: {}
|
||||||
|
|
||||||
fresh@0.5.2: {}
|
fresh@0.5.2: {}
|
||||||
@ -8748,8 +8633,6 @@ snapshots:
|
|||||||
|
|
||||||
is-arrayish@0.3.2: {}
|
is-arrayish@0.3.2: {}
|
||||||
|
|
||||||
is-buffer@1.1.6: {}
|
|
||||||
|
|
||||||
is-deflate@1.0.0: {}
|
is-deflate@1.0.0: {}
|
||||||
|
|
||||||
is-docker@2.2.1: {}
|
is-docker@2.2.1: {}
|
||||||
@ -9165,12 +9048,6 @@ snapshots:
|
|||||||
|
|
||||||
md5-file@5.0.0: {}
|
md5-file@5.0.0: {}
|
||||||
|
|
||||||
md5@2.3.0:
|
|
||||||
dependencies:
|
|
||||||
charenc: 0.0.2
|
|
||||||
crypt: 0.0.2
|
|
||||||
is-buffer: 1.1.6
|
|
||||||
|
|
||||||
mdast-util-find-and-replace@3.0.2:
|
mdast-util-find-and-replace@3.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.4
|
'@types/mdast': 4.0.4
|
||||||
@ -9645,12 +9522,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
lower-case: 1.1.4
|
lower-case: 1.1.4
|
||||||
|
|
||||||
node-domexception@1.0.0: {}
|
|
||||||
|
|
||||||
node-fetch@2.7.0:
|
|
||||||
dependencies:
|
|
||||||
whatwg-url: 5.0.0
|
|
||||||
|
|
||||||
node-forge@1.3.1: {}
|
node-forge@1.3.1: {}
|
||||||
|
|
||||||
nodemailer@6.9.16: {}
|
nodemailer@6.9.16: {}
|
||||||
@ -9699,21 +9570,6 @@ snapshots:
|
|||||||
is-docker: 2.2.1
|
is-docker: 2.2.1
|
||||||
is-wsl: 2.2.0
|
is-wsl: 2.2.0
|
||||||
|
|
||||||
openai@4.87.3(ws@8.18.1)(zod@3.24.2):
|
|
||||||
dependencies:
|
|
||||||
'@types/node': 18.19.80
|
|
||||||
'@types/node-fetch': 2.6.12
|
|
||||||
abort-controller: 3.0.0
|
|
||||||
agentkeepalive: 4.6.0
|
|
||||||
form-data-encoder: 1.7.2
|
|
||||||
formdata-node: 4.4.1
|
|
||||||
node-fetch: 2.7.0
|
|
||||||
optionalDependencies:
|
|
||||||
ws: 8.18.1
|
|
||||||
zod: 3.24.2
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- encoding
|
|
||||||
|
|
||||||
p-cancelable@3.0.0: {}
|
p-cancelable@3.0.0: {}
|
||||||
|
|
||||||
p-event@4.2.0:
|
p-event@4.2.0:
|
||||||
@ -10509,8 +10365,6 @@ snapshots:
|
|||||||
'@tokenizer/token': 0.3.0
|
'@tokenizer/token': 0.3.0
|
||||||
ieee754: 1.2.1
|
ieee754: 1.2.1
|
||||||
|
|
||||||
tr46@0.0.3: {}
|
|
||||||
|
|
||||||
tr46@3.0.0:
|
tr46@3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.1
|
punycode: 2.3.1
|
||||||
@ -10567,8 +10421,6 @@ snapshots:
|
|||||||
|
|
||||||
uint8array-extras@1.4.0: {}
|
uint8array-extras@1.4.0: {}
|
||||||
|
|
||||||
undici-types@5.26.5: {}
|
|
||||||
|
|
||||||
undici-types@6.20.0: {}
|
undici-types@6.20.0: {}
|
||||||
|
|
||||||
undici@5.28.4:
|
undici@5.28.4:
|
||||||
@ -10627,6 +10479,8 @@ snapshots:
|
|||||||
|
|
||||||
utils-merge@1.0.1: {}
|
utils-merge@1.0.1: {}
|
||||||
|
|
||||||
|
uuid@11.1.0: {}
|
||||||
|
|
||||||
uuid@9.0.1: {}
|
uuid@9.0.1: {}
|
||||||
|
|
||||||
vary@1.1.2: {}
|
vary@1.1.2: {}
|
||||||
@ -10641,12 +10495,6 @@ snapshots:
|
|||||||
'@types/unist': 3.0.3
|
'@types/unist': 3.0.3
|
||||||
vfile-message: 4.0.2
|
vfile-message: 4.0.2
|
||||||
|
|
||||||
web-streams-polyfill@3.3.3: {}
|
|
||||||
|
|
||||||
web-streams-polyfill@4.0.0-beta.3: {}
|
|
||||||
|
|
||||||
webidl-conversions@3.0.1: {}
|
|
||||||
|
|
||||||
webidl-conversions@7.0.0: {}
|
webidl-conversions@7.0.0: {}
|
||||||
|
|
||||||
whatwg-mimetype@3.0.0: {}
|
whatwg-mimetype@3.0.0: {}
|
||||||
@ -10661,11 +10509,6 @@ snapshots:
|
|||||||
tr46: 5.1.0
|
tr46: 5.1.0
|
||||||
webidl-conversions: 7.0.0
|
webidl-conversions: 7.0.0
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
|
||||||
dependencies:
|
|
||||||
tr46: 0.0.3
|
|
||||||
webidl-conversions: 3.0.1
|
|
||||||
|
|
||||||
which-module@2.0.1: {}
|
which-module@2.0.1: {}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/platformservice',
|
name: '@serve.zone/platformservice',
|
||||||
version: '1.1.0',
|
version: '2.0.0',
|
||||||
description: 'A multifaceted platform service handling mail, SMS, letter delivery, and AI services.'
|
description: 'A multifaceted platform service handling mail, SMS, letter delivery, and AI services.'
|
||||||
}
|
}
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
import * as plugins from './aibridge.plugins.js';
|
|
||||||
import * as paths from './aibridge.paths.js';
|
|
||||||
import { AiBridgeDb } from './aibridge.classes.aibridgedb.js';
|
|
||||||
import { OpenAiBridge } from './aibridge.classes.openaibridge.js';
|
|
||||||
|
|
||||||
export class AiBridge {
|
|
||||||
public projectinfo: plugins.projectinfo.ProjectInfo;
|
|
||||||
public serverInstance: plugins.loleServiceserver.ServiceServer;
|
|
||||||
public serviceQenv = new plugins.qenv.Qenv('./', './.nogit');
|
|
||||||
public aibridgeDb: AiBridgeDb;
|
|
||||||
|
|
||||||
public openAiBridge: OpenAiBridge;
|
|
||||||
|
|
||||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
||||||
|
|
||||||
public async start() {
|
|
||||||
this.aibridgeDb = new AiBridgeDb(this);
|
|
||||||
await this.aibridgeDb.start();
|
|
||||||
this.projectinfo = new plugins.projectinfo.ProjectInfo(paths.packageDir);
|
|
||||||
this.openAiBridge = new OpenAiBridge(this);
|
|
||||||
await this.openAiBridge.start();
|
|
||||||
|
|
||||||
// server
|
|
||||||
this.serverInstance = new plugins.loleServiceserver.ServiceServer({
|
|
||||||
serviceDomain: 'aibridge.lossless.one',
|
|
||||||
serviceName: 'aibridge',
|
|
||||||
serviceVersion: this.projectinfo.npm.version,
|
|
||||||
addCustomRoutes: async (serverArg) => {
|
|
||||||
// any custom route configs go here
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// lets implemenet the actual typedrequest functions
|
|
||||||
this.typedrouter.addTypedHandler<plugins.lointAiBridge.requests.IReq_Chat>(new plugins.typedrequest.TypedHandler('chat', async reqArg => {
|
|
||||||
const resultChat = await this.openAiBridge.chat(reqArg.chat.systemMessage, reqArg.chat.messages[reqArg.chat.messages.length - 1].content, reqArg.chat.messages);
|
|
||||||
return {
|
|
||||||
chat: reqArg.chat,
|
|
||||||
latestMessage: resultChat.message.content,
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
await this.serverInstance.start();
|
|
||||||
this.serverInstance.typedServer.typedrouter.addTypedRouter(this.typedrouter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async stop() {
|
|
||||||
await this.serverInstance.stop();
|
|
||||||
await this.aibridgeDb.stop();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import * as plugins from './aibridge.plugins.js';
|
|
||||||
import { AiBridge } from './aibridge.classes.aibridge.js';
|
|
||||||
|
|
||||||
export class AiBridgeDb {
|
|
||||||
public smartdataDb: plugins.smartdata.SmartdataDb;
|
|
||||||
public aibridgeRef: AiBridge;
|
|
||||||
|
|
||||||
constructor(aibridgeRefArg: AiBridge) {
|
|
||||||
this.aibridgeRef = aibridgeRefArg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async start() {
|
|
||||||
this.smartdataDb = new plugins.smartdata.SmartdataDb({
|
|
||||||
mongoDbUser: await this.aibridgeRef.serviceQenv.getEnvVarOnDemand('MONGO_DB_USER'),
|
|
||||||
mongoDbName: await this.aibridgeRef.serviceQenv.getEnvVarOnDemand('MONGO_DB_NAME'),
|
|
||||||
mongoDbPass: await this.aibridgeRef.serviceQenv.getEnvVarOnDemand('MONGO_DB_PASS'),
|
|
||||||
mongoDbUrl: await this.aibridgeRef.serviceQenv.getEnvVarOnDemand('MONGO_DB_URL'),
|
|
||||||
});
|
|
||||||
await this.smartdataDb.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async stop() {
|
|
||||||
await this.smartdataDb.close();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
import { AiBridge } from './aibridge.classes.aibridge.js';
|
|
||||||
import * as plugins from './aibridge.plugins.js';
|
|
||||||
import * as paths from './aibridge.paths.js';
|
|
||||||
|
|
||||||
export class OpenAiBridge {
|
|
||||||
public aiBridgeRef: AiBridge;
|
|
||||||
public openAiApiClient: plugins.openai.default;
|
|
||||||
constructor(aiBridgeRefArg: AiBridge) {
|
|
||||||
this.aiBridgeRef = aiBridgeRefArg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async start() {
|
|
||||||
const openAiToken = await this.aiBridgeRef.serviceQenv.getEnvVarOnDemand('OPENAI_TOKEN');
|
|
||||||
this.openAiApiClient = new plugins.openai.default({
|
|
||||||
apiKey: openAiToken,
|
|
||||||
dangerouslyAllowBrowser: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async stop() {}
|
|
||||||
|
|
||||||
public async chat(
|
|
||||||
systemMessage: string,
|
|
||||||
userMessage: string,
|
|
||||||
messageHistory: {
|
|
||||||
role: 'assistant' | 'user';
|
|
||||||
content: string;
|
|
||||||
}[]
|
|
||||||
) {
|
|
||||||
const result = await this.openAiApiClient.chat.completions.create({
|
|
||||||
model: 'gpt-4-turbo-preview',
|
|
||||||
messages: [
|
|
||||||
{ role: 'system', content: systemMessage },
|
|
||||||
...messageHistory,
|
|
||||||
{ role: 'user', content: userMessage },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
message: result.choices[0].message,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async audio(messageArg: string) {
|
|
||||||
const done = plugins.smartpromise.defer();
|
|
||||||
const result = await this.openAiApiClient.audio.speech.create({
|
|
||||||
model: 'tts-1-hd',
|
|
||||||
input: messageArg,
|
|
||||||
voice: 'nova',
|
|
||||||
response_format: 'mp3',
|
|
||||||
speed: 1,
|
|
||||||
});
|
|
||||||
const stream = result.body.pipe(plugins.smartfile.fsStream.createWriteStream(plugins.path.join(paths.nogitDir, 'output.mp3')));
|
|
||||||
stream.on('finish', () => {
|
|
||||||
done.resolve();
|
|
||||||
});
|
|
||||||
return done.promise;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
import * as plugins from './aibridge.plugins.js';
|
|
||||||
|
|
||||||
export const packageDir = plugins.path.join(
|
|
||||||
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
|
|
||||||
'../'
|
|
||||||
);
|
|
||||||
|
|
||||||
export const assetsDir = plugins.path.join(
|
|
||||||
packageDir,
|
|
||||||
'./assets/'
|
|
||||||
);
|
|
||||||
|
|
||||||
export const nogitDir = plugins.path.join(
|
|
||||||
packageDir,
|
|
||||||
'./.nogit/'
|
|
||||||
);
|
|
@ -1,32 +0,0 @@
|
|||||||
// node native
|
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
export { path };
|
|
||||||
|
|
||||||
// @losslessone_private scope
|
|
||||||
import * as loleServiceserver from '@losslessone_private/lole-serviceserver';
|
|
||||||
import * as lointAiBridge from '@losslessone_private/loint-aibridge';
|
|
||||||
|
|
||||||
export { loleServiceserver, lointAiBridge };
|
|
||||||
|
|
||||||
// apiglobal scope
|
|
||||||
import * as typedrequest from '@api.global/typedrequest';
|
|
||||||
|
|
||||||
export {
|
|
||||||
typedrequest,
|
|
||||||
}
|
|
||||||
|
|
||||||
// pushrocks scope
|
|
||||||
import * as projectinfo from '@push.rocks/projectinfo';
|
|
||||||
import * as qenv from '@push.rocks/qenv';
|
|
||||||
import * as smartdata from '@push.rocks/smartdata';
|
|
||||||
import * as smartfile from '@push.rocks/smartfile';
|
|
||||||
import * as smartpath from '@push.rocks/smartpath';
|
|
||||||
import * as smartpromise from '@push.rocks/smartpromise';
|
|
||||||
|
|
||||||
export { projectinfo, qenv, smartdata, smartfile, smartpath, smartpromise };
|
|
||||||
|
|
||||||
// thirdparty scope
|
|
||||||
import * as antrophic from '@anthropic-ai/sdk';
|
|
||||||
import * as openai from 'openai';
|
|
||||||
export { antrophic as anthropic, openai };
|
|
@ -1,17 +0,0 @@
|
|||||||
import { AiBridge } from './aibridge.classes.aibridge.js';
|
|
||||||
|
|
||||||
export {
|
|
||||||
AiBridge,
|
|
||||||
}
|
|
||||||
|
|
||||||
let aibridgeInstance: AiBridge;
|
|
||||||
export const runCli = async () => {
|
|
||||||
aibridgeInstance = new AiBridge();
|
|
||||||
await aibridgeInstance.start();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const stop = async () => {
|
|
||||||
if (aibridgeInstance) {
|
|
||||||
await aibridgeInstance.stop();
|
|
||||||
}
|
|
||||||
};
|
|
@ -4,12 +4,21 @@ import { logger } from '../logger.js';
|
|||||||
|
|
||||||
export class ApiManager {
|
export class ApiManager {
|
||||||
public emailRef: EmailService;
|
public emailRef: EmailService;
|
||||||
|
|
||||||
public typedRouter = new plugins.typedrequest.TypedRouter();
|
public typedRouter = new plugins.typedrequest.TypedRouter();
|
||||||
|
|
||||||
constructor(emailRefArg: EmailService) {
|
constructor(emailRefArg: EmailService) {
|
||||||
this.emailRef = emailRefArg;
|
this.emailRef = emailRefArg;
|
||||||
this.emailRef.typedrouter.addTypedRouter(this.typedRouter);
|
this.emailRef.typedrouter.addTypedRouter(this.typedRouter);
|
||||||
|
|
||||||
|
// Register API endpoints
|
||||||
|
this.registerApiEndpoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register API endpoints for email functionality
|
||||||
|
*/
|
||||||
|
private registerApiEndpoints() {
|
||||||
|
// Register the SendEmail endpoint
|
||||||
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.platformservice.mta.IRequest_SendEmail>(
|
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.platformservice.mta.IRequest_SendEmail>(
|
||||||
new plugins.typedrequest.TypedHandler('sendEmail', async (requestData) => {
|
new plugins.typedrequest.TypedHandler('sendEmail', async (requestData) => {
|
||||||
const mailToSend = new plugins.smartmail.Smartmail({
|
const mailToSend = new plugins.smartmail.Smartmail({
|
||||||
@ -30,10 +39,12 @@ export class ApiManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.emailRef.mailgunConnector.sendEmail(mailToSend, requestData.to, {});
|
// Send email through the service which will route to the appropriate connector
|
||||||
|
const emailId = await this.emailRef.sendEmail(mailToSend, requestData.to, {});
|
||||||
|
|
||||||
logger.log(
|
logger.log(
|
||||||
'info',
|
'info',
|
||||||
`send an email to ${requestData.to} with subject '${mailToSend.getSubject()}'`,
|
`sent an email to ${requestData.to} with subject '${mailToSend.getSubject()}'`,
|
||||||
{
|
{
|
||||||
eventType: 'sentEmail',
|
eventType: 'sentEmail',
|
||||||
email: {
|
email: {
|
||||||
@ -42,10 +53,35 @@ export class ApiManager {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
responseId: 'abc', // TODO: generate proper response id
|
responseId: emailId,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Add endpoint to check email status
|
||||||
|
this.typedRouter.addTypedHandler<{ emailId: string }>(
|
||||||
|
new plugins.typedrequest.TypedHandler('checkEmailStatus', async (requestData) => {
|
||||||
|
// If MTA is enabled, use it to check status
|
||||||
|
if (this.emailRef.mtaConnector) {
|
||||||
|
const status = await this.emailRef.mtaConnector.checkEmailStatus(requestData.emailId);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Mailgun, we don't have a status check implementation currently
|
||||||
|
return {
|
||||||
|
status: 'unknown',
|
||||||
|
details: { message: 'Status tracking not available for current provider' }
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add statistics endpoint
|
||||||
|
this.typedRouter.addTypedHandler<void>(
|
||||||
|
new plugins.typedrequest.TypedHandler('getEmailStats', async () => {
|
||||||
|
return this.emailRef.getStats();
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,30 +0,0 @@
|
|||||||
import * as plugins from './email.plugins.js';
|
|
||||||
import { EmailService } from './email.classes.emailservice.js';
|
|
||||||
|
|
||||||
export class MailgunConnector {
|
|
||||||
public emailRef: EmailService;
|
|
||||||
public mailgunAccount: plugins.mailgun.MailgunAccount;
|
|
||||||
|
|
||||||
constructor(emailRefArg: EmailService) {
|
|
||||||
this.emailRef = emailRefArg;
|
|
||||||
this.mailgunAccount = new plugins.mailgun.MailgunAccount({
|
|
||||||
apiToken: this.emailRef.qenv.getEnvVarOnDemand('MAILGUN_API_TOKEN'),
|
|
||||||
region: 'eu',
|
|
||||||
});
|
|
||||||
this.mailgunAccount.addSmtpCredentials(
|
|
||||||
this.emailRef.qenv.getEnvVarOnDemand('MAILGUN_SMTP_CREDENTIALS')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async sendEmail(
|
|
||||||
smartMailArg: plugins.smartmail.Smartmail<any>,
|
|
||||||
toArg: string,
|
|
||||||
dataArg: any = {}
|
|
||||||
) {
|
|
||||||
this.mailgunAccount.sendSmartMail(smartMailArg, toArg, dataArg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async receiveEmail(messageUrl: string) {
|
|
||||||
return await this.mailgunAccount.retrieveSmartMailFromMessageUrl(messageUrl);
|
|
||||||
}
|
|
||||||
}
|
|
169
ts/email/email.classes.connector.mta.ts
Normal file
169
ts/email/email.classes.connector.mta.ts
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
import * as plugins from '../plugins.js';
|
||||||
|
import { EmailService } from './email.classes.emailservice.js';
|
||||||
|
import { logger } from '../logger.js';
|
||||||
|
|
||||||
|
// Import MTA classes
|
||||||
|
import {
|
||||||
|
MtaService,
|
||||||
|
Email as MtaEmail,
|
||||||
|
type IEmailOptions,
|
||||||
|
DeliveryStatus,
|
||||||
|
type IAttachment
|
||||||
|
} from '../mta/index.js';
|
||||||
|
|
||||||
|
export class MtaConnector {
|
||||||
|
public emailRef: EmailService;
|
||||||
|
private mtaService: MtaService;
|
||||||
|
|
||||||
|
constructor(emailRefArg: EmailService, mtaService?: MtaService) {
|
||||||
|
this.emailRef = emailRefArg;
|
||||||
|
this.mtaService = mtaService || this.emailRef.mtaService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an email using the MTA service
|
||||||
|
* @param smartmail The email to send
|
||||||
|
* @param toAddresses Recipients (comma-separated or array)
|
||||||
|
* @param options Additional options
|
||||||
|
*/
|
||||||
|
public async sendEmail(
|
||||||
|
smartmail: plugins.smartmail.Smartmail<>,
|
||||||
|
toAddresses: string | string[],
|
||||||
|
options: any = {}
|
||||||
|
): Promise<string> {
|
||||||
|
try {
|
||||||
|
// Process recipients
|
||||||
|
const toArray = Array.isArray(toAddresses)
|
||||||
|
? toAddresses
|
||||||
|
: toAddresses.split(',').map(addr => addr.trim());
|
||||||
|
|
||||||
|
// Map SmartMail attachments to MTA attachments
|
||||||
|
const attachments: IAttachment[] = smartmail.attachments.map(attachment => {
|
||||||
|
return {
|
||||||
|
filename: attachment.parsedPath.base,
|
||||||
|
content: Buffer.from(attachment.contentBuffer),
|
||||||
|
contentType: (attachment as any)?.getContentType?.() || 'application/octet-stream' // TODO: revisit after smartfile has been updated
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create MTA Email
|
||||||
|
const mtaEmail = new MtaEmail({
|
||||||
|
from: smartmail.options.from,
|
||||||
|
to: toArray,
|
||||||
|
subject: smartmail.getSubject(),
|
||||||
|
text: smartmail.getBody(false), // Plain text version
|
||||||
|
html: smartmail.getBody(true), // HTML version
|
||||||
|
attachments
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send using MTA
|
||||||
|
const emailId = await this.mtaService.send(mtaEmail);
|
||||||
|
|
||||||
|
logger.log('info', `Email sent via MTA to ${toAddresses}`, {
|
||||||
|
eventType: 'sentEmail',
|
||||||
|
provider: 'mta',
|
||||||
|
emailId,
|
||||||
|
to: toAddresses
|
||||||
|
});
|
||||||
|
|
||||||
|
return emailId;
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Failed to send email via MTA: ${error.message}`, {
|
||||||
|
eventType: 'emailError',
|
||||||
|
provider: 'mta',
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve and process an incoming email
|
||||||
|
* For MTA, this would handle an email already received by the SMTP server
|
||||||
|
* @param emailData The raw email data or identifier
|
||||||
|
*/
|
||||||
|
public async receiveEmail(emailData: string): Promise<plugins.smartmail.Smartmail<>> {
|
||||||
|
try {
|
||||||
|
// In a real implementation, this would retrieve an email from the MTA storage
|
||||||
|
// For now, we can use a simplified approach:
|
||||||
|
|
||||||
|
// Parse the email (assuming emailData is a raw email or a file path)
|
||||||
|
const parsedEmail = await plugins.mailparser.simpleParser(emailData);
|
||||||
|
|
||||||
|
// Create a Smartmail from the parsed email
|
||||||
|
const smartmail = new plugins.smartmail.Smartmail({
|
||||||
|
from: parsedEmail.from?.text || '',
|
||||||
|
subject: parsedEmail.subject || '',
|
||||||
|
body: parsedEmail.html || parsedEmail.text || '',
|
||||||
|
creationObjectRef: {
|
||||||
|
From: parsedEmail.from?.text || '',
|
||||||
|
To: parsedEmail.to?.text || '',
|
||||||
|
Subject: parsedEmail.subject || ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add attachments if present
|
||||||
|
if (parsedEmail.attachments && parsedEmail.attachments.length > 0) {
|
||||||
|
for (const attachment of parsedEmail.attachments) {
|
||||||
|
smartmail.addAttachment(
|
||||||
|
await plugins.smartfile.SmartFile.fromBuffer(
|
||||||
|
attachment.filename || 'attachment',
|
||||||
|
attachment.content
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return smartmail;
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Failed to receive email via MTA: ${error.message}`, {
|
||||||
|
eventType: 'emailError',
|
||||||
|
provider: 'mta',
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the status of a sent email
|
||||||
|
* @param emailId The email ID to check
|
||||||
|
*/
|
||||||
|
public async checkEmailStatus(emailId: string): Promise<{
|
||||||
|
status: string;
|
||||||
|
details?: any;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const status = this.mtaService.getEmailStatus(emailId);
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
return {
|
||||||
|
status: 'unknown',
|
||||||
|
details: { message: 'Email not found' }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: status.status,
|
||||||
|
details: {
|
||||||
|
attempts: status.attempts,
|
||||||
|
lastAttempt: status.lastAttempt,
|
||||||
|
nextAttempt: status.nextAttempt,
|
||||||
|
error: status.error?.message
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Failed to check email status: ${error.message}`, {
|
||||||
|
eventType: 'emailError',
|
||||||
|
provider: 'mta',
|
||||||
|
emailId,
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 'error',
|
||||||
|
details: { message: error.message }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,22 @@
|
|||||||
import * as plugins from '../plugins.js';
|
import * as plugins from '../plugins.js';
|
||||||
import * as paths from '../paths.js';
|
import * as paths from '../paths.js';
|
||||||
import { MailgunConnector } from './email.classes.connector.mailgun.js';
|
import { MtaConnector } from './email.classes.connector.mta.js';
|
||||||
import { RuleManager } from './email.classes.rulemanager.js';
|
import { RuleManager } from './email.classes.rulemanager.js';
|
||||||
import { ApiManager } from './email.classes.apimanager.js';
|
import { ApiManager } from './email.classes.apimanager.js';
|
||||||
import { logger } from '../logger.js';
|
import { logger } from '../logger.js';
|
||||||
import type { SzPlatformService } from '../classes.platformservice.js';
|
import type { SzPlatformService } from '../classes.platformservice.js';
|
||||||
|
|
||||||
|
// Import MTA service
|
||||||
|
import { MtaService, type IMtaConfig } from '../mta/index.js';
|
||||||
|
|
||||||
export interface IEmailConstructorOptions {
|
export interface IEmailConstructorOptions {
|
||||||
mailgunApiKey: string;
|
useMta?: boolean;
|
||||||
|
mtaConfig?: IMtaConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email service with support for both Mailgun and local MTA
|
||||||
|
*/
|
||||||
export class EmailService {
|
export class EmailService {
|
||||||
public platformServiceRef: SzPlatformService;
|
public platformServiceRef: SzPlatformService;
|
||||||
|
|
||||||
@ -18,36 +24,108 @@ export class EmailService {
|
|||||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||||
|
|
||||||
// connectors
|
// connectors
|
||||||
public mailgunConnector: MailgunConnector;
|
public mtaConnector: MtaConnector;
|
||||||
public qenv = new plugins.qenv.Qenv('./', '.nogit/');
|
public qenv = new plugins.qenv.Qenv('./', '.nogit/');
|
||||||
|
|
||||||
// server
|
// MTA service
|
||||||
public apiManager = new ApiManager(this);
|
public mtaService: MtaService;
|
||||||
|
|
||||||
|
// services
|
||||||
|
public apiManager: ApiManager;
|
||||||
public ruleManager: RuleManager;
|
public ruleManager: RuleManager;
|
||||||
|
|
||||||
constructor(platformServiceRefArg: SzPlatformService) {
|
// configuration
|
||||||
|
private config: IEmailConstructorOptions;
|
||||||
|
|
||||||
|
constructor(platformServiceRefArg: SzPlatformService, options: IEmailConstructorOptions = {}) {
|
||||||
this.platformServiceRef = platformServiceRefArg;
|
this.platformServiceRef = platformServiceRefArg;
|
||||||
this.platformServiceRef.typedrouter.addTypedRouter(this.typedrouter);
|
this.platformServiceRef.typedrouter.addTypedRouter(this.typedrouter);
|
||||||
this.mailgunConnector = new MailgunConnector(this);
|
|
||||||
|
// Set default options
|
||||||
|
this.config = {
|
||||||
|
useMta: options.useMta ?? true,
|
||||||
|
mtaConfig: options.mtaConfig || {}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.config.useMta) {
|
||||||
|
// Initialize MTA service
|
||||||
|
this.mtaService = new MtaService(platformServiceRefArg, this.config.mtaConfig);
|
||||||
|
// Initialize MTA connector
|
||||||
|
this.mtaConnector = new MtaConnector(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize API manager and rule manager
|
||||||
|
this.apiManager = new ApiManager(this);
|
||||||
this.ruleManager = new RuleManager(this);
|
this.ruleManager = new RuleManager(this);
|
||||||
this.platformServiceRef.typedserver.server.addRoute(
|
|
||||||
'/mailgun-notify',
|
// Set up MTA SMTP server webhook if using MTA
|
||||||
new plugins.typedserver.servertools.Handler('POST', async (req, res) => {
|
if (this.config.useMta) {
|
||||||
console.log('Got a mailgun email notification');
|
// The MTA SMTP server will handle incoming emails directly
|
||||||
res.status(200);
|
// through its SMTP protocol. No additional webhook needed.
|
||||||
res.end();
|
}
|
||||||
this.ruleManager.handleNotification(req.body);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the email service
|
||||||
|
*/
|
||||||
public async start() {
|
public async start() {
|
||||||
|
// Initialize rule manager
|
||||||
await this.ruleManager.init();
|
await this.ruleManager.init();
|
||||||
|
|
||||||
|
// Start MTA service if enabled
|
||||||
|
if (this.config.useMta && this.mtaService) {
|
||||||
|
await this.mtaService.start();
|
||||||
|
logger.log('success', 'Started MTA service');
|
||||||
|
}
|
||||||
|
|
||||||
logger.log('success', `Started email service`);
|
logger.log('success', `Started email service`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the email service
|
||||||
|
*/
|
||||||
public async stop() {
|
public async stop() {
|
||||||
|
// Stop MTA service if it's running
|
||||||
|
if (this.config.useMta && this.mtaService) {
|
||||||
|
await this.mtaService.stop();
|
||||||
|
logger.log('info', 'Stopped MTA service');
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('info', 'Stopped email service');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an email using the configured provider (Mailgun or MTA)
|
||||||
|
* @param email The email to send
|
||||||
|
* @param to Recipient(s)
|
||||||
|
* @param options Additional options
|
||||||
|
*/
|
||||||
|
public async sendEmail(
|
||||||
|
email: plugins.smartmail.Smartmail<>,
|
||||||
|
to: string | string[],
|
||||||
|
options: any = {}
|
||||||
|
): Promise<string> {
|
||||||
|
// Determine which connector to use
|
||||||
|
if (this.config.useMta && this.mtaConnector) {
|
||||||
|
return this.mtaConnector.sendEmail(email, to, options);
|
||||||
|
} else {
|
||||||
|
throw new Error('No email provider configured');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get email service statistics
|
||||||
|
*/
|
||||||
|
public getStats() {
|
||||||
|
const stats: any = {
|
||||||
|
activeProviders: []
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.config.useMta) {
|
||||||
|
stats.activeProviders.push('mta');
|
||||||
|
stats.mta = this.mtaService.getStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,51 +1,101 @@
|
|||||||
import * as plugins from './email.plugins.js';
|
import * as plugins from '../plugins.js';
|
||||||
import { EmailService } from './email.classes.emailservice.js';
|
import { EmailService } from './email.classes.emailservice.js';
|
||||||
import { logger } from './email.logging.js';
|
import { logger } from '../logger.js';
|
||||||
|
|
||||||
export class RuleManager {
|
export class RuleManager {
|
||||||
public emailRef: EmailService;
|
public emailRef: EmailService;
|
||||||
public smartruleInstance = new plugins.smartrule.SmartRule<
|
public smartruleInstance = new plugins.smartrule.SmartRule<
|
||||||
plugins.smartmail.Smartmail<plugins.mailgun.IMailgunMessage>
|
plugins.smartmail.Smartmail<any>
|
||||||
>();
|
>();
|
||||||
|
|
||||||
constructor(emailRefArg: EmailService) {
|
constructor(emailRefArg: EmailService) {
|
||||||
this.emailRef = emailRefArg;
|
this.emailRef = emailRefArg;
|
||||||
|
|
||||||
|
// Register MTA handler for incoming emails if MTA is enabled
|
||||||
|
if (this.emailRef.mtaService) {
|
||||||
|
this.setupMtaIncomingHandler();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handleNotification(notification: plugins.mailgun.IMailgunNotification) {
|
/**
|
||||||
console.log(notification['message-url']);
|
* Set up handler for incoming emails via MTA's SMTP server
|
||||||
|
*/
|
||||||
|
private setupMtaIncomingHandler() {
|
||||||
|
// The original MtaService doesn't have a direct callback for incoming emails,
|
||||||
|
// but we can modify this approach based on how you prefer to integrate.
|
||||||
|
// One option would be to extend the MtaService to add an event emitter.
|
||||||
|
|
||||||
// basic checks here
|
// For now, we'll use a directory watcher as an example
|
||||||
// none for now
|
// This would watch the directory where MTA saves incoming emails
|
||||||
|
const incomingDir = this.emailRef.mtaService['receivedEmailsDir'] || './received';
|
||||||
|
|
||||||
const fetchedSmartmail = await this.emailRef.mailgunConnector.receiveEmail(
|
// Simple file watcher (in real implementation, use proper file watching)
|
||||||
notification['message-url']
|
// This is just conceptual - would need modification to work with your specific setup
|
||||||
);
|
this.watchIncomingEmails(incomingDir);
|
||||||
console.log('=======================');
|
}
|
||||||
console.log('Received a mail:');
|
|
||||||
console.log(`From: ${fetchedSmartmail.options.creationObjectRef.From}`);
|
|
||||||
console.log(`To: ${fetchedSmartmail.options.creationObjectRef.To}`);
|
|
||||||
console.log(`Subject: ${fetchedSmartmail.options.creationObjectRef.Subject}`);
|
|
||||||
console.log('^^^^^^^^^^^^^^^^^^^^^^^');
|
|
||||||
|
|
||||||
logger.log(
|
/**
|
||||||
'info',
|
* Watch directory for incoming emails (conceptual implementation)
|
||||||
`email from ${fetchedSmartmail.options.creationObjectRef.From} to ${fetchedSmartmail.options.creationObjectRef.To} with subject '${fetchedSmartmail.options.creationObjectRef.Subject}'`,
|
*/
|
||||||
{
|
private watchIncomingEmails(directory: string) {
|
||||||
eventType: 'receivedEmail',
|
console.log(`Watching for incoming emails in: ${directory}`);
|
||||||
email: {
|
|
||||||
from: fetchedSmartmail.options.creationObjectRef.From,
|
// Conceptual - in a real implementation, set up proper file watching
|
||||||
to: fetchedSmartmail.options.creationObjectRef.To,
|
// or modify the MTA to emit events when emails are received
|
||||||
subject: fetchedSmartmail.options.creationObjectRef.Subject,
|
|
||||||
},
|
/*
|
||||||
|
// Example using a file watcher:
|
||||||
|
const watcher = plugins.fs.watch(directory, async (eventType, filename) => {
|
||||||
|
if (eventType === 'rename' && filename.endsWith('.eml')) {
|
||||||
|
const filePath = plugins.path.join(directory, filename);
|
||||||
|
await this.handleMtaIncomingEmail(filePath);
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
this.smartruleInstance.makeDecision(fetchedSmartmail);
|
/**
|
||||||
|
* Handle incoming email received via MTA
|
||||||
|
*/
|
||||||
|
public async handleMtaIncomingEmail(emailPath: string) {
|
||||||
|
try {
|
||||||
|
// Process the email file
|
||||||
|
const fetchedSmartmail = await this.emailRef.mtaConnector.receiveEmail(emailPath);
|
||||||
|
|
||||||
|
console.log('=======================');
|
||||||
|
console.log('Received a mail via MTA:');
|
||||||
|
console.log(`From: ${fetchedSmartmail.options.creationObjectRef.From}`);
|
||||||
|
console.log(`To: ${fetchedSmartmail.options.creationObjectRef.To}`);
|
||||||
|
console.log(`Subject: ${fetchedSmartmail.options.creationObjectRef.Subject}`);
|
||||||
|
console.log('^^^^^^^^^^^^^^^^^^^^^^^');
|
||||||
|
|
||||||
|
logger.log(
|
||||||
|
'info',
|
||||||
|
`email from ${fetchedSmartmail.options.creationObjectRef.From} to ${fetchedSmartmail.options.creationObjectRef.To} with subject '${fetchedSmartmail.options.creationObjectRef.Subject}'`,
|
||||||
|
{
|
||||||
|
eventType: 'receivedEmail',
|
||||||
|
provider: 'mta',
|
||||||
|
email: {
|
||||||
|
from: fetchedSmartmail.options.creationObjectRef.From,
|
||||||
|
to: fetchedSmartmail.options.creationObjectRef.To,
|
||||||
|
subject: fetchedSmartmail.options.creationObjectRef.Subject,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Process with rules
|
||||||
|
this.smartruleInstance.makeDecision(fetchedSmartmail);
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Failed to process incoming MTA email: ${error.message}`, {
|
||||||
|
eventType: 'emailError',
|
||||||
|
provider: 'mta',
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init() {
|
public async init() {
|
||||||
// lets forward stuff
|
// Setup email rules
|
||||||
await this.createForwards();
|
await this.createForwards();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,20 +103,7 @@ export class RuleManager {
|
|||||||
* creates the default forwards
|
* creates the default forwards
|
||||||
*/
|
*/
|
||||||
public async createForwards() {
|
public async createForwards() {
|
||||||
const forwards: { originalToAddress: string[]; forwardedToAddress: string[] }[] = [
|
const forwards: { originalToAddress: string[]; forwardedToAddress: string[] }[] = [];
|
||||||
{
|
|
||||||
originalToAddress: ['bot@mail.nevermind.group'],
|
|
||||||
forwardedToAddress: ['phil@metadata.company', 'dominik@metadata.company'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
originalToAddress: ['legal@mail.lossless.com'],
|
|
||||||
forwardedToAddress: ['phil@lossless.com'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
originalToAddress: ['christine.nyamwaro@mail.lossless.com', 'christine@nyamwaro.com'],
|
|
||||||
forwardedToAddress: ['phil@lossless.com'],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
console.log(`${forwards.length} forward rules configured:`);
|
console.log(`${forwards.length} forward rules configured:`);
|
||||||
for (const forward of forwards) {
|
for (const forward of forwards) {
|
||||||
console.log(forward);
|
console.log(forward);
|
||||||
@ -87,7 +124,7 @@ export class RuleManager {
|
|||||||
return 'continue';
|
return 'continue';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async (smartmailArg: plugins.smartmail.Smartmail<plugins.mailgun.IMailgunMessage>) => {
|
async (smartmailArg: plugins.smartmail.Smartmail<any>) => {
|
||||||
forward.forwardedToAddress.map(async (toArg) => {
|
forward.forwardedToAddress.map(async (toArg) => {
|
||||||
const forwardedSmartMail = new plugins.smartmail.Smartmail({
|
const forwardedSmartMail = new plugins.smartmail.Smartmail({
|
||||||
body:
|
body:
|
||||||
@ -112,13 +149,16 @@ export class RuleManager {
|
|||||||
for (const attachment of smartmailArg.attachments) {
|
for (const attachment of smartmailArg.attachments) {
|
||||||
forwardedSmartMail.addAttachment(attachment);
|
forwardedSmartMail.addAttachment(attachment);
|
||||||
}
|
}
|
||||||
await this.emailRef.mailgunConnector.sendEmail(forwardedSmartMail, toArg);
|
|
||||||
|
// Use the EmailService's sendEmail method to send with the appropriate provider
|
||||||
|
await this.emailRef.sendEmail(forwardedSmartMail, toArg);
|
||||||
|
|
||||||
console.log(`forwarded mail to ${toArg}`);
|
console.log(`forwarded mail to ${toArg}`);
|
||||||
logger.log(
|
logger.log(
|
||||||
'info',
|
'info',
|
||||||
`email from ${
|
`email from ${
|
||||||
smartmailArg.options.creationObjectRef.From
|
smartmailArg.options.creationObjectRef.From
|
||||||
} to phil@lossless.com with subject '${smartmailArg.getSubject()}'`,
|
} to ${toArg} with subject '${smartmailArg.getSubject()}'`,
|
||||||
{
|
{
|
||||||
eventType: 'forwardedEmail',
|
eventType: 'forwardedEmail',
|
||||||
email: {
|
email: {
|
||||||
|
@ -4,5 +4,4 @@ export * from './mta.classes.dkimverifier.js';
|
|||||||
export * from './mta.classes.mta.js';
|
export * from './mta.classes.mta.js';
|
||||||
export * from './mta.classes.smtpserver.js';
|
export * from './mta.classes.smtpserver.js';
|
||||||
export * from './mta.classes.emailsendjob.js';
|
export * from './mta.classes.emailsendjob.js';
|
||||||
export * from './mta.classes.mta.js';
|
|
||||||
export * from './mta.classes.email.js';
|
export * from './mta.classes.email.js';
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as plugins from '../plugins.js';
|
import * as plugins from '../plugins.js';
|
||||||
import { Email, IEmailOptions } from './mta.classes.email.js';
|
import { Email } from './mta.classes.email.js';
|
||||||
|
import type { IEmailOptions } from './mta.classes.email.js';
|
||||||
import { DeliveryStatus } from './mta.classes.emailsendjob.js';
|
import { DeliveryStatus } from './mta.classes.emailsendjob.js';
|
||||||
import type { MtaService } from './mta.classes.mta.js';
|
import type { MtaService } from './mta.classes.mta.js';
|
||||||
import type { IDnsRecord } from './mta.classes.dnsmanager.js';
|
import type { IDnsRecord } from './mta.classes.dnsmanager.js';
|
||||||
@ -133,6 +134,38 @@ interface ApiError {
|
|||||||
details?: any;
|
details?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple HTTP Response helper
|
||||||
|
*/
|
||||||
|
class HttpResponse {
|
||||||
|
private headers: Record<string, string> = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
};
|
||||||
|
public statusCode: number = 200;
|
||||||
|
|
||||||
|
constructor(private res: any) {}
|
||||||
|
|
||||||
|
header(name: string, value: string): HttpResponse {
|
||||||
|
this.headers[name] = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
status(code: number): HttpResponse {
|
||||||
|
this.statusCode = code;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
json(data: any): void {
|
||||||
|
this.res.writeHead(this.statusCode, this.headers);
|
||||||
|
this.res.end(JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
end(): void {
|
||||||
|
this.res.writeHead(this.statusCode, this.headers);
|
||||||
|
this.res.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API Manager for MTA service
|
* API Manager for MTA service
|
||||||
*/
|
*/
|
||||||
@ -141,8 +174,8 @@ export class ApiManager {
|
|||||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||||
/** MTA service reference */
|
/** MTA service reference */
|
||||||
private mtaRef: MtaService;
|
private mtaRef: MtaService;
|
||||||
/** Express app */
|
/** HTTP server */
|
||||||
private app: any;
|
private server: any;
|
||||||
/** Authentication options */
|
/** Authentication options */
|
||||||
private authOptions: AuthOptions;
|
private authOptions: AuthOptions;
|
||||||
/** API routes */
|
/** API routes */
|
||||||
@ -164,9 +197,6 @@ export class ApiManager {
|
|||||||
constructor(mtaRef?: MtaService) {
|
constructor(mtaRef?: MtaService) {
|
||||||
this.mtaRef = mtaRef;
|
this.mtaRef = mtaRef;
|
||||||
|
|
||||||
// Initialize Express app
|
|
||||||
this.app = plugins.express();
|
|
||||||
|
|
||||||
// Default authentication options
|
// Default authentication options
|
||||||
this.authOptions = {
|
this.authOptions = {
|
||||||
apiKeys: new Map(),
|
apiKeys: new Map(),
|
||||||
@ -174,11 +204,11 @@ export class ApiManager {
|
|||||||
allowedIps: []
|
allowedIps: []
|
||||||
};
|
};
|
||||||
|
|
||||||
// Configure middleware
|
|
||||||
this.configureMiddleware();
|
|
||||||
|
|
||||||
// Register routes
|
// Register routes
|
||||||
this.registerRoutes();
|
this.registerRoutes();
|
||||||
|
|
||||||
|
// Create HTTP server with request handler
|
||||||
|
this.server = plugins.http.createServer(this.handleRequest.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -201,40 +231,63 @@ export class ApiManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure Express middleware
|
* Handle HTTP request
|
||||||
*/
|
*/
|
||||||
private configureMiddleware(): void {
|
private async handleRequest(req: any, res: any): Promise<void> {
|
||||||
// JSON body parser
|
const start = Date.now();
|
||||||
this.app.use(plugins.express.json({ limit: '10mb' }));
|
|
||||||
|
|
||||||
// CORS middleware
|
// Create a response helper
|
||||||
this.app.use((req: any, res: any, next: any) => {
|
const response = new HttpResponse(res);
|
||||||
res.header('Access-Control-Allow-Origin', '*');
|
|
||||||
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
||||||
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-API-Key');
|
|
||||||
|
|
||||||
if (req.method === 'OPTIONS') {
|
// Add CORS headers
|
||||||
return res.status(200).end();
|
response.header('Access-Control-Allow-Origin', '*');
|
||||||
|
response.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||||
|
response.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-API-Key');
|
||||||
|
|
||||||
|
// Handle preflight OPTIONS request
|
||||||
|
if (req.method === 'OPTIONS') {
|
||||||
|
return response.status(200).end();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Parse URL to get path and query
|
||||||
|
const url = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
|
||||||
|
const path = url.pathname;
|
||||||
|
|
||||||
|
// Collect request body if POST or PUT
|
||||||
|
let body = '';
|
||||||
|
if (req.method === 'POST' || req.method === 'PUT') {
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
req.on('data', (chunk: Buffer) => {
|
||||||
|
body += chunk.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('end', () => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', (err: Error) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Parse body as JSON if Content-Type is application/json
|
||||||
|
const contentType = req.headers['content-type'] || '';
|
||||||
|
if (contentType.includes('application/json')) {
|
||||||
|
try {
|
||||||
|
req.body = JSON.parse(body);
|
||||||
|
} catch (error) {
|
||||||
|
return response.status(400).json({
|
||||||
|
code: 'INVALID_JSON',
|
||||||
|
message: 'Invalid JSON in request body'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req.body = body;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
// Add authentication level to request
|
||||||
});
|
|
||||||
|
|
||||||
// Request logging
|
|
||||||
this.app.use((req: any, res: any, next: any) => {
|
|
||||||
const start = Date.now();
|
|
||||||
|
|
||||||
res.on('finish', () => {
|
|
||||||
const duration = Date.now() - start;
|
|
||||||
console.log(`[API] ${req.method} ${req.path} ${res.statusCode} ${duration}ms`);
|
|
||||||
});
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Authentication middleware
|
|
||||||
this.app.use((req: any, res: any, next: any) => {
|
|
||||||
// Store authentication level in request
|
|
||||||
req.authLevel = 'none';
|
req.authLevel = 'none';
|
||||||
|
|
||||||
// Check API key
|
// Check API key
|
||||||
@ -252,7 +305,9 @@ export class ApiManager {
|
|||||||
if (this.authOptions.jwtSecret && req.headers.authorization) {
|
if (this.authOptions.jwtSecret && req.headers.authorization) {
|
||||||
try {
|
try {
|
||||||
const token = req.headers.authorization.split(' ')[1];
|
const token = req.headers.authorization.split(' ')[1];
|
||||||
const decoded = plugins.jwt.verify(token, this.authOptions.jwtSecret);
|
// Note: We would need to add JWT verification
|
||||||
|
// Using a simple placeholder for now
|
||||||
|
const decoded = { level: 'none' }; // Simplified - would use actual JWT library
|
||||||
|
|
||||||
if (decoded && decoded.level) {
|
if (decoded && decoded.level) {
|
||||||
req.authLevel = decoded.level;
|
req.authLevel = decoded.level;
|
||||||
@ -266,17 +321,132 @@ export class ApiManager {
|
|||||||
|
|
||||||
// Check IP address (if configured)
|
// Check IP address (if configured)
|
||||||
if (this.authOptions.validateIp) {
|
if (this.authOptions.validateIp) {
|
||||||
const clientIp = req.ip || req.connection.remoteAddress;
|
const clientIp = req.socket.remoteAddress;
|
||||||
if (!this.authOptions.allowedIps.includes(clientIp)) {
|
if (!this.authOptions.allowedIps.includes(clientIp)) {
|
||||||
return res.status(403).json({
|
return response.status(403).json({
|
||||||
code: 'FORBIDDEN',
|
code: 'FORBIDDEN',
|
||||||
message: 'IP address not allowed'
|
message: 'IP address not allowed'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
// Find matching route
|
||||||
});
|
const route = this.findRoute(req.method, path);
|
||||||
|
|
||||||
|
if (!route) {
|
||||||
|
return response.status(404).json({
|
||||||
|
code: 'NOT_FOUND',
|
||||||
|
message: 'Endpoint not found'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check authentication
|
||||||
|
if (route.authLevel !== 'none' && req.authLevel !== route.authLevel && req.authLevel !== 'admin') {
|
||||||
|
return response.status(403).json({
|
||||||
|
code: 'FORBIDDEN',
|
||||||
|
message: `This endpoint requires ${route.authLevel} access`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check rate limit
|
||||||
|
if (route.rateLimit) {
|
||||||
|
const exceeded = this.checkRateLimit(route, req);
|
||||||
|
if (exceeded) {
|
||||||
|
return response.status(429).json({
|
||||||
|
code: 'RATE_LIMIT_EXCEEDED',
|
||||||
|
message: 'Rate limit exceeded, please try again later'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract path parameters
|
||||||
|
const pathParams = this.extractPathParams(route.path, path);
|
||||||
|
req.params = pathParams;
|
||||||
|
|
||||||
|
// Extract query parameters
|
||||||
|
req.query = {};
|
||||||
|
for (const [key, value] of url.searchParams.entries()) {
|
||||||
|
req.query[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the request
|
||||||
|
await route.handler(req, response);
|
||||||
|
|
||||||
|
// Log request
|
||||||
|
const duration = Date.now() - start;
|
||||||
|
console.log(`[API] ${req.method} ${path} ${response.statusCode} ${duration}ms`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error handling request:`, error);
|
||||||
|
|
||||||
|
// Send appropriate error response
|
||||||
|
const status = error.status || 500;
|
||||||
|
const apiError: ApiError = {
|
||||||
|
code: error.code || 'INTERNAL_ERROR',
|
||||||
|
message: error.message || 'Internal server error'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
apiError.details = error.stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.status(status).json(apiError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a route matching the method and path
|
||||||
|
*/
|
||||||
|
private findRoute(method: string, path: string): ApiRoute | null {
|
||||||
|
for (const route of this.routes) {
|
||||||
|
if (route.method === method && this.pathMatches(route.path, path)) {
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a path matches a route pattern
|
||||||
|
*/
|
||||||
|
private pathMatches(pattern: string, path: string): boolean {
|
||||||
|
// Convert route pattern to regex
|
||||||
|
const patternParts = pattern.split('/');
|
||||||
|
const pathParts = path.split('/');
|
||||||
|
|
||||||
|
if (patternParts.length !== pathParts.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < patternParts.length; i++) {
|
||||||
|
if (patternParts[i].startsWith(':')) {
|
||||||
|
// Parameter - always matches
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patternParts[i] !== pathParts[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract path parameters from URL
|
||||||
|
*/
|
||||||
|
private extractPathParams(pattern: string, path: string): Record<string, string> {
|
||||||
|
const params: Record<string, string> = {};
|
||||||
|
const patternParts = pattern.split('/');
|
||||||
|
const pathParts = path.split('/');
|
||||||
|
|
||||||
|
for (let i = 0; i < patternParts.length; i++) {
|
||||||
|
if (patternParts[i].startsWith(':')) {
|
||||||
|
const paramName = patternParts[i].substring(1);
|
||||||
|
params[paramName] = pathParts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -351,9 +521,6 @@ export class ApiManager {
|
|||||||
authLevel: 'none',
|
authLevel: 'none',
|
||||||
description: 'API documentation'
|
description: 'API documentation'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Map routes to Express
|
|
||||||
this.mapRoutesToExpress();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -364,65 +531,6 @@ export class ApiManager {
|
|||||||
this.routes.push(route);
|
this.routes.push(route);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Map defined routes to Express
|
|
||||||
*/
|
|
||||||
private mapRoutesToExpress(): void {
|
|
||||||
for (const route of this.routes) {
|
|
||||||
const { method, path, handler, authLevel } = route;
|
|
||||||
|
|
||||||
// Add Express route
|
|
||||||
this.app[method.toLowerCase()](path, async (req: any, res: any) => {
|
|
||||||
try {
|
|
||||||
// Check authentication
|
|
||||||
if (authLevel !== 'none' && req.authLevel !== authLevel && req.authLevel !== 'admin') {
|
|
||||||
return res.status(403).json({
|
|
||||||
code: 'FORBIDDEN',
|
|
||||||
message: `This endpoint requires ${authLevel} access`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check rate limit
|
|
||||||
if (route.rateLimit) {
|
|
||||||
const exceeded = this.checkRateLimit(route, req);
|
|
||||||
if (exceeded) {
|
|
||||||
return res.status(429).json({
|
|
||||||
code: 'RATE_LIMIT_EXCEEDED',
|
|
||||||
message: 'Rate limit exceeded, please try again later'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the request
|
|
||||||
await handler(req, res);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error handling ${method} ${path}:`, error);
|
|
||||||
|
|
||||||
// Send appropriate error response
|
|
||||||
const status = error.status || 500;
|
|
||||||
const apiError: ApiError = {
|
|
||||||
code: error.code || 'INTERNAL_ERROR',
|
|
||||||
message: error.message || 'Internal server error'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
|
||||||
apiError.details = error.stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(status).json(apiError);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add 404 handler
|
|
||||||
this.app.use((req: any, res: any) => {
|
|
||||||
res.status(404).json({
|
|
||||||
code: 'NOT_FOUND',
|
|
||||||
message: 'Endpoint not found'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check rate limit for a route
|
* Check rate limit for a route
|
||||||
* @param route Route definition
|
* @param route Route definition
|
||||||
@ -460,7 +568,7 @@ export class ApiManager {
|
|||||||
|
|
||||||
// Check per-IP limit if enabled
|
// Check per-IP limit if enabled
|
||||||
if (perIp) {
|
if (perIp) {
|
||||||
const clientIp = req.ip || req.connection.remoteAddress;
|
const clientIp = req.socket.remoteAddress;
|
||||||
let clientLimiter = limiter.clients.get(clientIp);
|
let clientLimiter = limiter.clients.get(clientIp);
|
||||||
|
|
||||||
if (!clientLimiter) {
|
if (!clientLimiter) {
|
||||||
@ -734,7 +842,7 @@ export class ApiManager {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Generate DKIM keys
|
// Generate DKIM keys
|
||||||
await this.mtaRef.dkimCreator.createAndStoreDKIMKeys(domain);
|
await this.mtaRef.dkimCreator.handleDKIMKeysForDomain(domain);
|
||||||
|
|
||||||
// Get DNS record
|
// Get DNS record
|
||||||
const dnsRecord = await this.mtaRef.dkimCreator.getDNSRecordForDomain(domain);
|
const dnsRecord = await this.mtaRef.dkimCreator.getDNSRecordForDomain(domain);
|
||||||
@ -825,7 +933,7 @@ export class ApiManager {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
// Start HTTP server
|
// Start HTTP server
|
||||||
this.app.listen(port, () => {
|
this.server.listen(port, () => {
|
||||||
console.log(`API server listening on port ${port}`);
|
console.log(`API server listening on port ${port}`);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
@ -840,7 +948,9 @@ export class ApiManager {
|
|||||||
* Stop the API server
|
* Stop the API server
|
||||||
*/
|
*/
|
||||||
public stop(): void {
|
public stop(): void {
|
||||||
// Nothing to do if not running
|
if (this.server) {
|
||||||
console.log('API server stopped');
|
this.server.close();
|
||||||
|
console.log('API server stopped');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,7 +16,7 @@ export interface IKeyPaths {
|
|||||||
export class DKIMCreator {
|
export class DKIMCreator {
|
||||||
private keysDir: string;
|
private keysDir: string;
|
||||||
|
|
||||||
constructor(metaRef: MtaService, keysDir = paths.keysDir) {
|
constructor(private metaRef: MtaService, keysDir = paths.keysDir) {
|
||||||
this.keysDir = keysDir;
|
this.keysDir = keysDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,8 +60,8 @@ export class DKIMCreator {
|
|||||||
return { privateKey, publicKey };
|
return { privateKey, publicKey };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a DKIM key pair
|
// Create a DKIM key pair - changed to public for API access
|
||||||
private async createDKIMKeys(): Promise<{ privateKey: string; publicKey: string }> {
|
public async createDKIMKeys(): Promise<{ privateKey: string; publicKey: string }> {
|
||||||
const { privateKey, publicKey } = await generateKeyPair('rsa', {
|
const { privateKey, publicKey } = await generateKeyPair('rsa', {
|
||||||
modulusLength: 2048,
|
modulusLength: 2048,
|
||||||
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
||||||
@ -71,8 +71,8 @@ export class DKIMCreator {
|
|||||||
return { privateKey, publicKey };
|
return { privateKey, publicKey };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store a DKIM key pair to disk
|
// Store a DKIM key pair to disk - changed to public for API access
|
||||||
private async storeDKIMKeys(
|
public async storeDKIMKeys(
|
||||||
privateKey: string,
|
privateKey: string,
|
||||||
publicKey: string,
|
publicKey: string,
|
||||||
privateKeyPath: string,
|
privateKeyPath: string,
|
||||||
@ -81,8 +81,8 @@ export class DKIMCreator {
|
|||||||
await Promise.all([writeFile(privateKeyPath, privateKey), writeFile(publicKeyPath, publicKey)]);
|
await Promise.all([writeFile(privateKeyPath, privateKey), writeFile(publicKeyPath, publicKey)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a DKIM key pair and store it to disk
|
// Create a DKIM key pair and store it to disk - changed to public for API access
|
||||||
private async createAndStoreDKIMKeys(domain: string): Promise<void> {
|
public async createAndStoreDKIMKeys(domain: string): Promise<void> {
|
||||||
const { privateKey, publicKey } = await this.createDKIMKeys();
|
const { privateKey, publicKey } = await this.createDKIMKeys();
|
||||||
const keyPaths = await this.getKeyPathsForDomain(domain);
|
const keyPaths = await this.getKeyPathsForDomain(domain);
|
||||||
await this.storeDKIMKeys(
|
await this.storeDKIMKeys(
|
||||||
@ -94,7 +94,8 @@ export class DKIMCreator {
|
|||||||
console.log(`DKIM keys for ${domain} created and stored.`);
|
console.log(`DKIM keys for ${domain} created and stored.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getDNSRecordForDomain(domainArg: string): Promise<plugins.tsclass.network.IDnsRecord> {
|
// Changed to public for API access
|
||||||
|
public async getDNSRecordForDomain(domainArg: string): Promise<plugins.tsclass.network.IDnsRecord> {
|
||||||
await this.handleDKIMKeysForDomain(domainArg);
|
await this.handleDKIMKeysForDomain(domainArg);
|
||||||
const keys = await this.readDKIMKeys(domainArg);
|
const keys = await this.readDKIMKeys(domainArg);
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ export class DNSManager {
|
|||||||
const cacheKey = `mx:${domain}`;
|
const cacheKey = `mx:${domain}`;
|
||||||
|
|
||||||
// Check cache first
|
// Check cache first
|
||||||
const cached = this.getFromCache(cacheKey);
|
const cached = this.getFromCache<plugins.dns.MxRecord[]>(cacheKey);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
@ -103,7 +103,7 @@ export class DNSManager {
|
|||||||
const cacheKey = `txt:${domain}`;
|
const cacheKey = `txt:${domain}`;
|
||||||
|
|
||||||
// Check cache first
|
// Check cache first
|
||||||
const cached = this.getFromCache(cacheKey);
|
const cached = this.getFromCache<string[][]>(cacheKey);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
@ -529,6 +529,7 @@ export class DNSManager {
|
|||||||
|
|
||||||
// Get DKIM record (already created by DKIMCreator)
|
// Get DKIM record (already created by DKIMCreator)
|
||||||
try {
|
try {
|
||||||
|
// Now using the public method
|
||||||
const dkimRecord = await this.mtaRef.dkimCreator.getDNSRecordForDomain(domain);
|
const dkimRecord = await this.mtaRef.dkimCreator.getDNSRecordForDomain(domain);
|
||||||
records.push(dkimRecord);
|
records.push(dkimRecord);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
29
ts/paths.ts
29
ts/paths.ts
@ -1,12 +1,29 @@
|
|||||||
import * as plugins from './plugins.js';
|
import * as plugins from './plugins.js';
|
||||||
|
|
||||||
|
// Base directories
|
||||||
|
export const baseDir = process.cwd();
|
||||||
export const packageDir = plugins.path.join(
|
export const packageDir = plugins.path.join(
|
||||||
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
|
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
|
||||||
'../'
|
'../'
|
||||||
);
|
);
|
||||||
export const assetsDir = plugins.path.join(packageDir, './assets');
|
export const dataDir = plugins.path.join(baseDir, 'data');
|
||||||
export const keysDir = plugins.path.join(assetsDir, './keys');
|
|
||||||
export const dnsRecordsDir = plugins.path.join(assetsDir, './dns-records');
|
// MTA directories
|
||||||
export const sentEmailsDir = plugins.path.join(assetsDir, './sent-emails');
|
export const keysDir = plugins.path.join(dataDir, 'keys');
|
||||||
export const receivedEmailsDir = plugins.path.join(assetsDir, './received-emails');
|
export const dnsRecordsDir = plugins.path.join(dataDir, 'dns');
|
||||||
plugins.smartfile.fs.ensureDirSync(keysDir);
|
export const sentEmailsDir = plugins.path.join(dataDir, 'emails', 'sent');
|
||||||
|
export const receivedEmailsDir = plugins.path.join(dataDir, 'emails', 'received');
|
||||||
|
export const failedEmailsDir = plugins.path.join(dataDir, 'emails', 'failed'); // For failed emails
|
||||||
|
export const logsDir = plugins.path.join(dataDir, 'logs'); // For logs
|
||||||
|
|
||||||
|
// Create directories if they don't exist
|
||||||
|
export function ensureDirectories() {
|
||||||
|
// Ensure data directories
|
||||||
|
plugins.smartfile.fs.ensureDirSync(dataDir);
|
||||||
|
plugins.smartfile.fs.ensureDirSync(keysDir);
|
||||||
|
plugins.smartfile.fs.ensureDirSync(dnsRecordsDir);
|
||||||
|
plugins.smartfile.fs.ensureDirSync(sentEmailsDir);
|
||||||
|
plugins.smartfile.fs.ensureDirSync(receivedEmailsDir);
|
||||||
|
plugins.smartfile.fs.ensureDirSync(failedEmailsDir);
|
||||||
|
plugins.smartfile.fs.ensureDirSync(logsDir);
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
import * as dns from 'dns';
|
import * as dns from 'dns';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
|
import * as http from 'http';
|
||||||
import * as net from 'net';
|
import * as net from 'net';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as tls from 'tls';
|
import * as tls from 'tls';
|
||||||
@ -11,6 +12,7 @@ export {
|
|||||||
dns,
|
dns,
|
||||||
fs,
|
fs,
|
||||||
crypto,
|
crypto,
|
||||||
|
http,
|
||||||
net,
|
net,
|
||||||
path,
|
path,
|
||||||
tls,
|
tls,
|
||||||
@ -45,9 +47,10 @@ import * as smartmail from '@push.rocks/smartmail';
|
|||||||
import * as smartpath from '@push.rocks/smartpath';
|
import * as smartpath from '@push.rocks/smartpath';
|
||||||
import * as smartpromise from '@push.rocks/smartpromise';
|
import * as smartpromise from '@push.rocks/smartpromise';
|
||||||
import * as smartrequest from '@push.rocks/smartrequest';
|
import * as smartrequest from '@push.rocks/smartrequest';
|
||||||
|
import * as smartrule from '@push.rocks/smartrule';
|
||||||
import * as smartrx from '@push.rocks/smartrx';
|
import * as smartrx from '@push.rocks/smartrx';
|
||||||
|
|
||||||
export { projectinfo, qenv, smartdata, smartfile, smartlog, smartmail, smartpath, smartpromise, smartrequest, smartrx };
|
export { projectinfo, qenv, smartdata, smartfile, smartlog, smartmail, smartpath, smartpromise, smartrequest, smartrule, smartrx };
|
||||||
|
|
||||||
// apiclient.xyz scope
|
// apiclient.xyz scope
|
||||||
import * as letterxpress from '@apiclient.xyz/letterxpress';
|
import * as letterxpress from '@apiclient.xyz/letterxpress';
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
"useDefineForClassFields": false,
|
"useDefineForClassFields": false,
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
|
Reference in New Issue
Block a user