Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ffe294643c | |||
| f1071faf3d | |||
| 6b082cee8f | |||
| 9185242530 | |||
| 8293663619 | |||
| 199b9b79d2 | |||
| 91b49182bb | |||
| 564b65c7f2 | |||
| 8bd8c295b0 | |||
| 237dba3bab | |||
| bcde137332 | |||
| 14be3cdb9a | |||
| f262f602a0 | |||
| 0ac598818f | |||
| c7b3206140 | |||
| 17f5661636 | |||
| 6523c55516 | |||
| 9cd15342e0 | |||
| 0018b19164 | |||
| 7ecdd9f1e4 | |||
| 1698df3a53 | |||
| d7f37afc30 | |||
| 27b6bb779e | |||
| d4778d15fc | |||
| 269bafc1bf | |||
| d6a1cf5bf4 | |||
| e49becec95 |
26
.gitea/release-template.md
Normal file
26
.gitea/release-template.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
## MAILER {{VERSION}}
|
||||||
|
|
||||||
|
Pre-compiled binaries for multiple platforms.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
#### Option 1: Via npm (recommended)
|
||||||
|
```bash
|
||||||
|
npm install -g @push.rocks/smartmta
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Option 2: Direct binary download
|
||||||
|
Download the appropriate binary for your platform from the assets below and make it executable.
|
||||||
|
|
||||||
|
### Supported Platforms
|
||||||
|
- Linux x86_64 (x64)
|
||||||
|
- Linux ARM64 (aarch64)
|
||||||
|
- macOS x86_64 (Intel)
|
||||||
|
- macOS ARM64 (Apple Silicon)
|
||||||
|
- Windows x86_64
|
||||||
|
|
||||||
|
### Checksums
|
||||||
|
SHA256 checksums are provided in `SHA256SUMS.txt` for binary verification.
|
||||||
|
|
||||||
|
### npm Package
|
||||||
|
The npm package includes automatic binary detection and installation for your platform.
|
||||||
84
.gitea/workflows/ci.yml
Normal file
84
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- 'migration/**'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Type Check & Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Deno
|
||||||
|
uses: denoland/setup-deno@v1
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
|
- name: Check TypeScript types
|
||||||
|
run: deno check mod.ts
|
||||||
|
|
||||||
|
- name: Lint code
|
||||||
|
run: deno lint
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Format check
|
||||||
|
run: deno fmt --check
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build Test (Current Platform)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Deno
|
||||||
|
uses: denoland/setup-deno@v1
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
|
- name: Compile for current platform
|
||||||
|
run: |
|
||||||
|
echo "Testing compilation for Linux x86_64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output mailer-test \
|
||||||
|
--target x86_64-unknown-linux-gnu mod.ts
|
||||||
|
|
||||||
|
- name: Test binary execution
|
||||||
|
run: |
|
||||||
|
chmod +x mailer-test
|
||||||
|
./mailer-test --version
|
||||||
|
./mailer-test help
|
||||||
|
|
||||||
|
build-all:
|
||||||
|
name: Build All Platforms
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Deno
|
||||||
|
uses: denoland/setup-deno@v1
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
|
- name: Compile all platform binaries
|
||||||
|
run: bash scripts/compile-all.sh
|
||||||
|
|
||||||
|
- name: Upload all binaries as artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: mailer-binaries.zip
|
||||||
|
path: dist/binaries/*
|
||||||
|
retention-days: 30
|
||||||
129
.gitea/workflows/npm-publish.yml
Normal file
129
.gitea/workflows/npm-publish.yml
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
name: Publish to npm
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
npm-publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Deno
|
||||||
|
uses: denoland/setup-deno@v1
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
|
- name: Setup Node.js for npm publishing
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '18.x'
|
||||||
|
registry-url: 'https://registry.npmjs.org/'
|
||||||
|
|
||||||
|
- name: Get version from tag
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/}
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "version_number=${VERSION#v}" >> $GITHUB_OUTPUT
|
||||||
|
echo "Publishing version: $VERSION"
|
||||||
|
|
||||||
|
- name: Verify deno.json version matches tag
|
||||||
|
run: |
|
||||||
|
DENO_VERSION=$(grep -o '"version": "[^"]*"' deno.json | cut -d'"' -f4)
|
||||||
|
TAG_VERSION="${{ steps.version.outputs.version_number }}"
|
||||||
|
echo "deno.json version: $DENO_VERSION"
|
||||||
|
echo "Tag version: $TAG_VERSION"
|
||||||
|
if [ "$DENO_VERSION" != "$TAG_VERSION" ]; then
|
||||||
|
echo "ERROR: Version mismatch!"
|
||||||
|
echo "deno.json has version $DENO_VERSION but tag is $TAG_VERSION"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Compile binaries for npm package
|
||||||
|
run: |
|
||||||
|
echo "Compiling binaries for npm package..."
|
||||||
|
deno task compile
|
||||||
|
echo ""
|
||||||
|
echo "Binary sizes:"
|
||||||
|
ls -lh dist/binaries/
|
||||||
|
|
||||||
|
- name: Generate SHA256 checksums
|
||||||
|
run: |
|
||||||
|
cd dist/binaries
|
||||||
|
sha256sum * > SHA256SUMS
|
||||||
|
cat SHA256SUMS
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
- name: Sync package.json version
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version_number }}"
|
||||||
|
echo "Syncing package.json to version ${VERSION}..."
|
||||||
|
npm version ${VERSION} --no-git-tag-version --allow-same-version
|
||||||
|
echo "package.json version: $(grep '"version"' package.json | head -1)"
|
||||||
|
|
||||||
|
- name: Create npm package
|
||||||
|
run: |
|
||||||
|
echo "Creating npm package..."
|
||||||
|
npm pack
|
||||||
|
echo ""
|
||||||
|
echo "Package created:"
|
||||||
|
ls -lh *.tgz
|
||||||
|
|
||||||
|
- name: Test local installation
|
||||||
|
run: |
|
||||||
|
echo "Testing local package installation..."
|
||||||
|
PACKAGE_FILE=$(ls *.tgz)
|
||||||
|
npm install -g ${PACKAGE_FILE}
|
||||||
|
echo ""
|
||||||
|
echo "Testing mailer command:"
|
||||||
|
mailer --version || echo "Note: Binary execution may fail in CI environment"
|
||||||
|
echo ""
|
||||||
|
echo "Checking installed files:"
|
||||||
|
npm ls -g @serve.zone/mailer || true
|
||||||
|
|
||||||
|
- name: Publish to npm
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
run: |
|
||||||
|
echo "Publishing to npm registry..."
|
||||||
|
npm publish --access public
|
||||||
|
echo ""
|
||||||
|
echo "✅ Successfully published @serve.zone/mailer to npm!"
|
||||||
|
echo ""
|
||||||
|
echo "Package info:"
|
||||||
|
npm view @serve.zone/mailer
|
||||||
|
|
||||||
|
- name: Verify npm package
|
||||||
|
run: |
|
||||||
|
echo "Waiting for npm propagation..."
|
||||||
|
sleep 30
|
||||||
|
echo ""
|
||||||
|
echo "Verifying published package..."
|
||||||
|
npm view @serve.zone/mailer
|
||||||
|
echo ""
|
||||||
|
echo "Testing installation from npm:"
|
||||||
|
npm install -g @serve.zone/mailer
|
||||||
|
echo ""
|
||||||
|
echo "Package installed successfully!"
|
||||||
|
which mailer || echo "Binary location check skipped"
|
||||||
|
|
||||||
|
- name: Publish Summary
|
||||||
|
run: |
|
||||||
|
echo "================================================"
|
||||||
|
echo " npm Publish Complete!"
|
||||||
|
echo "================================================"
|
||||||
|
echo ""
|
||||||
|
echo "✅ Package: @serve.zone/mailer"
|
||||||
|
echo "✅ Version: ${{ steps.version.outputs.version }}"
|
||||||
|
echo ""
|
||||||
|
echo "Installation:"
|
||||||
|
echo " npm install -g @serve.zone/mailer"
|
||||||
|
echo ""
|
||||||
|
echo "Registry:"
|
||||||
|
echo " https://www.npmjs.com/package/@serve.zone/mailer"
|
||||||
|
echo ""
|
||||||
249
.gitea/workflows/release.yml
Normal file
249
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Deno
|
||||||
|
uses: denoland/setup-deno@v1
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
|
||||||
|
- name: Get version from tag
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/}
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "version_number=${VERSION#v}" >> $GITHUB_OUTPUT
|
||||||
|
echo "Building version: $VERSION"
|
||||||
|
|
||||||
|
- name: Verify deno.json version matches tag
|
||||||
|
run: |
|
||||||
|
DENO_VERSION=$(grep -o '"version": "[^"]*"' deno.json | cut -d'"' -f4)
|
||||||
|
TAG_VERSION="${{ steps.version.outputs.version_number }}"
|
||||||
|
echo "deno.json version: $DENO_VERSION"
|
||||||
|
echo "Tag version: $TAG_VERSION"
|
||||||
|
if [ "$DENO_VERSION" != "$TAG_VERSION" ]; then
|
||||||
|
echo "ERROR: Version mismatch!"
|
||||||
|
echo "deno.json has version $DENO_VERSION but tag is $TAG_VERSION"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Compile binaries for all platforms
|
||||||
|
run: |
|
||||||
|
echo "================================================"
|
||||||
|
echo " MAILER Release Compilation"
|
||||||
|
echo " Version: ${{ steps.version.outputs.version }}"
|
||||||
|
echo "================================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Clean up old binaries and create fresh directory
|
||||||
|
rm -rf dist/binaries
|
||||||
|
mkdir -p dist/binaries
|
||||||
|
echo "→ Cleaned old binaries from dist/binaries"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Linux x86_64
|
||||||
|
echo "→ Compiling for Linux x86_64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output dist/binaries/mailer-linux-x64 \
|
||||||
|
--target x86_64-unknown-linux-gnu mod.ts
|
||||||
|
echo " ✓ Linux x86_64 complete"
|
||||||
|
|
||||||
|
# Linux ARM64
|
||||||
|
echo "→ Compiling for Linux ARM64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output dist/binaries/mailer-linux-arm64 \
|
||||||
|
--target aarch64-unknown-linux-gnu mod.ts
|
||||||
|
echo " ✓ Linux ARM64 complete"
|
||||||
|
|
||||||
|
# macOS x86_64
|
||||||
|
echo "→ Compiling for macOS x86_64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output dist/binaries/mailer-macos-x64 \
|
||||||
|
--target x86_64-apple-darwin mod.ts
|
||||||
|
echo " ✓ macOS x86_64 complete"
|
||||||
|
|
||||||
|
# macOS ARM64
|
||||||
|
echo "→ Compiling for macOS ARM64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output dist/binaries/mailer-macos-arm64 \
|
||||||
|
--target aarch64-apple-darwin mod.ts
|
||||||
|
echo " ✓ macOS ARM64 complete"
|
||||||
|
|
||||||
|
# Windows x86_64
|
||||||
|
echo "→ Compiling for Windows x86_64..."
|
||||||
|
deno compile --allow-all --no-check \
|
||||||
|
--output dist/binaries/mailer-windows-x64.exe \
|
||||||
|
--target x86_64-pc-windows-msvc mod.ts
|
||||||
|
echo " ✓ Windows x86_64 complete"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "All binaries compiled successfully!"
|
||||||
|
ls -lh dist/binaries/
|
||||||
|
|
||||||
|
- name: Generate SHA256 checksums
|
||||||
|
run: |
|
||||||
|
cd dist/binaries
|
||||||
|
sha256sum * > SHA256SUMS.txt
|
||||||
|
cat SHA256SUMS.txt
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
- name: Extract changelog for this version
|
||||||
|
id: changelog
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
|
||||||
|
# Check if CHANGELOG.md exists
|
||||||
|
if [ ! -f CHANGELOG.md ]; then
|
||||||
|
echo "No CHANGELOG.md found, using default release notes"
|
||||||
|
cat > /tmp/release_notes.md << EOF
|
||||||
|
## MAILER $VERSION
|
||||||
|
|
||||||
|
Pre-compiled binaries for multiple platforms.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Use the installation script:
|
||||||
|
\`\`\`bash
|
||||||
|
curl -sSL https://code.foss.global/serve.zone/mailer/raw/branch/main/install.sh | sudo bash
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Or download the binary for your platform and make it executable.
|
||||||
|
|
||||||
|
### Supported Platforms
|
||||||
|
- Linux x86_64 (x64)
|
||||||
|
- Linux ARM64 (aarch64)
|
||||||
|
- macOS x86_64 (Intel)
|
||||||
|
- macOS ARM64 (Apple Silicon)
|
||||||
|
- Windows x86_64
|
||||||
|
|
||||||
|
### Checksums
|
||||||
|
SHA256 checksums are provided in SHA256SUMS.txt
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
# Try to extract section for this version from CHANGELOG.md
|
||||||
|
# This is a simple extraction - adjust based on your CHANGELOG format
|
||||||
|
awk "/## \[$VERSION\]/,/## \[/" CHANGELOG.md | sed '$d' > /tmp/release_notes.md || cat > /tmp/release_notes.md << EOF
|
||||||
|
## MAILER $VERSION
|
||||||
|
|
||||||
|
See CHANGELOG.md for full details.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Use the installation script:
|
||||||
|
\`\`\`bash
|
||||||
|
curl -sSL https://code.foss.global/serve.zone/mailer/raw/branch/main/install.sh | sudo bash
|
||||||
|
\`\`\`
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Release notes:"
|
||||||
|
cat /tmp/release_notes.md
|
||||||
|
|
||||||
|
- name: Delete existing release if it exists
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
|
||||||
|
echo "Checking for existing release $VERSION..."
|
||||||
|
|
||||||
|
# Try to get existing release by tag
|
||||||
|
EXISTING_RELEASE_ID=$(curl -s \
|
||||||
|
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
"https://code.foss.global/api/v1/repos/serve.zone/mailer/releases/tags/$VERSION" \
|
||||||
|
| jq -r '.id // empty')
|
||||||
|
|
||||||
|
if [ -n "$EXISTING_RELEASE_ID" ]; then
|
||||||
|
echo "Found existing release (ID: $EXISTING_RELEASE_ID), deleting..."
|
||||||
|
curl -X DELETE -s \
|
||||||
|
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
"https://code.foss.global/api/v1/repos/serve.zone/mailer/releases/$EXISTING_RELEASE_ID"
|
||||||
|
echo "Existing release deleted"
|
||||||
|
sleep 2
|
||||||
|
else
|
||||||
|
echo "No existing release found, proceeding with creation"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create Gitea Release
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
|
RELEASE_NOTES=$(cat /tmp/release_notes.md)
|
||||||
|
|
||||||
|
# Create the release
|
||||||
|
echo "Creating release for $VERSION..."
|
||||||
|
RELEASE_ID=$(curl -X POST -s \
|
||||||
|
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"https://code.foss.global/api/v1/repos/serve.zone/mailer/releases" \
|
||||||
|
-d "{
|
||||||
|
\"tag_name\": \"$VERSION\",
|
||||||
|
\"name\": \"MAILER $VERSION\",
|
||||||
|
\"body\": $(jq -Rs . /tmp/release_notes.md),
|
||||||
|
\"draft\": false,
|
||||||
|
\"prerelease\": false
|
||||||
|
}" | jq -r '.id')
|
||||||
|
|
||||||
|
echo "Release created with ID: $RELEASE_ID"
|
||||||
|
|
||||||
|
# Upload binaries as release assets
|
||||||
|
for binary in dist/binaries/*; do
|
||||||
|
filename=$(basename "$binary")
|
||||||
|
echo "Uploading $filename..."
|
||||||
|
curl -X POST -s \
|
||||||
|
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary "@$binary" \
|
||||||
|
"https://code.foss.global/api/v1/repos/serve.zone/mailer/releases/$RELEASE_ID/assets?name=$filename"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "All assets uploaded successfully"
|
||||||
|
|
||||||
|
- name: Clean up old releases
|
||||||
|
run: |
|
||||||
|
echo "Cleaning up old releases (keeping only last 3)..."
|
||||||
|
|
||||||
|
# Fetch all releases sorted by creation date
|
||||||
|
RELEASES=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
"https://code.foss.global/api/v1/repos/serve.zone/mailer/releases" | \
|
||||||
|
jq -r 'sort_by(.created_at) | reverse | .[3:] | .[].id')
|
||||||
|
|
||||||
|
# Delete old releases
|
||||||
|
if [ -n "$RELEASES" ]; then
|
||||||
|
echo "Found releases to delete:"
|
||||||
|
for release_id in $RELEASES; do
|
||||||
|
echo " Deleting release ID: $release_id"
|
||||||
|
curl -X DELETE -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||||
|
"https://code.foss.global/api/v1/repos/serve.zone/mailer/releases/$release_id"
|
||||||
|
done
|
||||||
|
echo "Old releases deleted successfully"
|
||||||
|
else
|
||||||
|
echo "No old releases to delete (less than 4 releases total)"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
- name: Release Summary
|
||||||
|
run: |
|
||||||
|
echo "================================================"
|
||||||
|
echo " Release ${{ steps.version.outputs.version }} Complete!"
|
||||||
|
echo "================================================"
|
||||||
|
echo ""
|
||||||
|
echo "Binaries published:"
|
||||||
|
ls -lh dist/binaries/
|
||||||
|
echo ""
|
||||||
|
echo "Release URL:"
|
||||||
|
echo "https://code.foss.global/serve.zone/mailer/releases/tag/${{ steps.version.outputs.version }}"
|
||||||
|
echo ""
|
||||||
|
echo "Installation command:"
|
||||||
|
echo "curl -sSL https://code.foss.global/serve.zone/mailer/raw/branch/main/install.sh | sudo bash"
|
||||||
|
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();
|
|
||||||
66
changelog.md
66
changelog.md
@@ -1,5 +1,71 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
Add smartmail, mailauth and uuid to Deno dependencies and export them from plugins; include local dev permissions file
|
||||||
|
|
||||||
|
- Add @push.rocks/smartmail, mailauth and uuid to deno.json dependencies so email parsing, DKIM and UUID utilities are available.
|
||||||
|
- Export smartmail, mailauth and uuid from ts/plugins.ts for centralized access across the codebase.
|
||||||
|
- Add .claude/settings.local.json to define local development permissions (tooling/dev environment configuration).
|
||||||
|
|
||||||
|
## 2025-10-24 - 1.1.0 - feat(ci)
|
||||||
|
Add CI, release and npm-publish automation; introduce release template and local settings
|
||||||
|
|
||||||
|
- Add CI workflow (.gitea/workflows/ci.yml) to run TypeScript checks, linting, formatting and platform compilation tests
|
||||||
|
- Add release workflow (.gitea/workflows/release.yml) to compile binaries for multiple platforms, generate checksums, create/update Gitea releases and upload assets
|
||||||
|
- Add npm publish workflow (.gitea/workflows/npm-publish.yml) to build package from a tag and publish precompiled binaries to npm (tag-triggered)
|
||||||
|
- Add a Gitea release template (.gitea/release-template.md) describing platforms, checksums and installation options
|
||||||
|
- Add local tool permission settings (.claude/settings.local.json) used by local tooling
|
||||||
|
- No API or runtime source changes — this commit is focused on CI/CD, release automation and packaging
|
||||||
|
|
||||||
## 2025-10-24 - 1.0.1 - fix(dev)
|
## 2025-10-24 - 1.0.1 - fix(dev)
|
||||||
Add local development settings file to grant tooling permissions
|
Add local development settings file to grant tooling permissions
|
||||||
|
|
||||||
|
|||||||
47
deno.json
47
deno.json
@@ -1,47 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@serve.zone/mailer",
|
|
||||||
"version": "1.0.1",
|
|
||||||
"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",
|
|
||||||
"mailaddress-validator": "npm:mailaddress-validator@^1.0.11"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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.0.0',
|
||||||
|
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=
|
||||||
200
dist_ts/mail/core/classes.bouncemanager.d.ts
vendored
Normal file
200
dist_ts/mail/core/classes.bouncemanager.d.ts
vendored
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
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;
|
||||||
|
/**
|
||||||
|
* Analyze SMTP response and diagnostic codes to determine bounce type
|
||||||
|
* @param smtpResponse SMTP response string
|
||||||
|
* @param diagnosticCode Diagnostic code from bounce
|
||||||
|
* @param statusCode Status code from bounce
|
||||||
|
* @returns Detected bounce type and category
|
||||||
|
*/
|
||||||
|
private detectBounceType;
|
||||||
|
/**
|
||||||
|
* Check if text matches any pattern for a bounce type
|
||||||
|
* @param text Text to check against patterns
|
||||||
|
* @param bounceType Bounce type to get patterns for
|
||||||
|
* @returns Whether the text matches any pattern
|
||||||
|
*/
|
||||||
|
private matchesPattern;
|
||||||
|
/**
|
||||||
|
* 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 {};
|
||||||
781
dist_ts/mail/core/classes.bouncemanager.js
Normal file
781
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;
|
||||||
|
}
|
||||||
855
dist_ts/mail/delivery/classes.delivery.system.js
Normal file
855
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 {};
|
||||||
36
dist_ts/mail/delivery/classes.emailsignjob.js
Normal file
36
dist_ts/mail/delivery/classes.emailsignjob.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import * as plugins from '../../plugins.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 signResult = await plugins.dkimSign(emailMessage, {
|
||||||
|
signingDomain: this.jobOptions.domain,
|
||||||
|
selector: this.jobOptions.selector,
|
||||||
|
privateKey,
|
||||||
|
canonicalization: 'relaxed/relaxed',
|
||||||
|
algorithm: 'rsa-sha256',
|
||||||
|
signTime: new Date(),
|
||||||
|
signatureData: [
|
||||||
|
{
|
||||||
|
signingDomain: this.jobOptions.domain,
|
||||||
|
selector: this.jobOptions.selector,
|
||||||
|
privateKey,
|
||||||
|
algorithm: 'rsa-sha256',
|
||||||
|
canonicalization: 'relaxed/relaxed',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const signature = signResult.signatures;
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lbWFpbHNpZ25qb2IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L2NsYXNzZXMuZW1haWxzaWduam9iLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFjNUMsTUFBTSxPQUFPLFlBQVk7SUFDdkIsY0FBYyxDQUFxQjtJQUNuQyxVQUFVLENBQXVCO0lBRWpDLFlBQVksY0FBa0MsRUFBRSxPQUE2QjtRQUMzRSxJQUFJLENBQUMsY0FBYyxHQUFHLGNBQWMsQ0FBQztRQUNyQyxJQUFJLENBQUMsVUFBVSxHQUFHLE9BQU8sQ0FBQztJQUM1QixDQUFDO0lBRUQsS0FBSyxDQUFDLGNBQWM7UUFDbEIsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLFdBQVcsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMzRixPQUFPLE9BQU8sQ0FBQyxVQUFVLENBQUM7SUFDNUIsQ0FBQztJQUVNLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxZQUFvQjtRQUNsRCxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUMvQyxNQUFNLFVBQVUsR0FBRyxNQUFNLE9BQU8sQ0FBQyxRQUFRLENBQUMsWUFBWSxFQUFFO1lBQ3RELGFBQWEsRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU07WUFDckMsUUFBUSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUTtZQUNsQyxVQUFVO1lBQ1YsZ0JBQWdCLEVBQUUsaUJBQWlCO1lBQ25DLFNBQVMsRUFBRSxZQUFZO1lBQ3ZCLFFBQVEsRUFBRSxJQUFJLElBQUksRUFBRTtZQUNwQixhQUFhLEVBQUU7Z0JBQ2I7b0JBQ0UsYUFBYSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTTtvQkFDckMsUUFBUSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUTtvQkFDbEMsVUFBVTtvQkFDVixTQUFTLEVBQUUsWUFBWTtvQkFDdkIsZ0JBQWdCLEVBQUUsaUJBQWlCO2lCQUNwQzthQUNGO1NBQ0YsQ0FBQyxDQUFDO1FBQ0gsTUFBTSxTQUFTLEdBQUcsVUFBVSxDQUFDLFVBQVUsQ0FBQztRQUN4QyxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0NBQ0YifQ==
|
||||||
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;
|
||||||
|
}
|
||||||
986
dist_ts/mail/delivery/classes.smtp.client.legacy.js
Normal file
986
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
12
dist_ts/mail/delivery/index.d.ts
vendored
Normal file
12
dist_ts/mail/delivery/index.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
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';
|
||||||
|
import * as smtpServerMod from './smtpserver/index.js';
|
||||||
|
export { smtpClientMod, smtpServerMod };
|
||||||
18
dist_ts/mail/delivery/index.js
Normal file
18
dist_ts/mail/delivery/index.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// 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';
|
||||||
|
import * as smtpServerMod from './smtpserver/index.js';
|
||||||
|
export { smtpClientMod, smtpServerMod };
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDRCQUE0QjtBQUM1QixjQUFjLDJCQUEyQixDQUFDO0FBQzFDLGNBQWMsNkJBQTZCLENBQUM7QUFDNUMsY0FBYyw4QkFBOEIsQ0FBQztBQUU3Qyx1Q0FBdUM7QUFDdkMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3pELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUU5RCw2Q0FBNkM7QUFDN0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBR3ZELHVCQUF1QjtBQUN2QixjQUFjLG1DQUFtQyxDQUFDO0FBRWxELGdDQUFnQztBQUNoQyxjQUFjLHlCQUF5QixDQUFDO0FBRXhDLGtFQUFrRTtBQUNsRSxPQUFPLEtBQUssYUFBYSxNQUFNLHVCQUF1QixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxhQUFhLE1BQU0sdUJBQXVCLENBQUM7QUFFdkQsT0FBTyxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQUUsQ0FBQyJ9
|
||||||
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHMvbWFpbC9kZWxpdmVyeS9zbXRwY2xpZW50L2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSDs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRztJQUN4Qiw4QkFBOEI7SUFDOUIsYUFBYSxFQUFFLEdBQUc7SUFDbEIsZUFBZSxFQUFFLEdBQUc7SUFDcEIseUJBQXlCLEVBQUUsR0FBRztJQUM5QixtQkFBbUIsRUFBRSxHQUFHO0lBQ3hCLGNBQWMsRUFBRSxHQUFHO0lBQ25CLGtCQUFrQixFQUFFLEdBQUc7SUFFdkIsZ0NBQWdDO0lBQ2hDLGdCQUFnQixFQUFFLEdBQUc7SUFFckIsd0NBQXdDO0lBQ3hDLHFCQUFxQixFQUFFLEdBQUc7SUFDMUIsWUFBWSxFQUFFLEdBQUc7SUFDakIsV0FBVyxFQUFFLEdBQUc7SUFDaEIsb0JBQW9CLEVBQUUsR0FBRztJQUN6QixxQkFBcUIsRUFBRSxHQUFHO0lBRTFCLHdDQUF3QztJQUN4QyxZQUFZLEVBQUUsR0FBRztJQUNqQix1QkFBdUIsRUFBRSxHQUFHO0lBQzVCLHVCQUF1QixFQUFFLEdBQUc7SUFDNUIsWUFBWSxFQUFFLEdBQUc7SUFDakIseUJBQXlCLEVBQUUsR0FBRztJQUM5QixtQkFBbUIsRUFBRSxHQUFHO0lBQ3hCLDBCQUEwQixFQUFFLEdBQUc7SUFDL0IsZ0JBQWdCLEVBQUUsR0FBRztJQUNyQix3QkFBd0IsRUFBRSxHQUFHO0lBQzdCLGtCQUFrQixFQUFFLEdBQUc7Q0FDZixDQUFDO0FBRVg7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUc7SUFDM0IsSUFBSSxFQUFFLE1BQU07SUFDWixJQUFJLEVBQUUsTUFBTTtJQUNaLFNBQVMsRUFBRSxXQUFXO0lBQ3RCLE9BQU8sRUFBRSxTQUFTO0lBQ2xCLElBQUksRUFBRSxNQUFNO0lBQ1osSUFBSSxFQUFFLE1BQU07SUFDWixJQUFJLEVBQUUsTUFBTTtJQUNaLElBQUksRUFBRSxNQUFNO0lBQ1osUUFBUSxFQUFFLFVBQVU7SUFDcEIsSUFBSSxFQUFFLE1BQU07Q0FDSixDQUFDO0FBRVg7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxZQUFZLEdBQUc7SUFDMUIsS0FBSyxFQUFFLE9BQU87SUFDZCxLQUFLLEVBQUUsT0FBTztJQUNkLE1BQU0sRUFBRSxTQUFTO0lBQ2pCLFFBQVEsRUFBRSxVQUFVO0NBQ1osQ0FBQztBQUVYOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUFHO0lBQzdCLFVBQVUsRUFBRSxZQUFZO0lBQ3hCLElBQUksRUFBRSxNQUFNO0lBQ1osUUFBUSxFQUFFLFVBQVU7SUFDcEIsSUFBSSxFQUFFLE1BQU07SUFDWixjQUFjLEVBQUUsVUFBVTtJQUMxQixRQUFRLEVBQUUsVUFBVTtJQUNwQixxQkFBcUIsRUFBRSxxQkFBcUI7SUFDNUMsR0FBRyxFQUFFLEtBQUs7Q0FDRixDQUFDO0FBRVg7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxRQUFRLEdBQUc7SUFDdEIsa0JBQWtCLEVBQUUsS0FBSyxFQUFFLGFBQWE7SUFDeEMsY0FBYyxFQUFFLE1BQU0sRUFBSyxZQUFZO0lBQ3ZDLGVBQWUsRUFBRSxLQUFLLEVBQUssYUFBYTtJQUN4QyxlQUFlLEVBQUUsQ0FBQztJQUNsQixZQUFZLEVBQUUsR0FBRztJQUNqQixTQUFTLEVBQUUsRUFBRTtJQUNiLGVBQWUsRUFBRSxHQUFHO0lBQ3BCLFVBQVUsRUFBRSxHQUFHO0lBQ2YsY0FBYyxFQUFFLENBQUM7SUFDakIsV0FBVyxFQUFFLElBQUk7SUFDakIsaUJBQWlCLEVBQUUsS0FBSyxDQUFHLGFBQWE7Q0FDaEMsQ0FBQztBQUVYOztHQUVHO0FBQ0gsTUFBTSxDQUFOLElBQVksYUFVWDtBQVZELFdBQVksYUFBYTtJQUN2QixzREFBcUMsQ0FBQTtJQUNyQyw4REFBNkMsQ0FBQTtJQUM3QyxrREFBaUMsQ0FBQTtJQUNqQyxnREFBK0IsQ0FBQTtJQUMvQix3Q0FBdUIsQ0FBQTtJQUN2Qiw4Q0FBNkIsQ0FBQTtJQUM3QixnREFBK0IsQ0FBQTtJQUMvQiw0Q0FBMkIsQ0FBQTtJQUMzQixnREFBK0IsQ0FBQTtBQUNqQyxDQUFDLEVBVlcsYUFBYSxLQUFiLGFBQWEsUUFVeEI7QUFFRDs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRztJQUM1QixhQUFhLEVBQUUsNEJBQTRCO0lBQzNDLGFBQWEsRUFBRSxvQkFBb0I7SUFDbkMsZUFBZSxFQUFFLGlCQUFpQjtJQUNsQyxpQkFBaUIsRUFBRSxjQUFjO0lBQ2pDLGNBQWMsRUFBRSxlQUFlO0NBQ3ZCLENBQUM7QUFFWDs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLFlBQVksR0FBRztJQUMxQixJQUFJLEVBQUUsTUFBTTtJQUNaLEVBQUUsRUFBRSxJQUFJO0lBQ1IsRUFBRSxFQUFFLElBQUk7Q0FDQSxDQUFDO0FBRVg7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRztJQUMvQixZQUFZLEVBQUUsY0FBYztJQUM1QixVQUFVLEVBQUUsWUFBWTtJQUN4QixTQUFTLEVBQUUsV0FBVztJQUN0QixhQUFhLEVBQUUsZUFBZTtJQUM5QixLQUFLLEVBQUUsT0FBTztJQUNkLElBQUksRUFBRSxNQUFNO0lBQ1osT0FBTyxFQUFFLFNBQVM7SUFDbEIsS0FBSyxFQUFFLE9BQU87Q0FDTixDQUFDIn0=
|
||||||
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
45
dist_ts/mail/delivery/smtpserver/certificate-utils.d.ts
vendored
Normal file
45
dist_ts/mail/delivery/smtpserver/certificate-utils.d.ts
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Certificate Utilities for SMTP Server
|
||||||
|
* Provides utilities for managing TLS certificates
|
||||||
|
*/
|
||||||
|
import * as tls from 'tls';
|
||||||
|
/**
|
||||||
|
* Certificate data
|
||||||
|
*/
|
||||||
|
export interface ICertificateData {
|
||||||
|
key: Buffer;
|
||||||
|
cert: Buffer;
|
||||||
|
ca?: Buffer;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Load certificates from PEM format strings
|
||||||
|
* @param options - Certificate options
|
||||||
|
* @returns Certificate data with Buffer format
|
||||||
|
*/
|
||||||
|
export declare function loadCertificatesFromString(options: {
|
||||||
|
key: string | Buffer;
|
||||||
|
cert: string | Buffer;
|
||||||
|
ca?: string | Buffer;
|
||||||
|
}): ICertificateData;
|
||||||
|
/**
|
||||||
|
* Load certificates from files
|
||||||
|
* @param options - Certificate file paths
|
||||||
|
* @returns Certificate data with Buffer format
|
||||||
|
*/
|
||||||
|
export declare function loadCertificatesFromFiles(options: {
|
||||||
|
keyPath: string;
|
||||||
|
certPath: string;
|
||||||
|
caPath?: string;
|
||||||
|
}): ICertificateData;
|
||||||
|
/**
|
||||||
|
* Generate self-signed certificates for testing
|
||||||
|
* @returns Certificate data with Buffer format
|
||||||
|
*/
|
||||||
|
export declare function generateSelfSignedCertificates(): ICertificateData;
|
||||||
|
/**
|
||||||
|
* Create TLS options for secure server or STARTTLS
|
||||||
|
* @param certificates - Certificate data
|
||||||
|
* @param isServer - Whether this is for server (true) or client (false)
|
||||||
|
* @returns TLS options
|
||||||
|
*/
|
||||||
|
export declare function createTlsOptions(certificates: ICertificateData, isServer?: boolean): tls.TlsOptions;
|
||||||
345
dist_ts/mail/delivery/smtpserver/certificate-utils.js
Normal file
345
dist_ts/mail/delivery/smtpserver/certificate-utils.js
Normal file
File diff suppressed because one or more lines are too long
156
dist_ts/mail/delivery/smtpserver/command-handler.d.ts
vendored
Normal file
156
dist_ts/mail/delivery/smtpserver/command-handler.d.ts
vendored
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Command Handler
|
||||||
|
* Responsible for parsing and handling SMTP commands
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../plugins.js';
|
||||||
|
import type { ISmtpSession } from './interfaces.js';
|
||||||
|
import type { ICommandHandler, ISmtpServer } from './interfaces.js';
|
||||||
|
import { SmtpCommand } from './constants.js';
|
||||||
|
/**
|
||||||
|
* Handles SMTP commands and responses
|
||||||
|
*/
|
||||||
|
export declare class CommandHandler implements ICommandHandler {
|
||||||
|
/**
|
||||||
|
* Reference to the SMTP server instance
|
||||||
|
*/
|
||||||
|
private smtpServer;
|
||||||
|
/**
|
||||||
|
* Creates a new command handler
|
||||||
|
* @param smtpServer - SMTP server instance
|
||||||
|
*/
|
||||||
|
constructor(smtpServer: ISmtpServer);
|
||||||
|
/**
|
||||||
|
* Process a command from the client
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param commandLine - Command line from client
|
||||||
|
*/
|
||||||
|
processCommand(socket: plugins.net.Socket | plugins.tls.TLSSocket, commandLine: string): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Send a response to the client
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param response - Response to send
|
||||||
|
*/
|
||||||
|
sendResponse(socket: plugins.net.Socket | plugins.tls.TLSSocket, response: string): void;
|
||||||
|
/**
|
||||||
|
* Check if a socket error is potentially recoverable
|
||||||
|
* @param error - The error that occurred
|
||||||
|
* @returns Whether the error is potentially recoverable
|
||||||
|
*/
|
||||||
|
private isRecoverableSocketError;
|
||||||
|
/**
|
||||||
|
* Handle recoverable socket errors with retry logic
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param error - The error that occurred
|
||||||
|
* @param response - The response that failed to send
|
||||||
|
*/
|
||||||
|
private handleSocketError;
|
||||||
|
/**
|
||||||
|
* Handle EHLO command
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param clientHostname - Client hostname from EHLO command
|
||||||
|
*/
|
||||||
|
handleEhlo(socket: plugins.net.Socket | plugins.tls.TLSSocket, clientHostname: string): void;
|
||||||
|
/**
|
||||||
|
* Handle MAIL FROM command
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param args - Command arguments
|
||||||
|
*/
|
||||||
|
handleMailFrom(socket: plugins.net.Socket | plugins.tls.TLSSocket, args: string): void;
|
||||||
|
/**
|
||||||
|
* Handle RCPT TO command
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param args - Command arguments
|
||||||
|
*/
|
||||||
|
handleRcptTo(socket: plugins.net.Socket | plugins.tls.TLSSocket, args: string): void;
|
||||||
|
/**
|
||||||
|
* Handle DATA command
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
handleData(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||||
|
/**
|
||||||
|
* Handle RSET command
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
handleRset(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||||
|
/**
|
||||||
|
* Handle NOOP command
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
handleNoop(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||||
|
/**
|
||||||
|
* Handle QUIT command
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
handleQuit(socket: plugins.net.Socket | plugins.tls.TLSSocket, args?: string): void;
|
||||||
|
/**
|
||||||
|
* Handle AUTH command
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param args - Command arguments
|
||||||
|
*/
|
||||||
|
private handleAuth;
|
||||||
|
/**
|
||||||
|
* Handle AUTH PLAIN authentication
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param session - Session
|
||||||
|
* @param initialResponse - Optional initial response
|
||||||
|
*/
|
||||||
|
private handleAuthPlain;
|
||||||
|
/**
|
||||||
|
* Handle AUTH LOGIN authentication
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param session - Session
|
||||||
|
* @param initialResponse - Optional initial response
|
||||||
|
*/
|
||||||
|
private handleAuthLogin;
|
||||||
|
/**
|
||||||
|
* Handle AUTH LOGIN response
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param session - Session
|
||||||
|
* @param response - Response from client
|
||||||
|
*/
|
||||||
|
private handleAuthLoginResponse;
|
||||||
|
/**
|
||||||
|
* Handle HELP command
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param args - Command arguments
|
||||||
|
*/
|
||||||
|
private handleHelp;
|
||||||
|
/**
|
||||||
|
* Handle VRFY command (Verify user/mailbox)
|
||||||
|
* RFC 5321 Section 3.5.1: Server MAY respond with 252 to avoid disclosing sensitive information
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param args - Command arguments (username to verify)
|
||||||
|
*/
|
||||||
|
private handleVrfy;
|
||||||
|
/**
|
||||||
|
* Handle EXPN command (Expand mailing list)
|
||||||
|
* RFC 5321 Section 3.5.2: Server MAY disable this for security
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param args - Command arguments (mailing list to expand)
|
||||||
|
*/
|
||||||
|
private handleExpn;
|
||||||
|
/**
|
||||||
|
* Reset session to after-EHLO state
|
||||||
|
* @param session - SMTP session to reset
|
||||||
|
*/
|
||||||
|
private resetSession;
|
||||||
|
/**
|
||||||
|
* Validate command sequence based on current state
|
||||||
|
* @param command - Command to validate
|
||||||
|
* @param session - Current session
|
||||||
|
* @returns Whether the command is valid in the current state
|
||||||
|
*/
|
||||||
|
private validateCommandSequence;
|
||||||
|
/**
|
||||||
|
* Handle an SMTP command (interface requirement)
|
||||||
|
*/
|
||||||
|
handleCommand(socket: plugins.net.Socket | plugins.tls.TLSSocket, command: SmtpCommand, args: string, session: ISmtpSession): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Get supported commands for current session state (interface requirement)
|
||||||
|
*/
|
||||||
|
getSupportedCommands(session: ISmtpSession): SmtpCommand[];
|
||||||
|
/**
|
||||||
|
* Clean up resources
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
1163
dist_ts/mail/delivery/smtpserver/command-handler.js
Normal file
1163
dist_ts/mail/delivery/smtpserver/command-handler.js
Normal file
File diff suppressed because one or more lines are too long
159
dist_ts/mail/delivery/smtpserver/connection-manager.d.ts
vendored
Normal file
159
dist_ts/mail/delivery/smtpserver/connection-manager.d.ts
vendored
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Connection Manager
|
||||||
|
* Responsible for managing socket connections to the SMTP server
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../plugins.js';
|
||||||
|
import type { IConnectionManager, ISmtpServer } from './interfaces.js';
|
||||||
|
/**
|
||||||
|
* Manager for SMTP connections
|
||||||
|
* Handles connection setup, event listeners, and lifecycle management
|
||||||
|
* Provides resource management, connection tracking, and monitoring
|
||||||
|
*/
|
||||||
|
export declare class ConnectionManager implements IConnectionManager {
|
||||||
|
/**
|
||||||
|
* Reference to the SMTP server instance
|
||||||
|
*/
|
||||||
|
private smtpServer;
|
||||||
|
/**
|
||||||
|
* Set of active socket connections
|
||||||
|
*/
|
||||||
|
private activeConnections;
|
||||||
|
/**
|
||||||
|
* Connection tracking for resource management
|
||||||
|
*/
|
||||||
|
private connectionStats;
|
||||||
|
/**
|
||||||
|
* Per-IP connection tracking for rate limiting
|
||||||
|
*/
|
||||||
|
private ipConnections;
|
||||||
|
/**
|
||||||
|
* Resource monitoring interval
|
||||||
|
*/
|
||||||
|
private resourceCheckInterval;
|
||||||
|
/**
|
||||||
|
* Track cleanup timers so we can clear them
|
||||||
|
*/
|
||||||
|
private cleanupTimers;
|
||||||
|
/**
|
||||||
|
* SMTP server options with enhanced resource controls
|
||||||
|
*/
|
||||||
|
private options;
|
||||||
|
/**
|
||||||
|
* Creates a new connection manager with enhanced resource management
|
||||||
|
* @param smtpServer - SMTP server instance
|
||||||
|
*/
|
||||||
|
constructor(smtpServer: ISmtpServer);
|
||||||
|
/**
|
||||||
|
* Start resource monitoring interval to check resource usage
|
||||||
|
*/
|
||||||
|
private startResourceMonitoring;
|
||||||
|
/**
|
||||||
|
* Monitor resource usage and log statistics
|
||||||
|
*/
|
||||||
|
private monitorResourceUsage;
|
||||||
|
/**
|
||||||
|
* Clean up expired IP rate limits and perform additional resource monitoring
|
||||||
|
*/
|
||||||
|
private cleanupIpRateLimits;
|
||||||
|
/**
|
||||||
|
* Validate and repair resource tracking to prevent leaks
|
||||||
|
*/
|
||||||
|
private validateResourceTracking;
|
||||||
|
/**
|
||||||
|
* Handle a new connection with resource management
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
handleNewConnection(socket: plugins.net.Socket): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Check if an IP has exceeded the rate limit
|
||||||
|
* @param ip - Client IP address
|
||||||
|
* @returns True if rate limited
|
||||||
|
*/
|
||||||
|
private isIPRateLimited;
|
||||||
|
/**
|
||||||
|
* Track a new connection from an IP
|
||||||
|
* @param ip - Client IP address
|
||||||
|
*/
|
||||||
|
private trackIPConnection;
|
||||||
|
/**
|
||||||
|
* Check if an IP has reached its connection limit
|
||||||
|
* @param ip - Client IP address
|
||||||
|
* @returns True if limit reached
|
||||||
|
*/
|
||||||
|
private hasReachedIPConnectionLimit;
|
||||||
|
/**
|
||||||
|
* Handle a new secure TLS connection with resource management
|
||||||
|
* @param socket - Client TLS socket
|
||||||
|
*/
|
||||||
|
handleNewSecureConnection(socket: plugins.tls.TLSSocket): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Set up event handlers for a socket with enhanced resource management
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
setupSocketEventHandlers(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||||
|
/**
|
||||||
|
* Get the current connection count
|
||||||
|
* @returns Number of active connections
|
||||||
|
*/
|
||||||
|
getConnectionCount(): number;
|
||||||
|
/**
|
||||||
|
* Check if the server has reached the maximum number of connections
|
||||||
|
* @returns True if max connections reached
|
||||||
|
*/
|
||||||
|
hasReachedMaxConnections(): boolean;
|
||||||
|
/**
|
||||||
|
* Close all active connections
|
||||||
|
*/
|
||||||
|
closeAllConnections(): void;
|
||||||
|
/**
|
||||||
|
* Handle socket close event
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param hadError - Whether the socket was closed due to error
|
||||||
|
*/
|
||||||
|
private handleSocketClose;
|
||||||
|
/**
|
||||||
|
* Handle socket error event
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param error - Error object
|
||||||
|
*/
|
||||||
|
private handleSocketError;
|
||||||
|
/**
|
||||||
|
* Handle socket timeout event
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
private handleSocketTimeout;
|
||||||
|
/**
|
||||||
|
* Reject a connection
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param reason - Reason for rejection
|
||||||
|
*/
|
||||||
|
private rejectConnection;
|
||||||
|
/**
|
||||||
|
* Send greeting message
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
private sendGreeting;
|
||||||
|
/**
|
||||||
|
* Send service closing notification
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
private sendServiceClosing;
|
||||||
|
/**
|
||||||
|
* Send response to client
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param response - Response to send
|
||||||
|
*/
|
||||||
|
private sendResponse;
|
||||||
|
/**
|
||||||
|
* Handle a new connection (interface requirement)
|
||||||
|
*/
|
||||||
|
handleConnection(socket: plugins.net.Socket | plugins.tls.TLSSocket, secure: boolean): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Check if accepting new connections (interface requirement)
|
||||||
|
*/
|
||||||
|
canAcceptConnection(): boolean;
|
||||||
|
/**
|
||||||
|
* Clean up resources
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
918
dist_ts/mail/delivery/smtpserver/connection-manager.js
Normal file
918
dist_ts/mail/delivery/smtpserver/connection-manager.js
Normal file
File diff suppressed because one or more lines are too long
130
dist_ts/mail/delivery/smtpserver/constants.d.ts
vendored
Normal file
130
dist_ts/mail/delivery/smtpserver/constants.d.ts
vendored
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Server Constants
|
||||||
|
* This file contains all constants and enums used by the SMTP server
|
||||||
|
*/
|
||||||
|
import { SmtpState } from '../interfaces.js';
|
||||||
|
export { SmtpState };
|
||||||
|
/**
|
||||||
|
* SMTP Response Codes
|
||||||
|
* Based on RFC 5321 and common SMTP practice
|
||||||
|
*/
|
||||||
|
export declare enum SmtpResponseCode {
|
||||||
|
SUCCESS = 250,// Requested mail action okay, completed
|
||||||
|
SYSTEM_STATUS = 211,// System status, or system help reply
|
||||||
|
HELP_MESSAGE = 214,// Help message
|
||||||
|
SERVICE_READY = 220,// <domain> Service ready
|
||||||
|
SERVICE_CLOSING = 221,// <domain> Service closing transmission channel
|
||||||
|
AUTHENTICATION_SUCCESSFUL = 235,// Authentication successful
|
||||||
|
OK = 250,// Requested mail action okay, completed
|
||||||
|
FORWARD = 251,// User not local; will forward to <forward-path>
|
||||||
|
CANNOT_VRFY = 252,// Cannot VRFY user, but will accept message and attempt delivery
|
||||||
|
MORE_INFO_NEEDED = 334,// Server challenge for authentication
|
||||||
|
START_MAIL_INPUT = 354,// Start mail input; end with <CRLF>.<CRLF>
|
||||||
|
SERVICE_NOT_AVAILABLE = 421,// <domain> Service not available, closing transmission channel
|
||||||
|
MAILBOX_TEMPORARILY_UNAVAILABLE = 450,// Requested mail action not taken: mailbox unavailable
|
||||||
|
LOCAL_ERROR = 451,// Requested action aborted: local error in processing
|
||||||
|
INSUFFICIENT_STORAGE = 452,// Requested action not taken: insufficient system storage
|
||||||
|
TLS_UNAVAILABLE_TEMP = 454,// TLS not available due to temporary reason
|
||||||
|
SYNTAX_ERROR = 500,// Syntax error, command unrecognized
|
||||||
|
SYNTAX_ERROR_PARAMETERS = 501,// Syntax error in parameters or arguments
|
||||||
|
COMMAND_NOT_IMPLEMENTED = 502,// Command not implemented
|
||||||
|
BAD_SEQUENCE = 503,// Bad sequence of commands
|
||||||
|
COMMAND_PARAMETER_NOT_IMPLEMENTED = 504,// Command parameter not implemented
|
||||||
|
AUTH_REQUIRED = 530,// Authentication required
|
||||||
|
AUTH_FAILED = 535,// Authentication credentials invalid
|
||||||
|
MAILBOX_UNAVAILABLE = 550,// Requested action not taken: mailbox unavailable
|
||||||
|
USER_NOT_LOCAL = 551,// User not local; please try <forward-path>
|
||||||
|
EXCEEDED_STORAGE = 552,// Requested mail action aborted: exceeded storage allocation
|
||||||
|
MAILBOX_NAME_INVALID = 553,// Requested action not taken: mailbox name not allowed
|
||||||
|
TRANSACTION_FAILED = 554,// Transaction failed
|
||||||
|
MAIL_RCPT_PARAMETERS_INVALID = 555
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP Command Types
|
||||||
|
*/
|
||||||
|
export declare enum SmtpCommand {
|
||||||
|
HELO = "HELO",
|
||||||
|
EHLO = "EHLO",
|
||||||
|
MAIL_FROM = "MAIL",
|
||||||
|
RCPT_TO = "RCPT",
|
||||||
|
DATA = "DATA",
|
||||||
|
RSET = "RSET",
|
||||||
|
NOOP = "NOOP",
|
||||||
|
QUIT = "QUIT",
|
||||||
|
STARTTLS = "STARTTLS",
|
||||||
|
AUTH = "AUTH",
|
||||||
|
HELP = "HELP",
|
||||||
|
VRFY = "VRFY",
|
||||||
|
EXPN = "EXPN"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Security log event types
|
||||||
|
*/
|
||||||
|
export declare enum SecurityEventType {
|
||||||
|
CONNECTION = "connection",
|
||||||
|
AUTHENTICATION = "authentication",
|
||||||
|
COMMAND = "command",
|
||||||
|
DATA = "data",
|
||||||
|
IP_REPUTATION = "ip_reputation",
|
||||||
|
TLS_NEGOTIATION = "tls_negotiation",
|
||||||
|
DKIM = "dkim",
|
||||||
|
SPF = "spf",
|
||||||
|
DMARC = "dmarc",
|
||||||
|
EMAIL_VALIDATION = "email_validation",
|
||||||
|
SPAM = "spam",
|
||||||
|
ACCESS_CONTROL = "access_control"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Security log levels
|
||||||
|
*/
|
||||||
|
export declare enum SecurityLogLevel {
|
||||||
|
DEBUG = "debug",
|
||||||
|
INFO = "info",
|
||||||
|
WARN = "warn",
|
||||||
|
ERROR = "error"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP Server Defaults
|
||||||
|
*/
|
||||||
|
export declare const SMTP_DEFAULTS: {
|
||||||
|
CONNECTION_TIMEOUT: number;
|
||||||
|
SOCKET_TIMEOUT: number;
|
||||||
|
DATA_TIMEOUT: number;
|
||||||
|
CLEANUP_INTERVAL: number;
|
||||||
|
MAX_CONNECTIONS: number;
|
||||||
|
MAX_RECIPIENTS: number;
|
||||||
|
MAX_MESSAGE_SIZE: number;
|
||||||
|
SMTP_PORT: number;
|
||||||
|
SUBMISSION_PORT: number;
|
||||||
|
SECURE_PORT: number;
|
||||||
|
HOSTNAME: string;
|
||||||
|
CRLF: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* SMTP Command Patterns
|
||||||
|
* Regular expressions for parsing SMTP commands
|
||||||
|
*/
|
||||||
|
export declare const SMTP_PATTERNS: {
|
||||||
|
EHLO: RegExp;
|
||||||
|
MAIL_FROM: RegExp;
|
||||||
|
RCPT_TO: RegExp;
|
||||||
|
PARAM: RegExp;
|
||||||
|
EMAIL: RegExp;
|
||||||
|
END_DATA: RegExp;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* SMTP Extension List
|
||||||
|
* These extensions are advertised in the EHLO response
|
||||||
|
*/
|
||||||
|
export declare const SMTP_EXTENSIONS: {
|
||||||
|
PIPELINING: string;
|
||||||
|
SIZE: string;
|
||||||
|
EIGHTBITMIME: string;
|
||||||
|
STARTTLS: string;
|
||||||
|
AUTH: string;
|
||||||
|
ENHANCEDSTATUSCODES: string;
|
||||||
|
HELP: string;
|
||||||
|
CHUNKING: string;
|
||||||
|
DSN: string;
|
||||||
|
formatExtension(name: string, parameter?: string | number): string;
|
||||||
|
};
|
||||||
162
dist_ts/mail/delivery/smtpserver/constants.js
Normal file
162
dist_ts/mail/delivery/smtpserver/constants.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Server Constants
|
||||||
|
* This file contains all constants and enums used by the SMTP server
|
||||||
|
*/
|
||||||
|
import { SmtpState } from '../interfaces.js';
|
||||||
|
// Re-export SmtpState enum from the main interfaces file
|
||||||
|
export { SmtpState };
|
||||||
|
/**
|
||||||
|
* SMTP Response Codes
|
||||||
|
* Based on RFC 5321 and common SMTP practice
|
||||||
|
*/
|
||||||
|
export var SmtpResponseCode;
|
||||||
|
(function (SmtpResponseCode) {
|
||||||
|
// Success codes (2xx)
|
||||||
|
SmtpResponseCode[SmtpResponseCode["SUCCESS"] = 250] = "SUCCESS";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["SYSTEM_STATUS"] = 211] = "SYSTEM_STATUS";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["HELP_MESSAGE"] = 214] = "HELP_MESSAGE";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["SERVICE_READY"] = 220] = "SERVICE_READY";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["SERVICE_CLOSING"] = 221] = "SERVICE_CLOSING";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["AUTHENTICATION_SUCCESSFUL"] = 235] = "AUTHENTICATION_SUCCESSFUL";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["OK"] = 250] = "OK";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["FORWARD"] = 251] = "FORWARD";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["CANNOT_VRFY"] = 252] = "CANNOT_VRFY";
|
||||||
|
// Intermediate codes (3xx)
|
||||||
|
SmtpResponseCode[SmtpResponseCode["MORE_INFO_NEEDED"] = 334] = "MORE_INFO_NEEDED";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["START_MAIL_INPUT"] = 354] = "START_MAIL_INPUT";
|
||||||
|
// Temporary error codes (4xx)
|
||||||
|
SmtpResponseCode[SmtpResponseCode["SERVICE_NOT_AVAILABLE"] = 421] = "SERVICE_NOT_AVAILABLE";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["MAILBOX_TEMPORARILY_UNAVAILABLE"] = 450] = "MAILBOX_TEMPORARILY_UNAVAILABLE";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["LOCAL_ERROR"] = 451] = "LOCAL_ERROR";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["INSUFFICIENT_STORAGE"] = 452] = "INSUFFICIENT_STORAGE";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["TLS_UNAVAILABLE_TEMP"] = 454] = "TLS_UNAVAILABLE_TEMP";
|
||||||
|
// Permanent error codes (5xx)
|
||||||
|
SmtpResponseCode[SmtpResponseCode["SYNTAX_ERROR"] = 500] = "SYNTAX_ERROR";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["SYNTAX_ERROR_PARAMETERS"] = 501] = "SYNTAX_ERROR_PARAMETERS";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["COMMAND_NOT_IMPLEMENTED"] = 502] = "COMMAND_NOT_IMPLEMENTED";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["BAD_SEQUENCE"] = 503] = "BAD_SEQUENCE";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["COMMAND_PARAMETER_NOT_IMPLEMENTED"] = 504] = "COMMAND_PARAMETER_NOT_IMPLEMENTED";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["AUTH_REQUIRED"] = 530] = "AUTH_REQUIRED";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["AUTH_FAILED"] = 535] = "AUTH_FAILED";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["MAILBOX_UNAVAILABLE"] = 550] = "MAILBOX_UNAVAILABLE";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["USER_NOT_LOCAL"] = 551] = "USER_NOT_LOCAL";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["EXCEEDED_STORAGE"] = 552] = "EXCEEDED_STORAGE";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["MAILBOX_NAME_INVALID"] = 553] = "MAILBOX_NAME_INVALID";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["TRANSACTION_FAILED"] = 554] = "TRANSACTION_FAILED";
|
||||||
|
SmtpResponseCode[SmtpResponseCode["MAIL_RCPT_PARAMETERS_INVALID"] = 555] = "MAIL_RCPT_PARAMETERS_INVALID";
|
||||||
|
})(SmtpResponseCode || (SmtpResponseCode = {}));
|
||||||
|
/**
|
||||||
|
* SMTP Command Types
|
||||||
|
*/
|
||||||
|
export var SmtpCommand;
|
||||||
|
(function (SmtpCommand) {
|
||||||
|
SmtpCommand["HELO"] = "HELO";
|
||||||
|
SmtpCommand["EHLO"] = "EHLO";
|
||||||
|
SmtpCommand["MAIL_FROM"] = "MAIL";
|
||||||
|
SmtpCommand["RCPT_TO"] = "RCPT";
|
||||||
|
SmtpCommand["DATA"] = "DATA";
|
||||||
|
SmtpCommand["RSET"] = "RSET";
|
||||||
|
SmtpCommand["NOOP"] = "NOOP";
|
||||||
|
SmtpCommand["QUIT"] = "QUIT";
|
||||||
|
SmtpCommand["STARTTLS"] = "STARTTLS";
|
||||||
|
SmtpCommand["AUTH"] = "AUTH";
|
||||||
|
SmtpCommand["HELP"] = "HELP";
|
||||||
|
SmtpCommand["VRFY"] = "VRFY";
|
||||||
|
SmtpCommand["EXPN"] = "EXPN";
|
||||||
|
})(SmtpCommand || (SmtpCommand = {}));
|
||||||
|
/**
|
||||||
|
* Security log event types
|
||||||
|
*/
|
||||||
|
export var SecurityEventType;
|
||||||
|
(function (SecurityEventType) {
|
||||||
|
SecurityEventType["CONNECTION"] = "connection";
|
||||||
|
SecurityEventType["AUTHENTICATION"] = "authentication";
|
||||||
|
SecurityEventType["COMMAND"] = "command";
|
||||||
|
SecurityEventType["DATA"] = "data";
|
||||||
|
SecurityEventType["IP_REPUTATION"] = "ip_reputation";
|
||||||
|
SecurityEventType["TLS_NEGOTIATION"] = "tls_negotiation";
|
||||||
|
SecurityEventType["DKIM"] = "dkim";
|
||||||
|
SecurityEventType["SPF"] = "spf";
|
||||||
|
SecurityEventType["DMARC"] = "dmarc";
|
||||||
|
SecurityEventType["EMAIL_VALIDATION"] = "email_validation";
|
||||||
|
SecurityEventType["SPAM"] = "spam";
|
||||||
|
SecurityEventType["ACCESS_CONTROL"] = "access_control";
|
||||||
|
})(SecurityEventType || (SecurityEventType = {}));
|
||||||
|
/**
|
||||||
|
* Security log levels
|
||||||
|
*/
|
||||||
|
export var SecurityLogLevel;
|
||||||
|
(function (SecurityLogLevel) {
|
||||||
|
SecurityLogLevel["DEBUG"] = "debug";
|
||||||
|
SecurityLogLevel["INFO"] = "info";
|
||||||
|
SecurityLogLevel["WARN"] = "warn";
|
||||||
|
SecurityLogLevel["ERROR"] = "error";
|
||||||
|
})(SecurityLogLevel || (SecurityLogLevel = {}));
|
||||||
|
/**
|
||||||
|
* SMTP Server Defaults
|
||||||
|
*/
|
||||||
|
export const SMTP_DEFAULTS = {
|
||||||
|
// Default timeouts in milliseconds
|
||||||
|
CONNECTION_TIMEOUT: 30000, // 30 seconds
|
||||||
|
SOCKET_TIMEOUT: 300000, // 5 minutes
|
||||||
|
DATA_TIMEOUT: 60000, // 1 minute
|
||||||
|
CLEANUP_INTERVAL: 5000, // 5 seconds
|
||||||
|
// Default limits
|
||||||
|
MAX_CONNECTIONS: 100,
|
||||||
|
MAX_RECIPIENTS: 100,
|
||||||
|
MAX_MESSAGE_SIZE: 10485760, // 10MB
|
||||||
|
// Default ports
|
||||||
|
SMTP_PORT: 25,
|
||||||
|
SUBMISSION_PORT: 587,
|
||||||
|
SECURE_PORT: 465,
|
||||||
|
// Default hostname
|
||||||
|
HOSTNAME: 'mail.lossless.one',
|
||||||
|
// CRLF line ending required by SMTP protocol
|
||||||
|
CRLF: '\r\n',
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* SMTP Command Patterns
|
||||||
|
* Regular expressions for parsing SMTP commands
|
||||||
|
*/
|
||||||
|
export const SMTP_PATTERNS = {
|
||||||
|
// Match EHLO/HELO command: "EHLO example.com"
|
||||||
|
// Made very permissive to handle various client implementations
|
||||||
|
EHLO: /^(?:EHLO|HELO)\s+(.+)$/i,
|
||||||
|
// Match MAIL FROM command: "MAIL FROM:<user@example.com> [PARAM=VALUE]"
|
||||||
|
// Made more permissive with whitespace and parameter formats
|
||||||
|
MAIL_FROM: /^MAIL\s+FROM\s*:\s*<([^>]*)>((?:\s+[a-zA-Z0-9][a-zA-Z0-9\-]*(?:=[^\s]+)?)*)$/i,
|
||||||
|
// Match RCPT TO command: "RCPT TO:<user@example.com> [PARAM=VALUE]"
|
||||||
|
// Made more permissive with whitespace and parameter formats
|
||||||
|
RCPT_TO: /^RCPT\s+TO\s*:\s*<([^>]*)>((?:\s+[a-zA-Z0-9][a-zA-Z0-9\-]*(?:=[^\s]+)?)*)$/i,
|
||||||
|
// Match parameter format: "PARAM=VALUE"
|
||||||
|
PARAM: /\s+([A-Za-z0-9][A-Za-z0-9\-]*)(?:=([^\s]+))?/g,
|
||||||
|
// Match email address format - basic validation
|
||||||
|
// This pattern rejects common invalid formats while being permissive for edge cases
|
||||||
|
// Checks: no spaces, has @, has domain with dot, no double dots, proper domain format
|
||||||
|
EMAIL: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
||||||
|
// Match end of DATA marker: \r\n.\r\n or just .\r\n at the start of a line (to handle various client implementations)
|
||||||
|
END_DATA: /(\r\n\.\r\n$)|(\n\.\r\n$)|(\r\n\.\n$)|(\n\.\n$)|^\.(\r\n|\n)$/,
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* SMTP Extension List
|
||||||
|
* These extensions are advertised in the EHLO response
|
||||||
|
*/
|
||||||
|
export const SMTP_EXTENSIONS = {
|
||||||
|
// Basic extensions (RFC 1869)
|
||||||
|
PIPELINING: 'PIPELINING',
|
||||||
|
SIZE: 'SIZE',
|
||||||
|
EIGHTBITMIME: '8BITMIME',
|
||||||
|
// Security extensions
|
||||||
|
STARTTLS: 'STARTTLS',
|
||||||
|
AUTH: 'AUTH',
|
||||||
|
// Additional extensions
|
||||||
|
ENHANCEDSTATUSCODES: 'ENHANCEDSTATUSCODES',
|
||||||
|
HELP: 'HELP',
|
||||||
|
CHUNKING: 'CHUNKING',
|
||||||
|
DSN: 'DSN',
|
||||||
|
// Format an extension with a parameter
|
||||||
|
formatExtension(name, parameter) {
|
||||||
|
return parameter !== undefined ? `${name} ${parameter}` : name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHMvbWFpbC9kZWxpdmVyeS9zbXRwc2VydmVyL2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFN0MseURBQXlEO0FBQ3pELE9BQU8sRUFBRSxTQUFTLEVBQUUsQ0FBQztBQUVyQjs7O0dBR0c7QUFDSCxNQUFNLENBQU4sSUFBWSxnQkFxQ1g7QUFyQ0QsV0FBWSxnQkFBZ0I7SUFDMUIsc0JBQXNCO0lBQ3RCLCtEQUFhLENBQUE7SUFDYiwyRUFBbUIsQ0FBQTtJQUNuQix5RUFBa0IsQ0FBQTtJQUNsQiwyRUFBbUIsQ0FBQTtJQUNuQiwrRUFBcUIsQ0FBQTtJQUNyQixtR0FBK0IsQ0FBQTtJQUMvQixxREFBUSxDQUFBO0lBQ1IsK0RBQWEsQ0FBQTtJQUNiLHVFQUFpQixDQUFBO0lBRWpCLDJCQUEyQjtJQUMzQixpRkFBc0IsQ0FBQTtJQUN0QixpRkFBc0IsQ0FBQTtJQUV0Qiw4QkFBOEI7SUFDOUIsMkZBQTJCLENBQUE7SUFDM0IsK0dBQXFDLENBQUE7SUFDckMsdUVBQWlCLENBQUE7SUFDakIseUZBQTBCLENBQUE7SUFDMUIseUZBQTBCLENBQUE7SUFFMUIsOEJBQThCO0lBQzlCLHlFQUFrQixDQUFBO0lBQ2xCLCtGQUE2QixDQUFBO0lBQzdCLCtGQUE2QixDQUFBO0lBQzdCLHlFQUFrQixDQUFBO0lBQ2xCLG1IQUF1QyxDQUFBO0lBQ3ZDLDJFQUFtQixDQUFBO0lBQ25CLHVFQUFpQixDQUFBO0lBQ2pCLHVGQUF5QixDQUFBO0lBQ3pCLDZFQUFvQixDQUFBO0lBQ3BCLGlGQUFzQixDQUFBO0lBQ3RCLHlGQUEwQixDQUFBO0lBQzFCLHFGQUF3QixDQUFBO0lBQ3hCLHlHQUFrQyxDQUFBO0FBQ3BDLENBQUMsRUFyQ1csZ0JBQWdCLEtBQWhCLGdCQUFn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
|
||||||
14
dist_ts/mail/delivery/smtpserver/create-server.d.ts
vendored
Normal file
14
dist_ts/mail/delivery/smtpserver/create-server.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Server Creation Factory
|
||||||
|
* Provides a simple way to create a complete SMTP server
|
||||||
|
*/
|
||||||
|
import { SmtpServer } from './smtp-server.js';
|
||||||
|
import type { ISmtpServerOptions } from './interfaces.js';
|
||||||
|
import { UnifiedEmailServer } from '../../routing/classes.unified.email.server.js';
|
||||||
|
/**
|
||||||
|
* Create a complete SMTP server with all components
|
||||||
|
* @param emailServer - Email server reference
|
||||||
|
* @param options - SMTP server options
|
||||||
|
* @returns Configured SMTP server instance
|
||||||
|
*/
|
||||||
|
export declare function createSmtpServer(emailServer: UnifiedEmailServer, options: ISmtpServerOptions): SmtpServer;
|
||||||
28
dist_ts/mail/delivery/smtpserver/create-server.js
Normal file
28
dist_ts/mail/delivery/smtpserver/create-server.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Server Creation Factory
|
||||||
|
* Provides a simple way to create a complete SMTP server
|
||||||
|
*/
|
||||||
|
import { SmtpServer } from './smtp-server.js';
|
||||||
|
import { SessionManager } from './session-manager.js';
|
||||||
|
import { ConnectionManager } from './connection-manager.js';
|
||||||
|
import { CommandHandler } from './command-handler.js';
|
||||||
|
import { DataHandler } from './data-handler.js';
|
||||||
|
import { TlsHandler } from './tls-handler.js';
|
||||||
|
import { SecurityHandler } from './security-handler.js';
|
||||||
|
import { UnifiedEmailServer } from '../../routing/classes.unified.email.server.js';
|
||||||
|
/**
|
||||||
|
* Create a complete SMTP server with all components
|
||||||
|
* @param emailServer - Email server reference
|
||||||
|
* @param options - SMTP server options
|
||||||
|
* @returns Configured SMTP server instance
|
||||||
|
*/
|
||||||
|
export function createSmtpServer(emailServer, options) {
|
||||||
|
// First create the SMTP server instance
|
||||||
|
const smtpServer = new SmtpServer({
|
||||||
|
emailServer,
|
||||||
|
options
|
||||||
|
});
|
||||||
|
// Return the configured server
|
||||||
|
return smtpServer;
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cHNlcnZlci9jcmVhdGUtc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdEQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUNoRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDOUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBRXhELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLCtDQUErQyxDQUFDO0FBRW5GOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLGdCQUFnQixDQUFDLFdBQStCLEVBQUUsT0FBMkI7SUFDM0Ysd0NBQXdDO0lBQ3hDLE1BQU0sVUFBVSxHQUFHLElBQUksVUFBVSxDQUFDO1FBQ2hDLFdBQVc7UUFDWCxPQUFPO0tBQ1IsQ0FBQyxDQUFDO0lBRUgsK0JBQStCO0lBQy9CLE9BQU8sVUFBVSxDQUFDO0FBQ3BCLENBQUMifQ==
|
||||||
123
dist_ts/mail/delivery/smtpserver/data-handler.d.ts
vendored
Normal file
123
dist_ts/mail/delivery/smtpserver/data-handler.d.ts
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Data Handler
|
||||||
|
* Responsible for processing email data during and after DATA command
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../plugins.js';
|
||||||
|
import type { ISmtpSession, ISmtpTransactionResult } from './interfaces.js';
|
||||||
|
import type { IDataHandler, ISmtpServer } from './interfaces.js';
|
||||||
|
import { Email } from '../../core/classes.email.js';
|
||||||
|
/**
|
||||||
|
* Handles SMTP DATA command and email data processing
|
||||||
|
*/
|
||||||
|
export declare class DataHandler implements IDataHandler {
|
||||||
|
/**
|
||||||
|
* Reference to the SMTP server instance
|
||||||
|
*/
|
||||||
|
private smtpServer;
|
||||||
|
/**
|
||||||
|
* Creates a new data handler
|
||||||
|
* @param smtpServer - SMTP server instance
|
||||||
|
*/
|
||||||
|
constructor(smtpServer: ISmtpServer);
|
||||||
|
/**
|
||||||
|
* Process incoming email data
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param data - Data chunk
|
||||||
|
* @returns Promise that resolves when the data is processed
|
||||||
|
*/
|
||||||
|
processEmailData(socket: plugins.net.Socket | plugins.tls.TLSSocket, data: string): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Handle raw data chunks during DATA mode (optimized for large messages)
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param data - Raw data chunk
|
||||||
|
*/
|
||||||
|
handleDataReceived(socket: plugins.net.Socket | plugins.tls.TLSSocket, data: string): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Process email data chunks efficiently for large messages
|
||||||
|
* @param chunks - Array of email data chunks
|
||||||
|
* @returns Processed email data string
|
||||||
|
*/
|
||||||
|
private processEmailDataStreaming;
|
||||||
|
/**
|
||||||
|
* Process a complete email
|
||||||
|
* @param rawData - Raw email data
|
||||||
|
* @param session - SMTP session
|
||||||
|
* @returns Promise that resolves with the Email object
|
||||||
|
*/
|
||||||
|
processEmail(rawData: string, session: ISmtpSession): Promise<Email>;
|
||||||
|
/**
|
||||||
|
* Parse email from raw data
|
||||||
|
* @param rawData - Raw email data
|
||||||
|
* @param session - SMTP session
|
||||||
|
* @returns Email object
|
||||||
|
*/
|
||||||
|
private parseEmailFromData;
|
||||||
|
/**
|
||||||
|
* Process a complete email (legacy method)
|
||||||
|
* @param session - SMTP session
|
||||||
|
* @returns Promise that resolves with the result of the transaction
|
||||||
|
*/
|
||||||
|
processEmailLegacy(session: ISmtpSession): Promise<ISmtpTransactionResult>;
|
||||||
|
/**
|
||||||
|
* Save an email to disk
|
||||||
|
* @param session - SMTP session
|
||||||
|
*/
|
||||||
|
saveEmail(session: ISmtpSession): void;
|
||||||
|
/**
|
||||||
|
* Parse an email into an Email object
|
||||||
|
* @param session - SMTP session
|
||||||
|
* @returns Promise that resolves with the parsed Email object
|
||||||
|
*/
|
||||||
|
parseEmail(session: ISmtpSession): Promise<Email>;
|
||||||
|
/**
|
||||||
|
* Basic fallback method for parsing emails
|
||||||
|
* @param session - SMTP session
|
||||||
|
* @returns The parsed Email object
|
||||||
|
*/
|
||||||
|
private parseEmailBasic;
|
||||||
|
/**
|
||||||
|
* Handle multipart content parsing
|
||||||
|
* @param email - Email object to update
|
||||||
|
* @param bodyText - Body text to parse
|
||||||
|
* @param boundary - MIME boundary
|
||||||
|
*/
|
||||||
|
private handleMultipartContent;
|
||||||
|
/**
|
||||||
|
* Handle end of data marker received
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param session - SMTP session
|
||||||
|
*/
|
||||||
|
private handleEndOfData;
|
||||||
|
/**
|
||||||
|
* Reset session after email processing
|
||||||
|
* @param session - SMTP session
|
||||||
|
*/
|
||||||
|
private resetSession;
|
||||||
|
/**
|
||||||
|
* Send a response to the client
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param response - Response message
|
||||||
|
*/
|
||||||
|
private sendResponse;
|
||||||
|
/**
|
||||||
|
* Check if a socket error is potentially recoverable
|
||||||
|
* @param error - The error that occurred
|
||||||
|
* @returns Whether the error is potentially recoverable
|
||||||
|
*/
|
||||||
|
private isRecoverableSocketError;
|
||||||
|
/**
|
||||||
|
* Handle recoverable socket errors with retry logic
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param error - The error that occurred
|
||||||
|
* @param response - The response that failed to send
|
||||||
|
*/
|
||||||
|
private handleSocketError;
|
||||||
|
/**
|
||||||
|
* Handle email data (interface requirement)
|
||||||
|
*/
|
||||||
|
handleData(socket: plugins.net.Socket | plugins.tls.TLSSocket, data: string, session: ISmtpSession): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Clean up resources
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
1152
dist_ts/mail/delivery/smtpserver/data-handler.js
Normal file
1152
dist_ts/mail/delivery/smtpserver/data-handler.js
Normal file
File diff suppressed because one or more lines are too long
20
dist_ts/mail/delivery/smtpserver/index.d.ts
vendored
Normal file
20
dist_ts/mail/delivery/smtpserver/index.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Server Module Exports
|
||||||
|
* This file exports all components of the refactored SMTP server
|
||||||
|
*/
|
||||||
|
export * from './interfaces.js';
|
||||||
|
export { SmtpServer } from './smtp-server.js';
|
||||||
|
export { SessionManager } from './session-manager.js';
|
||||||
|
export { ConnectionManager } from './connection-manager.js';
|
||||||
|
export { CommandHandler } from './command-handler.js';
|
||||||
|
export { DataHandler } from './data-handler.js';
|
||||||
|
export { TlsHandler } from './tls-handler.js';
|
||||||
|
export { SecurityHandler } from './security-handler.js';
|
||||||
|
export * from './constants.js';
|
||||||
|
export { SmtpLogger } from './utils/logging.js';
|
||||||
|
export * from './utils/validation.js';
|
||||||
|
export * from './utils/helpers.js';
|
||||||
|
export * from './certificate-utils.js';
|
||||||
|
export * from './secure-server.js';
|
||||||
|
export * from './starttls-handler.js';
|
||||||
|
export { createSmtpServer } from './create-server.js';
|
||||||
27
dist_ts/mail/delivery/smtpserver/index.js
Normal file
27
dist_ts/mail/delivery/smtpserver/index.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Server Module Exports
|
||||||
|
* This file exports all components of the refactored SMTP server
|
||||||
|
*/
|
||||||
|
// Export interfaces
|
||||||
|
export * from './interfaces.js';
|
||||||
|
// Export server classes
|
||||||
|
export { SmtpServer } from './smtp-server.js';
|
||||||
|
export { SessionManager } from './session-manager.js';
|
||||||
|
export { ConnectionManager } from './connection-manager.js';
|
||||||
|
export { CommandHandler } from './command-handler.js';
|
||||||
|
export { DataHandler } from './data-handler.js';
|
||||||
|
export { TlsHandler } from './tls-handler.js';
|
||||||
|
export { SecurityHandler } from './security-handler.js';
|
||||||
|
// Export constants
|
||||||
|
export * from './constants.js';
|
||||||
|
// Export utilities
|
||||||
|
export { SmtpLogger } from './utils/logging.js';
|
||||||
|
export * from './utils/validation.js';
|
||||||
|
export * from './utils/helpers.js';
|
||||||
|
// Export TLS and certificate utilities
|
||||||
|
export * from './certificate-utils.js';
|
||||||
|
export * from './secure-server.js';
|
||||||
|
export * from './starttls-handler.js';
|
||||||
|
// Factory function to create a complete SMTP server with default components
|
||||||
|
export { createSmtpServer } from './create-server.js';
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L3NtdHBzZXJ2ZXIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsb0JBQW9CO0FBQ3BCLGNBQWMsaUJBQWlCLENBQUM7QUFFaEMsd0JBQXdCO0FBQ3hCLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdEQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUNoRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDOUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBRXhELG1CQUFtQjtBQUNuQixjQUFjLGdCQUFnQixDQUFDO0FBRS9CLG1CQUFtQjtBQUNuQixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDaEQsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLG9CQUFvQixDQUFDO0FBRW5DLHVDQUF1QztBQUN2QyxjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyx1QkFBdUIsQ0FBQztBQUV0Qyw0RUFBNEU7QUFDNUUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sb0JBQW9CLENBQUMifQ==
|
||||||
530
dist_ts/mail/delivery/smtpserver/interfaces.d.ts
vendored
Normal file
530
dist_ts/mail/delivery/smtpserver/interfaces.d.ts
vendored
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Server Interfaces
|
||||||
|
* Defines all the interfaces used by the SMTP server implementation
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../plugins.js';
|
||||||
|
import type { Email } from '../../core/classes.email.js';
|
||||||
|
import type { UnifiedEmailServer } from '../../routing/classes.unified.email.server.js';
|
||||||
|
import { SmtpState } from '../interfaces.js';
|
||||||
|
import { SmtpCommand } from './constants.js';
|
||||||
|
export { SmtpState, SmtpCommand };
|
||||||
|
export type { IEnvelopeRecipient } from '../interfaces.js';
|
||||||
|
/**
|
||||||
|
* Interface for components that need cleanup
|
||||||
|
*/
|
||||||
|
export interface IDestroyable {
|
||||||
|
/**
|
||||||
|
* Clean up all resources (timers, listeners, etc)
|
||||||
|
*/
|
||||||
|
destroy(): void | Promise<void>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP authentication credentials
|
||||||
|
*/
|
||||||
|
export interface ISmtpAuth {
|
||||||
|
/**
|
||||||
|
* Username for authentication
|
||||||
|
*/
|
||||||
|
username: string;
|
||||||
|
/**
|
||||||
|
* Password for authentication
|
||||||
|
*/
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP envelope (sender and recipients)
|
||||||
|
*/
|
||||||
|
export interface ISmtpEnvelope {
|
||||||
|
/**
|
||||||
|
* Mail from address
|
||||||
|
*/
|
||||||
|
mailFrom: {
|
||||||
|
address: string;
|
||||||
|
args?: Record<string, string>;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Recipients list
|
||||||
|
*/
|
||||||
|
rcptTo: Array<{
|
||||||
|
address: string;
|
||||||
|
args?: Record<string, string>;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP session representing a client connection
|
||||||
|
*/
|
||||||
|
export interface ISmtpSession {
|
||||||
|
/**
|
||||||
|
* Unique session identifier
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* Current state of the SMTP session
|
||||||
|
*/
|
||||||
|
state: SmtpState;
|
||||||
|
/**
|
||||||
|
* Client's hostname from EHLO/HELO
|
||||||
|
*/
|
||||||
|
clientHostname: string | null;
|
||||||
|
/**
|
||||||
|
* Whether TLS is active for this session
|
||||||
|
*/
|
||||||
|
secure: boolean;
|
||||||
|
/**
|
||||||
|
* Authentication status
|
||||||
|
*/
|
||||||
|
authenticated: boolean;
|
||||||
|
/**
|
||||||
|
* Authentication username if authenticated
|
||||||
|
*/
|
||||||
|
username?: string;
|
||||||
|
/**
|
||||||
|
* Transaction envelope
|
||||||
|
*/
|
||||||
|
envelope: ISmtpEnvelope;
|
||||||
|
/**
|
||||||
|
* When the session was created
|
||||||
|
*/
|
||||||
|
createdAt: Date;
|
||||||
|
/**
|
||||||
|
* Last activity timestamp
|
||||||
|
*/
|
||||||
|
lastActivity: number;
|
||||||
|
/**
|
||||||
|
* Client's IP address
|
||||||
|
*/
|
||||||
|
remoteAddress: string;
|
||||||
|
/**
|
||||||
|
* Client's port
|
||||||
|
*/
|
||||||
|
remotePort: number;
|
||||||
|
/**
|
||||||
|
* Additional session data
|
||||||
|
*/
|
||||||
|
data?: Record<string, any>;
|
||||||
|
/**
|
||||||
|
* Message size if SIZE extension is used
|
||||||
|
*/
|
||||||
|
messageSize?: number;
|
||||||
|
/**
|
||||||
|
* Server capabilities advertised to client
|
||||||
|
*/
|
||||||
|
capabilities?: string[];
|
||||||
|
/**
|
||||||
|
* Buffer for incomplete data
|
||||||
|
*/
|
||||||
|
dataBuffer?: string;
|
||||||
|
/**
|
||||||
|
* Flag to track if we're currently receiving DATA
|
||||||
|
*/
|
||||||
|
receivingData?: boolean;
|
||||||
|
/**
|
||||||
|
* The raw email data being received
|
||||||
|
*/
|
||||||
|
rawData?: string;
|
||||||
|
/**
|
||||||
|
* Greeting sent to client
|
||||||
|
*/
|
||||||
|
greeting?: string;
|
||||||
|
/**
|
||||||
|
* Whether EHLO has been sent
|
||||||
|
*/
|
||||||
|
ehloSent?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether HELO has been sent
|
||||||
|
*/
|
||||||
|
heloSent?: boolean;
|
||||||
|
/**
|
||||||
|
* TLS options for this session
|
||||||
|
*/
|
||||||
|
tlsOptions?: any;
|
||||||
|
/**
|
||||||
|
* Whether TLS is being used
|
||||||
|
*/
|
||||||
|
useTLS?: boolean;
|
||||||
|
/**
|
||||||
|
* Mail from address for this transaction
|
||||||
|
*/
|
||||||
|
mailFrom?: string;
|
||||||
|
/**
|
||||||
|
* Recipients for this transaction
|
||||||
|
*/
|
||||||
|
rcptTo?: string[];
|
||||||
|
/**
|
||||||
|
* Email data being received
|
||||||
|
*/
|
||||||
|
emailData?: string;
|
||||||
|
/**
|
||||||
|
* Chunks of email data
|
||||||
|
*/
|
||||||
|
emailDataChunks?: string[];
|
||||||
|
/**
|
||||||
|
* Timeout ID for data reception
|
||||||
|
*/
|
||||||
|
dataTimeoutId?: NodeJS.Timeout;
|
||||||
|
/**
|
||||||
|
* Whether connection has ended
|
||||||
|
*/
|
||||||
|
connectionEnded?: boolean;
|
||||||
|
/**
|
||||||
|
* Size of email data being received
|
||||||
|
*/
|
||||||
|
emailDataSize?: number;
|
||||||
|
/**
|
||||||
|
* Processing mode for this session
|
||||||
|
*/
|
||||||
|
processingMode?: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Session manager interface
|
||||||
|
*/
|
||||||
|
export interface ISessionManager extends IDestroyable {
|
||||||
|
/**
|
||||||
|
* Create a new session for a socket
|
||||||
|
*/
|
||||||
|
createSession(socket: plugins.net.Socket | plugins.tls.TLSSocket, secure?: boolean): ISmtpSession;
|
||||||
|
/**
|
||||||
|
* Get session by socket
|
||||||
|
*/
|
||||||
|
getSession(socket: plugins.net.Socket | plugins.tls.TLSSocket): ISmtpSession | undefined;
|
||||||
|
/**
|
||||||
|
* Update session state
|
||||||
|
*/
|
||||||
|
updateSessionState(session: ISmtpSession, newState: SmtpState): void;
|
||||||
|
/**
|
||||||
|
* Remove a session
|
||||||
|
*/
|
||||||
|
removeSession(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||||
|
/**
|
||||||
|
* Clear all sessions
|
||||||
|
*/
|
||||||
|
clearAllSessions(): void;
|
||||||
|
/**
|
||||||
|
* Get all active sessions
|
||||||
|
*/
|
||||||
|
getAllSessions(): ISmtpSession[];
|
||||||
|
/**
|
||||||
|
* Get session count
|
||||||
|
*/
|
||||||
|
getSessionCount(): number;
|
||||||
|
/**
|
||||||
|
* Update last activity for a session
|
||||||
|
*/
|
||||||
|
updateLastActivity(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||||
|
/**
|
||||||
|
* Check for timed out sessions
|
||||||
|
*/
|
||||||
|
checkTimeouts(timeoutMs: number): ISmtpSession[];
|
||||||
|
/**
|
||||||
|
* Update session activity timestamp
|
||||||
|
*/
|
||||||
|
updateSessionActivity(session: ISmtpSession): void;
|
||||||
|
/**
|
||||||
|
* Replace socket in session (for TLS upgrade)
|
||||||
|
*/
|
||||||
|
replaceSocket(oldSocket: plugins.net.Socket | plugins.tls.TLSSocket, newSocket: plugins.net.Socket | plugins.tls.TLSSocket): boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Connection manager interface
|
||||||
|
*/
|
||||||
|
export interface IConnectionManager extends IDestroyable {
|
||||||
|
/**
|
||||||
|
* Handle a new connection
|
||||||
|
*/
|
||||||
|
handleConnection(socket: plugins.net.Socket | plugins.tls.TLSSocket, secure: boolean): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Close all active connections
|
||||||
|
*/
|
||||||
|
closeAllConnections(): void;
|
||||||
|
/**
|
||||||
|
* Get active connection count
|
||||||
|
*/
|
||||||
|
getConnectionCount(): number;
|
||||||
|
/**
|
||||||
|
* Check if accepting new connections
|
||||||
|
*/
|
||||||
|
canAcceptConnection(): boolean;
|
||||||
|
/**
|
||||||
|
* Handle new connection (legacy method name)
|
||||||
|
*/
|
||||||
|
handleNewConnection(socket: plugins.net.Socket): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Handle new secure connection (legacy method name)
|
||||||
|
*/
|
||||||
|
handleNewSecureConnection(socket: plugins.tls.TLSSocket): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Setup socket event handlers
|
||||||
|
*/
|
||||||
|
setupSocketEventHandlers(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Command handler interface
|
||||||
|
*/
|
||||||
|
export interface ICommandHandler extends IDestroyable {
|
||||||
|
/**
|
||||||
|
* Handle an SMTP command
|
||||||
|
*/
|
||||||
|
handleCommand(socket: plugins.net.Socket | plugins.tls.TLSSocket, command: SmtpCommand, args: string, session: ISmtpSession): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Get supported commands for current session state
|
||||||
|
*/
|
||||||
|
getSupportedCommands(session: ISmtpSession): SmtpCommand[];
|
||||||
|
/**
|
||||||
|
* Process command (legacy method name)
|
||||||
|
*/
|
||||||
|
processCommand(socket: plugins.net.Socket | plugins.tls.TLSSocket, command: string): Promise<void>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Data handler interface
|
||||||
|
*/
|
||||||
|
export interface IDataHandler extends IDestroyable {
|
||||||
|
/**
|
||||||
|
* Handle email data
|
||||||
|
*/
|
||||||
|
handleData(socket: plugins.net.Socket | plugins.tls.TLSSocket, data: string, session: ISmtpSession): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Process a complete email
|
||||||
|
*/
|
||||||
|
processEmail(rawData: string, session: ISmtpSession): Promise<Email>;
|
||||||
|
/**
|
||||||
|
* Handle data received (legacy method name)
|
||||||
|
*/
|
||||||
|
handleDataReceived(socket: plugins.net.Socket | plugins.tls.TLSSocket, data: string): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Process email data (legacy method name)
|
||||||
|
*/
|
||||||
|
processEmailData(socket: plugins.net.Socket | plugins.tls.TLSSocket, data: string): Promise<void>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* TLS handler interface
|
||||||
|
*/
|
||||||
|
export interface ITlsHandler extends IDestroyable {
|
||||||
|
/**
|
||||||
|
* Handle STARTTLS command
|
||||||
|
*/
|
||||||
|
handleStartTls(socket: plugins.net.Socket, session: ISmtpSession): Promise<plugins.tls.TLSSocket | null>;
|
||||||
|
/**
|
||||||
|
* Check if TLS is available
|
||||||
|
*/
|
||||||
|
isTlsAvailable(): boolean;
|
||||||
|
/**
|
||||||
|
* Get TLS options
|
||||||
|
*/
|
||||||
|
getTlsOptions(): plugins.tls.TlsOptions;
|
||||||
|
/**
|
||||||
|
* Check if TLS is enabled
|
||||||
|
*/
|
||||||
|
isTlsEnabled(): boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Security handler interface
|
||||||
|
*/
|
||||||
|
export interface ISecurityHandler extends IDestroyable {
|
||||||
|
/**
|
||||||
|
* Check IP reputation
|
||||||
|
*/
|
||||||
|
checkIpReputation(socket: plugins.net.Socket | plugins.tls.TLSSocket): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* Validate email address
|
||||||
|
*/
|
||||||
|
isValidEmail(email: string): boolean;
|
||||||
|
/**
|
||||||
|
* Authenticate user
|
||||||
|
*/
|
||||||
|
authenticate(auth: ISmtpAuth): Promise<boolean>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP server options
|
||||||
|
*/
|
||||||
|
export interface ISmtpServerOptions {
|
||||||
|
/**
|
||||||
|
* Port to listen on
|
||||||
|
*/
|
||||||
|
port: number;
|
||||||
|
/**
|
||||||
|
* Hostname of the server
|
||||||
|
*/
|
||||||
|
hostname: string;
|
||||||
|
/**
|
||||||
|
* Host to bind to (optional, defaults to 0.0.0.0)
|
||||||
|
*/
|
||||||
|
host?: string;
|
||||||
|
/**
|
||||||
|
* Secure port for TLS connections
|
||||||
|
*/
|
||||||
|
securePort?: number;
|
||||||
|
/**
|
||||||
|
* TLS/SSL private key (PEM format)
|
||||||
|
*/
|
||||||
|
key?: string;
|
||||||
|
/**
|
||||||
|
* TLS/SSL certificate (PEM format)
|
||||||
|
*/
|
||||||
|
cert?: string;
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Interface for SMTP session events
|
||||||
|
* These events are emitted by the session manager
|
||||||
|
*/
|
||||||
|
export interface ISessionEvents {
|
||||||
|
created: (session: ISmtpSession, socket: plugins.net.Socket | plugins.tls.TLSSocket) => void;
|
||||||
|
stateChanged: (session: ISmtpSession, previousState: SmtpState, newState: SmtpState) => void;
|
||||||
|
timeout: (session: ISmtpSession, socket: plugins.net.Socket | plugins.tls.TLSSocket) => void;
|
||||||
|
completed: (session: ISmtpSession, socket: plugins.net.Socket | plugins.tls.TLSSocket) => void;
|
||||||
|
error: (session: ISmtpSession, error: Error) => void;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* SMTP Server interface
|
||||||
|
*/
|
||||||
|
export interface ISmtpServer extends IDestroyable {
|
||||||
|
/**
|
||||||
|
* Start the SMTP server
|
||||||
|
*/
|
||||||
|
listen(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Stop the SMTP server
|
||||||
|
*/
|
||||||
|
close(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Get the session manager
|
||||||
|
*/
|
||||||
|
getSessionManager(): ISessionManager;
|
||||||
|
/**
|
||||||
|
* Get the connection manager
|
||||||
|
*/
|
||||||
|
getConnectionManager(): IConnectionManager;
|
||||||
|
/**
|
||||||
|
* Get the command handler
|
||||||
|
*/
|
||||||
|
getCommandHandler(): ICommandHandler;
|
||||||
|
/**
|
||||||
|
* Get the data handler
|
||||||
|
*/
|
||||||
|
getDataHandler(): IDataHandler;
|
||||||
|
/**
|
||||||
|
* Get the TLS handler
|
||||||
|
*/
|
||||||
|
getTlsHandler(): ITlsHandler;
|
||||||
|
/**
|
||||||
|
* Get the security handler
|
||||||
|
*/
|
||||||
|
getSecurityHandler(): ISecurityHandler;
|
||||||
|
/**
|
||||||
|
* Get the server options
|
||||||
|
*/
|
||||||
|
getOptions(): ISmtpServerOptions;
|
||||||
|
/**
|
||||||
|
* Get the email server reference
|
||||||
|
*/
|
||||||
|
getEmailServer(): UnifiedEmailServer;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Configuration for creating SMTP server
|
||||||
|
*/
|
||||||
|
export interface ISmtpServerConfig {
|
||||||
|
/**
|
||||||
|
* Email server instance
|
||||||
|
*/
|
||||||
|
emailServer: UnifiedEmailServer;
|
||||||
|
/**
|
||||||
|
* Server options
|
||||||
|
*/
|
||||||
|
options: ISmtpServerOptions;
|
||||||
|
/**
|
||||||
|
* Optional custom session manager
|
||||||
|
*/
|
||||||
|
sessionManager?: ISessionManager;
|
||||||
|
/**
|
||||||
|
* Optional custom connection manager
|
||||||
|
*/
|
||||||
|
connectionManager?: IConnectionManager;
|
||||||
|
/**
|
||||||
|
* Optional custom command handler
|
||||||
|
*/
|
||||||
|
commandHandler?: ICommandHandler;
|
||||||
|
/**
|
||||||
|
* Optional custom data handler
|
||||||
|
*/
|
||||||
|
dataHandler?: IDataHandler;
|
||||||
|
/**
|
||||||
|
* Optional custom TLS handler
|
||||||
|
*/
|
||||||
|
tlsHandler?: ITlsHandler;
|
||||||
|
/**
|
||||||
|
* Optional custom security handler
|
||||||
|
*/
|
||||||
|
securityHandler?: ISecurityHandler;
|
||||||
|
}
|
||||||
10
dist_ts/mail/delivery/smtpserver/interfaces.js
Normal file
10
dist_ts/mail/delivery/smtpserver/interfaces.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Server Interfaces
|
||||||
|
* Defines all the interfaces used by the SMTP server implementation
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../plugins.js';
|
||||||
|
// Re-export types from other modules
|
||||||
|
import { SmtpState } from '../interfaces.js';
|
||||||
|
import { SmtpCommand } from './constants.js';
|
||||||
|
export { SmtpState, SmtpCommand };
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cHNlcnZlci9pbnRlcmZhY2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sS0FBSyxPQUFPLE1BQU0scUJBQXFCLENBQUM7QUFJL0MsdUNBQXVDO0FBQ3ZDLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM3QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDN0MsT0FBTyxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsQ0FBQyJ9
|
||||||
15
dist_ts/mail/delivery/smtpserver/secure-server.d.ts
vendored
Normal file
15
dist_ts/mail/delivery/smtpserver/secure-server.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Secure SMTP Server Utility Functions
|
||||||
|
* Provides helper functions for creating and managing secure TLS server
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../plugins.js';
|
||||||
|
/**
|
||||||
|
* Create a secure TLS server for direct TLS connections
|
||||||
|
* @param options - TLS certificate options
|
||||||
|
* @returns A configured TLS server or undefined if TLS is not available
|
||||||
|
*/
|
||||||
|
export declare function createSecureTlsServer(options: {
|
||||||
|
key: string;
|
||||||
|
cert: string;
|
||||||
|
ca?: string;
|
||||||
|
}): plugins.tls.Server | undefined;
|
||||||
79
dist_ts/mail/delivery/smtpserver/secure-server.js
Normal file
79
dist_ts/mail/delivery/smtpserver/secure-server.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* Secure SMTP Server Utility Functions
|
||||||
|
* Provides helper functions for creating and managing secure TLS server
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../plugins.js';
|
||||||
|
import { loadCertificatesFromString, generateSelfSignedCertificates, createTlsOptions } from './certificate-utils.js';
|
||||||
|
import { SmtpLogger } from './utils/logging.js';
|
||||||
|
/**
|
||||||
|
* Create a secure TLS server for direct TLS connections
|
||||||
|
* @param options - TLS certificate options
|
||||||
|
* @returns A configured TLS server or undefined if TLS is not available
|
||||||
|
*/
|
||||||
|
export function createSecureTlsServer(options) {
|
||||||
|
try {
|
||||||
|
// Log the creation attempt
|
||||||
|
SmtpLogger.info('Creating secure TLS server for direct connections');
|
||||||
|
// Load certificates from strings
|
||||||
|
let certificates;
|
||||||
|
try {
|
||||||
|
certificates = loadCertificatesFromString({
|
||||||
|
key: options.key,
|
||||||
|
cert: options.cert,
|
||||||
|
ca: options.ca
|
||||||
|
});
|
||||||
|
SmtpLogger.info('Successfully loaded TLS certificates for secure server');
|
||||||
|
}
|
||||||
|
catch (certificateError) {
|
||||||
|
SmtpLogger.warn(`Failed to load certificates, using self-signed: ${certificateError instanceof Error ? certificateError.message : String(certificateError)}`);
|
||||||
|
certificates = generateSelfSignedCertificates();
|
||||||
|
}
|
||||||
|
// Create server-side TLS options
|
||||||
|
const tlsOptions = createTlsOptions(certificates, true);
|
||||||
|
// Log details for debugging
|
||||||
|
SmtpLogger.debug('Creating secure server with options', {
|
||||||
|
certificates: {
|
||||||
|
keyLength: certificates.key.length,
|
||||||
|
certLength: certificates.cert.length,
|
||||||
|
caLength: certificates.ca ? certificates.ca.length : 0
|
||||||
|
},
|
||||||
|
tlsOptions: {
|
||||||
|
minVersion: tlsOptions.minVersion,
|
||||||
|
maxVersion: tlsOptions.maxVersion,
|
||||||
|
ciphers: tlsOptions.ciphers?.substring(0, 50) + '...' // Truncate long cipher list
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Create the TLS server
|
||||||
|
const server = new plugins.tls.Server(tlsOptions);
|
||||||
|
// Set up error handlers
|
||||||
|
server.on('error', (err) => {
|
||||||
|
SmtpLogger.error(`Secure server error: ${err.message}`, {
|
||||||
|
component: 'secure-server',
|
||||||
|
error: err,
|
||||||
|
stack: err.stack
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Log secure connections
|
||||||
|
server.on('secureConnection', (socket) => {
|
||||||
|
const protocol = socket.getProtocol();
|
||||||
|
const cipher = socket.getCipher();
|
||||||
|
SmtpLogger.info('New direct TLS connection established', {
|
||||||
|
component: 'secure-server',
|
||||||
|
remoteAddress: socket.remoteAddress,
|
||||||
|
remotePort: socket.remotePort,
|
||||||
|
protocol: protocol || 'unknown',
|
||||||
|
cipher: cipher?.name || 'unknown'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
SmtpLogger.error(`Failed to create secure TLS server: ${error instanceof Error ? error.message : String(error)}`, {
|
||||||
|
component: 'secure-server',
|
||||||
|
error: error instanceof Error ? error : new Error(String(error)),
|
||||||
|
stack: error instanceof Error ? error.stack : 'No stack trace available'
|
||||||
|
});
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VjdXJlLXNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cHNlcnZlci9zZWN1cmUtc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sS0FBSyxPQUFPLE1BQU0scUJBQXFCLENBQUM7QUFDL0MsT0FBTyxFQUNMLDBCQUEwQixFQUMxQiw4QkFBOEIsRUFDOUIsZ0JBQWdCLEVBRWpCLE1BQU0sd0JBQXdCLENBQUM7QUFDaEMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRWhEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUscUJBQXFCLENBQUMsT0FJckM7SUFDQyxJQUFJLENBQUM7UUFDSCwyQkFBMkI7UUFDM0IsVUFBVSxDQUFDLElBQUksQ0FBQyxtREFBbUQsQ0FBQyxDQUFDO1FBRXJFLGlDQUFpQztRQUNqQyxJQUFJLFlBQThCLENBQUM7UUFDbkMsSUFBSSxDQUFDO1lBQ0gsWUFBWSxHQUFHLDBCQUEwQixDQUFDO2dCQUN4QyxHQUFHLEVBQUUsT0FBTyxDQUFDLEdBQUc7Z0JBQ2hCLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSTtnQkFDbEIsRUFBRSxFQUFFLE9BQU8sQ0FBQyxFQUFFO2FBQ2YsQ0FBQyxDQUFDO1lBRUgsVUFBVSxDQUFDLElBQUksQ0FBQyx3REFBd0QsQ0FBQyxDQUFDO1FBQzVFLENBQUM7UUFBQyxPQUFPLGdCQUFnQixFQUFFLENBQUM7WUFDMUIsVUFBVSxDQUFDLElBQUksQ0FBQyxtREFBbUQsZ0JBQWdCLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUM5SixZQUFZLEdBQUcsOEJBQThCLEVBQUUsQ0FBQztRQUNsRCxDQUFDO1FBRUQsaUNBQWlDO1FBQ2pDLE1BQU0s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
|
||||||
86
dist_ts/mail/delivery/smtpserver/security-handler.d.ts
vendored
Normal file
86
dist_ts/mail/delivery/smtpserver/security-handler.d.ts
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Security Handler
|
||||||
|
* Responsible for security aspects including IP reputation checking,
|
||||||
|
* email validation, and authentication
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../plugins.js';
|
||||||
|
import type { ISmtpAuth } from './interfaces.js';
|
||||||
|
import type { ISecurityHandler, ISmtpServer } from './interfaces.js';
|
||||||
|
/**
|
||||||
|
* Handles security aspects for SMTP server
|
||||||
|
*/
|
||||||
|
export declare class SecurityHandler implements ISecurityHandler {
|
||||||
|
/**
|
||||||
|
* Reference to the SMTP server instance
|
||||||
|
*/
|
||||||
|
private smtpServer;
|
||||||
|
/**
|
||||||
|
* IP reputation checker service
|
||||||
|
*/
|
||||||
|
private ipReputationService;
|
||||||
|
/**
|
||||||
|
* Simple in-memory IP denylist
|
||||||
|
*/
|
||||||
|
private ipDenylist;
|
||||||
|
/**
|
||||||
|
* Cleanup interval timer
|
||||||
|
*/
|
||||||
|
private cleanupInterval;
|
||||||
|
/**
|
||||||
|
* Creates a new security handler
|
||||||
|
* @param smtpServer - SMTP server instance
|
||||||
|
*/
|
||||||
|
constructor(smtpServer: ISmtpServer);
|
||||||
|
/**
|
||||||
|
* Check IP reputation for a connection
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @returns Promise that resolves to true if IP is allowed, false if blocked
|
||||||
|
*/
|
||||||
|
checkIpReputation(socket: plugins.net.Socket | plugins.tls.TLSSocket): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* Validate an email address
|
||||||
|
* @param email - Email address to validate
|
||||||
|
* @returns Whether the email address is valid
|
||||||
|
*/
|
||||||
|
isValidEmail(email: string): boolean;
|
||||||
|
/**
|
||||||
|
* Validate authentication credentials
|
||||||
|
* @param auth - Authentication credentials
|
||||||
|
* @returns Promise that resolves to true if authenticated
|
||||||
|
*/
|
||||||
|
authenticate(auth: ISmtpAuth): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* Log a security event
|
||||||
|
* @param event - Event type
|
||||||
|
* @param level - Log level
|
||||||
|
* @param details - Event details
|
||||||
|
*/
|
||||||
|
logSecurityEvent(event: string, level: string, message: string, details: Record<string, any>): void;
|
||||||
|
/**
|
||||||
|
* Add an IP to the denylist
|
||||||
|
* @param ip - IP address
|
||||||
|
* @param reason - Reason for denylisting
|
||||||
|
* @param duration - Duration in milliseconds (optional, indefinite if not specified)
|
||||||
|
*/
|
||||||
|
private addToDenylist;
|
||||||
|
/**
|
||||||
|
* Check if an IP is denylisted
|
||||||
|
* @param ip - IP address
|
||||||
|
* @returns Whether the IP is denylisted
|
||||||
|
*/
|
||||||
|
private isIpDenylisted;
|
||||||
|
/**
|
||||||
|
* Get the reason an IP was denylisted
|
||||||
|
* @param ip - IP address
|
||||||
|
* @returns Reason for denylisting or undefined if not denylisted
|
||||||
|
*/
|
||||||
|
private getDenylistReason;
|
||||||
|
/**
|
||||||
|
* Clean expired denylist entries
|
||||||
|
*/
|
||||||
|
private cleanExpiredDenylistEntries;
|
||||||
|
/**
|
||||||
|
* Clean up resources
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
242
dist_ts/mail/delivery/smtpserver/security-handler.js
Normal file
242
dist_ts/mail/delivery/smtpserver/security-handler.js
Normal file
File diff suppressed because one or more lines are too long
140
dist_ts/mail/delivery/smtpserver/session-manager.d.ts
vendored
Normal file
140
dist_ts/mail/delivery/smtpserver/session-manager.d.ts
vendored
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Session Manager
|
||||||
|
* Responsible for creating, managing, and cleaning up SMTP sessions
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../plugins.js';
|
||||||
|
import { SmtpState } from './interfaces.js';
|
||||||
|
import type { ISmtpSession } from './interfaces.js';
|
||||||
|
import type { ISessionManager, ISessionEvents } from './interfaces.js';
|
||||||
|
/**
|
||||||
|
* Manager for SMTP sessions
|
||||||
|
* Handles session creation, tracking, timeout management, and cleanup
|
||||||
|
*/
|
||||||
|
export declare class SessionManager implements ISessionManager {
|
||||||
|
/**
|
||||||
|
* Map of socket ID to session
|
||||||
|
*/
|
||||||
|
private sessions;
|
||||||
|
/**
|
||||||
|
* Map of socket to socket ID
|
||||||
|
*/
|
||||||
|
private socketIds;
|
||||||
|
/**
|
||||||
|
* SMTP server options
|
||||||
|
*/
|
||||||
|
private options;
|
||||||
|
/**
|
||||||
|
* Event listeners
|
||||||
|
*/
|
||||||
|
private eventListeners;
|
||||||
|
/**
|
||||||
|
* Timer for cleanup interval
|
||||||
|
*/
|
||||||
|
private cleanupTimer;
|
||||||
|
/**
|
||||||
|
* Creates a new session manager
|
||||||
|
* @param options - Session manager options
|
||||||
|
*/
|
||||||
|
constructor(options?: {
|
||||||
|
socketTimeout?: number;
|
||||||
|
connectionTimeout?: number;
|
||||||
|
cleanupInterval?: number;
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* Creates a new session for a socket connection
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param secure - Whether the connection is secure (TLS)
|
||||||
|
* @returns New SMTP session
|
||||||
|
*/
|
||||||
|
createSession(socket: plugins.net.Socket | plugins.tls.TLSSocket, secure: boolean): ISmtpSession;
|
||||||
|
/**
|
||||||
|
* Updates the session state
|
||||||
|
* @param session - SMTP session
|
||||||
|
* @param newState - New state
|
||||||
|
*/
|
||||||
|
updateSessionState(session: ISmtpSession, newState: SmtpState): void;
|
||||||
|
/**
|
||||||
|
* Updates the session's last activity timestamp
|
||||||
|
* @param session - SMTP session
|
||||||
|
*/
|
||||||
|
updateSessionActivity(session: ISmtpSession): void;
|
||||||
|
/**
|
||||||
|
* Removes a session
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
removeSession(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||||
|
/**
|
||||||
|
* Gets a session for a socket
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @returns SMTP session or undefined if not found
|
||||||
|
*/
|
||||||
|
getSession(socket: plugins.net.Socket | plugins.tls.TLSSocket): ISmtpSession | undefined;
|
||||||
|
/**
|
||||||
|
* Cleans up idle sessions
|
||||||
|
*/
|
||||||
|
cleanupIdleSessions(): void;
|
||||||
|
/**
|
||||||
|
* Gets the current number of active sessions
|
||||||
|
* @returns Number of active sessions
|
||||||
|
*/
|
||||||
|
getSessionCount(): number;
|
||||||
|
/**
|
||||||
|
* Clears all sessions (used when shutting down)
|
||||||
|
*/
|
||||||
|
clearAllSessions(): void;
|
||||||
|
/**
|
||||||
|
* Register an event listener
|
||||||
|
* @param event - Event name
|
||||||
|
* @param listener - Event listener function
|
||||||
|
*/
|
||||||
|
on<K extends keyof ISessionEvents>(event: K, listener: ISessionEvents[K]): void;
|
||||||
|
/**
|
||||||
|
* Remove an event listener
|
||||||
|
* @param event - Event name
|
||||||
|
* @param listener - Event listener function
|
||||||
|
*/
|
||||||
|
off<K extends keyof ISessionEvents>(event: K, listener: ISessionEvents[K]): void;
|
||||||
|
/**
|
||||||
|
* Emit an event to registered listeners
|
||||||
|
* @param event - Event name
|
||||||
|
* @param args - Event arguments
|
||||||
|
*/
|
||||||
|
private emitEvent;
|
||||||
|
/**
|
||||||
|
* Start the cleanup timer
|
||||||
|
*/
|
||||||
|
private startCleanupTimer;
|
||||||
|
/**
|
||||||
|
* Stop the cleanup timer
|
||||||
|
*/
|
||||||
|
private stopCleanupTimer;
|
||||||
|
/**
|
||||||
|
* Replace socket mapping for STARTTLS upgrades
|
||||||
|
* @param oldSocket - Original plain socket
|
||||||
|
* @param newSocket - New TLS socket
|
||||||
|
* @returns Whether the replacement was successful
|
||||||
|
*/
|
||||||
|
replaceSocket(oldSocket: plugins.net.Socket | plugins.tls.TLSSocket, newSocket: plugins.net.Socket | plugins.tls.TLSSocket): boolean;
|
||||||
|
/**
|
||||||
|
* Gets a unique key for a socket
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @returns Socket key
|
||||||
|
*/
|
||||||
|
private getSocketKey;
|
||||||
|
/**
|
||||||
|
* Get all active sessions
|
||||||
|
*/
|
||||||
|
getAllSessions(): ISmtpSession[];
|
||||||
|
/**
|
||||||
|
* Update last activity for a session by socket
|
||||||
|
*/
|
||||||
|
updateLastActivity(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||||
|
/**
|
||||||
|
* Check for timed out sessions
|
||||||
|
*/
|
||||||
|
checkTimeouts(timeoutMs: number): ISmtpSession[];
|
||||||
|
/**
|
||||||
|
* Clean up resources
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
473
dist_ts/mail/delivery/smtpserver/session-manager.js
Normal file
473
dist_ts/mail/delivery/smtpserver/session-manager.js
Normal file
File diff suppressed because one or more lines are too long
137
dist_ts/mail/delivery/smtpserver/smtp-server.d.ts
vendored
Normal file
137
dist_ts/mail/delivery/smtpserver/smtp-server.d.ts
vendored
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
/**
|
||||||
|
* SMTP Server
|
||||||
|
* Core implementation for the refactored SMTP server
|
||||||
|
*/
|
||||||
|
import type { ISmtpServerOptions } from './interfaces.js';
|
||||||
|
import type { ISmtpServer, ISmtpServerConfig, ISessionManager, IConnectionManager, ICommandHandler, IDataHandler, ITlsHandler, ISecurityHandler } from './interfaces.js';
|
||||||
|
import { UnifiedEmailServer } from '../../routing/classes.unified.email.server.js';
|
||||||
|
/**
|
||||||
|
* SMTP Server implementation
|
||||||
|
* The main server class that coordinates all components
|
||||||
|
*/
|
||||||
|
export declare class SmtpServer implements ISmtpServer {
|
||||||
|
/**
|
||||||
|
* Email server reference
|
||||||
|
*/
|
||||||
|
private emailServer;
|
||||||
|
/**
|
||||||
|
* Session manager
|
||||||
|
*/
|
||||||
|
private sessionManager;
|
||||||
|
/**
|
||||||
|
* Connection manager
|
||||||
|
*/
|
||||||
|
private connectionManager;
|
||||||
|
/**
|
||||||
|
* Command handler
|
||||||
|
*/
|
||||||
|
private commandHandler;
|
||||||
|
/**
|
||||||
|
* Data handler
|
||||||
|
*/
|
||||||
|
private dataHandler;
|
||||||
|
/**
|
||||||
|
* TLS handler
|
||||||
|
*/
|
||||||
|
private tlsHandler;
|
||||||
|
/**
|
||||||
|
* Security handler
|
||||||
|
*/
|
||||||
|
private securityHandler;
|
||||||
|
/**
|
||||||
|
* SMTP server options
|
||||||
|
*/
|
||||||
|
private options;
|
||||||
|
/**
|
||||||
|
* Net server instance
|
||||||
|
*/
|
||||||
|
private server;
|
||||||
|
/**
|
||||||
|
* Secure server instance
|
||||||
|
*/
|
||||||
|
private secureServer;
|
||||||
|
/**
|
||||||
|
* Whether the server is running
|
||||||
|
*/
|
||||||
|
private running;
|
||||||
|
/**
|
||||||
|
* Server recovery state
|
||||||
|
*/
|
||||||
|
private recoveryState;
|
||||||
|
/**
|
||||||
|
* Creates a new SMTP server
|
||||||
|
* @param config - Server configuration
|
||||||
|
*/
|
||||||
|
constructor(config: ISmtpServerConfig);
|
||||||
|
/**
|
||||||
|
* Start the SMTP server
|
||||||
|
* @returns Promise that resolves when server is started
|
||||||
|
*/
|
||||||
|
listen(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Stop the SMTP server
|
||||||
|
* @returns Promise that resolves when server is stopped
|
||||||
|
*/
|
||||||
|
close(): Promise<void>;
|
||||||
|
/**
|
||||||
|
* Get the session manager
|
||||||
|
* @returns Session manager instance
|
||||||
|
*/
|
||||||
|
getSessionManager(): ISessionManager;
|
||||||
|
/**
|
||||||
|
* Get the connection manager
|
||||||
|
* @returns Connection manager instance
|
||||||
|
*/
|
||||||
|
getConnectionManager(): IConnectionManager;
|
||||||
|
/**
|
||||||
|
* Get the command handler
|
||||||
|
* @returns Command handler instance
|
||||||
|
*/
|
||||||
|
getCommandHandler(): ICommandHandler;
|
||||||
|
/**
|
||||||
|
* Get the data handler
|
||||||
|
* @returns Data handler instance
|
||||||
|
*/
|
||||||
|
getDataHandler(): IDataHandler;
|
||||||
|
/**
|
||||||
|
* Get the TLS handler
|
||||||
|
* @returns TLS handler instance
|
||||||
|
*/
|
||||||
|
getTlsHandler(): ITlsHandler;
|
||||||
|
/**
|
||||||
|
* Get the security handler
|
||||||
|
* @returns Security handler instance
|
||||||
|
*/
|
||||||
|
getSecurityHandler(): ISecurityHandler;
|
||||||
|
/**
|
||||||
|
* Get the server options
|
||||||
|
* @returns SMTP server options
|
||||||
|
*/
|
||||||
|
getOptions(): ISmtpServerOptions;
|
||||||
|
/**
|
||||||
|
* Get the email server reference
|
||||||
|
* @returns Email server instance
|
||||||
|
*/
|
||||||
|
getEmailServer(): UnifiedEmailServer;
|
||||||
|
/**
|
||||||
|
* Check if the server is running
|
||||||
|
* @returns Whether the server is running
|
||||||
|
*/
|
||||||
|
isRunning(): boolean;
|
||||||
|
/**
|
||||||
|
* Check if we should attempt to recover from an error
|
||||||
|
* @param error - The error that occurred
|
||||||
|
* @returns Whether recovery should be attempted
|
||||||
|
*/
|
||||||
|
private shouldAttemptRecovery;
|
||||||
|
/**
|
||||||
|
* Attempt to recover the server after a critical error
|
||||||
|
* @param serverType - The type of server to recover ('standard' or 'secure')
|
||||||
|
* @param error - The error that triggered recovery
|
||||||
|
*/
|
||||||
|
private attemptServerRecovery;
|
||||||
|
/**
|
||||||
|
* Clean up all component resources
|
||||||
|
*/
|
||||||
|
destroy(): Promise<void>;
|
||||||
|
}
|
||||||
698
dist_ts/mail/delivery/smtpserver/smtp-server.js
Normal file
698
dist_ts/mail/delivery/smtpserver/smtp-server.js
Normal file
File diff suppressed because one or more lines are too long
21
dist_ts/mail/delivery/smtpserver/starttls-handler.d.ts
vendored
Normal file
21
dist_ts/mail/delivery/smtpserver/starttls-handler.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* STARTTLS Implementation
|
||||||
|
* Provides an improved implementation for STARTTLS upgrades
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../plugins.js';
|
||||||
|
import type { ISmtpSession, ISessionManager, IConnectionManager } from './interfaces.js';
|
||||||
|
import { SmtpState } from '../interfaces.js';
|
||||||
|
/**
|
||||||
|
* Enhanced STARTTLS handler for more reliable TLS upgrades
|
||||||
|
*/
|
||||||
|
export declare function performStartTLS(socket: plugins.net.Socket, options: {
|
||||||
|
key: string;
|
||||||
|
cert: string;
|
||||||
|
ca?: string;
|
||||||
|
session?: ISmtpSession;
|
||||||
|
sessionManager?: ISessionManager;
|
||||||
|
connectionManager?: IConnectionManager;
|
||||||
|
onSuccess?: (tlsSocket: plugins.tls.TLSSocket) => void;
|
||||||
|
onFailure?: (error: Error) => void;
|
||||||
|
updateSessionState?: (session: ISmtpSession, state: SmtpState) => void;
|
||||||
|
}): Promise<plugins.tls.TLSSocket | undefined>;
|
||||||
207
dist_ts/mail/delivery/smtpserver/starttls-handler.js
Normal file
207
dist_ts/mail/delivery/smtpserver/starttls-handler.js
Normal file
File diff suppressed because one or more lines are too long
66
dist_ts/mail/delivery/smtpserver/tls-handler.d.ts
vendored
Normal file
66
dist_ts/mail/delivery/smtpserver/tls-handler.d.ts
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* SMTP TLS Handler
|
||||||
|
* Responsible for handling TLS-related SMTP functionality
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../plugins.js';
|
||||||
|
import type { ITlsHandler, ISmtpServer, ISmtpSession } from './interfaces.js';
|
||||||
|
/**
|
||||||
|
* Handles TLS functionality for SMTP server
|
||||||
|
*/
|
||||||
|
export declare class TlsHandler implements ITlsHandler {
|
||||||
|
/**
|
||||||
|
* Reference to the SMTP server instance
|
||||||
|
*/
|
||||||
|
private smtpServer;
|
||||||
|
/**
|
||||||
|
* Certificate data
|
||||||
|
*/
|
||||||
|
private certificates;
|
||||||
|
/**
|
||||||
|
* TLS options
|
||||||
|
*/
|
||||||
|
private options;
|
||||||
|
/**
|
||||||
|
* Creates a new TLS handler
|
||||||
|
* @param smtpServer - SMTP server instance
|
||||||
|
*/
|
||||||
|
constructor(smtpServer: ISmtpServer);
|
||||||
|
/**
|
||||||
|
* Handle STARTTLS command
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
handleStartTls(socket: plugins.net.Socket, session: ISmtpSession): Promise<plugins.tls.TLSSocket | null>;
|
||||||
|
/**
|
||||||
|
* Upgrade a connection to TLS
|
||||||
|
* @param socket - Client socket
|
||||||
|
*/
|
||||||
|
startTLS(socket: plugins.net.Socket): Promise<plugins.tls.TLSSocket>;
|
||||||
|
/**
|
||||||
|
* Create a secure server
|
||||||
|
* @returns TLS server instance or undefined if TLS is not enabled
|
||||||
|
*/
|
||||||
|
createSecureServer(): plugins.tls.Server | undefined;
|
||||||
|
/**
|
||||||
|
* Check if TLS is enabled
|
||||||
|
* @returns Whether TLS is enabled
|
||||||
|
*/
|
||||||
|
isTlsEnabled(): boolean;
|
||||||
|
/**
|
||||||
|
* Send a response to the client
|
||||||
|
* @param socket - Client socket
|
||||||
|
* @param response - Response message
|
||||||
|
*/
|
||||||
|
private sendResponse;
|
||||||
|
/**
|
||||||
|
* Check if TLS is available (interface requirement)
|
||||||
|
*/
|
||||||
|
isTlsAvailable(): boolean;
|
||||||
|
/**
|
||||||
|
* Get TLS options (interface requirement)
|
||||||
|
*/
|
||||||
|
getTlsOptions(): plugins.tls.TlsOptions;
|
||||||
|
/**
|
||||||
|
* Clean up resources
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
273
dist_ts/mail/delivery/smtpserver/tls-handler.js
Normal file
273
dist_ts/mail/delivery/smtpserver/tls-handler.js
Normal file
File diff suppressed because one or more lines are too long
117
dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.d.ts
vendored
Normal file
117
dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.d.ts
vendored
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* Adaptive SMTP Logging System
|
||||||
|
* Automatically switches between logging modes based on server load (active connections)
|
||||||
|
* to maintain performance during high-concurrency scenarios
|
||||||
|
*/
|
||||||
|
import * as plugins from '../../../../plugins.js';
|
||||||
|
import { SecurityLogLevel, SecurityEventType } from '../constants.js';
|
||||||
|
import type { ISmtpSession } from '../interfaces.js';
|
||||||
|
import type { LogLevel, ISmtpLogOptions } from './logging.js';
|
||||||
|
/**
|
||||||
|
* Log modes based on server load
|
||||||
|
*/
|
||||||
|
export declare enum LogMode {
|
||||||
|
VERBOSE = "VERBOSE",// < 20 connections: Full detailed logging
|
||||||
|
REDUCED = "REDUCED",// 20-40 connections: Limited command/response logging, full error logging
|
||||||
|
MINIMAL = "MINIMAL"
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Configuration for adaptive logging thresholds
|
||||||
|
*/
|
||||||
|
export interface IAdaptiveLogConfig {
|
||||||
|
verboseThreshold: number;
|
||||||
|
reducedThreshold: number;
|
||||||
|
aggregationInterval: number;
|
||||||
|
maxAggregatedEntries: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Connection metadata for aggregation tracking
|
||||||
|
*/
|
||||||
|
interface IConnectionTracker {
|
||||||
|
activeConnections: number;
|
||||||
|
peakConnections: number;
|
||||||
|
totalConnections: number;
|
||||||
|
connectionsPerSecond: number;
|
||||||
|
lastConnectionTime: number;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Adaptive SMTP Logger that scales logging based on server load
|
||||||
|
*/
|
||||||
|
export declare class AdaptiveSmtpLogger {
|
||||||
|
private static instance;
|
||||||
|
private currentMode;
|
||||||
|
private config;
|
||||||
|
private aggregatedEntries;
|
||||||
|
private aggregationTimer;
|
||||||
|
private connectionTracker;
|
||||||
|
private constructor();
|
||||||
|
/**
|
||||||
|
* Get singleton instance
|
||||||
|
*/
|
||||||
|
static getInstance(config?: Partial<IAdaptiveLogConfig>): AdaptiveSmtpLogger;
|
||||||
|
/**
|
||||||
|
* Update active connection count and adjust log mode if needed
|
||||||
|
*/
|
||||||
|
updateConnectionCount(activeConnections: number): void;
|
||||||
|
/**
|
||||||
|
* Track new connection for rate calculation
|
||||||
|
*/
|
||||||
|
trackConnection(): void;
|
||||||
|
/**
|
||||||
|
* Get current logging mode
|
||||||
|
*/
|
||||||
|
getCurrentMode(): LogMode;
|
||||||
|
/**
|
||||||
|
* Get connection statistics
|
||||||
|
*/
|
||||||
|
getConnectionStats(): IConnectionTracker;
|
||||||
|
/**
|
||||||
|
* Log a message with adaptive behavior
|
||||||
|
*/
|
||||||
|
log(level: LogLevel, message: string, options?: ISmtpLogOptions): void;
|
||||||
|
/**
|
||||||
|
* Log command with adaptive behavior
|
||||||
|
*/
|
||||||
|
logCommand(command: string, socket: plugins.net.Socket | plugins.tls.TLSSocket, session?: ISmtpSession): void;
|
||||||
|
/**
|
||||||
|
* Log response with adaptive behavior
|
||||||
|
*/
|
||||||
|
logResponse(response: string, socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||||
|
/**
|
||||||
|
* Log connection event with adaptive behavior
|
||||||
|
*/
|
||||||
|
logConnection(socket: plugins.net.Socket | plugins.tls.TLSSocket, eventType: 'connect' | 'close' | 'error', session?: ISmtpSession, error?: Error): void;
|
||||||
|
/**
|
||||||
|
* Log security event (always logged regardless of mode)
|
||||||
|
*/
|
||||||
|
logSecurityEvent(level: SecurityLogLevel, type: SecurityEventType, message: string, details: Record<string, any>, ipAddress?: string, domain?: string, success?: boolean): void;
|
||||||
|
/**
|
||||||
|
* Determine appropriate log mode based on connection count
|
||||||
|
*/
|
||||||
|
private determineLogMode;
|
||||||
|
/**
|
||||||
|
* Switch to a new log mode
|
||||||
|
*/
|
||||||
|
private switchLogMode;
|
||||||
|
/**
|
||||||
|
* Add entry to aggregation buffer
|
||||||
|
*/
|
||||||
|
private aggregateEntry;
|
||||||
|
/**
|
||||||
|
* Start the aggregation timer
|
||||||
|
*/
|
||||||
|
private startAggregationTimer;
|
||||||
|
/**
|
||||||
|
* Flush aggregated entries to logs
|
||||||
|
*/
|
||||||
|
private flushAggregatedEntries;
|
||||||
|
/**
|
||||||
|
* Cleanup resources
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Default instance for easy access
|
||||||
|
*/
|
||||||
|
export declare const adaptiveLogger: AdaptiveSmtpLogger;
|
||||||
|
export {};
|
||||||
413
dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.js
Normal file
413
dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.js
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user