@resolveio/server-lib 22.3.219 → 22.3.221
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nodemon.json +5 -0
- package/.vscode/settings.json +21 -0
- package/AGENTS.md +195 -0
- package/README.md +22 -0
- package/build_package.sh +5 -0
- package/compileDTS.pl +64 -0
- package/docs/ai-assistant-nightly-eval.md +65 -0
- package/docs/ai-assistant-preflight-checklist.md +23 -0
- package/docs/ai-assistant-report-builder-bridge-playbook.md +115 -0
- package/eslint-plugin-custom/index.js +7 -0
- package/eslint-plugin-custom/rules/no-filter-zero-index.js +44 -0
- package/eslint.config.js +103 -0
- package/gulpfile.js +216 -0
- package/methodAndPublicationListGenerator.py +375 -0
- package/mongodbensurers.js +2 -0
- package/mongostop.js +3 -0
- package/package.json +1 -1
- package/scripts/cleanup-bypassed-callmethod-logs.js +616 -0
- package/settings.development.json +25 -0
- package/settings.development.redacted.json +25 -0
- package/src/.env +12 -0
- package/src/ai/assistant-core-heuristics.ts +379 -0
- package/src/ai/resolveio-platform-intelligence-memory-corpus.ts +185 -0
- package/src/ai/resolveio-platform-intelligence-memory.ts +325 -0
- package/{ai/resolveio-platform-intelligence-types.d.ts → src/ai/resolveio-platform-intelligence-types.ts} +20 -15
- package/src/ai/resolveio-platform-intelligence.ts +462 -0
- package/src/client-server-app.ts +12 -0
- package/src/collections/ai-run.collection.ts +117 -0
- package/src/collections/ai-terminal-conversation.collection.ts +91 -0
- package/src/collections/ai-terminal-issue-report.collection.ts +99 -0
- package/src/collections/ai-terminal-message.collection.ts +77 -0
- package/src/collections/app-setting.collection.ts +104 -0
- package/src/collections/app-status.collection.ts +58 -0
- package/src/collections/communication-metric.collection.ts +84 -0
- package/src/collections/counter.collection.ts +56 -0
- package/src/collections/cron-job-history.collection.ts +94 -0
- package/src/collections/cron-job.collection.ts +92 -0
- package/src/collections/customer-notification.collection.ts +131 -0
- package/src/collections/customer-portal-password.collection.ts +76 -0
- package/src/collections/email-history.collection.ts +134 -0
- package/src/collections/email-verified.collection.ts +62 -0
- package/src/collections/file.collection.ts +74 -0
- package/src/collections/flag-update.collection.ts +57 -0
- package/src/collections/flag.collection.ts +57 -0
- package/src/collections/log-method-latency.collection.ts +77 -0
- package/src/collections/log-subscription.collection.ts +80 -0
- package/src/collections/log.collection.ts +93 -0
- package/src/collections/logged-in-users.collection.ts +67 -0
- package/src/collections/monitor-cpu.collection.ts +65 -0
- package/src/collections/monitor-function.collection.ts +74 -0
- package/src/collections/monitor-memory.collection.ts +77 -0
- package/src/collections/monitor-mongo.collection.ts +71 -0
- package/src/collections/notification.collection.ts +57 -0
- package/src/collections/openai-usage-ledger.collection.ts +131 -0
- package/src/collections/report-builder-dashboard-builder.collection.ts +109 -0
- package/src/collections/report-builder-library.collection.ts +89 -0
- package/src/collections/report-builder-report.collection.ts +184 -0
- package/src/collections/user-group.collection.ts +89 -0
- package/src/collections/user-guide.collection.ts +57 -0
- package/src/collections/user.collection.ts +181 -0
- package/src/cron/cron.ts +117 -0
- package/src/fixtures/cron-jobs.ts +95 -0
- package/src/fixtures/init.ts +35 -0
- package/src/http/auth.ts +818 -0
- package/src/http/health.ts +7 -0
- package/src/http/home.ts +90 -0
- package/src/http/slow-query-publication.ts +49 -0
- package/src/index.ts +1 -0
- package/src/managers/ai-assistant-codex-manager.manager.ts +1131 -0
- package/src/managers/ai-run-evidence.manager.ts +264 -0
- package/src/managers/communication-metric.manager.ts +82 -0
- package/src/managers/cron.manager.ts +333 -0
- package/src/managers/customer-notification-content.manager.ts +236 -0
- package/src/managers/diagnostic-manager-bootstrap.ts +165 -0
- package/src/managers/error-auto-fix.manager.ts +2767 -0
- package/src/managers/local-log.manager.ts +113 -0
- package/src/managers/method.manager.ts +1857 -0
- package/src/managers/mongo.manager.ts +4575 -0
- package/src/managers/monitor.manager.ts +507 -0
- package/src/managers/openai-usage-ledger.manager.ts +112 -0
- package/src/managers/slow-query-verifier.manager.ts +3590 -0
- package/src/managers/slow-query.manager.ts +519 -0
- package/src/managers/subscription.manager.ts +3128 -0
- package/src/managers/websocket.manager.ts +746 -0
- package/src/managers/worker-dispatcher.manager.ts +1360 -0
- package/src/managers/worker-server.manager.ts +536 -0
- package/src/methods/accounts.ts +532 -0
- package/src/methods/ai-terminal.ts +29070 -0
- package/src/methods/app-settings.ts +114 -0
- package/src/methods/aws.ts +649 -0
- package/src/methods/collections.ts +641 -0
- package/src/methods/counters.ts +69 -0
- package/src/methods/cron-jobs.ts +2614 -0
- package/src/methods/customer-notifications.ts +458 -0
- package/src/methods/diagnostics.ts +616 -0
- package/src/methods/flag-updates.ts +7 -0
- package/src/methods/flags.ts +7 -0
- package/src/methods/logs.ts +657 -0
- package/src/methods/mongo-explorer.ts +1880 -0
- package/src/methods/monitor.ts +540 -0
- package/src/methods/pdf.ts +1236 -0
- package/src/methods/publications.ts +129 -0
- package/src/methods/report-builder.ts +3300 -0
- package/src/methods/support.ts +335 -0
- package/src/models/ai-run.model.ts +27 -0
- package/src/models/ai-terminal-conversation.model.ts +19 -0
- package/src/models/ai-terminal-issue-report.model.ts +21 -0
- package/src/models/ai-terminal-message.model.ts +24 -0
- package/src/models/app-setting.model.ts +17 -0
- package/{models/app-status.model.d.ts → src/models/app-status.model.ts} +3 -2
- package/{models/billing-logged-in-users.model.d.ts → src/models/billing-logged-in-users.model.ts} +5 -4
- package/src/models/collection-document.model.ts +24 -0
- package/src/models/communication-metric.model.ts +23 -0
- package/{models/counter.model.d.ts → src/models/counter.model.ts} +4 -3
- package/src/models/cron-job-history.model.ts +16 -0
- package/src/models/cron-job.model.ts +15 -0
- package/src/models/customer-notification.model.ts +28 -0
- package/src/models/customer-portal-password.model.ts +12 -0
- package/src/models/dialog.model.ts +25 -0
- package/{models/email-history.model.js → src/models/email-history.model.ts} +36 -4
- package/{models/email-verified.model.d.ts → src/models/email-verified.model.ts} +6 -5
- package/{models/file.model.d.ts → src/models/file.model.ts} +8 -7
- package/{models/flag-update.model.d.ts → src/models/flag-update.model.ts} +4 -3
- package/{models/flag.model.d.ts → src/models/flag.model.ts} +4 -3
- package/src/models/log-method-latency.model.ts +11 -0
- package/{models/log-subscription.model.d.ts → src/models/log-subscription.model.ts} +11 -9
- package/src/models/log.model.ts +19 -0
- package/{models/logged-in-users.model.d.ts → src/models/logged-in-users.model.ts} +6 -5
- package/{models/method-response.model.d.ts → src/models/method-response.model.ts} +7 -6
- package/src/models/method.model.ts +25 -0
- package/{models/monitor-cpu.model.d.ts → src/models/monitor-cpu.model.ts} +9 -7
- package/src/models/monitor-function.model.ts +16 -0
- package/src/models/monitor-memory.model.ts +17 -0
- package/src/models/monitor-mongo.model.ts +15 -0
- package/{models/notification.model.d.ts → src/models/notification.model.ts} +6 -4
- package/src/models/openai-usage-ledger.model.ts +56 -0
- package/src/models/pagination.model.ts +35 -0
- package/src/models/permission.model.ts +14 -0
- package/src/models/report-builder-dashboard-builder.model.ts +29 -0
- package/src/models/report-builder-library.model.ts +20 -0
- package/src/models/report-builder-report.model.ts +136 -0
- package/src/models/report-builder.model.ts +68 -0
- package/src/models/select-data-label.model.ts +9 -0
- package/src/models/server-message.model.ts +31 -0
- package/src/models/slow-query-report.model.ts +23 -0
- package/src/models/subscription.model.ts +73 -0
- package/src/models/support-ticket.model.ts +104 -0
- package/src/models/user-group.model.ts +24 -0
- package/{models/user-guide.model.d.ts → src/models/user-guide.model.ts} +5 -4
- package/src/models/user.model.ts +96 -0
- package/src/private/images/ResolveIO.png +0 -0
- package/src/publications/ai-terminal.ts +73 -0
- package/src/publications/app-settings.ts +25 -0
- package/src/publications/app-status.ts +13 -0
- package/src/publications/cron-jobs.ts +40 -0
- package/src/publications/customer-notifications.ts +101 -0
- package/src/publications/files.ts +33 -0
- package/src/publications/flags-update.ts +19 -0
- package/src/publications/flags.ts +19 -0
- package/src/publications/logs.ts +163 -0
- package/src/publications/notifications.ts +13 -0
- package/src/publications/report-builder-dashboard-builders.ts +39 -0
- package/src/publications/report-builder-libraries.ts +41 -0
- package/src/publications/report-builder-reports.ts +47 -0
- package/src/publications/super-admin.ts +13 -0
- package/src/publications/user-groups.ts +12 -0
- package/src/publications/user-guides.ts +12 -0
- package/src/resolveio-server-app.ts +617 -0
- package/src/server-app.ts +3354 -0
- package/src/services/codex-client.ts +1231 -0
- package/src/services/openai-client.ts +265 -0
- package/src/types/error-report.ts +26 -0
- package/src/types/js-tiktoken.d.ts +11 -0
- package/src/types/slow-query-report.ts +28 -0
- package/src/util/ai-qa-policy.ts +925 -0
- package/src/util/ai-run-evidence-adapters.ts +8347 -0
- package/src/util/ai-run-evidence-dashboard.ts +323 -0
- package/src/util/ai-run-evidence-eval.ts +1057 -0
- package/src/util/ai-run-evidence.ts +1430 -0
- package/src/util/ai-runner-artifacts.ts +586 -0
- package/src/util/ai-runner-manager-autopilot.ts +961 -0
- package/src/util/ai-runner-manager-policy.ts +5011 -0
- package/src/util/ai-runner-qa-auth.ts +838 -0
- package/src/util/ai-runner-qa-tools.ts +3536 -0
- package/src/util/aicoder-runner-v6.ts +3121 -0
- package/src/util/common.ts +649 -0
- package/src/util/customer-portal-password.ts +183 -0
- package/src/util/error-reporter.ts +332 -0
- package/src/util/error-tracking.ts +79 -0
- package/src/util/openai-usage-cost.ts +114 -0
- package/src/util/report-builder-unwinds.ts +180 -0
- package/src/util/runner-process-janitor.ts +219 -0
- package/src/util/schema-report-builder.ts +448 -0
- package/src/util/slow-query-reporter.ts +216 -0
- package/src/util/subscription-dependency-context.ts +1096 -0
- package/src/util/support-runner-v5.ts +10040 -0
- package/src/util/tokenizer.ts +38 -0
- package/src/workers/codex-runner.worker.ts +142 -0
- package/start_server.sh +5 -0
- package/tests/ai-assistant-corpus-build.ts +484 -0
- package/tests/ai-assistant-corpus-replay-e2e.ts +774 -0
- package/tests/ai-assistant-data-parity-e2e.ts +1989 -0
- package/tests/ai-assistant-eval-triage.ts +831 -0
- package/tests/ai-assistant-openai-e2e.ts +1061 -0
- package/tests/ai-assistant-openai-git-e2e.ts +155 -0
- package/tests/ai-assistant-preflight-matrix.ts +215 -0
- package/tests/ai-assistant-routing-eval.test.ts +585 -0
- package/tests/ai-assistant-snf-live-eval.ts +975 -0
- package/tests/ai-assistant-utils.test.ts +4834 -0
- package/tests/ai-manager-autopilot-snapshot.test.ts +193 -0
- package/tests/ai-manager-recovery-checkpoint.test.ts +1383 -0
- package/tests/ai-run-eval.test.ts +132 -0
- package/tests/ai-run-evidence.test.ts +3773 -0
- package/tests/ai-runner-contract.test.ts +515 -0
- package/tests/aicoder-runner-v6.test.ts +822 -0
- package/tests/error-reporter.test.ts +145 -0
- package/tests/method-publication-generator.test.ts +46 -0
- package/tests/report-builder-linking.test.ts +79 -0
- package/tests/resolveio-platform-intelligence.test.ts +352 -0
- package/tests/server-app-cron-owner.test.ts +127 -0
- package/tests/subscription-connect-race.test.ts +158 -0
- package/tests/subscription-dependency-context.test.ts +324 -0
- package/tests/subscription-manager-collection-tracking.test.ts +86 -0
- package/tests/subscription-manager-invalidation.test.ts +86 -0
- package/tests/support-runner-v5.test.ts +3201 -0
- package/tsconfig.json +34 -0
- package/ai/assistant-core-heuristics.d.ts +0 -11
- package/ai/assistant-core-heuristics.js +0 -356
- package/ai/assistant-core-heuristics.js.map +0 -1
- package/ai/resolveio-platform-intelligence-memory-corpus.d.ts +0 -3
- package/ai/resolveio-platform-intelligence-memory-corpus.js +0 -214
- package/ai/resolveio-platform-intelligence-memory-corpus.js.map +0 -1
- package/ai/resolveio-platform-intelligence-memory.d.ts +0 -20
- package/ai/resolveio-platform-intelligence-memory.js +0 -341
- package/ai/resolveio-platform-intelligence-memory.js.map +0 -1
- package/ai/resolveio-platform-intelligence-types.js +0 -4
- package/ai/resolveio-platform-intelligence-types.js.map +0 -1
- package/ai/resolveio-platform-intelligence.d.ts +0 -6
- package/ai/resolveio-platform-intelligence.js +0 -463
- package/ai/resolveio-platform-intelligence.js.map +0 -1
- package/client-server-app.d.ts +0 -1
- package/client-server-app.js +0 -68
- package/client-server-app.js.map +0 -1
- package/collections/ai-run.collection.d.ts +0 -3
- package/collections/ai-run.collection.js +0 -170
- package/collections/ai-run.collection.js.map +0 -1
- package/collections/ai-terminal-conversation.collection.d.ts +0 -2
- package/collections/ai-terminal-conversation.collection.js +0 -140
- package/collections/ai-terminal-conversation.collection.js.map +0 -1
- package/collections/ai-terminal-issue-report.collection.d.ts +0 -2
- package/collections/ai-terminal-issue-report.collection.js +0 -148
- package/collections/ai-terminal-issue-report.collection.js.map +0 -1
- package/collections/ai-terminal-message.collection.d.ts +0 -2
- package/collections/ai-terminal-message.collection.js +0 -121
- package/collections/ai-terminal-message.collection.js.map +0 -1
- package/collections/app-setting.collection.d.ts +0 -3
- package/collections/app-setting.collection.js +0 -103
- package/collections/app-setting.collection.js.map +0 -1
- package/collections/app-status.collection.d.ts +0 -3
- package/collections/app-status.collection.js +0 -57
- package/collections/app-status.collection.js.map +0 -1
- package/collections/communication-metric.collection.d.ts +0 -2
- package/collections/communication-metric.collection.js +0 -133
- package/collections/communication-metric.collection.js.map +0 -1
- package/collections/counter.collection.d.ts +0 -3
- package/collections/counter.collection.js +0 -56
- package/collections/counter.collection.js.map +0 -1
- package/collections/cron-job-history.collection.d.ts +0 -3
- package/collections/cron-job-history.collection.js +0 -137
- package/collections/cron-job-history.collection.js.map +0 -1
- package/collections/cron-job.collection.d.ts +0 -3
- package/collections/cron-job.collection.js +0 -92
- package/collections/cron-job.collection.js.map +0 -1
- package/collections/customer-notification.collection.d.ts +0 -3
- package/collections/customer-notification.collection.js +0 -130
- package/collections/customer-notification.collection.js.map +0 -1
- package/collections/customer-portal-password.collection.d.ts +0 -3
- package/collections/customer-portal-password.collection.js +0 -75
- package/collections/customer-portal-password.collection.js.map +0 -1
- package/collections/email-history.collection.d.ts +0 -3
- package/collections/email-history.collection.js +0 -134
- package/collections/email-history.collection.js.map +0 -1
- package/collections/email-verified.collection.d.ts +0 -3
- package/collections/email-verified.collection.js +0 -62
- package/collections/email-verified.collection.js.map +0 -1
- package/collections/file.collection.d.ts +0 -3
- package/collections/file.collection.js +0 -74
- package/collections/file.collection.js.map +0 -1
- package/collections/flag-update.collection.d.ts +0 -3
- package/collections/flag-update.collection.js +0 -57
- package/collections/flag-update.collection.js.map +0 -1
- package/collections/flag.collection.d.ts +0 -3
- package/collections/flag.collection.js +0 -57
- package/collections/flag.collection.js.map +0 -1
- package/collections/log-method-latency.collection.d.ts +0 -3
- package/collections/log-method-latency.collection.js +0 -77
- package/collections/log-method-latency.collection.js.map +0 -1
- package/collections/log-subscription.collection.d.ts +0 -3
- package/collections/log-subscription.collection.js +0 -80
- package/collections/log-subscription.collection.js.map +0 -1
- package/collections/log.collection.d.ts +0 -3
- package/collections/log.collection.js +0 -93
- package/collections/log.collection.js.map +0 -1
- package/collections/logged-in-users.collection.d.ts +0 -3
- package/collections/logged-in-users.collection.js +0 -67
- package/collections/logged-in-users.collection.js.map +0 -1
- package/collections/monitor-cpu.collection.d.ts +0 -3
- package/collections/monitor-cpu.collection.js +0 -65
- package/collections/monitor-cpu.collection.js.map +0 -1
- package/collections/monitor-function.collection.d.ts +0 -3
- package/collections/monitor-function.collection.js +0 -74
- package/collections/monitor-function.collection.js.map +0 -1
- package/collections/monitor-memory.collection.d.ts +0 -3
- package/collections/monitor-memory.collection.js +0 -77
- package/collections/monitor-memory.collection.js.map +0 -1
- package/collections/monitor-mongo.collection.d.ts +0 -3
- package/collections/monitor-mongo.collection.js +0 -71
- package/collections/monitor-mongo.collection.js.map +0 -1
- package/collections/notification.collection.d.ts +0 -3
- package/collections/notification.collection.js +0 -57
- package/collections/notification.collection.js.map +0 -1
- package/collections/openai-usage-ledger.collection.d.ts +0 -2
- package/collections/openai-usage-ledger.collection.js +0 -188
- package/collections/openai-usage-ledger.collection.js.map +0 -1
- package/collections/report-builder-dashboard-builder.collection.d.ts +0 -3
- package/collections/report-builder-dashboard-builder.collection.js +0 -109
- package/collections/report-builder-dashboard-builder.collection.js.map +0 -1
- package/collections/report-builder-library.collection.d.ts +0 -3
- package/collections/report-builder-library.collection.js +0 -87
- package/collections/report-builder-library.collection.js.map +0 -1
- package/collections/report-builder-report.collection.d.ts +0 -4
- package/collections/report-builder-report.collection.js +0 -184
- package/collections/report-builder-report.collection.js.map +0 -1
- package/collections/user-group.collection.d.ts +0 -4
- package/collections/user-group.collection.js +0 -89
- package/collections/user-group.collection.js.map +0 -1
- package/collections/user-guide.collection.d.ts +0 -3
- package/collections/user-guide.collection.js +0 -57
- package/collections/user-guide.collection.js.map +0 -1
- package/collections/user.collection.d.ts +0 -4
- package/collections/user.collection.js +0 -180
- package/collections/user.collection.js.map +0 -1
- package/cron/cron.d.ts +0 -14
- package/cron/cron.js +0 -216
- package/cron/cron.js.map +0 -1
- package/fixtures/cron-jobs.d.ts +0 -1
- package/fixtures/cron-jobs.js +0 -150
- package/fixtures/cron-jobs.js.map +0 -1
- package/fixtures/init.d.ts +0 -1
- package/fixtures/init.js +0 -91
- package/fixtures/init.js.map +0 -1
- package/http/auth.d.ts +0 -2
- package/http/auth.js +0 -951
- package/http/auth.js.map +0 -1
- package/http/health.d.ts +0 -1
- package/http/health.js +0 -11
- package/http/health.js.map +0 -1
- package/http/home.d.ts +0 -1
- package/http/home.js +0 -134
- package/http/home.js.map +0 -1
- package/http/slow-query-publication.d.ts +0 -2
- package/http/slow-query-publication.js +0 -99
- package/http/slow-query-publication.js.map +0 -1
- package/index.d.ts +0 -1
- package/index.js +0 -19
- package/index.js.map +0 -1
- package/managers/ai-assistant-codex-manager.manager.d.ts +0 -67
- package/managers/ai-assistant-codex-manager.manager.js +0 -1113
- package/managers/ai-assistant-codex-manager.manager.js.map +0 -1
- package/managers/ai-run-evidence.manager.d.ts +0 -36
- package/managers/ai-run-evidence.manager.js +0 -377
- package/managers/ai-run-evidence.manager.js.map +0 -1
- package/managers/communication-metric.manager.d.ts +0 -16
- package/managers/communication-metric.manager.js +0 -134
- package/managers/communication-metric.manager.js.map +0 -1
- package/managers/cron.manager.d.ts +0 -20
- package/managers/cron.manager.js +0 -534
- package/managers/cron.manager.js.map +0 -1
- package/managers/customer-notification-content.manager.d.ts +0 -55
- package/managers/customer-notification-content.manager.js +0 -158
- package/managers/customer-notification-content.manager.js.map +0 -1
- package/managers/diagnostic-manager-bootstrap.d.ts +0 -9
- package/managers/diagnostic-manager-bootstrap.js +0 -260
- package/managers/diagnostic-manager-bootstrap.js.map +0 -1
- package/managers/error-auto-fix.manager.d.ts +0 -149
- package/managers/error-auto-fix.manager.js +0 -3064
- package/managers/error-auto-fix.manager.js.map +0 -1
- package/managers/local-log.manager.d.ts +0 -18
- package/managers/local-log.manager.js +0 -88
- package/managers/local-log.manager.js.map +0 -1
- package/managers/method.manager.d.ts +0 -84
- package/managers/method.manager.js +0 -1964
- package/managers/method.manager.js.map +0 -1
- package/managers/mongo.manager.d.ts +0 -224
- package/managers/mongo.manager.js +0 -5000
- package/managers/mongo.manager.js.map +0 -1
- package/managers/monitor.manager.d.ts +0 -70
- package/managers/monitor.manager.js +0 -550
- package/managers/monitor.manager.js.map +0 -1
- package/managers/openai-usage-ledger.manager.d.ts +0 -30
- package/managers/openai-usage-ledger.manager.js +0 -142
- package/managers/openai-usage-ledger.manager.js.map +0 -1
- package/managers/slow-query-verifier.manager.d.ts +0 -144
- package/managers/slow-query-verifier.manager.js +0 -3857
- package/managers/slow-query-verifier.manager.js.map +0 -1
- package/managers/slow-query.manager.d.ts +0 -28
- package/managers/slow-query.manager.js +0 -468
- package/managers/slow-query.manager.js.map +0 -1
- package/managers/subscription.manager.d.ts +0 -169
- package/managers/subscription.manager.js +0 -3434
- package/managers/subscription.manager.js.map +0 -1
- package/managers/websocket.manager.d.ts +0 -73
- package/managers/websocket.manager.js +0 -673
- package/managers/websocket.manager.js.map +0 -1
- package/managers/worker-dispatcher.manager.d.ts +0 -120
- package/managers/worker-dispatcher.manager.js +0 -1266
- package/managers/worker-dispatcher.manager.js.map +0 -1
- package/managers/worker-server.manager.d.ts +0 -35
- package/managers/worker-server.manager.js +0 -582
- package/managers/worker-server.manager.js.map +0 -1
- package/methods/accounts.d.ts +0 -2
- package/methods/accounts.js +0 -624
- package/methods/accounts.js.map +0 -1
- package/methods/ai-terminal.d.ts +0 -458
- package/methods/ai-terminal.js +0 -27991
- package/methods/ai-terminal.js.map +0 -1
- package/methods/app-settings.d.ts +0 -2
- package/methods/app-settings.js +0 -169
- package/methods/app-settings.js.map +0 -1
- package/methods/aws.d.ts +0 -2
- package/methods/aws.js +0 -877
- package/methods/aws.js.map +0 -1
- package/methods/collections.d.ts +0 -2
- package/methods/collections.js +0 -719
- package/methods/collections.js.map +0 -1
- package/methods/counters.d.ts +0 -2
- package/methods/counters.js +0 -113
- package/methods/counters.js.map +0 -1
- package/methods/cron-jobs.d.ts +0 -2
- package/methods/cron-jobs.js +0 -2475
- package/methods/cron-jobs.js.map +0 -1
- package/methods/customer-notifications.d.ts +0 -2
- package/methods/customer-notifications.js +0 -528
- package/methods/customer-notifications.js.map +0 -1
- package/methods/diagnostics.d.ts +0 -2
- package/methods/diagnostics.js +0 -703
- package/methods/diagnostics.js.map +0 -1
- package/methods/flag-updates.d.ts +0 -2
- package/methods/flag-updates.js +0 -8
- package/methods/flag-updates.js.map +0 -1
- package/methods/flags.d.ts +0 -2
- package/methods/flags.js +0 -8
- package/methods/flags.js.map +0 -1
- package/methods/logs.d.ts +0 -2
- package/methods/logs.js +0 -751
- package/methods/logs.js.map +0 -1
- package/methods/mongo-explorer.d.ts +0 -2
- package/methods/mongo-explorer.js +0 -1808
- package/methods/mongo-explorer.js.map +0 -1
- package/methods/monitor.d.ts +0 -2
- package/methods/monitor.js +0 -543
- package/methods/monitor.js.map +0 -1
- package/methods/pdf.d.ts +0 -2
- package/methods/pdf.js +0 -1216
- package/methods/pdf.js.map +0 -1
- package/methods/publications.d.ts +0 -1
- package/methods/publications.js +0 -183
- package/methods/publications.js.map +0 -1
- package/methods/report-builder.d.ts +0 -2
- package/methods/report-builder.js +0 -3094
- package/methods/report-builder.js.map +0 -1
- package/methods/support.d.ts +0 -2
- package/methods/support.js +0 -430
- package/methods/support.js.map +0 -1
- package/models/ai-run.model.d.ts +0 -19
- package/models/ai-run.model.js +0 -4
- package/models/ai-run.model.js.map +0 -1
- package/models/ai-terminal-conversation.model.d.ts +0 -17
- package/models/ai-terminal-conversation.model.js +0 -4
- package/models/ai-terminal-conversation.model.js.map +0 -1
- package/models/ai-terminal-issue-report.model.d.ts +0 -19
- package/models/ai-terminal-issue-report.model.js +0 -4
- package/models/ai-terminal-issue-report.model.js.map +0 -1
- package/models/ai-terminal-message.model.d.ts +0 -22
- package/models/ai-terminal-message.model.js +0 -4
- package/models/ai-terminal-message.model.js.map +0 -1
- package/models/app-setting.model.d.ts +0 -16
- package/models/app-setting.model.js +0 -4
- package/models/app-setting.model.js.map +0 -1
- package/models/app-status.model.js +0 -4
- package/models/app-status.model.js.map +0 -1
- package/models/billing-logged-in-users.model.js +0 -4
- package/models/billing-logged-in-users.model.js.map +0 -1
- package/models/collection-document.model.d.ts +0 -21
- package/models/collection-document.model.js +0 -4
- package/models/collection-document.model.js.map +0 -1
- package/models/communication-metric.model.d.ts +0 -20
- package/models/communication-metric.model.js +0 -4
- package/models/communication-metric.model.js.map +0 -1
- package/models/counter.model.js +0 -4
- package/models/counter.model.js.map +0 -1
- package/models/cron-job-history.model.d.ts +0 -15
- package/models/cron-job-history.model.js +0 -4
- package/models/cron-job-history.model.js.map +0 -1
- package/models/cron-job.model.d.ts +0 -14
- package/models/cron-job.model.js +0 -4
- package/models/cron-job.model.js.map +0 -1
- package/models/customer-notification.model.d.ts +0 -26
- package/models/customer-notification.model.js +0 -4
- package/models/customer-notification.model.js.map +0 -1
- package/models/customer-portal-password.model.d.ts +0 -11
- package/models/customer-portal-password.model.js +0 -4
- package/models/customer-portal-password.model.js.map +0 -1
- package/models/dialog.model.d.ts +0 -23
- package/models/dialog.model.js +0 -4
- package/models/dialog.model.js.map +0 -1
- package/models/email-history.model.d.ts +0 -32
- package/models/email-history.model.js.map +0 -1
- package/models/email-verified.model.js +0 -4
- package/models/email-verified.model.js.map +0 -1
- package/models/file.model.js +0 -4
- package/models/file.model.js.map +0 -1
- package/models/flag-update.model.js +0 -4
- package/models/flag-update.model.js.map +0 -1
- package/models/flag.model.js +0 -4
- package/models/flag.model.js.map +0 -1
- package/models/log-method-latency.model.d.ts +0 -10
- package/models/log-method-latency.model.js +0 -4
- package/models/log-method-latency.model.js.map +0 -1
- package/models/log-subscription.model.js +0 -4
- package/models/log-subscription.model.js.map +0 -1
- package/models/log.model.d.ts +0 -17
- package/models/log.model.js +0 -4
- package/models/log.model.js.map +0 -1
- package/models/logged-in-users.model.js +0 -4
- package/models/logged-in-users.model.js.map +0 -1
- package/models/method-response.model.js +0 -4
- package/models/method-response.model.js.map +0 -1
- package/models/method.model.d.ts +0 -26
- package/models/method.model.js +0 -4
- package/models/method.model.js.map +0 -1
- package/models/monitor-cpu.model.js +0 -4
- package/models/monitor-cpu.model.js.map +0 -1
- package/models/monitor-function.model.d.ts +0 -14
- package/models/monitor-function.model.js +0 -4
- package/models/monitor-function.model.js.map +0 -1
- package/models/monitor-memory.model.d.ts +0 -15
- package/models/monitor-memory.model.js +0 -4
- package/models/monitor-memory.model.js.map +0 -1
- package/models/monitor-mongo.model.d.ts +0 -13
- package/models/monitor-mongo.model.js +0 -4
- package/models/monitor-mongo.model.js.map +0 -1
- package/models/notification.model.js +0 -4
- package/models/notification.model.js.map +0 -1
- package/models/openai-usage-ledger.model.d.ts +0 -30
- package/models/openai-usage-ledger.model.js +0 -4
- package/models/openai-usage-ledger.model.js.map +0 -1
- package/models/pagination.model.d.ts +0 -11
- package/models/pagination.model.js +0 -28
- package/models/pagination.model.js.map +0 -1
- package/models/permission.model.d.ts +0 -12
- package/models/permission.model.js +0 -4
- package/models/permission.model.js.map +0 -1
- package/models/report-builder-dashboard-builder.model.d.ts +0 -25
- package/models/report-builder-dashboard-builder.model.js +0 -4
- package/models/report-builder-dashboard-builder.model.js.map +0 -1
- package/models/report-builder-library.model.d.ts +0 -17
- package/models/report-builder-library.model.js +0 -4
- package/models/report-builder-library.model.js.map +0 -1
- package/models/report-builder-report.model.d.ts +0 -121
- package/models/report-builder-report.model.js +0 -4
- package/models/report-builder-report.model.js.map +0 -1
- package/models/report-builder.model.d.ts +0 -61
- package/models/report-builder.model.js +0 -4
- package/models/report-builder.model.js.map +0 -1
- package/models/select-data-label.model.d.ts +0 -9
- package/models/select-data-label.model.js +0 -4
- package/models/select-data-label.model.js.map +0 -1
- package/models/server-message.model.d.ts +0 -32
- package/models/server-message.model.js +0 -4
- package/models/server-message.model.js.map +0 -1
- package/models/slow-query-report.model.d.ts +0 -23
- package/models/slow-query-report.model.js +0 -4
- package/models/slow-query-report.model.js.map +0 -1
- package/models/subscription.model.d.ts +0 -31
- package/models/subscription.model.js +0 -4
- package/models/subscription.model.js.map +0 -1
- package/models/support-ticket.model.d.ts +0 -87
- package/models/support-ticket.model.js +0 -4
- package/models/support-ticket.model.js.map +0 -1
- package/models/user-group.model.d.ts +0 -20
- package/models/user-group.model.js +0 -4
- package/models/user-group.model.js.map +0 -1
- package/models/user-guide.model.js +0 -4
- package/models/user-guide.model.js.map +0 -1
- package/models/user.model.d.ts +0 -84
- package/models/user.model.js +0 -4
- package/models/user.model.js.map +0 -1
- package/private/images/ResolveIO.png +0 -0
- package/public_api.js +0 -127
- package/public_api.js.map +0 -1
- package/publications/ai-terminal.d.ts +0 -1
- package/publications/ai-terminal.js +0 -122
- package/publications/ai-terminal.js.map +0 -1
- package/publications/app-settings.d.ts +0 -2
- package/publications/app-settings.js +0 -28
- package/publications/app-settings.js.map +0 -1
- package/publications/app-status.d.ts +0 -2
- package/publications/app-status.js +0 -16
- package/publications/app-status.js.map +0 -1
- package/publications/cron-jobs.d.ts +0 -2
- package/publications/cron-jobs.js +0 -88
- package/publications/cron-jobs.js.map +0 -1
- package/publications/customer-notifications.d.ts +0 -2
- package/publications/customer-notifications.js +0 -161
- package/publications/customer-notifications.js.map +0 -1
- package/publications/files.d.ts +0 -2
- package/publications/files.js +0 -36
- package/publications/files.js.map +0 -1
- package/publications/flags-update.d.ts +0 -2
- package/publications/flags-update.js +0 -22
- package/publications/flags-update.js.map +0 -1
- package/publications/flags.d.ts +0 -2
- package/publications/flags.js +0 -22
- package/publications/flags.js.map +0 -1
- package/publications/logs.d.ts +0 -2
- package/publications/logs.js +0 -164
- package/publications/logs.js.map +0 -1
- package/publications/notifications.d.ts +0 -2
- package/publications/notifications.js +0 -16
- package/publications/notifications.js.map +0 -1
- package/publications/report-builder-dashboard-builders.d.ts +0 -2
- package/publications/report-builder-dashboard-builders.js +0 -42
- package/publications/report-builder-dashboard-builders.js.map +0 -1
- package/publications/report-builder-libraries.d.ts +0 -2
- package/publications/report-builder-libraries.js +0 -90
- package/publications/report-builder-libraries.js.map +0 -1
- package/publications/report-builder-reports.d.ts +0 -2
- package/publications/report-builder-reports.js +0 -50
- package/publications/report-builder-reports.js.map +0 -1
- package/publications/super-admin.d.ts +0 -2
- package/publications/super-admin.js +0 -16
- package/publications/super-admin.js.map +0 -1
- package/publications/user-groups.d.ts +0 -1
- package/publications/user-groups.js +0 -16
- package/publications/user-groups.js.map +0 -1
- package/publications/user-guides.d.ts +0 -1
- package/publications/user-guides.js +0 -16
- package/publications/user-guides.js.map +0 -1
- package/resolveio-server-app.d.ts +0 -70
- package/resolveio-server-app.js +0 -801
- package/resolveio-server-app.js.map +0 -1
- package/server-app.d.ts +0 -228
- package/server-app.js +0 -3566
- package/server-app.js.map +0 -1
- package/services/codex-client.d.ts +0 -128
- package/services/codex-client.js +0 -1629
- package/services/codex-client.js.map +0 -1
- package/services/openai-client.d.ts +0 -46
- package/services/openai-client.js +0 -318
- package/services/openai-client.js.map +0 -1
- package/types/error-report.d.ts +0 -25
- package/types/error-report.js +0 -4
- package/types/error-report.js.map +0 -1
- package/types/slow-query-report.d.ts +0 -27
- package/types/slow-query-report.js +0 -6
- package/types/slow-query-report.js.map +0 -1
- package/util/ai-qa-policy.d.ts +0 -124
- package/util/ai-qa-policy.js +0 -736
- package/util/ai-qa-policy.js.map +0 -1
- package/util/ai-run-evidence-adapters.d.ts +0 -109
- package/util/ai-run-evidence-adapters.js +0 -7234
- package/util/ai-run-evidence-adapters.js.map +0 -1
- package/util/ai-run-evidence-dashboard.d.ts +0 -88
- package/util/ai-run-evidence-dashboard.js +0 -343
- package/util/ai-run-evidence-dashboard.js.map +0 -1
- package/util/ai-run-evidence-eval.d.ts +0 -86
- package/util/ai-run-evidence-eval.js +0 -1018
- package/util/ai-run-evidence-eval.js.map +0 -1
- package/util/ai-run-evidence.d.ts +0 -244
- package/util/ai-run-evidence.js +0 -1096
- package/util/ai-run-evidence.js.map +0 -1
- package/util/ai-runner-artifacts.d.ts +0 -82
- package/util/ai-runner-artifacts.js +0 -713
- package/util/ai-runner-artifacts.js.map +0 -1
- package/util/ai-runner-manager-autopilot.d.ts +0 -210
- package/util/ai-runner-manager-autopilot.js +0 -642
- package/util/ai-runner-manager-autopilot.js.map +0 -1
- package/util/ai-runner-manager-policy.d.ts +0 -807
- package/util/ai-runner-manager-policy.js +0 -3501
- package/util/ai-runner-manager-policy.js.map +0 -1
- package/util/ai-runner-qa-auth.d.ts +0 -5
- package/util/ai-runner-qa-auth.js +0 -839
- package/util/ai-runner-qa-auth.js.map +0 -1
- package/util/ai-runner-qa-tools.d.ts +0 -26
- package/util/ai-runner-qa-tools.js +0 -3520
- package/util/ai-runner-qa-tools.js.map +0 -1
- package/util/aicoder-runner-v6.d.ts +0 -426
- package/util/aicoder-runner-v6.js +0 -2464
- package/util/aicoder-runner-v6.js.map +0 -1
- package/util/common.d.ts +0 -31
- package/util/common.js +0 -683
- package/util/common.js.map +0 -1
- package/util/customer-portal-password.d.ts +0 -13
- package/util/customer-portal-password.js +0 -209
- package/util/customer-portal-password.js.map +0 -1
- package/util/error-reporter.d.ts +0 -52
- package/util/error-reporter.js +0 -326
- package/util/error-reporter.js.map +0 -1
- package/util/error-tracking.d.ts +0 -13
- package/util/error-tracking.js +0 -120
- package/util/error-tracking.js.map +0 -1
- package/util/openai-usage-cost.d.ts +0 -6
- package/util/openai-usage-cost.js +0 -103
- package/util/openai-usage-cost.js.map +0 -1
- package/util/report-builder-unwinds.d.ts +0 -15
- package/util/report-builder-unwinds.js +0 -156
- package/util/report-builder-unwinds.js.map +0 -1
- package/util/runner-process-janitor.d.ts +0 -27
- package/util/runner-process-janitor.js +0 -208
- package/util/runner-process-janitor.js.map +0 -1
- package/util/schema-report-builder.d.ts +0 -6
- package/util/schema-report-builder.js +0 -481
- package/util/schema-report-builder.js.map +0 -1
- package/util/slow-query-reporter.d.ts +0 -28
- package/util/slow-query-reporter.js +0 -226
- package/util/slow-query-reporter.js.map +0 -1
- package/util/subscription-dependency-context.d.ts +0 -34
- package/util/subscription-dependency-context.js +0 -1283
- package/util/subscription-dependency-context.js.map +0 -1
- package/util/support-runner-v5.d.ts +0 -1426
- package/util/support-runner-v5.js +0 -7624
- package/util/support-runner-v5.js.map +0 -1
- package/util/tokenizer.d.ts +0 -5
- package/util/tokenizer.js +0 -41
- package/util/tokenizer.js.map +0 -1
- package/workers/codex-runner.worker.d.ts +0 -1
- package/workers/codex-runner.worker.js +0 -192
- package/workers/codex-runner.worker.js.map +0 -1
- /package/{private → src/private}/email-templates/enrollment.html +0 -0
- /package/{private → src/private}/email-templates/forgot-password.html +0 -0
- /package/{private → src/private}/email-templates/support-ticket-deleted.html +0 -0
- /package/{private → src/private}/email-templates/support-ticket-modified.html +0 -0
- /package/{private → src/private}/email-templates/support-ticket.html +0 -0
- /package/{public_api.d.ts → src/public_api.ts} +0 -0
|
@@ -0,0 +1,1857 @@
|
|
|
1
|
+
import { S3 } from '@aws-sdk/client-s3';
|
|
2
|
+
import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2';
|
|
3
|
+
import { EventEmitter, once } from 'events';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as nodemailer from 'nodemailer';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { setTimeout as delay } from 'timers/promises';
|
|
8
|
+
import { EmailHistories } from '../collections/email-history.collection';
|
|
9
|
+
import { Flags } from '../collections/flag.collection';
|
|
10
|
+
import { Logs } from '../collections/log.collection';
|
|
11
|
+
import { loadServerCronJobs } from '../fixtures/cron-jobs';
|
|
12
|
+
import { loadServerInit } from '../fixtures/init';
|
|
13
|
+
import { loadAccountMethods } from '../methods/accounts';
|
|
14
|
+
import { loadAppSettingsMethods } from '../methods/app-settings';
|
|
15
|
+
import { loadAWSMethods } from '../methods/aws';
|
|
16
|
+
import { loadAiTerminalMethods } from '../methods/ai-terminal';
|
|
17
|
+
import { loadCollectionMethods } from '../methods/collections';
|
|
18
|
+
import { loadCustomerNotificationMethods } from '../methods/customer-notifications';
|
|
19
|
+
import { loadCounterMethods } from '../methods/counters';
|
|
20
|
+
import { loadCronJobMethods } from '../methods/cron-jobs';
|
|
21
|
+
import { loadDiagnosticMethods } from '../methods/diagnostics';
|
|
22
|
+
import { loadFlagUpdatesMethods } from '../methods/flag-updates';
|
|
23
|
+
import { loadFlagMethods } from '../methods/flags';
|
|
24
|
+
import { loadLogMethods } from '../methods/logs';
|
|
25
|
+
import { loadMonitorMethods } from '../methods/monitor';
|
|
26
|
+
import { loadMongoExplorerMethods } from '../methods/mongo-explorer';
|
|
27
|
+
import { loadPublicationMethods } from '../methods/publications';
|
|
28
|
+
import { loadPDFMethods } from '../methods/pdf';
|
|
29
|
+
import { loadReportBuilderMethods } from '../methods/report-builder';
|
|
30
|
+
import { loadSupportMethods } from '../methods/support';
|
|
31
|
+
import { EmailHistoryModel, EmailHistoryOccurrence } from '../models/email-history.model';
|
|
32
|
+
import { MethodAllModel, MethodModel } from '../models/method.model';
|
|
33
|
+
import { ResolveIOServer } from '../resolveio-server-app';
|
|
34
|
+
import { getBinarySize, objectIdHexString } from '../util/common';
|
|
35
|
+
import { ErrorReporter } from '../util/error-reporter';
|
|
36
|
+
import { ensureErrorWithCorrelation, getCorrelationId, runWithCorrelationContext } from '../util/error-tracking';
|
|
37
|
+
import { MonitorManagerFunction } from './monitor.manager';
|
|
38
|
+
import { WebSocketManager } from './websocket.manager';
|
|
39
|
+
import { recordEmailMetric, recordTextMessageMetric } from './communication-metric.manager';
|
|
40
|
+
|
|
41
|
+
interface SendEmailOptions {
|
|
42
|
+
correlationId?: string;
|
|
43
|
+
meta?: Record<string, any>;
|
|
44
|
+
headers?: Record<string, string>;
|
|
45
|
+
cc?: string | string[];
|
|
46
|
+
waitForCompletion?: boolean;
|
|
47
|
+
waitForCompletionTimeoutMs?: number;
|
|
48
|
+
waitForCompletionPollMs?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const CALL_METHOD_LOG_SKIP_LIST = new Set([
|
|
52
|
+
'countQuery',
|
|
53
|
+
'getSignedUrls',
|
|
54
|
+
'getSignedUrl',
|
|
55
|
+
'getSignedUrlWithId',
|
|
56
|
+
'getSignedUrlsAndFilesWithId',
|
|
57
|
+
'updateDocumentProps',
|
|
58
|
+
'insertDocument',
|
|
59
|
+
'updateDocument',
|
|
60
|
+
'uploadFileAndSave'
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
function shouldWriteCallMethodLog(methodName: string, method?: MethodAllModel): boolean {
|
|
64
|
+
if (method?.bypassLog || method?.bypassLogs) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (CALL_METHOD_LOG_SKIP_LIST.has(methodName)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function appendCorrelationIdToSubject(subject: string, correlationId?: string): string {
|
|
76
|
+
if (!correlationId) {
|
|
77
|
+
return subject;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const correlationTag = `[${correlationId}]`;
|
|
81
|
+
|
|
82
|
+
if (subject && subject.includes(correlationTag)) {
|
|
83
|
+
return subject;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return `${subject} ${correlationTag}`.trim();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function isDevEmailRedirectEnvironment(): boolean {
|
|
90
|
+
const rootUrl = ResolveIOServer.getServerConfig()['ROOT_URL'];
|
|
91
|
+
return !!(
|
|
92
|
+
rootUrl.match(/https:\/\/dev\./) ||
|
|
93
|
+
rootUrl.match(/https:\/\/www\.dev\./) ||
|
|
94
|
+
rootUrl === 'http://localhost:4200'
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function normalizeEmailRecipients(recipients?: string | string[] | null): string[] {
|
|
99
|
+
if (!recipients) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const list = Array.isArray(recipients) ? recipients : [recipients];
|
|
104
|
+
return list
|
|
105
|
+
.flatMap(recipient => String(recipient).split(/[;,]/))
|
|
106
|
+
.map(recipient => recipient.trim())
|
|
107
|
+
.filter(Boolean);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function createEmailOccurrence(subject: string, text?: string, html?: string, meta?: Record<string, any>): EmailHistoryOccurrence {
|
|
111
|
+
return {
|
|
112
|
+
_id: objectIdHexString(),
|
|
113
|
+
createdAt: new Date(),
|
|
114
|
+
subject,
|
|
115
|
+
text,
|
|
116
|
+
html,
|
|
117
|
+
meta
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function formatGroupedEmailContent(correlationId: string, occurrences: EmailHistoryOccurrence[]): { text: string; html: string } {
|
|
122
|
+
const lines: string[] = [];
|
|
123
|
+
lines.push(`Correlation ID: ${correlationId}`);
|
|
124
|
+
|
|
125
|
+
occurrences.forEach((occurrence, index) => {
|
|
126
|
+
const timestamp = occurrence.createdAt instanceof Date ? occurrence.createdAt.toISOString() : new Date(occurrence.createdAt).toISOString();
|
|
127
|
+
lines.push('');
|
|
128
|
+
lines.push(`Occurrence #${index + 1} (${timestamp})`);
|
|
129
|
+
lines.push(`Subject: ${occurrence.subject}`);
|
|
130
|
+
if (occurrence.text) {
|
|
131
|
+
lines.push(occurrence.text);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const htmlSections = occurrences.map((occurrence, index) => {
|
|
136
|
+
const timestamp = occurrence.createdAt instanceof Date ? occurrence.createdAt.toISOString() : new Date(occurrence.createdAt).toISOString();
|
|
137
|
+
const escapedText = occurrence.text ? occurrence.text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') : '';
|
|
138
|
+
return `<div><h4>Occurrence #${index + 1} (${timestamp})</h4><p><strong>Subject:</strong> ${occurrence.subject}</p>${escapedText ? `<pre>${escapedText}</pre>` : ''}</div>`;
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const html = [
|
|
142
|
+
`<p><strong>Correlation ID:</strong> ${correlationId}</p>`,
|
|
143
|
+
...htmlSections
|
|
144
|
+
].join('');
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
text: lines.join('\n'),
|
|
148
|
+
html
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const EMAIL_HISTORY_TEXT_MAX_CHARS = 12000;
|
|
153
|
+
const EMAIL_HISTORY_HTML_MAX_CHARS = 20000;
|
|
154
|
+
const EMAIL_HISTORY_ERROR_MAX_CHARS = 8000;
|
|
155
|
+
const EMAIL_HISTORY_META_MAX_CHARS = 6000;
|
|
156
|
+
const EMAIL_HISTORY_ATTACHMENT_MAX_COUNT = 10;
|
|
157
|
+
const EMAIL_HISTORY_ATTACHMENT_FIELD_MAX_CHARS = 1024;
|
|
158
|
+
const EMAIL_HISTORY_OCCURRENCE_MAX_COUNT = 20;
|
|
159
|
+
const EMAIL_HISTORY_OCCURRENCE_TEXT_MAX_CHARS = 4000;
|
|
160
|
+
const EMAIL_HISTORY_OCCURRENCE_HTML_MAX_CHARS = 6000;
|
|
161
|
+
|
|
162
|
+
function truncateTextForHistory(value: unknown, maxChars: number): string {
|
|
163
|
+
if (typeof value !== 'string') {
|
|
164
|
+
return '';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (value.length <= maxChars) {
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const suffix = '...[truncated]';
|
|
172
|
+
const keepLength = Math.max(0, maxChars - suffix.length);
|
|
173
|
+
return `${value.slice(0, keepLength)}${suffix}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function sanitizeMetaForHistory(meta?: Record<string, any>): Record<string, any> | undefined {
|
|
177
|
+
if (!meta || typeof meta !== 'object') {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const serialized = JSON.stringify(meta);
|
|
183
|
+
if (!serialized) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (serialized.length <= EMAIL_HISTORY_META_MAX_CHARS) {
|
|
188
|
+
return JSON.parse(serialized);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
truncated: true,
|
|
193
|
+
originalSize: serialized.length,
|
|
194
|
+
preview: truncateTextForHistory(serialized, EMAIL_HISTORY_META_MAX_CHARS)
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return {
|
|
199
|
+
truncated: true,
|
|
200
|
+
preview: '[unserializable meta]'
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function estimateAttachmentContentLength(content: any, encoding?: string): number | undefined {
|
|
206
|
+
if (Buffer.isBuffer(content)) {
|
|
207
|
+
return content.length;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (content instanceof Uint8Array) {
|
|
211
|
+
return content.byteLength;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (content && typeof content === 'object') {
|
|
215
|
+
if (content._bsontype === 'Binary' && content.buffer && typeof content.buffer.length === 'number') {
|
|
216
|
+
return content.buffer.length;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (Array.isArray(content.data)) {
|
|
220
|
+
return content.data.length;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (typeof content === 'string') {
|
|
225
|
+
if (encoding === 'base64') {
|
|
226
|
+
const normalized = content.replace(/\s/g, '');
|
|
227
|
+
const padding = normalized.endsWith('==') ? 2 : normalized.endsWith('=') ? 1 : 0;
|
|
228
|
+
return Math.max(0, Math.floor((normalized.length * 3) / 4) - padding);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return Buffer.byteLength(content, 'utf8');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function sanitizeAttachmentForHistory(attachment: any): Record<string, any> {
|
|
238
|
+
const source = attachment && typeof attachment === 'object' ? attachment : {};
|
|
239
|
+
const summary: Record<string, any> = {
|
|
240
|
+
contentStored: false
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
if (typeof source.filename === 'string') {
|
|
244
|
+
summary.filename = truncateTextForHistory(source.filename, EMAIL_HISTORY_ATTACHMENT_FIELD_MAX_CHARS);
|
|
245
|
+
}
|
|
246
|
+
if (typeof source.contentType === 'string') {
|
|
247
|
+
summary.contentType = truncateTextForHistory(source.contentType, EMAIL_HISTORY_ATTACHMENT_FIELD_MAX_CHARS);
|
|
248
|
+
}
|
|
249
|
+
if (typeof source.contentDisposition === 'string') {
|
|
250
|
+
summary.contentDisposition = truncateTextForHistory(source.contentDisposition, EMAIL_HISTORY_ATTACHMENT_FIELD_MAX_CHARS);
|
|
251
|
+
}
|
|
252
|
+
if (typeof source.cid === 'string') {
|
|
253
|
+
summary.cid = truncateTextForHistory(source.cid, EMAIL_HISTORY_ATTACHMENT_FIELD_MAX_CHARS);
|
|
254
|
+
}
|
|
255
|
+
if (typeof source.path === 'string') {
|
|
256
|
+
summary.path = truncateTextForHistory(source.path, EMAIL_HISTORY_ATTACHMENT_FIELD_MAX_CHARS);
|
|
257
|
+
}
|
|
258
|
+
if (typeof source.href === 'string') {
|
|
259
|
+
summary.href = truncateTextForHistory(source.href, EMAIL_HISTORY_ATTACHMENT_FIELD_MAX_CHARS);
|
|
260
|
+
}
|
|
261
|
+
if (typeof source.encoding === 'string') {
|
|
262
|
+
summary.encoding = truncateTextForHistory(source.encoding, EMAIL_HISTORY_ATTACHMENT_FIELD_MAX_CHARS);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const contentLength = typeof source.size === 'number'
|
|
266
|
+
? source.size
|
|
267
|
+
: estimateAttachmentContentLength(source.content, source.encoding);
|
|
268
|
+
if (typeof contentLength === 'number' && Number.isFinite(contentLength) && contentLength >= 0) {
|
|
269
|
+
summary.contentLength = contentLength;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return summary;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function sanitizeAttachmentsForHistory(attachments: any[]): Record<string, any>[] {
|
|
276
|
+
if (!Array.isArray(attachments) || !attachments.length) {
|
|
277
|
+
return [];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const summarized = attachments
|
|
281
|
+
.slice(0, EMAIL_HISTORY_ATTACHMENT_MAX_COUNT)
|
|
282
|
+
.map((attachment) => sanitizeAttachmentForHistory(attachment));
|
|
283
|
+
|
|
284
|
+
if (attachments.length > EMAIL_HISTORY_ATTACHMENT_MAX_COUNT) {
|
|
285
|
+
summarized.push({
|
|
286
|
+
contentStored: false,
|
|
287
|
+
omittedCount: attachments.length - EMAIL_HISTORY_ATTACHMENT_MAX_COUNT
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return summarized;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function sanitizeOccurrencesForHistory(occurrences?: EmailHistoryOccurrence[]): EmailHistoryOccurrence[] | undefined {
|
|
295
|
+
if (!Array.isArray(occurrences) || !occurrences.length) {
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return occurrences.slice(-EMAIL_HISTORY_OCCURRENCE_MAX_COUNT).map((occurrence) => {
|
|
300
|
+
const createdAtCandidate = occurrence.createdAt instanceof Date ? occurrence.createdAt : new Date(occurrence.createdAt);
|
|
301
|
+
const createdAt = Number.isNaN(createdAtCandidate.getTime()) ? new Date() : createdAtCandidate;
|
|
302
|
+
const sanitized: EmailHistoryOccurrence = {
|
|
303
|
+
_id: occurrence._id || objectIdHexString(),
|
|
304
|
+
createdAt,
|
|
305
|
+
subject: truncateTextForHistory(occurrence.subject || '', EMAIL_HISTORY_ATTACHMENT_FIELD_MAX_CHARS)
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
if (typeof occurrence.text === 'string' && occurrence.text.length > 0) {
|
|
309
|
+
sanitized.text = truncateTextForHistory(occurrence.text, EMAIL_HISTORY_OCCURRENCE_TEXT_MAX_CHARS);
|
|
310
|
+
}
|
|
311
|
+
if (typeof occurrence.html === 'string' && occurrence.html.length > 0) {
|
|
312
|
+
sanitized.html = truncateTextForHistory(occurrence.html, EMAIL_HISTORY_OCCURRENCE_HTML_MAX_CHARS);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const sanitizedMeta = sanitizeMetaForHistory(occurrence.meta);
|
|
316
|
+
if (sanitizedMeta) {
|
|
317
|
+
sanitized.meta = sanitizedMeta;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return sanitized;
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function createNodeMailerSESV2Transport(sesClient: SESv2Client): nodemailer.Transporter {
|
|
325
|
+
const transport: any = {
|
|
326
|
+
name: 'resolveio-sesv2-transport',
|
|
327
|
+
version: '1.0.0',
|
|
328
|
+
send: (mail: any, callback: any) => {
|
|
329
|
+
const stream = mail.message.createReadStream();
|
|
330
|
+
const chunks: Uint8Array[] = [];
|
|
331
|
+
let didFinish = false;
|
|
332
|
+
|
|
333
|
+
const finish = (error: any, info?: any) => {
|
|
334
|
+
if (didFinish) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
didFinish = true;
|
|
339
|
+
callback(error, info);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
stream.on('data', (chunk) => {
|
|
343
|
+
const chunkData = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
344
|
+
chunks.push(new Uint8Array(chunkData));
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
stream.on('error', (error) => {
|
|
348
|
+
finish(error);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
stream.on('end', async () => {
|
|
352
|
+
try {
|
|
353
|
+
const rawData = Buffer.concat(chunks);
|
|
354
|
+
const rawMessageData = new Uint8Array(rawData);
|
|
355
|
+
const envelope = typeof mail.message.getEnvelope === 'function' ? mail.message.getEnvelope() : mail.data?.envelope;
|
|
356
|
+
|
|
357
|
+
const fromEmailAddress = envelope?.from || undefined;
|
|
358
|
+
const toAddresses = envelope?.to
|
|
359
|
+
? (Array.isArray(envelope.to) ? envelope.to : [envelope.to]).filter(Boolean)
|
|
360
|
+
: [];
|
|
361
|
+
|
|
362
|
+
const replyToAddresses = mail.data?.replyTo
|
|
363
|
+
? (Array.isArray(mail.data.replyTo) ? mail.data.replyTo : [mail.data.replyTo]).filter(Boolean)
|
|
364
|
+
: undefined;
|
|
365
|
+
|
|
366
|
+
const response = await sesClient.send(new SendEmailCommand({
|
|
367
|
+
FromEmailAddress: fromEmailAddress,
|
|
368
|
+
Destination: toAddresses.length ? { ToAddresses: toAddresses } : undefined,
|
|
369
|
+
ReplyToAddresses: replyToAddresses,
|
|
370
|
+
Content: {
|
|
371
|
+
Raw: {
|
|
372
|
+
Data: rawMessageData
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}));
|
|
376
|
+
|
|
377
|
+
finish(null, {
|
|
378
|
+
envelope,
|
|
379
|
+
messageId: response.MessageId,
|
|
380
|
+
response: response.MessageId
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
finish(error);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
return nodemailer.createTransport(transport);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export class AWS {
|
|
394
|
+
private _s3: S3 = null;
|
|
395
|
+
private _s3USEast1: S3 = null;
|
|
396
|
+
|
|
397
|
+
constructor() {}
|
|
398
|
+
|
|
399
|
+
public create() {
|
|
400
|
+
const aws = new AWS();
|
|
401
|
+
aws.initialize();
|
|
402
|
+
return aws;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private initialize() {
|
|
406
|
+
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
public s3(): S3 {
|
|
410
|
+
if (this._s3) {
|
|
411
|
+
return this._s3;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
this._s3 = new S3({
|
|
415
|
+
credentials: {
|
|
416
|
+
accessKeyId: process.env.AWS_ACCESS_KEY,
|
|
417
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
|
|
418
|
+
},
|
|
419
|
+
region: process.env.AWS_REGION,
|
|
420
|
+
apiVersion: '2006-03-01'
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
return this._s3;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
public s3USEast1(): S3 {
|
|
427
|
+
if (process.env.AWS_REGION === 'us-east-1') {
|
|
428
|
+
return this.s3();
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
if (this._s3USEast1) {
|
|
432
|
+
return this._s3USEast1;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
this._s3USEast1 = new S3({
|
|
436
|
+
credentials: {
|
|
437
|
+
accessKeyId: process.env.AWS_ACCESS_KEY,
|
|
438
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
|
|
439
|
+
},
|
|
440
|
+
region: 'us-east-1',
|
|
441
|
+
apiVersion: '2006-03-01'
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
return this._s3USEast1;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export class MethodManager {
|
|
450
|
+
private _websocketManager: WebSocketManager;
|
|
451
|
+
public _methods: MethodModel = {};
|
|
452
|
+
private _mailerSES: nodemailer.Transporter;
|
|
453
|
+
private _mailerCustom: nodemailer.Transporter;
|
|
454
|
+
private _aws: AWS;
|
|
455
|
+
private _monitorManagerFunction: MonitorManagerFunction;
|
|
456
|
+
private _isWorkersEnabled = false;
|
|
457
|
+
private _isWorkerInstance = false;
|
|
458
|
+
|
|
459
|
+
private emailQueue: Set<string> = new Set(); // Set to store pending email IDs
|
|
460
|
+
private isEmailProcessing = false;
|
|
461
|
+
private readonly _emailProcessingDelayMs = 5000;
|
|
462
|
+
|
|
463
|
+
private _debugcallMethodHits = 0;
|
|
464
|
+
private _debugCallMethodHits = 0;
|
|
465
|
+
private _debugCallMethodCronJobHits = 0;
|
|
466
|
+
private _debugSendQueueHits = 0;
|
|
467
|
+
private _enableDebug = false;
|
|
468
|
+
private _ready = false;
|
|
469
|
+
private _readyEmitter = new EventEmitter();
|
|
470
|
+
private _readyError: any = null;
|
|
471
|
+
|
|
472
|
+
private _localActiveCounts: Map<string, number> = new Map();
|
|
473
|
+
private _localWaitQueues: Map<string, Array<() => void>> = new Map();
|
|
474
|
+
|
|
475
|
+
public clientDir = '';
|
|
476
|
+
public serverConfig: Record<string, any> = {};
|
|
477
|
+
|
|
478
|
+
constructor() {}
|
|
479
|
+
|
|
480
|
+
static create(websocketManager: WebSocketManager, monitorManagerFunction: MonitorManagerFunction, isWorkersEnabled, isWorkerInstance) {
|
|
481
|
+
const methodManager = new MethodManager();
|
|
482
|
+
methodManager.initialize(websocketManager, monitorManagerFunction, isWorkersEnabled, isWorkerInstance);
|
|
483
|
+
return methodManager;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
private initialize(websocketManager: WebSocketManager, monitorManagerFunction: MonitorManagerFunction, isWorkersEnabled, isWorkerInstance) {
|
|
487
|
+
this._websocketManager = websocketManager;
|
|
488
|
+
this._monitorManagerFunction = monitorManagerFunction;
|
|
489
|
+
this._isWorkersEnabled = isWorkersEnabled;
|
|
490
|
+
this._isWorkerInstance = isWorkerInstance;
|
|
491
|
+
|
|
492
|
+
this.clientDir = ResolveIOServer.getClientDir();
|
|
493
|
+
this.serverConfig = ResolveIOServer.getServerConfig();
|
|
494
|
+
|
|
495
|
+
// Fixtures
|
|
496
|
+
if (!process.env.IS_WORKERS_ENABLED || process.env.IS_WORKERS_ENABLED === 'false' || (process.env.IS_WORKER_INSTANCE === 'true' && process.env.WORKER_INDEX === '0')) {
|
|
497
|
+
if (process.env.IS_WORKERS_ENABLED === 'false' || process.env.NODE_APP_INSTANCE === '1') {
|
|
498
|
+
setTimeout(async () => {
|
|
499
|
+
console.log(new Date(), 'Start Server Fixture');
|
|
500
|
+
await loadServerInit();
|
|
501
|
+
console.log(new Date(), 'End Server Fixture');
|
|
502
|
+
}, 5000);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
setImmediate(async () => {
|
|
507
|
+
try {
|
|
508
|
+
// Methods
|
|
509
|
+
await loadServerCronJobs();
|
|
510
|
+
|
|
511
|
+
loadAccountMethods(this);
|
|
512
|
+
loadAppSettingsMethods(this);
|
|
513
|
+
loadAWSMethods(this);
|
|
514
|
+
loadAiTerminalMethods(this);
|
|
515
|
+
loadCollectionMethods(this);
|
|
516
|
+
loadCustomerNotificationMethods(this);
|
|
517
|
+
loadCounterMethods(this);
|
|
518
|
+
loadLogMethods(this);
|
|
519
|
+
loadPDFMethods(this);
|
|
520
|
+
loadCronJobMethods(this);
|
|
521
|
+
loadFlagMethods(this);
|
|
522
|
+
loadFlagUpdatesMethods(this);
|
|
523
|
+
loadReportBuilderMethods(this);
|
|
524
|
+
loadSupportMethods(this);
|
|
525
|
+
loadMonitorMethods(this);
|
|
526
|
+
loadMongoExplorerMethods(this);
|
|
527
|
+
loadPublicationMethods(this);
|
|
528
|
+
loadDiagnosticMethods(this);
|
|
529
|
+
|
|
530
|
+
let flag = await Flags.findOne({type: 'Enable Debug'});
|
|
531
|
+
|
|
532
|
+
if (flag && flag.value) {
|
|
533
|
+
this._enableDebug = true;
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
this._enableDebug = false;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
await this.loadPendingEmails();
|
|
540
|
+
this.markReady();
|
|
541
|
+
}
|
|
542
|
+
catch (error) {
|
|
543
|
+
console.error(new Date(), 'Method Manager init failed', error);
|
|
544
|
+
this.markReadyFailure(error);
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
this._aws = new AWS();
|
|
549
|
+
|
|
550
|
+
const sesRegion = process.env.AWS_SES_REGION
|
|
551
|
+
|| this.serverConfig?.AWS_SES_REGION
|
|
552
|
+
|| this.serverConfig?.AWSSES_REGION
|
|
553
|
+
|| process.env.AWS_REGION
|
|
554
|
+
|| process.env.AWS_DEFAULT_REGION
|
|
555
|
+
|| 'us-east-1';
|
|
556
|
+
const sesCredentials = process.env.AWS_ACCESS_KEY && process.env.AWS_SECRET_ACCESS_KEY ? {
|
|
557
|
+
accessKeyId: process.env.AWS_ACCESS_KEY,
|
|
558
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
559
|
+
sessionToken: process.env.AWS_SESSION_TOKEN
|
|
560
|
+
} : undefined;
|
|
561
|
+
|
|
562
|
+
this._mailerSES = createNodeMailerSESV2Transport(new SESv2Client({
|
|
563
|
+
region: sesRegion,
|
|
564
|
+
credentials: sesCredentials
|
|
565
|
+
}));
|
|
566
|
+
|
|
567
|
+
if (!ResolveIOServer.getSESMail()) {
|
|
568
|
+
this._mailerCustom = nodemailer.createTransport({
|
|
569
|
+
host: ResolveIOServer.getServerConfig()['MAIL_HOST'], // 'smtp.office365.com', // Office 365 server
|
|
570
|
+
port: ResolveIOServer.getServerConfig()['MAIL_PORT'], //587, // secure SMTP
|
|
571
|
+
secure: false, // false for TLS - as a boolean not string - but the default is false so just remove this completely
|
|
572
|
+
auth: {
|
|
573
|
+
user: ResolveIOServer.getServerConfig()['MAIL_USERNAME'],
|
|
574
|
+
pass: ResolveIOServer.getServerConfig()['MAIL_PASSWORD']
|
|
575
|
+
},
|
|
576
|
+
tls: {
|
|
577
|
+
ciphers: 'SSLv3'
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
setInterval(async () => {
|
|
583
|
+
let flag = await Flags.findOne({type: 'Enable Debug'});
|
|
584
|
+
|
|
585
|
+
if (flag && flag.value) {
|
|
586
|
+
this._enableDebug = true;
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
this._enableDebug = false;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (this._enableDebug) {
|
|
593
|
+
console.log(new Date(), 'Method Manager', 'Send Queue Hits', this._debugSendQueueHits);
|
|
594
|
+
console.log(new Date(), 'Method Manager', 'Call Method Internal Hits', this._debugcallMethodHits);
|
|
595
|
+
console.log(new Date(), 'Method Manager', 'Call Method Hits', this._debugCallMethodHits);
|
|
596
|
+
console.log(new Date(), 'Method Manager', 'Call Method Cron Hits', this._debugCallMethodCronJobHits);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
this._debugcallMethodHits = 0;
|
|
600
|
+
this._debugCallMethodHits = 0;
|
|
601
|
+
this._debugCallMethodCronJobHits = 0;
|
|
602
|
+
this._debugSendQueueHits = 0;
|
|
603
|
+
}, 60000);
|
|
604
|
+
|
|
605
|
+
if (!this._isWorkersEnabled || this._isWorkerInstance) {
|
|
606
|
+
this.setupEmailWatcher();
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
private markReady(): void {
|
|
611
|
+
if (this._ready) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
this._ready = true;
|
|
615
|
+
this._readyEmitter.emit('ready');
|
|
616
|
+
this._readyEmitter.removeAllListeners('ready');
|
|
617
|
+
this._readyEmitter.removeAllListeners('readyError');
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
private markReadyFailure(error: any): void {
|
|
621
|
+
if (this._ready) {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
this._readyError = error;
|
|
625
|
+
this._readyEmitter.emit('readyError', error);
|
|
626
|
+
this._readyEmitter.removeAllListeners('ready');
|
|
627
|
+
this._readyEmitter.removeAllListeners('readyError');
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
public onReady(callback: () => void): () => void {
|
|
631
|
+
if (this._ready) {
|
|
632
|
+
callback();
|
|
633
|
+
return () => {};
|
|
634
|
+
}
|
|
635
|
+
if (this._readyError) {
|
|
636
|
+
return () => {};
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const handleReady = () => {
|
|
640
|
+
callback();
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
this._readyEmitter.once('ready', handleReady);
|
|
644
|
+
|
|
645
|
+
return () => {
|
|
646
|
+
this._readyEmitter.removeListener('ready', handleReady);
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
private async waitForReadyEvent(): Promise<void> {
|
|
651
|
+
await once(this._readyEmitter, 'ready');
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
private async waitForReadyError(): Promise<void> {
|
|
655
|
+
const [error] = await once(this._readyEmitter, 'readyError');
|
|
656
|
+
throw error || new Error('MethodManager failed to initialize');
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
private async waitForReadyTimeout(timeoutMs: number): Promise<void> {
|
|
660
|
+
await delay(timeoutMs);
|
|
661
|
+
throw new Error('MethodManager ready timeout');
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
public isReady(): boolean {
|
|
665
|
+
return this._ready;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
public getReadyError(): any {
|
|
669
|
+
return this._readyError;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
public async waitUntilReady(timeoutMs: number = 30000): Promise<void> {
|
|
673
|
+
if (this._ready) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (this._readyError) {
|
|
677
|
+
throw this._readyError;
|
|
678
|
+
}
|
|
679
|
+
if (!timeoutMs || timeoutMs <= 0) {
|
|
680
|
+
await Promise.race([
|
|
681
|
+
this.waitForReadyEvent(),
|
|
682
|
+
this.waitForReadyError()
|
|
683
|
+
]);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
await Promise.race([
|
|
687
|
+
this.waitForReadyEvent(),
|
|
688
|
+
this.waitForReadyError(),
|
|
689
|
+
this.waitForReadyTimeout(timeoutMs)
|
|
690
|
+
]);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
public getMethod(methodName: string): MethodAllModel {
|
|
694
|
+
return this._methods[methodName];
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Add methods to private methods object
|
|
698
|
+
public methods(method: MethodModel) {
|
|
699
|
+
this._methods = Object.assign(this._methods, method);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
private async reportMethodError(
|
|
703
|
+
subject: string,
|
|
704
|
+
correlationId: string,
|
|
705
|
+
context: Record<string, any> | string,
|
|
706
|
+
meta: Record<string, any>,
|
|
707
|
+
stack?: string
|
|
708
|
+
) {
|
|
709
|
+
const metadata = Object.assign({}, meta || {});
|
|
710
|
+
if (correlationId && !metadata.correlationId) {
|
|
711
|
+
metadata.correlationId = correlationId;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
await ErrorReporter.report({
|
|
715
|
+
sourceApp: 'method-manager',
|
|
716
|
+
message: subject,
|
|
717
|
+
environment: this.serverConfig?.ROOT_URL,
|
|
718
|
+
clientSlug: ResolveIOServer.getClientName(),
|
|
719
|
+
clientName: this.serverConfig?.CLIENT_NAME,
|
|
720
|
+
severity: 'error',
|
|
721
|
+
stack,
|
|
722
|
+
context,
|
|
723
|
+
metadata,
|
|
724
|
+
correlationId
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
public async callMethodCron(method: string, ...methodData: any[]) {
|
|
729
|
+
this._debugCallMethodCronJobHits += 1;
|
|
730
|
+
|
|
731
|
+
const existingCorrelationId = getCorrelationId();
|
|
732
|
+
const correlationId = existingCorrelationId || objectIdHexString();
|
|
733
|
+
|
|
734
|
+
const execute = async () => {
|
|
735
|
+
const cronMethod = this._methods[method];
|
|
736
|
+
|
|
737
|
+
if (!cronMethod) {
|
|
738
|
+
console.log('No Method: ' + method);
|
|
739
|
+
|
|
740
|
+
await this.reportMethodError(
|
|
741
|
+
'SERVER - Error Detected - ' + this.serverConfig['CLIENT_NAME'],
|
|
742
|
+
correlationId,
|
|
743
|
+
{ message: 'No Method registered for cron execution', method },
|
|
744
|
+
{ context: 'callMethodCron', method }
|
|
745
|
+
);
|
|
746
|
+
|
|
747
|
+
throw new Error('No Method: ' + method);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if ((methodData.length > 1 || methodData[0]) && !cronMethod.skipValidation) {
|
|
751
|
+
if (!cronMethod.check) {
|
|
752
|
+
console.error(new Date(), 'No Check Function For Cron Method ' + method);
|
|
753
|
+
|
|
754
|
+
await this.reportMethodError(
|
|
755
|
+
'SERVER - Error Detected - ' + this.serverConfig['CLIENT_NAME'],
|
|
756
|
+
correlationId,
|
|
757
|
+
{ message: 'Missing check function for cron method', method },
|
|
758
|
+
{ context: 'callMethodCron-validation', method }
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
throw new Error('No Check Function For Cron Method ' + method);
|
|
762
|
+
}
|
|
763
|
+
else if (!cronMethod.check._schema) {
|
|
764
|
+
console.error(new Date(), 'No Check Schema For Cron Method ' + method);
|
|
765
|
+
|
|
766
|
+
await this.reportMethodError(
|
|
767
|
+
'SERVER - Error Detected - ' + this.serverConfig['CLIENT_NAME'],
|
|
768
|
+
correlationId,
|
|
769
|
+
{ message: 'Missing check schema for cron method', method },
|
|
770
|
+
{ context: 'callMethodCron-validation', method }
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
throw new Error('No Check Schema For Cron Method ' + method);
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
const valObj = {};
|
|
777
|
+
const valKeys = Object.keys(cronMethod.check._schema);
|
|
778
|
+
|
|
779
|
+
const rootKeys = valKeys.filter(a => !a.includes('.'));
|
|
780
|
+
|
|
781
|
+
for (let i = 0; i < methodData.length; i++) {
|
|
782
|
+
valObj[rootKeys[i]] = methodData[i];
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
try {
|
|
786
|
+
cronMethod.check.validate(valObj);
|
|
787
|
+
}
|
|
788
|
+
catch (err) {
|
|
789
|
+
console.error(new Date(), 'Error in Cron Method Check (' + method + ')', err);
|
|
790
|
+
|
|
791
|
+
const { error: normalizedError, correlationId: resolvedCorrelationId } = ensureErrorWithCorrelation(err, correlationId);
|
|
792
|
+
|
|
793
|
+
await this.reportMethodError(
|
|
794
|
+
'SERVER - Error Detected - ' + this.serverConfig['CLIENT_NAME'],
|
|
795
|
+
resolvedCorrelationId,
|
|
796
|
+
{
|
|
797
|
+
message: 'Match error on cron method',
|
|
798
|
+
method,
|
|
799
|
+
data: valObj,
|
|
800
|
+
validationError: normalizedError
|
|
801
|
+
},
|
|
802
|
+
{ context: 'callMethodCron-validation-error', method },
|
|
803
|
+
normalizedError.stack
|
|
804
|
+
);
|
|
805
|
+
normalizedError.message = `${new Date().toISOString()} - Error in Cron Method Check (${method}): ${normalizedError.message}`;
|
|
806
|
+
|
|
807
|
+
throw normalizedError;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const monitor = this._monitorManagerFunction.startMonitorFunction('Cron Method', method, '', '', methodData);
|
|
813
|
+
|
|
814
|
+
try {
|
|
815
|
+
const res = await cronMethod.function.call(Object.assign({}, this, MethodManager.prototype, {id_user: '', user: '', id_ws: ''}), ...methodData);
|
|
816
|
+
return res;
|
|
817
|
+
}
|
|
818
|
+
catch (err) {
|
|
819
|
+
const { error: normalizedError, correlationId: resolvedCorrelationId } = ensureErrorWithCorrelation(err, correlationId);
|
|
820
|
+
|
|
821
|
+
await this.reportMethodError(
|
|
822
|
+
'SERVER - Error Detected - ' + this.serverConfig['CLIENT_NAME'],
|
|
823
|
+
resolvedCorrelationId,
|
|
824
|
+
{
|
|
825
|
+
message: 'Error detected during cron method execution',
|
|
826
|
+
method,
|
|
827
|
+
data: methodData,
|
|
828
|
+
error: normalizedError
|
|
829
|
+
},
|
|
830
|
+
{ context: 'callMethodCron-execution', method },
|
|
831
|
+
normalizedError.stack
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
normalizedError.message = `${new Date().toISOString()} - Error in Cron Method (${method}): ${normalizedError.message}`;
|
|
835
|
+
|
|
836
|
+
throw normalizedError;
|
|
837
|
+
}
|
|
838
|
+
finally {
|
|
839
|
+
await this._monitorManagerFunction.finishMonitorFunction(monitor);
|
|
840
|
+
}
|
|
841
|
+
};
|
|
842
|
+
|
|
843
|
+
return await runWithCorrelationContext(correlationId, execute);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// Call/run method internal (No Emit on Socket)
|
|
847
|
+
public async callMethod(methodName: string, ...methodData: any[]): Promise<any> {
|
|
848
|
+
this._debugcallMethodHits += 1;
|
|
849
|
+
|
|
850
|
+
const existingCorrelationId = getCorrelationId();
|
|
851
|
+
const correlationId = existingCorrelationId || objectIdHexString();
|
|
852
|
+
|
|
853
|
+
return await runWithCorrelationContext(correlationId, async () => await this.callMethodInternal(correlationId, methodName, methodData));
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
private async callMethodInternal(correlationId: string, methodName: string, methodData: any[]): Promise<any> {
|
|
857
|
+
const method = this.getMethod(methodName);
|
|
858
|
+
|
|
859
|
+
if (!method) {
|
|
860
|
+
console.log('No Method: ' + methodName);
|
|
861
|
+
throw new Error(`No Method: ${methodName}`);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const shouldTrackTextMetric = this.shouldTrackTextMessageMetric(methodName);
|
|
865
|
+
const textMetricProvider = shouldTrackTextMetric ? this.getTextMessageProvider() : undefined;
|
|
866
|
+
|
|
867
|
+
let releaseLocalConcurrency = await this.acquireLocalConcurrency(methodName, method.maxConcurrency);
|
|
868
|
+
|
|
869
|
+
try {
|
|
870
|
+
if ((methodData.length > 1 || (methodData[0] && typeof methodData[0] !== 'function')) && !method.skipValidation) {
|
|
871
|
+
if (!method.check) {
|
|
872
|
+
console.error(new Date(), 'No Check Function For Method ' + methodName);
|
|
873
|
+
throw new Error(`No Check Function For Method: ${methodName}`);
|
|
874
|
+
}
|
|
875
|
+
else if (!method.check._schema) {
|
|
876
|
+
console.error(new Date(), 'No Check Schema For Method ' + methodName);
|
|
877
|
+
throw new Error(`No Check Schema For Method: ${methodName}`);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
if (shouldWriteCallMethodLog(methodName, method)) {
|
|
882
|
+
if (
|
|
883
|
+
ResolveIOServer.shouldWriteLogsOffline()
|
|
884
|
+
) {
|
|
885
|
+
ResolveIOServer.getLocalLogManager().writeLog({
|
|
886
|
+
type: 'log',
|
|
887
|
+
data: {
|
|
888
|
+
_id: objectIdHexString(),
|
|
889
|
+
createdAt: new Date(),
|
|
890
|
+
type: 'callMethod',
|
|
891
|
+
collection: '',
|
|
892
|
+
id_document: '',
|
|
893
|
+
payload: getBinarySize(JSON.stringify([methodData])) < 1000000 ? JSON.stringify([methodData], null, 2) : 'Too Big',
|
|
894
|
+
method: methodName,
|
|
895
|
+
id_user: this['id_user'] || '',
|
|
896
|
+
user: this['user'] || '',
|
|
897
|
+
messageId: 0,
|
|
898
|
+
route: '',
|
|
899
|
+
instance_index: process.env.NODE_APP_INSTANCE || '0',
|
|
900
|
+
correlationId
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
await Logs.insertOne({
|
|
906
|
+
_id: objectIdHexString(),
|
|
907
|
+
type: 'callMethod',
|
|
908
|
+
collection: '',
|
|
909
|
+
id_document: '',
|
|
910
|
+
payload: getBinarySize(JSON.stringify([methodData])) < 1000000 ? JSON.stringify([methodData], null, 2) : 'Too Big',
|
|
911
|
+
method: methodName,
|
|
912
|
+
id_user: this['id_user'] || '',
|
|
913
|
+
user: this['user'] || '',
|
|
914
|
+
messageId: 0,
|
|
915
|
+
route: '',
|
|
916
|
+
client: 'ResolveIO',
|
|
917
|
+
instance: ResolveIOServer.getInstanceHost(),
|
|
918
|
+
instance_index: process.env.NODE_APP_INSTANCE || '0',
|
|
919
|
+
correlationId
|
|
920
|
+
})
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const methodCallback = typeof(methodData[methodData.length - 1]) === 'function' ? methodData[methodData.length - 1] : null;
|
|
925
|
+
const functionMethodData = methodCallback ? methodData.slice(0, -1) : methodData;
|
|
926
|
+
|
|
927
|
+
const session = ResolveIOServer.getMongoManager().getSession();
|
|
928
|
+
const shouldStartTransaction = !method.bypassSession &&
|
|
929
|
+
!session &&
|
|
930
|
+
![
|
|
931
|
+
'insertErrorLog', // CIRCULAR LOOP - DO NOT REMOVE
|
|
932
|
+
'countWithQuery', // MONGO SESSIONS/TRANSACTIONS DONT WORK WITH COUNTS
|
|
933
|
+
'sendEmail' // ALWAYS SEND SO ALWAYS HAVE RECORD - DONT ROLL BACK - BYPASS SESSION
|
|
934
|
+
].includes(methodName) &&
|
|
935
|
+
!methodName.startsWith('monitor-') &&
|
|
936
|
+
!methodName.startsWith('log');
|
|
937
|
+
|
|
938
|
+
const executeWithExistingSession = async () => {
|
|
939
|
+
// console.log(new Date(), 'Calling Method - Existing Session', methodName);
|
|
940
|
+
let monitor = this._monitorManagerFunction.startMonitorFunction('Method', methodName, this['user'] || '', '', functionMethodData);
|
|
941
|
+
|
|
942
|
+
try {
|
|
943
|
+
let res = await method.function.call(Object.assign({}, this, MethodManager.prototype), ...functionMethodData);
|
|
944
|
+
|
|
945
|
+
if (methodCallback) {
|
|
946
|
+
methodCallback(null, res);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
if (shouldTrackTextMetric) {
|
|
950
|
+
try {
|
|
951
|
+
await recordTextMessageMetric({
|
|
952
|
+
status: 'sent',
|
|
953
|
+
provider: textMetricProvider,
|
|
954
|
+
metadata: {
|
|
955
|
+
method: methodName
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
catch (error) {
|
|
960
|
+
console.error('Error recording text message metric:', error);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
return res;
|
|
965
|
+
}
|
|
966
|
+
catch (err) {
|
|
967
|
+
if (err.code === 112 || err.codeName === 'WriteConflict' || err.code === 251 || err.codeName === 'NoSuchTransaction') {
|
|
968
|
+
throw err; // Write error, retry
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
const { error: normalizedError, correlationId: resolvedCorrelationId } = ensureErrorWithCorrelation(err, correlationId);
|
|
972
|
+
|
|
973
|
+
console.log(JSON.stringify([new Date(), 'Error Method Manager - Run Method - Existing Session', methodName, {
|
|
974
|
+
code: normalizedError.code,
|
|
975
|
+
codeName: normalizedError.codeName,
|
|
976
|
+
message: normalizedError.message,
|
|
977
|
+
stack: normalizedError.stack,
|
|
978
|
+
correlationId: resolvedCorrelationId
|
|
979
|
+
}], null, 2));
|
|
980
|
+
|
|
981
|
+
await this.reportMethodError(
|
|
982
|
+
'SERVER - Error Detected - ' + this.serverConfig['CLIENT_NAME'],
|
|
983
|
+
resolvedCorrelationId,
|
|
984
|
+
{
|
|
985
|
+
message: 'Error detected during method execution (existing session)',
|
|
986
|
+
method: methodName,
|
|
987
|
+
data: methodData,
|
|
988
|
+
error: normalizedError
|
|
989
|
+
},
|
|
990
|
+
{ context: 'callMethod-existing-session', methodName },
|
|
991
|
+
normalizedError.stack
|
|
992
|
+
);
|
|
993
|
+
normalizedError.message = `${new Date().toISOString()} - Error in Method (${methodName}) - Existing Session: ${normalizedError.message}`;
|
|
994
|
+
|
|
995
|
+
if (methodCallback) {
|
|
996
|
+
methodCallback(normalizedError, null);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
if (shouldTrackTextMetric) {
|
|
1000
|
+
try {
|
|
1001
|
+
await recordTextMessageMetric({
|
|
1002
|
+
status: 'failed',
|
|
1003
|
+
provider: textMetricProvider,
|
|
1004
|
+
metadata: {
|
|
1005
|
+
method: methodName
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
catch (error) {
|
|
1010
|
+
console.error('Error recording text message metric:', error);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
if (!process.env.IS_WORKER_INSTANCE) {
|
|
1015
|
+
await this.callMethod(
|
|
1016
|
+
'insertErrorLog',
|
|
1017
|
+
`Error in Method: ${methodName}`,
|
|
1018
|
+
{
|
|
1019
|
+
method: methodName,
|
|
1020
|
+
methodData,
|
|
1021
|
+
error: {
|
|
1022
|
+
message: normalizedError.message,
|
|
1023
|
+
stack: normalizedError.stack,
|
|
1024
|
+
code: normalizedError.code,
|
|
1025
|
+
codeName: normalizedError.codeName
|
|
1026
|
+
},
|
|
1027
|
+
correlationId: resolvedCorrelationId
|
|
1028
|
+
}
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
throw normalizedError;
|
|
1033
|
+
}
|
|
1034
|
+
finally {
|
|
1035
|
+
await this._monitorManagerFunction.finishMonitorFunction(monitor);
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
|
|
1039
|
+
if (shouldStartTransaction) {
|
|
1040
|
+
let monitor = null;
|
|
1041
|
+
|
|
1042
|
+
return ResolveIOServer.getMongoManager().oneTimeTransaction(async () => {
|
|
1043
|
+
// console.log(new Date(), 'Calling Method - New Session', methodName);
|
|
1044
|
+
|
|
1045
|
+
monitor = this._monitorManagerFunction.startMonitorFunction('Method', methodName, this['user'] || '', '', functionMethodData);
|
|
1046
|
+
|
|
1047
|
+
try {
|
|
1048
|
+
let res = await method.function.call(Object.assign({}, this, MethodManager.prototype), ...functionMethodData);
|
|
1049
|
+
|
|
1050
|
+
if (methodCallback) {
|
|
1051
|
+
methodCallback(null, res);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
if (shouldTrackTextMetric) {
|
|
1055
|
+
try {
|
|
1056
|
+
await recordTextMessageMetric({
|
|
1057
|
+
status: 'sent',
|
|
1058
|
+
provider: textMetricProvider,
|
|
1059
|
+
metadata: {
|
|
1060
|
+
method: methodName
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
catch (error) {
|
|
1065
|
+
console.error('Error recording text message metric:', error);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
return res;
|
|
1070
|
+
}
|
|
1071
|
+
catch (err) {
|
|
1072
|
+
if (err.code === 112 || err.codeName === 'WriteConflict' || err.code === 251 || err.codeName === 'NoSuchTransaction') {
|
|
1073
|
+
throw err; // Write error, retry
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
const { error: normalizedError, correlationId: resolvedCorrelationId } = ensureErrorWithCorrelation(err, correlationId);
|
|
1077
|
+
|
|
1078
|
+
console.log(JSON.stringify([new Date(), 'Error Method Manager - Run Method - New Session', methodName, {
|
|
1079
|
+
code: normalizedError.code,
|
|
1080
|
+
codeName: normalizedError.codeName,
|
|
1081
|
+
message: normalizedError.message,
|
|
1082
|
+
stack: normalizedError.stack,
|
|
1083
|
+
correlationId: resolvedCorrelationId
|
|
1084
|
+
}], null, 2));
|
|
1085
|
+
|
|
1086
|
+
await this.reportMethodError(
|
|
1087
|
+
'SERVER - Error Detected - ' + this.serverConfig['CLIENT_NAME'],
|
|
1088
|
+
resolvedCorrelationId,
|
|
1089
|
+
{
|
|
1090
|
+
message: 'Error detected during method execution (new session)',
|
|
1091
|
+
method: methodName,
|
|
1092
|
+
data: methodData,
|
|
1093
|
+
error: normalizedError
|
|
1094
|
+
},
|
|
1095
|
+
{ context: 'callMethod-new-session', methodName },
|
|
1096
|
+
normalizedError.stack
|
|
1097
|
+
);
|
|
1098
|
+
normalizedError.message = `${new Date().toISOString()} - Error in Method With Session (${methodName}) - New Session: ${normalizedError.message}`;
|
|
1099
|
+
|
|
1100
|
+
if (methodCallback) {
|
|
1101
|
+
methodCallback(normalizedError, null);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
if (shouldTrackTextMetric) {
|
|
1105
|
+
try {
|
|
1106
|
+
await recordTextMessageMetric({
|
|
1107
|
+
status: 'failed',
|
|
1108
|
+
provider: textMetricProvider,
|
|
1109
|
+
metadata: {
|
|
1110
|
+
method: methodName
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
catch (error) {
|
|
1115
|
+
console.error('Error recording text message metric:', error);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
if (!process.env.IS_WORKER_INSTANCE) {
|
|
1120
|
+
await this.callMethod(
|
|
1121
|
+
'insertErrorLog',
|
|
1122
|
+
`Error in Method: ${methodName}`,
|
|
1123
|
+
{
|
|
1124
|
+
method: methodName,
|
|
1125
|
+
methodData,
|
|
1126
|
+
error: {
|
|
1127
|
+
message: normalizedError.message,
|
|
1128
|
+
stack: normalizedError.stack,
|
|
1129
|
+
code: normalizedError.code,
|
|
1130
|
+
codeName: normalizedError.codeName
|
|
1131
|
+
},
|
|
1132
|
+
correlationId: resolvedCorrelationId
|
|
1133
|
+
}
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
throw normalizedError;
|
|
1138
|
+
}
|
|
1139
|
+
finally {
|
|
1140
|
+
await this._monitorManagerFunction.finishMonitorFunction(monitor);
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
else if (method.bypassSession && session) {
|
|
1145
|
+
return ResolveIOServer.getMongoManager().runWithoutSession(async () => await executeWithExistingSession());
|
|
1146
|
+
}
|
|
1147
|
+
else {
|
|
1148
|
+
return executeWithExistingSession();
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
finally {
|
|
1152
|
+
releaseLocalConcurrency();
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
private async acquireLocalConcurrency(methodName: string, maxConcurrency?: number): Promise<() => void> {
|
|
1157
|
+
if (!maxConcurrency || maxConcurrency < 1) {
|
|
1158
|
+
return () => {};
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
let current = this._localActiveCounts.get(methodName) || 0;
|
|
1162
|
+
|
|
1163
|
+
if (current < maxConcurrency) {
|
|
1164
|
+
this._localActiveCounts.set(methodName, current + 1);
|
|
1165
|
+
return this.createLocalRelease(methodName);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
1169
|
+
return new Promise((resolve) => {
|
|
1170
|
+
let queue = this._localWaitQueues.get(methodName);
|
|
1171
|
+
|
|
1172
|
+
if (!queue) {
|
|
1173
|
+
queue = [];
|
|
1174
|
+
this._localWaitQueues.set(methodName, queue);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
queue.push(() => {
|
|
1178
|
+
let curr = this._localActiveCounts.get(methodName) || 0;
|
|
1179
|
+
this._localActiveCounts.set(methodName, curr + 1);
|
|
1180
|
+
resolve(this.createLocalRelease(methodName));
|
|
1181
|
+
});
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
private createLocalRelease(methodName: string): () => void {
|
|
1186
|
+
let released = false;
|
|
1187
|
+
|
|
1188
|
+
return () => {
|
|
1189
|
+
if (released) {
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
released = true;
|
|
1194
|
+
|
|
1195
|
+
let current = this._localActiveCounts.get(methodName) || 0;
|
|
1196
|
+
this._localActiveCounts.set(methodName, current > 0 ? current - 1 : 0);
|
|
1197
|
+
|
|
1198
|
+
let queue = this._localWaitQueues.get(methodName);
|
|
1199
|
+
|
|
1200
|
+
if (queue && queue.length) {
|
|
1201
|
+
let next = queue.shift();
|
|
1202
|
+
|
|
1203
|
+
if (next) {
|
|
1204
|
+
next();
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
setupEmailWatcher() {
|
|
1211
|
+
const changeStream = EmailHistories.watchCollection([]);
|
|
1212
|
+
|
|
1213
|
+
changeStream.on('change', async (change) => {
|
|
1214
|
+
if (change.operationType === 'insert' && change.fullDocument && change.fullDocument.status === 'pending') {
|
|
1215
|
+
this.emailQueue.add(change.fullDocument._id.toString());
|
|
1216
|
+
await this.tryProcessEmail();
|
|
1217
|
+
}
|
|
1218
|
+
else if (change.operationType === 'update' || change.operationType === 'replace') {
|
|
1219
|
+
const updatedEmail = change.fullDocument;
|
|
1220
|
+
if (updatedEmail && updatedEmail.status !== 'pending' && this.emailQueue.has(updatedEmail._id.toString())) {
|
|
1221
|
+
this.emailQueue.delete(updatedEmail._id.toString());
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
})
|
|
1225
|
+
.on('error', async err => {
|
|
1226
|
+
console.error('Email history changestream error', err);
|
|
1227
|
+
await changeStream.close();
|
|
1228
|
+
})
|
|
1229
|
+
.on('close', () => {
|
|
1230
|
+
this.setupEmailWatcher();
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
async loadPendingEmails() {
|
|
1235
|
+
// Load any pending emails on startup
|
|
1236
|
+
const pendingEmails = await EmailHistories.find({ status: 'pending' }, {sort: {_id: 1}});
|
|
1237
|
+
for (const email of pendingEmails) {
|
|
1238
|
+
this.emailQueue.add(email._id.toString());
|
|
1239
|
+
}
|
|
1240
|
+
// Try to process emails
|
|
1241
|
+
await this.tryProcessEmail();
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
async tryProcessEmail() {
|
|
1245
|
+
if (this.isEmailProcessing || this.emailQueue.size === 0) {
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
this.isEmailProcessing = true;
|
|
1250
|
+
|
|
1251
|
+
try {
|
|
1252
|
+
while (this.emailQueue.size > 0) {
|
|
1253
|
+
const emailId = this.emailQueue.values().next().value;
|
|
1254
|
+
this.emailQueue.delete(emailId);
|
|
1255
|
+
|
|
1256
|
+
const pendingEmail = await EmailHistories.findOne(
|
|
1257
|
+
{
|
|
1258
|
+
_id: emailId,
|
|
1259
|
+
status: 'pending',
|
|
1260
|
+
}
|
|
1261
|
+
);
|
|
1262
|
+
|
|
1263
|
+
if (!pendingEmail) {
|
|
1264
|
+
continue;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
let queuedAt: Date;
|
|
1268
|
+
if (pendingEmail.date instanceof Date) {
|
|
1269
|
+
queuedAt = pendingEmail.date;
|
|
1270
|
+
}
|
|
1271
|
+
else if (pendingEmail.createdAt instanceof Date) {
|
|
1272
|
+
queuedAt = pendingEmail.createdAt;
|
|
1273
|
+
}
|
|
1274
|
+
else {
|
|
1275
|
+
queuedAt = new Date();
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
const timeSinceQueued = Date.now() - queuedAt.getTime();
|
|
1279
|
+
const remainingDelay = this._emailProcessingDelayMs - timeSinceQueued;
|
|
1280
|
+
|
|
1281
|
+
if (remainingDelay > 0) {
|
|
1282
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
1283
|
+
await new Promise(resolve => setTimeout(resolve, remainingDelay));
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
const emailHistory = await EmailHistories.findOneAndUpdate(
|
|
1287
|
+
{
|
|
1288
|
+
_id: emailId,
|
|
1289
|
+
status: 'pending',
|
|
1290
|
+
},
|
|
1291
|
+
{
|
|
1292
|
+
$set: { status: 'processing', processingAt: new Date() },
|
|
1293
|
+
}
|
|
1294
|
+
);
|
|
1295
|
+
|
|
1296
|
+
if (!emailHistory) {
|
|
1297
|
+
continue;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// Fetch and process attachments
|
|
1301
|
+
if (emailHistory.attachments && emailHistory.attachments.length > 0) {
|
|
1302
|
+
const validAttachments: any[] = [];
|
|
1303
|
+
let attachmentError = false;
|
|
1304
|
+
|
|
1305
|
+
for (const att of emailHistory.attachments) {
|
|
1306
|
+
if (att.path && att.path.startsWith('http')) {
|
|
1307
|
+
try {
|
|
1308
|
+
const response = await fetch(att.path);
|
|
1309
|
+
if (!response.ok) {
|
|
1310
|
+
attachmentError = true;
|
|
1311
|
+
continue;
|
|
1312
|
+
}
|
|
1313
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
1314
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
1315
|
+
|
|
1316
|
+
// Check the size of the attachment
|
|
1317
|
+
const maxSize = 20 * 1024 * 1024; // 20MB in bytes
|
|
1318
|
+
if (buffer.length <= maxSize) {
|
|
1319
|
+
// Attachment is within size limits, include it
|
|
1320
|
+
att.content = buffer;
|
|
1321
|
+
delete att.path;
|
|
1322
|
+
validAttachments.push(att);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
catch (err) {
|
|
1326
|
+
console.error('Failed to fetch attachment:', err);
|
|
1327
|
+
attachmentError = true;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
else {
|
|
1331
|
+
validAttachments.push(att);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
emailHistory.attachments = validAttachments;
|
|
1336
|
+
if (attachmentError) {
|
|
1337
|
+
emailHistory.text =
|
|
1338
|
+
(typeof emailHistory.text === 'string' ? emailHistory.text : '') +
|
|
1339
|
+
'\n\nCould not load attachments.';
|
|
1340
|
+
emailHistory.html =
|
|
1341
|
+
(typeof emailHistory.html === 'string' ? emailHistory.html : '') +
|
|
1342
|
+
'<p>Could not load attachments.</p>';
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// Prepare email options
|
|
1347
|
+
const mailOptions: any = {
|
|
1348
|
+
replyTo: emailHistory.reply_to || (ResolveIOServer.getServerConfig()['MAIL_REPLY_TO'] || undefined),
|
|
1349
|
+
from: emailHistory.send_from || ResolveIOServer.getServerConfig().MAIL_FROM,
|
|
1350
|
+
to: emailHistory.email,
|
|
1351
|
+
cc: emailHistory.cc && emailHistory.cc.length ? emailHistory.cc : undefined,
|
|
1352
|
+
subject:
|
|
1353
|
+
(ResolveIOServer.getServerConfig()['ROOT_URL'].match(/https:\/\/dev\./) ||
|
|
1354
|
+
ResolveIOServer.getServerConfig()['ROOT_URL'].match(/https:\/\/www\.dev\./)
|
|
1355
|
+
? '(DEV SERVER) - '
|
|
1356
|
+
: '') + emailHistory.subject,
|
|
1357
|
+
text: typeof emailHistory.text === 'string' ? emailHistory.text : '',
|
|
1358
|
+
html: typeof emailHistory.html === 'string' ? emailHistory.html : '',
|
|
1359
|
+
attachments: emailHistory.attachments || [],
|
|
1360
|
+
headers: emailHistory.headers || undefined,
|
|
1361
|
+
force_ses: emailHistory.force_ses
|
|
1362
|
+
};
|
|
1363
|
+
|
|
1364
|
+
// Process attachments before sending
|
|
1365
|
+
if (mailOptions.attachments && mailOptions.attachments.length > 0) {
|
|
1366
|
+
mailOptions.attachments = mailOptions.attachments.map((att) => {
|
|
1367
|
+
const newAtt = { ...att };
|
|
1368
|
+
// Handle attachments stored as BinData or Buffer
|
|
1369
|
+
if (newAtt.content && typeof newAtt.content === 'object' && !(newAtt.content instanceof Buffer)) {
|
|
1370
|
+
// Convert MongoDB's Binary data to Buffer
|
|
1371
|
+
if (newAtt.content._bsontype === 'Binary' && newAtt.content.sub_type === 0) {
|
|
1372
|
+
newAtt.content = Buffer.from(newAtt.content.buffer);
|
|
1373
|
+
}
|
|
1374
|
+
else {
|
|
1375
|
+
// Handle other types if necessary
|
|
1376
|
+
newAtt.content = Buffer.from(newAtt.content);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
// Handle attachments stored as Base64 strings
|
|
1380
|
+
else if (typeof newAtt.content === 'string' && newAtt.encoding === 'base64') {
|
|
1381
|
+
newAtt.content = Buffer.from(newAtt.content, 'base64');
|
|
1382
|
+
delete newAtt.encoding;
|
|
1383
|
+
}
|
|
1384
|
+
// Ensure the content is a Buffer
|
|
1385
|
+
else if (typeof newAtt.content === 'string') {
|
|
1386
|
+
newAtt.content = Buffer.from(newAtt.content);
|
|
1387
|
+
}
|
|
1388
|
+
return newAtt;
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// Send the email
|
|
1393
|
+
const emailProvider = (!mailOptions.force_ses && this._mailerCustom) ? 'custom' : 'ses';
|
|
1394
|
+
(!mailOptions.force_ses && this._mailerCustom ? this._mailerCustom : this._mailerSES).sendMail(mailOptions, async err => {
|
|
1395
|
+
const metricStatus = err ? 'failed' : 'sent';
|
|
1396
|
+
|
|
1397
|
+
try {
|
|
1398
|
+
if (err) {
|
|
1399
|
+
console.error('Failed to send email:', err);
|
|
1400
|
+
await this.finalizeEmailHistoryStorage(
|
|
1401
|
+
emailHistory,
|
|
1402
|
+
'failed',
|
|
1403
|
+
typeof err === 'string' ? err : this.safeStringify(err)
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
else {
|
|
1407
|
+
if (emailHistory.email === 'dev@resolveio.com') {
|
|
1408
|
+
await EmailHistories.deleteOne({ _id: emailHistory._id });
|
|
1409
|
+
}
|
|
1410
|
+
else {
|
|
1411
|
+
await this.finalizeEmailHistoryStorage(emailHistory, 'completed');
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
catch (error) {
|
|
1416
|
+
console.error('Error in sendMail callback:', error);
|
|
1417
|
+
await this.finalizeEmailHistoryStorage(
|
|
1418
|
+
emailHistory,
|
|
1419
|
+
'failed',
|
|
1420
|
+
typeof error === 'string' ? error : this.safeStringify(error)
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1423
|
+
finally {
|
|
1424
|
+
try {
|
|
1425
|
+
await recordEmailMetric({
|
|
1426
|
+
status: metricStatus,
|
|
1427
|
+
provider: emailProvider,
|
|
1428
|
+
metadata: {
|
|
1429
|
+
method: 'sendEmail'
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
catch (error) {
|
|
1434
|
+
console.error('Error recording email metric:', error);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
// Wait for at least one second before sending the next email
|
|
1440
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
1441
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
catch (err) {
|
|
1445
|
+
console.error('Error processing email queue:', err);
|
|
1446
|
+
}
|
|
1447
|
+
finally {
|
|
1448
|
+
this.isEmailProcessing = false;
|
|
1449
|
+
// Check if new emails arrived while processing
|
|
1450
|
+
if (this.emailQueue.size > 0) {
|
|
1451
|
+
await this.tryProcessEmail();
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
private shouldTrackTextMessageMetric(methodName: string): boolean {
|
|
1457
|
+
const normalized = String(methodName || '').trim().toLowerCase();
|
|
1458
|
+
if (!normalized) {
|
|
1459
|
+
return false;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
return normalized.startsWith('sendsms') || normalized.startsWith('sendtext');
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
private getTextMessageProvider(): string | undefined {
|
|
1466
|
+
const provider = this.serverConfig?.['SMS_PROVIDER'] || '';
|
|
1467
|
+
if (typeof provider === 'string' && provider.trim().length) {
|
|
1468
|
+
return provider.trim().toLowerCase();
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
if (this.serverConfig?.['TWILIO_SID'] || this.serverConfig?.['TWILIO_AUTH_TOKEN']) {
|
|
1472
|
+
return 'twilio';
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
return undefined;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
safeStringify(obj)
|
|
1479
|
+
{
|
|
1480
|
+
try
|
|
1481
|
+
{
|
|
1482
|
+
return JSON.stringify(obj, this.getCircularReplacer());
|
|
1483
|
+
}
|
|
1484
|
+
catch (e)
|
|
1485
|
+
{
|
|
1486
|
+
return `Error in JSON stringifying: ${e.message}`;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
getCircularReplacer()
|
|
1491
|
+
{
|
|
1492
|
+
const seen = new WeakSet();
|
|
1493
|
+
return (key, value) =>
|
|
1494
|
+
{
|
|
1495
|
+
if (typeof value === "object" && value !== null)
|
|
1496
|
+
{
|
|
1497
|
+
if (seen.has(value))
|
|
1498
|
+
{
|
|
1499
|
+
return "[Circular]";
|
|
1500
|
+
}
|
|
1501
|
+
seen.add(value);
|
|
1502
|
+
}
|
|
1503
|
+
return value;
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
private async finalizeEmailHistoryStorage(
|
|
1508
|
+
emailHistory: EmailHistoryModel,
|
|
1509
|
+
status: 'completed' | 'failed',
|
|
1510
|
+
error?: string
|
|
1511
|
+
): Promise<void> {
|
|
1512
|
+
const setValues: Record<string, any> = {
|
|
1513
|
+
status,
|
|
1514
|
+
text: truncateTextForHistory(emailHistory.text, EMAIL_HISTORY_TEXT_MAX_CHARS),
|
|
1515
|
+
html: truncateTextForHistory(emailHistory.html, EMAIL_HISTORY_HTML_MAX_CHARS),
|
|
1516
|
+
attachments: sanitizeAttachmentsForHistory(emailHistory.attachments),
|
|
1517
|
+
completedAt: new Date()
|
|
1518
|
+
};
|
|
1519
|
+
|
|
1520
|
+
const sanitizedOccurrences = sanitizeOccurrencesForHistory(emailHistory.occurrences);
|
|
1521
|
+
if (sanitizedOccurrences) {
|
|
1522
|
+
setValues.occurrences = sanitizedOccurrences;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
if (status === 'failed') {
|
|
1526
|
+
setValues.error = truncateTextForHistory(error || '', EMAIL_HISTORY_ERROR_MAX_CHARS);
|
|
1527
|
+
}
|
|
1528
|
+
else {
|
|
1529
|
+
setValues.error = '';
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
await EmailHistories.updateOne(
|
|
1533
|
+
{ _id: emailHistory._id },
|
|
1534
|
+
{
|
|
1535
|
+
$set: setValues
|
|
1536
|
+
}
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
private async tryMergeEmailOccurrence(
|
|
1541
|
+
email: string,
|
|
1542
|
+
subject: string,
|
|
1543
|
+
correlationId: string,
|
|
1544
|
+
occurrence: EmailHistoryOccurrence,
|
|
1545
|
+
meta?: Record<string, any>
|
|
1546
|
+
): Promise<EmailHistoryModel | null> {
|
|
1547
|
+
const existing = await EmailHistories.findOne(
|
|
1548
|
+
{
|
|
1549
|
+
email,
|
|
1550
|
+
correlationId,
|
|
1551
|
+
status: 'pending'
|
|
1552
|
+
},
|
|
1553
|
+
{ sort: { date: 1 } }
|
|
1554
|
+
);
|
|
1555
|
+
|
|
1556
|
+
if (!existing) {
|
|
1557
|
+
return null;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
const normalizedOccurrences: EmailHistoryOccurrence[] = Array.isArray(existing.occurrences) && existing.occurrences.length
|
|
1561
|
+
? existing.occurrences.map((item: EmailHistoryOccurrence) => ({
|
|
1562
|
+
_id: item._id || objectIdHexString(),
|
|
1563
|
+
createdAt: item.createdAt instanceof Date ? item.createdAt : new Date(item.createdAt),
|
|
1564
|
+
subject: item.subject || existing.subject || '',
|
|
1565
|
+
text: item.text,
|
|
1566
|
+
html: item.html,
|
|
1567
|
+
meta: item.meta
|
|
1568
|
+
}))
|
|
1569
|
+
: [createEmailOccurrence(
|
|
1570
|
+
existing.subject || '',
|
|
1571
|
+
typeof existing.text === 'string' ? existing.text : undefined,
|
|
1572
|
+
typeof existing.html === 'string' ? existing.html : undefined,
|
|
1573
|
+
existing.meta
|
|
1574
|
+
)];
|
|
1575
|
+
|
|
1576
|
+
normalizedOccurrences.push(occurrence);
|
|
1577
|
+
|
|
1578
|
+
const { text: aggregatedText, html: aggregatedHtml } = formatGroupedEmailContent(correlationId, normalizedOccurrences);
|
|
1579
|
+
|
|
1580
|
+
const updated = await EmailHistories.findOneAndUpdate(
|
|
1581
|
+
{
|
|
1582
|
+
_id: existing._id,
|
|
1583
|
+
status: 'pending'
|
|
1584
|
+
},
|
|
1585
|
+
{
|
|
1586
|
+
$set: {
|
|
1587
|
+
subject,
|
|
1588
|
+
text: aggregatedText,
|
|
1589
|
+
html: aggregatedHtml,
|
|
1590
|
+
occurrences: normalizedOccurrences,
|
|
1591
|
+
meta: meta || existing.meta,
|
|
1592
|
+
updatedAt: new Date()
|
|
1593
|
+
}
|
|
1594
|
+
},
|
|
1595
|
+
{ returnDocument: 'after' }
|
|
1596
|
+
);
|
|
1597
|
+
|
|
1598
|
+
return updated || null;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
private async waitForEmailCompletion(emailHistory: EmailHistoryModel, options?: SendEmailOptions): Promise<EmailHistoryModel> {
|
|
1602
|
+
const timeoutMs = Math.max(1000, Number(options?.waitForCompletionTimeoutMs) || 30000);
|
|
1603
|
+
const pollMs = Math.max(100, Number(options?.waitForCompletionPollMs) || 250);
|
|
1604
|
+
const deadline = Date.now() + timeoutMs;
|
|
1605
|
+
|
|
1606
|
+
while (Date.now() <= deadline) {
|
|
1607
|
+
const latest = await EmailHistories.findOne({_id: emailHistory._id}, {
|
|
1608
|
+
projection: {
|
|
1609
|
+
status: 1,
|
|
1610
|
+
error: 1,
|
|
1611
|
+
date: 1,
|
|
1612
|
+
id_user: 1,
|
|
1613
|
+
user: 1,
|
|
1614
|
+
email: 1,
|
|
1615
|
+
subject: 1,
|
|
1616
|
+
text: 1,
|
|
1617
|
+
html: 1,
|
|
1618
|
+
attachments: 1,
|
|
1619
|
+
send_from: 1,
|
|
1620
|
+
reply_to: 1,
|
|
1621
|
+
force_ses: 1,
|
|
1622
|
+
cc: 1,
|
|
1623
|
+
headers: 1,
|
|
1624
|
+
correlationId: 1,
|
|
1625
|
+
meta: 1,
|
|
1626
|
+
occurrences: 1,
|
|
1627
|
+
processingAt: 1,
|
|
1628
|
+
completedAt: 1
|
|
1629
|
+
}
|
|
1630
|
+
});
|
|
1631
|
+
|
|
1632
|
+
if (!latest) {
|
|
1633
|
+
return {
|
|
1634
|
+
...emailHistory,
|
|
1635
|
+
status: 'completed',
|
|
1636
|
+
error: '',
|
|
1637
|
+
completedAt: new Date()
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
if (latest.status === 'completed') {
|
|
1642
|
+
return latest;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
if (latest.status === 'failed') {
|
|
1646
|
+
throw new Error(latest.error || `Email failed to send: ${latest.subject}`);
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
1650
|
+
await new Promise(resolve => setTimeout(resolve, pollMs));
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
throw new Error(`Email send timed out waiting for completion: ${emailHistory.subject}`);
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
public async sendEmail(
|
|
1657
|
+
sendTo: string,
|
|
1658
|
+
subject: string,
|
|
1659
|
+
text?: string | null,
|
|
1660
|
+
html?: string | null,
|
|
1661
|
+
attachments?: any[] | null,
|
|
1662
|
+
send_from?: string | null,
|
|
1663
|
+
reply_to?: string | null,
|
|
1664
|
+
force_ses = false,
|
|
1665
|
+
local_override = false,
|
|
1666
|
+
options?: SendEmailOptions
|
|
1667
|
+
) {
|
|
1668
|
+
// Modify sendTo in development environments
|
|
1669
|
+
const isDevEmailEnvironment = isDevEmailRedirectEnvironment();
|
|
1670
|
+
let cc = normalizeEmailRecipients(options?.cc);
|
|
1671
|
+
if (isDevEmailEnvironment) {
|
|
1672
|
+
cc = cc.filter(email => email.match(/\@resolveio\.com/));
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
if (isDevEmailEnvironment && !sendTo.match(/\@resolveio\.com/)) {
|
|
1676
|
+
sendTo = 'dev@resolveio.com';
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
const normalizedSubject = subject || '';
|
|
1680
|
+
const correlationId = options?.correlationId;
|
|
1681
|
+
const finalSubject = appendCorrelationIdToSubject(normalizedSubject, correlationId);
|
|
1682
|
+
const groupByCorrelationId = !!correlationId;
|
|
1683
|
+
const normalizedText = typeof text === 'string' ? text : '';
|
|
1684
|
+
const normalizedHtml = typeof html === 'string' ? html : '';
|
|
1685
|
+
const occurrence = groupByCorrelationId
|
|
1686
|
+
? createEmailOccurrence(normalizedSubject, normalizedText, normalizedHtml, options?.meta)
|
|
1687
|
+
: null;
|
|
1688
|
+
let allowGrouping = groupByCorrelationId;
|
|
1689
|
+
|
|
1690
|
+
if (sendTo) {
|
|
1691
|
+
if (
|
|
1692
|
+
ResolveIOServer.getServerConfig()['ROOT_URL'] !== 'http://localhost:4200' ||
|
|
1693
|
+
local_override
|
|
1694
|
+
) {
|
|
1695
|
+
let normalizedAttachments: any[] = [];
|
|
1696
|
+
if (Array.isArray(attachments)) {
|
|
1697
|
+
normalizedAttachments = attachments.slice();
|
|
1698
|
+
}
|
|
1699
|
+
else if (attachments) {
|
|
1700
|
+
normalizedAttachments = [attachments];
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
normalizedAttachments = normalizedAttachments.map(att => {
|
|
1704
|
+
const newAtt = { ...att };
|
|
1705
|
+
if (Buffer.isBuffer(newAtt.content)) {
|
|
1706
|
+
newAtt.content = newAtt.content.toString('base64');
|
|
1707
|
+
newAtt.encoding = 'base64';
|
|
1708
|
+
}
|
|
1709
|
+
return newAtt;
|
|
1710
|
+
});
|
|
1711
|
+
|
|
1712
|
+
if (normalizedAttachments.length > 0) {
|
|
1713
|
+
allowGrouping = false;
|
|
1714
|
+
}
|
|
1715
|
+
if (cc.length > 0) {
|
|
1716
|
+
allowGrouping = false;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
if (allowGrouping && occurrence && correlationId) {
|
|
1720
|
+
const merged = await this.tryMergeEmailOccurrence(sendTo, finalSubject, correlationId, occurrence, options?.meta);
|
|
1721
|
+
if (merged) {
|
|
1722
|
+
return merged;
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
const emailHistory: EmailHistoryModel = {
|
|
1727
|
+
_id: objectIdHexString(),
|
|
1728
|
+
__v: 0,
|
|
1729
|
+
date: new Date(),
|
|
1730
|
+
id_user: this['id_user'] || '',
|
|
1731
|
+
user: this['user'] || '',
|
|
1732
|
+
email: sendTo,
|
|
1733
|
+
subject: finalSubject,
|
|
1734
|
+
text: normalizedText,
|
|
1735
|
+
html: normalizedHtml,
|
|
1736
|
+
attachments: normalizedAttachments,
|
|
1737
|
+
cc: cc,
|
|
1738
|
+
send_from: send_from || '',
|
|
1739
|
+
reply_to: reply_to || '',
|
|
1740
|
+
status: 'pending',
|
|
1741
|
+
error: '',
|
|
1742
|
+
force_ses,
|
|
1743
|
+
headers: options?.headers
|
|
1744
|
+
};
|
|
1745
|
+
|
|
1746
|
+
if (allowGrouping && occurrence && correlationId) {
|
|
1747
|
+
const { text: aggregatedText, html: aggregatedHtml } = formatGroupedEmailContent(correlationId, [occurrence]);
|
|
1748
|
+
emailHistory.text = aggregatedText;
|
|
1749
|
+
emailHistory.html = aggregatedHtml;
|
|
1750
|
+
emailHistory.occurrences = [occurrence];
|
|
1751
|
+
emailHistory.correlationId = correlationId;
|
|
1752
|
+
if (options?.meta) {
|
|
1753
|
+
emailHistory.meta = options.meta;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
else {
|
|
1757
|
+
if (correlationId) {
|
|
1758
|
+
emailHistory.correlationId = correlationId;
|
|
1759
|
+
}
|
|
1760
|
+
if (options?.meta) {
|
|
1761
|
+
emailHistory.meta = options.meta;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
try {
|
|
1766
|
+
let history = await EmailHistories.insertOne(emailHistory);
|
|
1767
|
+
if (options?.waitForCompletion) {
|
|
1768
|
+
return await this.waitForEmailCompletion(history, options);
|
|
1769
|
+
}
|
|
1770
|
+
return history;
|
|
1771
|
+
}
|
|
1772
|
+
catch (err) {
|
|
1773
|
+
console.error('Failed to queue email:', err);
|
|
1774
|
+
err.message = `Failed to queue email: ${err.message}`;
|
|
1775
|
+
throw err;
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
else {
|
|
1779
|
+
console.log(
|
|
1780
|
+
'Send email',
|
|
1781
|
+
sendTo,
|
|
1782
|
+
finalSubject,
|
|
1783
|
+
normalizedText,
|
|
1784
|
+
normalizedHtml,
|
|
1785
|
+
attachments,
|
|
1786
|
+
send_from,
|
|
1787
|
+
force_ses,
|
|
1788
|
+
correlationId,
|
|
1789
|
+
cc
|
|
1790
|
+
);
|
|
1791
|
+
|
|
1792
|
+
return true;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
else {
|
|
1796
|
+
return true;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
public getAWS(): AWS {
|
|
1801
|
+
return this._aws;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
public readFile(fileName) {
|
|
1805
|
+
if (fs.existsSync(path.join(__dirname, ('../private/' + fileName)))) {
|
|
1806
|
+
try {
|
|
1807
|
+
return fs.readFileSync(path.join(__dirname, ('../private/' + fileName)), 'utf-8');
|
|
1808
|
+
}
|
|
1809
|
+
catch (err) {
|
|
1810
|
+
err.message = `Error in readFile: ${err.message}`;
|
|
1811
|
+
throw err;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
else {
|
|
1815
|
+
if (fs.existsSync(path.join(ResolveIOServer.getClientDir(), ('./private/' + fileName)))) {
|
|
1816
|
+
try {
|
|
1817
|
+
return fs.readFileSync(path.join(ResolveIOServer.getClientDir(), ('./private/' + fileName)), 'utf-8');
|
|
1818
|
+
}
|
|
1819
|
+
catch (err) {
|
|
1820
|
+
err.message = `Error in readFile: ${err.message}`;
|
|
1821
|
+
throw err;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
throw new Error ('Error in readFile: File Not Found');
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
public readImage(fileName) {
|
|
1830
|
+
if (fs.existsSync(path.join(__dirname, ('../private/' + fileName)))) {
|
|
1831
|
+
try {
|
|
1832
|
+
return fs.readFileSync(path.join(__dirname, ('../private/' + fileName)), 'base64');
|
|
1833
|
+
}
|
|
1834
|
+
catch (err) {
|
|
1835
|
+
err.message = `Error in readImage: ${err.message}`;
|
|
1836
|
+
throw err;
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
else {
|
|
1840
|
+
if (fs.existsSync(path.join(ResolveIOServer.getClientDir(), ('./private/' + fileName)))) {
|
|
1841
|
+
try {
|
|
1842
|
+
return fs.readFileSync(path.join(ResolveIOServer.getClientDir(), ('./private/' + fileName)), 'base64');
|
|
1843
|
+
}
|
|
1844
|
+
catch (err) {
|
|
1845
|
+
err.message = `Error in readImage: ${err.message}`;
|
|
1846
|
+
throw err;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
throw new Error ('Error in readImage: File Not Found');
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
public getEnableDebug() {
|
|
1855
|
+
return this._enableDebug;
|
|
1856
|
+
}
|
|
1857
|
+
}
|