Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f601859f8b | |||
| eb2643de93 | |||
| 595634fb0f | |||
| cee8a51081 | |||
| f1c5546186 | |||
| 5220ee0857 | |||
| fc2e6d44f4 | |||
| 15a45089aa | |||
| b82468ab1e | |||
| ffe294643c | |||
| f1071faf3d | |||
| 6b082cee8f | |||
| 9185242530 | |||
| 8293663619 | |||
| 199b9b79d2 | |||
| 91b49182bb | |||
| 564b65c7f2 | |||
| 8bd8c295b0 | |||
| 237dba3bab | |||
| bcde137332 | |||
| 14be3cdb9a | |||
| f262f602a0 | |||
| 0ac598818f | |||
| c7b3206140 | |||
| 17f5661636 | |||
| 6523c55516 | |||
| 9cd15342e0 | |||
| 0018b19164 | |||
| 7ecdd9f1e4 | |||
| 1698df3a53 | |||
| d7f37afc30 | |||
| 27b6bb779e |
@@ -6,7 +6,7 @@ Pre-compiled binaries for multiple platforms.
|
|||||||
|
|
||||||
#### Option 1: Via npm (recommended)
|
#### Option 1: Via npm (recommended)
|
||||||
```bash
|
```bash
|
||||||
npm install -g @serve.zone/mailer
|
npm install -g @push.rocks/smartmta
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Option 2: Direct binary download
|
#### Option 2: Direct binary download
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ jobs:
|
|||||||
mailer --version || echo "Note: Binary execution may fail in CI environment"
|
mailer --version || echo "Note: Binary execution may fail in CI environment"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Checking installed files:"
|
echo "Checking installed files:"
|
||||||
npm ls -g @serve.zone/mailer || true
|
npm ls -g @push.rocks/smartmta || true
|
||||||
|
|
||||||
- name: Publish to npm
|
- name: Publish to npm
|
||||||
env:
|
env:
|
||||||
@@ -93,10 +93,10 @@ jobs:
|
|||||||
echo "Publishing to npm registry..."
|
echo "Publishing to npm registry..."
|
||||||
npm publish --access public
|
npm publish --access public
|
||||||
echo ""
|
echo ""
|
||||||
echo "✅ Successfully published @serve.zone/mailer to npm!"
|
echo "✅ Successfully published @push.rocks/smartmta to npm!"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Package info:"
|
echo "Package info:"
|
||||||
npm view @serve.zone/mailer
|
npm view @push.rocks/smartmta
|
||||||
|
|
||||||
- name: Verify npm package
|
- name: Verify npm package
|
||||||
run: |
|
run: |
|
||||||
@@ -104,10 +104,10 @@ jobs:
|
|||||||
sleep 30
|
sleep 30
|
||||||
echo ""
|
echo ""
|
||||||
echo "Verifying published package..."
|
echo "Verifying published package..."
|
||||||
npm view @serve.zone/mailer
|
npm view @push.rocks/smartmta
|
||||||
echo ""
|
echo ""
|
||||||
echo "Testing installation from npm:"
|
echo "Testing installation from npm:"
|
||||||
npm install -g @serve.zone/mailer
|
npm install -g @push.rocks/smartmta
|
||||||
echo ""
|
echo ""
|
||||||
echo "Package installed successfully!"
|
echo "Package installed successfully!"
|
||||||
which mailer || echo "Binary location check skipped"
|
which mailer || echo "Binary location check skipped"
|
||||||
@@ -118,12 +118,12 @@ jobs:
|
|||||||
echo " npm Publish Complete!"
|
echo " npm Publish Complete!"
|
||||||
echo "================================================"
|
echo "================================================"
|
||||||
echo ""
|
echo ""
|
||||||
echo "✅ Package: @serve.zone/mailer"
|
echo "✅ Package: @push.rocks/smartmta"
|
||||||
echo "✅ Version: ${{ steps.version.outputs.version }}"
|
echo "✅ Version: ${{ steps.version.outputs.version }}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Installation:"
|
echo "Installation:"
|
||||||
echo " npm install -g @serve.zone/mailer"
|
echo " npm install -g @push.rocks/smartmta"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Registry:"
|
echo "Registry:"
|
||||||
echo " https://www.npmjs.com/package/@serve.zone/mailer"
|
echo " https://www.npmjs.com/package/@push.rocks/smartmta"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,8 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
.nogit/
|
.nogit/
|
||||||
dist/
|
dist/
|
||||||
|
dist_rust/
|
||||||
|
rust/target/
|
||||||
deno.lock
|
deno.lock
|
||||||
*.log
|
*.log
|
||||||
.env
|
.env
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MAILER npm wrapper
|
|
||||||
* This script executes the appropriate pre-compiled binary based on the current platform
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { spawn } from 'child_process';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import { dirname, join } from 'path';
|
|
||||||
import { existsSync } from 'fs';
|
|
||||||
import { platform, arch } from 'os';
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = dirname(__filename);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the binary name for the current platform
|
|
||||||
*/
|
|
||||||
function getBinaryName() {
|
|
||||||
const plat = platform();
|
|
||||||
const architecture = arch();
|
|
||||||
|
|
||||||
// Map Node's platform/arch to our binary naming
|
|
||||||
const platformMap = {
|
|
||||||
'darwin': 'macos',
|
|
||||||
'linux': 'linux',
|
|
||||||
'win32': 'windows'
|
|
||||||
};
|
|
||||||
|
|
||||||
const archMap = {
|
|
||||||
'x64': 'x64',
|
|
||||||
'arm64': 'arm64'
|
|
||||||
};
|
|
||||||
|
|
||||||
const mappedPlatform = platformMap[plat];
|
|
||||||
const mappedArch = archMap[architecture];
|
|
||||||
|
|
||||||
if (!mappedPlatform || !mappedArch) {
|
|
||||||
console.error(`Error: Unsupported platform/architecture: ${plat}/${architecture}`);
|
|
||||||
console.error('Supported platforms: Linux, macOS, Windows');
|
|
||||||
console.error('Supported architectures: x64, arm64');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct binary name
|
|
||||||
let binaryName = `mailer-${mappedPlatform}-${mappedArch}`;
|
|
||||||
if (plat === 'win32') {
|
|
||||||
binaryName += '.exe';
|
|
||||||
}
|
|
||||||
|
|
||||||
return binaryName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the binary
|
|
||||||
*/
|
|
||||||
function executeBinary() {
|
|
||||||
const binaryName = getBinaryName();
|
|
||||||
const binaryPath = join(__dirname, '..', 'dist', 'binaries', binaryName);
|
|
||||||
|
|
||||||
// Check if binary exists
|
|
||||||
if (!existsSync(binaryPath)) {
|
|
||||||
console.error(`Error: Binary not found at ${binaryPath}`);
|
|
||||||
console.error('This might happen if:');
|
|
||||||
console.error('1. The postinstall script failed to run');
|
|
||||||
console.error('2. The platform is not supported');
|
|
||||||
console.error('3. The package was not installed correctly');
|
|
||||||
console.error('');
|
|
||||||
console.error('Try reinstalling the package:');
|
|
||||||
console.error(' npm uninstall -g @serve.zone/mailer');
|
|
||||||
console.error(' npm install -g @serve.zone/mailer');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spawn the binary with all arguments passed through
|
|
||||||
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
||||||
stdio: 'inherit',
|
|
||||||
shell: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle child process events
|
|
||||||
child.on('error', (err) => {
|
|
||||||
console.error(`Error executing mailer: ${err.message}`);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
child.on('exit', (code, signal) => {
|
|
||||||
if (signal) {
|
|
||||||
process.kill(process.pid, signal);
|
|
||||||
} else {
|
|
||||||
process.exit(code || 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Forward signals to child process
|
|
||||||
const signals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
|
|
||||||
signals.forEach(signal => {
|
|
||||||
process.on(signal, () => {
|
|
||||||
if (!child.killed) {
|
|
||||||
child.kill(signal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute
|
|
||||||
executeBinary();
|
|
||||||
90
changelog.md
90
changelog.md
@@ -1,5 +1,95 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-02-10 - 2.3.0 - feat(mailer-smtp)
|
||||||
|
add in-process security pipeline for SMTP delivery (DKIM/SPF/DMARC, content scanning, IP reputation)
|
||||||
|
|
||||||
|
- Integrate mailer_security verification (DKIM/SPF/DMARC) and IP reputation checks into the Rust SMTP server; run concurrently and wrapped with a 30s timeout.
|
||||||
|
- Add MIME parsing using mailparse and an extract_mime_parts helper to extract subject, text/html bodies and attachment filenames for content scanning.
|
||||||
|
- Wire MessageAuthenticator and TokioResolver into server and connection startup; pass them into the delivery pipeline and connection handlers.
|
||||||
|
- Run content scanning (mailer_security::content_scanner), combine results (dkim/spf/dmarc, contentScan, ipReputation) into a JSON object and attach as security_results on EmailReceived events.
|
||||||
|
- Update Rust crates (Cargo.toml/Cargo.lock) to include mailparse and resolver usage and add serde::Deserialize where required; add unit tests for MIME extraction.
|
||||||
|
- Remove the TypeScript SMTP server implementation and many TS tests; replace test helper (server.loader.ts) with a stub that points tests to use the Rust SMTP server and provide small utilities (getAvailablePort/isPortFree).
|
||||||
|
|
||||||
|
## 2026-02-10 - 2.2.1 - fix(readme)
|
||||||
|
Clarify Rust-powered architecture and mandatory Rust bridge; expand README with Rust workspace details and project structure updates
|
||||||
|
|
||||||
|
- Emphasizes that the SMTP server is Rust-powered (high-performance) and not a nodemailer-based TS server.
|
||||||
|
- Documents that the Rust binary (mailer-bin) is required — if unavailable UnifiedEmailServer.start() will throw an error.
|
||||||
|
- Adds installation/build note: run `pnpm build` to compile the Rust binary.
|
||||||
|
- Adds a new Rust Acceleration Layer section listing workspace crates and responsibilities (mailer-core, mailer-security, mailer-smtp, mailer-bin, mailer-napi).
|
||||||
|
- Updates project structure: marks legacy TS SMTP server as fallback/legacy, adds dist_rust output, and clarifies which operations run in Rust vs TypeScript.
|
||||||
|
|
||||||
|
## 2026-02-10 - 2.2.0 - feat(mailer-smtp)
|
||||||
|
implement in-process SMTP server and management IPC integration
|
||||||
|
|
||||||
|
- Add full SMTP protocol engine crate (mailer-smtp) with modules: command, config, connection, data, response, session, state, validation, rate_limiter and server
|
||||||
|
- Introduce SmtpServerConfig, DataAccumulator (DATA phase handling, dot-unstuffing, size limits) and SmtpResponse builder with EHLO capability construction
|
||||||
|
- Add in-process RateLimiter using DashMap and runtime-configurable RateLimitConfig
|
||||||
|
- Add TCP/TLS server start/stop API (start_server) with TlsAcceptor building from PEM and SmtpServerHandle for shutdown and status
|
||||||
|
- Integrate callback registry and oneshot-based correlation callbacks in mailer-bin management mode for email processing/auth results and JSON IPC parsing for SmtpServerConfig
|
||||||
|
- TypeScript bridge and routing updates: new IPC commands/types (startSmtpServer, stopSmtpServer, emailProcessingResult, authResult, configureRateLimits) and event handlers (emailReceived, authRequest)
|
||||||
|
- Update Cargo manifests and lockfile to add dependencies (dashmap, regex, rustls, rustls-pemfile, rustls-pki-types, uuid, serde_json, base64, etc.)
|
||||||
|
- Add comprehensive unit tests for new modules (config, data, response, session, state, rate_limiter, validation)
|
||||||
|
|
||||||
|
## 2026-02-10 - 2.1.0 - feat(security)
|
||||||
|
migrate content scanning and bounce detection to Rust security bridge; add scanContent IPC command and Rust content scanner with tests; update TS RustSecurityBridge and callers, and adjust CI package references
|
||||||
|
|
||||||
|
- Add Rust content scanner implementation (rust/crates/mailer-security/src/content_scanner.rs) with pattern-based detection and unit tests (~515 lines)
|
||||||
|
- Expose new IPC command 'scanContent' in mailer-bin and marshal results via JSON for the RustSecurityBridge
|
||||||
|
- Update TypeScript RustSecurityBridge with scanContent typing and method, and replace local JS detection logic (bounce/content) to call Rust bridge
|
||||||
|
- Update tests to start/stop the RustSecurityBridge and rely on Rust-based detection (test updates in test.bouncemanager.ts and test.contentscanner.ts)
|
||||||
|
- Update CI workflow messages and package references from @serve.zone/mailer to @push.rocks/smartmta
|
||||||
|
- Add regex dependency to rust mailer-security workspace (Cargo.toml / Cargo.lock updated)
|
||||||
|
|
||||||
|
## 2026-02-10 - 2.0.1 - fix(docs/readme)
|
||||||
|
update README: clarify APIs, document RustSecurityBridge, update examples and architecture diagram
|
||||||
|
|
||||||
|
- Documented RustSecurityBridge: startup/shutdown, automatic delegation, compound verifyEmail API, and individual operations
|
||||||
|
- Clarified verification APIs: SpfVerifier.verify() and DmarcVerifier.verify() examples now take an Email object as the first argument
|
||||||
|
- Updated example method names/usages: scanEmail, createEmail, evaluateRoutes, checkMessageLimit, isEmailSuppressed, DKIMCreator rotation and output formatting
|
||||||
|
- Reformatted architecture diagram and added Rust Security Bridge and expanded Rust Acceleration details
|
||||||
|
- Rate limiter example updated: renamed/standardized config keys (maxMessagesPerMinute, domains) and added additional limits (maxRecipientsPerMessage, maxConnectionsPerIP, etc.)
|
||||||
|
- DNS management documentation reorganized: UnifiedEmailServer now handles DNS record setup automatically; DNSManager usage clarified for standalone checks
|
||||||
|
- Minor wording/formatting tweaks throughout README (arrow styles, headings, test counts)
|
||||||
|
|
||||||
|
## 2026-02-10 - 2.0.0 - BREAKING CHANGE(smartmta)
|
||||||
|
Rebrand package to @push.rocks/smartmta, add consolidated email security verification and IPC handler
|
||||||
|
|
||||||
|
- Package renamed from @serve.zone/mailer to @push.rocks/smartmta (package.json, commitinfo, README and homepage/bugs/repository URLs updated) — breaking for consumers who import by package name.
|
||||||
|
- Added new compound email security API verify_email_security that runs DKIM, SPF and DMARC in a single call (rust/crates/mailer-security/src/verify.rs) and exposed it from the mailer-security crate.
|
||||||
|
- Added IPC handler "verifyEmail" in mailer-bin to call the new verify_email_security function from the Rust side.
|
||||||
|
- Refactored DKIM and SPF code to convert mail-auth outputs to serializable results (dkim_outputs_to_results and SpfResult::from_output) and wired them into the combined verifier.
|
||||||
|
- Updated TypeScript plugin exports and dependencies: added @push.rocks/smartrust and exported smartrust in ts/plugins.ts.
|
||||||
|
- Large README overhaul to reflect rebranding, install instructions, architecture and legal/company info.
|
||||||
|
|
||||||
|
## 2026-02-10 - 1.3.1 - fix(deps)
|
||||||
|
add workspace dependency entries for multiple crates across mailer-bin, mailer-core, and mailer-security
|
||||||
|
|
||||||
|
- rust/crates/mailer-bin/Cargo.toml: add clap.workspace = true
|
||||||
|
- rust/crates/mailer-core/Cargo.toml: add regex.workspace = true, base64.workspace = true, uuid.workspace = true
|
||||||
|
- rust/crates/mailer-security/Cargo.toml: add serde_json.workspace = true, tokio.workspace = true, hickory-resolver.workspace = true, ipnet.workspace = true, rustls-pki-types.workspace = true, psl.workspace = true
|
||||||
|
- Purpose: align and enable workspace-managed dependencies for the affected crates
|
||||||
|
|
||||||
|
## 2026-02-10 - 1.3.0 - feat(mail/delivery)
|
||||||
|
add error-count based blocking to rate limiter; improve test SMTP server port selection; add tsbuild scripts and devDependency; remove stale backup file
|
||||||
|
|
||||||
|
- Add TokenBucket error tracking (errors, firstErrorTime) and initialize fields for global and per-key buckets
|
||||||
|
- Introduce RateLimiter.recordError(key, window, threshold) to track errors and decide blocking when threshold exceeded
|
||||||
|
- Update test SMTP server loader to dynamically find a free port using smartnetwork and add a recordError stub to the mock server
|
||||||
|
- Add build and check scripts to package.json and add @git.zone/tsbuild to devDependencies
|
||||||
|
- Remove a large backup file (classes.emailsendjob.ts.backup) from the repo; minor whitespace and logging cleanups in SMTP server code
|
||||||
|
|
||||||
|
## 2025-10-24 - 1.2.1 - fix(mail/delivery)
|
||||||
|
Centralize runtime/plugin imports and switch modules to use plugins exports; unify EventEmitter usage; update Deno dependencies and small path/server refactors
|
||||||
|
|
||||||
|
- Centralized Node and third-party imports in ts/plugins.ts and re-exported commonly used utilities (net, tls, dns, fs, smartfile, smartdns, smartmail, mailauth, uuid, ip, LRUCache, etc).
|
||||||
|
- Replaced direct EventEmitter / Node built-in imports with plugins.EventEmitter across delivery, smtpclient, routing and the unified email server to standardize runtime integration.
|
||||||
|
- Updated deno.json dependency map: added @push.rocks/smartfile, @push.rocks/smartdns, @tsclass/tsclass and ip; reordered lru-cache entry.
|
||||||
|
- Stopped exporting ./dns/index.ts from ts/index.ts (DNS is available via mail/routing) to avoid duplicate exports.
|
||||||
|
- Added keysDir alias and dnsRecordsDir in ts/paths.ts and small path-related fixes.
|
||||||
|
- Added a placeholder SmtpServer and other minor delivery/smtpserver refactors and sanitizations.
|
||||||
|
- Added a local .claude/settings.local.json for development permissions (local-only configuration).
|
||||||
|
|
||||||
## 2025-10-24 - 1.2.0 - feat(plugins)
|
## 2025-10-24 - 1.2.0 - feat(plugins)
|
||||||
Add smartmail, mailauth and uuid to Deno dependencies and export them from plugins; include local dev permissions file
|
Add smartmail, mailauth and uuid to Deno dependencies and export them from plugins; include local dev permissions file
|
||||||
|
|
||||||
|
|||||||
49
deno.json
49
deno.json
@@ -1,49 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@serve.zone/mailer",
|
|
||||||
"version": "1.2.0",
|
|
||||||
"exports": "./mod.ts",
|
|
||||||
"nodeModulesDir": "auto",
|
|
||||||
"tasks": {
|
|
||||||
"dev": "deno run --allow-all mod.ts",
|
|
||||||
"compile": "deno task compile:all",
|
|
||||||
"compile:all": "bash scripts/compile-all.sh",
|
|
||||||
"test": "deno test --allow-all test/",
|
|
||||||
"test:watch": "deno test --allow-all --watch test/",
|
|
||||||
"check": "deno check mod.ts",
|
|
||||||
"fmt": "deno fmt",
|
|
||||||
"lint": "deno lint"
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"rules": {
|
|
||||||
"tags": [
|
|
||||||
"recommended"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fmt": {
|
|
||||||
"useTabs": false,
|
|
||||||
"lineWidth": 100,
|
|
||||||
"indentWidth": 2,
|
|
||||||
"semiColons": true,
|
|
||||||
"singleQuote": true
|
|
||||||
},
|
|
||||||
"compilerOptions": {
|
|
||||||
"lib": [
|
|
||||||
"deno.window"
|
|
||||||
],
|
|
||||||
"strict": true
|
|
||||||
},
|
|
||||||
"imports": {
|
|
||||||
"@std/cli": "jsr:@std/cli@^1.0.0",
|
|
||||||
"@std/fmt": "jsr:@std/fmt@^1.0.0",
|
|
||||||
"@std/path": "jsr:@std/path@^1.0.0",
|
|
||||||
"@std/http": "jsr:@std/http@^1.0.0",
|
|
||||||
"@std/crypto": "jsr:@std/crypto@^1.0.0",
|
|
||||||
"@std/assert": "jsr:@std/assert@^1.0.0",
|
|
||||||
"@apiclient.xyz/cloudflare": "npm:@apiclient.xyz/cloudflare@latest",
|
|
||||||
"lru-cache": "npm:lru-cache@^11.0.0",
|
|
||||||
"@push.rocks/smartmail": "npm:@push.rocks/smartmail@^2.0.0",
|
|
||||||
"mailauth": "npm:mailauth@^4.0.0",
|
|
||||||
"uuid": "npm:uuid@^9.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
8
dist_ts/00_commitinfo_data.d.ts
vendored
Normal file
8
dist_ts/00_commitinfo_data.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* autocreated commitinfo by @push.rocks/commitinfo
|
||||||
|
*/
|
||||||
|
export declare const commitinfo: {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
9
dist_ts/00_commitinfo_data.js
Normal file
9
dist_ts/00_commitinfo_data.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* autocreated commitinfo by @push.rocks/commitinfo
|
||||||
|
*/
|
||||||
|
export const commitinfo = {
|
||||||
|
name: '@push.rocks/smartmta',
|
||||||
|
version: '2.2.1',
|
||||||
|
description: 'A high-performance, enterprise-grade Mail Transfer Agent (MTA) built from scratch in TypeScript with Rust acceleration.'
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxzQkFBc0I7SUFDNUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHlIQUF5SDtDQUN2SSxDQUFBIn0=
|
||||||
41
dist_ts/errors/index.d.ts
vendored
Normal file
41
dist_ts/errors/index.d.ts
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* MTA error classes for SMTP client operations
|
||||||
|
*/
|
||||||
|
export declare class MtaConnectionError extends Error {
|
||||||
|
code: string;
|
||||||
|
details?: any;
|
||||||
|
constructor(message: string, detailsOrCode?: any);
|
||||||
|
static timeout(host: string, port: number, timeoutMs?: number): MtaConnectionError;
|
||||||
|
static refused(host: string, port: number): MtaConnectionError;
|
||||||
|
static dnsError(host: string, err?: any): MtaConnectionError;
|
||||||
|
}
|
||||||
|
export declare class MtaAuthenticationError extends Error {
|
||||||
|
code: string;
|
||||||
|
details?: any;
|
||||||
|
constructor(message: string, detailsOrCode?: any);
|
||||||
|
static invalidCredentials(host?: string, user?: string): MtaAuthenticationError;
|
||||||
|
}
|
||||||
|
export declare class MtaDeliveryError extends Error {
|
||||||
|
code: string;
|
||||||
|
responseCode?: number;
|
||||||
|
details?: any;
|
||||||
|
constructor(message: string, detailsOrCode?: any, responseCode?: number);
|
||||||
|
static temporary(message: string, ...args: any[]): MtaDeliveryError;
|
||||||
|
static permanent(message: string, ...args: any[]): MtaDeliveryError;
|
||||||
|
}
|
||||||
|
export declare class MtaConfigurationError extends Error {
|
||||||
|
code: string;
|
||||||
|
details?: any;
|
||||||
|
constructor(message: string, detailsOrCode?: any);
|
||||||
|
}
|
||||||
|
export declare class MtaTimeoutError extends Error {
|
||||||
|
code: string;
|
||||||
|
details?: any;
|
||||||
|
constructor(message: string, detailsOrCode?: any);
|
||||||
|
static commandTimeout(command: string, hostOrTimeout?: any, timeoutMs?: number): MtaTimeoutError;
|
||||||
|
}
|
||||||
|
export declare class MtaProtocolError extends Error {
|
||||||
|
code: string;
|
||||||
|
details?: any;
|
||||||
|
constructor(message: string, detailsOrCode?: any);
|
||||||
|
}
|
||||||
120
dist_ts/errors/index.js
Normal file
120
dist_ts/errors/index.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/**
|
||||||
|
* MTA error classes for SMTP client operations
|
||||||
|
*/
|
||||||
|
export class MtaConnectionError extends Error {
|
||||||
|
code;
|
||||||
|
details;
|
||||||
|
constructor(message, detailsOrCode) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'MtaConnectionError';
|
||||||
|
if (typeof detailsOrCode === 'string') {
|
||||||
|
this.code = detailsOrCode;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.code = 'CONNECTION_ERROR';
|
||||||
|
this.details = detailsOrCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static timeout(host, port, timeoutMs) {
|
||||||
|
return new MtaConnectionError(`Connection to ${host}:${port} timed out${timeoutMs ? ` after ${timeoutMs}ms` : ''}`, 'TIMEOUT');
|
||||||
|
}
|
||||||
|
static refused(host, port) {
|
||||||
|
return new MtaConnectionError(`Connection to ${host}:${port} refused`, 'REFUSED');
|
||||||
|
}
|
||||||
|
static dnsError(host, err) {
|
||||||
|
const errMsg = typeof err === 'string' ? err : err?.message || '';
|
||||||
|
return new MtaConnectionError(`DNS resolution failed for ${host}${errMsg ? `: ${errMsg}` : ''}`, 'DNS_ERROR');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class MtaAuthenticationError extends Error {
|
||||||
|
code;
|
||||||
|
details;
|
||||||
|
constructor(message, detailsOrCode) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'MtaAuthenticationError';
|
||||||
|
if (typeof detailsOrCode === 'string') {
|
||||||
|
this.code = detailsOrCode;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.code = 'AUTH_ERROR';
|
||||||
|
this.details = detailsOrCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static invalidCredentials(host, user) {
|
||||||
|
const detail = host && user ? `${user}@${host}` : host || user || '';
|
||||||
|
return new MtaAuthenticationError(`Authentication failed${detail ? `: ${detail}` : ''}`, 'INVALID_CREDENTIALS');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class MtaDeliveryError extends Error {
|
||||||
|
code;
|
||||||
|
responseCode;
|
||||||
|
details;
|
||||||
|
constructor(message, detailsOrCode, responseCode) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'MtaDeliveryError';
|
||||||
|
if (typeof detailsOrCode === 'string') {
|
||||||
|
this.code = detailsOrCode;
|
||||||
|
this.responseCode = responseCode;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.code = 'DELIVERY_ERROR';
|
||||||
|
this.details = detailsOrCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static temporary(message, ...args) {
|
||||||
|
return new MtaDeliveryError(message, 'TEMPORARY');
|
||||||
|
}
|
||||||
|
static permanent(message, ...args) {
|
||||||
|
return new MtaDeliveryError(message, 'PERMANENT');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class MtaConfigurationError extends Error {
|
||||||
|
code;
|
||||||
|
details;
|
||||||
|
constructor(message, detailsOrCode) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'MtaConfigurationError';
|
||||||
|
if (typeof detailsOrCode === 'string') {
|
||||||
|
this.code = detailsOrCode;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.code = 'CONFIG_ERROR';
|
||||||
|
this.details = detailsOrCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class MtaTimeoutError extends Error {
|
||||||
|
code;
|
||||||
|
details;
|
||||||
|
constructor(message, detailsOrCode) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'MtaTimeoutError';
|
||||||
|
if (typeof detailsOrCode === 'string') {
|
||||||
|
this.code = detailsOrCode;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.code = 'TIMEOUT';
|
||||||
|
this.details = detailsOrCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static commandTimeout(command, hostOrTimeout, timeoutMs) {
|
||||||
|
const timeout = typeof hostOrTimeout === 'number' ? hostOrTimeout : timeoutMs;
|
||||||
|
return new MtaTimeoutError(`Command '${command}' timed out${timeout ? ` after ${timeout}ms` : ''}`, 'COMMAND_TIMEOUT');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class MtaProtocolError extends Error {
|
||||||
|
code;
|
||||||
|
details;
|
||||||
|
constructor(message, detailsOrCode) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'MtaProtocolError';
|
||||||
|
if (typeof detailsOrCode === 'string') {
|
||||||
|
this.code = detailsOrCode;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.code = 'PROTOCOL_ERROR';
|
||||||
|
this.details = detailsOrCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9lcnJvcnMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxNQUFNLE9BQU8sa0JBQW1CLFNBQVEsS0FBSztJQUNwQyxJQUFJLENBQVM7SUFDYixPQUFPLENBQU87SUFDckIsWUFBWSxPQUFlLEVBQUUsYUFBbUI7UUFDOUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2YsSUFBSSxDQUFDLElBQUksR0FBRyxvQkFBb0IsQ0FBQztRQUNqQyxJQUFJLE9BQU8sYUFBYSxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ3RDLElBQUksQ0FBQyxJQUFJLEdBQUcsYUFBYSxDQUFDO1FBQzVCLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLElBQUksR0FBRyxrQkFBa0IsQ0FBQztZQUMvQixJQUFJLENBQUMsT0FBTyxHQUFHLGFBQWEsQ0FBQztRQUMvQixDQUFDO0lBQ0gsQ0FBQztJQUNELE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBWSxFQUFFLElBQVksRUFBRSxTQUFrQjtRQUMzRCxPQUFPLElBQUksa0JBQWtCLENBQUMsaUJBQWlCLElBQUksSUFBSSxJQUFJLGFBQWEsU0FBUyxDQUFDLENBQUMsQ0FBQyxVQUFVLFNBQVMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxTQUFTLENBQUMsQ0FBQztJQUNqSSxDQUFDO0lBQ0QsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFZLEVBQUUsSUFBWTtRQUN2QyxPQUFPLElBQUksa0JBQWtCLENBQUMsaUJBQWlCLElBQUksSUFBSSxJQUFJLFVBQVUsRUFBRSxTQUFTLENBQUMsQ0FBQztJQUNwRixDQUFDO0lBQ0QsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFZLEVBQUUsR0FBUztRQUNyQyxNQUFNLE1BQU0sR0FBRyxPQUFPLEdBQUcsS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxD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
|
||||||
17
dist_ts/logger.d.ts
vendored
Normal file
17
dist_ts/logger.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
declare class StandardLogger {
|
||||||
|
private defaultContext;
|
||||||
|
private correlationId;
|
||||||
|
constructor();
|
||||||
|
log(level: 'error' | 'warn' | 'info' | 'success' | 'debug', message: string, context?: Record<string, any>): void;
|
||||||
|
error(message: string, context?: Record<string, any>): void;
|
||||||
|
warn(message: string, context?: Record<string, any>): void;
|
||||||
|
info(message: string, context?: Record<string, any>): void;
|
||||||
|
success(message: string, context?: Record<string, any>): void;
|
||||||
|
debug(message: string, context?: Record<string, any>): void;
|
||||||
|
setContext(context: Record<string, any>, overwrite?: boolean): void;
|
||||||
|
setCorrelationId(id?: string | null): string;
|
||||||
|
getCorrelationId(): string | null;
|
||||||
|
clearCorrelationId(): void;
|
||||||
|
}
|
||||||
|
export declare const logger: StandardLogger;
|
||||||
|
export {};
|
||||||
76
dist_ts/logger.js
Normal file
76
dist_ts/logger.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import * as plugins from './plugins.js';
|
||||||
|
import { randomUUID } from 'node:crypto';
|
||||||
|
// Map NODE_ENV to valid TEnvironment
|
||||||
|
const nodeEnv = process.env.NODE_ENV || 'production';
|
||||||
|
const envMap = {
|
||||||
|
'development': 'local',
|
||||||
|
'test': 'test',
|
||||||
|
'staging': 'staging',
|
||||||
|
'production': 'production'
|
||||||
|
};
|
||||||
|
// Default Smartlog instance
|
||||||
|
const baseLogger = new plugins.smartlog.Smartlog({
|
||||||
|
logContext: {
|
||||||
|
environment: envMap[nodeEnv] || 'production',
|
||||||
|
runtime: 'node',
|
||||||
|
zone: 'serve.zone',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Extended logger compatible with the original enhanced logger API
|
||||||
|
class StandardLogger {
|
||||||
|
defaultContext = {};
|
||||||
|
correlationId = null;
|
||||||
|
constructor() { }
|
||||||
|
// Log methods
|
||||||
|
log(level, message, context = {}) {
|
||||||
|
const combinedContext = {
|
||||||
|
...this.defaultContext,
|
||||||
|
...context
|
||||||
|
};
|
||||||
|
if (this.correlationId) {
|
||||||
|
combinedContext.correlation_id = this.correlationId;
|
||||||
|
}
|
||||||
|
baseLogger.log(level, message, combinedContext);
|
||||||
|
}
|
||||||
|
error(message, context = {}) {
|
||||||
|
this.log('error', message, context);
|
||||||
|
}
|
||||||
|
warn(message, context = {}) {
|
||||||
|
this.log('warn', message, context);
|
||||||
|
}
|
||||||
|
info(message, context = {}) {
|
||||||
|
this.log('info', message, context);
|
||||||
|
}
|
||||||
|
success(message, context = {}) {
|
||||||
|
this.log('success', message, context);
|
||||||
|
}
|
||||||
|
debug(message, context = {}) {
|
||||||
|
this.log('debug', message, context);
|
||||||
|
}
|
||||||
|
// Context management
|
||||||
|
setContext(context, overwrite = false) {
|
||||||
|
if (overwrite) {
|
||||||
|
this.defaultContext = context;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.defaultContext = {
|
||||||
|
...this.defaultContext,
|
||||||
|
...context
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Correlation ID management
|
||||||
|
setCorrelationId(id = null) {
|
||||||
|
this.correlationId = id || randomUUID();
|
||||||
|
return this.correlationId;
|
||||||
|
}
|
||||||
|
getCorrelationId() {
|
||||||
|
return this.correlationId;
|
||||||
|
}
|
||||||
|
clearCorrelationId() {
|
||||||
|
this.correlationId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Export a singleton instance
|
||||||
|
export const logger = new StandardLogger();
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvbG9nZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFekMscUNBQXFDO0FBQ3JDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxJQUFJLFlBQVksQ0FBQztBQUNyRCxNQUFNLE1BQU0sR0FBZ0U7SUFDMUUsYUFBYSxFQUFFLE9BQU87SUFDdEIsTUFBTSxFQUFFLE1BQU07SUFDZCxTQUFTLEVBQUUsU0FBUztJQUNwQixZQUFZLEVBQUUsWUFBWTtDQUMzQixDQUFDO0FBRUYsNEJBQTRCO0FBQzVCLE1BQU0sVUFBVSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUM7SUFDL0MsVUFBVSxFQUFFO1FBQ1YsV0FBVyxFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxZQUFZO1FBQzVDLE9BQU8sRUFBRSxNQUFNO1FBQ2YsSUFBSSxFQUFFLFlBQVk7S0FDbkI7Q0FDRixDQUFDLENBQUM7QUFFSCxtRUFBbUU7QUFDbkUsTUFBTSxjQUFjO0lBQ1YsY0FBYyxHQUF3QixFQUFFLENBQUM7SUFDekMsYUFBYSxHQUFrQixJQUFJLENBQUM7SUFFNUMsZ0JBQWUsQ0FBQztJQUVoQixjQUFjO0lBQ1AsR0FBRyxDQUFDLEtBQXNELEVBQUUsT0FBZSxFQUFFLFVBQStCLEVBQUU7UUFDbkgsTUFBTSxlQUFlLEdBQUc7WUFDdEIsR0FBRyxJQUFJLENBQUMsY0FBYztZQUN0QixHQUFHLE9BQU87U0FDWCxDQUFDO1FBRUYsSUFBSSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDdkIsZUFBZSxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDO1FBQ3RELENBQUM7UUFFRCxVQUFVLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsZUFBZSxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUVNLEtBQUssQ0FBQyxPQUFlLEVBQUUsVUFBK0IsRUFBRTtRQUM3RCxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDdEMsQ0FBQztJQUVNLElBQUksQ0FBQyxPQUFlLEVBQUUsVUFBK0IsRUFBRTtRQUM1RCxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDckMsQ0FBQztJQUVNLElBQUksQ0FBQyxPQUFlLEVBQUUsVUFBK0IsRUFBRTtRQUM1RCxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDckMsQ0FBQztJQUVNLE9BQU8sQ0FBQyxPQUFlLEVBQUUsVUFBK0IsRUFBRTtRQUMvRCxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDeEMsQ0FBQztJQUVNLEtBQUssQ0FBQyxPQUFlLEVBQUUsVUFBK0IsRUFBRTtRQUM3RCxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDdEMsQ0FBQztJQUVELHFCQUFxQjtJQUNkLFVBQVUsQ0FBQyxPQUE0QixFQUFFLFlBQXFCLEtBQUs7UUFDeEUsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNkLElBQUksQ0FBQyxjQUFjLEdBQUcsT0FBTyxDQUFDO1FBQ2hDLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLGNBQWMsR0FBRztnQkFDcEIsR0FBRyxJQUFJLENBQUMsY0FBYztnQkFDdEIsR0FBRyxPQUFPO2FBQ1gsQ0FBQztRQUNKLENBQUM7SUFDSCxDQUFDO0lBRUQsNEJBQTRCO0lBQ3JCLGdCQUFnQixDQUFDLEtBQW9CLElBQUk7UUFDOUMsSUFBSSxDQUFDLGFBQWEsR0FBRyxFQUFFLElBQUksVUFBVSxFQUFFLENBQUM7UUFDeEMsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDO0lBQzVCLENBQUM7SUFFTSxnQkFBZ0I7UUFDckIsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDO0lBQzVCLENBQUM7SUFFTSxrQkFBa0I7UUFDdkIsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUM7SUFDNUIsQ0FBQztDQUNGO0FBRUQsOEJBQThCO0FBQzlCLE1BQU0sQ0FBQyxNQUFNLE1BQU0sR0FBRyxJQUFJLGNBQWMsRUFBRSxDQUFDIn0=
|
||||||
185
dist_ts/mail/core/classes.bouncemanager.d.ts
vendored
Normal file
185
dist_ts/mail/core/classes.bouncemanager.d.ts
vendored
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
import type { Email } from './classes.email.js';
|
||||||
|
/**
|
||||||
|
* Bounce types for categorizing the reasons for bounces
|
||||||
|
*/
|
||||||
|
export declare enum BounceType {
|
||||||
|
INVALID_RECIPIENT = "invalid_recipient",
|
||||||
|
DOMAIN_NOT_FOUND = "domain_not_found",
|
||||||
|
MAILBOX_FULL = "mailbox_full",
|
||||||
|
MAILBOX_INACTIVE = "mailbox_inactive",
|
||||||
|
BLOCKED = "blocked",
|
||||||
|
SPAM_RELATED = "spam_related",
|
||||||
|
POLICY_RELATED = "policy_related",
|
||||||
|
SERVER_UNAVAILABLE = "server_unavailable",
|
||||||
|
TEMPORARY_FAILURE = "temporary_failure",
|
||||||
|
QUOTA_EXCEEDED = "quota_exceeded",
|
||||||
|
NETWORK_ERROR = "network_error",
|
||||||
|
TIMEOUT = "timeout",
|
||||||
|
AUTO_RESPONSE = "auto_response",
|
||||||
|
CHALLENGE_RESPONSE = "challenge_response",
|
||||||
|
UNKNOWN = "unknown"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Hard vs soft bounce classification
|
||||||
|
*/
|
||||||
|
export declare enum BounceCategory {
|
||||||
|
HARD = "hard",
|
||||||
|
SOFT = "soft",
|
||||||
|
AUTO_RESPONSE = "auto_response",
|
||||||
|
UNKNOWN = "unknown"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Bounce data structure
|
||||||
|
*/
|
||||||
|
export interface BounceRecord {
|
||||||
|
id: string;
|
||||||
|
originalEmailId?: string;
|
||||||
|
recipient: string;
|
||||||
|
sender: string;
|
||||||
|
domain: string;
|
||||||
|
subject?: string;
|
||||||
|
bounceType: BounceType;
|
||||||
|
bounceCategory: BounceCategory;
|
||||||
|
timestamp: number;
|
||||||
|
smtpResponse?: string;
|
||||||
|
diagnosticCode?: string;
|
||||||
|
statusCode?: string;
|
||||||
|
headers?: Record<string, string>;
|
||||||
|
processed: boolean;
|
||||||
|
retryCount?: number;
|
||||||
|
nextRetryTime?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retry strategy configuration for soft bounces
|
||||||
|
*/
|
||||||
|
interface RetryStrategy {
|
||||||
|
maxRetries: number;
|
||||||
|
initialDelay: number;
|
||||||
|
maxDelay: number;
|
||||||
|
backoffFactor: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Manager for handling email bounces
|
||||||
|
*/
|
||||||
|
export declare class BounceManager {
|
||||||
|
private retryStrategy;
|
||||||
|
private bounceStore;
|
||||||
|
private bounceCache;
|
||||||
|
private suppressionList;
|
||||||
|
private storageManager?;
|
||||||
|
constructor(options?: {
|
||||||
|
retryStrategy?: Partial<RetryStrategy>;
|
||||||
|
maxCacheSize?: number;
|
||||||
|
cacheTTL?: number;
|
||||||
|
storageManager?: any;
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* Process a bounce notification
|
||||||
|
* @param bounceData Bounce data to process
|
||||||
|
* @returns Processed bounce record
|
||||||
|
*/
|
||||||
|
processBounce(bounceData: Partial<BounceRecord>): Promise<BounceRecord>;
|
||||||
|
/**
|
||||||
|
* Process an SMTP failure as a bounce
|
||||||
|
* @param recipient Recipient email
|
||||||
|
* @param smtpResponse SMTP error response
|
||||||
|
* @param options Additional options
|
||||||
|
* @returns Processed bounce record
|
||||||
|
*/
|
||||||
|
processSmtpFailure(recipient: string, smtpResponse: string, options?: {
|
||||||
|
sender?: string;
|
||||||
|
originalEmailId?: string;
|
||||||
|
statusCode?: string;
|
||||||
|
headers?: Record<string, string>;
|
||||||
|
}): Promise<BounceRecord>;
|
||||||
|
/**
|
||||||
|
* Process a bounce notification email
|
||||||
|
* @param bounceEmail The email containing bounce information
|
||||||
|
* @returns Processed bounce record or null if not a bounce
|
||||||
|
*/
|
||||||
|
processBounceEmail(bounceEmail: Email): Promise<BounceRecord | null>;
|
||||||
|
/**
|
||||||
|
* Handle a hard bounce by adding to suppression list
|
||||||
|
* @param bounce The bounce record
|
||||||
|
*/
|
||||||
|
private handleHardBounce;
|
||||||
|
/**
|
||||||
|
* Handle a soft bounce by scheduling a retry if eligible
|
||||||
|
* @param bounce The bounce record
|
||||||
|
*/
|
||||||
|
private handleSoftBounce;
|
||||||
|
/**
|
||||||
|
* Add an email address to the suppression list
|
||||||
|
* @param email Email address to suppress
|
||||||
|
* @param reason Reason for suppression
|
||||||
|
* @param expiresAt Expiration timestamp (undefined for permanent)
|
||||||
|
*/
|
||||||
|
addToSuppressionList(email: string, reason: string, expiresAt?: number): void;
|
||||||
|
/**
|
||||||
|
* Remove an email address from the suppression list
|
||||||
|
* @param email Email address to remove
|
||||||
|
*/
|
||||||
|
removeFromSuppressionList(email: string): void;
|
||||||
|
/**
|
||||||
|
* Check if an email is on the suppression list
|
||||||
|
* @param email Email address to check
|
||||||
|
* @returns Whether the email is suppressed
|
||||||
|
*/
|
||||||
|
isEmailSuppressed(email: string): boolean;
|
||||||
|
/**
|
||||||
|
* Get suppression information for an email
|
||||||
|
* @param email Email address to check
|
||||||
|
* @returns Suppression information or null if not suppressed
|
||||||
|
*/
|
||||||
|
getSuppressionInfo(email: string): {
|
||||||
|
reason: string;
|
||||||
|
timestamp: number;
|
||||||
|
expiresAt?: number;
|
||||||
|
} | null;
|
||||||
|
/**
|
||||||
|
* Save suppression list to disk
|
||||||
|
*/
|
||||||
|
private saveSuppressionList;
|
||||||
|
/**
|
||||||
|
* Load suppression list from disk
|
||||||
|
*/
|
||||||
|
private loadSuppressionList;
|
||||||
|
/**
|
||||||
|
* Save bounce record to disk
|
||||||
|
* @param bounce Bounce record to save
|
||||||
|
*/
|
||||||
|
private saveBounceRecord;
|
||||||
|
/**
|
||||||
|
* Update bounce cache with new bounce information
|
||||||
|
* @param bounce Bounce record to update cache with
|
||||||
|
*/
|
||||||
|
private updateBounceCache;
|
||||||
|
/**
|
||||||
|
* Check bounce history for an email address
|
||||||
|
* @param email Email address to check
|
||||||
|
* @returns Bounce information or null if no bounces
|
||||||
|
*/
|
||||||
|
getBounceInfo(email: string): {
|
||||||
|
lastBounce: number;
|
||||||
|
count: number;
|
||||||
|
type: BounceType;
|
||||||
|
category: BounceCategory;
|
||||||
|
} | null;
|
||||||
|
/**
|
||||||
|
* Get all known hard bounced addresses
|
||||||
|
* @returns Array of hard bounced email addresses
|
||||||
|
*/
|
||||||
|
getHardBouncedAddresses(): string[];
|
||||||
|
/**
|
||||||
|
* Get suppression list
|
||||||
|
* @returns Array of suppressed email addresses
|
||||||
|
*/
|
||||||
|
getSuppressionList(): string[];
|
||||||
|
/**
|
||||||
|
* Clear old bounce records (for maintenance)
|
||||||
|
* @param olderThan Timestamp to remove records older than
|
||||||
|
* @returns Number of records removed
|
||||||
|
*/
|
||||||
|
clearOldBounceRecords(olderThan: number): number;
|
||||||
|
}
|
||||||
|
export {};
|
||||||
569
dist_ts/mail/core/classes.bouncemanager.js
Normal file
569
dist_ts/mail/core/classes.bouncemanager.js
Normal file
File diff suppressed because one or more lines are too long
291
dist_ts/mail/core/classes.email.d.ts
vendored
Normal file
291
dist_ts/mail/core/classes.email.d.ts
vendored
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
import * as plugins from '../../plugins.js';
|
||||||
|
export interface IAttachment {
|
||||||
|
filename: string;
|
||||||
|
content: Buffer;
|
||||||
|
contentType: string;
|
||||||
|
contentId?: string;
|
||||||
|
encoding?: string;
|
||||||
|
}
|
||||||
|
export interface IEmailOptions {
|
||||||
|
from: string;
|
||||||
|
to?: string | string[];
|
||||||
|
cc?: string | string[];
|
||||||
|
bcc?: string | string[];
|
||||||
|
subject: string;
|
||||||
|
text: string;
|
||||||
|
html?: string;
|
||||||
|
attachments?: IAttachment[];
|
||||||
|
headers?: Record<string, string>;
|
||||||
|
mightBeSpam?: boolean;
|
||||||
|
priority?: 'high' | 'normal' | 'low';
|
||||||
|
skipAdvancedValidation?: boolean;
|
||||||
|
variables?: Record<string, any>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Email class represents a complete email message.
|
||||||
|
*
|
||||||
|
* This class takes IEmailOptions in the constructor and normalizes the data:
|
||||||
|
* - 'to', 'cc', 'bcc' are always converted to arrays
|
||||||
|
* - Optional properties get default values
|
||||||
|
* - Additional properties like messageId and envelopeFrom are generated
|
||||||
|
*/
|
||||||
|
export declare class Email {
|
||||||
|
from: string;
|
||||||
|
to: string[];
|
||||||
|
cc: string[];
|
||||||
|
bcc: string[];
|
||||||
|
subject: string;
|
||||||
|
text: string;
|
||||||
|
html?: string;
|
||||||
|
attachments: IAttachment[];
|
||||||
|
headers: Record<string, string>;
|
||||||
|
mightBeSpam: boolean;
|
||||||
|
priority: 'high' | 'normal' | 'low';
|
||||||
|
variables: Record<string, any>;
|
||||||
|
private envelopeFrom;
|
||||||
|
private messageId;
|
||||||
|
private static emailValidator;
|
||||||
|
constructor(options: IEmailOptions);
|
||||||
|
/**
|
||||||
|
* Validates an email address using smartmail's EmailAddressValidator
|
||||||
|
* For constructor validation, we only check syntax to avoid delays
|
||||||
|
* Supports RFC-compliant addresses including display names and bounce addresses.
|
||||||
|
*
|
||||||
|
* @param email The email address to validate
|
||||||
|
* @returns boolean indicating if the email is valid
|
||||||
|
*/
|
||||||
|
private isValidEmail;
|
||||||
|
/**
|
||||||
|
* Extracts the email address from a string that may contain a display name.
|
||||||
|
* Handles formats like:
|
||||||
|
* - simple@example.com
|
||||||
|
* - "John Doe" <john@example.com>
|
||||||
|
* - John Doe <john@example.com>
|
||||||
|
*
|
||||||
|
* @param emailString The email string to parse
|
||||||
|
* @returns The extracted email address or null
|
||||||
|
*/
|
||||||
|
private extractEmailAddress;
|
||||||
|
/**
|
||||||
|
* Parses and validates recipient email addresses
|
||||||
|
* @param recipients A string or array of recipient emails
|
||||||
|
* @returns Array of validated email addresses
|
||||||
|
*/
|
||||||
|
private parseRecipients;
|
||||||
|
/**
|
||||||
|
* Basic sanitization for strings to prevent header injection
|
||||||
|
* @param input The string to sanitize
|
||||||
|
* @returns Sanitized string
|
||||||
|
*/
|
||||||
|
private sanitizeString;
|
||||||
|
/**
|
||||||
|
* Gets the domain part of the from email address
|
||||||
|
* @returns The domain part of the from email or null if invalid
|
||||||
|
*/
|
||||||
|
getFromDomain(): string | null;
|
||||||
|
/**
|
||||||
|
* Gets the clean from email address without display name
|
||||||
|
* @returns The email address without display name
|
||||||
|
*/
|
||||||
|
getFromAddress(): string;
|
||||||
|
/**
|
||||||
|
* Converts IDN (International Domain Names) to ASCII
|
||||||
|
* @param email The email address to convert
|
||||||
|
* @returns The email with ASCII-converted domain
|
||||||
|
*/
|
||||||
|
private convertIDNToASCII;
|
||||||
|
/**
|
||||||
|
* Gets clean to email addresses without display names
|
||||||
|
* @returns Array of email addresses without display names
|
||||||
|
*/
|
||||||
|
getToAddresses(): string[];
|
||||||
|
/**
|
||||||
|
* Gets clean cc email addresses without display names
|
||||||
|
* @returns Array of email addresses without display names
|
||||||
|
*/
|
||||||
|
getCcAddresses(): string[];
|
||||||
|
/**
|
||||||
|
* Gets clean bcc email addresses without display names
|
||||||
|
* @returns Array of email addresses without display names
|
||||||
|
*/
|
||||||
|
getBccAddresses(): string[];
|
||||||
|
/**
|
||||||
|
* Gets all recipients (to, cc, bcc) as a unique array
|
||||||
|
* @returns Array of all unique recipient email addresses
|
||||||
|
*/
|
||||||
|
getAllRecipients(): string[];
|
||||||
|
/**
|
||||||
|
* Gets primary recipient (first in the to field)
|
||||||
|
* @returns The primary recipient email or null if none exists
|
||||||
|
*/
|
||||||
|
getPrimaryRecipient(): string | null;
|
||||||
|
/**
|
||||||
|
* Checks if the email has attachments
|
||||||
|
* @returns Boolean indicating if the email has attachments
|
||||||
|
*/
|
||||||
|
hasAttachments(): boolean;
|
||||||
|
/**
|
||||||
|
* Add a recipient to the email
|
||||||
|
* @param email The recipient email address
|
||||||
|
* @param type The recipient type (to, cc, bcc)
|
||||||
|
* @returns This instance for method chaining
|
||||||
|
*/
|
||||||
|
addRecipient(email: string, type?: 'to' | 'cc' | 'bcc'): this;
|
||||||
|
/**
|
||||||
|
* Add an attachment to the email
|
||||||
|
* @param attachment The attachment to add
|
||||||
|
* @returns This instance for method chaining
|
||||||
|
*/
|
||||||
|
addAttachment(attachment: IAttachment): this;
|
||||||
|
/**
|
||||||
|
* Add a custom header to the email
|
||||||
|
* @param name The header name
|
||||||
|
* @param value The header value
|
||||||
|
* @returns This instance for method chaining
|
||||||
|
*/
|
||||||
|
addHeader(name: string, value: string): this;
|
||||||
|
/**
|
||||||
|
* Set the email priority
|
||||||
|
* @param priority The priority level
|
||||||
|
* @returns This instance for method chaining
|
||||||
|
*/
|
||||||
|
setPriority(priority: 'high' | 'normal' | 'low'): this;
|
||||||
|
/**
|
||||||
|
* Set a template variable
|
||||||
|
* @param key The variable key
|
||||||
|
* @param value The variable value
|
||||||
|
* @returns This instance for method chaining
|
||||||
|
*/
|
||||||
|
setVariable(key: string, value: any): this;
|
||||||
|
/**
|
||||||
|
* Set multiple template variables at once
|
||||||
|
* @param variables The variables object
|
||||||
|
* @returns This instance for method chaining
|
||||||
|
*/
|
||||||
|
setVariables(variables: Record<string, any>): this;
|
||||||
|
/**
|
||||||
|
* Get the subject with variables applied
|
||||||
|
* @param variables Optional additional variables to apply
|
||||||
|
* @returns The processed subject
|
||||||
|
*/
|
||||||
|
getSubjectWithVariables(variables?: Record<string, any>): string;
|
||||||
|
/**
|
||||||
|
* Get the text content with variables applied
|
||||||
|
* @param variables Optional additional variables to apply
|
||||||
|
* @returns The processed text content
|
||||||
|
*/
|
||||||
|
getTextWithVariables(variables?: Record<string, any>): string;
|
||||||
|
/**
|
||||||
|
* Get the HTML content with variables applied
|
||||||
|
* @param variables Optional additional variables to apply
|
||||||
|
* @returns The processed HTML content or undefined if none
|
||||||
|
*/
|
||||||
|
getHtmlWithVariables(variables?: Record<string, any>): string | undefined;
|
||||||
|
/**
|
||||||
|
* Apply template variables to a string
|
||||||
|
* @param template The template string
|
||||||
|
* @param additionalVariables Optional additional variables to apply
|
||||||
|
* @returns The processed string
|
||||||
|
*/
|
||||||
|
private applyVariables;
|
||||||
|
/**
|
||||||
|
* Gets the total size of all attachments in bytes
|
||||||
|
* @returns Total size of all attachments in bytes
|
||||||
|
*/
|
||||||
|
getAttachmentsSize(): number;
|
||||||
|
/**
|
||||||
|
* Perform advanced validation on sender and recipient email addresses
|
||||||
|
* This should be called separately after instantiation when ready to check MX records
|
||||||
|
* @param options Validation options
|
||||||
|
* @returns Promise resolving to validation results for all addresses
|
||||||
|
*/
|
||||||
|
validateAddresses(options?: {
|
||||||
|
checkMx?: boolean;
|
||||||
|
checkDisposable?: boolean;
|
||||||
|
checkSenderOnly?: boolean;
|
||||||
|
checkFirstRecipientOnly?: boolean;
|
||||||
|
}): Promise<{
|
||||||
|
sender: {
|
||||||
|
email: string;
|
||||||
|
result: any;
|
||||||
|
};
|
||||||
|
recipients: Array<{
|
||||||
|
email: string;
|
||||||
|
result: any;
|
||||||
|
}>;
|
||||||
|
isValid: boolean;
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* Convert this email to a smartmail instance
|
||||||
|
* @returns A new Smartmail instance
|
||||||
|
*/
|
||||||
|
toSmartmail(): Promise<plugins.smartmail.Smartmail<any>>;
|
||||||
|
/**
|
||||||
|
* Get the from email address
|
||||||
|
* @returns The from email address
|
||||||
|
*/
|
||||||
|
getFromEmail(): string;
|
||||||
|
/**
|
||||||
|
* Get the subject (Smartmail compatibility method)
|
||||||
|
* @returns The email subject
|
||||||
|
*/
|
||||||
|
getSubject(): string;
|
||||||
|
/**
|
||||||
|
* Get the body content (Smartmail compatibility method)
|
||||||
|
* @param isHtml Whether to return HTML content if available
|
||||||
|
* @returns The email body (HTML if requested and available, otherwise plain text)
|
||||||
|
*/
|
||||||
|
getBody(isHtml?: boolean): string;
|
||||||
|
/**
|
||||||
|
* Get the from address (Smartmail compatibility method)
|
||||||
|
* @returns The sender email address
|
||||||
|
*/
|
||||||
|
getFrom(): string;
|
||||||
|
/**
|
||||||
|
* Get the message ID
|
||||||
|
* @returns The message ID
|
||||||
|
*/
|
||||||
|
getMessageId(): string;
|
||||||
|
/**
|
||||||
|
* Convert the Email instance back to IEmailOptions format.
|
||||||
|
* Useful for serialization or passing to APIs that expect IEmailOptions.
|
||||||
|
* Note: This loses some Email-specific properties like messageId and envelopeFrom.
|
||||||
|
*
|
||||||
|
* @returns IEmailOptions representation of this email
|
||||||
|
*/
|
||||||
|
toEmailOptions(): IEmailOptions;
|
||||||
|
/**
|
||||||
|
* Set a custom message ID
|
||||||
|
* @param id The message ID to set
|
||||||
|
* @returns This instance for method chaining
|
||||||
|
*/
|
||||||
|
setMessageId(id: string): this;
|
||||||
|
/**
|
||||||
|
* Get the envelope from address (return-path)
|
||||||
|
* @returns The envelope from address
|
||||||
|
*/
|
||||||
|
getEnvelopeFrom(): string;
|
||||||
|
/**
|
||||||
|
* Set the envelope from address (return-path)
|
||||||
|
* @param address The envelope from address to set
|
||||||
|
* @returns This instance for method chaining
|
||||||
|
*/
|
||||||
|
setEnvelopeFrom(address: string): this;
|
||||||
|
/**
|
||||||
|
* Creates an RFC822 compliant email string
|
||||||
|
* @param variables Optional template variables to apply
|
||||||
|
* @returns The email formatted as an RFC822 compliant string
|
||||||
|
*/
|
||||||
|
toRFC822String(variables?: Record<string, any>): string;
|
||||||
|
/**
|
||||||
|
* Convert to simple Smartmail-compatible object (for backward compatibility)
|
||||||
|
* @returns A Promise with a simple Smartmail-compatible object
|
||||||
|
*/
|
||||||
|
toSmartmailBasic(): Promise<any>;
|
||||||
|
/**
|
||||||
|
* Create an Email instance from a Smartmail object
|
||||||
|
* @param smartmail The Smartmail instance to convert
|
||||||
|
* @returns A new Email instance
|
||||||
|
*/
|
||||||
|
static fromSmartmail(smartmail: plugins.smartmail.Smartmail<any>): Email;
|
||||||
|
}
|
||||||
802
dist_ts/mail/core/classes.email.js
Normal file
802
dist_ts/mail/core/classes.email.js
Normal file
File diff suppressed because one or more lines are too long
61
dist_ts/mail/core/classes.emailvalidator.d.ts
vendored
Normal file
61
dist_ts/mail/core/classes.emailvalidator.d.ts
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
export interface IEmailValidationResult {
|
||||||
|
isValid: boolean;
|
||||||
|
hasMx: boolean;
|
||||||
|
hasSpamMarkings: boolean;
|
||||||
|
score: number;
|
||||||
|
details?: {
|
||||||
|
formatValid?: boolean;
|
||||||
|
mxRecords?: string[];
|
||||||
|
disposable?: boolean;
|
||||||
|
role?: boolean;
|
||||||
|
spamIndicators?: string[];
|
||||||
|
errorMessage?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Advanced email validator class using smartmail's capabilities
|
||||||
|
*/
|
||||||
|
export declare class EmailValidator {
|
||||||
|
private validator;
|
||||||
|
private dnsCache;
|
||||||
|
constructor(options?: {
|
||||||
|
maxCacheSize?: number;
|
||||||
|
cacheTTL?: number;
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* Validates an email address using comprehensive checks
|
||||||
|
* @param email The email to validate
|
||||||
|
* @param options Validation options
|
||||||
|
* @returns Validation result with details
|
||||||
|
*/
|
||||||
|
validate(email: string, options?: {
|
||||||
|
checkMx?: boolean;
|
||||||
|
checkDisposable?: boolean;
|
||||||
|
checkRole?: boolean;
|
||||||
|
checkSyntaxOnly?: boolean;
|
||||||
|
}): Promise<IEmailValidationResult>;
|
||||||
|
/**
|
||||||
|
* Gets MX records for a domain with caching
|
||||||
|
* @param domain Domain to check
|
||||||
|
* @returns Array of MX records
|
||||||
|
*/
|
||||||
|
private getMxRecords;
|
||||||
|
/**
|
||||||
|
* Validates multiple email addresses in batch
|
||||||
|
* @param emails Array of emails to validate
|
||||||
|
* @param options Validation options
|
||||||
|
* @returns Object with email addresses as keys and validation results as values
|
||||||
|
*/
|
||||||
|
validateBatch(emails: string[], options?: {
|
||||||
|
checkMx?: boolean;
|
||||||
|
checkDisposable?: boolean;
|
||||||
|
checkRole?: boolean;
|
||||||
|
checkSyntaxOnly?: boolean;
|
||||||
|
}): Promise<Record<string, IEmailValidationResult>>;
|
||||||
|
/**
|
||||||
|
* Quick check if an email format is valid (synchronous, no DNS checks)
|
||||||
|
* @param email Email to check
|
||||||
|
* @returns Boolean indicating if format is valid
|
||||||
|
*/
|
||||||
|
isValidFormat(email: string): boolean;
|
||||||
|
}
|
||||||
184
dist_ts/mail/core/classes.emailvalidator.js
Normal file
184
dist_ts/mail/core/classes.emailvalidator.js
Normal file
File diff suppressed because one or more lines are too long
95
dist_ts/mail/core/classes.templatemanager.d.ts
vendored
Normal file
95
dist_ts/mail/core/classes.templatemanager.d.ts
vendored
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { Email } from './classes.email.js';
|
||||||
|
/**
|
||||||
|
* Email template type definition
|
||||||
|
*/
|
||||||
|
export interface IEmailTemplate<T = any> {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
from: string;
|
||||||
|
subject: string;
|
||||||
|
bodyHtml: string;
|
||||||
|
bodyText?: string;
|
||||||
|
category?: string;
|
||||||
|
sampleData?: T;
|
||||||
|
attachments?: Array<{
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
contentType?: string;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Email template context - data used to render the template
|
||||||
|
*/
|
||||||
|
export interface ITemplateContext {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Template category definitions
|
||||||
|
*/
|
||||||
|
export declare enum TemplateCategory {
|
||||||
|
NOTIFICATION = "notification",
|
||||||
|
TRANSACTIONAL = "transactional",
|
||||||
|
MARKETING = "marketing",
|
||||||
|
SYSTEM = "system"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Enhanced template manager using Email class for template rendering
|
||||||
|
*/
|
||||||
|
export declare class TemplateManager {
|
||||||
|
private templates;
|
||||||
|
private defaultConfig;
|
||||||
|
constructor(defaultConfig?: {
|
||||||
|
from?: string;
|
||||||
|
replyTo?: string;
|
||||||
|
footerHtml?: string;
|
||||||
|
footerText?: string;
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* Register built-in email templates
|
||||||
|
*/
|
||||||
|
private registerBuiltinTemplates;
|
||||||
|
/**
|
||||||
|
* Register a new email template
|
||||||
|
* @param template The email template to register
|
||||||
|
*/
|
||||||
|
registerTemplate<T = any>(template: IEmailTemplate<T>): void;
|
||||||
|
/**
|
||||||
|
* Get an email template by ID
|
||||||
|
* @param templateId The template ID
|
||||||
|
* @returns The template or undefined if not found
|
||||||
|
*/
|
||||||
|
getTemplate<T = any>(templateId: string): IEmailTemplate<T> | undefined;
|
||||||
|
/**
|
||||||
|
* List all available templates
|
||||||
|
* @param category Optional category filter
|
||||||
|
* @returns Array of email templates
|
||||||
|
*/
|
||||||
|
listTemplates(category?: TemplateCategory): IEmailTemplate[];
|
||||||
|
/**
|
||||||
|
* Create an Email instance from a template
|
||||||
|
* @param templateId The template ID
|
||||||
|
* @param context The template context data
|
||||||
|
* @returns A configured Email instance
|
||||||
|
*/
|
||||||
|
createEmail<T = any>(templateId: string, context?: ITemplateContext): Promise<Email>;
|
||||||
|
/**
|
||||||
|
* Create and completely process an Email instance from a template
|
||||||
|
* @param templateId The template ID
|
||||||
|
* @param context The template context data
|
||||||
|
* @returns A complete, processed Email instance ready to send
|
||||||
|
*/
|
||||||
|
prepareEmail<T = any>(templateId: string, context?: ITemplateContext): Promise<Email>;
|
||||||
|
/**
|
||||||
|
* Create a MIME-formatted email from a template
|
||||||
|
* @param templateId The template ID
|
||||||
|
* @param context The template context data
|
||||||
|
* @returns A MIME-formatted email string
|
||||||
|
*/
|
||||||
|
createMimeEmail(templateId: string, context?: ITemplateContext): Promise<string>;
|
||||||
|
/**
|
||||||
|
* Load templates from a directory
|
||||||
|
* @param directory The directory containing template JSON files
|
||||||
|
*/
|
||||||
|
loadTemplatesFromDirectory(directory: string): Promise<void>;
|
||||||
|
}
|
||||||
240
dist_ts/mail/core/classes.templatemanager.js
Normal file
240
dist_ts/mail/core/classes.templatemanager.js
Normal file
File diff suppressed because one or more lines are too long
4
dist_ts/mail/core/index.d.ts
vendored
Normal file
4
dist_ts/mail/core/index.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export * from './classes.email.js';
|
||||||
|
export * from './classes.emailvalidator.js';
|
||||||
|
export * from './classes.templatemanager.js';
|
||||||
|
export * from './classes.bouncemanager.js';
|
||||||
6
dist_ts/mail/core/index.js
Normal file
6
dist_ts/mail/core/index.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Core email components
|
||||||
|
export * from './classes.email.js';
|
||||||
|
export * from './classes.emailvalidator.js';
|
||||||
|
export * from './classes.templatemanager.js';
|
||||||
|
export * from './classes.bouncemanager.js';
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2NvcmUvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsd0JBQXdCO0FBQ3hCLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyw2QkFBNkIsQ0FBQztBQUM1QyxjQUFjLDhCQUE4QixDQUFDO0FBQzdDLGNBQWMsNEJBQTRCLENBQUMifQ==
|
||||||
163
dist_ts/mail/delivery/classes.delivery.queue.d.ts
vendored
Normal file
163
dist_ts/mail/delivery/classes.delivery.queue.d.ts
vendored
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
import { EventEmitter } from 'node:events';
|
||||||
|
import { type EmailProcessingMode } from '../routing/classes.email.config.js';
|
||||||
|
import type { IEmailRoute } from '../routing/interfaces.js';
|
||||||
|
/**
|
||||||
|
* Queue item status
|
||||||
|
*/
|
||||||
|
export type QueueItemStatus = 'pending' | 'processing' | 'delivered' | 'failed' | 'deferred';
|
||||||
|
/**
|
||||||
|
* Queue item interface
|
||||||
|
*/
|
||||||
|
export interface IQueueItem {
|
||||||
|
id: string;
|
||||||
|
processingMode: EmailProcessingMode;
|
||||||
|
processingResult: any;
|
||||||
|
route: IEmailRoute;
|
||||||
|
status: QueueItemStatus;
|
||||||
|
attempts: number;
|
||||||
|
nextAttempt: Date;
|
||||||
|
lastError?: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
deliveredAt?: Date;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Queue options interface
|
||||||
|
*/
|
||||||
|
export interface IQueueOptions {
|
||||||
|
storageType?: 'memory' | 'disk';
|
||||||
|
persistentPath?: string;
|
||||||
|
checkInterval?: number;
|
||||||
|
maxQueueSize?: number;
|
||||||
|
maxPerDestination?: number;
|
||||||
|
maxRetries?: number;
|
||||||
|
baseRetryDelay?: number;
|
||||||
|
maxRetryDelay?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Queue statistics interface
|
||||||
|
*/
|
||||||
|
export interface IQueueStats {
|
||||||
|
queueSize: number;
|
||||||
|
status: {
|
||||||
|
pending: number;
|
||||||
|
processing: number;
|
||||||
|
delivered: number;
|
||||||
|
failed: number;
|
||||||
|
deferred: number;
|
||||||
|
};
|
||||||
|
modes: {
|
||||||
|
forward: number;
|
||||||
|
mta: number;
|
||||||
|
process: number;
|
||||||
|
};
|
||||||
|
oldestItem?: Date;
|
||||||
|
newestItem?: Date;
|
||||||
|
averageAttempts: number;
|
||||||
|
totalProcessed: number;
|
||||||
|
processingActive: boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* A unified queue for all email modes
|
||||||
|
*/
|
||||||
|
export declare class UnifiedDeliveryQueue extends EventEmitter {
|
||||||
|
private options;
|
||||||
|
private queue;
|
||||||
|
private checkTimer?;
|
||||||
|
private stats;
|
||||||
|
private processing;
|
||||||
|
private totalProcessed;
|
||||||
|
/**
|
||||||
|
* Create a new unified delivery queue
|
||||||
|
* @param options Queue options
|
||||||
|
*/
|
||||||
|
constructor(options: IQueueOptions);
|
||||||
|
/**
|
||||||
|
* Initialize the queue
|
||||||
|
*/
|
||||||
|
initialize(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Start queue processing
|
||||||
|
*/
|
||||||
|
private startProcessing;
|
||||||
|
/**
|
||||||
|
* Stop queue processing
|
||||||
|
*/
|
||||||
|
private stopProcessing;
|
||||||
|
/**
|
||||||
|
* Check for items that need to be processed
|
||||||
|
*/
|
||||||
|
private processQueue;
|
||||||
|
/**
|
||||||
|
* Add an item to the queue
|
||||||
|
* @param processingResult Processing result to queue
|
||||||
|
* @param mode Processing mode
|
||||||
|
* @param route Email route
|
||||||
|
*/
|
||||||
|
enqueue(processingResult: any, mode: EmailProcessingMode, route: IEmailRoute): Promise<string>;
|
||||||
|
/**
|
||||||
|
* Get an item from the queue
|
||||||
|
* @param id Item ID
|
||||||
|
*/
|
||||||
|
getItem(id: string): IQueueItem | undefined;
|
||||||
|
/**
|
||||||
|
* Mark an item as being processed
|
||||||
|
* @param id Item ID
|
||||||
|
*/
|
||||||
|
markProcessing(id: string): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* Mark an item as delivered
|
||||||
|
* @param id Item ID
|
||||||
|
*/
|
||||||
|
markDelivered(id: string): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* Mark an item as failed
|
||||||
|
* @param id Item ID
|
||||||
|
* @param error Error message
|
||||||
|
*/
|
||||||
|
markFailed(id: string, error: string): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* Remove an item from the queue
|
||||||
|
* @param id Item ID
|
||||||
|
*/
|
||||||
|
removeItem(id: string): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* Persist an item to disk
|
||||||
|
* @param item Item to persist
|
||||||
|
*/
|
||||||
|
private persistItem;
|
||||||
|
/**
|
||||||
|
* Remove an item from disk
|
||||||
|
* @param id Item ID
|
||||||
|
*/
|
||||||
|
private removeItemFromDisk;
|
||||||
|
/**
|
||||||
|
* Load queue items from disk
|
||||||
|
*/
|
||||||
|
private loadFromDisk;
|
||||||
|
/**
|
||||||
|
* Update queue statistics
|
||||||
|
*/
|
||||||
|
private updateStats;
|
||||||
|
/**
|
||||||
|
* Get queue statistics
|
||||||
|
*/
|
||||||
|
getStats(): IQueueStats;
|
||||||
|
/**
|
||||||
|
* Pause queue processing
|
||||||
|
*/
|
||||||
|
pause(): void;
|
||||||
|
/**
|
||||||
|
* Resume queue processing
|
||||||
|
*/
|
||||||
|
resume(): void;
|
||||||
|
/**
|
||||||
|
* Clean up old delivered and failed items
|
||||||
|
* @param maxAge Maximum age in milliseconds (default: 7 days)
|
||||||
|
*/
|
||||||
|
cleanupOldItems(maxAge?: number): Promise<number>;
|
||||||
|
/**
|
||||||
|
* Shutdown the queue
|
||||||
|
*/
|
||||||
|
shutdown(): Promise<void>;
|
||||||
|
}
|
||||||
488
dist_ts/mail/delivery/classes.delivery.queue.js
Normal file
488
dist_ts/mail/delivery/classes.delivery.queue.js
Normal file
File diff suppressed because one or more lines are too long
186
dist_ts/mail/delivery/classes.delivery.system.d.ts
vendored
Normal file
186
dist_ts/mail/delivery/classes.delivery.system.d.ts
vendored
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
import { EventEmitter } from 'node:events';
|
||||||
|
import { UnifiedDeliveryQueue, type IQueueItem } from './classes.delivery.queue.js';
|
||||||
|
import type { UnifiedEmailServer } from '../routing/classes.unified.email.server.js';
|
||||||
|
/**
|
||||||
|
* Delivery status enumeration
|
||||||
|
*/
|
||||||
|
export declare enum DeliveryStatus {
|
||||||
|
PENDING = "pending",
|
||||||
|
DELIVERING = "delivering",
|
||||||
|
DELIVERED = "delivered",
|
||||||
|
DEFERRED = "deferred",
|
||||||
|
FAILED = "failed"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Delivery handler interface
|
||||||
|
*/
|
||||||
|
export interface IDeliveryHandler {
|
||||||
|
deliver(item: IQueueItem): Promise<any>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Delivery options
|
||||||
|
*/
|
||||||
|
export interface IMultiModeDeliveryOptions {
|
||||||
|
connectionPoolSize?: number;
|
||||||
|
socketTimeout?: number;
|
||||||
|
concurrentDeliveries?: number;
|
||||||
|
sendTimeout?: number;
|
||||||
|
verifyCertificates?: boolean;
|
||||||
|
tlsMinVersion?: string;
|
||||||
|
forwardHandler?: IDeliveryHandler;
|
||||||
|
deliveryHandler?: IDeliveryHandler;
|
||||||
|
processHandler?: IDeliveryHandler;
|
||||||
|
globalRateLimit?: number;
|
||||||
|
perPatternRateLimit?: Record<string, number>;
|
||||||
|
processBounces?: boolean;
|
||||||
|
bounceHandler?: {
|
||||||
|
processSmtpFailure: (recipient: string, smtpResponse: string, options: any) => Promise<any>;
|
||||||
|
};
|
||||||
|
onDeliveryStart?: (item: IQueueItem) => Promise<void>;
|
||||||
|
onDeliverySuccess?: (item: IQueueItem, result: any) => Promise<void>;
|
||||||
|
onDeliveryFailed?: (item: IQueueItem, error: string) => Promise<void>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Delivery system statistics
|
||||||
|
*/
|
||||||
|
export interface IDeliveryStats {
|
||||||
|
activeDeliveries: number;
|
||||||
|
totalSuccessful: number;
|
||||||
|
totalFailed: number;
|
||||||
|
avgDeliveryTime: number;
|
||||||
|
byMode: {
|
||||||
|
forward: {
|
||||||
|
successful: number;
|
||||||
|
failed: number;
|
||||||
|
};
|
||||||
|
mta: {
|
||||||
|
successful: number;
|
||||||
|
failed: number;
|
||||||
|
};
|
||||||
|
process: {
|
||||||
|
successful: number;
|
||||||
|
failed: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
rateLimiting: {
|
||||||
|
currentRate: number;
|
||||||
|
globalLimit: number;
|
||||||
|
throttled: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handles delivery for all email processing modes
|
||||||
|
*/
|
||||||
|
export declare class MultiModeDeliverySystem extends EventEmitter {
|
||||||
|
private queue;
|
||||||
|
private options;
|
||||||
|
private stats;
|
||||||
|
private deliveryTimes;
|
||||||
|
private activeDeliveries;
|
||||||
|
private running;
|
||||||
|
private throttled;
|
||||||
|
private rateLimitLastCheck;
|
||||||
|
private rateLimitCounter;
|
||||||
|
private emailServer?;
|
||||||
|
/**
|
||||||
|
* Create a new multi-mode delivery system
|
||||||
|
* @param queue Unified delivery queue
|
||||||
|
* @param options Delivery options
|
||||||
|
* @param emailServer Optional reference to unified email server for SmtpClient access
|
||||||
|
*/
|
||||||
|
constructor(queue: UnifiedDeliveryQueue, options: IMultiModeDeliveryOptions, emailServer?: UnifiedEmailServer);
|
||||||
|
/**
|
||||||
|
* Start the delivery system
|
||||||
|
*/
|
||||||
|
start(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Stop the delivery system
|
||||||
|
*/
|
||||||
|
stop(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Process ready items from the queue
|
||||||
|
* @param items Queue items ready for processing
|
||||||
|
*/
|
||||||
|
private processItems;
|
||||||
|
/**
|
||||||
|
* Deliver an item from the queue
|
||||||
|
* @param item Queue item to deliver
|
||||||
|
*/
|
||||||
|
private deliverItem;
|
||||||
|
/**
|
||||||
|
* Default handler for forward mode delivery
|
||||||
|
* @param item Queue item
|
||||||
|
*/
|
||||||
|
private handleForwardDelivery;
|
||||||
|
/**
|
||||||
|
* Legacy forward delivery using raw sockets (fallback)
|
||||||
|
* @param item Queue item
|
||||||
|
*/
|
||||||
|
private handleForwardDeliveryLegacy;
|
||||||
|
/**
|
||||||
|
* Complete the SMTP exchange after connection and initial setup
|
||||||
|
* @param socket Network socket
|
||||||
|
* @param email Email to send
|
||||||
|
* @param rule Domain rule
|
||||||
|
*/
|
||||||
|
private completeSMTPExchange;
|
||||||
|
/**
|
||||||
|
* Default handler for MTA mode delivery
|
||||||
|
* @param item Queue item
|
||||||
|
*/
|
||||||
|
private handleMtaDelivery;
|
||||||
|
/**
|
||||||
|
* Default handler for process mode delivery
|
||||||
|
* @param item Queue item
|
||||||
|
*/
|
||||||
|
private handleProcessDelivery;
|
||||||
|
/**
|
||||||
|
* Get file extension from filename
|
||||||
|
*/
|
||||||
|
private getFileExtension;
|
||||||
|
/**
|
||||||
|
* Apply DKIM signing to an email
|
||||||
|
*/
|
||||||
|
private applyDkimSigning;
|
||||||
|
/**
|
||||||
|
* Format email for SMTP transmission
|
||||||
|
* @param email Email to format
|
||||||
|
*/
|
||||||
|
private getFormattedEmail;
|
||||||
|
/**
|
||||||
|
* Send SMTP command and wait for response
|
||||||
|
* @param socket Socket connection
|
||||||
|
* @param command SMTP command to send
|
||||||
|
*/
|
||||||
|
private smtpCommand;
|
||||||
|
/**
|
||||||
|
* Send SMTP DATA command with content
|
||||||
|
* @param socket Socket connection
|
||||||
|
* @param data Email content to send
|
||||||
|
*/
|
||||||
|
private smtpData;
|
||||||
|
/**
|
||||||
|
* Upgrade socket to TLS
|
||||||
|
* @param socket Socket connection
|
||||||
|
* @param hostname Target hostname for TLS
|
||||||
|
*/
|
||||||
|
private upgradeTls;
|
||||||
|
/**
|
||||||
|
* Update delivery time statistics
|
||||||
|
*/
|
||||||
|
private updateDeliveryTimeStats;
|
||||||
|
/**
|
||||||
|
* Check if rate limit is exceeded
|
||||||
|
* @returns True if rate limited, false otherwise
|
||||||
|
*/
|
||||||
|
private checkRateLimit;
|
||||||
|
/**
|
||||||
|
* Update delivery options
|
||||||
|
* @param options New options
|
||||||
|
*/
|
||||||
|
updateOptions(options: Partial<IMultiModeDeliveryOptions>): void;
|
||||||
|
/**
|
||||||
|
* Get delivery statistics
|
||||||
|
*/
|
||||||
|
getStats(): IDeliveryStats;
|
||||||
|
}
|
||||||
846
dist_ts/mail/delivery/classes.delivery.system.js
Normal file
846
dist_ts/mail/delivery/classes.delivery.system.js
Normal file
File diff suppressed because one or more lines are too long
84
dist_ts/mail/delivery/classes.emailsendjob.d.ts
vendored
Normal file
84
dist_ts/mail/delivery/classes.emailsendjob.d.ts
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import * as plugins from '../../plugins.js';
|
||||||
|
import { Email } from '../core/classes.email.js';
|
||||||
|
import type { UnifiedEmailServer } from '../routing/classes.unified.email.server.js';
|
||||||
|
export interface IEmailSendOptions {
|
||||||
|
maxRetries?: number;
|
||||||
|
retryDelay?: number;
|
||||||
|
connectionTimeout?: number;
|
||||||
|
tlsOptions?: plugins.tls.ConnectionOptions;
|
||||||
|
debugMode?: boolean;
|
||||||
|
}
|
||||||
|
export declare enum DeliveryStatus {
|
||||||
|
PENDING = "pending",
|
||||||
|
SENDING = "sending",
|
||||||
|
DELIVERED = "delivered",
|
||||||
|
FAILED = "failed",
|
||||||
|
DEFERRED = "deferred"
|
||||||
|
}
|
||||||
|
export interface DeliveryInfo {
|
||||||
|
status: DeliveryStatus;
|
||||||
|
attempts: number;
|
||||||
|
error?: Error;
|
||||||
|
lastAttempt?: Date;
|
||||||
|
nextAttempt?: Date;
|
||||||
|
mxServer?: string;
|
||||||
|
deliveryTime?: Date;
|
||||||
|
logs: string[];
|
||||||
|
}
|
||||||
|
export declare class EmailSendJob {
|
||||||
|
emailServerRef: UnifiedEmailServer;
|
||||||
|
private email;
|
||||||
|
private mxServers;
|
||||||
|
private currentMxIndex;
|
||||||
|
private options;
|
||||||
|
deliveryInfo: DeliveryInfo;
|
||||||
|
constructor(emailServerRef: UnifiedEmailServer, emailArg: Email, options?: IEmailSendOptions);
|
||||||
|
/**
|
||||||
|
* Send the email to its recipients
|
||||||
|
*/
|
||||||
|
send(): Promise<DeliveryStatus>;
|
||||||
|
/**
|
||||||
|
* Validate the email before sending
|
||||||
|
*/
|
||||||
|
private validateEmail;
|
||||||
|
/**
|
||||||
|
* Resolve MX records for the recipient domain
|
||||||
|
*/
|
||||||
|
private resolveMxRecords;
|
||||||
|
/**
|
||||||
|
* Attempt to deliver the email with retries
|
||||||
|
*/
|
||||||
|
private attemptDelivery;
|
||||||
|
/**
|
||||||
|
* Connect to a specific MX server and send the email using SmtpClient
|
||||||
|
*/
|
||||||
|
private connectAndSend;
|
||||||
|
/**
|
||||||
|
* Record delivery event for monitoring
|
||||||
|
*/
|
||||||
|
private recordDeliveryEvent;
|
||||||
|
/**
|
||||||
|
* Check if an error represents a permanent failure
|
||||||
|
*/
|
||||||
|
private isPermanentFailure;
|
||||||
|
/**
|
||||||
|
* Resolve MX records for a domain
|
||||||
|
*/
|
||||||
|
private resolveMx;
|
||||||
|
/**
|
||||||
|
* Log a message with timestamp
|
||||||
|
*/
|
||||||
|
private log;
|
||||||
|
/**
|
||||||
|
* Save successful email to storage
|
||||||
|
*/
|
||||||
|
private saveSuccess;
|
||||||
|
/**
|
||||||
|
* Save failed email to storage
|
||||||
|
*/
|
||||||
|
private saveFailed;
|
||||||
|
/**
|
||||||
|
* Delay for specified milliseconds
|
||||||
|
*/
|
||||||
|
private delay;
|
||||||
|
}
|
||||||
366
dist_ts/mail/delivery/classes.emailsendjob.js
Normal file
366
dist_ts/mail/delivery/classes.emailsendjob.js
Normal file
File diff suppressed because one or more lines are too long
18
dist_ts/mail/delivery/classes.emailsignjob.d.ts
vendored
Normal file
18
dist_ts/mail/delivery/classes.emailsignjob.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import type { UnifiedEmailServer } from '../routing/classes.unified.email.server.js';
|
||||||
|
interface Headers {
|
||||||
|
[key: string]: string;
|
||||||
|
}
|
||||||
|
interface IEmailSignJobOptions {
|
||||||
|
domain: string;
|
||||||
|
selector: string;
|
||||||
|
headers: Headers;
|
||||||
|
body: string;
|
||||||
|
}
|
||||||
|
export declare class EmailSignJob {
|
||||||
|
emailServerRef: UnifiedEmailServer;
|
||||||
|
jobOptions: IEmailSignJobOptions;
|
||||||
|
constructor(emailServerRef: UnifiedEmailServer, options: IEmailSignJobOptions);
|
||||||
|
loadPrivateKey(): Promise<string>;
|
||||||
|
getSignatureHeader(emailMessage: string): Promise<string>;
|
||||||
|
}
|
||||||
|
export {};
|
||||||
26
dist_ts/mail/delivery/classes.emailsignjob.js
Normal file
26
dist_ts/mail/delivery/classes.emailsignjob.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import * as plugins from '../../plugins.js';
|
||||||
|
import { RustSecurityBridge } from '../../security/classes.rustsecuritybridge.js';
|
||||||
|
export class EmailSignJob {
|
||||||
|
emailServerRef;
|
||||||
|
jobOptions;
|
||||||
|
constructor(emailServerRef, options) {
|
||||||
|
this.emailServerRef = emailServerRef;
|
||||||
|
this.jobOptions = options;
|
||||||
|
}
|
||||||
|
async loadPrivateKey() {
|
||||||
|
const keyInfo = await this.emailServerRef.dkimCreator.readDKIMKeys(this.jobOptions.domain);
|
||||||
|
return keyInfo.privateKey;
|
||||||
|
}
|
||||||
|
async getSignatureHeader(emailMessage) {
|
||||||
|
const privateKey = await this.loadPrivateKey();
|
||||||
|
const bridge = RustSecurityBridge.getInstance();
|
||||||
|
const signResult = await bridge.signDkim({
|
||||||
|
rawMessage: emailMessage,
|
||||||
|
domain: this.jobOptions.domain,
|
||||||
|
selector: this.jobOptions.selector,
|
||||||
|
privateKey,
|
||||||
|
});
|
||||||
|
return signResult.header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lbWFpbHNpZ25qb2IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L2NsYXNzZXMuZW1haWxzaWduam9iLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFFNUMsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sOENBQThDLENBQUM7QUFhbEYsTUFBTSxPQUFPLFlBQVk7SUFDdkIsY0FBYyxDQUFxQjtJQUNuQyxVQUFVLENBQXVCO0lBRWpDLFlBQVksY0FBa0MsRUFBRSxPQUE2QjtRQUMzRSxJQUFJLENBQUMsY0FBYyxHQUFHLGNBQWMsQ0FBQztRQUNyQyxJQUFJLENBQUMsVUFBVSxHQUFHLE9BQU8sQ0FBQztJQUM1QixDQUFDO0lBRUQsS0FBSyxDQUFDLGNBQWM7UUFDbEIsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLFdBQVcsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMzRixPQUFPLE9BQU8sQ0FBQyxVQUFVLENBQUM7SUFDNUIsQ0FBQztJQUVNLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxZQUFvQjtRQUNsRCxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUMvQyxNQUFNLE1BQU0sR0FBRyxrQkFBa0IsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUNoRCxNQUFNLFVBQVUsR0FBRyxNQUFNLE1BQU0sQ0FBQyxRQUFRLENBQUM7WUFDdkMsVUFBVSxFQUFFLFlBQVk7WUFDeEIsTUFBTSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTTtZQUM5QixRQUFRLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxRQUFRO1lBQ2xDLFVBQVU7U0FDWCxDQUFDLENBQUM7UUFDSCxPQUFPLFVBQVUsQ0FBQyxNQUFNLENBQUM7SUFDM0IsQ0FBQztDQUNGIn0=
|
||||||
22
dist_ts/mail/delivery/classes.mta.config.d.ts
vendored
Normal file
22
dist_ts/mail/delivery/classes.mta.config.d.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import type { UnifiedEmailServer } from '../routing/classes.unified.email.server.js';
|
||||||
|
/**
|
||||||
|
* Configures email server storage settings
|
||||||
|
* @param emailServer Reference to the unified email server
|
||||||
|
* @param options Configuration options containing storage paths
|
||||||
|
*/
|
||||||
|
export declare function configureEmailStorage(emailServer: UnifiedEmailServer, options: any): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Configure email server with port and storage settings
|
||||||
|
* @param emailServer Reference to the unified email server
|
||||||
|
* @param config Configuration settings for email server
|
||||||
|
*/
|
||||||
|
export declare function configureEmailServer(emailServer: UnifiedEmailServer, config: {
|
||||||
|
ports?: number[];
|
||||||
|
hostname?: string;
|
||||||
|
tls?: {
|
||||||
|
certPath?: string;
|
||||||
|
keyPath?: string;
|
||||||
|
caPath?: string;
|
||||||
|
};
|
||||||
|
storagePath?: string;
|
||||||
|
}): Promise<boolean>;
|
||||||
51
dist_ts/mail/delivery/classes.mta.config.js
Normal file
51
dist_ts/mail/delivery/classes.mta.config.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import * as plugins from '../../plugins.js';
|
||||||
|
import * as paths from '../../paths.js';
|
||||||
|
/**
|
||||||
|
* Configures email server storage settings
|
||||||
|
* @param emailServer Reference to the unified email server
|
||||||
|
* @param options Configuration options containing storage paths
|
||||||
|
*/
|
||||||
|
export async function configureEmailStorage(emailServer, options) {
|
||||||
|
// Extract the receivedEmailsPath if available
|
||||||
|
if (options?.emailPortConfig?.receivedEmailsPath) {
|
||||||
|
const receivedEmailsPath = options.emailPortConfig.receivedEmailsPath;
|
||||||
|
// Ensure the directory exists
|
||||||
|
await plugins.smartfs.directory(receivedEmailsPath).recursive().create();
|
||||||
|
// Set path for received emails
|
||||||
|
if (emailServer) {
|
||||||
|
// Storage paths are now handled by the unified email server system
|
||||||
|
await plugins.smartfs.directory(paths.receivedEmailsDir).recursive().create();
|
||||||
|
console.log(`Configured email server to store received emails to: ${receivedEmailsPath}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Configure email server with port and storage settings
|
||||||
|
* @param emailServer Reference to the unified email server
|
||||||
|
* @param config Configuration settings for email server
|
||||||
|
*/
|
||||||
|
export async function configureEmailServer(emailServer, config) {
|
||||||
|
if (!emailServer) {
|
||||||
|
console.error('Email server not available');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Configure the email server with updated options
|
||||||
|
const serverOptions = {
|
||||||
|
ports: config.ports || [25, 587, 465],
|
||||||
|
hostname: config.hostname || 'localhost',
|
||||||
|
tls: config.tls
|
||||||
|
};
|
||||||
|
// Update the email server options
|
||||||
|
emailServer.updateOptions(serverOptions);
|
||||||
|
console.log(`Configured email server on ports ${serverOptions.ports.join(', ')}`);
|
||||||
|
// Set up storage path if provided
|
||||||
|
if (config.storagePath) {
|
||||||
|
await configureEmailStorage(emailServer, {
|
||||||
|
emailPortConfig: {
|
||||||
|
receivedEmailsPath: config.storagePath
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tdGEuY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvbWFpbC9kZWxpdmVyeS9jbGFzc2VzLm10YS5jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUM1QyxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBR3hDOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLHFCQUFxQixDQUFDLFdBQStCLEVBQUUsT0FBWTtJQUN2Riw4Q0FBOEM7SUFDOUMsSUFBSSxPQUFPLEVBQUUsZUFBZSxFQUFFLGtCQUFrQixFQUFFLENBQUM7UUFDakQsTUFBTSxrQkFBa0IsR0FBRyxPQUFPLENBQUMsZUFBZSxDQUFDLGtCQUFrQixDQUFDO1FBRXRFLDhCQUE4QjtRQUM5QixNQUFNLE9BQU8sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLGtCQUFrQixDQUFDLENBQUMsU0FBUyxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUM7UUFFekUsK0JBQStCO1FBQy9CLElBQUksV0FBVyxFQUFFLENBQUM7WUFDaEIsbUVBQW1FO1lBQ25FLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLGlCQUFpQixDQUFDLENBQUMsU0FBUyxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUM7WUFFOUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyx3REFBd0Qsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDO1FBQzVGLENBQUM7SUFDSCxDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLG9CQUFvQixDQUN4QyxXQUErQixFQUMvQixNQVNDO0lBRUQsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2pCLE9BQU8sQ0FBQyxLQUFLLENBQUMsNEJBQTRCLENBQUMsQ0FBQztRQUM1QyxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCxrREFBa0Q7SUFDbEQsTUFBTSxhQUFhLEdBQUc7UUFDcEIsS0FBSyxFQUFFLE1BQU0sQ0FBQyxLQUFLLElBQUksQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQztRQUNyQyxRQUFRLEVBQUUsTUFBTSxDQUFDLFFBQVEsSUFBSSxXQUFXO1FBQ3hDLEdBQUcsRUFBRSxNQUFNLENBQUMsR0FBRztLQUNoQixDQUFDO0lBRUYsa0NBQWtDO0lBQ2xDLFdBQVcsQ0FBQyxhQUFhLENBQUMsYUFBYSxDQUFDLENBQUM7SUFFekMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQ0FBb0MsYUFBYSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBRWxGLGtDQUFrQztJQUNsQyxJQUFJLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUN2QixNQUFNLHFCQUFxQixDQUFDLFdBQVcsRUFBRTtZQUN2QyxlQUFlLEVBQUU7Z0JBQ2Ysa0JBQWtCLEVBQUUsTUFBTSxDQUFDLFdBQVc7YUFDdkM7U0FDRixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDIn0=
|
||||||
108
dist_ts/mail/delivery/classes.ratelimiter.d.ts
vendored
Normal file
108
dist_ts/mail/delivery/classes.ratelimiter.d.ts
vendored
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/**
|
||||||
|
* Configuration options for rate limiter
|
||||||
|
*/
|
||||||
|
export interface IRateLimitConfig {
|
||||||
|
/** Maximum tokens per period */
|
||||||
|
maxPerPeriod: number;
|
||||||
|
/** Time period in milliseconds */
|
||||||
|
periodMs: number;
|
||||||
|
/** Whether to apply per domain/key (vs globally) */
|
||||||
|
perKey: boolean;
|
||||||
|
/** Initial token count (defaults to max) */
|
||||||
|
initialTokens?: number;
|
||||||
|
/** Grace tokens to allow occasional bursts */
|
||||||
|
burstTokens?: number;
|
||||||
|
/** Apply global limit in addition to per-key limits */
|
||||||
|
useGlobalLimit?: boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Rate limiter using token bucket algorithm
|
||||||
|
* Provides more sophisticated rate limiting with burst handling
|
||||||
|
*/
|
||||||
|
export declare class RateLimiter {
|
||||||
|
/** Rate limit configuration */
|
||||||
|
private config;
|
||||||
|
/** Token buckets per key */
|
||||||
|
private buckets;
|
||||||
|
/** Global bucket for non-keyed rate limiting */
|
||||||
|
private globalBucket;
|
||||||
|
/**
|
||||||
|
* Create a new rate limiter
|
||||||
|
* @param config Rate limiter configuration
|
||||||
|
*/
|
||||||
|
constructor(config: IRateLimitConfig);
|
||||||
|
/**
|
||||||
|
* Check if a request is allowed under rate limits
|
||||||
|
* @param key Key to check rate limit for (e.g. domain, user, IP)
|
||||||
|
* @param cost Token cost (defaults to 1)
|
||||||
|
* @returns Whether the request is allowed
|
||||||
|
*/
|
||||||
|
isAllowed(key?: string, cost?: number): boolean;
|
||||||
|
/**
|
||||||
|
* Check if a bucket has enough tokens and consume them
|
||||||
|
* @param bucket The token bucket to check
|
||||||
|
* @param cost Token cost
|
||||||
|
* @returns Whether tokens were consumed
|
||||||
|
*/
|
||||||
|
private checkBucket;
|
||||||
|
/**
|
||||||
|
* Consume tokens for a request (if available)
|
||||||
|
* @param key Key to consume tokens for
|
||||||
|
* @param cost Token cost (defaults to 1)
|
||||||
|
* @returns Whether tokens were consumed
|
||||||
|
*/
|
||||||
|
consume(key?: string, cost?: number): boolean;
|
||||||
|
/**
|
||||||
|
* Get the remaining tokens for a key
|
||||||
|
* @param key Key to check
|
||||||
|
* @returns Number of remaining tokens
|
||||||
|
*/
|
||||||
|
getRemainingTokens(key?: string): number;
|
||||||
|
/**
|
||||||
|
* Get stats for a specific key
|
||||||
|
* @param key Key to get stats for
|
||||||
|
* @returns Rate limit statistics
|
||||||
|
*/
|
||||||
|
getStats(key?: string): {
|
||||||
|
remaining: number;
|
||||||
|
limit: number;
|
||||||
|
resetIn: number;
|
||||||
|
allowed: number;
|
||||||
|
denied: number;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Get or create a token bucket for a key
|
||||||
|
* @param key The rate limit key
|
||||||
|
* @returns Token bucket
|
||||||
|
*/
|
||||||
|
private getBucket;
|
||||||
|
/**
|
||||||
|
* Refill tokens in a bucket based on elapsed time
|
||||||
|
* @param bucket Token bucket to refill
|
||||||
|
*/
|
||||||
|
private refillBucket;
|
||||||
|
/**
|
||||||
|
* Reset rate limits for a specific key
|
||||||
|
* @param key Key to reset
|
||||||
|
*/
|
||||||
|
reset(key?: string): void;
|
||||||
|
/**
|
||||||
|
* Reset all rate limiters
|
||||||
|
*/
|
||||||
|
resetAll(): void;
|
||||||
|
/**
|
||||||
|
* Cleanup old buckets to prevent memory leaks
|
||||||
|
* @param maxAge Maximum age in milliseconds
|
||||||
|
*/
|
||||||
|
cleanup(maxAge?: number): void;
|
||||||
|
/**
|
||||||
|
* Record an error for a key (e.g., IP address) and determine if blocking is needed
|
||||||
|
* RFC 5321 Section 4.5.4.1 suggests limiting errors to prevent abuse
|
||||||
|
*
|
||||||
|
* @param key Key to record error for (typically an IP address)
|
||||||
|
* @param errorWindow Time window for error tracking in ms (default: 5 minutes)
|
||||||
|
* @param errorThreshold Maximum errors before blocking (default: 10)
|
||||||
|
* @returns true if the key should be blocked due to excessive errors
|
||||||
|
*/
|
||||||
|
recordError(key: string, errorWindow?: number, errorThreshold?: number): boolean;
|
||||||
|
}
|
||||||
241
dist_ts/mail/delivery/classes.ratelimiter.js
Normal file
241
dist_ts/mail/delivery/classes.ratelimiter.js
Normal file
File diff suppressed because one or more lines are too long
275
dist_ts/mail/delivery/classes.smtp.client.legacy.d.ts
vendored
Normal file
275
dist_ts/mail/delivery/classes.smtp.client.legacy.d.ts
vendored
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
import { Email } from '../core/classes.email.js';
|
||||||
|
import type { EmailProcessingMode } from './interfaces.js';
|
||||||
|
/**
|
||||||
|
* SMTP client connection options
|
||||||
|
*/
|
||||||
|
export type ISmtpClientOptions = {
|
||||||
|
/**
|
||||||
|
* Hostname of the SMTP server
|
||||||
|
*/
|
||||||
|
host: string;
|
||||||
|
/**
|
||||||
|
* Port to connect to
|
||||||
|
*/
|
||||||
|
port: number;
|
||||||
|
/**
|
||||||
|
* Whether to use TLS for the connection
|
||||||
|
*/
|
||||||
|
secure?: boolean;
|
||||||
|
/**
|
||||||
|
* Connection timeout in milliseconds
|
||||||
|
*/
|
||||||
|
connectionTimeout?: number;
|
||||||
|
/**
|
||||||
|
* Socket timeout in milliseconds
|
||||||
|
*/
|
||||||
|
socketTimeout?: number;
|
||||||
|
/**
|
||||||
|
* Command timeout in milliseconds
|
||||||
|
*/
|
||||||
|
commandTimeout?: number;
|
||||||
|
/**
|
||||||
|
* TLS options
|
||||||
|
*/
|
||||||
|
tls?: {
|
||||||
|
/**
|
||||||
|
* Whether to verify certificates
|
||||||
|
*/
|
||||||
|
rejectUnauthorized?: boolean;
|
||||||
|
/**
|
||||||
|
* Minimum TLS version
|
||||||
|
*/
|
||||||
|
minVersion?: string;
|
||||||
|
/**
|
||||||
|
* CA certificate path
|
||||||
|
*/
|
||||||
|
ca?: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Authentication options
|
||||||
|
*/
|
||||||
|
auth?: {
|
||||||
|
/**
|
||||||
|
* Authentication user
|
||||||
|
*/
|
||||||
|
user: string;
|
||||||
|
/**
|
||||||
|
* Authentication password
|
||||||
|
*/
|
||||||
|
pass: string;
|
||||||
|
/**
|
||||||
|
* Authentication method
|
||||||
|
*/
|
||||||
|
method?: 'PLAIN' | 'LOGIN' | 'OAUTH2';
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Domain name for EHLO
|
||||||
|
*/
|
||||||
|
domain?: string;
|
||||||
|
/**
|
||||||
|
* DKIM options for signing outgoing emails
|
||||||
|
*/
|
||||||
|
dkim?: {
|
||||||
|
/**
|
||||||
|
* Whether to sign emails with DKIM
|
||||||
|
*/
|
||||||
|
enabled: boolean;
|
||||||
|
/**
|
||||||
|
* Domain name for DKIM
|
||||||
|
*/
|
||||||
|
domain: string;
|
||||||
|
/**
|
||||||
|
* Selector for DKIM
|
||||||
|
*/
|
||||||
|
selector: string;
|
||||||
|
/**
|
||||||
|
* Private key for DKIM signing
|
||||||
|
*/
|
||||||
|
privateKey: string;
|
||||||
|
/**
|
||||||
|
* Headers to sign
|
||||||
|
*/
|
||||||
|
headers?: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* SMTP delivery result
|
||||||
|
*/
|
||||||
|
export type ISmtpDeliveryResult = {
|
||||||
|
/**
|
||||||
|
* Whether the delivery was successful
|
||||||
|
*/
|
||||||
|
success: boolean;
|
||||||
|
/**
|
||||||
|
* Message ID if successful
|
||||||
|
*/
|
||||||
|
messageId?: string;
|
||||||
|
/**
|
||||||
|
* Error message if failed
|
||||||
|
*/
|
||||||
|
error?: string;
|
||||||
|
/**
|
||||||
|
* SMTP response code
|
||||||
|
*/
|
||||||
|
responseCode?: string;
|
||||||
|
/**
|
||||||
|
* Recipients successfully delivered to
|
||||||
|
*/
|
||||||
|
acceptedRecipients: string[];
|
||||||
|
/**
|
||||||
|
* Recipients rejected during delivery
|
||||||
|
*/
|
||||||
|
rejectedRecipients: string[];
|
||||||
|
/**
|
||||||
|
* Server response
|
||||||
|
*/
|
||||||
|
response?: string;
|
||||||
|
/**
|
||||||
|
* Timestamp of the delivery attempt
|
||||||
|
*/
|
||||||
|
timestamp: number;
|
||||||
|
/**
|
||||||
|
* Whether DKIM signing was applied
|
||||||
|
*/
|
||||||
|
dkimSigned?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether this was a TLS secured delivery
|
||||||
|
*/
|
||||||
|
secure?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether authentication was used
|
||||||
|
*/
|
||||||
|
authenticated?: boolean;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* SMTP client for sending emails to remote mail servers
|
||||||
|
*/
|
||||||
|
export declare class SmtpClient {
|
||||||
|
private options;
|
||||||
|
private connected;
|
||||||
|
private socket?;
|
||||||
|
private supportedExtensions;
|
||||||
|
/**
|
||||||
|
* Create a new SMTP client instance
|
||||||
|
* @param options SMTP client connection options
|
||||||
|
*/
|
||||||
|
constructor(options: ISmtpClientOptions);
|
||||||
|
/**
|
||||||
|
* Connect to the SMTP server
|
||||||
|
*/
|
||||||
|
connect(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Send EHLO command to the server
|
||||||
|
*/
|
||||||
|
private sendEhlo;
|
||||||
|
/**
|
||||||
|
* Start TLS negotiation
|
||||||
|
*/
|
||||||
|
private startTls;
|
||||||
|
/**
|
||||||
|
* Upgrade socket to TLS
|
||||||
|
* @param socket Original socket
|
||||||
|
*/
|
||||||
|
private upgradeTls;
|
||||||
|
/**
|
||||||
|
* Authenticate with the server
|
||||||
|
*/
|
||||||
|
private authenticate;
|
||||||
|
/**
|
||||||
|
* Authenticate using PLAIN method
|
||||||
|
* @param user Username
|
||||||
|
* @param pass Password
|
||||||
|
*/
|
||||||
|
private authPlain;
|
||||||
|
/**
|
||||||
|
* Authenticate using LOGIN method
|
||||||
|
* @param user Username
|
||||||
|
* @param pass Password
|
||||||
|
*/
|
||||||
|
private authLogin;
|
||||||
|
/**
|
||||||
|
* Authenticate using OAuth2 method
|
||||||
|
* @param user Username
|
||||||
|
* @param token OAuth2 token
|
||||||
|
*/
|
||||||
|
private authOAuth2;
|
||||||
|
/**
|
||||||
|
* Send an email through the SMTP client
|
||||||
|
* @param email Email to send
|
||||||
|
* @param processingMode Optional processing mode
|
||||||
|
*/
|
||||||
|
sendMail(email: Email, processingMode?: EmailProcessingMode): Promise<ISmtpDeliveryResult>;
|
||||||
|
/**
|
||||||
|
* Apply DKIM signature to email
|
||||||
|
* @param email Email to sign
|
||||||
|
*/
|
||||||
|
private applyDkimSignature;
|
||||||
|
/**
|
||||||
|
* Format email for SMTP transmission
|
||||||
|
* @param email Email to format
|
||||||
|
*/
|
||||||
|
private getFormattedEmail;
|
||||||
|
/**
|
||||||
|
* Get size of email in bytes
|
||||||
|
* @param email Email to measure
|
||||||
|
*/
|
||||||
|
private getEmailSize;
|
||||||
|
/**
|
||||||
|
* Send SMTP command and wait for response
|
||||||
|
* @param command SMTP command to send
|
||||||
|
*/
|
||||||
|
private commandQueue;
|
||||||
|
private processingCommands;
|
||||||
|
private supportsPipelining;
|
||||||
|
/**
|
||||||
|
* Send an SMTP command and wait for response
|
||||||
|
* @param command SMTP command to send
|
||||||
|
* @param allowPipelining Whether this command can be pipelined
|
||||||
|
*/
|
||||||
|
private sendCommand;
|
||||||
|
/**
|
||||||
|
* Process the command queue - either one by one or pipelined if supported
|
||||||
|
*/
|
||||||
|
private processCommandQueue;
|
||||||
|
/**
|
||||||
|
* Process the next command in the queue (non-pipelined mode)
|
||||||
|
*/
|
||||||
|
private processNextCommand;
|
||||||
|
/**
|
||||||
|
* Process responses for pipelined commands
|
||||||
|
*/
|
||||||
|
private processResponses;
|
||||||
|
/**
|
||||||
|
* Read response from the server
|
||||||
|
*/
|
||||||
|
private readResponse;
|
||||||
|
/**
|
||||||
|
* Check if the response is complete
|
||||||
|
* @param response Response to check
|
||||||
|
*/
|
||||||
|
private isCompleteResponse;
|
||||||
|
/**
|
||||||
|
* Check if the response is an error
|
||||||
|
* @param response Response to check
|
||||||
|
*/
|
||||||
|
private isErrorResponse;
|
||||||
|
/**
|
||||||
|
* Create appropriate error from response
|
||||||
|
* @param response Error response
|
||||||
|
* @param code SMTP status code
|
||||||
|
*/
|
||||||
|
private createErrorFromResponse;
|
||||||
|
/**
|
||||||
|
* Close the connection to the server
|
||||||
|
*/
|
||||||
|
close(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Checks if the connection is active
|
||||||
|
*/
|
||||||
|
isConnected(): boolean;
|
||||||
|
/**
|
||||||
|
* Update SMTP client options
|
||||||
|
* @param options New options
|
||||||
|
*/
|
||||||
|
updateOptions(options: Partial<ISmtpClientOptions>): void;
|
||||||
|
}
|
||||||
969
dist_ts/mail/delivery/classes.smtp.client.legacy.js
Normal file
969
dist_ts/mail/delivery/classes.smtp.client.legacy.js
Normal file
File diff suppressed because one or more lines are too long
200
dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts
vendored
Normal file
200
dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts
vendored
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
import { EventEmitter } from 'node:events';
|
||||||
|
/**
|
||||||
|
* Interface for rate limit configuration
|
||||||
|
*/
|
||||||
|
export interface IRateLimitConfig {
|
||||||
|
maxMessagesPerMinute?: number;
|
||||||
|
maxRecipientsPerMessage?: number;
|
||||||
|
maxConnectionsPerIP?: number;
|
||||||
|
maxErrorsPerIP?: number;
|
||||||
|
maxAuthFailuresPerIP?: number;
|
||||||
|
blockDuration?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Interface for hierarchical rate limits
|
||||||
|
*/
|
||||||
|
export interface IHierarchicalRateLimits {
|
||||||
|
global: IRateLimitConfig;
|
||||||
|
patterns?: Record<string, IRateLimitConfig>;
|
||||||
|
ips?: Record<string, IRateLimitConfig>;
|
||||||
|
domains?: Record<string, IRateLimitConfig>;
|
||||||
|
blocks?: Record<string, number>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Rate limiter statistics
|
||||||
|
*/
|
||||||
|
export interface IRateLimiterStats {
|
||||||
|
activeCounters: number;
|
||||||
|
totalBlocked: number;
|
||||||
|
currentlyBlocked: number;
|
||||||
|
byPattern: Record<string, {
|
||||||
|
messagesPerMinute: number;
|
||||||
|
totalMessages: number;
|
||||||
|
totalBlocked: number;
|
||||||
|
}>;
|
||||||
|
byIp: Record<string, {
|
||||||
|
messagesPerMinute: number;
|
||||||
|
totalMessages: number;
|
||||||
|
totalBlocked: number;
|
||||||
|
connections: number;
|
||||||
|
errors: number;
|
||||||
|
authFailures: number;
|
||||||
|
blocked: boolean;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Result of a rate limit check
|
||||||
|
*/
|
||||||
|
export interface IRateLimitResult {
|
||||||
|
allowed: boolean;
|
||||||
|
reason?: string;
|
||||||
|
limit?: number;
|
||||||
|
current?: number;
|
||||||
|
resetIn?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Unified rate limiter for all email processing modes
|
||||||
|
*/
|
||||||
|
export declare class UnifiedRateLimiter extends EventEmitter {
|
||||||
|
private config;
|
||||||
|
private counters;
|
||||||
|
private patternCounters;
|
||||||
|
private ipCounters;
|
||||||
|
private domainCounters;
|
||||||
|
private cleanupInterval?;
|
||||||
|
private stats;
|
||||||
|
/**
|
||||||
|
* Create a new unified rate limiter
|
||||||
|
* @param config Rate limit configuration
|
||||||
|
*/
|
||||||
|
constructor(config: IHierarchicalRateLimits);
|
||||||
|
/**
|
||||||
|
* Start the cleanup interval
|
||||||
|
*/
|
||||||
|
private startCleanupInterval;
|
||||||
|
/**
|
||||||
|
* Stop the cleanup interval
|
||||||
|
*/
|
||||||
|
stop(): void;
|
||||||
|
/**
|
||||||
|
* Destroy the rate limiter and clean up all resources
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
/**
|
||||||
|
* Clean up expired counters and blocks
|
||||||
|
*/
|
||||||
|
private cleanup;
|
||||||
|
/**
|
||||||
|
* Check if a message is allowed by rate limits
|
||||||
|
* @param email Email address
|
||||||
|
* @param ip IP address
|
||||||
|
* @param recipients Number of recipients
|
||||||
|
* @param pattern Matched pattern
|
||||||
|
* @param domain Domain name for domain-specific limits
|
||||||
|
* @returns Result of rate limit check
|
||||||
|
*/
|
||||||
|
checkMessageLimit(email: string, ip: string, recipients: number, pattern?: string, domain?: string): IRateLimitResult;
|
||||||
|
/**
|
||||||
|
* Check global message rate limit
|
||||||
|
* @param email Email address
|
||||||
|
*/
|
||||||
|
private checkGlobalMessageLimit;
|
||||||
|
/**
|
||||||
|
* Check pattern-specific message rate limit
|
||||||
|
* @param pattern Pattern to check
|
||||||
|
*/
|
||||||
|
private checkPatternMessageLimit;
|
||||||
|
/**
|
||||||
|
* Check domain-specific message rate limit
|
||||||
|
* @param domain Domain to check
|
||||||
|
*/
|
||||||
|
private checkDomainMessageLimit;
|
||||||
|
/**
|
||||||
|
* Check IP-specific message rate limit
|
||||||
|
* @param ip IP address
|
||||||
|
*/
|
||||||
|
private checkIpMessageLimit;
|
||||||
|
/**
|
||||||
|
* Check recipient limit
|
||||||
|
* @param email Email address
|
||||||
|
* @param recipients Number of recipients
|
||||||
|
* @param pattern Matched pattern
|
||||||
|
* @param domain Domain name
|
||||||
|
*/
|
||||||
|
private checkRecipientLimit;
|
||||||
|
/**
|
||||||
|
* Record a connection from an IP
|
||||||
|
* @param ip IP address
|
||||||
|
* @returns Result of rate limit check
|
||||||
|
*/
|
||||||
|
recordConnection(ip: string): IRateLimitResult;
|
||||||
|
/**
|
||||||
|
* Record an error from an IP
|
||||||
|
* @param ip IP address
|
||||||
|
* @returns True if IP should be blocked
|
||||||
|
*/
|
||||||
|
recordError(ip: string): boolean;
|
||||||
|
/**
|
||||||
|
* Record an authentication failure from an IP
|
||||||
|
* @param ip IP address
|
||||||
|
* @returns True if IP should be blocked
|
||||||
|
*/
|
||||||
|
recordAuthFailure(ip: string): boolean;
|
||||||
|
/**
|
||||||
|
* Block an IP address
|
||||||
|
* @param ip IP address to block
|
||||||
|
* @param duration Override the default block duration (milliseconds)
|
||||||
|
*/
|
||||||
|
blockIp(ip: string, duration?: number): void;
|
||||||
|
/**
|
||||||
|
* Unblock an IP address
|
||||||
|
* @param ip IP address to unblock
|
||||||
|
*/
|
||||||
|
unblockIp(ip: string): void;
|
||||||
|
/**
|
||||||
|
* Check if an IP is blocked
|
||||||
|
* @param ip IP address to check
|
||||||
|
*/
|
||||||
|
isIpBlocked(ip: string): boolean;
|
||||||
|
/**
|
||||||
|
* Get the time until a block is released
|
||||||
|
* @param ip IP address
|
||||||
|
* @returns Milliseconds until release or 0 if not blocked
|
||||||
|
*/
|
||||||
|
getBlockReleaseTime(ip: string): number;
|
||||||
|
/**
|
||||||
|
* Update rate limiter statistics
|
||||||
|
*/
|
||||||
|
private updateStats;
|
||||||
|
/**
|
||||||
|
* Get rate limiter statistics
|
||||||
|
*/
|
||||||
|
getStats(): IRateLimiterStats;
|
||||||
|
/**
|
||||||
|
* Update rate limiter configuration
|
||||||
|
* @param config New configuration
|
||||||
|
*/
|
||||||
|
updateConfig(config: Partial<IHierarchicalRateLimits>): void;
|
||||||
|
/**
|
||||||
|
* Get configuration for debugging
|
||||||
|
*/
|
||||||
|
getConfig(): IHierarchicalRateLimits;
|
||||||
|
/**
|
||||||
|
* Apply domain-specific rate limits
|
||||||
|
* Merges domain limits with existing configuration
|
||||||
|
* @param domain Domain name
|
||||||
|
* @param limits Rate limit configuration for the domain
|
||||||
|
*/
|
||||||
|
applyDomainLimits(domain: string, limits: IRateLimitConfig): void;
|
||||||
|
/**
|
||||||
|
* Remove domain-specific rate limits
|
||||||
|
* @param domain Domain name
|
||||||
|
*/
|
||||||
|
removeDomainLimits(domain: string): void;
|
||||||
|
/**
|
||||||
|
* Get domain-specific rate limits
|
||||||
|
* @param domain Domain name
|
||||||
|
* @returns Domain rate limit config or undefined
|
||||||
|
*/
|
||||||
|
getDomainLimits(domain: string): IRateLimitConfig | undefined;
|
||||||
|
}
|
||||||
820
dist_ts/mail/delivery/classes.unified.rate.limiter.js
Normal file
820
dist_ts/mail/delivery/classes.unified.rate.limiter.js
Normal file
File diff suppressed because one or more lines are too long
11
dist_ts/mail/delivery/index.d.ts
vendored
Normal file
11
dist_ts/mail/delivery/index.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export * from './classes.emailsignjob.js';
|
||||||
|
export * from './classes.delivery.queue.js';
|
||||||
|
export * from './classes.delivery.system.js';
|
||||||
|
export { EmailSendJob } from './classes.emailsendjob.js';
|
||||||
|
export { DeliveryStatus } from './classes.delivery.system.js';
|
||||||
|
export { RateLimiter } from './classes.ratelimiter.js';
|
||||||
|
export type { IRateLimitConfig } from './classes.ratelimiter.js';
|
||||||
|
export * from './classes.unified.rate.limiter.js';
|
||||||
|
export * from './classes.mta.config.js';
|
||||||
|
import * as smtpClientMod from './smtpclient/index.js';
|
||||||
|
export { smtpClientMod };
|
||||||
17
dist_ts/mail/delivery/index.js
Normal file
17
dist_ts/mail/delivery/index.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Email delivery components
|
||||||
|
export * from './classes.emailsignjob.js';
|
||||||
|
export * from './classes.delivery.queue.js';
|
||||||
|
export * from './classes.delivery.system.js';
|
||||||
|
// Handle exports with naming conflicts
|
||||||
|
export { EmailSendJob } from './classes.emailsendjob.js';
|
||||||
|
export { DeliveryStatus } from './classes.delivery.system.js';
|
||||||
|
// Rate limiter exports - fix naming conflict
|
||||||
|
export { RateLimiter } from './classes.ratelimiter.js';
|
||||||
|
// Unified rate limiter
|
||||||
|
export * from './classes.unified.rate.limiter.js';
|
||||||
|
// SMTP client and configuration
|
||||||
|
export * from './classes.mta.config.js';
|
||||||
|
// Import and export SMTP modules as namespaces to avoid conflicts
|
||||||
|
import * as smtpClientMod from './smtpclient/index.js';
|
||||||
|
export { smtpClientMod };
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDRCQUE0QjtBQUM1QixjQUFjLDJCQUEyQixDQUFDO0FBQzFDLGNBQWMsNkJBQTZCLENBQUM7QUFDNUMsY0FBYyw4QkFBOEIsQ0FBQztBQUU3Qyx1Q0FBdUM7QUFDdkMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3pELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUU5RCw2Q0FBNkM7QUFDN0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBR3ZELHVCQUF1QjtBQUN2QixjQUFjLG1DQUFtQyxDQUFDO0FBRWxELGdDQUFnQztBQUNoQyxjQUFjLHlCQUF5QixDQUFDO0FBRXhDLGtFQUFrRTtBQUNsRSxPQUFPLEtBQUssYUFBYSxNQUFNLHVCQUF1QixDQUFDO0FBRXZELE9BQU8sRUFBRSxhQUFhLEVBQUUsQ0FBQyJ9
|
||||||
243
dist_ts/mail/delivery/interfaces.d.ts
vendored
Normal file
243
dist_ts/mail/delivery/interfaces.d.ts
vendored
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
/**
|
||||||
|
* SMTP and email delivery interface definitions
|
||||||
|
*/
|
||||||
|
import type { Email } from '../core/classes.email.js';
|
||||||
|
/**
|
||||||
|
* SMTP session state enumeration
|
||||||
|
*/
|
||||||
|
export declare enum SmtpState {
|
||||||
|
GREETING = "GREETING",
|
||||||
|
AFTER_EHLO = "AFTER_EHLO",
|
||||||
|
MAIL_FROM = "MAIL_FROM",
|
||||||
|
RCPT_TO = "RCPT_TO",
|
||||||
|
DATA = "DATA",
|
||||||
|
DATA_RECEIVING = "DATA_RECEIVING",
|
||||||
|
FINISHED = "FINISHED"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Email processing mode type
|
||||||
|
*/
|
||||||
|
export type EmailProcessingMode = 'forward' | 'mta' | 'process';
|
||||||
|
/**
|
||||||
|
* Envelope recipient information
|
||||||
|
*/
|
||||||
|
export interface IEnvelopeRecipient {
|
||||||
|
/**
|
||||||
|
* Email address of the recipient
|
||||||
|
*/
|
||||||
|
address: string;
|
||||||
|
/**
|
||||||
|
* Additional SMTP command arguments
|
||||||
|
*/
|
||||||
|
args: Record<string, string>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP session envelope information
|
||||||
|
*/
|
||||||
|
export interface ISmtpEnvelope {
|
||||||
|
/**
|
||||||
|
* Envelope sender (MAIL FROM) information
|
||||||
|
*/
|
||||||
|
mailFrom: {
|
||||||
|
/**
|
||||||
|
* Email address of the sender
|
||||||
|
*/
|
||||||
|
address: string;
|
||||||
|
/**
|
||||||
|
* Additional SMTP command arguments
|
||||||
|
*/
|
||||||
|
args: Record<string, string>;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Envelope recipients (RCPT TO) information
|
||||||
|
*/
|
||||||
|
rcptTo: IEnvelopeRecipient[];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP Session interface - represents an active SMTP connection
|
||||||
|
*/
|
||||||
|
export interface ISmtpSession {
|
||||||
|
/**
|
||||||
|
* Unique session identifier
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* Current session state in the SMTP conversation
|
||||||
|
*/
|
||||||
|
state: SmtpState;
|
||||||
|
/**
|
||||||
|
* Hostname provided by the client in EHLO/HELO command
|
||||||
|
*/
|
||||||
|
clientHostname: string;
|
||||||
|
/**
|
||||||
|
* MAIL FROM email address (legacy format)
|
||||||
|
*/
|
||||||
|
mailFrom: string;
|
||||||
|
/**
|
||||||
|
* RCPT TO email addresses (legacy format)
|
||||||
|
*/
|
||||||
|
rcptTo: string[];
|
||||||
|
/**
|
||||||
|
* Raw email data being received
|
||||||
|
*/
|
||||||
|
emailData: string;
|
||||||
|
/**
|
||||||
|
* Chunks of email data for more efficient buffer management
|
||||||
|
*/
|
||||||
|
emailDataChunks?: string[];
|
||||||
|
/**
|
||||||
|
* Whether the connection is using TLS
|
||||||
|
*/
|
||||||
|
useTLS: boolean;
|
||||||
|
/**
|
||||||
|
* Whether the connection has ended
|
||||||
|
*/
|
||||||
|
connectionEnded: boolean;
|
||||||
|
/**
|
||||||
|
* Remote IP address of the client
|
||||||
|
*/
|
||||||
|
remoteAddress: string;
|
||||||
|
/**
|
||||||
|
* Whether the connection is secure (TLS)
|
||||||
|
*/
|
||||||
|
secure: boolean;
|
||||||
|
/**
|
||||||
|
* Whether the client has been authenticated
|
||||||
|
*/
|
||||||
|
authenticated: boolean;
|
||||||
|
/**
|
||||||
|
* SMTP envelope information (structured format)
|
||||||
|
*/
|
||||||
|
envelope: ISmtpEnvelope;
|
||||||
|
/**
|
||||||
|
* Email processing mode to use for this session
|
||||||
|
*/
|
||||||
|
processingMode?: EmailProcessingMode;
|
||||||
|
/**
|
||||||
|
* Timestamp of last activity for session timeout tracking
|
||||||
|
*/
|
||||||
|
lastActivity?: number;
|
||||||
|
/**
|
||||||
|
* Timeout ID for DATA command timeout
|
||||||
|
*/
|
||||||
|
dataTimeoutId?: NodeJS.Timeout;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP authentication data
|
||||||
|
*/
|
||||||
|
export interface ISmtpAuth {
|
||||||
|
/**
|
||||||
|
* Authentication method used
|
||||||
|
*/
|
||||||
|
method: 'PLAIN' | 'LOGIN' | 'OAUTH2' | string;
|
||||||
|
/**
|
||||||
|
* Username for authentication
|
||||||
|
*/
|
||||||
|
username: string;
|
||||||
|
/**
|
||||||
|
* Password or token for authentication
|
||||||
|
*/
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP server options
|
||||||
|
*/
|
||||||
|
export interface ISmtpServerOptions {
|
||||||
|
/**
|
||||||
|
* Port to listen on
|
||||||
|
*/
|
||||||
|
port: number;
|
||||||
|
/**
|
||||||
|
* TLS private key (PEM format)
|
||||||
|
*/
|
||||||
|
key: string;
|
||||||
|
/**
|
||||||
|
* TLS certificate (PEM format)
|
||||||
|
*/
|
||||||
|
cert: string;
|
||||||
|
/**
|
||||||
|
* Server hostname for SMTP banner
|
||||||
|
*/
|
||||||
|
hostname?: string;
|
||||||
|
/**
|
||||||
|
* Host address to bind to (defaults to all interfaces)
|
||||||
|
*/
|
||||||
|
host?: string;
|
||||||
|
/**
|
||||||
|
* Secure port for dedicated TLS connections
|
||||||
|
*/
|
||||||
|
securePort?: number;
|
||||||
|
/**
|
||||||
|
* CA certificates for TLS (PEM format)
|
||||||
|
*/
|
||||||
|
ca?: string;
|
||||||
|
/**
|
||||||
|
* Maximum size of messages in bytes
|
||||||
|
*/
|
||||||
|
maxSize?: number;
|
||||||
|
/**
|
||||||
|
* Maximum number of concurrent connections
|
||||||
|
*/
|
||||||
|
maxConnections?: number;
|
||||||
|
/**
|
||||||
|
* Authentication options
|
||||||
|
*/
|
||||||
|
auth?: {
|
||||||
|
/**
|
||||||
|
* Whether authentication is required
|
||||||
|
*/
|
||||||
|
required: boolean;
|
||||||
|
/**
|
||||||
|
* Allowed authentication methods
|
||||||
|
*/
|
||||||
|
methods: ('PLAIN' | 'LOGIN' | 'OAUTH2')[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Socket timeout in milliseconds (default: 5 minutes / 300000ms)
|
||||||
|
*/
|
||||||
|
socketTimeout?: number;
|
||||||
|
/**
|
||||||
|
* Initial connection timeout in milliseconds (default: 30 seconds / 30000ms)
|
||||||
|
*/
|
||||||
|
connectionTimeout?: number;
|
||||||
|
/**
|
||||||
|
* Interval for checking idle sessions in milliseconds (default: 5 seconds / 5000ms)
|
||||||
|
* For testing, can be set lower (e.g. 1000ms) to detect timeouts more quickly
|
||||||
|
*/
|
||||||
|
cleanupInterval?: number;
|
||||||
|
/**
|
||||||
|
* Maximum number of recipients allowed per message (default: 100)
|
||||||
|
*/
|
||||||
|
maxRecipients?: number;
|
||||||
|
/**
|
||||||
|
* Maximum message size in bytes (default: 10MB / 10485760 bytes)
|
||||||
|
* This is advertised in the EHLO SIZE extension
|
||||||
|
*/
|
||||||
|
size?: number;
|
||||||
|
/**
|
||||||
|
* Timeout for the DATA command in milliseconds (default: 60000ms / 1 minute)
|
||||||
|
* This controls how long to wait for the complete email data
|
||||||
|
*/
|
||||||
|
dataTimeout?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Result of SMTP transaction
|
||||||
|
*/
|
||||||
|
export interface ISmtpTransactionResult {
|
||||||
|
/**
|
||||||
|
* Whether the transaction was successful
|
||||||
|
*/
|
||||||
|
success: boolean;
|
||||||
|
/**
|
||||||
|
* Error message if failed
|
||||||
|
*/
|
||||||
|
error?: string;
|
||||||
|
/**
|
||||||
|
* Message ID if successful
|
||||||
|
*/
|
||||||
|
messageId?: string;
|
||||||
|
/**
|
||||||
|
* Resulting email if successful
|
||||||
|
*/
|
||||||
|
email?: Email;
|
||||||
|
}
|
||||||
17
dist_ts/mail/delivery/interfaces.js
Normal file
17
dist_ts/mail/delivery/interfaces.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* SMTP and email delivery interface definitions
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* SMTP session state enumeration
|
||||||
|
*/
|
||||||
|
export var SmtpState;
|
||||||
|
(function (SmtpState) {
|
||||||
|
SmtpState["GREETING"] = "GREETING";
|
||||||
|
SmtpState["AFTER_EHLO"] = "AFTER_EHLO";
|
||||||
|
SmtpState["MAIL_FROM"] = "MAIL_FROM";
|
||||||
|
SmtpState["RCPT_TO"] = "RCPT_TO";
|
||||||
|
SmtpState["DATA"] = "DATA";
|
||||||
|
SmtpState["DATA_RECEIVING"] = "DATA_RECEIVING";
|
||||||
|
SmtpState["FINISHED"] = "FINISHED";
|
||||||
|
})(SmtpState || (SmtpState = {}));
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvaW50ZXJmYWNlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUlIOztHQUVHO0FBQ0gsTUFBTSxDQUFOLElBQVksU0FRWDtBQVJELFdBQVksU0FBUztJQUNuQixrQ0FBcUIsQ0FBQTtJQUNyQixzQ0FBeUIsQ0FBQTtJQUN6QixvQ0FBdUIsQ0FBQTtJQUN2QixnQ0FBbUIsQ0FBQTtJQUNuQiwwQkFBYSxDQUFBO0lBQ2IsOENBQWlDLENBQUE7SUFDakMsa0NBQXFCLENBQUE7QUFDdkIsQ0FBQyxFQVJXLFNBQVMsS0FBVCxTQUFTLFFBUXBCIn0=
|
||||||
43
dist_ts/mail/delivery/smtpclient/auth-handler.d.ts
vendored
Normal file
43
dist_ts/mail/delivery/smtpclient/auth-handler.d.ts
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Authentication Handler
|
||||||
|
* Authentication mechanisms implementation
|
||||||
|
*/
|
||||||
|
import type { ISmtpConnection, ISmtpAuthOptions, ISmtpClientOptions } from './interfaces.js';
|
||||||
|
import type { CommandHandler } from './command-handler.js';
|
||||||
|
export declare class AuthHandler {
|
||||||
|
private options;
|
||||||
|
private commandHandler;
|
||||||
|
constructor(options: ISmtpClientOptions, commandHandler: CommandHandler);
|
||||||
|
/**
|
||||||
|
* Authenticate using the configured method
|
||||||
|
*/
|
||||||
|
authenticate(connection: ISmtpConnection): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Authenticate using AUTH PLAIN
|
||||||
|
*/
|
||||||
|
private authenticatePlain;
|
||||||
|
/**
|
||||||
|
* Authenticate using AUTH LOGIN
|
||||||
|
*/
|
||||||
|
private authenticateLogin;
|
||||||
|
/**
|
||||||
|
* Authenticate using OAuth2
|
||||||
|
*/
|
||||||
|
private authenticateOAuth2;
|
||||||
|
/**
|
||||||
|
* Select appropriate authentication method
|
||||||
|
*/
|
||||||
|
private selectAuthMethod;
|
||||||
|
/**
|
||||||
|
* Check if OAuth2 token is expired
|
||||||
|
*/
|
||||||
|
private isTokenExpired;
|
||||||
|
/**
|
||||||
|
* Refresh OAuth2 access token
|
||||||
|
*/
|
||||||
|
private refreshOAuth2Token;
|
||||||
|
/**
|
||||||
|
* Validate authentication configuration
|
||||||
|
*/
|
||||||
|
validateAuthConfig(auth: ISmtpAuthOptions): string[];
|
||||||
|
}
|
||||||
190
dist_ts/mail/delivery/smtpclient/auth-handler.js
Normal file
190
dist_ts/mail/delivery/smtpclient/auth-handler.js
Normal file
File diff suppressed because one or more lines are too long
67
dist_ts/mail/delivery/smtpclient/command-handler.d.ts
vendored
Normal file
67
dist_ts/mail/delivery/smtpclient/command-handler.d.ts
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Command Handler
|
||||||
|
* SMTP command sending and response parsing
|
||||||
|
*/
|
||||||
|
import { EventEmitter } from 'node:events';
|
||||||
|
import type { ISmtpConnection, ISmtpResponse, ISmtpClientOptions, ISmtpCapabilities } from './interfaces.js';
|
||||||
|
export declare class CommandHandler extends EventEmitter {
|
||||||
|
private options;
|
||||||
|
private responseBuffer;
|
||||||
|
private pendingCommand;
|
||||||
|
private commandTimeout;
|
||||||
|
constructor(options: ISmtpClientOptions);
|
||||||
|
/**
|
||||||
|
* Send EHLO command and parse capabilities
|
||||||
|
*/
|
||||||
|
sendEhlo(connection: ISmtpConnection, domain?: string): Promise<ISmtpCapabilities>;
|
||||||
|
/**
|
||||||
|
* Send MAIL FROM command
|
||||||
|
*/
|
||||||
|
sendMailFrom(connection: ISmtpConnection, fromAddress: string): Promise<ISmtpResponse>;
|
||||||
|
/**
|
||||||
|
* Send RCPT TO command
|
||||||
|
*/
|
||||||
|
sendRcptTo(connection: ISmtpConnection, toAddress: string): Promise<ISmtpResponse>;
|
||||||
|
/**
|
||||||
|
* Send DATA command
|
||||||
|
*/
|
||||||
|
sendData(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||||
|
/**
|
||||||
|
* Send email data content
|
||||||
|
*/
|
||||||
|
sendDataContent(connection: ISmtpConnection, emailData: string): Promise<ISmtpResponse>;
|
||||||
|
/**
|
||||||
|
* Send RSET command
|
||||||
|
*/
|
||||||
|
sendRset(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||||
|
/**
|
||||||
|
* Send NOOP command
|
||||||
|
*/
|
||||||
|
sendNoop(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||||
|
/**
|
||||||
|
* Send QUIT command
|
||||||
|
*/
|
||||||
|
sendQuit(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||||
|
/**
|
||||||
|
* Send STARTTLS command
|
||||||
|
*/
|
||||||
|
sendStartTls(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||||
|
/**
|
||||||
|
* Send AUTH command
|
||||||
|
*/
|
||||||
|
sendAuth(connection: ISmtpConnection, method: string, credentials?: string): Promise<ISmtpResponse>;
|
||||||
|
/**
|
||||||
|
* Send a generic SMTP command
|
||||||
|
*/
|
||||||
|
sendCommand(connection: ISmtpConnection, command: string): Promise<ISmtpResponse>;
|
||||||
|
/**
|
||||||
|
* Send raw data without command formatting
|
||||||
|
*/
|
||||||
|
sendRawData(connection: ISmtpConnection, data: string): Promise<ISmtpResponse>;
|
||||||
|
/**
|
||||||
|
* Wait for server greeting
|
||||||
|
*/
|
||||||
|
waitForGreeting(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||||
|
private handleIncomingData;
|
||||||
|
private isCompleteResponse;
|
||||||
|
}
|
||||||
277
dist_ts/mail/delivery/smtpclient/command-handler.js
Normal file
277
dist_ts/mail/delivery/smtpclient/command-handler.js
Normal file
File diff suppressed because one or more lines are too long
48
dist_ts/mail/delivery/smtpclient/connection-manager.d.ts
vendored
Normal file
48
dist_ts/mail/delivery/smtpclient/connection-manager.d.ts
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Connection Manager
|
||||||
|
* Connection pooling and lifecycle management
|
||||||
|
*/
|
||||||
|
import { EventEmitter } from 'node:events';
|
||||||
|
import type { ISmtpClientOptions, ISmtpConnection, IConnectionPoolStatus } from './interfaces.js';
|
||||||
|
export declare class ConnectionManager extends EventEmitter {
|
||||||
|
private options;
|
||||||
|
private connections;
|
||||||
|
private pendingConnections;
|
||||||
|
private idleTimeout;
|
||||||
|
constructor(options: ISmtpClientOptions);
|
||||||
|
/**
|
||||||
|
* Get or create a connection
|
||||||
|
*/
|
||||||
|
getConnection(): Promise<ISmtpConnection>;
|
||||||
|
/**
|
||||||
|
* Create a new connection
|
||||||
|
*/
|
||||||
|
createConnection(): Promise<ISmtpConnection>;
|
||||||
|
/**
|
||||||
|
* Release a connection back to the pool or close it
|
||||||
|
*/
|
||||||
|
releaseConnection(connection: ISmtpConnection): void;
|
||||||
|
/**
|
||||||
|
* Close a specific connection
|
||||||
|
*/
|
||||||
|
closeConnection(connection: ISmtpConnection): void;
|
||||||
|
/**
|
||||||
|
* Close all connections
|
||||||
|
*/
|
||||||
|
closeAllConnections(): void;
|
||||||
|
/**
|
||||||
|
* Get connection pool status
|
||||||
|
*/
|
||||||
|
getPoolStatus(): IConnectionPoolStatus;
|
||||||
|
/**
|
||||||
|
* Update connection activity timestamp
|
||||||
|
*/
|
||||||
|
updateActivity(connection: ISmtpConnection): void;
|
||||||
|
private establishSocket;
|
||||||
|
private setupSocketHandlers;
|
||||||
|
private findIdleConnection;
|
||||||
|
private shouldReuseConnection;
|
||||||
|
private getActiveConnectionCount;
|
||||||
|
private getConnectionId;
|
||||||
|
private setupIdleCleanup;
|
||||||
|
}
|
||||||
239
dist_ts/mail/delivery/smtpclient/connection-manager.js
Normal file
239
dist_ts/mail/delivery/smtpclient/connection-manager.js
Normal file
File diff suppressed because one or more lines are too long
129
dist_ts/mail/delivery/smtpclient/constants.d.ts
vendored
Normal file
129
dist_ts/mail/delivery/smtpclient/constants.d.ts
vendored
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Constants and Error Codes
|
||||||
|
* All constants, error codes, and enums for SMTP client operations
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* SMTP response codes
|
||||||
|
*/
|
||||||
|
export declare const SMTP_CODES: {
|
||||||
|
readonly SERVICE_READY: 220;
|
||||||
|
readonly SERVICE_CLOSING: 221;
|
||||||
|
readonly AUTHENTICATION_SUCCESSFUL: 235;
|
||||||
|
readonly REQUESTED_ACTION_OK: 250;
|
||||||
|
readonly USER_NOT_LOCAL: 251;
|
||||||
|
readonly CANNOT_VERIFY_USER: 252;
|
||||||
|
readonly START_MAIL_INPUT: 354;
|
||||||
|
readonly SERVICE_NOT_AVAILABLE: 421;
|
||||||
|
readonly MAILBOX_BUSY: 450;
|
||||||
|
readonly LOCAL_ERROR: 451;
|
||||||
|
readonly INSUFFICIENT_STORAGE: 452;
|
||||||
|
readonly UNABLE_TO_ACCOMMODATE: 455;
|
||||||
|
readonly SYNTAX_ERROR: 500;
|
||||||
|
readonly SYNTAX_ERROR_PARAMETERS: 501;
|
||||||
|
readonly COMMAND_NOT_IMPLEMENTED: 502;
|
||||||
|
readonly BAD_SEQUENCE: 503;
|
||||||
|
readonly PARAMETER_NOT_IMPLEMENTED: 504;
|
||||||
|
readonly MAILBOX_UNAVAILABLE: 550;
|
||||||
|
readonly USER_NOT_LOCAL_TRY_FORWARD: 551;
|
||||||
|
readonly EXCEEDED_STORAGE: 552;
|
||||||
|
readonly MAILBOX_NAME_NOT_ALLOWED: 553;
|
||||||
|
readonly TRANSACTION_FAILED: 554;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* SMTP command names
|
||||||
|
*/
|
||||||
|
export declare const SMTP_COMMANDS: {
|
||||||
|
readonly HELO: "HELO";
|
||||||
|
readonly EHLO: "EHLO";
|
||||||
|
readonly MAIL_FROM: "MAIL FROM";
|
||||||
|
readonly RCPT_TO: "RCPT TO";
|
||||||
|
readonly DATA: "DATA";
|
||||||
|
readonly RSET: "RSET";
|
||||||
|
readonly NOOP: "NOOP";
|
||||||
|
readonly QUIT: "QUIT";
|
||||||
|
readonly STARTTLS: "STARTTLS";
|
||||||
|
readonly AUTH: "AUTH";
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Authentication methods
|
||||||
|
*/
|
||||||
|
export declare const AUTH_METHODS: {
|
||||||
|
readonly PLAIN: "PLAIN";
|
||||||
|
readonly LOGIN: "LOGIN";
|
||||||
|
readonly OAUTH2: "XOAUTH2";
|
||||||
|
readonly CRAM_MD5: "CRAM-MD5";
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Common SMTP extensions
|
||||||
|
*/
|
||||||
|
export declare const SMTP_EXTENSIONS: {
|
||||||
|
readonly PIPELINING: "PIPELINING";
|
||||||
|
readonly SIZE: "SIZE";
|
||||||
|
readonly STARTTLS: "STARTTLS";
|
||||||
|
readonly AUTH: "AUTH";
|
||||||
|
readonly EIGHT_BIT_MIME: "8BITMIME";
|
||||||
|
readonly CHUNKING: "CHUNKING";
|
||||||
|
readonly ENHANCED_STATUS_CODES: "ENHANCEDSTATUSCODES";
|
||||||
|
readonly DSN: "DSN";
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Default configuration values
|
||||||
|
*/
|
||||||
|
export declare const DEFAULTS: {
|
||||||
|
readonly CONNECTION_TIMEOUT: 60000;
|
||||||
|
readonly SOCKET_TIMEOUT: 300000;
|
||||||
|
readonly COMMAND_TIMEOUT: 30000;
|
||||||
|
readonly MAX_CONNECTIONS: 5;
|
||||||
|
readonly MAX_MESSAGES: 100;
|
||||||
|
readonly PORT_SMTP: 25;
|
||||||
|
readonly PORT_SUBMISSION: 587;
|
||||||
|
readonly PORT_SMTPS: 465;
|
||||||
|
readonly RETRY_ATTEMPTS: 3;
|
||||||
|
readonly RETRY_DELAY: 1000;
|
||||||
|
readonly POOL_IDLE_TIMEOUT: 30000;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Error types for classification
|
||||||
|
*/
|
||||||
|
export declare enum SmtpErrorType {
|
||||||
|
CONNECTION_ERROR = "CONNECTION_ERROR",
|
||||||
|
AUTHENTICATION_ERROR = "AUTHENTICATION_ERROR",
|
||||||
|
PROTOCOL_ERROR = "PROTOCOL_ERROR",
|
||||||
|
TIMEOUT_ERROR = "TIMEOUT_ERROR",
|
||||||
|
TLS_ERROR = "TLS_ERROR",
|
||||||
|
SYNTAX_ERROR = "SYNTAX_ERROR",
|
||||||
|
MAILBOX_ERROR = "MAILBOX_ERROR",
|
||||||
|
QUOTA_ERROR = "QUOTA_ERROR",
|
||||||
|
UNKNOWN_ERROR = "UNKNOWN_ERROR"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Regular expressions for parsing
|
||||||
|
*/
|
||||||
|
export declare const REGEX_PATTERNS: {
|
||||||
|
readonly EMAIL_ADDRESS: RegExp;
|
||||||
|
readonly RESPONSE_CODE: RegExp;
|
||||||
|
readonly ENHANCED_STATUS: RegExp;
|
||||||
|
readonly AUTH_CAPABILITIES: RegExp;
|
||||||
|
readonly SIZE_EXTENSION: RegExp;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Line endings and separators
|
||||||
|
*/
|
||||||
|
export declare const LINE_ENDINGS: {
|
||||||
|
readonly CRLF: "\r\n";
|
||||||
|
readonly LF: "\n";
|
||||||
|
readonly CR: "\r";
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Connection states for internal use
|
||||||
|
*/
|
||||||
|
export declare const CONNECTION_STATES: {
|
||||||
|
readonly DISCONNECTED: "disconnected";
|
||||||
|
readonly CONNECTING: "connecting";
|
||||||
|
readonly CONNECTED: "connected";
|
||||||
|
readonly AUTHENTICATED: "authenticated";
|
||||||
|
readonly READY: "ready";
|
||||||
|
readonly BUSY: "busy";
|
||||||
|
readonly CLOSING: "closing";
|
||||||
|
readonly ERROR: "error";
|
||||||
|
};
|
||||||
135
dist_ts/mail/delivery/smtpclient/constants.js
Normal file
135
dist_ts/mail/delivery/smtpclient/constants.js
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Constants and Error Codes
|
||||||
|
* All constants, error codes, and enums for SMTP client operations
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* SMTP response codes
|
||||||
|
*/
|
||||||
|
export const SMTP_CODES = {
|
||||||
|
// Positive completion replies
|
||||||
|
SERVICE_READY: 220,
|
||||||
|
SERVICE_CLOSING: 221,
|
||||||
|
AUTHENTICATION_SUCCESSFUL: 235,
|
||||||
|
REQUESTED_ACTION_OK: 250,
|
||||||
|
USER_NOT_LOCAL: 251,
|
||||||
|
CANNOT_VERIFY_USER: 252,
|
||||||
|
// Positive intermediate replies
|
||||||
|
START_MAIL_INPUT: 354,
|
||||||
|
// Transient negative completion replies
|
||||||
|
SERVICE_NOT_AVAILABLE: 421,
|
||||||
|
MAILBOX_BUSY: 450,
|
||||||
|
LOCAL_ERROR: 451,
|
||||||
|
INSUFFICIENT_STORAGE: 452,
|
||||||
|
UNABLE_TO_ACCOMMODATE: 455,
|
||||||
|
// Permanent negative completion replies
|
||||||
|
SYNTAX_ERROR: 500,
|
||||||
|
SYNTAX_ERROR_PARAMETERS: 501,
|
||||||
|
COMMAND_NOT_IMPLEMENTED: 502,
|
||||||
|
BAD_SEQUENCE: 503,
|
||||||
|
PARAMETER_NOT_IMPLEMENTED: 504,
|
||||||
|
MAILBOX_UNAVAILABLE: 550,
|
||||||
|
USER_NOT_LOCAL_TRY_FORWARD: 551,
|
||||||
|
EXCEEDED_STORAGE: 552,
|
||||||
|
MAILBOX_NAME_NOT_ALLOWED: 553,
|
||||||
|
TRANSACTION_FAILED: 554
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* SMTP command names
|
||||||
|
*/
|
||||||
|
export const SMTP_COMMANDS = {
|
||||||
|
HELO: 'HELO',
|
||||||
|
EHLO: 'EHLO',
|
||||||
|
MAIL_FROM: 'MAIL FROM',
|
||||||
|
RCPT_TO: 'RCPT TO',
|
||||||
|
DATA: 'DATA',
|
||||||
|
RSET: 'RSET',
|
||||||
|
NOOP: 'NOOP',
|
||||||
|
QUIT: 'QUIT',
|
||||||
|
STARTTLS: 'STARTTLS',
|
||||||
|
AUTH: 'AUTH'
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Authentication methods
|
||||||
|
*/
|
||||||
|
export const AUTH_METHODS = {
|
||||||
|
PLAIN: 'PLAIN',
|
||||||
|
LOGIN: 'LOGIN',
|
||||||
|
OAUTH2: 'XOAUTH2',
|
||||||
|
CRAM_MD5: 'CRAM-MD5'
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Common SMTP extensions
|
||||||
|
*/
|
||||||
|
export const SMTP_EXTENSIONS = {
|
||||||
|
PIPELINING: 'PIPELINING',
|
||||||
|
SIZE: 'SIZE',
|
||||||
|
STARTTLS: 'STARTTLS',
|
||||||
|
AUTH: 'AUTH',
|
||||||
|
EIGHT_BIT_MIME: '8BITMIME',
|
||||||
|
CHUNKING: 'CHUNKING',
|
||||||
|
ENHANCED_STATUS_CODES: 'ENHANCEDSTATUSCODES',
|
||||||
|
DSN: 'DSN'
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Default configuration values
|
||||||
|
*/
|
||||||
|
export const DEFAULTS = {
|
||||||
|
CONNECTION_TIMEOUT: 60000, // 60 seconds
|
||||||
|
SOCKET_TIMEOUT: 300000, // 5 minutes
|
||||||
|
COMMAND_TIMEOUT: 30000, // 30 seconds
|
||||||
|
MAX_CONNECTIONS: 5,
|
||||||
|
MAX_MESSAGES: 100,
|
||||||
|
PORT_SMTP: 25,
|
||||||
|
PORT_SUBMISSION: 587,
|
||||||
|
PORT_SMTPS: 465,
|
||||||
|
RETRY_ATTEMPTS: 3,
|
||||||
|
RETRY_DELAY: 1000,
|
||||||
|
POOL_IDLE_TIMEOUT: 30000 // 30 seconds
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Error types for classification
|
||||||
|
*/
|
||||||
|
export var SmtpErrorType;
|
||||||
|
(function (SmtpErrorType) {
|
||||||
|
SmtpErrorType["CONNECTION_ERROR"] = "CONNECTION_ERROR";
|
||||||
|
SmtpErrorType["AUTHENTICATION_ERROR"] = "AUTHENTICATION_ERROR";
|
||||||
|
SmtpErrorType["PROTOCOL_ERROR"] = "PROTOCOL_ERROR";
|
||||||
|
SmtpErrorType["TIMEOUT_ERROR"] = "TIMEOUT_ERROR";
|
||||||
|
SmtpErrorType["TLS_ERROR"] = "TLS_ERROR";
|
||||||
|
SmtpErrorType["SYNTAX_ERROR"] = "SYNTAX_ERROR";
|
||||||
|
SmtpErrorType["MAILBOX_ERROR"] = "MAILBOX_ERROR";
|
||||||
|
SmtpErrorType["QUOTA_ERROR"] = "QUOTA_ERROR";
|
||||||
|
SmtpErrorType["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
||||||
|
})(SmtpErrorType || (SmtpErrorType = {}));
|
||||||
|
/**
|
||||||
|
* Regular expressions for parsing
|
||||||
|
*/
|
||||||
|
export const REGEX_PATTERNS = {
|
||||||
|
EMAIL_ADDRESS: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
||||||
|
RESPONSE_CODE: /^(\d{3})([ -])(.*)/,
|
||||||
|
ENHANCED_STATUS: /^(\d\.\d\.\d)\s/,
|
||||||
|
AUTH_CAPABILITIES: /AUTH\s+(.+)/i,
|
||||||
|
SIZE_EXTENSION: /SIZE\s+(\d+)/i
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Line endings and separators
|
||||||
|
*/
|
||||||
|
export const LINE_ENDINGS = {
|
||||||
|
CRLF: '\r\n',
|
||||||
|
LF: '\n',
|
||||||
|
CR: '\r'
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Connection states for internal use
|
||||||
|
*/
|
||||||
|
export const CONNECTION_STATES = {
|
||||||
|
DISCONNECTED: 'disconnected',
|
||||||
|
CONNECTING: 'connecting',
|
||||||
|
CONNECTED: 'connected',
|
||||||
|
AUTHENTICATED: 'authenticated',
|
||||||
|
READY: 'ready',
|
||||||
|
BUSY: 'busy',
|
||||||
|
CLOSING: 'closing',
|
||||||
|
ERROR: 'error'
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHMvbWFpbC9kZWxpdmVyeS9zbXRwY2xpZW50L2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSDs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRztJQUN4Qiw4QkFBOEI7SUFDOUIsYUFBYSxFQUFFLEdBQUc7SUFDbEIsZUFBZSxFQUFFLEdBQUc7SUFDcEIseUJBQXlCLEVBQUUsR0FBRztJQUM5QixtQkFBbUIsRUFBRSxHQUFHO0lBQ3hCLGNBQWMsRUFBRSxHQUFHO0lBQ25CLGtCQUFrQixFQUFFLEdBQUc7SUFFdkIsZ0NBQWdDO0lBQ2hDLGdCQUFnQixFQUFFLEdBQUc7SUFFckIsd0NBQXdDO0lBQ3hDLHFCQUFxQixFQUFFLEdBQUc7SUFDMUIsWUFBWSxFQUFFLEdBQUc7SUFDakIsV0FBVyxFQUFFLEdBQUc7SUFDaEIsb0JBQW9CLEVBQUUsR0FBRztJQUN6QixxQkFBcUIsRUFBRSxHQUFHO0lBRTFCLHdDQUF3QztJQUN4QyxZQUFZLEVBQUUsR0FBRztJQUNqQix1QkFBdUIsRUFBRSxHQUFHO0lBQzVCLHVCQUF1QixFQUFFLEdBQUc7SUFDNUIsWUFBWSxFQUFFLEdBQUc7SUFDakIseUJBQXlCLEVBQUUsR0FBRztJQUM5QixtQkFBbUIsRUFBRSxHQUFHO0lBQ3hCLDBCQUEwQixFQUFFLEdBQUc7SUFDL0IsZ0JBQWdCLEVBQUUsR0FBRztJQUNyQix3QkFBd0IsRUFBRSxHQUFHO0lBQzdCLGtCQUFrQixFQUFFLEdBQUc7Q0FDZixDQUFDO0FBRVg7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUc7SUFDM0IsSUFBSSxFQUFFLE1BQU07SUFDWixJQUFJLEVBQUUsTUFBTTtJQUNaLFNBQVMsRUFBRSxXQUFXO0lBQ3RCLE9BQU8sRUFBRSxTQUFTO0lBQ2xCLElBQUksRUFBRSxNQUFNO0lBQ1osSUFB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
|
||||||
22
dist_ts/mail/delivery/smtpclient/create-client.d.ts
vendored
Normal file
22
dist_ts/mail/delivery/smtpclient/create-client.d.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Factory
|
||||||
|
* Factory function for client creation and dependency injection
|
||||||
|
*/
|
||||||
|
import { SmtpClient } from './smtp-client.js';
|
||||||
|
import type { ISmtpClientOptions } from './interfaces.js';
|
||||||
|
/**
|
||||||
|
* Create a complete SMTP client with all components
|
||||||
|
*/
|
||||||
|
export declare function createSmtpClient(options: ISmtpClientOptions): SmtpClient;
|
||||||
|
/**
|
||||||
|
* Create SMTP client with connection pooling enabled
|
||||||
|
*/
|
||||||
|
export declare function createPooledSmtpClient(options: ISmtpClientOptions): SmtpClient;
|
||||||
|
/**
|
||||||
|
* Create SMTP client for high-volume sending
|
||||||
|
*/
|
||||||
|
export declare function createBulkSmtpClient(options: ISmtpClientOptions): SmtpClient;
|
||||||
|
/**
|
||||||
|
* Create SMTP client for transactional emails
|
||||||
|
*/
|
||||||
|
export declare function createTransactionalSmtpClient(options: ISmtpClientOptions): SmtpClient;
|
||||||
86
dist_ts/mail/delivery/smtpclient/create-client.js
Normal file
86
dist_ts/mail/delivery/smtpclient/create-client.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Factory
|
||||||
|
* Factory function for client creation and dependency injection
|
||||||
|
*/
|
||||||
|
import { SmtpClient } from './smtp-client.js';
|
||||||
|
import { ConnectionManager } from './connection-manager.js';
|
||||||
|
import { CommandHandler } from './command-handler.js';
|
||||||
|
import { AuthHandler } from './auth-handler.js';
|
||||||
|
import { TlsHandler } from './tls-handler.js';
|
||||||
|
import { SmtpErrorHandler } from './error-handler.js';
|
||||||
|
import { validateClientOptions } from './utils/validation.js';
|
||||||
|
import { DEFAULTS } from './constants.js';
|
||||||
|
/**
|
||||||
|
* Create a complete SMTP client with all components
|
||||||
|
*/
|
||||||
|
export function createSmtpClient(options) {
|
||||||
|
// Validate options
|
||||||
|
const errors = validateClientOptions(options);
|
||||||
|
if (errors.length > 0) {
|
||||||
|
throw new Error(`Invalid client options: ${errors.join(', ')}`);
|
||||||
|
}
|
||||||
|
// Apply defaults
|
||||||
|
const clientOptions = {
|
||||||
|
connectionTimeout: DEFAULTS.CONNECTION_TIMEOUT,
|
||||||
|
socketTimeout: DEFAULTS.SOCKET_TIMEOUT,
|
||||||
|
maxConnections: DEFAULTS.MAX_CONNECTIONS,
|
||||||
|
maxMessages: DEFAULTS.MAX_MESSAGES,
|
||||||
|
pool: false,
|
||||||
|
secure: false,
|
||||||
|
debug: false,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
// Create handlers
|
||||||
|
const errorHandler = new SmtpErrorHandler(clientOptions);
|
||||||
|
const connectionManager = new ConnectionManager(clientOptions);
|
||||||
|
const commandHandler = new CommandHandler(clientOptions);
|
||||||
|
const authHandler = new AuthHandler(clientOptions, commandHandler);
|
||||||
|
const tlsHandler = new TlsHandler(clientOptions, commandHandler);
|
||||||
|
// Create and return SMTP client
|
||||||
|
return new SmtpClient({
|
||||||
|
options: clientOptions,
|
||||||
|
connectionManager,
|
||||||
|
commandHandler,
|
||||||
|
authHandler,
|
||||||
|
tlsHandler,
|
||||||
|
errorHandler
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create SMTP client with connection pooling enabled
|
||||||
|
*/
|
||||||
|
export function createPooledSmtpClient(options) {
|
||||||
|
return createSmtpClient({
|
||||||
|
...options,
|
||||||
|
pool: true,
|
||||||
|
maxConnections: options.maxConnections || DEFAULTS.MAX_CONNECTIONS,
|
||||||
|
maxMessages: options.maxMessages || DEFAULTS.MAX_MESSAGES
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create SMTP client for high-volume sending
|
||||||
|
*/
|
||||||
|
export function createBulkSmtpClient(options) {
|
||||||
|
return createSmtpClient({
|
||||||
|
...options,
|
||||||
|
pool: true,
|
||||||
|
maxConnections: Math.max(options.maxConnections || 10, 10),
|
||||||
|
maxMessages: Math.max(options.maxMessages || 1000, 1000),
|
||||||
|
connectionTimeout: options.connectionTimeout || 30000,
|
||||||
|
socketTimeout: options.socketTimeout || 120000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create SMTP client for transactional emails
|
||||||
|
*/
|
||||||
|
export function createTransactionalSmtpClient(options) {
|
||||||
|
return createSmtpClient({
|
||||||
|
...options,
|
||||||
|
pool: false, // Use fresh connections for transactional emails
|
||||||
|
maxConnections: 1,
|
||||||
|
maxMessages: 1,
|
||||||
|
connectionTimeout: options.connectionTimeout || 10000,
|
||||||
|
socketTimeout: options.socketTimeout || 30000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cGNsaWVudC9jcmVhdGUtY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUV0RCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUM5RCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFMUM7O0dBRUc7QUFDSCxNQUFNLFVBQVUsZ0JBQWdCLENBQUMsT0FBMkI7SUFDMUQsbUJBQW1CO0lBQ25CLE1BQU0sTUFBTSxHQUFHLHFCQUFxQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzlDLElBQUksTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNsRSxDQUFDO0lBRUQsaUJBQWlCO0lBQ2pCLE1BQU0sYUFBYSxHQUF1QjtRQUN4QyxpQkFBaUIsRUFBRSxRQUFRLENBQUMsa0JBQWtCO1FBQzlDLGFBQWEsRUFBRSxRQUFRLENBQUMsY0FBYztRQUN0QyxjQUFjLEVBQUUsUUFBUSxDQUFDLGVBQWU7UUFDeEMsV0FBVyxFQUFFLFFBQVEsQ0FBQyxZQUFZO1FBQ2xDLElBQUksRUFBRSxLQUFLO1FBQ1gsTUFBTSxFQUFFLEtBQUs7UUFDYixLQUFLLEVBQUUsS0FBSztRQUNaLEdBQUcsT0FBTztLQUNYLENBQUM7SUFFRixrQkFBa0I7SUFDbEIsTUFBTSxZQUFZLEdBQUcsSUFBSSxnQkFBZ0IsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUN6RCxNQUFNLGlCQUFpQixHQUFHLElBQUksaUJBQWlCLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDL0QsTUFBTSxjQUFjLEdBQUcsSUFBSSxjQUFjLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDekQsTUFBTSxXQUFXLEdBQUcsSUFBSSxXQUFXLENBQUMsYUFBYSxFQUFFLGNBQWMsQ0FBQyxDQUFDO0lBQ25FLE1BQU0sVUFBVSxHQUFHLElBQUksVUFBVSxDQUFDLGFBQWEsRUFBRSxjQUFjLENBQUMsQ0FBQztJQUVqRSxnQ0FBZ0M7SUFDaEMsT0FBTyxJQUFJLFVBQVUsQ0FBQztRQUNwQixPQUFPLEVBQUUsYUFBYTtRQUN0QixpQkFBaUI7UUFDakIsY0FBYztRQUNkLFdBQVc7UUFDWCxVQUFVO1FBQ1YsWUFBWTtLQUNiLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxzQkFBc0IsQ0FBQyxPQUEyQjtJQUNoRSxPQUFPLGdCQUFnQixDQUFDO1FBQ3RCLEdBQUcsT0FBTztRQUNWLElBQUksRUFBRSxJQUFJO1FBQ1YsY0FBYyxFQUFFLE9BQU8sQ0FBQyxjQUFjLElBQUksUUFBUSxDQUFDLGVBQWU7UUFDbEUsV0FBVyxFQUFFLE9BQU8sQ0FBQyxXQUFXLElBQUksUUFBUSxDQUFDLFlBQVk7S0FDMUQsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFDLE9BQTJCO0lBQzlELE9BQU8sZ0JBQWdCLENBQUM7UUFDdEIsR0FBRyxPQUFPO1FBQ1YsSUFBSSxFQUFFLElBQUk7UUFDVixjQUFjLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsY0FBYyxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUM7UUFDMUQsV0FBVyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLFdBQVcsSUFBSSxJQUFJLEVBQUUsSUFBSSxDQUFDO1FBQ3hELGlCQUFpQixFQUFFLE9BQU8sQ0FBQyxpQkFBaUIsSUFBSSxLQUFLO1FBQ3JELGFBQWEsRUFBRSxPQUFPLENBQUMsYUFBYSxJQUFJLE1BQU07S0FDL0MsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxVQUFVLDZCQUE2QixDQUFDLE9BQTJCO0lBQ3ZFLE9BQU8sZ0JBQWdCLENBQUM7UUFDdEIsR0FBRyxPQUFPO1FBQ1YsSUFBSSxFQUFFLEtBQUssRUFBRSxpREFBaUQ7UUFDOUQsY0FBYyxFQUFFLENBQUM7UUFDakIsV0FBVyxFQUFFLENBQUM7UUFDZCxpQkFBaUIsRUFBRSxPQUFPLENBQUMsaUJBQWlCLElBQUksS0FBSztRQUNyRCxhQUFhLEVBQUUsT0FBTyxDQUFDLGFBQWEsSUFBSSxLQUFLO0tBQzlDLENBQUMsQ0FBQztBQUNMLENBQUMifQ==
|
||||||
28
dist_ts/mail/delivery/smtpclient/error-handler.d.ts
vendored
Normal file
28
dist_ts/mail/delivery/smtpclient/error-handler.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Error Handler
|
||||||
|
* Error classification and recovery strategies
|
||||||
|
*/
|
||||||
|
import { SmtpErrorType } from './constants.js';
|
||||||
|
import type { ISmtpResponse, ISmtpErrorContext, ISmtpClientOptions } from './interfaces.js';
|
||||||
|
export declare class SmtpErrorHandler {
|
||||||
|
private options;
|
||||||
|
constructor(options: ISmtpClientOptions);
|
||||||
|
/**
|
||||||
|
* Classify error type based on response or error
|
||||||
|
*/
|
||||||
|
classifyError(error: Error | ISmtpResponse, context?: ISmtpErrorContext): SmtpErrorType;
|
||||||
|
/**
|
||||||
|
* Determine if error is retryable
|
||||||
|
*/
|
||||||
|
isRetryable(errorType: SmtpErrorType, response?: ISmtpResponse): boolean;
|
||||||
|
/**
|
||||||
|
* Get retry delay for error type
|
||||||
|
*/
|
||||||
|
getRetryDelay(attempt: number, errorType: SmtpErrorType): number;
|
||||||
|
/**
|
||||||
|
* Create enhanced error with context
|
||||||
|
*/
|
||||||
|
createError(message: string, errorType: SmtpErrorType, context?: ISmtpErrorContext, originalError?: Error): Error;
|
||||||
|
private classifyErrorByMessage;
|
||||||
|
private classifyErrorByCode;
|
||||||
|
}
|
||||||
111
dist_ts/mail/delivery/smtpclient/error-handler.js
Normal file
111
dist_ts/mail/delivery/smtpclient/error-handler.js
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Error Handler
|
||||||
|
* Error classification and recovery strategies
|
||||||
|
*/
|
||||||
|
import { SmtpErrorType } from './constants.js';
|
||||||
|
import { logDebug } from './utils/logging.js';
|
||||||
|
export class SmtpErrorHandler {
|
||||||
|
options;
|
||||||
|
constructor(options) {
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Classify error type based on response or error
|
||||||
|
*/
|
||||||
|
classifyError(error, context) {
|
||||||
|
logDebug('Classifying error', this.options, { errorMessage: error instanceof Error ? error.message : String(error), context });
|
||||||
|
// Handle Error objects
|
||||||
|
if (error instanceof Error) {
|
||||||
|
return this.classifyErrorByMessage(error);
|
||||||
|
}
|
||||||
|
// Handle SMTP response codes
|
||||||
|
if (typeof error === 'object' && 'code' in error) {
|
||||||
|
return this.classifyErrorByCode(error.code);
|
||||||
|
}
|
||||||
|
return SmtpErrorType.UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Determine if error is retryable
|
||||||
|
*/
|
||||||
|
isRetryable(errorType, response) {
|
||||||
|
switch (errorType) {
|
||||||
|
case SmtpErrorType.CONNECTION_ERROR:
|
||||||
|
case SmtpErrorType.TIMEOUT_ERROR:
|
||||||
|
return true;
|
||||||
|
case SmtpErrorType.PROTOCOL_ERROR:
|
||||||
|
// Only retry on temporary failures (4xx codes)
|
||||||
|
return response ? response.code >= 400 && response.code < 500 : false;
|
||||||
|
case SmtpErrorType.AUTHENTICATION_ERROR:
|
||||||
|
case SmtpErrorType.TLS_ERROR:
|
||||||
|
case SmtpErrorType.SYNTAX_ERROR:
|
||||||
|
case SmtpErrorType.MAILBOX_ERROR:
|
||||||
|
case SmtpErrorType.QUOTA_ERROR:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get retry delay for error type
|
||||||
|
*/
|
||||||
|
getRetryDelay(attempt, errorType) {
|
||||||
|
const baseDelay = 1000; // 1 second
|
||||||
|
const maxDelay = 30000; // 30 seconds
|
||||||
|
// Exponential backoff with jitter
|
||||||
|
const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
|
||||||
|
const jitter = Math.random() * 0.1 * delay; // 10% jitter
|
||||||
|
return Math.floor(delay + jitter);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create enhanced error with context
|
||||||
|
*/
|
||||||
|
createError(message, errorType, context, originalError) {
|
||||||
|
const error = new Error(message);
|
||||||
|
error.type = errorType;
|
||||||
|
error.context = context;
|
||||||
|
error.originalError = originalError;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
classifyErrorByMessage(error) {
|
||||||
|
const message = error.message.toLowerCase();
|
||||||
|
if (message.includes('timeout') || message.includes('etimedout')) {
|
||||||
|
return SmtpErrorType.TIMEOUT_ERROR;
|
||||||
|
}
|
||||||
|
if (message.includes('connect') || message.includes('econnrefused') ||
|
||||||
|
message.includes('enotfound') || message.includes('enetunreach')) {
|
||||||
|
return SmtpErrorType.CONNECTION_ERROR;
|
||||||
|
}
|
||||||
|
if (message.includes('tls') || message.includes('ssl') ||
|
||||||
|
message.includes('certificate') || message.includes('handshake')) {
|
||||||
|
return SmtpErrorType.TLS_ERROR;
|
||||||
|
}
|
||||||
|
if (message.includes('auth')) {
|
||||||
|
return SmtpErrorType.AUTHENTICATION_ERROR;
|
||||||
|
}
|
||||||
|
return SmtpErrorType.UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
classifyErrorByCode(code) {
|
||||||
|
if (code >= 500) {
|
||||||
|
// Permanent failures
|
||||||
|
if (code === 550 || code === 551 || code === 553) {
|
||||||
|
return SmtpErrorType.MAILBOX_ERROR;
|
||||||
|
}
|
||||||
|
if (code === 552) {
|
||||||
|
return SmtpErrorType.QUOTA_ERROR;
|
||||||
|
}
|
||||||
|
if (code === 500 || code === 501 || code === 502 || code === 504) {
|
||||||
|
return SmtpErrorType.SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
return SmtpErrorType.PROTOCOL_ERROR;
|
||||||
|
}
|
||||||
|
if (code >= 400) {
|
||||||
|
// Temporary failures
|
||||||
|
if (code === 450 || code === 451 || code === 452) {
|
||||||
|
return SmtpErrorType.QUOTA_ERROR;
|
||||||
|
}
|
||||||
|
return SmtpErrorType.PROTOCOL_ERROR;
|
||||||
|
}
|
||||||
|
return SmtpErrorType.UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3ItaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cGNsaWVudC9lcnJvci1oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUUvQyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFOUMsTUFBTSxPQUFPLGdCQUFnQjtJQUNuQixPQUFPLENBQXFCO0lBRXBDLFlBQVksT0FBMkI7UUFDckMsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7SUFDekIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksYUFBYSxDQUFDLEtBQTRCLEVBQUUsT0FBMkI7UUFDNUUsUUFBUSxDQUFDLG1CQUFtQixFQUFFLElBQUksQ0FBQyxPQUFPLEVBQUUsRUFBRSxZQUFZLEVBQUUsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFFL0gsdUJBQXVCO1FBQ3ZCLElBQUksS0FBSyxZQUFZLEtBQUssRUFBRSxDQUFDO1lBQzNCLE9BQU8sSUFBSSxDQUFDLHNCQUFzQixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzVDLENBQUM7UUFFRCw2QkFBNkI7UUFDN0IsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksTUFBTSxJQUFJLEtBQUssRUFBRSxDQUFDO1lBQ2pELE9BQU8sSUFBSSxDQUFDLG1CQUFtQixDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM5QyxDQUFDO1FBRUQsT0FBTyxhQUFhLENBQUMsYUFBYSxDQUFDO0lBQ3JDLENBQUM7SUFFRDs7T0FFRztJQUNJLFdBQVcsQ0FBQyxTQUF3QixFQUFFLFFBQXdCO1FBQ25F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
|
||||||
16
dist_ts/mail/delivery/smtpclient/index.d.ts
vendored
Normal file
16
dist_ts/mail/delivery/smtpclient/index.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Module Exports
|
||||||
|
* Modular SMTP client implementation for robust email delivery
|
||||||
|
*/
|
||||||
|
export * from './smtp-client.js';
|
||||||
|
export * from './create-client.js';
|
||||||
|
export * from './connection-manager.js';
|
||||||
|
export * from './command-handler.js';
|
||||||
|
export * from './auth-handler.js';
|
||||||
|
export * from './tls-handler.js';
|
||||||
|
export * from './error-handler.js';
|
||||||
|
export * from './interfaces.js';
|
||||||
|
export * from './constants.js';
|
||||||
|
export * from './utils/validation.js';
|
||||||
|
export * from './utils/logging.js';
|
||||||
|
export * from './utils/helpers.js';
|
||||||
21
dist_ts/mail/delivery/smtpclient/index.js
Normal file
21
dist_ts/mail/delivery/smtpclient/index.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Module Exports
|
||||||
|
* Modular SMTP client implementation for robust email delivery
|
||||||
|
*/
|
||||||
|
// Main client class and factory
|
||||||
|
export * from './smtp-client.js';
|
||||||
|
export * from './create-client.js';
|
||||||
|
// Core handlers
|
||||||
|
export * from './connection-manager.js';
|
||||||
|
export * from './command-handler.js';
|
||||||
|
export * from './auth-handler.js';
|
||||||
|
export * from './tls-handler.js';
|
||||||
|
export * from './error-handler.js';
|
||||||
|
// Interfaces and types
|
||||||
|
export * from './interfaces.js';
|
||||||
|
export * from './constants.js';
|
||||||
|
// Utilities
|
||||||
|
export * from './utils/validation.js';
|
||||||
|
export * from './utils/logging.js';
|
||||||
|
export * from './utils/helpers.js';
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L3NtdHBjbGllbnQvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsZ0NBQWdDO0FBQ2hDLGNBQWMsa0JBQWtCLENBQUM7QUFDakMsY0FBYyxvQkFBb0IsQ0FBQztBQUVuQyxnQkFBZ0I7QUFDaEIsY0FBYyx5QkFBeUIsQ0FBQztBQUN4QyxjQUFjLHNCQUFzQixDQUFDO0FBQ3JDLGNBQWMsbUJBQW1CLENBQUM7QUFDbEMsY0FBYyxrQkFBa0IsQ0FBQztBQUNqQyxjQUFjLG9CQUFvQixDQUFDO0FBRW5DLHVCQUF1QjtBQUN2QixjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMsZ0JBQWdCLENBQUM7QUFFL0IsWUFBWTtBQUNaLGNBQWMsdUJBQXVCLENBQUM7QUFDdEMsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxjQUFjLG9CQUFvQixDQUFDIn0=
|
||||||
183
dist_ts/mail/delivery/smtpclient/interfaces.d.ts
vendored
Normal file
183
dist_ts/mail/delivery/smtpclient/interfaces.d.ts
vendored
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Interfaces and Types
|
||||||
|
* All interface definitions for the modular SMTP client
|
||||||
|
*/
|
||||||
|
import type * as tls from 'node:tls';
|
||||||
|
import type * as net from 'node:net';
|
||||||
|
/**
|
||||||
|
* SMTP client connection options
|
||||||
|
*/
|
||||||
|
export interface ISmtpClientOptions {
|
||||||
|
/** Hostname of the SMTP server */
|
||||||
|
host: string;
|
||||||
|
/** Port to connect to */
|
||||||
|
port: number;
|
||||||
|
/** Whether to use TLS for the connection */
|
||||||
|
secure?: boolean;
|
||||||
|
/** Connection timeout in milliseconds */
|
||||||
|
connectionTimeout?: number;
|
||||||
|
/** Socket timeout in milliseconds */
|
||||||
|
socketTimeout?: number;
|
||||||
|
/** Domain name for EHLO command */
|
||||||
|
domain?: string;
|
||||||
|
/** Authentication options */
|
||||||
|
auth?: ISmtpAuthOptions;
|
||||||
|
/** TLS options */
|
||||||
|
tls?: tls.ConnectionOptions;
|
||||||
|
/** Maximum number of connections in pool */
|
||||||
|
pool?: boolean;
|
||||||
|
maxConnections?: number;
|
||||||
|
maxMessages?: number;
|
||||||
|
/** Enable debug logging */
|
||||||
|
debug?: boolean;
|
||||||
|
/** Proxy settings */
|
||||||
|
proxy?: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Authentication options for SMTP
|
||||||
|
*/
|
||||||
|
export interface ISmtpAuthOptions {
|
||||||
|
/** Username */
|
||||||
|
user?: string;
|
||||||
|
/** Password */
|
||||||
|
pass?: string;
|
||||||
|
/** OAuth2 settings */
|
||||||
|
oauth2?: IOAuth2Options;
|
||||||
|
/** Authentication method preference */
|
||||||
|
method?: 'PLAIN' | 'LOGIN' | 'OAUTH2' | 'AUTO';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* OAuth2 authentication options
|
||||||
|
*/
|
||||||
|
export interface IOAuth2Options {
|
||||||
|
/** OAuth2 user identifier */
|
||||||
|
user: string;
|
||||||
|
/** OAuth2 client ID */
|
||||||
|
clientId: string;
|
||||||
|
/** OAuth2 client secret */
|
||||||
|
clientSecret: string;
|
||||||
|
/** OAuth2 refresh token */
|
||||||
|
refreshToken: string;
|
||||||
|
/** OAuth2 access token */
|
||||||
|
accessToken?: string;
|
||||||
|
/** Token expiry time */
|
||||||
|
expires?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Result of an email send operation
|
||||||
|
*/
|
||||||
|
export interface ISmtpSendResult {
|
||||||
|
/** Whether the send was successful */
|
||||||
|
success: boolean;
|
||||||
|
/** Message ID from server */
|
||||||
|
messageId?: string;
|
||||||
|
/** List of accepted recipients */
|
||||||
|
acceptedRecipients: string[];
|
||||||
|
/** List of rejected recipients */
|
||||||
|
rejectedRecipients: string[];
|
||||||
|
/** Error information if failed */
|
||||||
|
error?: Error;
|
||||||
|
/** Server response */
|
||||||
|
response?: string;
|
||||||
|
/** Envelope information */
|
||||||
|
envelope?: ISmtpEnvelope;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP envelope information
|
||||||
|
*/
|
||||||
|
export interface ISmtpEnvelope {
|
||||||
|
/** Sender address */
|
||||||
|
from: string;
|
||||||
|
/** Recipient addresses */
|
||||||
|
to: string[];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Connection pool status
|
||||||
|
*/
|
||||||
|
export interface IConnectionPoolStatus {
|
||||||
|
/** Total connections in pool */
|
||||||
|
total: number;
|
||||||
|
/** Active connections */
|
||||||
|
active: number;
|
||||||
|
/** Idle connections */
|
||||||
|
idle: number;
|
||||||
|
/** Pending connection requests */
|
||||||
|
pending: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP command response
|
||||||
|
*/
|
||||||
|
export interface ISmtpResponse {
|
||||||
|
/** Response code */
|
||||||
|
code: number;
|
||||||
|
/** Response message */
|
||||||
|
message: string;
|
||||||
|
/** Enhanced status code */
|
||||||
|
enhancedCode?: string;
|
||||||
|
/** Raw response */
|
||||||
|
raw: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Connection state
|
||||||
|
*/
|
||||||
|
export declare enum ConnectionState {
|
||||||
|
DISCONNECTED = "disconnected",
|
||||||
|
CONNECTING = "connecting",
|
||||||
|
CONNECTED = "connected",
|
||||||
|
AUTHENTICATED = "authenticated",
|
||||||
|
READY = "ready",
|
||||||
|
BUSY = "busy",
|
||||||
|
CLOSING = "closing",
|
||||||
|
ERROR = "error"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP capabilities
|
||||||
|
*/
|
||||||
|
export interface ISmtpCapabilities {
|
||||||
|
/** Supported extensions */
|
||||||
|
extensions: Set<string>;
|
||||||
|
/** Maximum message size */
|
||||||
|
maxSize?: number;
|
||||||
|
/** Supported authentication methods */
|
||||||
|
authMethods: Set<string>;
|
||||||
|
/** Support for pipelining */
|
||||||
|
pipelining: boolean;
|
||||||
|
/** Support for STARTTLS */
|
||||||
|
starttls: boolean;
|
||||||
|
/** Support for 8BITMIME */
|
||||||
|
eightBitMime: boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Internal connection interface
|
||||||
|
*/
|
||||||
|
export interface ISmtpConnection {
|
||||||
|
/** Socket connection */
|
||||||
|
socket: net.Socket | tls.TLSSocket;
|
||||||
|
/** Connection state */
|
||||||
|
state: ConnectionState;
|
||||||
|
/** Server capabilities */
|
||||||
|
capabilities?: ISmtpCapabilities;
|
||||||
|
/** Connection options */
|
||||||
|
options: ISmtpClientOptions;
|
||||||
|
/** Whether connection is secure */
|
||||||
|
secure: boolean;
|
||||||
|
/** Connection creation time */
|
||||||
|
createdAt: Date;
|
||||||
|
/** Last activity time */
|
||||||
|
lastActivity: Date;
|
||||||
|
/** Number of messages sent */
|
||||||
|
messageCount: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Error context for detailed error reporting
|
||||||
|
*/
|
||||||
|
export interface ISmtpErrorContext {
|
||||||
|
/** Command that caused the error */
|
||||||
|
command?: string;
|
||||||
|
/** Server response */
|
||||||
|
response?: ISmtpResponse;
|
||||||
|
/** Connection state */
|
||||||
|
connectionState?: ConnectionState;
|
||||||
|
/** Additional context data */
|
||||||
|
data?: Record<string, any>;
|
||||||
|
}
|
||||||
19
dist_ts/mail/delivery/smtpclient/interfaces.js
Normal file
19
dist_ts/mail/delivery/smtpclient/interfaces.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Interfaces and Types
|
||||||
|
* All interface definitions for the modular SMTP client
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Connection state
|
||||||
|
*/
|
||||||
|
export var ConnectionState;
|
||||||
|
(function (ConnectionState) {
|
||||||
|
ConnectionState["DISCONNECTED"] = "disconnected";
|
||||||
|
ConnectionState["CONNECTING"] = "connecting";
|
||||||
|
ConnectionState["CONNECTED"] = "connected";
|
||||||
|
ConnectionState["AUTHENTICATED"] = "authenticated";
|
||||||
|
ConnectionState["READY"] = "ready";
|
||||||
|
ConnectionState["BUSY"] = "busy";
|
||||||
|
ConnectionState["CLOSING"] = "closing";
|
||||||
|
ConnectionState["ERROR"] = "error";
|
||||||
|
})(ConnectionState || (ConnectionState = {}));
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cGNsaWVudC9pbnRlcmZhY2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQTZKSDs7R0FFRztBQUNILE1BQU0sQ0FBTixJQUFZLGVBU1g7QUFURCxXQUFZLGVBQWU7SUFDekIsZ0RBQTZCLENBQUE7SUFDN0IsNENBQXlCLENBQUE7SUFDekIsMENBQXVCLENBQUE7SUFDdkIsa0RBQStCLENBQUE7SUFDL0Isa0NBQWUsQ0FBQTtJQUNmLGdDQUFhLENBQUE7SUFDYixzQ0FBbUIsQ0FBQTtJQUNuQixrQ0FBZSxDQUFBO0FBQ2pCLENBQUMsRUFUVyxlQUFlLEtBQWYsZUFBZSxRQVMxQiJ9
|
||||||
58
dist_ts/mail/delivery/smtpclient/smtp-client.d.ts
vendored
Normal file
58
dist_ts/mail/delivery/smtpclient/smtp-client.d.ts
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Core Implementation
|
||||||
|
* Main client class with delegation to handlers
|
||||||
|
*/
|
||||||
|
import { EventEmitter } from 'node:events';
|
||||||
|
import type { Email } from '../../core/classes.email.js';
|
||||||
|
import type { ISmtpClientOptions, ISmtpSendResult, IConnectionPoolStatus } from './interfaces.js';
|
||||||
|
import type { ConnectionManager } from './connection-manager.js';
|
||||||
|
import type { CommandHandler } from './command-handler.js';
|
||||||
|
import type { AuthHandler } from './auth-handler.js';
|
||||||
|
import type { TlsHandler } from './tls-handler.js';
|
||||||
|
import type { SmtpErrorHandler } from './error-handler.js';
|
||||||
|
interface ISmtpClientDependencies {
|
||||||
|
options: ISmtpClientOptions;
|
||||||
|
connectionManager: ConnectionManager;
|
||||||
|
commandHandler: CommandHandler;
|
||||||
|
authHandler: AuthHandler;
|
||||||
|
tlsHandler: TlsHandler;
|
||||||
|
errorHandler: SmtpErrorHandler;
|
||||||
|
}
|
||||||
|
export declare class SmtpClient extends EventEmitter {
|
||||||
|
private options;
|
||||||
|
private connectionManager;
|
||||||
|
private commandHandler;
|
||||||
|
private authHandler;
|
||||||
|
private tlsHandler;
|
||||||
|
private errorHandler;
|
||||||
|
private isShuttingDown;
|
||||||
|
constructor(dependencies: ISmtpClientDependencies);
|
||||||
|
/**
|
||||||
|
* Send an email
|
||||||
|
*/
|
||||||
|
sendMail(email: Email): Promise<ISmtpSendResult>;
|
||||||
|
/**
|
||||||
|
* Test connection to SMTP server
|
||||||
|
*/
|
||||||
|
verify(): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* Check if client is connected
|
||||||
|
*/
|
||||||
|
isConnected(): boolean;
|
||||||
|
/**
|
||||||
|
* Get connection pool status
|
||||||
|
*/
|
||||||
|
getPoolStatus(): IConnectionPoolStatus;
|
||||||
|
/**
|
||||||
|
* Update client options
|
||||||
|
*/
|
||||||
|
updateOptions(newOptions: Partial<ISmtpClientOptions>): void;
|
||||||
|
/**
|
||||||
|
* Close all connections and shutdown client
|
||||||
|
*/
|
||||||
|
close(): Promise<void>;
|
||||||
|
private formatEmailData;
|
||||||
|
private extractMessageId;
|
||||||
|
private setupEventForwarding;
|
||||||
|
}
|
||||||
|
export {};
|
||||||
285
dist_ts/mail/delivery/smtpclient/smtp-client.js
Normal file
285
dist_ts/mail/delivery/smtpclient/smtp-client.js
Normal file
File diff suppressed because one or more lines are too long
33
dist_ts/mail/delivery/smtpclient/tls-handler.d.ts
vendored
Normal file
33
dist_ts/mail/delivery/smtpclient/tls-handler.d.ts
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client TLS Handler
|
||||||
|
* TLS and STARTTLS client functionality
|
||||||
|
*/
|
||||||
|
import * as tls from 'node:tls';
|
||||||
|
import type { ISmtpConnection, ISmtpClientOptions } from './interfaces.js';
|
||||||
|
import type { CommandHandler } from './command-handler.js';
|
||||||
|
export declare class TlsHandler {
|
||||||
|
private options;
|
||||||
|
private commandHandler;
|
||||||
|
constructor(options: ISmtpClientOptions, commandHandler: CommandHandler);
|
||||||
|
/**
|
||||||
|
* Upgrade connection to TLS using STARTTLS
|
||||||
|
*/
|
||||||
|
upgradeToTLS(connection: ISmtpConnection): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Create a direct TLS connection
|
||||||
|
*/
|
||||||
|
createTLSConnection(host: string, port: number): Promise<tls.TLSSocket>;
|
||||||
|
/**
|
||||||
|
* Validate TLS certificate
|
||||||
|
*/
|
||||||
|
validateCertificate(socket: tls.TLSSocket): boolean;
|
||||||
|
/**
|
||||||
|
* Get TLS connection information
|
||||||
|
*/
|
||||||
|
getTLSInfo(socket: tls.TLSSocket): any;
|
||||||
|
/**
|
||||||
|
* Check if TLS upgrade is required or recommended
|
||||||
|
*/
|
||||||
|
shouldUseTLS(connection: ISmtpConnection): boolean;
|
||||||
|
private performTLSUpgrade;
|
||||||
|
}
|
||||||
204
dist_ts/mail/delivery/smtpclient/tls-handler.js
Normal file
204
dist_ts/mail/delivery/smtpclient/tls-handler.js
Normal file
File diff suppressed because one or more lines are too long
77
dist_ts/mail/delivery/smtpclient/utils/helpers.d.ts
vendored
Normal file
77
dist_ts/mail/delivery/smtpclient/utils/helpers.d.ts
vendored
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Helper Functions
|
||||||
|
* Protocol helper functions and utilities
|
||||||
|
*/
|
||||||
|
import type { ISmtpResponse, ISmtpCapabilities } from '../interfaces.js';
|
||||||
|
/**
|
||||||
|
* Parse SMTP server response
|
||||||
|
*/
|
||||||
|
export declare function parseSmtpResponse(data: string): ISmtpResponse;
|
||||||
|
/**
|
||||||
|
* Parse EHLO response and extract capabilities
|
||||||
|
*/
|
||||||
|
export declare function parseEhloResponse(response: string): ISmtpCapabilities;
|
||||||
|
/**
|
||||||
|
* Format SMTP command with proper line ending
|
||||||
|
*/
|
||||||
|
export declare function formatCommand(command: string, ...args: string[]): string;
|
||||||
|
/**
|
||||||
|
* Encode authentication string for AUTH PLAIN
|
||||||
|
*/
|
||||||
|
export declare function encodeAuthPlain(username: string, password: string): string;
|
||||||
|
/**
|
||||||
|
* Encode authentication string for AUTH LOGIN
|
||||||
|
*/
|
||||||
|
export declare function encodeAuthLogin(value: string): string;
|
||||||
|
/**
|
||||||
|
* Generate OAuth2 authentication string
|
||||||
|
*/
|
||||||
|
export declare function generateOAuth2String(username: string, accessToken: string): string;
|
||||||
|
/**
|
||||||
|
* Check if response code indicates success
|
||||||
|
*/
|
||||||
|
export declare function isSuccessCode(code: number): boolean;
|
||||||
|
/**
|
||||||
|
* Check if response code indicates temporary failure
|
||||||
|
*/
|
||||||
|
export declare function isTemporaryFailure(code: number): boolean;
|
||||||
|
/**
|
||||||
|
* Check if response code indicates permanent failure
|
||||||
|
*/
|
||||||
|
export declare function isPermanentFailure(code: number): boolean;
|
||||||
|
/**
|
||||||
|
* Escape email address for SMTP commands
|
||||||
|
*/
|
||||||
|
export declare function escapeEmailAddress(email: string): string;
|
||||||
|
/**
|
||||||
|
* Extract email address from angle brackets
|
||||||
|
*/
|
||||||
|
export declare function extractEmailAddress(email: string): string;
|
||||||
|
/**
|
||||||
|
* Generate unique connection ID
|
||||||
|
*/
|
||||||
|
export declare function generateConnectionId(): string;
|
||||||
|
/**
|
||||||
|
* Format timeout duration for human readability
|
||||||
|
*/
|
||||||
|
export declare function formatTimeout(milliseconds: number): string;
|
||||||
|
/**
|
||||||
|
* Validate and normalize email data size
|
||||||
|
*/
|
||||||
|
export declare function validateEmailSize(emailData: string, maxSize?: number): boolean;
|
||||||
|
/**
|
||||||
|
* Clean sensitive data from logs
|
||||||
|
*/
|
||||||
|
export declare function sanitizeForLogging(data: any): any;
|
||||||
|
/**
|
||||||
|
* Calculate exponential backoff delay
|
||||||
|
*/
|
||||||
|
export declare function calculateBackoffDelay(attempt: number, baseDelay?: number): number;
|
||||||
|
/**
|
||||||
|
* Parse enhanced status code
|
||||||
|
*/
|
||||||
|
export declare function parseEnhancedStatusCode(code: string): {
|
||||||
|
class: number;
|
||||||
|
subject: number;
|
||||||
|
detail: number;
|
||||||
|
} | null;
|
||||||
196
dist_ts/mail/delivery/smtpclient/utils/helpers.js
Normal file
196
dist_ts/mail/delivery/smtpclient/utils/helpers.js
Normal file
File diff suppressed because one or more lines are too long
46
dist_ts/mail/delivery/smtpclient/utils/logging.d.ts
vendored
Normal file
46
dist_ts/mail/delivery/smtpclient/utils/logging.d.ts
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Logging Utilities
|
||||||
|
* Client-side logging utilities for SMTP operations
|
||||||
|
*/
|
||||||
|
import type { ISmtpResponse, ISmtpClientOptions } from '../interfaces.js';
|
||||||
|
export interface ISmtpClientLogData {
|
||||||
|
component: string;
|
||||||
|
host?: string;
|
||||||
|
port?: number;
|
||||||
|
secure?: boolean;
|
||||||
|
command?: string;
|
||||||
|
response?: ISmtpResponse;
|
||||||
|
error?: Error;
|
||||||
|
connectionId?: string;
|
||||||
|
messageId?: string;
|
||||||
|
duration?: number;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Log SMTP client connection events
|
||||||
|
*/
|
||||||
|
export declare function logConnection(event: 'connecting' | 'connected' | 'disconnected' | 'error', options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||||
|
/**
|
||||||
|
* Log SMTP command execution
|
||||||
|
*/
|
||||||
|
export declare function logCommand(command: string, response?: ISmtpResponse, options?: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||||
|
/**
|
||||||
|
* Log authentication events
|
||||||
|
*/
|
||||||
|
export declare function logAuthentication(event: 'start' | 'success' | 'failure', method: string, options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||||
|
/**
|
||||||
|
* Log TLS/STARTTLS events
|
||||||
|
*/
|
||||||
|
export declare function logTLS(event: 'starttls_start' | 'starttls_success' | 'starttls_failure' | 'tls_connected', options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||||
|
/**
|
||||||
|
* Log email sending events
|
||||||
|
*/
|
||||||
|
export declare function logEmailSend(event: 'start' | 'success' | 'failure', recipients: string[], options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||||
|
/**
|
||||||
|
* Log performance metrics
|
||||||
|
*/
|
||||||
|
export declare function logPerformance(operation: string, duration: number, options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||||
|
/**
|
||||||
|
* Log debug information (only when debug is enabled)
|
||||||
|
*/
|
||||||
|
export declare function logDebug(message: string, options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||||
153
dist_ts/mail/delivery/smtpclient/utils/logging.js
Normal file
153
dist_ts/mail/delivery/smtpclient/utils/logging.js
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Logging Utilities
|
||||||
|
* Client-side logging utilities for SMTP operations
|
||||||
|
*/
|
||||||
|
import { logger } from '../../../../logger.js';
|
||||||
|
/**
|
||||||
|
* Log SMTP client connection events
|
||||||
|
*/
|
||||||
|
export function logConnection(event, options, data) {
|
||||||
|
const logData = {
|
||||||
|
component: 'smtp-client',
|
||||||
|
event,
|
||||||
|
host: options.host,
|
||||||
|
port: options.port,
|
||||||
|
secure: options.secure,
|
||||||
|
...data
|
||||||
|
};
|
||||||
|
switch (event) {
|
||||||
|
case 'connecting':
|
||||||
|
logger.info('SMTP client connecting', logData);
|
||||||
|
break;
|
||||||
|
case 'connected':
|
||||||
|
logger.info('SMTP client connected', logData);
|
||||||
|
break;
|
||||||
|
case 'disconnected':
|
||||||
|
logger.info('SMTP client disconnected', logData);
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
logger.error('SMTP client connection error', logData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Log SMTP command execution
|
||||||
|
*/
|
||||||
|
export function logCommand(command, response, options, data) {
|
||||||
|
const logData = {
|
||||||
|
component: 'smtp-client',
|
||||||
|
command,
|
||||||
|
response,
|
||||||
|
host: options?.host,
|
||||||
|
port: options?.port,
|
||||||
|
...data
|
||||||
|
};
|
||||||
|
if (response && response.code >= 400) {
|
||||||
|
logger.warn('SMTP command failed', logData);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.debug('SMTP command executed', logData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Log authentication events
|
||||||
|
*/
|
||||||
|
export function logAuthentication(event, method, options, data) {
|
||||||
|
const logData = {
|
||||||
|
component: 'smtp-client',
|
||||||
|
event: `auth_${event}`,
|
||||||
|
authMethod: method,
|
||||||
|
host: options.host,
|
||||||
|
port: options.port,
|
||||||
|
...data
|
||||||
|
};
|
||||||
|
switch (event) {
|
||||||
|
case 'start':
|
||||||
|
logger.debug('SMTP authentication started', logData);
|
||||||
|
break;
|
||||||
|
case 'success':
|
||||||
|
logger.info('SMTP authentication successful', logData);
|
||||||
|
break;
|
||||||
|
case 'failure':
|
||||||
|
logger.error('SMTP authentication failed', logData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Log TLS/STARTTLS events
|
||||||
|
*/
|
||||||
|
export function logTLS(event, options, data) {
|
||||||
|
const logData = {
|
||||||
|
component: 'smtp-client',
|
||||||
|
event,
|
||||||
|
host: options.host,
|
||||||
|
port: options.port,
|
||||||
|
...data
|
||||||
|
};
|
||||||
|
if (event.includes('failure')) {
|
||||||
|
logger.error('SMTP TLS operation failed', logData);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.info('SMTP TLS operation', logData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Log email sending events
|
||||||
|
*/
|
||||||
|
export function logEmailSend(event, recipients, options, data) {
|
||||||
|
const logData = {
|
||||||
|
component: 'smtp-client',
|
||||||
|
event: `send_${event}`,
|
||||||
|
recipientCount: recipients.length,
|
||||||
|
recipients: recipients.slice(0, 5), // Only log first 5 recipients for privacy
|
||||||
|
host: options.host,
|
||||||
|
port: options.port,
|
||||||
|
...data
|
||||||
|
};
|
||||||
|
switch (event) {
|
||||||
|
case 'start':
|
||||||
|
logger.info('SMTP email send started', logData);
|
||||||
|
break;
|
||||||
|
case 'success':
|
||||||
|
logger.info('SMTP email send successful', logData);
|
||||||
|
break;
|
||||||
|
case 'failure':
|
||||||
|
logger.error('SMTP email send failed', logData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Log performance metrics
|
||||||
|
*/
|
||||||
|
export function logPerformance(operation, duration, options, data) {
|
||||||
|
const logData = {
|
||||||
|
component: 'smtp-client',
|
||||||
|
operation,
|
||||||
|
duration,
|
||||||
|
host: options.host,
|
||||||
|
port: options.port,
|
||||||
|
...data
|
||||||
|
};
|
||||||
|
if (duration > 10000) { // Log slow operations (>10s)
|
||||||
|
logger.warn('SMTP slow operation detected', logData);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.debug('SMTP operation performance', logData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Log debug information (only when debug is enabled)
|
||||||
|
*/
|
||||||
|
export function logDebug(message, options, data) {
|
||||||
|
if (!options.debug) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const logData = {
|
||||||
|
component: 'smtp-client-debug',
|
||||||
|
host: options.host,
|
||||||
|
port: options.port,
|
||||||
|
...data
|
||||||
|
};
|
||||||
|
logger.debug(`[SMTP Client Debug] ${message}`, logData);
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2luZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cGNsaWVudC91dGlscy9sb2dnaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQWlCL0M7O0dBRUc7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUMzQixLQUE0RCxFQUM1RCxPQUEyQixFQUMzQixJQUFrQztJQUVsQyxNQUFNLE9BQU8sR0FBdUI7UUFDbEMsU0FBUyxFQUFFLGFBQWE7UUFDeEIsS0FBSztRQUNMLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSTtRQUNsQixJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUk7UUFDbEIsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNO1FBQ3RCLEdBQUcsSUFBSTtLQUNSLENBQUM7SUFFRixRQUFRLEtBQUssRUFBRSxDQUFDO1FBQ2QsS0FBSyxZQUFZO1lBQ2YsTUFBTSxDQUFDLElBQUksQ0FBQyx3QkFBd0IsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUMvQyxNQUFNO1FBQ1IsS0FBSyxXQUFXO1lBQ2QsTUFBTSxDQUFDLElBQUksQ0FBQyx1QkFBdUIsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUM5QyxNQUFNO1FBQ1IsS0FBSyxjQUFjO1lBQ2pCLE1BQU0sQ0FBQyxJQUFJLENBQUMsMEJBQTBCLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDakQsTUFBTTtRQUNSLEtBQUssT0FBTztZQUNWLE1BQU0sQ0FBQyxLQUFLLENBQUMsOEJBQThCLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDdEQsTUFBTTtJQUNWLENBQUM7QUFDSCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLFVBQVUsVUFBVSxDQUN4QixPQUFlLEVBQ2YsUUFBd0IsRUFDeEIsT0FBNEIsRUFDNUIsSUFBa0M7SUFFbEMsTUFBTSxPQUFPLEdBQXVCO1FBQ2xDLFNBQVMsRUFBRSxhQUFhO1FBQ3hCLE9BQU87UUFDUCxRQUFRO1FBQ1IsSUFBSSxFQUFFLE9BQU8sRUFBRSxJQUFJO1FBQ25CLElBQUksRUFBRSxPQUFPLEVBQUUsSUFBSTtRQUNuQixHQUFHLElBQUk7S0FDUixDQUFDO0lBRUYsSUFBSSxRQUFRLElBQUksUUFBUSxDQUFDLElBQUksSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUNyQyxNQUFNLENBQUMsSUFBSSxDQUFDLHFCQUFxQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQzlDLENBQUM7U0FBTSxDQUFDO1FBQ04sTUFBTSxDQUFDLEtBQUssQ0FBQyx1QkFBdUIsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNqRCxDQUFDO0FBQ0gsQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUMvQixLQUFzQyxFQUN0QyxNQUFjLEVBQ2QsT0FBMkIsRUFDM0IsSUFBa0M7SUFFbEMsTUFBTSxPQUFPLEdBQXVCO1FBQ2xDLFNBQVMsRUFBRSxhQUFhO1FBQ3hCLEtBQUssRUFBRSxRQUFRLEtBQUssRUFBRTtRQUN0QixVQUFVLEVBQUUsTUFBTTtRQUNsQixJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUk7UUFDbEIsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJO1FBQ2xCLEdBQUcsSUFBSTtLQUNSLENBQUM7SUFFRixRQUFRLEtBQUssRUFBRSxDQUFDO1FBQ2QsS0FBSyxPQUFPO1lBQ1YsTUFBTSxDQUFDLEtBQUssQ0FBQyw2QkFBNkIsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUNyRCxNQUFNO1FBQ1IsS0FBSyxTQUFTO1lBQ1osTUFBTSxDQUFDLElBQUksQ0FBQyxnQ0FBZ0MsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUN2RCxNQUFNO1FBQ1IsS0FBSyxTQUFTO1lBQ1osTUFBTSxDQUFDLEtBQUssQ0FBQyw0QkFBNEIsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUNwRCxNQUFNO0lBQ1YsQ0FBQztBQUNILENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxNQUFNLENBQ3BCLEtBQW1GLEVBQ25GLE9BQTJCLEVBQzNCLElBQWtDO0lBRWxDLE1BQU0sT0FBTyxHQUF1QjtRQUNsQyxTQUFTLEVBQUUsYUFBYTtRQUN4QixLQUFLO1FBQ0wsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJO1FBQ2xCLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSTtRQUNsQixHQUFHLElBQUk7S0FDUixDQUFDO0lBRUYsSUFBSSxLQUFLLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7UUFDOUIsTUFBTSxDQUFDLEtBQUssQ0FBQywyQkFBMkIsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNyRCxDQUFDO1NBQU0sQ0FBQztRQUNOLE1BQU0sQ0FBQyxJQUFJLENBQUMsb0JBQW9CLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDN0MsQ0FBQztBQUNILENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxZQUFZLENBQzFCLEtBQXNDLEVBQ3RDLFVBQW9CLEVBQ3BCLE9BQTJCLEVBQzNCLElBQWtDO0lBRWxDLE1BQU0sT0FBTyxHQUF1QjtRQUNsQyxTQUFTLEVBQUUsYUFBYTtRQUN4QixLQUFLLEVBQUUsUUFBUSxLQUFLLEVBQUU7UUFDdEIsY0FBYyxFQUFFLFVBQVUsQ0FBQyxNQUFNO1FBQ2pDLFVBQVUsRUFBRSxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRSwwQ0FBMEM7UUFDOUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJO1FBQ2xCLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSTtRQUNsQixHQUFHLElBQUk7S0FDUixDQUFDO0lBRUYsUUFBUSxLQUFLLEVBQUUsQ0FBQztRQUNkLEtBQUssT0FBTztZQUNWLE1BQU0sQ0FBQyxJQUFJLENBQUMseUJBQXlCLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDaEQsTUFBTTtRQUNSLEtBQUssU0FBUztZQUNaLE1BQU0sQ0FBQyxJQUFJLENBQUMsNEJBQTRCLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDbkQsTUFBTTtRQUNSLEtBQUssU0FBUztZQUNaLE1BQU0sQ0FBQyxLQUFLLENBQUMsd0JBQXdCLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDaEQsTUFBTTtJQUNWLENBQUM7QUFDSCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLFVBQVUsY0FBYyxDQUM1QixTQUFpQixFQUNqQixRQUFnQixFQUNoQixPQUEyQixFQUMzQixJQUFrQztJQUVsQyxNQUFNLE9BQU8sR0FBdUI7UUFDbEMsU0FBUyxFQUFFLGFBQWE7UUFDeEIsU0FBUztRQUNULFFBQVE7UUFDUixJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUk7UUFDbEIsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJO1FBQ2xCLEdBQUcsSUFBSTtLQUNSLENBQUM7SUFFRixJQUFJLFFBQVEsR0FBRyxLQUFLLEVBQUUsQ0FBQyxDQUFDLDZCQUE2QjtRQUNuRCxNQUFNLENBQUMsSUFBSSxDQUFDLDhCQUE4QixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3ZELENBQUM7U0FBTSxDQUFDO1FBQ04sTUFBTSxDQUFDLEtBQUssQ0FBQyw0QkFBNEIsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUN0RCxDQUFDO0FBQ0gsQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxVQUFVLFFBQVEsQ0FDdEIsT0FBZSxFQUNmLE9BQTJCLEVBQzNCLElBQWtDO0lBRWxDLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDbkIsT0FBTztJQUNULENBQUM7SUFFRCxNQUFNLE9BQU8sR0FBdUI7UUFDbEMsU0FBUyxFQUFFLG1CQUFtQjtRQUM5QixJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUk7UUFDbEIsSUFBSSxFQUFFLE9BQU8sQ0FBQyxJQUFJO1FBQ2xCLEdBQUcsSUFBSTtLQUNSLENBQUM7SUFFRixNQUFNLENBQUMsS0FBSyxDQUFDLHVCQUF1QixPQUFPLEVBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUMxRCxDQUFDIn0=
|
||||||
38
dist_ts/mail/delivery/smtpclient/utils/validation.d.ts
vendored
Normal file
38
dist_ts/mail/delivery/smtpclient/utils/validation.d.ts
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Client Validation Utilities
|
||||||
|
* Input validation functions for SMTP client operations
|
||||||
|
*/
|
||||||
|
import type { ISmtpClientOptions, ISmtpAuthOptions } from '../interfaces.js';
|
||||||
|
/**
|
||||||
|
* Validate email address format
|
||||||
|
* Supports RFC-compliant addresses including empty return paths for bounces
|
||||||
|
*/
|
||||||
|
export declare function validateEmailAddress(email: string): boolean;
|
||||||
|
/**
|
||||||
|
* Validate SMTP client options
|
||||||
|
*/
|
||||||
|
export declare function validateClientOptions(options: ISmtpClientOptions): string[];
|
||||||
|
/**
|
||||||
|
* Validate authentication options
|
||||||
|
*/
|
||||||
|
export declare function validateAuthOptions(auth: ISmtpAuthOptions): string[];
|
||||||
|
/**
|
||||||
|
* Validate hostname format
|
||||||
|
*/
|
||||||
|
export declare function validateHostname(hostname: string): boolean;
|
||||||
|
/**
|
||||||
|
* Validate port number
|
||||||
|
*/
|
||||||
|
export declare function validatePort(port: number): boolean;
|
||||||
|
/**
|
||||||
|
* Sanitize and validate domain name for EHLO
|
||||||
|
*/
|
||||||
|
export declare function validateAndSanitizeDomain(domain: string): string;
|
||||||
|
/**
|
||||||
|
* Validate recipient list
|
||||||
|
*/
|
||||||
|
export declare function validateRecipients(recipients: string | string[]): string[];
|
||||||
|
/**
|
||||||
|
* Validate sender address
|
||||||
|
*/
|
||||||
|
export declare function validateSender(sender: string): boolean;
|
||||||
139
dist_ts/mail/delivery/smtpclient/utils/validation.js
Normal file
139
dist_ts/mail/delivery/smtpclient/utils/validation.js
Normal file
File diff suppressed because one or more lines are too long
7
dist_ts/mail/index.d.ts
vendored
Normal file
7
dist_ts/mail/index.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export * from './routing/index.js';
|
||||||
|
export * from './security/index.js';
|
||||||
|
import * as Core from './core/index.js';
|
||||||
|
import * as Delivery from './delivery/index.js';
|
||||||
|
export { Core, Delivery };
|
||||||
|
import { Email } from './core/classes.email.js';
|
||||||
|
export { Email, };
|
||||||
12
dist_ts/mail/index.js
Normal file
12
dist_ts/mail/index.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Export all mail modules for simplified imports
|
||||||
|
export * from './routing/index.js';
|
||||||
|
export * from './security/index.js';
|
||||||
|
// Make the core and delivery modules accessible
|
||||||
|
import * as Core from './core/index.js';
|
||||||
|
import * as Delivery from './delivery/index.js';
|
||||||
|
export { Core, Delivery };
|
||||||
|
// For direct imports
|
||||||
|
import { Email } from './core/classes.email.js';
|
||||||
|
// Re-export commonly used classes
|
||||||
|
export { Email, };
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9tYWlsL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGlEQUFpRDtBQUNqRCxjQUFjLG9CQUFvQixDQUFDO0FBQ25DLGNBQWMscUJBQXFCLENBQUM7QUFFcEMsZ0RBQWdEO0FBQ2hELE9BQU8sS0FBSyxJQUFJLE1BQU0saUJBQWlCLENBQUM7QUFDeEMsT0FBTyxLQUFLLFFBQVEsTUFBTSxxQkFBcUIsQ0FBQztBQUVoRCxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUFDO0FBRTFCLHFCQUFxQjtBQUNyQixPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFFaEQsa0NBQWtDO0FBQ2xDLE9BQU8sRUFDTCxLQUFLLEdBQ04sQ0FBQyJ9
|
||||||
79
dist_ts/mail/routing/classes.dns.manager.d.ts
vendored
Normal file
79
dist_ts/mail/routing/classes.dns.manager.d.ts
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import type { IEmailDomainConfig } from './interfaces.js';
|
||||||
|
/** External DcRouter interface shape used by DnsManager */
|
||||||
|
interface IDcRouterLike {
|
||||||
|
storageManager: IStorageManagerLike;
|
||||||
|
dnsServer?: any;
|
||||||
|
options?: {
|
||||||
|
dnsNsDomains?: string[];
|
||||||
|
dnsScopes?: string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/** External StorageManager interface shape used by DnsManager */
|
||||||
|
interface IStorageManagerLike {
|
||||||
|
get(key: string): Promise<string | null>;
|
||||||
|
set(key: string, value: string): Promise<void>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* DNS validation result
|
||||||
|
*/
|
||||||
|
export interface IDnsValidationResult {
|
||||||
|
valid: boolean;
|
||||||
|
errors: string[];
|
||||||
|
warnings: string[];
|
||||||
|
requiredChanges: string[];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Manages DNS configuration for email domains
|
||||||
|
* Handles both validation and creation of DNS records
|
||||||
|
*/
|
||||||
|
export declare class DnsManager {
|
||||||
|
private dcRouter;
|
||||||
|
private storageManager;
|
||||||
|
constructor(dcRouter: IDcRouterLike);
|
||||||
|
/**
|
||||||
|
* Validate all domain configurations
|
||||||
|
*/
|
||||||
|
validateAllDomains(domainConfigs: IEmailDomainConfig[]): Promise<Map<string, IDnsValidationResult>>;
|
||||||
|
/**
|
||||||
|
* Validate a single domain configuration
|
||||||
|
*/
|
||||||
|
validateDomain(config: IEmailDomainConfig): Promise<IDnsValidationResult>;
|
||||||
|
/**
|
||||||
|
* Validate forward mode configuration
|
||||||
|
*/
|
||||||
|
private validateForwardMode;
|
||||||
|
/**
|
||||||
|
* Validate internal DNS mode configuration
|
||||||
|
*/
|
||||||
|
private validateInternalDnsMode;
|
||||||
|
/**
|
||||||
|
* Validate external DNS mode configuration
|
||||||
|
*/
|
||||||
|
private validateExternalDnsMode;
|
||||||
|
/**
|
||||||
|
* Check DNS records for a domain
|
||||||
|
*/
|
||||||
|
private checkDnsRecords;
|
||||||
|
/**
|
||||||
|
* Resolve NS records for a domain
|
||||||
|
*/
|
||||||
|
private resolveNs;
|
||||||
|
/**
|
||||||
|
* Get base domain from email domain (e.g., mail.example.com -> example.com)
|
||||||
|
*/
|
||||||
|
private getBaseDomain;
|
||||||
|
/**
|
||||||
|
* Ensure all DNS records are created for configured domains
|
||||||
|
* This is the main entry point for DNS record management
|
||||||
|
*/
|
||||||
|
ensureDnsRecords(domainConfigs: IEmailDomainConfig[], dkimCreator?: any): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Create DNS records for internal-dns mode domains
|
||||||
|
*/
|
||||||
|
private createInternalDnsRecords;
|
||||||
|
/**
|
||||||
|
* Create DKIM DNS records for all domains
|
||||||
|
*/
|
||||||
|
private createDkimRecords;
|
||||||
|
}
|
||||||
|
export {};
|
||||||
415
dist_ts/mail/routing/classes.dns.manager.js
Normal file
415
dist_ts/mail/routing/classes.dns.manager.js
Normal file
File diff suppressed because one or more lines are too long
165
dist_ts/mail/routing/classes.dnsmanager.d.ts
vendored
Normal file
165
dist_ts/mail/routing/classes.dnsmanager.d.ts
vendored
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
import * as plugins from '../../plugins.js';
|
||||||
|
import { DKIMCreator } from '../security/classes.dkimcreator.js';
|
||||||
|
/**
|
||||||
|
* Interface for DNS record information
|
||||||
|
*/
|
||||||
|
export interface IDnsRecord {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
value: string;
|
||||||
|
ttl?: number;
|
||||||
|
dnsSecEnabled?: boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Interface for DNS lookup options
|
||||||
|
*/
|
||||||
|
export interface IDnsLookupOptions {
|
||||||
|
/** Cache time to live in milliseconds, 0 to disable caching */
|
||||||
|
cacheTtl?: number;
|
||||||
|
/** Timeout for DNS queries in milliseconds */
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Interface for DNS verification result
|
||||||
|
*/
|
||||||
|
export interface IDnsVerificationResult {
|
||||||
|
record: string;
|
||||||
|
found: boolean;
|
||||||
|
valid: boolean;
|
||||||
|
value?: string;
|
||||||
|
expectedValue?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Manager for DNS-related operations, including record lookups, verification, and generation
|
||||||
|
*/
|
||||||
|
export declare class DNSManager {
|
||||||
|
dkimCreator: DKIMCreator;
|
||||||
|
private cache;
|
||||||
|
private defaultOptions;
|
||||||
|
constructor(dkimCreatorArg: DKIMCreator, options?: IDnsLookupOptions);
|
||||||
|
/**
|
||||||
|
* Lookup MX records for a domain
|
||||||
|
* @param domain Domain to look up
|
||||||
|
* @param options Lookup options
|
||||||
|
* @returns Array of MX records sorted by priority
|
||||||
|
*/
|
||||||
|
lookupMx(domain: string, options?: IDnsLookupOptions): Promise<plugins.dns.MxRecord[]>;
|
||||||
|
/**
|
||||||
|
* Lookup TXT records for a domain
|
||||||
|
* @param domain Domain to look up
|
||||||
|
* @param options Lookup options
|
||||||
|
* @returns Array of TXT records
|
||||||
|
*/
|
||||||
|
lookupTxt(domain: string, options?: IDnsLookupOptions): Promise<string[][]>;
|
||||||
|
/**
|
||||||
|
* Find specific TXT record by subdomain and prefix
|
||||||
|
* @param domain Base domain
|
||||||
|
* @param subdomain Subdomain prefix (e.g., "dkim._domainkey")
|
||||||
|
* @param prefix Record prefix to match (e.g., "v=DKIM1")
|
||||||
|
* @param options Lookup options
|
||||||
|
* @returns Matching TXT record or null if not found
|
||||||
|
*/
|
||||||
|
findTxtRecord(domain: string, subdomain?: string, prefix?: string, options?: IDnsLookupOptions): Promise<string | null>;
|
||||||
|
/**
|
||||||
|
* Verify if a domain has a valid SPF record
|
||||||
|
* @param domain Domain to verify
|
||||||
|
* @returns Verification result
|
||||||
|
*/
|
||||||
|
verifySpfRecord(domain: string): Promise<IDnsVerificationResult>;
|
||||||
|
/**
|
||||||
|
* Verify if a domain has a valid DKIM record
|
||||||
|
* @param domain Domain to verify
|
||||||
|
* @param selector DKIM selector (usually "mta" in our case)
|
||||||
|
* @returns Verification result
|
||||||
|
*/
|
||||||
|
verifyDkimRecord(domain: string, selector?: string): Promise<IDnsVerificationResult>;
|
||||||
|
/**
|
||||||
|
* Verify if a domain has a valid DMARC record
|
||||||
|
* @param domain Domain to verify
|
||||||
|
* @returns Verification result
|
||||||
|
*/
|
||||||
|
verifyDmarcRecord(domain: string): Promise<IDnsVerificationResult>;
|
||||||
|
/**
|
||||||
|
* Check all email authentication records (SPF, DKIM, DMARC) for a domain
|
||||||
|
* @param domain Domain to check
|
||||||
|
* @param dkimSelector DKIM selector
|
||||||
|
* @returns Object with verification results for each record type
|
||||||
|
*/
|
||||||
|
verifyEmailAuthRecords(domain: string, dkimSelector?: string): Promise<{
|
||||||
|
spf: IDnsVerificationResult;
|
||||||
|
dkim: IDnsVerificationResult;
|
||||||
|
dmarc: IDnsVerificationResult;
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* Generate a recommended SPF record for a domain
|
||||||
|
* @param domain Domain name
|
||||||
|
* @param options Configuration options for the SPF record
|
||||||
|
* @returns Generated SPF record
|
||||||
|
*/
|
||||||
|
generateSpfRecord(domain: string, options?: {
|
||||||
|
includeMx?: boolean;
|
||||||
|
includeA?: boolean;
|
||||||
|
includeIps?: string[];
|
||||||
|
includeSpf?: string[];
|
||||||
|
policy?: 'none' | 'neutral' | 'softfail' | 'fail' | 'reject';
|
||||||
|
}): IDnsRecord;
|
||||||
|
/**
|
||||||
|
* Generate a recommended DMARC record for a domain
|
||||||
|
* @param domain Domain name
|
||||||
|
* @param options Configuration options for the DMARC record
|
||||||
|
* @returns Generated DMARC record
|
||||||
|
*/
|
||||||
|
generateDmarcRecord(domain: string, options?: {
|
||||||
|
policy?: 'none' | 'quarantine' | 'reject';
|
||||||
|
subdomainPolicy?: 'none' | 'quarantine' | 'reject';
|
||||||
|
pct?: number;
|
||||||
|
rua?: string;
|
||||||
|
ruf?: string;
|
||||||
|
daysInterval?: number;
|
||||||
|
}): IDnsRecord;
|
||||||
|
/**
|
||||||
|
* Save DNS record recommendations to a file
|
||||||
|
* @param domain Domain name
|
||||||
|
* @param records DNS records to save
|
||||||
|
*/
|
||||||
|
saveDnsRecommendations(domain: string, records: IDnsRecord[]): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Get cache key value
|
||||||
|
* @param key Cache key
|
||||||
|
* @returns Cached value or undefined if not found or expired
|
||||||
|
*/
|
||||||
|
private getFromCache;
|
||||||
|
/**
|
||||||
|
* Set cache key value
|
||||||
|
* @param key Cache key
|
||||||
|
* @param data Data to cache
|
||||||
|
* @param ttl TTL in milliseconds
|
||||||
|
*/
|
||||||
|
private setInCache;
|
||||||
|
/**
|
||||||
|
* Clear the DNS cache
|
||||||
|
* @param key Optional specific key to clear, or all cache if not provided
|
||||||
|
*/
|
||||||
|
clearCache(key?: string): void;
|
||||||
|
/**
|
||||||
|
* Promise-based wrapper for dns.resolveMx
|
||||||
|
* @param domain Domain to resolve
|
||||||
|
* @param timeout Timeout in milliseconds
|
||||||
|
* @returns Promise resolving to MX records
|
||||||
|
*/
|
||||||
|
private dnsResolveMx;
|
||||||
|
/**
|
||||||
|
* Promise-based wrapper for dns.resolveTxt
|
||||||
|
* @param domain Domain to resolve
|
||||||
|
* @param timeout Timeout in milliseconds
|
||||||
|
* @returns Promise resolving to TXT records
|
||||||
|
*/
|
||||||
|
private dnsResolveTxt;
|
||||||
|
/**
|
||||||
|
* Generate all recommended DNS records for proper email authentication
|
||||||
|
* @param domain Domain to generate records for
|
||||||
|
* @returns Array of recommended DNS records
|
||||||
|
*/
|
||||||
|
generateAllRecommendedRecords(domain: string): Promise<IDnsRecord[]>;
|
||||||
|
}
|
||||||
431
dist_ts/mail/routing/classes.dnsmanager.js
Normal file
431
dist_ts/mail/routing/classes.dnsmanager.js
Normal file
File diff suppressed because one or more lines are too long
54
dist_ts/mail/routing/classes.domain.registry.d.ts
vendored
Normal file
54
dist_ts/mail/routing/classes.domain.registry.d.ts
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import type { IEmailDomainConfig } from './interfaces.js';
|
||||||
|
/**
|
||||||
|
* Registry for email domain configurations
|
||||||
|
* Provides fast lookups and validation for domains
|
||||||
|
*/
|
||||||
|
export declare class DomainRegistry {
|
||||||
|
private domains;
|
||||||
|
private defaults;
|
||||||
|
constructor(domainConfigs: IEmailDomainConfig[], defaults?: {
|
||||||
|
dnsMode?: 'forward' | 'internal-dns' | 'external-dns';
|
||||||
|
dkim?: IEmailDomainConfig['dkim'];
|
||||||
|
rateLimits?: IEmailDomainConfig['rateLimits'];
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* Get default DKIM configuration
|
||||||
|
*/
|
||||||
|
private getDefaultDkimConfig;
|
||||||
|
/**
|
||||||
|
* Apply defaults to a domain configuration
|
||||||
|
*/
|
||||||
|
private applyDefaults;
|
||||||
|
/**
|
||||||
|
* Check if a domain is registered
|
||||||
|
*/
|
||||||
|
isDomainRegistered(domain: string): boolean;
|
||||||
|
/**
|
||||||
|
* Check if an email address belongs to a registered domain
|
||||||
|
*/
|
||||||
|
isEmailRegistered(email: string): boolean;
|
||||||
|
/**
|
||||||
|
* Get domain configuration
|
||||||
|
*/
|
||||||
|
getDomainConfig(domain: string): IEmailDomainConfig | undefined;
|
||||||
|
/**
|
||||||
|
* Get domain configuration for an email address
|
||||||
|
*/
|
||||||
|
getEmailDomainConfig(email: string): IEmailDomainConfig | undefined;
|
||||||
|
/**
|
||||||
|
* Extract domain from email address
|
||||||
|
*/
|
||||||
|
private extractDomain;
|
||||||
|
/**
|
||||||
|
* Get all registered domains
|
||||||
|
*/
|
||||||
|
getAllDomains(): string[];
|
||||||
|
/**
|
||||||
|
* Get all domain configurations
|
||||||
|
*/
|
||||||
|
getAllConfigs(): IEmailDomainConfig[];
|
||||||
|
/**
|
||||||
|
* Get domains by DNS mode
|
||||||
|
*/
|
||||||
|
getDomainsByMode(mode: 'forward' | 'internal-dns' | 'external-dns'): IEmailDomainConfig[];
|
||||||
|
}
|
||||||
119
dist_ts/mail/routing/classes.domain.registry.js
Normal file
119
dist_ts/mail/routing/classes.domain.registry.js
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import { logger } from '../../logger.js';
|
||||||
|
/**
|
||||||
|
* Registry for email domain configurations
|
||||||
|
* Provides fast lookups and validation for domains
|
||||||
|
*/
|
||||||
|
export class DomainRegistry {
|
||||||
|
domains = new Map();
|
||||||
|
defaults;
|
||||||
|
constructor(domainConfigs, defaults) {
|
||||||
|
// Set defaults
|
||||||
|
this.defaults = {
|
||||||
|
dnsMode: defaults?.dnsMode || 'external-dns',
|
||||||
|
...this.getDefaultDkimConfig(),
|
||||||
|
...defaults?.dkim,
|
||||||
|
rateLimits: defaults?.rateLimits
|
||||||
|
};
|
||||||
|
// Process and store domain configurations
|
||||||
|
for (const config of domainConfigs) {
|
||||||
|
const processedConfig = this.applyDefaults(config);
|
||||||
|
this.domains.set(config.domain.toLowerCase(), processedConfig);
|
||||||
|
logger.log('info', `Registered domain: ${config.domain} with DNS mode: ${processedConfig.dnsMode}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get default DKIM configuration
|
||||||
|
*/
|
||||||
|
getDefaultDkimConfig() {
|
||||||
|
return {
|
||||||
|
selector: 'default',
|
||||||
|
keySize: 2048,
|
||||||
|
rotateKeys: false,
|
||||||
|
rotationInterval: 90
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Apply defaults to a domain configuration
|
||||||
|
*/
|
||||||
|
applyDefaults(config) {
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
dnsMode: config.dnsMode || this.defaults.dnsMode,
|
||||||
|
dkim: {
|
||||||
|
...this.getDefaultDkimConfig(),
|
||||||
|
...this.defaults,
|
||||||
|
...config.dkim
|
||||||
|
},
|
||||||
|
rateLimits: {
|
||||||
|
...this.defaults.rateLimits,
|
||||||
|
...config.rateLimits,
|
||||||
|
outbound: {
|
||||||
|
...this.defaults.rateLimits?.outbound,
|
||||||
|
...config.rateLimits?.outbound
|
||||||
|
},
|
||||||
|
inbound: {
|
||||||
|
...this.defaults.rateLimits?.inbound,
|
||||||
|
...config.rateLimits?.inbound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check if a domain is registered
|
||||||
|
*/
|
||||||
|
isDomainRegistered(domain) {
|
||||||
|
return this.domains.has(domain.toLowerCase());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check if an email address belongs to a registered domain
|
||||||
|
*/
|
||||||
|
isEmailRegistered(email) {
|
||||||
|
const domain = this.extractDomain(email);
|
||||||
|
if (!domain)
|
||||||
|
return false;
|
||||||
|
return this.isDomainRegistered(domain);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get domain configuration
|
||||||
|
*/
|
||||||
|
getDomainConfig(domain) {
|
||||||
|
return this.domains.get(domain.toLowerCase());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get domain configuration for an email address
|
||||||
|
*/
|
||||||
|
getEmailDomainConfig(email) {
|
||||||
|
const domain = this.extractDomain(email);
|
||||||
|
if (!domain)
|
||||||
|
return undefined;
|
||||||
|
return this.getDomainConfig(domain);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extract domain from email address
|
||||||
|
*/
|
||||||
|
extractDomain(email) {
|
||||||
|
const parts = email.toLowerCase().split('@');
|
||||||
|
if (parts.length !== 2)
|
||||||
|
return null;
|
||||||
|
return parts[1];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get all registered domains
|
||||||
|
*/
|
||||||
|
getAllDomains() {
|
||||||
|
return Array.from(this.domains.keys());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get all domain configurations
|
||||||
|
*/
|
||||||
|
getAllConfigs() {
|
||||||
|
return Array.from(this.domains.values());
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get domains by DNS mode
|
||||||
|
*/
|
||||||
|
getDomainsByMode(mode) {
|
||||||
|
return Array.from(this.domains.values()).filter(config => config.dnsMode === mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kb21haW4ucmVnaXN0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL3JvdXRpbmcvY2xhc3Nlcy5kb21haW4ucmVnaXN0cnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRXpDOzs7R0FHRztBQUNILE1BQU0sT0FBTyxjQUFjO0lBQ2pCLE9BQU8sR0FBb0MsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUNyRCxRQUFRLENBR2Q7SUFFRixZQUNFLGFBQW1DLEVBQ25DLFFBSUM7UUFFRCxlQUFlO1FBQ2YsSUFBSSxDQUFDLFFBQVEsR0FBRztZQUNkLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxJQUFJLGNBQWM7WUFDNUMsR0FBRyxJQUFJLENBQUMsb0JBQW9CLEVBQUU7WUFDOUIsR0FBRyxRQUFRLEVBQUUsSUFBSTtZQUNqQixVQUFVLEVBQUUsUUFBUSxFQUFFLFVBQVU7U0FDakMsQ0FBQztRQUVGLDBDQUEwQztRQUMxQyxLQUFLLE1BQU0sTUFBTSxJQUFJLGFBQWEsRUFBRSxDQUFDO1lBQ25DLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDbkQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUUsRUFBRSxlQUFlLENBQUMsQ0FBQztZQUMvRCxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxzQkFBc0IsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLGVBQWUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQ3RHLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxvQkFBb0I7UUFDMUIsT0FBTztZQUNMLFFBQVEsRUFBRSxTQUFTO1lBQ25CLE9BQU8sRUFBRSxJQUFJO1lBQ2IsVUFBVSxFQUFFLEtBQUs7WUFDakIsZ0JB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
|
||||||
64
dist_ts/mail/routing/classes.email.config.d.ts
vendored
Normal file
64
dist_ts/mail/routing/classes.email.config.d.ts
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import type { EmailProcessingMode } from '../delivery/interfaces.js';
|
||||||
|
export type { EmailProcessingMode };
|
||||||
|
/**
|
||||||
|
* Domain rule interface for pattern-based routing
|
||||||
|
*/
|
||||||
|
export interface IDomainRule {
|
||||||
|
pattern: string;
|
||||||
|
mode: EmailProcessingMode;
|
||||||
|
target?: {
|
||||||
|
server: string;
|
||||||
|
port?: number;
|
||||||
|
useTls?: boolean;
|
||||||
|
authentication?: {
|
||||||
|
user?: string;
|
||||||
|
pass?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
mtaOptions?: IMtaOptions;
|
||||||
|
contentScanning?: boolean;
|
||||||
|
scanners?: IContentScanner[];
|
||||||
|
transformations?: ITransformation[];
|
||||||
|
rateLimits?: {
|
||||||
|
maxMessagesPerMinute?: number;
|
||||||
|
maxRecipientsPerMessage?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* MTA options interface
|
||||||
|
*/
|
||||||
|
export interface IMtaOptions {
|
||||||
|
domain?: string;
|
||||||
|
allowLocalDelivery?: boolean;
|
||||||
|
localDeliveryPath?: string;
|
||||||
|
dkimSign?: boolean;
|
||||||
|
dkimOptions?: {
|
||||||
|
domainName: string;
|
||||||
|
keySelector: string;
|
||||||
|
privateKey?: string;
|
||||||
|
};
|
||||||
|
smtpBanner?: string;
|
||||||
|
maxConnections?: number;
|
||||||
|
connTimeout?: number;
|
||||||
|
spoolDir?: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Content scanner interface
|
||||||
|
*/
|
||||||
|
export interface IContentScanner {
|
||||||
|
type: 'spam' | 'virus' | 'attachment';
|
||||||
|
threshold?: number;
|
||||||
|
action: 'tag' | 'reject';
|
||||||
|
blockedExtensions?: string[];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Transformation interface
|
||||||
|
*/
|
||||||
|
export interface ITransformation {
|
||||||
|
type: string;
|
||||||
|
header?: string;
|
||||||
|
value?: string;
|
||||||
|
domains?: string[];
|
||||||
|
append?: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
2
dist_ts/mail/routing/classes.email.config.js
Normal file
2
dist_ts/mail/routing/classes.email.config.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export {};
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lbWFpbC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL3JvdXRpbmcvY2xhc3Nlcy5lbWFpbC5jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
|
||||||
171
dist_ts/mail/routing/classes.email.router.d.ts
vendored
Normal file
171
dist_ts/mail/routing/classes.email.router.d.ts
vendored
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
import { EventEmitter } from 'node:events';
|
||||||
|
import type { IEmailRoute, IEmailContext } from './interfaces.js';
|
||||||
|
/**
|
||||||
|
* Email router that evaluates routes and determines actions
|
||||||
|
*/
|
||||||
|
export declare class EmailRouter extends EventEmitter {
|
||||||
|
private routes;
|
||||||
|
private patternCache;
|
||||||
|
private storageManager?;
|
||||||
|
private persistChanges;
|
||||||
|
/**
|
||||||
|
* Create a new email router
|
||||||
|
* @param routes Array of email routes
|
||||||
|
* @param options Router options
|
||||||
|
*/
|
||||||
|
constructor(routes: IEmailRoute[], options?: {
|
||||||
|
storageManager?: any;
|
||||||
|
persistChanges?: boolean;
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* Sort routes by priority (higher priority first)
|
||||||
|
* @param routes Routes to sort
|
||||||
|
* @returns Sorted routes
|
||||||
|
*/
|
||||||
|
private sortRoutesByPriority;
|
||||||
|
/**
|
||||||
|
* Get all configured routes
|
||||||
|
* @returns Array of routes
|
||||||
|
*/
|
||||||
|
getRoutes(): IEmailRoute[];
|
||||||
|
/**
|
||||||
|
* Update routes
|
||||||
|
* @param routes New routes
|
||||||
|
* @param persist Whether to persist changes (defaults to persistChanges setting)
|
||||||
|
*/
|
||||||
|
updateRoutes(routes: IEmailRoute[], persist?: boolean): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Set routes (alias for updateRoutes)
|
||||||
|
* @param routes New routes
|
||||||
|
* @param persist Whether to persist changes
|
||||||
|
*/
|
||||||
|
setRoutes(routes: IEmailRoute[], persist?: boolean): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Clear the pattern cache
|
||||||
|
*/
|
||||||
|
clearCache(): void;
|
||||||
|
/**
|
||||||
|
* Evaluate routes and find the first match
|
||||||
|
* @param context Email context
|
||||||
|
* @returns Matched route or null
|
||||||
|
*/
|
||||||
|
evaluateRoutes(context: IEmailContext): Promise<IEmailRoute | null>;
|
||||||
|
/**
|
||||||
|
* Check if a route matches the context
|
||||||
|
* @param route Route to check
|
||||||
|
* @param context Email context
|
||||||
|
* @returns True if route matches
|
||||||
|
*/
|
||||||
|
private matchesRoute;
|
||||||
|
/**
|
||||||
|
* Check if email recipients match patterns
|
||||||
|
* @param email Email to check
|
||||||
|
* @param patterns Patterns to match
|
||||||
|
* @returns True if any recipient matches
|
||||||
|
*/
|
||||||
|
private matchesRecipients;
|
||||||
|
/**
|
||||||
|
* Check if email sender matches patterns
|
||||||
|
* @param email Email to check
|
||||||
|
* @param patterns Patterns to match
|
||||||
|
* @returns True if sender matches
|
||||||
|
*/
|
||||||
|
private matchesSenders;
|
||||||
|
/**
|
||||||
|
* Check if client IP matches patterns
|
||||||
|
* @param context Email context
|
||||||
|
* @param patterns IP patterns to match
|
||||||
|
* @returns True if IP matches
|
||||||
|
*/
|
||||||
|
private matchesClientIp;
|
||||||
|
/**
|
||||||
|
* Check if email headers match patterns
|
||||||
|
* @param email Email to check
|
||||||
|
* @param headerPatterns Header patterns to match
|
||||||
|
* @returns True if headers match
|
||||||
|
*/
|
||||||
|
private matchesHeaders;
|
||||||
|
/**
|
||||||
|
* Check if email size matches range
|
||||||
|
* @param email Email to check
|
||||||
|
* @param sizeRange Size range to match
|
||||||
|
* @returns True if size is in range
|
||||||
|
*/
|
||||||
|
private matchesSize;
|
||||||
|
/**
|
||||||
|
* Check if email subject matches pattern
|
||||||
|
* @param email Email to check
|
||||||
|
* @param pattern Pattern to match
|
||||||
|
* @returns True if subject matches
|
||||||
|
*/
|
||||||
|
private matchesSubject;
|
||||||
|
/**
|
||||||
|
* Check if a string matches a glob pattern
|
||||||
|
* @param str String to check
|
||||||
|
* @param pattern Glob pattern
|
||||||
|
* @returns True if matches
|
||||||
|
*/
|
||||||
|
private matchesPattern;
|
||||||
|
/**
|
||||||
|
* Convert glob pattern to RegExp
|
||||||
|
* @param pattern Glob pattern
|
||||||
|
* @returns Regular expression
|
||||||
|
*/
|
||||||
|
private globToRegExp;
|
||||||
|
/**
|
||||||
|
* Check if IP is in CIDR range
|
||||||
|
* @param ip IP address to check
|
||||||
|
* @param cidr CIDR notation (e.g., '192.168.0.0/16')
|
||||||
|
* @returns True if IP is in range
|
||||||
|
*/
|
||||||
|
private ipInCidr;
|
||||||
|
/**
|
||||||
|
* Convert IP address to number
|
||||||
|
* @param ip IP address
|
||||||
|
* @returns Number representation
|
||||||
|
*/
|
||||||
|
private ipToNumber;
|
||||||
|
/**
|
||||||
|
* Calculate approximate email size in bytes
|
||||||
|
* @param email Email to measure
|
||||||
|
* @returns Size in bytes
|
||||||
|
*/
|
||||||
|
private calculateEmailSize;
|
||||||
|
/**
|
||||||
|
* Save current routes to storage
|
||||||
|
*/
|
||||||
|
saveRoutes(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Load routes from storage
|
||||||
|
* @param options Load options
|
||||||
|
*/
|
||||||
|
loadRoutes(options?: {
|
||||||
|
merge?: boolean;
|
||||||
|
replace?: boolean;
|
||||||
|
}): Promise<IEmailRoute[]>;
|
||||||
|
/**
|
||||||
|
* Add a route
|
||||||
|
* @param route Route to add
|
||||||
|
* @param persist Whether to persist changes
|
||||||
|
*/
|
||||||
|
addRoute(route: IEmailRoute, persist?: boolean): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Remove a route by name
|
||||||
|
* @param name Route name
|
||||||
|
* @param persist Whether to persist changes
|
||||||
|
*/
|
||||||
|
removeRoute(name: string, persist?: boolean): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Update a route
|
||||||
|
* @param name Route name
|
||||||
|
* @param route Updated route data
|
||||||
|
* @param persist Whether to persist changes
|
||||||
|
*/
|
||||||
|
updateRoute(name: string, route: IEmailRoute, persist?: boolean): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Get a route by name
|
||||||
|
* @param name Route name
|
||||||
|
* @returns Route or undefined
|
||||||
|
*/
|
||||||
|
getRoute(name: string): IEmailRoute | undefined;
|
||||||
|
}
|
||||||
494
dist_ts/mail/routing/classes.email.router.js
Normal file
494
dist_ts/mail/routing/classes.email.router.js
Normal file
File diff suppressed because one or more lines are too long
451
dist_ts/mail/routing/classes.unified.email.server.d.ts
vendored
Normal file
451
dist_ts/mail/routing/classes.unified.email.server.d.ts
vendored
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import { DKIMCreator } from '../security/classes.dkimcreator.js';
|
||||||
|
interface IIPWarmupConfig {
|
||||||
|
enabled?: boolean;
|
||||||
|
ips?: string[];
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
interface IReputationMonitorConfig {
|
||||||
|
enabled?: boolean;
|
||||||
|
domains?: string[];
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
import type { IEmailRoute, IEmailDomainConfig } from './interfaces.js';
|
||||||
|
import { Email } from '../core/classes.email.js';
|
||||||
|
import { DomainRegistry } from './classes.domain.registry.js';
|
||||||
|
import { BounceType, BounceCategory } from '../core/classes.bouncemanager.js';
|
||||||
|
import type { SmtpClient } from '../delivery/smtpclient/smtp-client.js';
|
||||||
|
import { MultiModeDeliverySystem } from '../delivery/classes.delivery.system.js';
|
||||||
|
import { UnifiedDeliveryQueue } from '../delivery/classes.delivery.queue.js';
|
||||||
|
import { UnifiedRateLimiter, type IHierarchicalRateLimits } from '../delivery/classes.unified.rate.limiter.js';
|
||||||
|
import type { EmailProcessingMode, ISmtpSession as IBaseSmtpSession } from '../delivery/interfaces.js';
|
||||||
|
/** External DcRouter interface shape used by UnifiedEmailServer */
|
||||||
|
interface DcRouter {
|
||||||
|
storageManager: any;
|
||||||
|
dnsServer?: any;
|
||||||
|
options?: any;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extended SMTP session interface with route information
|
||||||
|
*/
|
||||||
|
export interface IExtendedSmtpSession extends ISmtpSession {
|
||||||
|
/**
|
||||||
|
* Matched route for this session
|
||||||
|
*/
|
||||||
|
matchedRoute?: IEmailRoute;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Options for the unified email server
|
||||||
|
*/
|
||||||
|
export interface IUnifiedEmailServerOptions {
|
||||||
|
ports: number[];
|
||||||
|
hostname: string;
|
||||||
|
domains: IEmailDomainConfig[];
|
||||||
|
banner?: string;
|
||||||
|
debug?: boolean;
|
||||||
|
useSocketHandler?: boolean;
|
||||||
|
auth?: {
|
||||||
|
required?: boolean;
|
||||||
|
methods?: ('PLAIN' | 'LOGIN' | 'OAUTH2')[];
|
||||||
|
users?: Array<{
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
tls?: {
|
||||||
|
certPath?: string;
|
||||||
|
keyPath?: string;
|
||||||
|
caPath?: string;
|
||||||
|
minVersion?: string;
|
||||||
|
ciphers?: string;
|
||||||
|
};
|
||||||
|
maxMessageSize?: number;
|
||||||
|
maxClients?: number;
|
||||||
|
maxConnections?: number;
|
||||||
|
connectionTimeout?: number;
|
||||||
|
socketTimeout?: number;
|
||||||
|
routes: IEmailRoute[];
|
||||||
|
defaults?: {
|
||||||
|
dnsMode?: 'forward' | 'internal-dns' | 'external-dns';
|
||||||
|
dkim?: IEmailDomainConfig['dkim'];
|
||||||
|
rateLimits?: IEmailDomainConfig['rateLimits'];
|
||||||
|
};
|
||||||
|
outbound?: {
|
||||||
|
maxConnections?: number;
|
||||||
|
connectionTimeout?: number;
|
||||||
|
socketTimeout?: number;
|
||||||
|
retryAttempts?: number;
|
||||||
|
defaultFrom?: string;
|
||||||
|
};
|
||||||
|
rateLimits?: IHierarchicalRateLimits;
|
||||||
|
ipWarmupConfig?: IIPWarmupConfig;
|
||||||
|
reputationMonitorConfig?: IReputationMonitorConfig;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Extended SMTP session interface for UnifiedEmailServer
|
||||||
|
*/
|
||||||
|
export interface ISmtpSession extends IBaseSmtpSession {
|
||||||
|
/**
|
||||||
|
* User information if authenticated
|
||||||
|
*/
|
||||||
|
user?: {
|
||||||
|
username: string;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Matched route for this session
|
||||||
|
*/
|
||||||
|
matchedRoute?: IEmailRoute;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Authentication data for SMTP
|
||||||
|
*/
|
||||||
|
import type { ISmtpAuth } from '../delivery/interfaces.js';
|
||||||
|
export type IAuthData = ISmtpAuth;
|
||||||
|
/**
|
||||||
|
* Server statistics
|
||||||
|
*/
|
||||||
|
export interface IServerStats {
|
||||||
|
startTime: Date;
|
||||||
|
connections: {
|
||||||
|
current: number;
|
||||||
|
total: number;
|
||||||
|
};
|
||||||
|
messages: {
|
||||||
|
processed: number;
|
||||||
|
delivered: number;
|
||||||
|
failed: number;
|
||||||
|
};
|
||||||
|
processingTime: {
|
||||||
|
avg: number;
|
||||||
|
max: number;
|
||||||
|
min: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Unified email server that handles all email traffic with pattern-based routing
|
||||||
|
*/
|
||||||
|
export declare class UnifiedEmailServer extends EventEmitter {
|
||||||
|
private dcRouter;
|
||||||
|
private options;
|
||||||
|
private emailRouter;
|
||||||
|
domainRegistry: DomainRegistry;
|
||||||
|
private servers;
|
||||||
|
private stats;
|
||||||
|
dkimCreator: DKIMCreator;
|
||||||
|
private rustBridge;
|
||||||
|
private ipReputationChecker;
|
||||||
|
private bounceManager;
|
||||||
|
private ipWarmupManager;
|
||||||
|
private senderReputationMonitor;
|
||||||
|
deliveryQueue: UnifiedDeliveryQueue;
|
||||||
|
deliverySystem: MultiModeDeliverySystem;
|
||||||
|
private rateLimiter;
|
||||||
|
private dkimKeys;
|
||||||
|
private smtpClients;
|
||||||
|
constructor(dcRouter: DcRouter, options: IUnifiedEmailServerOptions);
|
||||||
|
/**
|
||||||
|
* Get or create an SMTP client for the given host and port
|
||||||
|
* Uses connection pooling for efficiency
|
||||||
|
*/
|
||||||
|
getSmtpClient(host: string, port?: number): SmtpClient;
|
||||||
|
/**
|
||||||
|
* Start the unified email server
|
||||||
|
*/
|
||||||
|
start(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Stop the unified email server
|
||||||
|
*/
|
||||||
|
stop(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Handle an emailReceived event from the Rust SMTP server.
|
||||||
|
* Decodes the email data, processes it through the routing system,
|
||||||
|
* and sends back the result via the correlation-ID callback.
|
||||||
|
*/
|
||||||
|
private handleRustEmailReceived;
|
||||||
|
/**
|
||||||
|
* Handle an authRequest event from the Rust SMTP server.
|
||||||
|
* Validates credentials and sends back the result.
|
||||||
|
*/
|
||||||
|
private handleRustAuthRequest;
|
||||||
|
/**
|
||||||
|
* Verify inbound email security (DKIM/SPF/DMARC) using pre-computed Rust results
|
||||||
|
* or falling back to IPC call if no pre-computed results are available.
|
||||||
|
*/
|
||||||
|
private verifyInboundSecurity;
|
||||||
|
/**
|
||||||
|
* Process email based on routing rules
|
||||||
|
*/
|
||||||
|
processEmailByMode(emailData: Email | Buffer, session: IExtendedSmtpSession): Promise<Email>;
|
||||||
|
/**
|
||||||
|
* Execute action based on route configuration
|
||||||
|
*/
|
||||||
|
private executeAction;
|
||||||
|
/**
|
||||||
|
* Handle forward action
|
||||||
|
*/
|
||||||
|
private handleForwardAction;
|
||||||
|
/**
|
||||||
|
* Handle process action
|
||||||
|
*/
|
||||||
|
private handleProcessAction;
|
||||||
|
/**
|
||||||
|
* Handle deliver action
|
||||||
|
*/
|
||||||
|
private handleDeliverAction;
|
||||||
|
/**
|
||||||
|
* Handle reject action
|
||||||
|
*/
|
||||||
|
private handleRejectAction;
|
||||||
|
/**
|
||||||
|
* Handle email in MTA mode (programmatic processing)
|
||||||
|
*/
|
||||||
|
private _handleMtaMode;
|
||||||
|
/**
|
||||||
|
* Handle email in process mode (store-and-forward with scanning)
|
||||||
|
*/
|
||||||
|
private _handleProcessMode;
|
||||||
|
/**
|
||||||
|
* Get file extension from filename
|
||||||
|
*/
|
||||||
|
private getFileExtension;
|
||||||
|
/**
|
||||||
|
* Set up DKIM configuration for all domains
|
||||||
|
*/
|
||||||
|
private setupDkimForDomains;
|
||||||
|
/**
|
||||||
|
* Apply per-domain rate limits from domain configurations
|
||||||
|
*/
|
||||||
|
private applyDomainRateLimits;
|
||||||
|
/**
|
||||||
|
* Check and rotate DKIM keys if needed
|
||||||
|
*/
|
||||||
|
private checkAndRotateDkimKeys;
|
||||||
|
/**
|
||||||
|
* Generate SmartProxy routes for email ports
|
||||||
|
*/
|
||||||
|
generateProxyRoutes(portMapping?: Record<number, number>): any[];
|
||||||
|
/**
|
||||||
|
* Update server configuration
|
||||||
|
*/
|
||||||
|
updateOptions(options: Partial<IUnifiedEmailServerOptions>): void;
|
||||||
|
/**
|
||||||
|
* Update email routes
|
||||||
|
*/
|
||||||
|
updateEmailRoutes(routes: IEmailRoute[]): void;
|
||||||
|
/**
|
||||||
|
* Get server statistics
|
||||||
|
*/
|
||||||
|
getStats(): IServerStats;
|
||||||
|
/**
|
||||||
|
* Get domain registry
|
||||||
|
*/
|
||||||
|
getDomainRegistry(): DomainRegistry;
|
||||||
|
/**
|
||||||
|
* Update email routes dynamically
|
||||||
|
*/
|
||||||
|
updateRoutes(routes: IEmailRoute[]): void;
|
||||||
|
/**
|
||||||
|
* Send an email through the delivery system
|
||||||
|
* @param email The email to send
|
||||||
|
* @param mode The processing mode to use
|
||||||
|
* @param rule Optional rule to apply
|
||||||
|
* @param options Optional sending options
|
||||||
|
* @returns The ID of the queued email
|
||||||
|
*/
|
||||||
|
sendEmail(email: Email, mode?: EmailProcessingMode, route?: IEmailRoute, options?: {
|
||||||
|
skipSuppressionCheck?: boolean;
|
||||||
|
ipAddress?: string;
|
||||||
|
isTransactional?: boolean;
|
||||||
|
}): Promise<string>;
|
||||||
|
/**
|
||||||
|
* Handle DKIM signing for an email
|
||||||
|
* @param email The email to sign
|
||||||
|
* @param domain The domain to sign with
|
||||||
|
* @param selector The DKIM selector
|
||||||
|
*/
|
||||||
|
private handleDkimSigning;
|
||||||
|
/**
|
||||||
|
* Process a bounce notification email
|
||||||
|
* @param bounceEmail The email containing bounce notification information
|
||||||
|
* @returns Processed bounce record or null if not a bounce
|
||||||
|
*/
|
||||||
|
processBounceNotification(bounceEmail: Email): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* Process an SMTP failure as a bounce
|
||||||
|
* @param recipient Recipient email that failed
|
||||||
|
* @param smtpResponse SMTP error response
|
||||||
|
* @param options Additional options for bounce processing
|
||||||
|
* @returns Processed bounce record
|
||||||
|
*/
|
||||||
|
processSmtpFailure(recipient: string, smtpResponse: string, options?: {
|
||||||
|
sender?: string;
|
||||||
|
originalEmailId?: string;
|
||||||
|
statusCode?: string;
|
||||||
|
headers?: Record<string, string>;
|
||||||
|
}): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* Check if an email address is suppressed (has bounced previously)
|
||||||
|
* @param email Email address to check
|
||||||
|
* @returns Whether the email is suppressed
|
||||||
|
*/
|
||||||
|
isEmailSuppressed(email: string): boolean;
|
||||||
|
/**
|
||||||
|
* Get suppression information for an email
|
||||||
|
* @param email Email address to check
|
||||||
|
* @returns Suppression information or null if not suppressed
|
||||||
|
*/
|
||||||
|
getSuppressionInfo(email: string): {
|
||||||
|
reason: string;
|
||||||
|
timestamp: number;
|
||||||
|
expiresAt?: number;
|
||||||
|
} | null;
|
||||||
|
/**
|
||||||
|
* Get bounce history information for an email
|
||||||
|
* @param email Email address to check
|
||||||
|
* @returns Bounce history or null if no bounces
|
||||||
|
*/
|
||||||
|
getBounceHistory(email: string): {
|
||||||
|
lastBounce: number;
|
||||||
|
count: number;
|
||||||
|
type: BounceType;
|
||||||
|
category: BounceCategory;
|
||||||
|
} | null;
|
||||||
|
/**
|
||||||
|
* Get all suppressed email addresses
|
||||||
|
* @returns Array of suppressed email addresses
|
||||||
|
*/
|
||||||
|
getSuppressionList(): string[];
|
||||||
|
/**
|
||||||
|
* Get all hard bounced email addresses
|
||||||
|
* @returns Array of hard bounced email addresses
|
||||||
|
*/
|
||||||
|
getHardBouncedAddresses(): string[];
|
||||||
|
/**
|
||||||
|
* Add an email to the suppression list
|
||||||
|
* @param email Email address to suppress
|
||||||
|
* @param reason Reason for suppression
|
||||||
|
* @param expiresAt Optional expiration time (undefined for permanent)
|
||||||
|
*/
|
||||||
|
addToSuppressionList(email: string, reason: string, expiresAt?: number): void;
|
||||||
|
/**
|
||||||
|
* Remove an email from the suppression list
|
||||||
|
* @param email Email address to remove from suppression
|
||||||
|
*/
|
||||||
|
removeFromSuppressionList(email: string): void;
|
||||||
|
/**
|
||||||
|
* Get the status of IP warmup process
|
||||||
|
* @param ipAddress Optional specific IP to check
|
||||||
|
* @returns Status of IP warmup
|
||||||
|
*/
|
||||||
|
getIPWarmupStatus(ipAddress?: string): any;
|
||||||
|
/**
|
||||||
|
* Add a new IP address to the warmup process
|
||||||
|
* @param ipAddress IP address to add
|
||||||
|
*/
|
||||||
|
addIPToWarmup(ipAddress: string): void;
|
||||||
|
/**
|
||||||
|
* Remove an IP address from the warmup process
|
||||||
|
* @param ipAddress IP address to remove
|
||||||
|
*/
|
||||||
|
removeIPFromWarmup(ipAddress: string): void;
|
||||||
|
/**
|
||||||
|
* Update metrics for an IP in the warmup process
|
||||||
|
* @param ipAddress IP address
|
||||||
|
* @param metrics Metrics to update
|
||||||
|
*/
|
||||||
|
updateIPWarmupMetrics(ipAddress: string, metrics: {
|
||||||
|
openRate?: number;
|
||||||
|
bounceRate?: number;
|
||||||
|
complaintRate?: number;
|
||||||
|
}): void;
|
||||||
|
/**
|
||||||
|
* Check if an IP can send more emails today
|
||||||
|
* @param ipAddress IP address to check
|
||||||
|
* @returns Whether the IP can send more today
|
||||||
|
*/
|
||||||
|
canIPSendMoreToday(ipAddress: string): boolean;
|
||||||
|
/**
|
||||||
|
* Check if an IP can send more emails in the current hour
|
||||||
|
* @param ipAddress IP address to check
|
||||||
|
* @returns Whether the IP can send more this hour
|
||||||
|
*/
|
||||||
|
canIPSendMoreThisHour(ipAddress: string): boolean;
|
||||||
|
/**
|
||||||
|
* Get the best IP to use for sending an email based on warmup status
|
||||||
|
* @param emailInfo Information about the email being sent
|
||||||
|
* @returns Best IP to use or null
|
||||||
|
*/
|
||||||
|
getBestIPForSending(emailInfo: {
|
||||||
|
from: string;
|
||||||
|
to: string[];
|
||||||
|
domain: string;
|
||||||
|
isTransactional?: boolean;
|
||||||
|
}): string | null;
|
||||||
|
/**
|
||||||
|
* Set the active IP allocation policy for warmup
|
||||||
|
* @param policyName Name of the policy to set
|
||||||
|
*/
|
||||||
|
setIPAllocationPolicy(policyName: string): void;
|
||||||
|
/**
|
||||||
|
* Record that an email was sent using a specific IP
|
||||||
|
* @param ipAddress IP address used for sending
|
||||||
|
*/
|
||||||
|
recordIPSend(ipAddress: string): void;
|
||||||
|
/**
|
||||||
|
* Get reputation data for a domain
|
||||||
|
* @param domain Domain to get reputation for
|
||||||
|
* @returns Domain reputation metrics
|
||||||
|
*/
|
||||||
|
getDomainReputationData(domain: string): any;
|
||||||
|
/**
|
||||||
|
* Get summary reputation data for all monitored domains
|
||||||
|
* @returns Summary data for all domains
|
||||||
|
*/
|
||||||
|
getReputationSummary(): any;
|
||||||
|
/**
|
||||||
|
* Add a domain to the reputation monitoring system
|
||||||
|
* @param domain Domain to add
|
||||||
|
*/
|
||||||
|
addDomainToMonitoring(domain: string): void;
|
||||||
|
/**
|
||||||
|
* Remove a domain from the reputation monitoring system
|
||||||
|
* @param domain Domain to remove
|
||||||
|
*/
|
||||||
|
removeDomainFromMonitoring(domain: string): void;
|
||||||
|
/**
|
||||||
|
* Record an email event for domain reputation tracking
|
||||||
|
* @param domain Domain sending the email
|
||||||
|
* @param event Event details
|
||||||
|
*/
|
||||||
|
recordReputationEvent(domain: string, event: {
|
||||||
|
type: 'sent' | 'delivered' | 'bounce' | 'complaint' | 'open' | 'click';
|
||||||
|
count?: number;
|
||||||
|
hardBounce?: boolean;
|
||||||
|
receivingDomain?: string;
|
||||||
|
}): void;
|
||||||
|
/**
|
||||||
|
* Check if DKIM key exists for a domain
|
||||||
|
* @param domain Domain to check
|
||||||
|
*/
|
||||||
|
hasDkimKey(domain: string): boolean;
|
||||||
|
/**
|
||||||
|
* Record successful email delivery
|
||||||
|
* @param domain Sending domain
|
||||||
|
*/
|
||||||
|
recordDelivery(domain: string): void;
|
||||||
|
/**
|
||||||
|
* Record email bounce
|
||||||
|
* @param domain Sending domain
|
||||||
|
* @param receivingDomain Receiving domain that bounced
|
||||||
|
* @param bounceType Type of bounce (hard/soft)
|
||||||
|
* @param reason Bounce reason
|
||||||
|
*/
|
||||||
|
recordBounce(domain: string, receivingDomain: string, bounceType: 'hard' | 'soft', reason: string): void;
|
||||||
|
/**
|
||||||
|
* Get the rate limiter instance
|
||||||
|
* @returns The unified rate limiter
|
||||||
|
*/
|
||||||
|
getRateLimiter(): UnifiedRateLimiter;
|
||||||
|
}
|
||||||
|
export {};
|
||||||
1555
dist_ts/mail/routing/classes.unified.email.server.js
Normal file
1555
dist_ts/mail/routing/classes.unified.email.server.js
Normal file
File diff suppressed because one or more lines are too long
5
dist_ts/mail/routing/index.d.ts
vendored
Normal file
5
dist_ts/mail/routing/index.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export * from './classes.email.router.js';
|
||||||
|
export * from './classes.unified.email.server.js';
|
||||||
|
export * from './classes.dns.manager.js';
|
||||||
|
export * from './interfaces.js';
|
||||||
|
export * from './classes.domain.registry.js';
|
||||||
7
dist_ts/mail/routing/index.js
Normal file
7
dist_ts/mail/routing/index.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Email routing components
|
||||||
|
export * from './classes.email.router.js';
|
||||||
|
export * from './classes.unified.email.server.js';
|
||||||
|
export * from './classes.dns.manager.js';
|
||||||
|
export * from './interfaces.js';
|
||||||
|
export * from './classes.domain.registry.js';
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL3JvdXRpbmcvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMkJBQTJCO0FBQzNCLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyxtQ0FBbUMsQ0FBQztBQUNsRCxjQUFjLDBCQUEwQixDQUFDO0FBQ3pDLGNBQWMsaUJBQWlCLENBQUM7QUFDaEMsY0FBYyw4QkFBOEIsQ0FBQyJ9
|
||||||
187
dist_ts/mail/routing/interfaces.d.ts
vendored
Normal file
187
dist_ts/mail/routing/interfaces.d.ts
vendored
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
import type { Email } from '../core/classes.email.js';
|
||||||
|
import type { IExtendedSmtpSession } from './classes.unified.email.server.js';
|
||||||
|
/**
|
||||||
|
* Route configuration for email routing
|
||||||
|
*/
|
||||||
|
export interface IEmailRoute {
|
||||||
|
/** Route identifier */
|
||||||
|
name: string;
|
||||||
|
/** Order of evaluation (higher priority evaluated first, default: 0) */
|
||||||
|
priority?: number;
|
||||||
|
/** Conditions to match */
|
||||||
|
match: IEmailMatch;
|
||||||
|
/** Action to take when matched */
|
||||||
|
action: IEmailAction;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Match criteria for email routing
|
||||||
|
*/
|
||||||
|
export interface IEmailMatch {
|
||||||
|
/** Email patterns to match recipients: "*@example.com", "admin@*" */
|
||||||
|
recipients?: string | string[];
|
||||||
|
/** Email patterns to match senders */
|
||||||
|
senders?: string | string[];
|
||||||
|
/** IP addresses or CIDR ranges to match */
|
||||||
|
clientIp?: string | string[];
|
||||||
|
/** Require authentication status */
|
||||||
|
authenticated?: boolean;
|
||||||
|
/** Headers to match */
|
||||||
|
headers?: Record<string, string | RegExp>;
|
||||||
|
/** Message size range */
|
||||||
|
sizeRange?: {
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
};
|
||||||
|
/** Subject line patterns */
|
||||||
|
subject?: string | RegExp;
|
||||||
|
/** Has attachments */
|
||||||
|
hasAttachments?: boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Action to take when route matches
|
||||||
|
*/
|
||||||
|
export interface IEmailAction {
|
||||||
|
/** Type of action to perform */
|
||||||
|
type: 'forward' | 'deliver' | 'reject' | 'process';
|
||||||
|
/** Forward action configuration */
|
||||||
|
forward?: {
|
||||||
|
/** Target host to forward to */
|
||||||
|
host: string;
|
||||||
|
/** Target port (default: 25) */
|
||||||
|
port?: number;
|
||||||
|
/** Authentication credentials */
|
||||||
|
auth?: {
|
||||||
|
user: string;
|
||||||
|
pass: string;
|
||||||
|
};
|
||||||
|
/** Preserve original headers */
|
||||||
|
preserveHeaders?: boolean;
|
||||||
|
/** Additional headers to add */
|
||||||
|
addHeaders?: Record<string, string>;
|
||||||
|
};
|
||||||
|
/** Reject action configuration */
|
||||||
|
reject?: {
|
||||||
|
/** SMTP response code */
|
||||||
|
code: number;
|
||||||
|
/** SMTP response message */
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
/** Process action configuration */
|
||||||
|
process?: {
|
||||||
|
/** Enable content scanning */
|
||||||
|
scan?: boolean;
|
||||||
|
/** Enable DKIM signing */
|
||||||
|
dkim?: boolean;
|
||||||
|
/** Delivery queue priority */
|
||||||
|
queue?: 'normal' | 'priority' | 'bulk';
|
||||||
|
};
|
||||||
|
/** Options for various action types */
|
||||||
|
options?: {
|
||||||
|
/** MTA specific options */
|
||||||
|
mtaOptions?: {
|
||||||
|
domain?: string;
|
||||||
|
allowLocalDelivery?: boolean;
|
||||||
|
localDeliveryPath?: string;
|
||||||
|
dkimSign?: boolean;
|
||||||
|
dkimOptions?: {
|
||||||
|
domainName: string;
|
||||||
|
keySelector: string;
|
||||||
|
privateKey?: string;
|
||||||
|
};
|
||||||
|
smtpBanner?: string;
|
||||||
|
maxConnections?: number;
|
||||||
|
connTimeout?: number;
|
||||||
|
spoolDir?: string;
|
||||||
|
};
|
||||||
|
/** Content scanning configuration */
|
||||||
|
contentScanning?: boolean;
|
||||||
|
scanners?: Array<{
|
||||||
|
type: 'spam' | 'virus' | 'attachment';
|
||||||
|
threshold?: number;
|
||||||
|
action: 'tag' | 'reject';
|
||||||
|
blockedExtensions?: string[];
|
||||||
|
}>;
|
||||||
|
/** Email transformations */
|
||||||
|
transformations?: Array<{
|
||||||
|
type: string;
|
||||||
|
header?: string;
|
||||||
|
value?: string;
|
||||||
|
domains?: string[];
|
||||||
|
append?: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
/** Delivery options (applies to forward/process/deliver) */
|
||||||
|
delivery?: {
|
||||||
|
/** Rate limit (messages per minute) */
|
||||||
|
rateLimit?: number;
|
||||||
|
/** Number of retry attempts */
|
||||||
|
retries?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Context for route evaluation
|
||||||
|
*/
|
||||||
|
export interface IEmailContext {
|
||||||
|
/** The email being routed */
|
||||||
|
email: Email;
|
||||||
|
/** The SMTP session */
|
||||||
|
session: IExtendedSmtpSession;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Email domain configuration
|
||||||
|
*/
|
||||||
|
export interface IEmailDomainConfig {
|
||||||
|
/** Domain name */
|
||||||
|
domain: string;
|
||||||
|
/** DNS handling mode */
|
||||||
|
dnsMode: 'forward' | 'internal-dns' | 'external-dns';
|
||||||
|
/** DNS configuration based on mode */
|
||||||
|
dns?: {
|
||||||
|
/** For 'forward' mode */
|
||||||
|
forward?: {
|
||||||
|
/** Skip DNS validation (default: false) */
|
||||||
|
skipDnsValidation?: boolean;
|
||||||
|
/** Target server's expected domain */
|
||||||
|
targetDomain?: string;
|
||||||
|
};
|
||||||
|
/** For 'internal-dns' mode */
|
||||||
|
internal?: {
|
||||||
|
/** TTL for DNS records in seconds (default: 3600) */
|
||||||
|
ttl?: number;
|
||||||
|
/** MX record priority (default: 10) */
|
||||||
|
mxPriority?: number;
|
||||||
|
};
|
||||||
|
/** For 'external-dns' mode */
|
||||||
|
external?: {
|
||||||
|
/** Custom DNS servers (default: system DNS) */
|
||||||
|
servers?: string[];
|
||||||
|
/** Which records to validate (default: ['MX', 'SPF', 'DKIM', 'DMARC']) */
|
||||||
|
requiredRecords?: ('MX' | 'SPF' | 'DKIM' | 'DMARC')[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** Per-domain DKIM settings (DKIM always enabled) */
|
||||||
|
dkim?: {
|
||||||
|
/** DKIM selector (default: 'default') */
|
||||||
|
selector?: string;
|
||||||
|
/** Key size in bits (default: 2048) */
|
||||||
|
keySize?: number;
|
||||||
|
/** Automatically rotate keys (default: false) */
|
||||||
|
rotateKeys?: boolean;
|
||||||
|
/** Days between key rotations (default: 90) */
|
||||||
|
rotationInterval?: number;
|
||||||
|
};
|
||||||
|
/** Per-domain rate limits */
|
||||||
|
rateLimits?: {
|
||||||
|
outbound?: {
|
||||||
|
messagesPerMinute?: number;
|
||||||
|
messagesPerHour?: number;
|
||||||
|
messagesPerDay?: number;
|
||||||
|
};
|
||||||
|
inbound?: {
|
||||||
|
messagesPerMinute?: number;
|
||||||
|
connectionsPerIp?: number;
|
||||||
|
recipientsPerMessage?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
2
dist_ts/mail/routing/interfaces.js
Normal file
2
dist_ts/mail/routing/interfaces.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export {};
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvcm91dGluZy9pbnRlcmZhY2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIifQ==
|
||||||
68
dist_ts/mail/security/classes.dkimcreator.d.ts
vendored
Normal file
68
dist_ts/mail/security/classes.dkimcreator.d.ts
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import * as plugins from '../../plugins.js';
|
||||||
|
import { Email } from '../core/classes.email.js';
|
||||||
|
export interface IKeyPaths {
|
||||||
|
privateKeyPath: string;
|
||||||
|
publicKeyPath: string;
|
||||||
|
}
|
||||||
|
export interface IDkimKeyMetadata {
|
||||||
|
domain: string;
|
||||||
|
selector: string;
|
||||||
|
createdAt: number;
|
||||||
|
rotatedAt?: number;
|
||||||
|
previousSelector?: string;
|
||||||
|
keySize: number;
|
||||||
|
}
|
||||||
|
export declare class DKIMCreator {
|
||||||
|
private keysDir;
|
||||||
|
private storageManager?;
|
||||||
|
constructor(keysDir?: string, storageManager?: any);
|
||||||
|
getKeyPathsForDomain(domainArg: string): Promise<IKeyPaths>;
|
||||||
|
handleDKIMKeysForDomain(domainArg: string): Promise<void>;
|
||||||
|
handleDKIMKeysForEmail(email: Email): Promise<void>;
|
||||||
|
readDKIMKeys(domainArg: string): Promise<{
|
||||||
|
privateKey: string;
|
||||||
|
publicKey: string;
|
||||||
|
}>;
|
||||||
|
createDKIMKeys(): Promise<{
|
||||||
|
privateKey: string;
|
||||||
|
publicKey: string;
|
||||||
|
}>;
|
||||||
|
storeDKIMKeys(privateKey: string, publicKey: string, privateKeyPath: string, publicKeyPath: string): Promise<void>;
|
||||||
|
createAndStoreDKIMKeys(domain: string): Promise<void>;
|
||||||
|
getDNSRecordForDomain(domainArg: string): Promise<plugins.tsclass.network.IDnsRecord>;
|
||||||
|
/**
|
||||||
|
* Get DKIM key metadata for a domain
|
||||||
|
*/
|
||||||
|
private getKeyMetadata;
|
||||||
|
/**
|
||||||
|
* Save DKIM key metadata
|
||||||
|
*/
|
||||||
|
private saveKeyMetadata;
|
||||||
|
/**
|
||||||
|
* Check if DKIM keys need rotation
|
||||||
|
*/
|
||||||
|
needsRotation(domain: string, selector?: string, rotationIntervalDays?: number): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* Rotate DKIM keys for a domain
|
||||||
|
*/
|
||||||
|
rotateDkimKeys(domain: string, currentSelector?: string, keySize?: number): Promise<string>;
|
||||||
|
/**
|
||||||
|
* Get key paths for a specific selector
|
||||||
|
*/
|
||||||
|
getKeyPathsForSelector(domain: string, selector: string): Promise<IKeyPaths>;
|
||||||
|
/**
|
||||||
|
* Read DKIM keys for a specific selector
|
||||||
|
*/
|
||||||
|
readDKIMKeysForSelector(domain: string, selector: string): Promise<{
|
||||||
|
privateKey: string;
|
||||||
|
publicKey: string;
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* Get DNS record for a specific selector
|
||||||
|
*/
|
||||||
|
getDNSRecordForSelector(domain: string, selector: string): Promise<plugins.tsclass.network.IDnsRecord>;
|
||||||
|
/**
|
||||||
|
* Clean up old DKIM keys after grace period
|
||||||
|
*/
|
||||||
|
cleanupOldKeys(domain: string, gracePeriodDays?: number): Promise<void>;
|
||||||
|
}
|
||||||
348
dist_ts/mail/security/classes.dkimcreator.js
Normal file
348
dist_ts/mail/security/classes.dkimcreator.js
Normal file
File diff suppressed because one or more lines are too long
29
dist_ts/mail/security/classes.dkimverifier.d.ts
vendored
Normal file
29
dist_ts/mail/security/classes.dkimverifier.d.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Result of a DKIM verification
|
||||||
|
*/
|
||||||
|
export interface IDkimVerificationResult {
|
||||||
|
isValid: boolean;
|
||||||
|
domain?: string;
|
||||||
|
selector?: string;
|
||||||
|
status?: string;
|
||||||
|
details?: any;
|
||||||
|
errorMessage?: string;
|
||||||
|
signatureFields?: Record<string, string>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* DKIM verifier — delegates to the Rust security bridge.
|
||||||
|
*/
|
||||||
|
export declare class DKIMVerifier {
|
||||||
|
constructor();
|
||||||
|
/**
|
||||||
|
* Verify DKIM signature for an email via Rust bridge
|
||||||
|
*/
|
||||||
|
verify(emailData: string, options?: {
|
||||||
|
useCache?: boolean;
|
||||||
|
returnDetails?: boolean;
|
||||||
|
}): Promise<IDkimVerificationResult>;
|
||||||
|
/** No-op — Rust bridge handles its own caching */
|
||||||
|
clearCache(): void;
|
||||||
|
/** Always 0 — cache is managed by the Rust side */
|
||||||
|
getCacheSize(): number;
|
||||||
|
}
|
||||||
58
dist_ts/mail/security/classes.dkimverifier.js
Normal file
58
dist_ts/mail/security/classes.dkimverifier.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { logger } from '../../logger.js';
|
||||||
|
import { SecurityLogger, SecurityLogLevel, SecurityEventType } from '../../security/index.js';
|
||||||
|
import { RustSecurityBridge } from '../../security/classes.rustsecuritybridge.js';
|
||||||
|
/**
|
||||||
|
* DKIM verifier — delegates to the Rust security bridge.
|
||||||
|
*/
|
||||||
|
export class DKIMVerifier {
|
||||||
|
constructor() { }
|
||||||
|
/**
|
||||||
|
* Verify DKIM signature for an email via Rust bridge
|
||||||
|
*/
|
||||||
|
async verify(emailData, options = {}) {
|
||||||
|
try {
|
||||||
|
const bridge = RustSecurityBridge.getInstance();
|
||||||
|
const results = await bridge.verifyDkim(emailData);
|
||||||
|
const first = results[0];
|
||||||
|
const result = {
|
||||||
|
isValid: first?.is_valid ?? false,
|
||||||
|
domain: first?.domain ?? undefined,
|
||||||
|
selector: first?.selector ?? undefined,
|
||||||
|
status: first?.status ?? 'none',
|
||||||
|
details: options.returnDetails ? results : undefined,
|
||||||
|
};
|
||||||
|
SecurityLogger.getInstance().logEvent({
|
||||||
|
level: result.isValid ? SecurityLogLevel.INFO : SecurityLogLevel.WARN,
|
||||||
|
type: SecurityEventType.DKIM,
|
||||||
|
message: `DKIM verification ${result.isValid ? 'passed' : 'failed'} for domain ${result.domain || 'unknown'}`,
|
||||||
|
details: { selector: result.selector, status: result.status },
|
||||||
|
domain: result.domain || 'unknown',
|
||||||
|
success: result.isValid
|
||||||
|
});
|
||||||
|
logger.log(result.isValid ? 'info' : 'warn', `DKIM verification: ${result.status} for domain ${result.domain || 'unknown'}`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
logger.log('error', `DKIM verification failed: ${error.message}`);
|
||||||
|
SecurityLogger.getInstance().logEvent({
|
||||||
|
level: SecurityLogLevel.ERROR,
|
||||||
|
type: SecurityEventType.DKIM,
|
||||||
|
message: `DKIM verification error`,
|
||||||
|
details: { error: error.message },
|
||||||
|
success: false
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
status: 'temperror',
|
||||||
|
errorMessage: `Verification error: ${error.message}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** No-op — Rust bridge handles its own caching */
|
||||||
|
clearCache() { }
|
||||||
|
/** Always 0 — cache is managed by the Rust side */
|
||||||
|
getCacheSize() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ka2ltdmVyaWZpZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL3NlY3VyaXR5L2NsYXNzZXMuZGtpbXZlcmlmaWVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QyxPQUFPLEVBQUUsY0FBYyxFQUFFLGdCQUFnQixFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDOUYsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sOENBQThDLENBQUM7QUFlbEY7O0dBRUc7QUFDSCxNQUFNLE9BQU8sWUFBWTtJQUN2QixnQkFBZSxDQUFDO0lBRWhCOztPQUVHO0lBQ0ksS0FBSyxDQUFDLE1BQU0sQ0FDakIsU0FBaUIsRUFDakIsVUFHSSxFQUFFO1FBRU4sSUFBSSxDQUFDO1lBQ0gsTUFBTSxNQUFNLEdBQUcsa0JBQWtCLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDaEQsTUFBTSxPQUFPLEdBQUcsTUFBTSxNQUFNLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQ25ELE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUV6QixNQUFNLE1BQU0sR0FBNEI7Z0JBQ3RDLE9BQU8sRUFBRSxLQUFLLEVBQUUsUUFBUSxJQUFJLEtBQUs7Z0JBQ2pDLE1BQU0sRUFBRSxLQUFLLEVBQUUsTUFBTSxJQUFJLFNBQVM7Z0JBQ2xDLFFBQVEsRUFBRSxLQUFLLEVBQUUsUUFBUSxJQUFJLFNBQVM7Z0JBQ3RDLE1BQU0sRUFBRSxLQUFLLEVBQUUsTUFBTSxJQUFJLE1BQU07Z0JBQy9CLE9BQU8sRUFBRSxPQUFPLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFNBQVM7YUFDckQsQ0FBQztZQUVGLGNBQWMsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxRQUFRLENBQUM7Z0JBQ3BDLEtBQUssRUFBRSxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLElBQUk7Z0JBQ3JFLElBQUksRUFBRSxpQkFBaUIsQ0FBQyxJQUFJO2dCQUM1QixPQUFPLEVBQUUscUJBQXFCLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsUUFBUSxlQUFlLE1BQU0sQ0FBQyxNQUFNLElBQUksU0FBUyxFQUFFO2dCQUM3RyxPQUFPLEVBQUUsRUFBRSxRQUFRLEVBQUUsTUFBTSxDQUFDLFFBQVEsRUFBRSxNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU0sRUFBRTtnQkFDN0QsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNLElBQUksU0FBUztnQkFDbEMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPO2FBQ3hCLENBQUMsQ0FBQztZQUVILE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQ3pDLHNCQUFzQixNQUFNLENBQUMsTUFBTSxlQUFlLE1BQU0sQ0FBQyxNQUFNLElBQUksU0FBUyxFQUFFLENBQUMsQ0FBQztZQUVsRixPQUFPLE1BQU0sQ0FBQztRQUNoQixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDZCQUE2QixLQUFLLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUVsRSxjQUFjLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDO2dCQUNwQyxLQUFLLEVBQUUsZ0JBQWdCLENBQUMsS0FBSztnQkFDN0IsSUFBSSxFQUFFLGlCQUFpQixDQUFDLElBQUk7Z0JBQzVCLE9BQU8sRUFBRSx5QkFBeUI7Z0JBQ2xDLE9BQU8sRUFBRSxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsT0FBTyxFQUFFO2dCQUNqQyxPQUFPLEVBQUUsS0FBSzthQUNmLENBQUMsQ0FBQztZQUVILE9BQU87Z0JBQ0wsT0FBTyxFQUFFLEtBQUs7Z0JBQ2QsTUFBTSxFQUFFLFdBQVc7Z0JBQ25CLFlBQVksRUFBRSx1QkFBdUIsS0FBSyxDQUFDLE9BQU8sRUFBRTthQUNyRCxDQUFDO1FBQ0osQ0FBQztJQUNILENBQUM7SUFFRCxrREFBa0Q7SUFDM0MsVUFBVSxLQUFVLENBQUM7SUFFNUIsbURBQW1EO0lBQzVDLFlBQVk7UUFDakIsT0FBTyxDQUFDLENBQUM7SUFDWCxDQUFDO0NBQ0YifQ==
|
||||||
123
dist_ts/mail/security/classes.dmarcverifier.d.ts
vendored
Normal file
123
dist_ts/mail/security/classes.dmarcverifier.d.ts
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import type { Email } from '../core/classes.email.js';
|
||||||
|
/**
|
||||||
|
* DMARC policy types
|
||||||
|
*/
|
||||||
|
export declare enum DmarcPolicy {
|
||||||
|
NONE = "none",
|
||||||
|
QUARANTINE = "quarantine",
|
||||||
|
REJECT = "reject"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* DMARC alignment modes
|
||||||
|
*/
|
||||||
|
export declare enum DmarcAlignment {
|
||||||
|
RELAXED = "r",
|
||||||
|
STRICT = "s"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* DMARC record fields
|
||||||
|
*/
|
||||||
|
export interface DmarcRecord {
|
||||||
|
version: string;
|
||||||
|
policy: DmarcPolicy;
|
||||||
|
subdomainPolicy?: DmarcPolicy;
|
||||||
|
pct?: number;
|
||||||
|
adkim?: DmarcAlignment;
|
||||||
|
aspf?: DmarcAlignment;
|
||||||
|
reportInterval?: number;
|
||||||
|
failureOptions?: string;
|
||||||
|
reportUriAggregate?: string[];
|
||||||
|
reportUriForensic?: string[];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* DMARC verification result
|
||||||
|
*/
|
||||||
|
export interface DmarcResult {
|
||||||
|
hasDmarc: boolean;
|
||||||
|
record?: DmarcRecord;
|
||||||
|
spfDomainAligned: boolean;
|
||||||
|
dkimDomainAligned: boolean;
|
||||||
|
spfPassed: boolean;
|
||||||
|
dkimPassed: boolean;
|
||||||
|
policyEvaluated: DmarcPolicy;
|
||||||
|
actualPolicy: DmarcPolicy;
|
||||||
|
appliedPercentage: number;
|
||||||
|
action: 'pass' | 'quarantine' | 'reject';
|
||||||
|
details: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Class for verifying and enforcing DMARC policies
|
||||||
|
*/
|
||||||
|
export declare class DmarcVerifier {
|
||||||
|
private dnsManager?;
|
||||||
|
constructor(dnsManager?: any);
|
||||||
|
/**
|
||||||
|
* Parse a DMARC record from a TXT record string
|
||||||
|
* @param record DMARC TXT record string
|
||||||
|
* @returns Parsed DMARC record or null if invalid
|
||||||
|
*/
|
||||||
|
parseDmarcRecord(record: string): DmarcRecord | null;
|
||||||
|
/**
|
||||||
|
* Check if domains are aligned according to DMARC policy
|
||||||
|
* @param headerDomain Domain from header (From)
|
||||||
|
* @param authDomain Domain from authentication (SPF, DKIM)
|
||||||
|
* @param alignment Alignment mode
|
||||||
|
* @returns Whether the domains are aligned
|
||||||
|
*/
|
||||||
|
private isDomainAligned;
|
||||||
|
/**
|
||||||
|
* Extract domain from an email address
|
||||||
|
* @param email Email address
|
||||||
|
* @returns Domain part of the email
|
||||||
|
*/
|
||||||
|
private getDomainFromEmail;
|
||||||
|
/**
|
||||||
|
* Check if DMARC verification should be applied based on percentage
|
||||||
|
* @param record DMARC record
|
||||||
|
* @returns Whether DMARC verification should be applied
|
||||||
|
*/
|
||||||
|
private shouldApplyDmarc;
|
||||||
|
/**
|
||||||
|
* Determine the action to take based on DMARC policy
|
||||||
|
* @param policy DMARC policy
|
||||||
|
* @returns Action to take
|
||||||
|
*/
|
||||||
|
private determineAction;
|
||||||
|
/**
|
||||||
|
* Verify DMARC for an incoming email
|
||||||
|
* @param email Email to verify
|
||||||
|
* @param spfResult SPF verification result
|
||||||
|
* @param dkimResult DKIM verification result
|
||||||
|
* @returns DMARC verification result
|
||||||
|
*/
|
||||||
|
verify(email: Email, spfResult: {
|
||||||
|
domain: string;
|
||||||
|
result: boolean;
|
||||||
|
}, dkimResult: {
|
||||||
|
domain: string;
|
||||||
|
result: boolean;
|
||||||
|
}): Promise<DmarcResult>;
|
||||||
|
/**
|
||||||
|
* Apply DMARC policy to an email
|
||||||
|
* @param email Email to apply policy to
|
||||||
|
* @param dmarcResult DMARC verification result
|
||||||
|
* @returns Whether the email should be accepted
|
||||||
|
*/
|
||||||
|
applyPolicy(email: Email, dmarcResult: DmarcResult): boolean;
|
||||||
|
/**
|
||||||
|
* End-to-end DMARC verification and policy application
|
||||||
|
* This method should be called after SPF and DKIM verification
|
||||||
|
* @param email Email to verify
|
||||||
|
* @param spfResult SPF verification result
|
||||||
|
* @param dkimResult DKIM verification result
|
||||||
|
* @returns Whether the email should be accepted
|
||||||
|
*/
|
||||||
|
verifyAndApply(email: Email, spfResult: {
|
||||||
|
domain: string;
|
||||||
|
result: boolean;
|
||||||
|
}, dkimResult: {
|
||||||
|
domain: string;
|
||||||
|
result: boolean;
|
||||||
|
}): Promise<boolean>;
|
||||||
|
}
|
||||||
366
dist_ts/mail/security/classes.dmarcverifier.js
Normal file
366
dist_ts/mail/security/classes.dmarcverifier.js
Normal file
File diff suppressed because one or more lines are too long
71
dist_ts/mail/security/classes.spfverifier.d.ts
vendored
Normal file
71
dist_ts/mail/security/classes.spfverifier.d.ts
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import type { Email } from '../core/classes.email.js';
|
||||||
|
/**
|
||||||
|
* SPF result qualifiers
|
||||||
|
*/
|
||||||
|
export declare enum SpfQualifier {
|
||||||
|
PASS = "+",
|
||||||
|
NEUTRAL = "?",
|
||||||
|
SOFTFAIL = "~",
|
||||||
|
FAIL = "-"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SPF mechanism types
|
||||||
|
*/
|
||||||
|
export declare enum SpfMechanismType {
|
||||||
|
ALL = "all",
|
||||||
|
INCLUDE = "include",
|
||||||
|
A = "a",
|
||||||
|
MX = "mx",
|
||||||
|
IP4 = "ip4",
|
||||||
|
IP6 = "ip6",
|
||||||
|
EXISTS = "exists",
|
||||||
|
REDIRECT = "redirect",
|
||||||
|
EXP = "exp"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SPF mechanism definition
|
||||||
|
*/
|
||||||
|
export interface SpfMechanism {
|
||||||
|
qualifier: SpfQualifier;
|
||||||
|
type: SpfMechanismType;
|
||||||
|
value?: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SPF record parsed data
|
||||||
|
*/
|
||||||
|
export interface SpfRecord {
|
||||||
|
version: string;
|
||||||
|
mechanisms: SpfMechanism[];
|
||||||
|
modifiers: Record<string, string>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SPF verification result
|
||||||
|
*/
|
||||||
|
export interface SpfResult {
|
||||||
|
result: 'pass' | 'neutral' | 'softfail' | 'fail' | 'temperror' | 'permerror' | 'none';
|
||||||
|
explanation?: string;
|
||||||
|
domain: string;
|
||||||
|
ip: string;
|
||||||
|
record?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Class for verifying SPF records.
|
||||||
|
* Delegates actual SPF evaluation to the Rust security bridge.
|
||||||
|
* Retains parseSpfRecord() for lightweight local parsing.
|
||||||
|
*/
|
||||||
|
export declare class SpfVerifier {
|
||||||
|
constructor(_dnsManager?: any);
|
||||||
|
/**
|
||||||
|
* Parse SPF record from TXT record (pure string parsing, no DNS)
|
||||||
|
*/
|
||||||
|
parseSpfRecord(record: string): SpfRecord | null;
|
||||||
|
/**
|
||||||
|
* Verify SPF for a given email — delegates to Rust bridge
|
||||||
|
*/
|
||||||
|
verify(email: Email, ip: string, heloDomain: string): Promise<SpfResult>;
|
||||||
|
/**
|
||||||
|
* Check if email passes SPF verification and apply headers
|
||||||
|
*/
|
||||||
|
verifyAndApply(email: Email, ip: string, heloDomain: string): Promise<boolean>;
|
||||||
|
}
|
||||||
171
dist_ts/mail/security/classes.spfverifier.js
Normal file
171
dist_ts/mail/security/classes.spfverifier.js
Normal file
File diff suppressed because one or more lines are too long
4
dist_ts/mail/security/index.d.ts
vendored
Normal file
4
dist_ts/mail/security/index.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export * from './classes.dkimcreator.js';
|
||||||
|
export * from './classes.dkimverifier.js';
|
||||||
|
export * from './classes.dmarcverifier.js';
|
||||||
|
export * from './classes.spfverifier.js';
|
||||||
6
dist_ts/mail/security/index.js
Normal file
6
dist_ts/mail/security/index.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Email security components
|
||||||
|
export * from './classes.dkimcreator.js';
|
||||||
|
export * from './classes.dkimverifier.js';
|
||||||
|
export * from './classes.dmarcverifier.js';
|
||||||
|
export * from './classes.spfverifier.js';
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL3NlY3VyaXR5L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDRCQUE0QjtBQUM1QixjQUFjLDBCQUEwQixDQUFDO0FBQ3pDLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyw0QkFBNEIsQ0FBQztBQUMzQyxjQUFjLDBCQUEwQixDQUFDIn0=
|
||||||
14
dist_ts/paths.d.ts
vendored
Normal file
14
dist_ts/paths.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export declare const baseDir: string;
|
||||||
|
export declare const packageDir: string;
|
||||||
|
export declare const distServe: string;
|
||||||
|
export declare const dataDir: string;
|
||||||
|
export declare const keysDir: string;
|
||||||
|
export declare const dnsRecordsDir: string;
|
||||||
|
export declare const sentEmailsDir: string;
|
||||||
|
export declare const receivedEmailsDir: string;
|
||||||
|
export declare const failedEmailsDir: string;
|
||||||
|
export declare const logsDir: string;
|
||||||
|
export declare const emailTemplatesDir: string;
|
||||||
|
export declare const MtaAttachmentsDir: string;
|
||||||
|
export declare const configPath: string;
|
||||||
|
export declare function ensureDirectories(): Promise<void>;
|
||||||
39
dist_ts/paths.js
Normal file
39
dist_ts/paths.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import * as plugins from './plugins.js';
|
||||||
|
// Base directories
|
||||||
|
export const baseDir = process.cwd();
|
||||||
|
export const packageDir = plugins.path.join(plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), '../');
|
||||||
|
export const distServe = plugins.path.join(packageDir, './dist_serve');
|
||||||
|
// Configure data directory with environment variable or default to .nogit/data
|
||||||
|
const DEFAULT_DATA_PATH = '.nogit/data';
|
||||||
|
export const dataDir = process.env.DATA_DIR
|
||||||
|
? process.env.DATA_DIR
|
||||||
|
: plugins.path.join(baseDir, DEFAULT_DATA_PATH);
|
||||||
|
// MTA directories
|
||||||
|
export const keysDir = plugins.path.join(dataDir, 'keys');
|
||||||
|
export const dnsRecordsDir = plugins.path.join(dataDir, 'dns');
|
||||||
|
export const sentEmailsDir = plugins.path.join(dataDir, 'emails', 'sent');
|
||||||
|
export const receivedEmailsDir = plugins.path.join(dataDir, 'emails', 'received');
|
||||||
|
export const failedEmailsDir = plugins.path.join(dataDir, 'emails', 'failed'); // For failed emails
|
||||||
|
export const logsDir = plugins.path.join(dataDir, 'logs'); // For logs
|
||||||
|
// Email template directories
|
||||||
|
export const emailTemplatesDir = plugins.path.join(dataDir, 'templates', 'email');
|
||||||
|
export const MtaAttachmentsDir = plugins.path.join(dataDir, 'attachments'); // For email attachments
|
||||||
|
// Configuration path
|
||||||
|
export const configPath = process.env.CONFIG_PATH
|
||||||
|
? process.env.CONFIG_PATH
|
||||||
|
: plugins.path.join(baseDir, 'config.json');
|
||||||
|
// Create directories if they don't exist
|
||||||
|
export async function ensureDirectories() {
|
||||||
|
// Ensure data directories
|
||||||
|
await plugins.smartfs.directory(dataDir).recursive().create();
|
||||||
|
await plugins.smartfs.directory(keysDir).recursive().create();
|
||||||
|
await plugins.smartfs.directory(dnsRecordsDir).recursive().create();
|
||||||
|
await plugins.smartfs.directory(sentEmailsDir).recursive().create();
|
||||||
|
await plugins.smartfs.directory(receivedEmailsDir).recursive().create();
|
||||||
|
await plugins.smartfs.directory(failedEmailsDir).recursive().create();
|
||||||
|
await plugins.smartfs.directory(logsDir).recursive().create();
|
||||||
|
// Ensure email template directories
|
||||||
|
await plugins.smartfs.directory(emailTemplatesDir).recursive().create();
|
||||||
|
await plugins.smartfs.directory(MtaAttachmentsDir).recursive().create();
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGF0aHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9wYXRocy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUV4QyxtQkFBbUI7QUFDbkIsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztBQUNyQyxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQ3pDLE9BQU8sQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLHdCQUF3QixDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQy9ELEtBQUssQ0FDTixDQUFDO0FBQ0YsTUFBTSxDQUFDLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxjQUFjLENBQUMsQ0FBQztBQUV2RSwrRUFBK0U7QUFDL0UsTUFBTSxpQkFBaUIsR0FBRyxhQUFhLENBQUM7QUFDeEMsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUTtJQUN6QyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRO0lBQ3RCLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztBQUVsRCxtQkFBbUI7QUFDbkIsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztBQUMxRCxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDO0FBQy9ELE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUks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
|
||||||
47
dist_ts/plugins.d.ts
vendored
Normal file
47
dist_ts/plugins.d.ts
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import * as dns from 'dns';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
import * as http from 'http';
|
||||||
|
import * as net from 'net';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as tls from 'tls';
|
||||||
|
import * as util from 'util';
|
||||||
|
export { dns, fs, crypto, http, net, os, path, tls, util, };
|
||||||
|
import * as servezoneInterfaces from '@serve.zone/interfaces';
|
||||||
|
export { servezoneInterfaces };
|
||||||
|
import * as typedrequest from '@api.global/typedrequest';
|
||||||
|
import * as typedserver from '@api.global/typedserver';
|
||||||
|
import * as typedsocket from '@api.global/typedsocket';
|
||||||
|
export { typedrequest, typedserver, typedsocket, };
|
||||||
|
import * as projectinfo from '@push.rocks/projectinfo';
|
||||||
|
import * as qenv from '@push.rocks/qenv';
|
||||||
|
import * as smartacme from '@push.rocks/smartacme';
|
||||||
|
import * as smartdata from '@push.rocks/smartdata';
|
||||||
|
import * as smartdns from '@push.rocks/smartdns';
|
||||||
|
import * as smartfile from '@push.rocks/smartfile';
|
||||||
|
import { SmartFs } from '@push.rocks/smartfs';
|
||||||
|
import * as smartguard from '@push.rocks/smartguard';
|
||||||
|
import * as smartjwt from '@push.rocks/smartjwt';
|
||||||
|
import * as smartlog from '@push.rocks/smartlog';
|
||||||
|
import * as smartmail from '@push.rocks/smartmail';
|
||||||
|
import * as smartmetrics from '@push.rocks/smartmetrics';
|
||||||
|
import * as smartnetwork from '@push.rocks/smartnetwork';
|
||||||
|
import * as smartpath from '@push.rocks/smartpath';
|
||||||
|
import * as smartproxy from '@push.rocks/smartproxy';
|
||||||
|
import * as smartpromise from '@push.rocks/smartpromise';
|
||||||
|
import * as smartrequest from '@push.rocks/smartrequest';
|
||||||
|
import * as smartrule from '@push.rocks/smartrule';
|
||||||
|
import * as smartrust from '@push.rocks/smartrust';
|
||||||
|
import * as smartrx from '@push.rocks/smartrx';
|
||||||
|
import * as smartunique from '@push.rocks/smartunique';
|
||||||
|
export declare const smartfs: SmartFs;
|
||||||
|
export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfile, SmartFs, smartguard, smartjwt, smartlog, smartmail, smartmetrics, smartnetwork, smartpath, smartproxy, smartpromise, smartrequest, smartrule, smartrust, smartrx, smartunique };
|
||||||
|
export type TLogLevel = 'error' | 'warn' | 'info' | 'success' | 'debug';
|
||||||
|
import * as cloudflare from '@apiclient.xyz/cloudflare';
|
||||||
|
export { cloudflare, };
|
||||||
|
import * as tsclass from '@tsclass/tsclass';
|
||||||
|
export { tsclass, };
|
||||||
|
import mailparser from 'mailparser';
|
||||||
|
import * as uuid from 'uuid';
|
||||||
|
export { mailparser, uuid, };
|
||||||
54
dist_ts/plugins.js
Normal file
54
dist_ts/plugins.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// node native
|
||||||
|
import * as dns from 'dns';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
import * as http from 'http';
|
||||||
|
import * as net from 'net';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as tls from 'tls';
|
||||||
|
import * as util from 'util';
|
||||||
|
export { dns, fs, crypto, http, net, os, path, tls, util, };
|
||||||
|
// @serve.zone scope
|
||||||
|
import * as servezoneInterfaces from '@serve.zone/interfaces';
|
||||||
|
export { servezoneInterfaces };
|
||||||
|
// @api.global scope
|
||||||
|
import * as typedrequest from '@api.global/typedrequest';
|
||||||
|
import * as typedserver from '@api.global/typedserver';
|
||||||
|
import * as typedsocket from '@api.global/typedsocket';
|
||||||
|
export { typedrequest, typedserver, typedsocket, };
|
||||||
|
// @push.rocks scope
|
||||||
|
import * as projectinfo from '@push.rocks/projectinfo';
|
||||||
|
import * as qenv from '@push.rocks/qenv';
|
||||||
|
import * as smartacme from '@push.rocks/smartacme';
|
||||||
|
import * as smartdata from '@push.rocks/smartdata';
|
||||||
|
import * as smartdns from '@push.rocks/smartdns';
|
||||||
|
import * as smartfile from '@push.rocks/smartfile';
|
||||||
|
import { SmartFs, SmartFsProviderNode } from '@push.rocks/smartfs';
|
||||||
|
import * as smartguard from '@push.rocks/smartguard';
|
||||||
|
import * as smartjwt from '@push.rocks/smartjwt';
|
||||||
|
import * as smartlog from '@push.rocks/smartlog';
|
||||||
|
import * as smartmail from '@push.rocks/smartmail';
|
||||||
|
import * as smartmetrics from '@push.rocks/smartmetrics';
|
||||||
|
import * as smartnetwork from '@push.rocks/smartnetwork';
|
||||||
|
import * as smartpath from '@push.rocks/smartpath';
|
||||||
|
import * as smartproxy from '@push.rocks/smartproxy';
|
||||||
|
import * as smartpromise from '@push.rocks/smartpromise';
|
||||||
|
import * as smartrequest from '@push.rocks/smartrequest';
|
||||||
|
import * as smartrule from '@push.rocks/smartrule';
|
||||||
|
import * as smartrust from '@push.rocks/smartrust';
|
||||||
|
import * as smartrx from '@push.rocks/smartrx';
|
||||||
|
import * as smartunique from '@push.rocks/smartunique';
|
||||||
|
export const smartfs = new SmartFs(new SmartFsProviderNode());
|
||||||
|
export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfile, SmartFs, smartguard, smartjwt, smartlog, smartmail, smartmetrics, smartnetwork, smartpath, smartproxy, smartpromise, smartrequest, smartrule, smartrust, smartrx, smartunique };
|
||||||
|
// apiclient.xyz scope
|
||||||
|
import * as cloudflare from '@apiclient.xyz/cloudflare';
|
||||||
|
export { cloudflare, };
|
||||||
|
// tsclass scope
|
||||||
|
import * as tsclass from '@tsclass/tsclass';
|
||||||
|
export { tsclass, };
|
||||||
|
// third party
|
||||||
|
import mailparser from 'mailparser';
|
||||||
|
import * as uuid from 'uuid';
|
||||||
|
export { mailparser, uuid, };
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYztBQUNkLE9BQU8sS0FBSyxHQUFHLE1BQU0sS0FBSyxDQUFDO0FBQzNCLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBQ2pDLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxHQUFHLE1BQU0sS0FBSyxDQUFDO0FBQzNCLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxHQUFHLE1BQU0sS0FBSyxDQUFDO0FBQzNCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBRTdCLE9BQU8sRUFDTCxHQUFHLEVBQ0gsRUFBRSxFQUNGLE1BQU0sRUFDTixJQUFJLEVBQ0osR0FBRyxFQUNILEVBQUUsRUFDRixJQUFJLEVBQ0osR0FBRyxFQUNILElBQUksR0FDTCxDQUFBO0FBRUQsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxtQkFBbUIsTUFBTSx3QkFBd0IsQ0FBQztBQUU5RCxPQUFPLEVBQ0wsbUJBQW1CLEVBQ3BCLENBQUE7QUFFRCxvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLFlBQVksTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEtBQUssV0FBVyxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFFdkQsT0FBTyxFQUNMLFlBQVksRUFDWixXQUFXLEVBQ1gsV0FBVyxHQUNaLENBQUE7QUFFRCxvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLFdBQVcsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEtBQUssSUFBSSxNQUFNLGtCQUFrQixDQUFDO0FBQ3pDLE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRCxPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxFQUFFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ25FLE9BQU8sS0FBSyxVQUFVLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxLQUFLLFFBQVEsTUFBTSxzQkFBc0IsQ0FBQztBQUNqRCxPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxLQUFLLFlBQVksTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEtBQUssWUFBWSxNQUFNLDBCQUEwQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxLQUFLLFVBQVUsTUFBTSx3QkFBd0IsQ0FBQztBQUNyRCxPQUFPLEtBQUssWUFBWSxNQUFNLDBCQUEwQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxZQUFZLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRCxPQUFPLEtBQUssU0FBUyxNQUFNLHVCQUF1QixDQUFDO0FBQ25ELE9BQU8sS0FBSyxPQUFPLE1BQU0scUJBQXFCLENBQUM7QUFDL0MsT0FBTyxLQUFLLFdBQVcsTUFBTSx5QkFBeUIsQ0FBQztBQUV2RCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsSUFBSSxPQUFPLENBQUMsSUFBSSxtQkFBbUIsRUFBRSxDQUFDLENBQUM7QUFFOUQsT0FBTyxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsV0FBVyxFQUFFLENBQUM7QUFLdlAsc0JBQXNCO0FBQ3RCLE9BQU8sS0FBSyxVQUFVLE1BQU0sMkJBQTJCLENBQUM7QUFFeEQsT0FBTyxFQUNMLFVBQVUsR0FDWCxDQUFBO0FBRUQsZ0JBQWdCO0FBQ2hCLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFFNUMsT0FBTyxFQUNMLE9BQU8sR0FDUixDQUFBO0FBRUQsY0FBYztBQUNkLE9BQU8sVUFBVSxNQUFNLFlBQVksQ0FBQztBQUNwQyxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUU3QixPQUFPLEVBQ0wsVUFBVSxFQUNWLElBQUksR0FDTCxDQUFBIn0=
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user