@serve.zone/dcrouter 2.12.4
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/.dockerignore +1 -0
- package/.gitea/workflows/docker_nottags.yaml +71 -0
- package/.gitea/workflows/docker_tags.yaml +106 -0
- package/.vscode/launch.json +11 -0
- package/.vscode/settings.json +26 -0
- package/Dockerfile +46 -0
- package/changelog.md +247 -0
- package/cli.child.js +4 -0
- package/cli.child.ts +4 -0
- package/cli.js +4 -0
- package/cli.ts.js +5 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/classes.dcrouter.d.ts +238 -0
- package/dist_ts/classes.dcrouter.js +1008 -0
- package/dist_ts/config/index.d.ts +1 -0
- package/dist_ts/config/index.js +3 -0
- package/dist_ts/config/validator.d.ts +104 -0
- package/dist_ts/config/validator.js +152 -0
- package/dist_ts/deliverability/classes.ipwarmupmanager.d.ts +253 -0
- package/dist_ts/deliverability/classes.ipwarmupmanager.js +639 -0
- package/dist_ts/deliverability/classes.senderreputationmonitor.d.ts +300 -0
- package/dist_ts/deliverability/classes.senderreputationmonitor.js +961 -0
- package/dist_ts/deliverability/index.d.ts +2 -0
- package/dist_ts/deliverability/index.js +3 -0
- package/dist_ts/errors/base.errors.d.ts +224 -0
- package/dist_ts/errors/base.errors.js +310 -0
- package/dist_ts/errors/email.errors.d.ts +175 -0
- package/dist_ts/errors/email.errors.js +265 -0
- package/dist_ts/errors/error-handler.d.ts +98 -0
- package/dist_ts/errors/error-handler.js +282 -0
- package/dist_ts/errors/error.codes.d.ts +115 -0
- package/dist_ts/errors/error.codes.js +136 -0
- package/dist_ts/errors/index.d.ts +56 -0
- package/dist_ts/errors/index.js +138 -0
- package/dist_ts/errors/mta.errors.d.ts +259 -0
- package/dist_ts/errors/mta.errors.js +472 -0
- package/dist_ts/errors/reputation.errors.d.ts +183 -0
- package/dist_ts/errors/reputation.errors.js +292 -0
- package/dist_ts/index.d.ts +4 -0
- package/dist_ts/index.js +6 -0
- package/dist_ts/logger.d.ts +17 -0
- package/dist_ts/logger.js +77 -0
- package/dist_ts/mail/core/classes.bouncemanager.d.ts +200 -0
- package/dist_ts/mail/core/classes.bouncemanager.js +778 -0
- package/dist_ts/mail/core/classes.email.d.ts +291 -0
- package/dist_ts/mail/core/classes.email.js +780 -0
- package/dist_ts/mail/core/classes.emailvalidator.d.ts +61 -0
- package/dist_ts/mail/core/classes.emailvalidator.js +182 -0
- package/dist_ts/mail/core/classes.templatemanager.d.ts +95 -0
- package/dist_ts/mail/core/classes.templatemanager.js +239 -0
- package/dist_ts/mail/core/index.d.ts +4 -0
- package/dist_ts/mail/core/index.js +6 -0
- package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +163 -0
- package/dist_ts/mail/delivery/classes.delivery.queue.js +485 -0
- package/dist_ts/mail/delivery/classes.delivery.system.d.ts +186 -0
- package/dist_ts/mail/delivery/classes.delivery.system.js +846 -0
- package/dist_ts/mail/delivery/classes.emailsendjob.d.ts +84 -0
- package/dist_ts/mail/delivery/classes.emailsendjob.js +362 -0
- package/dist_ts/mail/delivery/classes.emailsignjob.d.ts +18 -0
- package/dist_ts/mail/delivery/classes.emailsignjob.js +44 -0
- package/dist_ts/mail/delivery/classes.mta.config.d.ts +22 -0
- package/dist_ts/mail/delivery/classes.mta.config.js +51 -0
- package/dist_ts/mail/delivery/classes.ratelimiter.d.ts +98 -0
- package/dist_ts/mail/delivery/classes.ratelimiter.js +205 -0
- package/dist_ts/mail/delivery/classes.smtp.client.legacy.d.ts +275 -0
- package/dist_ts/mail/delivery/classes.smtp.client.legacy.js +973 -0
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts +200 -0
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +817 -0
- package/dist_ts/mail/delivery/index.d.ts +12 -0
- package/dist_ts/mail/delivery/index.js +18 -0
- package/dist_ts/mail/delivery/interfaces.d.ts +243 -0
- package/dist_ts/mail/delivery/interfaces.js +17 -0
- package/dist_ts/mail/delivery/smtpclient/auth-handler.d.ts +43 -0
- package/dist_ts/mail/delivery/smtpclient/auth-handler.js +188 -0
- package/dist_ts/mail/delivery/smtpclient/command-handler.d.ts +67 -0
- package/dist_ts/mail/delivery/smtpclient/command-handler.js +276 -0
- package/dist_ts/mail/delivery/smtpclient/connection-manager.d.ts +48 -0
- package/dist_ts/mail/delivery/smtpclient/connection-manager.js +238 -0
- package/dist_ts/mail/delivery/smtpclient/constants.d.ts +129 -0
- package/dist_ts/mail/delivery/smtpclient/constants.js +135 -0
- package/dist_ts/mail/delivery/smtpclient/create-client.d.ts +22 -0
- package/dist_ts/mail/delivery/smtpclient/create-client.js +86 -0
- package/dist_ts/mail/delivery/smtpclient/error-handler.d.ts +28 -0
- package/dist_ts/mail/delivery/smtpclient/error-handler.js +110 -0
- package/dist_ts/mail/delivery/smtpclient/index.d.ts +16 -0
- package/dist_ts/mail/delivery/smtpclient/index.js +21 -0
- package/dist_ts/mail/delivery/smtpclient/interfaces.d.ts +183 -0
- package/dist_ts/mail/delivery/smtpclient/interfaces.js +19 -0
- package/dist_ts/mail/delivery/smtpclient/smtp-client.d.ts +58 -0
- package/dist_ts/mail/delivery/smtpclient/smtp-client.js +279 -0
- package/dist_ts/mail/delivery/smtpclient/tls-handler.d.ts +33 -0
- package/dist_ts/mail/delivery/smtpclient/tls-handler.js +202 -0
- package/dist_ts/mail/delivery/smtpclient/utils/helpers.d.ts +77 -0
- package/dist_ts/mail/delivery/smtpclient/utils/helpers.js +196 -0
- package/dist_ts/mail/delivery/smtpclient/utils/logging.d.ts +46 -0
- package/dist_ts/mail/delivery/smtpclient/utils/logging.js +153 -0
- package/dist_ts/mail/delivery/smtpclient/utils/validation.d.ts +38 -0
- package/dist_ts/mail/delivery/smtpclient/utils/validation.js +139 -0
- package/dist_ts/mail/delivery/smtpserver/certificate-utils.d.ts +45 -0
- package/dist_ts/mail/delivery/smtpserver/certificate-utils.js +345 -0
- package/dist_ts/mail/delivery/smtpserver/command-handler.d.ts +156 -0
- package/dist_ts/mail/delivery/smtpserver/command-handler.js +1159 -0
- package/dist_ts/mail/delivery/smtpserver/connection-manager.d.ts +159 -0
- package/dist_ts/mail/delivery/smtpserver/connection-manager.js +894 -0
- package/dist_ts/mail/delivery/smtpserver/constants.d.ts +130 -0
- package/dist_ts/mail/delivery/smtpserver/constants.js +162 -0
- package/dist_ts/mail/delivery/smtpserver/create-server.d.ts +14 -0
- package/dist_ts/mail/delivery/smtpserver/create-server.js +28 -0
- package/dist_ts/mail/delivery/smtpserver/data-handler.d.ts +123 -0
- package/dist_ts/mail/delivery/smtpserver/data-handler.js +1148 -0
- package/dist_ts/mail/delivery/smtpserver/index.d.ts +20 -0
- package/dist_ts/mail/delivery/smtpserver/index.js +27 -0
- package/dist_ts/mail/delivery/smtpserver/interfaces.d.ts +530 -0
- package/dist_ts/mail/delivery/smtpserver/interfaces.js +10 -0
- package/dist_ts/mail/delivery/smtpserver/secure-server.d.ts +15 -0
- package/dist_ts/mail/delivery/smtpserver/secure-server.js +79 -0
- package/dist_ts/mail/delivery/smtpserver/security-handler.d.ts +86 -0
- package/dist_ts/mail/delivery/smtpserver/security-handler.js +234 -0
- package/dist_ts/mail/delivery/smtpserver/session-manager.d.ts +140 -0
- package/dist_ts/mail/delivery/smtpserver/session-manager.js +469 -0
- package/dist_ts/mail/delivery/smtpserver/smtp-server.d.ts +137 -0
- package/dist_ts/mail/delivery/smtpserver/smtp-server.js +666 -0
- package/dist_ts/mail/delivery/smtpserver/starttls-handler.d.ts +21 -0
- package/dist_ts/mail/delivery/smtpserver/starttls-handler.js +207 -0
- package/dist_ts/mail/delivery/smtpserver/tls-handler.d.ts +66 -0
- package/dist_ts/mail/delivery/smtpserver/tls-handler.js +261 -0
- package/dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.d.ts +117 -0
- package/dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.js +411 -0
- package/dist_ts/mail/delivery/smtpserver/utils/helpers.d.ts +78 -0
- package/dist_ts/mail/delivery/smtpserver/utils/helpers.js +208 -0
- package/dist_ts/mail/delivery/smtpserver/utils/logging.d.ts +106 -0
- package/dist_ts/mail/delivery/smtpserver/utils/logging.js +181 -0
- package/dist_ts/mail/delivery/smtpserver/utils/validation.d.ts +69 -0
- package/dist_ts/mail/delivery/smtpserver/utils/validation.js +360 -0
- package/dist_ts/mail/index.d.ts +8 -0
- package/dist_ts/mail/index.js +13 -0
- package/dist_ts/mail/routing/classes.dns.manager.d.ts +65 -0
- package/dist_ts/mail/routing/classes.dns.manager.js +413 -0
- package/dist_ts/mail/routing/classes.dnsmanager.d.ts +165 -0
- package/dist_ts/mail/routing/classes.dnsmanager.js +430 -0
- package/dist_ts/mail/routing/classes.domain.registry.d.ts +54 -0
- package/dist_ts/mail/routing/classes.domain.registry.js +118 -0
- package/dist_ts/mail/routing/classes.email.config.d.ts +64 -0
- package/dist_ts/mail/routing/classes.email.config.js +2 -0
- package/dist_ts/mail/routing/classes.email.router.d.ts +171 -0
- package/dist_ts/mail/routing/classes.email.router.js +491 -0
- package/dist_ts/mail/routing/classes.unified.email.server.d.ts +426 -0
- package/dist_ts/mail/routing/classes.unified.email.server.js +1454 -0
- package/dist_ts/mail/routing/index.d.ts +5 -0
- package/dist_ts/mail/routing/index.js +7 -0
- package/dist_ts/mail/routing/interfaces.d.ts +187 -0
- package/dist_ts/mail/routing/interfaces.js +2 -0
- package/dist_ts/mail/security/classes.dkimcreator.d.ts +68 -0
- package/dist_ts/mail/security/classes.dkimcreator.js +346 -0
- package/dist_ts/mail/security/classes.dkimverifier.d.ts +46 -0
- package/dist_ts/mail/security/classes.dkimverifier.js +317 -0
- package/dist_ts/mail/security/classes.dmarcverifier.d.ts +123 -0
- package/dist_ts/mail/security/classes.dmarcverifier.js +365 -0
- package/dist_ts/mail/security/classes.spfverifier.d.ts +103 -0
- package/dist_ts/mail/security/classes.spfverifier.js +492 -0
- package/dist_ts/mail/security/index.d.ts +4 -0
- package/dist_ts/mail/security/index.js +6 -0
- package/dist_ts/opsserver/classes.opsserver.d.ts +14 -0
- package/dist_ts/opsserver/classes.opsserver.js +37 -0
- package/dist_ts/opsserver/index.d.ts +1 -0
- package/dist_ts/opsserver/index.js +2 -0
- package/dist_ts/paths.d.ts +14 -0
- package/dist_ts/paths.js +39 -0
- package/dist_ts/plugins.d.ts +43 -0
- package/dist_ts/plugins.js +50 -0
- package/dist_ts/security/classes.contentscanner.d.ts +160 -0
- package/dist_ts/security/classes.contentscanner.js +634 -0
- package/dist_ts/security/classes.ipreputationchecker.d.ts +150 -0
- package/dist_ts/security/classes.ipreputationchecker.js +508 -0
- package/dist_ts/security/classes.securitylogger.d.ts +140 -0
- package/dist_ts/security/classes.securitylogger.js +232 -0
- package/dist_ts/security/index.d.ts +3 -0
- package/dist_ts/security/index.js +4 -0
- package/dist_ts/sms/classes.smsservice.d.ts +15 -0
- package/dist_ts/sms/classes.smsservice.js +72 -0
- package/dist_ts/sms/config/sms.config.d.ts +93 -0
- package/dist_ts/sms/config/sms.config.js +2 -0
- package/dist_ts/sms/config/sms.schema.d.ts +5 -0
- package/dist_ts/sms/config/sms.schema.js +121 -0
- package/dist_ts/sms/index.d.ts +1 -0
- package/dist_ts/sms/index.js +2 -0
- package/dist_ts/storage/classes.storagemanager.d.ts +82 -0
- package/dist_ts/storage/classes.storagemanager.js +341 -0
- package/dist_ts/storage/index.d.ts +1 -0
- package/dist_ts/storage/index.js +3 -0
- package/dist_ts/ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/ts/00_commitinfo_data.js +9 -0
- package/dist_ts/ts/classes.dcrouter.d.ts +238 -0
- package/dist_ts/ts/classes.dcrouter.js +1008 -0
- package/dist_ts/ts/config/index.d.ts +1 -0
- package/dist_ts/ts/config/index.js +3 -0
- package/dist_ts/ts/config/validator.d.ts +104 -0
- package/dist_ts/ts/config/validator.js +152 -0
- package/dist_ts/ts/deliverability/classes.ipwarmupmanager.d.ts +253 -0
- package/dist_ts/ts/deliverability/classes.ipwarmupmanager.js +639 -0
- package/dist_ts/ts/deliverability/classes.senderreputationmonitor.d.ts +300 -0
- package/dist_ts/ts/deliverability/classes.senderreputationmonitor.js +961 -0
- package/dist_ts/ts/deliverability/index.d.ts +2 -0
- package/dist_ts/ts/deliverability/index.js +3 -0
- package/dist_ts/ts/errors/base.errors.d.ts +224 -0
- package/dist_ts/ts/errors/base.errors.js +310 -0
- package/dist_ts/ts/errors/email.errors.d.ts +175 -0
- package/dist_ts/ts/errors/email.errors.js +265 -0
- package/dist_ts/ts/errors/error-handler.d.ts +98 -0
- package/dist_ts/ts/errors/error-handler.js +282 -0
- package/dist_ts/ts/errors/error.codes.d.ts +115 -0
- package/dist_ts/ts/errors/error.codes.js +136 -0
- package/dist_ts/ts/errors/index.d.ts +56 -0
- package/dist_ts/ts/errors/index.js +138 -0
- package/dist_ts/ts/errors/mta.errors.d.ts +259 -0
- package/dist_ts/ts/errors/mta.errors.js +472 -0
- package/dist_ts/ts/errors/reputation.errors.d.ts +183 -0
- package/dist_ts/ts/errors/reputation.errors.js +292 -0
- package/dist_ts/ts/index.d.ts +4 -0
- package/dist_ts/ts/index.js +6 -0
- package/dist_ts/ts/logger.d.ts +17 -0
- package/dist_ts/ts/logger.js +77 -0
- package/dist_ts/ts/mail/core/classes.bouncemanager.d.ts +200 -0
- package/dist_ts/ts/mail/core/classes.bouncemanager.js +778 -0
- package/dist_ts/ts/mail/core/classes.email.d.ts +291 -0
- package/dist_ts/ts/mail/core/classes.email.js +780 -0
- package/dist_ts/ts/mail/core/classes.emailvalidator.d.ts +61 -0
- package/dist_ts/ts/mail/core/classes.emailvalidator.js +182 -0
- package/dist_ts/ts/mail/core/classes.templatemanager.d.ts +95 -0
- package/dist_ts/ts/mail/core/classes.templatemanager.js +239 -0
- package/dist_ts/ts/mail/core/index.d.ts +4 -0
- package/dist_ts/ts/mail/core/index.js +6 -0
- package/dist_ts/ts/mail/delivery/classes.delivery.queue.d.ts +163 -0
- package/dist_ts/ts/mail/delivery/classes.delivery.queue.js +485 -0
- package/dist_ts/ts/mail/delivery/classes.delivery.system.d.ts +186 -0
- package/dist_ts/ts/mail/delivery/classes.delivery.system.js +846 -0
- package/dist_ts/ts/mail/delivery/classes.emailsendjob.d.ts +84 -0
- package/dist_ts/ts/mail/delivery/classes.emailsendjob.js +362 -0
- package/dist_ts/ts/mail/delivery/classes.emailsignjob.d.ts +18 -0
- package/dist_ts/ts/mail/delivery/classes.emailsignjob.js +44 -0
- package/dist_ts/ts/mail/delivery/classes.mta.config.d.ts +22 -0
- package/dist_ts/ts/mail/delivery/classes.mta.config.js +51 -0
- package/dist_ts/ts/mail/delivery/classes.ratelimiter.d.ts +98 -0
- package/dist_ts/ts/mail/delivery/classes.ratelimiter.js +205 -0
- package/dist_ts/ts/mail/delivery/classes.smtp.client.legacy.d.ts +275 -0
- package/dist_ts/ts/mail/delivery/classes.smtp.client.legacy.js +973 -0
- package/dist_ts/ts/mail/delivery/classes.unified.rate.limiter.d.ts +200 -0
- package/dist_ts/ts/mail/delivery/classes.unified.rate.limiter.js +817 -0
- package/dist_ts/ts/mail/delivery/index.d.ts +12 -0
- package/dist_ts/ts/mail/delivery/index.js +18 -0
- package/dist_ts/ts/mail/delivery/interfaces.d.ts +243 -0
- package/dist_ts/ts/mail/delivery/interfaces.js +17 -0
- package/dist_ts/ts/mail/delivery/smtpclient/auth-handler.d.ts +43 -0
- package/dist_ts/ts/mail/delivery/smtpclient/auth-handler.js +188 -0
- package/dist_ts/ts/mail/delivery/smtpclient/command-handler.d.ts +67 -0
- package/dist_ts/ts/mail/delivery/smtpclient/command-handler.js +276 -0
- package/dist_ts/ts/mail/delivery/smtpclient/connection-manager.d.ts +48 -0
- package/dist_ts/ts/mail/delivery/smtpclient/connection-manager.js +238 -0
- package/dist_ts/ts/mail/delivery/smtpclient/constants.d.ts +129 -0
- package/dist_ts/ts/mail/delivery/smtpclient/constants.js +135 -0
- package/dist_ts/ts/mail/delivery/smtpclient/create-client.d.ts +22 -0
- package/dist_ts/ts/mail/delivery/smtpclient/create-client.js +86 -0
- package/dist_ts/ts/mail/delivery/smtpclient/error-handler.d.ts +28 -0
- package/dist_ts/ts/mail/delivery/smtpclient/error-handler.js +110 -0
- package/dist_ts/ts/mail/delivery/smtpclient/index.d.ts +16 -0
- package/dist_ts/ts/mail/delivery/smtpclient/index.js +21 -0
- package/dist_ts/ts/mail/delivery/smtpclient/interfaces.d.ts +183 -0
- package/dist_ts/ts/mail/delivery/smtpclient/interfaces.js +19 -0
- package/dist_ts/ts/mail/delivery/smtpclient/smtp-client.d.ts +58 -0
- package/dist_ts/ts/mail/delivery/smtpclient/smtp-client.js +279 -0
- package/dist_ts/ts/mail/delivery/smtpclient/tls-handler.d.ts +33 -0
- package/dist_ts/ts/mail/delivery/smtpclient/tls-handler.js +202 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/helpers.d.ts +77 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/helpers.js +196 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/logging.d.ts +46 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/logging.js +153 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/validation.d.ts +38 -0
- package/dist_ts/ts/mail/delivery/smtpclient/utils/validation.js +139 -0
- package/dist_ts/ts/mail/delivery/smtpserver/certificate-utils.d.ts +45 -0
- package/dist_ts/ts/mail/delivery/smtpserver/certificate-utils.js +345 -0
- package/dist_ts/ts/mail/delivery/smtpserver/command-handler.d.ts +156 -0
- package/dist_ts/ts/mail/delivery/smtpserver/command-handler.js +1159 -0
- package/dist_ts/ts/mail/delivery/smtpserver/connection-manager.d.ts +159 -0
- package/dist_ts/ts/mail/delivery/smtpserver/connection-manager.js +894 -0
- package/dist_ts/ts/mail/delivery/smtpserver/constants.d.ts +130 -0
- package/dist_ts/ts/mail/delivery/smtpserver/constants.js +162 -0
- package/dist_ts/ts/mail/delivery/smtpserver/create-server.d.ts +14 -0
- package/dist_ts/ts/mail/delivery/smtpserver/create-server.js +28 -0
- package/dist_ts/ts/mail/delivery/smtpserver/data-handler.d.ts +123 -0
- package/dist_ts/ts/mail/delivery/smtpserver/data-handler.js +1148 -0
- package/dist_ts/ts/mail/delivery/smtpserver/index.d.ts +20 -0
- package/dist_ts/ts/mail/delivery/smtpserver/index.js +27 -0
- package/dist_ts/ts/mail/delivery/smtpserver/interfaces.d.ts +530 -0
- package/dist_ts/ts/mail/delivery/smtpserver/interfaces.js +10 -0
- package/dist_ts/ts/mail/delivery/smtpserver/secure-server.d.ts +15 -0
- package/dist_ts/ts/mail/delivery/smtpserver/secure-server.js +79 -0
- package/dist_ts/ts/mail/delivery/smtpserver/security-handler.d.ts +86 -0
- package/dist_ts/ts/mail/delivery/smtpserver/security-handler.js +234 -0
- package/dist_ts/ts/mail/delivery/smtpserver/session-manager.d.ts +140 -0
- package/dist_ts/ts/mail/delivery/smtpserver/session-manager.js +469 -0
- package/dist_ts/ts/mail/delivery/smtpserver/smtp-server.d.ts +137 -0
- package/dist_ts/ts/mail/delivery/smtpserver/smtp-server.js +666 -0
- package/dist_ts/ts/mail/delivery/smtpserver/starttls-handler.d.ts +21 -0
- package/dist_ts/ts/mail/delivery/smtpserver/starttls-handler.js +207 -0
- package/dist_ts/ts/mail/delivery/smtpserver/tls-handler.d.ts +66 -0
- package/dist_ts/ts/mail/delivery/smtpserver/tls-handler.js +261 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/adaptive-logging.d.ts +117 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/adaptive-logging.js +411 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/helpers.d.ts +78 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/helpers.js +208 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/logging.d.ts +106 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/logging.js +181 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/validation.d.ts +69 -0
- package/dist_ts/ts/mail/delivery/smtpserver/utils/validation.js +360 -0
- package/dist_ts/ts/mail/index.d.ts +8 -0
- package/dist_ts/ts/mail/index.js +13 -0
- package/dist_ts/ts/mail/routing/classes.dns.manager.d.ts +65 -0
- package/dist_ts/ts/mail/routing/classes.dns.manager.js +413 -0
- package/dist_ts/ts/mail/routing/classes.dnsmanager.d.ts +165 -0
- package/dist_ts/ts/mail/routing/classes.dnsmanager.js +430 -0
- package/dist_ts/ts/mail/routing/classes.domain.registry.d.ts +54 -0
- package/dist_ts/ts/mail/routing/classes.domain.registry.js +118 -0
- package/dist_ts/ts/mail/routing/classes.email.config.d.ts +64 -0
- package/dist_ts/ts/mail/routing/classes.email.config.js +2 -0
- package/dist_ts/ts/mail/routing/classes.email.router.d.ts +171 -0
- package/dist_ts/ts/mail/routing/classes.email.router.js +491 -0
- package/dist_ts/ts/mail/routing/classes.unified.email.server.d.ts +426 -0
- package/dist_ts/ts/mail/routing/classes.unified.email.server.js +1454 -0
- package/dist_ts/ts/mail/routing/index.d.ts +5 -0
- package/dist_ts/ts/mail/routing/index.js +7 -0
- package/dist_ts/ts/mail/routing/interfaces.d.ts +187 -0
- package/dist_ts/ts/mail/routing/interfaces.js +2 -0
- package/dist_ts/ts/mail/security/classes.dkimcreator.d.ts +68 -0
- package/dist_ts/ts/mail/security/classes.dkimcreator.js +346 -0
- package/dist_ts/ts/mail/security/classes.dkimverifier.d.ts +46 -0
- package/dist_ts/ts/mail/security/classes.dkimverifier.js +317 -0
- package/dist_ts/ts/mail/security/classes.dmarcverifier.d.ts +123 -0
- package/dist_ts/ts/mail/security/classes.dmarcverifier.js +365 -0
- package/dist_ts/ts/mail/security/classes.spfverifier.d.ts +103 -0
- package/dist_ts/ts/mail/security/classes.spfverifier.js +492 -0
- package/dist_ts/ts/mail/security/index.d.ts +4 -0
- package/dist_ts/ts/mail/security/index.js +6 -0
- package/dist_ts/ts/opsserver/classes.opsserver.d.ts +20 -0
- package/dist_ts/ts/opsserver/classes.opsserver.js +44 -0
- package/dist_ts/ts/opsserver/handlers/admin.handler.d.ts +31 -0
- package/dist_ts/ts/opsserver/handlers/admin.handler.js +177 -0
- package/dist_ts/ts/opsserver/handlers/config.handler.d.ts +10 -0
- package/dist_ts/ts/opsserver/handlers/config.handler.js +100 -0
- package/dist_ts/ts/opsserver/handlers/index.d.ts +5 -0
- package/dist_ts/ts/opsserver/handlers/index.js +6 -0
- package/dist_ts/ts/opsserver/handlers/logs.handler.d.ts +10 -0
- package/dist_ts/ts/opsserver/handlers/logs.handler.js +121 -0
- package/dist_ts/ts/opsserver/handlers/security.handler.d.ts +11 -0
- package/dist_ts/ts/opsserver/handlers/security.handler.js +118 -0
- package/dist_ts/ts/opsserver/handlers/stats.handler.d.ts +13 -0
- package/dist_ts/ts/opsserver/handlers/stats.handler.js +233 -0
- package/dist_ts/ts/opsserver/helpers/guards.d.ts +25 -0
- package/dist_ts/ts/opsserver/helpers/guards.js +41 -0
- package/dist_ts/ts/opsserver/index.d.ts +1 -0
- package/dist_ts/ts/opsserver/index.js +2 -0
- package/dist_ts/ts/paths.d.ts +14 -0
- package/dist_ts/ts/paths.js +39 -0
- package/dist_ts/ts/plugins.d.ts +46 -0
- package/dist_ts/ts/plugins.js +53 -0
- package/dist_ts/ts/security/classes.contentscanner.d.ts +160 -0
- package/dist_ts/ts/security/classes.contentscanner.js +634 -0
- package/dist_ts/ts/security/classes.ipreputationchecker.d.ts +150 -0
- package/dist_ts/ts/security/classes.ipreputationchecker.js +508 -0
- package/dist_ts/ts/security/classes.securitylogger.d.ts +140 -0
- package/dist_ts/ts/security/classes.securitylogger.js +232 -0
- package/dist_ts/ts/security/index.d.ts +3 -0
- package/dist_ts/ts/security/index.js +4 -0
- package/dist_ts/ts/sms/classes.smsservice.d.ts +15 -0
- package/dist_ts/ts/sms/classes.smsservice.js +72 -0
- package/dist_ts/ts/sms/config/sms.config.d.ts +93 -0
- package/dist_ts/ts/sms/config/sms.config.js +2 -0
- package/dist_ts/ts/sms/config/sms.schema.d.ts +5 -0
- package/dist_ts/ts/sms/config/sms.schema.js +121 -0
- package/dist_ts/ts/sms/index.d.ts +1 -0
- package/dist_ts/ts/sms/index.js +2 -0
- package/dist_ts/ts/storage/classes.storagemanager.d.ts +82 -0
- package/dist_ts/ts/storage/classes.storagemanager.js +341 -0
- package/dist_ts/ts/storage/index.d.ts +1 -0
- package/dist_ts/ts/storage/index.js +3 -0
- package/dist_ts/ts_interfaces/data/auth.d.ts +8 -0
- package/dist_ts/ts_interfaces/data/auth.js +2 -0
- package/dist_ts/ts_interfaces/data/index.d.ts +2 -0
- package/dist_ts/ts_interfaces/data/index.js +3 -0
- package/dist_ts/ts_interfaces/data/stats.d.ts +93 -0
- package/dist_ts/ts_interfaces/data/stats.js +2 -0
- package/dist_ts/ts_interfaces/index.d.ts +5 -0
- package/dist_ts/ts_interfaces/index.js +8 -0
- package/dist_ts/ts_interfaces/plugins.d.ts +2 -0
- package/dist_ts/ts_interfaces/plugins.js +4 -0
- package/dist_ts/ts_interfaces/requests/admin.d.ts +31 -0
- package/dist_ts/ts_interfaces/requests/admin.js +3 -0
- package/dist_ts/ts_interfaces/requests/config.d.ts +25 -0
- package/dist_ts/ts_interfaces/requests/config.js +3 -0
- package/dist_ts/ts_interfaces/requests/index.d.ts +4 -0
- package/dist_ts/ts_interfaces/requests/index.js +5 -0
- package/dist_ts/ts_interfaces/requests/logs.d.ts +34 -0
- package/dist_ts/ts_interfaces/requests/logs.js +4 -0
- package/dist_ts/ts_interfaces/requests/stats.d.ts +131 -0
- package/dist_ts/ts_interfaces/requests/stats.js +4 -0
- package/html/index.html +121 -0
- package/npmextra.json +45 -0
- package/package.json +83 -0
- package/readme.hints.md +906 -0
- package/readme.md +1253 -0
- package/readme.opsserver.md +351 -0
- package/test/helpers/server.loader.ts +347 -0
- package/test/helpers/smtp.client.ts +209 -0
- package/test/helpers/utils.ts +311 -0
- package/test/readme.md +443 -0
- package/test/suite/smtpclient_commands/test.ccmd-01.ehlo-helo-sending.ts +168 -0
- package/test/suite/smtpclient_commands/test.ccmd-02.mail-from-parameters.ts +277 -0
- package/test/suite/smtpclient_commands/test.ccmd-03.rcpt-to-multiple.ts +283 -0
- package/test/suite/smtpclient_commands/test.ccmd-04.data-transmission.ts +274 -0
- package/test/suite/smtpclient_commands/test.ccmd-05.auth-mechanisms.ts +306 -0
- package/test/suite/smtpclient_commands/test.ccmd-06.command-pipelining.ts +233 -0
- package/test/suite/smtpclient_commands/test.ccmd-07.response-parsing.ts +243 -0
- package/test/suite/smtpclient_commands/test.ccmd-08.rset-command.ts +333 -0
- package/test/suite/smtpclient_commands/test.ccmd-09.noop-command.ts +339 -0
- package/test/suite/smtpclient_commands/test.ccmd-10.vrfy-expn.ts +457 -0
- package/test/suite/smtpclient_commands/test.ccmd-11.help-command.ts +409 -0
- package/test/suite/smtpclient_connection/test.ccm-01.basic-tcp-connection.ts +150 -0
- package/test/suite/smtpclient_connection/test.ccm-02.tls-connection.ts +140 -0
- package/test/suite/smtpclient_connection/test.ccm-03.starttls-upgrade.ts +208 -0
- package/test/suite/smtpclient_connection/test.ccm-04.connection-pooling.ts +250 -0
- package/test/suite/smtpclient_connection/test.ccm-05.connection-reuse.ts +288 -0
- package/test/suite/smtpclient_connection/test.ccm-06.connection-timeout.ts +267 -0
- package/test/suite/smtpclient_connection/test.ccm-07.automatic-reconnection.ts +324 -0
- package/test/suite/smtpclient_connection/test.ccm-08.dns-resolution.ts +139 -0
- package/test/suite/smtpclient_connection/test.ccm-09.ipv6-dual-stack.ts +167 -0
- package/test/suite/smtpclient_connection/test.ccm-10.proxy-support.ts +305 -0
- package/test/suite/smtpclient_connection/test.ccm-11.keepalive.ts +299 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-01.unusual-server-responses.ts +529 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-02.malformed-commands.ts +438 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-03.protocol-violations.ts +446 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-04.resource-constraints.ts +530 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-05.encoding-issues.ts +145 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-06.large-headers.ts +180 -0
- package/test/suite/smtpclient_edge-cases/test.cedge-07.concurrent-operations.ts +204 -0
- package/test/suite/smtpclient_email-composition/test.cep-01.basic-headers.ts +245 -0
- package/test/suite/smtpclient_email-composition/test.cep-02.mime-multipart.ts +321 -0
- package/test/suite/smtpclient_email-composition/test.cep-03.attachment-encoding.ts +334 -0
- package/test/suite/smtpclient_email-composition/test.cep-04.bcc-handling.ts +187 -0
- package/test/suite/smtpclient_email-composition/test.cep-05.reply-to-return-path.ts +277 -0
- package/test/suite/smtpclient_email-composition/test.cep-06.utf8-international.ts +235 -0
- package/test/suite/smtpclient_email-composition/test.cep-07.html-inline-images.ts +489 -0
- package/test/suite/smtpclient_email-composition/test.cep-08.custom-headers.ts +293 -0
- package/test/suite/smtpclient_email-composition/test.cep-09.priority-importance.ts +314 -0
- package/test/suite/smtpclient_email-composition/test.cep-10.receipts-dsn.ts +411 -0
- package/test/suite/smtpclient_error-handling/test.cerr-01.4xx-errors.ts +232 -0
- package/test/suite/smtpclient_error-handling/test.cerr-02.5xx-errors.ts +309 -0
- package/test/suite/smtpclient_error-handling/test.cerr-03.network-failures.ts +299 -0
- package/test/suite/smtpclient_error-handling/test.cerr-04.greylisting-handling.ts +255 -0
- package/test/suite/smtpclient_error-handling/test.cerr-05.quota-exceeded.ts +273 -0
- package/test/suite/smtpclient_error-handling/test.cerr-06.invalid-recipients.ts +320 -0
- package/test/suite/smtpclient_error-handling/test.cerr-07.message-size-limits.ts +320 -0
- package/test/suite/smtpclient_error-handling/test.cerr-08.rate-limiting.ts +261 -0
- package/test/suite/smtpclient_error-handling/test.cerr-09.connection-pool-errors.ts +299 -0
- package/test/suite/smtpclient_error-handling/test.cerr-10.partial-failure.ts +373 -0
- package/test/suite/smtpclient_performance/test.cperf-01.bulk-sending.ts +332 -0
- package/test/suite/smtpclient_performance/test.cperf-02.message-throughput.ts +304 -0
- package/test/suite/smtpclient_performance/test.cperf-03.memory-usage.ts +332 -0
- package/test/suite/smtpclient_performance/test.cperf-04.cpu-utilization.ts +373 -0
- package/test/suite/smtpclient_performance/test.cperf-05.network-efficiency.ts +181 -0
- package/test/suite/smtpclient_performance/test.cperf-06.caching-strategies.ts +190 -0
- package/test/suite/smtpclient_performance/test.cperf-07.queue-management.ts +171 -0
- package/test/suite/smtpclient_performance/test.cperf-08.dns-caching.ts +50 -0
- package/test/suite/smtpclient_reliability/test.crel-01.reconnection-logic.ts +305 -0
- package/test/suite/smtpclient_reliability/test.crel-02.network-interruption.ts +207 -0
- package/test/suite/smtpclient_reliability/test.crel-03.queue-persistence.ts +469 -0
- package/test/suite/smtpclient_reliability/test.crel-04.crash-recovery.ts +520 -0
- package/test/suite/smtpclient_reliability/test.crel-05.memory-leaks.ts +503 -0
- package/test/suite/smtpclient_reliability/test.crel-06.concurrency-safety.ts +558 -0
- package/test/suite/smtpclient_reliability/test.crel-07.resource-cleanup.ts +52 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-01.rfc5321-client.ts +283 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-02.esmtp-compliance.ts +77 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-03.command-syntax.ts +67 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-04.response-codes.ts +54 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-05.state-machine.ts +703 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-06.protocol-negotiation.ts +688 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-07.interoperability.ts +728 -0
- package/test/suite/smtpclient_rfc-compliance/test.crfc-08.smtp-extensions.ts +656 -0
- package/test/suite/smtpclient_security/test.csec-01.tls-verification.ts +88 -0
- package/test/suite/smtpclient_security/test.csec-02.oauth2-authentication.ts +132 -0
- package/test/suite/smtpclient_security/test.csec-03.dkim-signing.ts +138 -0
- package/test/suite/smtpclient_security/test.csec-04.spf-compliance.ts +163 -0
- package/test/suite/smtpclient_security/test.csec-05.dmarc-policy.ts +200 -0
- package/test/suite/smtpclient_security/test.csec-06.certificate-validation.ts +145 -0
- package/test/suite/smtpclient_security/test.csec-07.cipher-suites.ts +153 -0
- package/test/suite/smtpclient_security/test.csec-08.authentication-fallback.ts +154 -0
- package/test/suite/smtpclient_security/test.csec-09.relay-restrictions.ts +166 -0
- package/test/suite/smtpclient_security/test.csec-10.anti-spam-measures.ts +196 -0
- package/test/suite/smtpserver_commands/test.cmd-01.ehlo-command.ts +193 -0
- package/test/suite/smtpserver_commands/test.cmd-02.mail-from.ts +330 -0
- package/test/suite/smtpserver_commands/test.cmd-03.rcpt-to.ts +296 -0
- package/test/suite/smtpserver_commands/test.cmd-04.data-command.ts +395 -0
- package/test/suite/smtpserver_commands/test.cmd-05.noop-command.ts +320 -0
- package/test/suite/smtpserver_commands/test.cmd-06.rset-command.ts +399 -0
- package/test/suite/smtpserver_commands/test.cmd-07.vrfy-command.ts +391 -0
- package/test/suite/smtpserver_commands/test.cmd-08.expn-command.ts +450 -0
- package/test/suite/smtpserver_commands/test.cmd-09.size-extension.ts +465 -0
- package/test/suite/smtpserver_commands/test.cmd-10.help-command.ts +454 -0
- package/test/suite/smtpserver_commands/test.cmd-11.command-pipelining.ts +334 -0
- package/test/suite/smtpserver_commands/test.cmd-12.helo-command.ts +420 -0
- package/test/suite/smtpserver_commands/test.cmd-13.quit-command.ts +384 -0
- package/test/suite/smtpserver_connection/test.cm-01.tls-connection.ts +61 -0
- package/test/suite/smtpserver_connection/test.cm-02.multiple-connections.ts +112 -0
- package/test/suite/smtpserver_connection/test.cm-03.connection-timeout.ts +134 -0
- package/test/suite/smtpserver_connection/test.cm-04.connection-limits.ts +374 -0
- package/test/suite/smtpserver_connection/test.cm-05.connection-rejection.ts +296 -0
- package/test/suite/smtpserver_connection/test.cm-06.starttls-upgrade.ts +468 -0
- package/test/suite/smtpserver_connection/test.cm-07.abrupt-disconnection.ts +321 -0
- package/test/suite/smtpserver_connection/test.cm-08.tls-versions.ts +361 -0
- package/test/suite/smtpserver_connection/test.cm-09.tls-ciphers.ts +556 -0
- package/test/suite/smtpserver_connection/test.cm-10.plain-connection.ts +293 -0
- package/test/suite/smtpserver_connection/test.cm-11.keepalive.ts +382 -0
- package/test/suite/smtpserver_edge-cases/test.edge-01.very-large-email.ts +239 -0
- package/test/suite/smtpserver_edge-cases/test.edge-02.very-small-email.ts +389 -0
- package/test/suite/smtpserver_edge-cases/test.edge-03.invalid-character-handling.ts +479 -0
- package/test/suite/smtpserver_edge-cases/test.edge-04.empty-commands.ts +430 -0
- package/test/suite/smtpserver_edge-cases/test.edge-05.extremely-long-lines.ts +425 -0
- package/test/suite/smtpserver_edge-cases/test.edge-06.extremely-long-headers.ts +404 -0
- package/test/suite/smtpserver_edge-cases/test.edge-07.unusual-mime-types.ts +333 -0
- package/test/suite/smtpserver_edge-cases/test.edge-08.nested-mime-structures.ts +379 -0
- package/test/suite/smtpserver_email-processing/test.ep-01.basic-email-sending.ts +338 -0
- package/test/suite/smtpserver_email-processing/test.ep-02.invalid-email-addresses.ts +315 -0
- package/test/suite/smtpserver_email-processing/test.ep-03.multiple-recipients.ts +493 -0
- package/test/suite/smtpserver_email-processing/test.ep-04.large-email.ts +528 -0
- package/test/suite/smtpserver_email-processing/test.ep-05.mime-handling.ts +515 -0
- package/test/suite/smtpserver_email-processing/test.ep-06.attachment-handling.ts +629 -0
- package/test/suite/smtpserver_email-processing/test.ep-07.special-character-handling.ts +462 -0
- package/test/suite/smtpserver_email-processing/test.ep-08.email-routing.ts +527 -0
- package/test/suite/smtpserver_email-processing/test.ep-09.delivery-status-notifications.ts +486 -0
- package/test/suite/smtpserver_error-handling/test.err-01.syntax-errors.ts +475 -0
- package/test/suite/smtpserver_error-handling/test.err-02.invalid-sequence.ts +450 -0
- package/test/suite/smtpserver_error-handling/test.err-03.temporary-failures.ts +453 -0
- package/test/suite/smtpserver_error-handling/test.err-04.permanent-failures.ts +325 -0
- package/test/suite/smtpserver_error-handling/test.err-05.resource-exhaustion.ts +302 -0
- package/test/suite/smtpserver_error-handling/test.err-06.malformed-mime.ts +374 -0
- package/test/suite/smtpserver_error-handling/test.err-07.exception-handling.ts +333 -0
- package/test/suite/smtpserver_error-handling/test.err-08.error-logging.ts +324 -0
- package/test/suite/smtpserver_performance/test.perf-01.throughput.ts +183 -0
- package/test/suite/smtpserver_performance/test.perf-02.concurrency.ts +388 -0
- package/test/suite/smtpserver_performance/test.perf-03.cpu-utilization.ts +245 -0
- package/test/suite/smtpserver_performance/test.perf-04.memory-usage.ts +238 -0
- package/test/suite/smtpserver_performance/test.perf-05.connection-processing-time.ts +363 -0
- package/test/suite/smtpserver_performance/test.perf-06.message-processing-time.ts +252 -0
- package/test/suite/smtpserver_performance/test.perf-07.resource-cleanup.ts +317 -0
- package/test/suite/smtpserver_reliability/test.rel-01.long-running-operation.ts +344 -0
- package/test/suite/smtpserver_reliability/test.rel-02.restart-recovery.ts +328 -0
- package/test/suite/smtpserver_reliability/test.rel-03.resource-leak-detection.ts +394 -0
- package/test/suite/smtpserver_reliability/test.rel-04.error-recovery.ts +401 -0
- package/test/suite/smtpserver_reliability/test.rel-05.dns-resolution-failure.ts +335 -0
- package/test/suite/smtpserver_reliability/test.rel-06.network-interruption.ts +410 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-01.rfc5321-compliance.ts +382 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-02.rfc5322-compliance.ts +428 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-03.rfc7208-spf-compliance.ts +330 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-04.rfc6376-dkim-compliance.ts +450 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-05.rfc7489-dmarc-compliance.ts +408 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-06.rfc8314-tls-compliance.ts +366 -0
- package/test/suite/smtpserver_rfc-compliance/test.rfc-07.rfc3461-dsn-compliance.ts +399 -0
- package/test/suite/smtpserver_security/test.sec-01.authentication.ts +218 -0
- package/test/suite/smtpserver_security/test.sec-02.authorization.ts +286 -0
- package/test/suite/smtpserver_security/test.sec-03.dkim-processing.ts +414 -0
- package/test/suite/smtpserver_security/test.sec-04.spf-checking.ts +280 -0
- package/test/suite/smtpserver_security/test.sec-05.dmarc-policy.ts +374 -0
- package/test/suite/smtpserver_security/test.sec-06.ip-reputation.ts +303 -0
- package/test/suite/smtpserver_security/test.sec-07.content-scanning.ts +409 -0
- package/test/suite/smtpserver_security/test.sec-08.rate-limiting.ts +324 -0
- package/test/suite/smtpserver_security/test.sec-09.tls-certificate-validation.ts +312 -0
- package/test/suite/smtpserver_security/test.sec-10.header-injection-prevention.ts +332 -0
- package/test/suite/smtpserver_security/test.sec-11.bounce-management.ts +363 -0
- package/test/test.base.ts +65 -0
- package/test/test.bouncemanager.ts +196 -0
- package/test/test.config.md +175 -0
- package/test/test.contentscanner.ts +265 -0
- package/test/test.dcrouter.email.ts +201 -0
- package/test/test.deliverability.ts +55 -0
- package/test/test.dns-manager-creation.ts +141 -0
- package/test/test.dns-mode-switching.ts +257 -0
- package/test/test.dns-server-config.ts +140 -0
- package/test/test.dns-socket-handler.ts +169 -0
- package/test/test.dns-validation.ts +283 -0
- package/test/test.email-socket-handler.ts +228 -0
- package/test/test.email.integration.ts +377 -0
- package/test/test.email.router.ts +283 -0
- package/test/test.emailauth.ts +195 -0
- package/test/test.errors.ts +408 -0
- package/test/test.integration.storage.ts +313 -0
- package/test/test.integration.ts +75 -0
- package/test/test.ipreputationchecker.ts +179 -0
- package/test/test.ipwarmupmanager.ts +323 -0
- package/test/test.jwt-auth.ts +130 -0
- package/test/test.minimal.ts +66 -0
- package/test/test.opsserver-api.ts +83 -0
- package/test/test.protected-endpoint.ts +115 -0
- package/test/test.rate-limiting-integration.ts +236 -0
- package/test/test.ratelimiter.ts +141 -0
- package/test/test.reputationmonitor.ts +262 -0
- package/test/test.smartmail.ts +248 -0
- package/test/test.smtp.client.compatibility.ts +154 -0
- package/test/test.smtp.client.ts +191 -0
- package/test/test.smtp.server.ts +180 -0
- package/test/test.socket-handler-integration.ts +240 -0
- package/test/test.socket-handler-unit.ts +198 -0
- package/test/test.storagemanager.ts +289 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/classes.dcrouter.ts +1310 -0
- package/ts/config/index.ts +2 -0
- package/ts/config/validator.ts +266 -0
- package/ts/deliverability/classes.ipwarmupmanager.ts +896 -0
- package/ts/deliverability/classes.senderreputationmonitor.ts +1244 -0
- package/ts/deliverability/index.ts +13 -0
- package/ts/errors/base.errors.ts +525 -0
- package/ts/errors/email.errors.ts +383 -0
- package/ts/errors/error-handler.ts +412 -0
- package/ts/errors/error.codes.ts +165 -0
- package/ts/errors/index.ts +195 -0
- package/ts/errors/mta.errors.ts +681 -0
- package/ts/errors/reputation.errors.ts +422 -0
- package/ts/index.ts +7 -0
- package/ts/logger.ts +91 -0
- package/ts/mail/core/classes.bouncemanager.ts +965 -0
- package/ts/mail/core/classes.email.ts +941 -0
- package/ts/mail/core/classes.emailvalidator.ts +239 -0
- package/ts/mail/core/classes.templatemanager.ts +320 -0
- package/ts/mail/core/index.ts +5 -0
- package/ts/mail/delivery/classes.delivery.queue.ts +645 -0
- package/ts/mail/delivery/classes.delivery.system.ts +1089 -0
- package/ts/mail/delivery/classes.emailsendjob.ts +447 -0
- package/ts/mail/delivery/classes.emailsendjob.ts.backup +691 -0
- package/ts/mail/delivery/classes.emailsignjob.ts +67 -0
- package/ts/mail/delivery/classes.mta.config.ts +73 -0
- package/ts/mail/delivery/classes.ratelimiter.ts +281 -0
- package/ts/mail/delivery/classes.smtp.client.legacy.ts +1422 -0
- package/ts/mail/delivery/classes.unified.rate.limiter.ts +1053 -0
- package/ts/mail/delivery/index.ts +24 -0
- package/ts/mail/delivery/interfaces.ts +291 -0
- package/ts/mail/delivery/smtpclient/auth-handler.ts +232 -0
- package/ts/mail/delivery/smtpclient/command-handler.ts +343 -0
- package/ts/mail/delivery/smtpclient/connection-manager.ts +289 -0
- package/ts/mail/delivery/smtpclient/constants.ts +145 -0
- package/ts/mail/delivery/smtpclient/create-client.ts +94 -0
- package/ts/mail/delivery/smtpclient/error-handler.ts +141 -0
- package/ts/mail/delivery/smtpclient/index.ts +24 -0
- package/ts/mail/delivery/smtpclient/interfaces.ts +242 -0
- package/ts/mail/delivery/smtpclient/smtp-client.ts +357 -0
- package/ts/mail/delivery/smtpclient/tls-handler.ts +254 -0
- package/ts/mail/delivery/smtpclient/utils/helpers.ts +224 -0
- package/ts/mail/delivery/smtpclient/utils/logging.ts +212 -0
- package/ts/mail/delivery/smtpclient/utils/validation.ts +170 -0
- package/ts/mail/delivery/smtpserver/certificate-utils.ts +398 -0
- package/ts/mail/delivery/smtpserver/command-handler.ts +1340 -0
- package/ts/mail/delivery/smtpserver/connection-manager.ts +1045 -0
- package/ts/mail/delivery/smtpserver/constants.ts +181 -0
- package/ts/mail/delivery/smtpserver/create-server.ts +31 -0
- package/ts/mail/delivery/smtpserver/data-handler.ts +1283 -0
- package/ts/mail/delivery/smtpserver/index.ts +32 -0
- package/ts/mail/delivery/smtpserver/interfaces.ts +655 -0
- package/ts/mail/delivery/smtpserver/secure-server.ts +97 -0
- package/ts/mail/delivery/smtpserver/security-handler.ts +345 -0
- package/ts/mail/delivery/smtpserver/session-manager.ts +557 -0
- package/ts/mail/delivery/smtpserver/smtp-server.ts +804 -0
- package/ts/mail/delivery/smtpserver/starttls-handler.ts +262 -0
- package/ts/mail/delivery/smtpserver/tls-handler.ts +346 -0
- package/ts/mail/delivery/smtpserver/utils/adaptive-logging.ts +514 -0
- package/ts/mail/delivery/smtpserver/utils/helpers.ts +246 -0
- package/ts/mail/delivery/smtpserver/utils/logging.ts +246 -0
- package/ts/mail/delivery/smtpserver/utils/validation.ts +436 -0
- package/ts/mail/index.ts +19 -0
- package/ts/mail/routing/classes.dns.manager.ts +563 -0
- package/ts/mail/routing/classes.dnsmanager.ts +559 -0
- package/ts/mail/routing/classes.domain.registry.ts +139 -0
- package/ts/mail/routing/classes.email.config.ts +82 -0
- package/ts/mail/routing/classes.email.router.ts +575 -0
- package/ts/mail/routing/classes.unified.email.server.ts +1873 -0
- package/ts/mail/routing/index.ts +6 -0
- package/ts/mail/routing/interfaces.ts +202 -0
- package/ts/mail/security/classes.dkimcreator.ts +431 -0
- package/ts/mail/security/classes.dkimverifier.ts +382 -0
- package/ts/mail/security/classes.dmarcverifier.ts +478 -0
- package/ts/mail/security/classes.spfverifier.ts +606 -0
- package/ts/mail/security/index.ts +5 -0
- package/ts/opsserver/classes.opsserver.ts +65 -0
- package/ts/opsserver/handlers/admin.handler.ts +240 -0
- package/ts/opsserver/handlers/config.handler.ts +150 -0
- package/ts/opsserver/handlers/index.ts +5 -0
- package/ts/opsserver/handlers/logs.handler.ts +195 -0
- package/ts/opsserver/handlers/security.handler.ts +208 -0
- package/ts/opsserver/handlers/stats.handler.ts +344 -0
- package/ts/opsserver/helpers/guards.ts +56 -0
- package/ts/opsserver/index.ts +1 -0
- package/ts/paths.ts +48 -0
- package/ts/plugins.ts +94 -0
- package/ts/security/classes.contentscanner.ts +739 -0
- package/ts/security/classes.ipreputationchecker.ts +592 -0
- package/ts/security/classes.securitylogger.ts +299 -0
- package/ts/security/index.ts +21 -0
- package/ts/sms/classes.smsservice.ts +98 -0
- package/ts/sms/config/sms.config.ts +109 -0
- package/ts/sms/config/sms.schema.ts +122 -0
- package/ts/sms/index.ts +1 -0
- package/ts/storage/classes.storagemanager.ts +400 -0
- package/ts/storage/index.ts +2 -0
- package/ts/tspublish.json +3 -0
- package/ts_interfaces/data/auth.ts +8 -0
- package/ts_interfaces/data/index.ts +2 -0
- package/ts_interfaces/data/stats.ts +101 -0
- package/ts_interfaces/index.ts +9 -0
- package/ts_interfaces/plugins.ts +6 -0
- package/ts_interfaces/requests/admin.ts +46 -0
- package/ts_interfaces/requests/config.ts +35 -0
- package/ts_interfaces/requests/index.ts +4 -0
- package/ts_interfaces/requests/logs.ts +44 -0
- package/ts_interfaces/requests/stats.ts +162 -0
- package/ts_interfaces/tspublish.json +3 -0
- package/ts_web/00_commitinfo_data.ts +8 -0
- package/ts_web/appstate.ts +361 -0
- package/ts_web/elements/index.ts +7 -0
- package/ts_web/elements/ops-dashboard.ts +165 -0
- package/ts_web/elements/ops-view-config.ts +268 -0
- package/ts_web/elements/ops-view-logs.ts +207 -0
- package/ts_web/elements/ops-view-overview.ts +222 -0
- package/ts_web/elements/ops-view-security.ts +471 -0
- package/ts_web/elements/ops-view-stats.ts +299 -0
- package/ts_web/elements/shared/css.ts +10 -0
- package/ts_web/elements/shared/index.ts +2 -0
- package/ts_web/elements/shared/ops-sectionheading.ts +42 -0
- package/ts_web/index.ts +9 -0
- package/ts_web/plugins.ts +11 -0
- package/ts_web/tspublish.json +3 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,1244 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import * as paths from '../paths.js';
|
|
3
|
+
import { logger } from '../logger.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Domain reputation metrics
|
|
7
|
+
*/
|
|
8
|
+
export interface IDomainReputationMetrics {
|
|
9
|
+
/** Domain being monitored */
|
|
10
|
+
domain: string;
|
|
11
|
+
/** Date the metrics were last updated */
|
|
12
|
+
lastUpdated: Date;
|
|
13
|
+
/** Sending volume metrics */
|
|
14
|
+
volume: {
|
|
15
|
+
/** Total emails sent in the tracking period */
|
|
16
|
+
sent: number;
|
|
17
|
+
/** Delivered emails (excluding bounces) */
|
|
18
|
+
delivered: number;
|
|
19
|
+
/** Hard bounces */
|
|
20
|
+
hardBounces: number;
|
|
21
|
+
/** Soft bounces */
|
|
22
|
+
softBounces: number;
|
|
23
|
+
/** Daily sending volume for the last 30 days */
|
|
24
|
+
dailySendVolume: Record<string, number>;
|
|
25
|
+
};
|
|
26
|
+
/** Engagement metrics */
|
|
27
|
+
engagement: {
|
|
28
|
+
/** Number of opens */
|
|
29
|
+
opens: number;
|
|
30
|
+
/** Number of clicks */
|
|
31
|
+
clicks: number;
|
|
32
|
+
/** Calculated open rate (percentage) */
|
|
33
|
+
openRate: number;
|
|
34
|
+
/** Calculated click rate (percentage) */
|
|
35
|
+
clickRate: number;
|
|
36
|
+
/** Click-to-open rate (percentage) */
|
|
37
|
+
clickToOpenRate: number;
|
|
38
|
+
};
|
|
39
|
+
/** Complaint metrics */
|
|
40
|
+
complaints: {
|
|
41
|
+
/** Number of spam complaints */
|
|
42
|
+
total: number;
|
|
43
|
+
/** Complaint rate (percentage) */
|
|
44
|
+
rate: number;
|
|
45
|
+
/** Domains with highest complaint rates */
|
|
46
|
+
topDomains: Array<{ domain: string; rate: number; count: number }>;
|
|
47
|
+
};
|
|
48
|
+
/** Authentication metrics */
|
|
49
|
+
authentication: {
|
|
50
|
+
/** Percentage of emails with valid SPF */
|
|
51
|
+
spfPassRate: number;
|
|
52
|
+
/** Percentage of emails with valid DKIM */
|
|
53
|
+
dkimPassRate: number;
|
|
54
|
+
/** Percentage of emails with valid DMARC */
|
|
55
|
+
dmarcPassRate: number;
|
|
56
|
+
/** Authentication failures */
|
|
57
|
+
failures: Array<{ type: string; domain: string; count: number }>;
|
|
58
|
+
};
|
|
59
|
+
/** Blocklist status */
|
|
60
|
+
blocklist: {
|
|
61
|
+
/** Current blocklist status */
|
|
62
|
+
listed: boolean;
|
|
63
|
+
/** Blocklists the domain is on, if any */
|
|
64
|
+
activeListings: Array<{ list: string; listedSince: Date }>;
|
|
65
|
+
/** Recent delistings */
|
|
66
|
+
recentDelistings: Array<{ list: string; listedFrom: Date; listedTo: Date }>;
|
|
67
|
+
};
|
|
68
|
+
/** Inbox placement estimates */
|
|
69
|
+
inboxPlacement: {
|
|
70
|
+
/** Overall inbox placement rate estimate */
|
|
71
|
+
overall: number;
|
|
72
|
+
/** Inbox placement rates by major provider */
|
|
73
|
+
providers: Record<string, number>;
|
|
74
|
+
};
|
|
75
|
+
/** Historical reputation scores */
|
|
76
|
+
historical: {
|
|
77
|
+
/** Reputation scores for the last 30 days */
|
|
78
|
+
reputationScores: Record<string, number>;
|
|
79
|
+
/** Trends in key metrics */
|
|
80
|
+
trends: {
|
|
81
|
+
/** Open rate trend (positive or negative percentage) */
|
|
82
|
+
openRate: number;
|
|
83
|
+
/** Complaint rate trend */
|
|
84
|
+
complaintRate: number;
|
|
85
|
+
/** Bounce rate trend */
|
|
86
|
+
bounceRate: number;
|
|
87
|
+
/** Spam listing trend */
|
|
88
|
+
spamListings: number;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Configuration for reputation monitoring
|
|
95
|
+
*/
|
|
96
|
+
export interface IReputationMonitorConfig {
|
|
97
|
+
/** Whether monitoring is enabled */
|
|
98
|
+
enabled?: boolean;
|
|
99
|
+
/** Domains to monitor */
|
|
100
|
+
domains?: string[];
|
|
101
|
+
/** How frequently to update metrics (ms) */
|
|
102
|
+
updateFrequency?: number;
|
|
103
|
+
/** Endpoints for external data sources */
|
|
104
|
+
dataSources?: {
|
|
105
|
+
/** Spam list monitoring service */
|
|
106
|
+
spamLists?: string[];
|
|
107
|
+
/** Deliverability monitoring service endpoint */
|
|
108
|
+
deliverabilityMonitor?: string;
|
|
109
|
+
};
|
|
110
|
+
/** Alerting thresholds */
|
|
111
|
+
alertThresholds?: {
|
|
112
|
+
/** Minimum safe reputation score */
|
|
113
|
+
minReputationScore?: number;
|
|
114
|
+
/** Maximum acceptable complaint rate */
|
|
115
|
+
maxComplaintRate?: number;
|
|
116
|
+
/** Maximum acceptable bounce rate */
|
|
117
|
+
maxBounceRate?: number;
|
|
118
|
+
/** Minimum acceptable open rate */
|
|
119
|
+
minOpenRate?: number;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Reputation score components
|
|
125
|
+
*/
|
|
126
|
+
interface IReputationComponents {
|
|
127
|
+
/** Engagement score (0-100) */
|
|
128
|
+
engagement: number;
|
|
129
|
+
/** Complaint score (0-100) */
|
|
130
|
+
complaints: number;
|
|
131
|
+
/** Authentication score (0-100) */
|
|
132
|
+
authentication: number;
|
|
133
|
+
/** Volume stability score (0-100) */
|
|
134
|
+
volumeStability: number;
|
|
135
|
+
/** Infrastructure score (0-100) */
|
|
136
|
+
infrastructure: number;
|
|
137
|
+
/** Blocklist score (0-100) */
|
|
138
|
+
blocklist: number;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Default configuration
|
|
143
|
+
*/
|
|
144
|
+
const DEFAULT_CONFIG: Required<IReputationMonitorConfig> = {
|
|
145
|
+
enabled: true,
|
|
146
|
+
domains: [],
|
|
147
|
+
updateFrequency: 24 * 60 * 60 * 1000, // Daily
|
|
148
|
+
dataSources: {
|
|
149
|
+
spamLists: [
|
|
150
|
+
'zen.spamhaus.org',
|
|
151
|
+
'bl.spamcop.net',
|
|
152
|
+
'dnsbl.sorbs.net',
|
|
153
|
+
'b.barracudacentral.org'
|
|
154
|
+
],
|
|
155
|
+
deliverabilityMonitor: null
|
|
156
|
+
},
|
|
157
|
+
alertThresholds: {
|
|
158
|
+
minReputationScore: 70,
|
|
159
|
+
maxComplaintRate: 0.1, // 0.1%
|
|
160
|
+
maxBounceRate: 5, // 5%
|
|
161
|
+
minOpenRate: 15 // 15%
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Class for monitoring and tracking sender reputation for domains
|
|
167
|
+
*/
|
|
168
|
+
export class SenderReputationMonitor {
|
|
169
|
+
private static instance: SenderReputationMonitor;
|
|
170
|
+
private config: Required<IReputationMonitorConfig>;
|
|
171
|
+
private reputationData: Map<string, IDomainReputationMetrics> = new Map();
|
|
172
|
+
private updateTimer: NodeJS.Timeout = null;
|
|
173
|
+
private isInitialized: boolean = false;
|
|
174
|
+
private storageManager?: any; // StorageManager instance
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Constructor for SenderReputationMonitor
|
|
178
|
+
* @param config Configuration options
|
|
179
|
+
* @param storageManager Optional StorageManager instance
|
|
180
|
+
*/
|
|
181
|
+
constructor(config: IReputationMonitorConfig = {}, storageManager?: any) {
|
|
182
|
+
// Merge with default config
|
|
183
|
+
this.config = {
|
|
184
|
+
...DEFAULT_CONFIG,
|
|
185
|
+
...config,
|
|
186
|
+
dataSources: {
|
|
187
|
+
...DEFAULT_CONFIG.dataSources,
|
|
188
|
+
...config.dataSources
|
|
189
|
+
},
|
|
190
|
+
alertThresholds: {
|
|
191
|
+
...DEFAULT_CONFIG.alertThresholds,
|
|
192
|
+
...config.alertThresholds
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
this.storageManager = storageManager;
|
|
197
|
+
|
|
198
|
+
// If no storage manager provided, log warning
|
|
199
|
+
if (!storageManager) {
|
|
200
|
+
logger.log('warn',
|
|
201
|
+
'⚠️ WARNING: SenderReputationMonitor initialized without StorageManager.\n' +
|
|
202
|
+
' Reputation data will only be stored to filesystem.\n' +
|
|
203
|
+
' Consider passing a StorageManager instance for better storage flexibility.'
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Initialize (async, but we don't await here to avoid blocking constructor)
|
|
208
|
+
this.initialize().catch(error => {
|
|
209
|
+
logger.log('error', `Failed to initialize SenderReputationMonitor: ${error.message}`, {
|
|
210
|
+
stack: error.stack
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get the singleton instance
|
|
217
|
+
* @param config Configuration options
|
|
218
|
+
* @param storageManager Optional StorageManager instance
|
|
219
|
+
* @returns Singleton instance
|
|
220
|
+
*/
|
|
221
|
+
public static getInstance(config: IReputationMonitorConfig = {}, storageManager?: any): SenderReputationMonitor {
|
|
222
|
+
if (!SenderReputationMonitor.instance) {
|
|
223
|
+
SenderReputationMonitor.instance = new SenderReputationMonitor(config, storageManager);
|
|
224
|
+
}
|
|
225
|
+
return SenderReputationMonitor.instance;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Initialize the reputation monitor
|
|
230
|
+
*/
|
|
231
|
+
private async initialize(): Promise<void> {
|
|
232
|
+
if (this.isInitialized) return;
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
// Only load data if not running in a test environment
|
|
236
|
+
const isTestEnvironment = process.env.NODE_ENV === 'test' || !!process.env.JEST_WORKER_ID;
|
|
237
|
+
|
|
238
|
+
if (!isTestEnvironment) {
|
|
239
|
+
// Load existing reputation data
|
|
240
|
+
await this.loadReputationData();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Initialize data for any new domains
|
|
244
|
+
for (const domain of this.config.domains) {
|
|
245
|
+
if (!this.reputationData.has(domain)) {
|
|
246
|
+
this.initializeDomainData(domain);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Schedule updates if enabled and not in test environment
|
|
251
|
+
if (this.config.enabled && !isTestEnvironment) {
|
|
252
|
+
this.scheduleUpdates();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
this.isInitialized = true;
|
|
256
|
+
logger.log('info', `Sender Reputation Monitor initialized for ${this.config.domains.length} domains`);
|
|
257
|
+
} catch (error) {
|
|
258
|
+
logger.log('error', `Failed to initialize Sender Reputation Monitor: ${error.message}`, {
|
|
259
|
+
stack: error.stack
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Initialize reputation data for a new domain
|
|
266
|
+
* @param domain Domain to initialize
|
|
267
|
+
*/
|
|
268
|
+
private initializeDomainData(domain: string): void {
|
|
269
|
+
// Create new domain reputation metrics with default values
|
|
270
|
+
const newMetrics: IDomainReputationMetrics = {
|
|
271
|
+
domain,
|
|
272
|
+
lastUpdated: new Date(),
|
|
273
|
+
volume: {
|
|
274
|
+
sent: 0,
|
|
275
|
+
delivered: 0,
|
|
276
|
+
hardBounces: 0,
|
|
277
|
+
softBounces: 0,
|
|
278
|
+
dailySendVolume: {}
|
|
279
|
+
},
|
|
280
|
+
engagement: {
|
|
281
|
+
opens: 0,
|
|
282
|
+
clicks: 0,
|
|
283
|
+
openRate: 0,
|
|
284
|
+
clickRate: 0,
|
|
285
|
+
clickToOpenRate: 0
|
|
286
|
+
},
|
|
287
|
+
complaints: {
|
|
288
|
+
total: 0,
|
|
289
|
+
rate: 0,
|
|
290
|
+
topDomains: []
|
|
291
|
+
},
|
|
292
|
+
authentication: {
|
|
293
|
+
spfPassRate: 100, // Assume perfect initially
|
|
294
|
+
dkimPassRate: 100,
|
|
295
|
+
dmarcPassRate: 100,
|
|
296
|
+
failures: []
|
|
297
|
+
},
|
|
298
|
+
blocklist: {
|
|
299
|
+
listed: false,
|
|
300
|
+
activeListings: [],
|
|
301
|
+
recentDelistings: []
|
|
302
|
+
},
|
|
303
|
+
inboxPlacement: {
|
|
304
|
+
overall: 95, // Start with optimistic estimate
|
|
305
|
+
providers: {
|
|
306
|
+
gmail: 95,
|
|
307
|
+
outlook: 95,
|
|
308
|
+
yahoo: 95,
|
|
309
|
+
aol: 95,
|
|
310
|
+
other: 95
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
historical: {
|
|
314
|
+
reputationScores: {},
|
|
315
|
+
trends: {
|
|
316
|
+
openRate: 0,
|
|
317
|
+
complaintRate: 0,
|
|
318
|
+
bounceRate: 0,
|
|
319
|
+
spamListings: 0
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// Generate some initial historical data points
|
|
325
|
+
const today = new Date();
|
|
326
|
+
for (let i = 0; i < 30; i++) {
|
|
327
|
+
const date = new Date(today);
|
|
328
|
+
date.setDate(date.getDate() - i);
|
|
329
|
+
const dateKey = date.toISOString().split('T')[0];
|
|
330
|
+
newMetrics.historical.reputationScores[dateKey] = 95; // Default good score
|
|
331
|
+
newMetrics.volume.dailySendVolume[dateKey] = 0;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Save the new metrics
|
|
335
|
+
this.reputationData.set(domain, newMetrics);
|
|
336
|
+
logger.log('info', `Initialized reputation data for domain ${domain}`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Schedule regular reputation data updates
|
|
341
|
+
*/
|
|
342
|
+
private scheduleUpdates(): void {
|
|
343
|
+
if (this.updateTimer) {
|
|
344
|
+
clearTimeout(this.updateTimer);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
this.updateTimer = setTimeout(async () => {
|
|
348
|
+
await this.updateAllDomainMetrics();
|
|
349
|
+
this.scheduleUpdates(); // Reschedule for next update
|
|
350
|
+
}, this.config.updateFrequency);
|
|
351
|
+
|
|
352
|
+
logger.log('info', `Scheduled reputation updates every ${this.config.updateFrequency / (60 * 60 * 1000)} hours`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Update metrics for all monitored domains
|
|
357
|
+
*/
|
|
358
|
+
private async updateAllDomainMetrics(): Promise<void> {
|
|
359
|
+
if (!this.config.enabled) return;
|
|
360
|
+
|
|
361
|
+
logger.log('info', 'Starting reputation metrics update for all domains');
|
|
362
|
+
|
|
363
|
+
for (const domain of this.config.domains) {
|
|
364
|
+
try {
|
|
365
|
+
await this.updateDomainMetrics(domain);
|
|
366
|
+
logger.log('info', `Updated reputation metrics for ${domain}`);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
logger.log('error', `Error updating metrics for ${domain}: ${error.message}`, {
|
|
369
|
+
stack: error.stack
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Save all updated data
|
|
375
|
+
await this.saveReputationData();
|
|
376
|
+
|
|
377
|
+
logger.log('info', 'Completed reputation metrics update for all domains');
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Update reputation metrics for a specific domain
|
|
382
|
+
* @param domain Domain to update
|
|
383
|
+
*/
|
|
384
|
+
private async updateDomainMetrics(domain: string): Promise<void> {
|
|
385
|
+
const metrics = this.reputationData.get(domain);
|
|
386
|
+
if (!metrics) {
|
|
387
|
+
logger.log('warn', `No reputation data found for domain ${domain}`);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
// Update last updated timestamp
|
|
393
|
+
metrics.lastUpdated = new Date();
|
|
394
|
+
|
|
395
|
+
// Check blocklist status
|
|
396
|
+
await this.checkBlocklistStatus(domain, metrics);
|
|
397
|
+
|
|
398
|
+
// Update historical data
|
|
399
|
+
this.updateHistoricalData(metrics);
|
|
400
|
+
|
|
401
|
+
// Calculate current reputation score
|
|
402
|
+
const reputationScore = this.calculateReputationScore(metrics);
|
|
403
|
+
|
|
404
|
+
// Save current reputation score to historical data
|
|
405
|
+
const today = new Date().toISOString().split('T')[0];
|
|
406
|
+
metrics.historical.reputationScores[today] = reputationScore;
|
|
407
|
+
|
|
408
|
+
// Calculate trends
|
|
409
|
+
this.calculateTrends(metrics);
|
|
410
|
+
|
|
411
|
+
// Check alert thresholds
|
|
412
|
+
this.checkAlertThresholds(metrics);
|
|
413
|
+
} catch (error) {
|
|
414
|
+
logger.log('error', `Error in updateDomainMetrics for ${domain}: ${error.message}`, {
|
|
415
|
+
stack: error.stack
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Check domain blocklist status
|
|
422
|
+
* @param domain Domain to check
|
|
423
|
+
* @param metrics Metrics to update
|
|
424
|
+
*/
|
|
425
|
+
private async checkBlocklistStatus(domain: string, metrics: IDomainReputationMetrics): Promise<void> {
|
|
426
|
+
// Skip DNS lookups in test environment
|
|
427
|
+
const isTestEnvironment = process.env.NODE_ENV === 'test' || !!process.env.JEST_WORKER_ID;
|
|
428
|
+
if (isTestEnvironment || !this.config.dataSources.spamLists?.length) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const previouslyListed = metrics.blocklist.listed;
|
|
433
|
+
const previousListings = new Set(metrics.blocklist.activeListings.map(l => l.list));
|
|
434
|
+
|
|
435
|
+
// Store current listings to detect changes
|
|
436
|
+
const currentListings: Array<{ list: string; listedSince: Date }> = [];
|
|
437
|
+
|
|
438
|
+
// Check each blocklist
|
|
439
|
+
for (const list of this.config.dataSources.spamLists) {
|
|
440
|
+
try {
|
|
441
|
+
const isListed = await this.checkDomainOnBlocklist(domain, list);
|
|
442
|
+
|
|
443
|
+
if (isListed) {
|
|
444
|
+
// If already known to be listed on this one, keep the original listing date
|
|
445
|
+
const existingListing = metrics.blocklist.activeListings.find(l => l.list === list);
|
|
446
|
+
if (existingListing) {
|
|
447
|
+
currentListings.push(existingListing);
|
|
448
|
+
} else {
|
|
449
|
+
// New listing
|
|
450
|
+
currentListings.push({
|
|
451
|
+
list,
|
|
452
|
+
listedSince: new Date()
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
} catch (error) {
|
|
457
|
+
logger.log('warn', `Error checking ${domain} on blocklist ${list}: ${error.message}`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Update active listings
|
|
462
|
+
metrics.blocklist.activeListings = currentListings;
|
|
463
|
+
metrics.blocklist.listed = currentListings.length > 0;
|
|
464
|
+
|
|
465
|
+
// Check for delistings
|
|
466
|
+
if (previouslyListed) {
|
|
467
|
+
const currentListsSet = new Set(currentListings.map(l => l.list));
|
|
468
|
+
|
|
469
|
+
// Convert Set to Array for compatibility with older JS versions
|
|
470
|
+
Array.from(previousListings).forEach(list => {
|
|
471
|
+
if (!currentListsSet.has(list)) {
|
|
472
|
+
// This list no longer contains the domain - it was delisted
|
|
473
|
+
const previousListing = metrics.blocklist.activeListings.find(l => l.list === list);
|
|
474
|
+
|
|
475
|
+
if (previousListing) {
|
|
476
|
+
metrics.blocklist.recentDelistings.push({
|
|
477
|
+
list,
|
|
478
|
+
listedFrom: previousListing.listedSince,
|
|
479
|
+
listedTo: new Date()
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// Keep only recent delistings (last 90 days)
|
|
486
|
+
const ninetyDaysAgo = new Date();
|
|
487
|
+
ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
|
|
488
|
+
|
|
489
|
+
metrics.blocklist.recentDelistings = metrics.blocklist.recentDelistings
|
|
490
|
+
.filter(d => d.listedTo > ninetyDaysAgo);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Check if a domain is on a specific blocklist
|
|
496
|
+
* @param domain Domain to check
|
|
497
|
+
* @param list Blocklist to check
|
|
498
|
+
* @returns Whether the domain is listed
|
|
499
|
+
*/
|
|
500
|
+
private async checkDomainOnBlocklist(domain: string, list: string): Promise<boolean> {
|
|
501
|
+
try {
|
|
502
|
+
// Look up the domain in the blocklist (simplified)
|
|
503
|
+
if (list === 'zen.spamhaus.org') {
|
|
504
|
+
// For Spamhaus and similar lists, we check the domain MX IPs
|
|
505
|
+
const mxRecords = await plugins.dns.promises.resolveMx(domain);
|
|
506
|
+
|
|
507
|
+
if (mxRecords && mxRecords.length > 0) {
|
|
508
|
+
// Check the primary MX record
|
|
509
|
+
const primaryMx = mxRecords.sort((a, b) => a.priority - b.priority)[0].exchange;
|
|
510
|
+
|
|
511
|
+
// Resolve IP addresses for the MX
|
|
512
|
+
const ips = await plugins.dns.promises.resolve(primaryMx);
|
|
513
|
+
|
|
514
|
+
// Check the first IP
|
|
515
|
+
if (ips.length > 0) {
|
|
516
|
+
const ip = ips[0];
|
|
517
|
+
const reversedIp = ip.split('.').reverse().join('.');
|
|
518
|
+
const lookupDomain = `${reversedIp}.${list}`;
|
|
519
|
+
|
|
520
|
+
try {
|
|
521
|
+
await plugins.dns.promises.resolve(lookupDomain);
|
|
522
|
+
return true; // Listed
|
|
523
|
+
} catch (err) {
|
|
524
|
+
if (err.code === 'ENOTFOUND') {
|
|
525
|
+
return false; // Not listed
|
|
526
|
+
}
|
|
527
|
+
throw err; // Other error
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return false;
|
|
532
|
+
} else {
|
|
533
|
+
// For domain-based blocklists
|
|
534
|
+
const lookupDomain = `${domain}.${list}`;
|
|
535
|
+
try {
|
|
536
|
+
await plugins.dns.promises.resolve(lookupDomain);
|
|
537
|
+
return true; // Listed
|
|
538
|
+
} catch (err) {
|
|
539
|
+
if (err.code === 'ENOTFOUND') {
|
|
540
|
+
return false; // Not listed
|
|
541
|
+
}
|
|
542
|
+
throw err; // Other error
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
} catch (error) {
|
|
546
|
+
logger.log('warn', `Error checking blocklist status for ${domain} on ${list}: ${error.message}`);
|
|
547
|
+
return false; // Assume not listed on error
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Update historical data in metrics
|
|
553
|
+
* @param metrics Metrics to update
|
|
554
|
+
*/
|
|
555
|
+
private updateHistoricalData(metrics: IDomainReputationMetrics): void {
|
|
556
|
+
// Keep only the last 30 days of data
|
|
557
|
+
const dates = Object.keys(metrics.historical.reputationScores)
|
|
558
|
+
.sort((a, b) => b.localeCompare(a)); // Sort descending
|
|
559
|
+
|
|
560
|
+
if (dates.length > 30) {
|
|
561
|
+
const daysToKeep = dates.slice(0, 30);
|
|
562
|
+
const newScores: Record<string, number> = {};
|
|
563
|
+
|
|
564
|
+
for (const day of daysToKeep) {
|
|
565
|
+
newScores[day] = metrics.historical.reputationScores[day];
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
metrics.historical.reputationScores = newScores;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Same for daily send volume
|
|
572
|
+
const volumeDates = Object.keys(metrics.volume.dailySendVolume)
|
|
573
|
+
.sort((a, b) => b.localeCompare(a));
|
|
574
|
+
|
|
575
|
+
if (volumeDates.length > 30) {
|
|
576
|
+
const daysToKeep = volumeDates.slice(0, 30);
|
|
577
|
+
const newVolume: Record<string, number> = {};
|
|
578
|
+
|
|
579
|
+
for (const day of daysToKeep) {
|
|
580
|
+
newVolume[day] = metrics.volume.dailySendVolume[day];
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
metrics.volume.dailySendVolume = newVolume;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Calculate reputation score from metrics
|
|
589
|
+
* @param metrics Domain reputation metrics
|
|
590
|
+
* @returns Reputation score (0-100)
|
|
591
|
+
*/
|
|
592
|
+
private calculateReputationScore(metrics: IDomainReputationMetrics): number {
|
|
593
|
+
// Calculate component scores
|
|
594
|
+
const components: IReputationComponents = {
|
|
595
|
+
engagement: this.calculateEngagementScore(metrics),
|
|
596
|
+
complaints: this.calculateComplaintScore(metrics),
|
|
597
|
+
authentication: this.calculateAuthenticationScore(metrics),
|
|
598
|
+
volumeStability: this.calculateVolumeStabilityScore(metrics),
|
|
599
|
+
infrastructure: this.calculateInfrastructureScore(metrics),
|
|
600
|
+
blocklist: this.calculateBlocklistScore(metrics)
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// Apply weights to components
|
|
604
|
+
const weightedScore =
|
|
605
|
+
components.engagement * 0.25 +
|
|
606
|
+
components.complaints * 0.25 +
|
|
607
|
+
components.authentication * 0.2 +
|
|
608
|
+
components.volumeStability * 0.1 +
|
|
609
|
+
components.infrastructure * 0.1 +
|
|
610
|
+
components.blocklist * 0.1;
|
|
611
|
+
|
|
612
|
+
// Round to 2 decimal places
|
|
613
|
+
return Math.round(weightedScore * 100) / 100;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Calculate engagement component score
|
|
618
|
+
* @param metrics Domain metrics
|
|
619
|
+
* @returns Engagement score (0-100)
|
|
620
|
+
*/
|
|
621
|
+
private calculateEngagementScore(metrics: IDomainReputationMetrics): number {
|
|
622
|
+
const openRate = metrics.engagement.openRate;
|
|
623
|
+
const clickRate = metrics.engagement.clickRate;
|
|
624
|
+
|
|
625
|
+
// Benchmark open and click rates
|
|
626
|
+
// <5% open rate = poor (score: 0-30)
|
|
627
|
+
// 5-15% = average (score: 30-70)
|
|
628
|
+
// >15% = good (score: 70-100)
|
|
629
|
+
let openScore = 0;
|
|
630
|
+
if (openRate < 5) {
|
|
631
|
+
openScore = openRate * 6; // 0-30 scale
|
|
632
|
+
} else if (openRate < 15) {
|
|
633
|
+
openScore = 30 + (openRate - 5) * 4; // 30-70 scale
|
|
634
|
+
} else {
|
|
635
|
+
openScore = 70 + Math.min(30, (openRate - 15) * 2); // 70-100 scale
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Similarly for click rate
|
|
639
|
+
let clickScore = 0;
|
|
640
|
+
if (clickRate < 1) {
|
|
641
|
+
clickScore = clickRate * 30; // 0-30 scale
|
|
642
|
+
} else if (clickRate < 5) {
|
|
643
|
+
clickScore = 30 + (clickRate - 1) * 10; // 30-70 scale
|
|
644
|
+
} else {
|
|
645
|
+
clickScore = 70 + Math.min(30, (clickRate - 5) * 6); // 70-100 scale
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Combine with 60% weight to open rate, 40% to click rate
|
|
649
|
+
return (openScore * 0.6 + clickScore * 0.4);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Calculate complaint component score
|
|
654
|
+
* @param metrics Domain metrics
|
|
655
|
+
* @returns Complaint score (0-100)
|
|
656
|
+
*/
|
|
657
|
+
private calculateComplaintScore(metrics: IDomainReputationMetrics): number {
|
|
658
|
+
const complaintRate = metrics.complaints.rate;
|
|
659
|
+
|
|
660
|
+
// Industry standard: complaint rate should be under 0.1%
|
|
661
|
+
// 0% = perfect (score: 100)
|
|
662
|
+
// 0.1% = threshold (score: 70)
|
|
663
|
+
// 0.5% = problematic (score: 30)
|
|
664
|
+
// 1%+ = critical (score: 0)
|
|
665
|
+
|
|
666
|
+
if (complaintRate === 0) return 100;
|
|
667
|
+
if (complaintRate >= 1) return 0;
|
|
668
|
+
|
|
669
|
+
if (complaintRate < 0.1) {
|
|
670
|
+
// 0-0.1% maps to 100-70
|
|
671
|
+
return 100 - (complaintRate / 0.1) * 30;
|
|
672
|
+
} else if (complaintRate < 0.5) {
|
|
673
|
+
// 0.1-0.5% maps to 70-30
|
|
674
|
+
return 70 - ((complaintRate - 0.1) / 0.4) * 40;
|
|
675
|
+
} else {
|
|
676
|
+
// 0.5-1% maps to 30-0
|
|
677
|
+
return 30 - ((complaintRate - 0.5) / 0.5) * 30;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Calculate authentication component score
|
|
683
|
+
* @param metrics Domain metrics
|
|
684
|
+
* @returns Authentication score (0-100)
|
|
685
|
+
*/
|
|
686
|
+
private calculateAuthenticationScore(metrics: IDomainReputationMetrics): number {
|
|
687
|
+
const spfRate = metrics.authentication.spfPassRate;
|
|
688
|
+
const dkimRate = metrics.authentication.dkimPassRate;
|
|
689
|
+
const dmarcRate = metrics.authentication.dmarcPassRate;
|
|
690
|
+
|
|
691
|
+
// Weight SPF, DKIM, and DMARC
|
|
692
|
+
return (spfRate * 0.3 + dkimRate * 0.3 + dmarcRate * 0.4);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Calculate volume stability component score
|
|
697
|
+
* @param metrics Domain metrics
|
|
698
|
+
* @returns Volume stability score (0-100)
|
|
699
|
+
*/
|
|
700
|
+
private calculateVolumeStabilityScore(metrics: IDomainReputationMetrics): number {
|
|
701
|
+
const volumes = Object.values(metrics.volume.dailySendVolume);
|
|
702
|
+
|
|
703
|
+
if (volumes.length < 2) return 100; // Not enough data
|
|
704
|
+
|
|
705
|
+
// Calculate coefficient of variation (stdev / mean)
|
|
706
|
+
const mean = volumes.reduce((sum, v) => sum + v, 0) / volumes.length;
|
|
707
|
+
if (mean === 0) return 100; // No sending activity
|
|
708
|
+
|
|
709
|
+
const variance = volumes.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / volumes.length;
|
|
710
|
+
const stdev = Math.sqrt(variance);
|
|
711
|
+
const cv = stdev / mean;
|
|
712
|
+
|
|
713
|
+
// Convert to score: lower CV means more stability
|
|
714
|
+
// CV < 0.1 is very stable (score: 90-100)
|
|
715
|
+
// CV < 0.5 is normal (score: 60-90)
|
|
716
|
+
// CV < 1.0 is somewhat unstable (score: 30-60)
|
|
717
|
+
// CV >= 1.0 is unstable (score: 0-30)
|
|
718
|
+
|
|
719
|
+
if (cv < 0.1) {
|
|
720
|
+
return 90 + (1 - cv / 0.1) * 10;
|
|
721
|
+
} else if (cv < 0.5) {
|
|
722
|
+
return 60 + (1 - (cv - 0.1) / 0.4) * 30;
|
|
723
|
+
} else if (cv < 1.0) {
|
|
724
|
+
return 30 + (1 - (cv - 0.5) / 0.5) * 30;
|
|
725
|
+
} else {
|
|
726
|
+
return Math.max(0, 30 - (cv - 1.0) * 10);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Calculate infrastructure component score
|
|
732
|
+
* @param metrics Domain metrics
|
|
733
|
+
* @returns Infrastructure score (0-100)
|
|
734
|
+
*/
|
|
735
|
+
private calculateInfrastructureScore(metrics: IDomainReputationMetrics): number {
|
|
736
|
+
// This is a placeholder; in reality, this would be based on:
|
|
737
|
+
// - IP reputation
|
|
738
|
+
// - Reverse DNS configuration
|
|
739
|
+
// - IP warming status
|
|
740
|
+
// - Historical IP behavior
|
|
741
|
+
|
|
742
|
+
// For now, assume good infrastructure
|
|
743
|
+
return 90;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Calculate blocklist component score
|
|
748
|
+
* @param metrics Domain metrics
|
|
749
|
+
* @returns Blocklist score (0-100)
|
|
750
|
+
*/
|
|
751
|
+
private calculateBlocklistScore(metrics: IDomainReputationMetrics): number {
|
|
752
|
+
// If currently listed on any blocklist, score is heavily impacted
|
|
753
|
+
if (metrics.blocklist.listed) {
|
|
754
|
+
// Number of active listings determines severity
|
|
755
|
+
const listingCount = metrics.blocklist.activeListings.length;
|
|
756
|
+
if (listingCount >= 3) return 0; // Critical: listed on 3+ lists
|
|
757
|
+
if (listingCount === 2) return 20; // Severe: listed on 2 lists
|
|
758
|
+
return 40; // Serious: listed on 1 list
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// If recently delisted, some penalty still applies
|
|
762
|
+
if (metrics.blocklist.recentDelistings.length > 0) {
|
|
763
|
+
// Check how recent the delistings are
|
|
764
|
+
const now = new Date();
|
|
765
|
+
const mostRecent = metrics.blocklist.recentDelistings
|
|
766
|
+
.reduce((latest, delisting) =>
|
|
767
|
+
delisting.listedTo > latest ? delisting.listedTo : latest,
|
|
768
|
+
new Date(0));
|
|
769
|
+
|
|
770
|
+
const daysSinceDelisting = Math.floor(
|
|
771
|
+
(now.getTime() - mostRecent.getTime()) / (24 * 60 * 60 * 1000)
|
|
772
|
+
);
|
|
773
|
+
|
|
774
|
+
// Score improves as time passes since delisting
|
|
775
|
+
if (daysSinceDelisting < 7) return 60; // Delisted within last week
|
|
776
|
+
if (daysSinceDelisting < 30) return 80; // Delisted within last month
|
|
777
|
+
return 90; // Delisted over a month ago
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// Never listed
|
|
781
|
+
return 100;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Calculate trend metrics
|
|
786
|
+
* @param metrics Domain metrics to update
|
|
787
|
+
*/
|
|
788
|
+
private calculateTrends(metrics: IDomainReputationMetrics): void {
|
|
789
|
+
// Get dates in descending order
|
|
790
|
+
const dates = Object.keys(metrics.historical.reputationScores)
|
|
791
|
+
.sort((a, b) => b.localeCompare(a));
|
|
792
|
+
|
|
793
|
+
if (dates.length < 7) {
|
|
794
|
+
// Not enough data for trends
|
|
795
|
+
metrics.historical.trends = {
|
|
796
|
+
openRate: 0,
|
|
797
|
+
complaintRate: 0,
|
|
798
|
+
bounceRate: 0,
|
|
799
|
+
spamListings: 0
|
|
800
|
+
};
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// Calculate trends over past 7 days compared to previous 7 days
|
|
805
|
+
const current7Days = dates.slice(0, 7);
|
|
806
|
+
const previous7Days = dates.slice(7, 14);
|
|
807
|
+
|
|
808
|
+
if (previous7Days.length < 7) {
|
|
809
|
+
// Not enough historical data
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Calculate averages for the periods
|
|
814
|
+
const currentReputation = current7Days.reduce(
|
|
815
|
+
(sum, date) => sum + metrics.historical.reputationScores[date], 0
|
|
816
|
+
) / current7Days.length;
|
|
817
|
+
|
|
818
|
+
const previousReputation = previous7Days.reduce(
|
|
819
|
+
(sum, date) => sum + metrics.historical.reputationScores[date], 0
|
|
820
|
+
) / previous7Days.length;
|
|
821
|
+
|
|
822
|
+
// Calculate percent change
|
|
823
|
+
const reputationChange = ((currentReputation - previousReputation) / previousReputation) * 100;
|
|
824
|
+
|
|
825
|
+
// For now, use reputation change for all trends (in a real implementation
|
|
826
|
+
// we would calculate each metric's trend separately)
|
|
827
|
+
metrics.historical.trends = {
|
|
828
|
+
openRate: reputationChange,
|
|
829
|
+
complaintRate: -reputationChange, // Inverse for complaint rate (negative is good)
|
|
830
|
+
bounceRate: -reputationChange, // Inverse for bounce rate
|
|
831
|
+
spamListings: -reputationChange // Inverse for spam listings
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Check if metrics exceed alert thresholds
|
|
837
|
+
* @param metrics Domain metrics to check
|
|
838
|
+
*/
|
|
839
|
+
private checkAlertThresholds(metrics: IDomainReputationMetrics): void {
|
|
840
|
+
const thresholds = this.config.alertThresholds;
|
|
841
|
+
const today = new Date().toISOString().split('T')[0];
|
|
842
|
+
const todayScore = metrics.historical.reputationScores[today] || 0;
|
|
843
|
+
|
|
844
|
+
// Check reputation score
|
|
845
|
+
if (todayScore < thresholds.minReputationScore) {
|
|
846
|
+
this.sendAlert(metrics.domain, 'reputation_score', {
|
|
847
|
+
score: todayScore,
|
|
848
|
+
threshold: thresholds.minReputationScore
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// Check complaint rate
|
|
853
|
+
if (metrics.complaints.rate > thresholds.maxComplaintRate) {
|
|
854
|
+
this.sendAlert(metrics.domain, 'complaint_rate', {
|
|
855
|
+
rate: metrics.complaints.rate,
|
|
856
|
+
threshold: thresholds.maxComplaintRate
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// Check bounce rate
|
|
861
|
+
const bounceRate = (metrics.volume.hardBounces + metrics.volume.softBounces) /
|
|
862
|
+
Math.max(1, metrics.volume.sent) * 100;
|
|
863
|
+
|
|
864
|
+
if (bounceRate > thresholds.maxBounceRate) {
|
|
865
|
+
this.sendAlert(metrics.domain, 'bounce_rate', {
|
|
866
|
+
rate: bounceRate,
|
|
867
|
+
threshold: thresholds.maxBounceRate
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Check open rate
|
|
872
|
+
if (metrics.engagement.openRate < thresholds.minOpenRate) {
|
|
873
|
+
this.sendAlert(metrics.domain, 'open_rate', {
|
|
874
|
+
rate: metrics.engagement.openRate,
|
|
875
|
+
threshold: thresholds.minOpenRate
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// Check blocklist status
|
|
880
|
+
if (metrics.blocklist.listed) {
|
|
881
|
+
this.sendAlert(metrics.domain, 'blocklist', {
|
|
882
|
+
lists: metrics.blocklist.activeListings.map(l => l.list)
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* Send an alert for a reputation issue
|
|
889
|
+
* @param domain Domain with the issue
|
|
890
|
+
* @param alertType Type of alert
|
|
891
|
+
* @param data Alert data
|
|
892
|
+
*/
|
|
893
|
+
private sendAlert(domain: string, alertType: string, data: any): void {
|
|
894
|
+
logger.log('warn', `Reputation alert for ${domain}: ${alertType}`, data);
|
|
895
|
+
|
|
896
|
+
// In a real implementation, this would send alerts via email,
|
|
897
|
+
// notification systems, webhooks, etc.
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Record a send event for domain reputation tracking
|
|
902
|
+
* @param domain The domain sending the email
|
|
903
|
+
* @param event Event details
|
|
904
|
+
*/
|
|
905
|
+
public recordSendEvent(domain: string, event: {
|
|
906
|
+
type: 'sent' | 'delivered' | 'bounce' | 'complaint' | 'open' | 'click';
|
|
907
|
+
count?: number;
|
|
908
|
+
hardBounce?: boolean;
|
|
909
|
+
receivingDomain?: string;
|
|
910
|
+
}): void {
|
|
911
|
+
// Ensure we have metrics for this domain
|
|
912
|
+
if (!this.reputationData.has(domain)) {
|
|
913
|
+
this.initializeDomainData(domain);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
const metrics = this.reputationData.get(domain);
|
|
917
|
+
const count = event.count || 1;
|
|
918
|
+
const today = new Date().toISOString().split('T')[0];
|
|
919
|
+
|
|
920
|
+
// Update metrics based on event type
|
|
921
|
+
switch (event.type) {
|
|
922
|
+
case 'sent':
|
|
923
|
+
metrics.volume.sent += count;
|
|
924
|
+
// Update daily send volume
|
|
925
|
+
metrics.volume.dailySendVolume[today] =
|
|
926
|
+
(metrics.volume.dailySendVolume[today] || 0) + count;
|
|
927
|
+
break;
|
|
928
|
+
|
|
929
|
+
case 'delivered':
|
|
930
|
+
metrics.volume.delivered += count;
|
|
931
|
+
break;
|
|
932
|
+
|
|
933
|
+
case 'bounce':
|
|
934
|
+
if (event.hardBounce) {
|
|
935
|
+
metrics.volume.hardBounces += count;
|
|
936
|
+
} else {
|
|
937
|
+
metrics.volume.softBounces += count;
|
|
938
|
+
}
|
|
939
|
+
break;
|
|
940
|
+
|
|
941
|
+
case 'complaint':
|
|
942
|
+
metrics.complaints.total += count;
|
|
943
|
+
|
|
944
|
+
// Track by receiving domain
|
|
945
|
+
if (event.receivingDomain) {
|
|
946
|
+
const domainIndex = metrics.complaints.topDomains.findIndex(
|
|
947
|
+
d => d.domain === event.receivingDomain
|
|
948
|
+
);
|
|
949
|
+
|
|
950
|
+
if (domainIndex >= 0) {
|
|
951
|
+
metrics.complaints.topDomains[domainIndex].count += count;
|
|
952
|
+
metrics.complaints.topDomains[domainIndex].rate =
|
|
953
|
+
metrics.complaints.topDomains[domainIndex].count / Math.max(1, metrics.volume.sent);
|
|
954
|
+
} else {
|
|
955
|
+
metrics.complaints.topDomains.push({
|
|
956
|
+
domain: event.receivingDomain,
|
|
957
|
+
count,
|
|
958
|
+
rate: count / Math.max(1, metrics.volume.sent)
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// Sort by count
|
|
963
|
+
metrics.complaints.topDomains.sort((a, b) => b.count - a.count);
|
|
964
|
+
|
|
965
|
+
// Keep only top 10
|
|
966
|
+
if (metrics.complaints.topDomains.length > 10) {
|
|
967
|
+
metrics.complaints.topDomains = metrics.complaints.topDomains.slice(0, 10);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// Update overall complaint rate
|
|
972
|
+
metrics.complaints.rate =
|
|
973
|
+
metrics.complaints.total / Math.max(1, metrics.volume.sent);
|
|
974
|
+
break;
|
|
975
|
+
|
|
976
|
+
case 'open':
|
|
977
|
+
metrics.engagement.opens += count;
|
|
978
|
+
metrics.engagement.openRate =
|
|
979
|
+
metrics.engagement.opens / Math.max(1, metrics.volume.delivered);
|
|
980
|
+
break;
|
|
981
|
+
|
|
982
|
+
case 'click':
|
|
983
|
+
metrics.engagement.clicks += count;
|
|
984
|
+
metrics.engagement.clickRate =
|
|
985
|
+
metrics.engagement.clicks / Math.max(1, metrics.volume.delivered);
|
|
986
|
+
metrics.engagement.clickToOpenRate =
|
|
987
|
+
metrics.engagement.clicks / Math.max(1, metrics.engagement.opens);
|
|
988
|
+
break;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// Update last updated timestamp
|
|
992
|
+
metrics.lastUpdated = new Date();
|
|
993
|
+
|
|
994
|
+
// Save data periodically (not after every event to avoid excessive I/O)
|
|
995
|
+
// Skip in test environment
|
|
996
|
+
const isTestEnvironment = process.env.NODE_ENV === 'test' || !!process.env.JEST_WORKER_ID;
|
|
997
|
+
if (!isTestEnvironment && Math.random() < 0.01) { // ~1% chance to save on each event
|
|
998
|
+
this.saveReputationData().catch(error => {
|
|
999
|
+
logger.log('error', `Failed to save reputation data: ${error.message}`, {
|
|
1000
|
+
stack: error.stack
|
|
1001
|
+
});
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* Get reputation data for a domain
|
|
1008
|
+
* @param domain Domain to get data for
|
|
1009
|
+
* @returns Reputation data
|
|
1010
|
+
*/
|
|
1011
|
+
public getReputationData(domain: string): IDomainReputationMetrics | null {
|
|
1012
|
+
return this.reputationData.get(domain) || null;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Get summary reputation data for all domains
|
|
1017
|
+
* @returns Summary data for all domains
|
|
1018
|
+
*/
|
|
1019
|
+
public getReputationSummary(): Array<{
|
|
1020
|
+
domain: string;
|
|
1021
|
+
score: number;
|
|
1022
|
+
status: 'excellent' | 'good' | 'fair' | 'poor' | 'critical';
|
|
1023
|
+
listed: boolean;
|
|
1024
|
+
trend: number;
|
|
1025
|
+
}> {
|
|
1026
|
+
return Array.from(this.reputationData.entries())
|
|
1027
|
+
.map(([domain, metrics]) => {
|
|
1028
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1029
|
+
const score = metrics.historical.reputationScores[today] || 0;
|
|
1030
|
+
|
|
1031
|
+
// Determine status based on score
|
|
1032
|
+
let status: 'excellent' | 'good' | 'fair' | 'poor' | 'critical';
|
|
1033
|
+
if (score >= 90) status = 'excellent';
|
|
1034
|
+
else if (score >= 75) status = 'good';
|
|
1035
|
+
else if (score >= 60) status = 'fair';
|
|
1036
|
+
else if (score >= 40) status = 'poor';
|
|
1037
|
+
else status = 'critical';
|
|
1038
|
+
|
|
1039
|
+
return {
|
|
1040
|
+
domain,
|
|
1041
|
+
score,
|
|
1042
|
+
status,
|
|
1043
|
+
listed: metrics.blocklist.listed,
|
|
1044
|
+
trend: metrics.historical.trends.openRate // Use open rate trend as overall trend
|
|
1045
|
+
};
|
|
1046
|
+
})
|
|
1047
|
+
.sort((a, b) => b.score - a.score); // Sort by score descending
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Add a domain to monitor
|
|
1052
|
+
* @param domain Domain to monitor
|
|
1053
|
+
*/
|
|
1054
|
+
public addDomain(domain: string): void {
|
|
1055
|
+
if (this.config.domains.includes(domain)) {
|
|
1056
|
+
logger.log('info', `Domain ${domain} is already being monitored`);
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
this.config.domains.push(domain);
|
|
1061
|
+
this.initializeDomainData(domain);
|
|
1062
|
+
this.saveReputationData().catch(error => {
|
|
1063
|
+
logger.log('error', `Failed to save reputation data after adding domain: ${error.message}`, {
|
|
1064
|
+
stack: error.stack
|
|
1065
|
+
});
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
logger.log('info', `Added ${domain} to reputation monitoring`);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* Remove a domain from monitoring
|
|
1073
|
+
* @param domain Domain to remove
|
|
1074
|
+
*/
|
|
1075
|
+
public removeDomain(domain: string): void {
|
|
1076
|
+
const index = this.config.domains.indexOf(domain);
|
|
1077
|
+
if (index === -1) {
|
|
1078
|
+
logger.log('info', `Domain ${domain} is not being monitored`);
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
this.config.domains.splice(index, 1);
|
|
1083
|
+
this.reputationData.delete(domain);
|
|
1084
|
+
this.saveReputationData().catch(error => {
|
|
1085
|
+
logger.log('error', `Failed to save reputation data after removing domain: ${error.message}`, {
|
|
1086
|
+
stack: error.stack
|
|
1087
|
+
});
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
logger.log('info', `Removed ${domain} from reputation monitoring`);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* Load reputation data from storage
|
|
1095
|
+
*/
|
|
1096
|
+
private async loadReputationData(): Promise<void> {
|
|
1097
|
+
// Skip loading in test environment to prevent file system race conditions
|
|
1098
|
+
const isTestEnvironment = process.env.NODE_ENV === 'test' || !!process.env.JEST_WORKER_ID;
|
|
1099
|
+
if (isTestEnvironment) {
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
try {
|
|
1104
|
+
// Try to load from storage manager first
|
|
1105
|
+
if (this.storageManager) {
|
|
1106
|
+
try {
|
|
1107
|
+
const data = await this.storageManager.get('/email/reputation/domain-reputation.json');
|
|
1108
|
+
if (data) {
|
|
1109
|
+
const reputationEntries = JSON.parse(data);
|
|
1110
|
+
|
|
1111
|
+
for (const entry of reputationEntries) {
|
|
1112
|
+
// Restore Date objects
|
|
1113
|
+
entry.lastUpdated = new Date(entry.lastUpdated);
|
|
1114
|
+
|
|
1115
|
+
for (const listing of entry.blocklist.activeListings) {
|
|
1116
|
+
listing.listedSince = new Date(listing.listedSince);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
for (const delisting of entry.blocklist.recentDelistings) {
|
|
1120
|
+
delisting.listedFrom = new Date(delisting.listedFrom);
|
|
1121
|
+
delisting.listedTo = new Date(delisting.listedTo);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
this.reputationData.set(entry.domain, entry);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
logger.log('info', `Loaded reputation data for ${this.reputationData.size} domains from StorageManager`);
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
} catch (error) {
|
|
1131
|
+
// Fall through to filesystem migration check
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// Check if data exists in filesystem and migrate it to storage manager
|
|
1135
|
+
const reputationDir = plugins.path.join(paths.dataDir, 'reputation');
|
|
1136
|
+
const dataFile = plugins.path.join(reputationDir, 'domain_reputation.json');
|
|
1137
|
+
|
|
1138
|
+
if (plugins.fs.existsSync(dataFile)) {
|
|
1139
|
+
const data = plugins.fs.readFileSync(dataFile, 'utf8');
|
|
1140
|
+
const reputationEntries = JSON.parse(data);
|
|
1141
|
+
|
|
1142
|
+
for (const entry of reputationEntries) {
|
|
1143
|
+
// Restore Date objects
|
|
1144
|
+
entry.lastUpdated = new Date(entry.lastUpdated);
|
|
1145
|
+
|
|
1146
|
+
for (const listing of entry.blocklist.activeListings) {
|
|
1147
|
+
listing.listedSince = new Date(listing.listedSince);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
for (const delisting of entry.blocklist.recentDelistings) {
|
|
1151
|
+
delisting.listedFrom = new Date(delisting.listedFrom);
|
|
1152
|
+
delisting.listedTo = new Date(delisting.listedTo);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
this.reputationData.set(entry.domain, entry);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// Migrate to storage manager
|
|
1159
|
+
logger.log('info', `Migrating reputation data for ${this.reputationData.size} domains from filesystem to StorageManager`);
|
|
1160
|
+
await this.storageManager.set(
|
|
1161
|
+
'/email/reputation/domain-reputation.json',
|
|
1162
|
+
JSON.stringify(Array.from(this.reputationData.values()), null, 2)
|
|
1163
|
+
);
|
|
1164
|
+
|
|
1165
|
+
logger.log('info', `Loaded and migrated reputation data for ${this.reputationData.size} domains`);
|
|
1166
|
+
}
|
|
1167
|
+
} else {
|
|
1168
|
+
// No storage manager, use filesystem directly
|
|
1169
|
+
const reputationDir = plugins.path.join(paths.dataDir, 'reputation');
|
|
1170
|
+
plugins.smartfile.fs.ensureDirSync(reputationDir);
|
|
1171
|
+
|
|
1172
|
+
const dataFile = plugins.path.join(reputationDir, 'domain_reputation.json');
|
|
1173
|
+
|
|
1174
|
+
if (plugins.fs.existsSync(dataFile)) {
|
|
1175
|
+
const data = plugins.fs.readFileSync(dataFile, 'utf8');
|
|
1176
|
+
const reputationEntries = JSON.parse(data);
|
|
1177
|
+
|
|
1178
|
+
for (const entry of reputationEntries) {
|
|
1179
|
+
// Restore Date objects
|
|
1180
|
+
entry.lastUpdated = new Date(entry.lastUpdated);
|
|
1181
|
+
|
|
1182
|
+
for (const listing of entry.blocklist.activeListings) {
|
|
1183
|
+
listing.listedSince = new Date(listing.listedSince);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
for (const delisting of entry.blocklist.recentDelistings) {
|
|
1187
|
+
delisting.listedFrom = new Date(delisting.listedFrom);
|
|
1188
|
+
delisting.listedTo = new Date(delisting.listedTo);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
this.reputationData.set(entry.domain, entry);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
logger.log('info', `Loaded reputation data for ${this.reputationData.size} domains from filesystem`);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
} catch (error) {
|
|
1198
|
+
logger.log('error', `Failed to load reputation data: ${error.message}`, {
|
|
1199
|
+
stack: error.stack
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
/**
|
|
1205
|
+
* Save reputation data to storage
|
|
1206
|
+
*/
|
|
1207
|
+
private async saveReputationData(): Promise<void> {
|
|
1208
|
+
// Skip saving in test environment to prevent file system race conditions
|
|
1209
|
+
const isTestEnvironment = process.env.NODE_ENV === 'test' || !!process.env.JEST_WORKER_ID;
|
|
1210
|
+
if (isTestEnvironment) {
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
try {
|
|
1215
|
+
const reputationEntries = Array.from(this.reputationData.values());
|
|
1216
|
+
|
|
1217
|
+
// Save to storage manager if available
|
|
1218
|
+
if (this.storageManager) {
|
|
1219
|
+
await this.storageManager.set(
|
|
1220
|
+
'/email/reputation/domain-reputation.json',
|
|
1221
|
+
JSON.stringify(reputationEntries, null, 2)
|
|
1222
|
+
);
|
|
1223
|
+
logger.log('debug', `Saved reputation data for ${reputationEntries.length} domains to StorageManager`);
|
|
1224
|
+
} else {
|
|
1225
|
+
// No storage manager, use filesystem directly
|
|
1226
|
+
const reputationDir = plugins.path.join(paths.dataDir, 'reputation');
|
|
1227
|
+
plugins.smartfile.fs.ensureDirSync(reputationDir);
|
|
1228
|
+
|
|
1229
|
+
const dataFile = plugins.path.join(reputationDir, 'domain_reputation.json');
|
|
1230
|
+
|
|
1231
|
+
plugins.smartfile.memory.toFsSync(
|
|
1232
|
+
JSON.stringify(reputationEntries, null, 2),
|
|
1233
|
+
dataFile
|
|
1234
|
+
);
|
|
1235
|
+
|
|
1236
|
+
logger.log('debug', `Saved reputation data for ${reputationEntries.length} domains to filesystem`);
|
|
1237
|
+
}
|
|
1238
|
+
} catch (error) {
|
|
1239
|
+
logger.log('error', `Failed to save reputation data: ${error.message}`, {
|
|
1240
|
+
stack: error.stack
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|