Implement Metrics Manager and Integrate Metrics Collection

- Removed the existing readme.opsserver.md file as it is no longer needed.
- Added a new MetricsManager class to handle metrics collection using @push.rocks/smartmetrics.
- Integrated MetricsManager into the DcRouter and OpsServer classes.
- Updated StatsHandler and SecurityHandler to retrieve metrics from MetricsManager.
- Implemented methods for tracking email, DNS, and security metrics.
- Added connection tracking capabilities to the MetricsManager.
- Created a new readme.metrics.md file outlining the metrics implementation plan.
- Adjusted plugins.ts to include smartmetrics.
- Added a new monitoring directory with classes for metrics management.
- Created readme.module-adjustments.md to document necessary adjustments for SmartProxy and SmartDNS.
This commit is contained in:
2025-06-09 16:03:27 +00:00
parent 554d245c0c
commit 93995d5031
11 changed files with 826 additions and 384 deletions

View File

@ -41,10 +41,11 @@
"@push.rocks/smartjwt": "^2.2.1", "@push.rocks/smartjwt": "^2.2.1",
"@push.rocks/smartlog": "^3.1.8", "@push.rocks/smartlog": "^3.1.8",
"@push.rocks/smartmail": "^2.1.0", "@push.rocks/smartmail": "^2.1.0",
"@push.rocks/smartmetrics": "^2.0.9",
"@push.rocks/smartnetwork": "^4.0.2", "@push.rocks/smartnetwork": "^4.0.2",
"@push.rocks/smartpath": "^5.0.5", "@push.rocks/smartpath": "^5.0.5",
"@push.rocks/smartpromise": "^4.0.3", "@push.rocks/smartpromise": "^4.0.3",
"@push.rocks/smartproxy": "^19.5.26", "@push.rocks/smartproxy": "^19.6.0",
"@push.rocks/smartrequest": "^2.1.0", "@push.rocks/smartrequest": "^2.1.0",
"@push.rocks/smartrule": "^2.0.1", "@push.rocks/smartrule": "^2.0.1",
"@push.rocks/smartrx": "^3.0.10", "@push.rocks/smartrx": "^3.0.10",

77
pnpm-lock.yaml generated
View File

@ -59,6 +59,9 @@ importers:
'@push.rocks/smartmail': '@push.rocks/smartmail':
specifier: ^2.1.0 specifier: ^2.1.0
version: 2.1.0 version: 2.1.0
'@push.rocks/smartmetrics':
specifier: ^2.0.9
version: 2.0.9
'@push.rocks/smartnetwork': '@push.rocks/smartnetwork':
specifier: ^4.0.2 specifier: ^4.0.2
version: 4.0.2 version: 4.0.2
@ -69,8 +72,8 @@ importers:
specifier: ^4.0.3 specifier: ^4.0.3
version: 4.2.3 version: 4.2.3
'@push.rocks/smartproxy': '@push.rocks/smartproxy':
specifier: ^19.5.26 specifier: ^19.6.0
version: 19.5.26(@aws-sdk/credential-providers@3.817.0)(socks@2.8.4) version: 19.6.0(@aws-sdk/credential-providers@3.817.0)(socks@2.8.4)
'@push.rocks/smartrequest': '@push.rocks/smartrequest':
specifier: ^2.1.0 specifier: ^2.1.0
version: 2.1.0 version: 2.1.0
@ -814,6 +817,10 @@ packages:
resolution: {integrity: sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==} resolution: {integrity: sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==}
engines: {node: '>=8.0'} engines: {node: '>=8.0'}
'@opentelemetry/api@1.9.0':
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
'@pdf-lib/standard-fonts@1.0.0': '@pdf-lib/standard-fonts@1.0.0':
resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==} resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==}
@ -1010,6 +1017,9 @@ packages:
'@push.rocks/smartmatch@2.0.0': '@push.rocks/smartmatch@2.0.0':
resolution: {integrity: sha512-MBzP++1yNIBeox71X6VxpIgZ8m4bXnJpZJ4nWVH6IWpmO38MXTu4X0QF8tQnyT4LFcwvc9iiWaD15cstHa7Mmw==} resolution: {integrity: sha512-MBzP++1yNIBeox71X6VxpIgZ8m4bXnJpZJ4nWVH6IWpmO38MXTu4X0QF8tQnyT4LFcwvc9iiWaD15cstHa7Mmw==}
'@push.rocks/smartmetrics@2.0.9':
resolution: {integrity: sha512-3rKHItgIL4Pt/kFH12qCmTxsnB2EcBgO6H0MYe9dPjDI7yl7IDiLzZg2Abp4q9LEPVw009ATJsxklPQSNqI5gQ==}
'@push.rocks/smartmime@1.0.6': '@push.rocks/smartmime@1.0.6':
resolution: {integrity: sha512-PHd+I4UcsnOATNg8wjDsSAmmJ4CwQFrQCNzd0HSJMs4ZpiK3Ya91almd6GLpDPU370U4HFh4FaPF4eEAI6vkJQ==} resolution: {integrity: sha512-PHd+I4UcsnOATNg8wjDsSAmmJ4CwQFrQCNzd0HSJMs4ZpiK3Ya91almd6GLpDPU370U4HFh4FaPF4eEAI6vkJQ==}
@ -1052,8 +1062,8 @@ packages:
'@push.rocks/smartpromise@4.2.3': '@push.rocks/smartpromise@4.2.3':
resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==} resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==}
'@push.rocks/smartproxy@19.5.26': '@push.rocks/smartproxy@19.6.0':
resolution: {integrity: sha512-hCQcIZWX6wjKuom7HYYeGhuRijgU+R958WACDauKfQEDTTzF00STH4GB5KcL7kbaF+20Tx5IBR7pvzHTRjJmQg==} resolution: {integrity: sha512-qMayuTkKpgQM14Z2cksjG8NmF6KLogcmXQIKUSFx/W7kKoGSkZ+/kYbnngDpc87n86nathr0p2H2euUt0lHfRQ==}
'@push.rocks/smartpuppeteer@2.0.5': '@push.rocks/smartpuppeteer@2.0.5':
resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==} resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==}
@ -1654,6 +1664,9 @@ packages:
'@types/node@22.15.30': '@types/node@22.15.30':
resolution: {integrity: sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==} resolution: {integrity: sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==}
'@types/pidusage@2.0.5':
resolution: {integrity: sha512-MIiyZI4/MK9UGUXWt0jJcCZhVw7YdhBuTOuqP/BjuLDLZ2PmmViMIQgZiWxtaMicQfAz/kMrZ5T7PKxFSkTeUA==}
'@types/ping@0.4.4': '@types/ping@0.4.4':
resolution: {integrity: sha512-ifvo6w2f5eJYlXm+HiVx67iJe8WZp87sfa683nlqED5Vnt9Z93onkokNoWqOG21EaE8fMxyKPobE+mkPEyxsdw==} resolution: {integrity: sha512-ifvo6w2f5eJYlXm+HiVx67iJe8WZp87sfa683nlqED5Vnt9Z93onkokNoWqOG21EaE8fMxyKPobE+mkPEyxsdw==}
@ -1865,6 +1878,9 @@ packages:
resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
bintrees@1.0.2:
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
bn.js@4.12.2: bn.js@4.12.2:
resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==} resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==}
@ -3615,6 +3631,15 @@ packages:
picocolors@1.1.1: picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
pidtree@0.6.0:
resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
engines: {node: '>=0.10'}
hasBin: true
pidusage@4.0.1:
resolution: {integrity: sha512-yCH2dtLHfEBnzlHUJymR/Z1nN2ePG3m392Mv8TFlTP1B0xkpMQNHAnfkY0n2tAi6ceKO6YWhxYfZ96V4vVkh/g==}
engines: {node: '>=18'}
ping@0.4.4: ping@0.4.4:
resolution: {integrity: sha512-56ZMC0j7SCsMMLdOoUg12VZCfj/+ZO+yfOSjaNCRrmZZr6GLbN2X/Ui56T15dI8NhiHckaw5X2pvyfAomanwqQ==} resolution: {integrity: sha512-56ZMC0j7SCsMMLdOoUg12VZCfj/+ZO+yfOSjaNCRrmZZr6GLbN2X/Ui56T15dI8NhiHckaw5X2pvyfAomanwqQ==}
engines: {node: '>=4.0.0'} engines: {node: '>=4.0.0'}
@ -3642,6 +3667,10 @@ packages:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
prom-client@15.1.3:
resolution: {integrity: sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==}
engines: {node: ^16 || ^18 || >=20}
property-information@7.1.0: property-information@7.1.0:
resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
@ -4031,6 +4060,9 @@ packages:
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
engines: {node: '>=10'} engines: {node: '>=10'}
tdigest@0.1.2:
resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==}
text-decoder@1.2.3: text-decoder@1.2.3:
resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==}
@ -5408,8 +5440,10 @@ snapshots:
'@push.rocks/taskbuffer': 3.1.7 '@push.rocks/taskbuffer': 3.1.7
transitivePeerDependencies: transitivePeerDependencies:
- '@nuxt/kit' - '@nuxt/kit'
- bufferutil
- react - react
- supports-color - supports-color
- utf-8-validate
- vue - vue
'@hapi/hoek@9.3.0': {} '@hapi/hoek@9.3.0': {}
@ -5516,6 +5550,8 @@ snapshots:
'@oozcitak/util@8.3.8': {} '@oozcitak/util@8.3.8': {}
'@opentelemetry/api@1.9.0': {}
'@pdf-lib/standard-fonts@1.0.0': '@pdf-lib/standard-fonts@1.0.0':
dependencies: dependencies:
pako: 1.0.11 pako: 1.0.11
@ -5750,7 +5786,6 @@ snapshots:
- '@mongodb-js/zstd' - '@mongodb-js/zstd'
- '@nuxt/kit' - '@nuxt/kit'
- aws-crt - aws-crt
- bufferutil
- encoding - encoding
- gcp-metadata - gcp-metadata
- kerberos - kerberos
@ -5759,7 +5794,6 @@ snapshots:
- snappy - snappy
- socks - socks
- supports-color - supports-color
- utf-8-validate
- vue - vue
'@push.rocks/smartarchive@3.0.8': '@push.rocks/smartarchive@3.0.8':
@ -6060,6 +6094,15 @@ snapshots:
dependencies: dependencies:
matcher: 5.0.0 matcher: 5.0.0
'@push.rocks/smartmetrics@2.0.9':
dependencies:
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartlog': 3.1.8
'@types/pidusage': 2.0.5
pidtree: 0.6.0
pidusage: 4.0.1
prom-client: 15.1.3
'@push.rocks/smartmime@1.0.6': '@push.rocks/smartmime@1.0.6':
dependencies: dependencies:
'@types/mime-types': 2.1.4 '@types/mime-types': 2.1.4
@ -6180,7 +6223,7 @@ snapshots:
'@push.rocks/smartpromise@4.2.3': {} '@push.rocks/smartpromise@4.2.3': {}
'@push.rocks/smartproxy@19.5.26(@aws-sdk/credential-providers@3.817.0)(socks@2.8.4)': '@push.rocks/smartproxy@19.6.0(@aws-sdk/credential-providers@3.817.0)(socks@2.8.4)':
dependencies: dependencies:
'@push.rocks/lik': 6.2.2 '@push.rocks/lik': 6.2.2
'@push.rocks/smartacme': 8.0.0(@aws-sdk/credential-providers@3.817.0)(socks@2.8.4) '@push.rocks/smartacme': 8.0.0(@aws-sdk/credential-providers@3.817.0)(socks@2.8.4)
@ -6191,6 +6234,7 @@ snapshots:
'@push.rocks/smartnetwork': 4.0.2 '@push.rocks/smartnetwork': 4.0.2
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 2.1.0 '@push.rocks/smartrequest': 2.1.0
'@push.rocks/smartrx': 3.0.10
'@push.rocks/smartstring': 4.0.15 '@push.rocks/smartstring': 4.0.15
'@push.rocks/taskbuffer': 3.1.7 '@push.rocks/taskbuffer': 3.1.7
'@tsclass/tsclass': 9.2.0 '@tsclass/tsclass': 9.2.0
@ -7147,6 +7191,8 @@ snapshots:
dependencies: dependencies:
undici-types: 6.21.0 undici-types: 6.21.0
'@types/pidusage@2.0.5': {}
'@types/ping@0.4.4': {} '@types/ping@0.4.4': {}
'@types/qs@6.14.0': {} '@types/qs@6.14.0': {}
@ -7352,6 +7398,8 @@ snapshots:
basic-ftp@5.0.5: {} basic-ftp@5.0.5: {}
bintrees@1.0.2: {}
bn.js@4.12.2: {} bn.js@4.12.2: {}
body-parser@1.20.3: body-parser@1.20.3:
@ -9441,6 +9489,12 @@ snapshots:
picocolors@1.1.1: {} picocolors@1.1.1: {}
pidtree@0.6.0: {}
pidusage@4.0.1:
dependencies:
safe-buffer: 5.2.1
ping@0.4.4: {} ping@0.4.4: {}
pkg-dir@4.2.0: pkg-dir@4.2.0:
@ -9463,6 +9517,11 @@ snapshots:
progress@2.0.3: {} progress@2.0.3: {}
prom-client@15.1.3:
dependencies:
'@opentelemetry/api': 1.9.0
tdigest: 0.1.2
property-information@7.1.0: {} property-information@7.1.0: {}
proto-list@1.2.4: {} proto-list@1.2.4: {}
@ -9992,6 +10051,10 @@ snapshots:
mkdirp: 1.0.4 mkdirp: 1.0.4
yallist: 4.0.0 yallist: 4.0.0
tdigest@0.1.2:
dependencies:
bintrees: 1.0.2
text-decoder@1.2.3: text-decoder@1.2.3:
dependencies: dependencies:
b4a: 1.6.7 b4a: 1.6.7

202
readme.metrics.md Normal file
View File

@ -0,0 +1,202 @@
# Metrics Implementation Plan with @push.rocks/smartmetrics
Command to reread CLAUDE.md: `cat /home/centraluser/eu.central.ingress-2/CLAUDE.md`
## Overview
This plan outlines the migration from placeholder/demo metrics to real metrics using @push.rocks/smartmetrics for the dcrouter project.
## Current State Analysis
### Currently Implemented (Real Data)
- CPU usage (basic calculation from os.loadavg)
- Memory usage (from process.memoryUsage)
- System uptime
### Currently Stubbed (Returns 0 or Demo Data)
- Active connections (HTTP/HTTPS/WebSocket)
- Total connections
- Requests per second
- Email statistics (sent/received/failed/queued/bounce rate)
- DNS statistics (queries/cache hits/response times)
- Security metrics (blocked IPs/auth failures/spam detection)
## Implementation Plan
### Phase 1: Core Infrastructure Setup
1. **Install Dependencies**
```bash
pnpm install --save @push.rocks/smartmetrics
```
2. **Update plugins.ts**
- Add smartmetrics to ts/plugins.ts
- Import as: `import * as smartmetrics from '@push.rocks/smartmetrics';`
3. **Create Metrics Manager Class**
- Location: `ts/monitoring/classes.metricsmanager.ts`
- Initialize SmartMetrics with existing logger
- Configure for dcrouter service identification
- Set up automatic metric collection intervals
### Phase 2: Connection Tracking Implementation
1. **HTTP/HTTPS Connection Tracking**
- Instrument the SmartProxy connection handlers
- Track active connections in real-time
- Monitor connection lifecycle (open/close events)
- Location: Update connection managers in routing system
2. **Email Connection Tracking**
- Instrument SMTP server connection handlers
- Track both incoming and outgoing connections
- Location: `ts/mail/delivery/smtpserver/connection-manager.ts`
3. **DNS Query Tracking**
- Instrument DNS server handlers
- Track query counts and response times
- Location: `ts/mail/routing/classes.dns.manager.ts`
### Phase 3: Email Metrics Collection
1. **Email Processing Metrics**
- Track sent/received/failed emails
- Monitor queue sizes
- Calculate delivery and bounce rates
- Location: Instrument `classes.delivery.queue.ts` and `classes.emailsendjob.ts`
2. **Email Performance Metrics**
- Track processing times
- Monitor queue throughput
- Location: Update delivery system classes
### Phase 4: Security Metrics Integration
1. **Security Event Tracking**
- Track blocked IPs from IPReputationChecker
- Monitor authentication failures
- Count spam/malware/phishing detections
- Location: Instrument security classes in `ts/security/`
### Phase 5: Stats Handler Refactoring
1. **Update Stats Handler**
- Location: `ts/opsserver/handlers/stats.handler.ts`
- Replace all stub implementations with MetricsManager calls
- Maintain existing API interface structure
2. **Metrics Aggregation**
- Implement proper time-window aggregations
- Add historical data storage (last hour/day)
- Calculate rates and percentages accurately
### Phase 6: Prometheus Integration (Optional Enhancement)
1. **Enable Prometheus Endpoint**
- Add Prometheus metrics endpoint
- Configure port (default: 9090)
- Document metrics for monitoring systems
## Implementation Details
### MetricsManager Core Structure
```typescript
export class MetricsManager {
private smartMetrics: smartmetrics.SmartMetrics;
private connectionTrackers: Map<string, ConnectionTracker>;
private emailMetrics: EmailMetricsCollector;
private dnsMetrics: DnsMetricsCollector;
private securityMetrics: SecurityMetricsCollector;
// Real-time counters
private activeConnections = {
http: 0,
https: 0,
websocket: 0,
smtp: 0
};
// Initialize and start collection
public async start(): Promise<void>;
// Get aggregated metrics for stats handler
public async getServerStats(): Promise<IServerStats>;
public async getEmailStats(): Promise<IEmailStats>;
public async getDnsStats(): Promise<IDnsStats>;
public async getSecurityStats(): Promise<ISecurityStats>;
}
```
### Connection Tracking Pattern
```typescript
// Example for HTTP connections
onConnectionOpen(type: string) {
this.activeConnections[type]++;
this.totalConnections[type]++;
}
onConnectionClose(type: string) {
this.activeConnections[type]--;
}
```
### Email Metrics Pattern
```typescript
// Track email events
onEmailSent() { this.emailsSentToday++; }
onEmailReceived() { this.emailsReceivedToday++; }
onEmailFailed() { this.emailsFailedToday++; }
onEmailQueued() { this.queueSize++; }
onEmailDequeued() { this.queueSize--; }
```
## Testing Strategy
1. **Unit Tests**
- Test MetricsManager initialization
- Test metric collection accuracy
- Test aggregation calculations
2. **Integration Tests**
- Test metrics flow from source to API
- Verify real-time updates
- Test under load conditions
3. **Debug Utilities**
- Create `.nogit/debug/test-metrics.ts` for quick testing
- Add metrics dump endpoint for debugging
## Migration Steps
1. Implement MetricsManager without breaking existing code
2. Wire up one metric type at a time
3. Verify each metric shows real data
4. Remove TODO comments from stats handler
5. Update tests to expect real values
## Success Criteria
- [ ] All metrics show real, accurate data
- [ ] No performance degradation
- [ ] Metrics update in real-time
- [ ] Historical data is collected
- [ ] All TODO comments removed from stats handler
- [ ] Tests pass with real metric values
## Notes
- SmartMetrics provides CPU and memory metrics out of the box
- We'll need custom collectors for application-specific metrics
- Consider adding metric persistence for historical data
- Prometheus integration provides industry-standard monitoring
## Questions to Address
1. Should we persist metrics to disk for historical analysis?
2. What time windows should we support (5min, 1hour, 1day)?
3. Should we add alerting thresholds?
4. Do we need custom metric types beyond the current interface?
---
This plan ensures a systematic migration from demo metrics to real, actionable data using @push.rocks/smartmetrics while maintaining the existing API structure and adding powerful monitoring capabilities.

View File

@ -0,0 +1,173 @@
# Module Adjustments for Metrics Collection
Command to reread CLAUDE.md: `cat /home/centraluser/eu.central.ingress-2/CLAUDE.md`
## SmartProxy Adjustments
### Current State
SmartProxy (@push.rocks/smartproxy) provides:
- Route-level `maxConnections` limiting
- Event emission system (currently only for certificates)
- NFTables integration with packet statistics
- Connection monitoring during active sessions
### Missing Capabilities for Metrics
1. **No Connection Lifecycle Events**
- No `connection-open` or `connection-close` events
- No way to track active connections in real-time
- No exposure of internal connection tracking
2. **No Statistics API**
- No methods like `getActiveConnections()` or `getConnectionStats()`
- No access to connection counts per route
- No throughput or performance metrics exposed
3. **Limited Event System**
- Currently only emits certificate-related events
- No connection, request, or performance events
### Required Adjustments
1. **Add Connection Tracking Events**
```typescript
// Emit on new connection
smartProxy.emit('connection-open', {
type: 'http' | 'https' | 'websocket',
routeName: string,
clientIp: string,
timestamp: Date
});
// Emit on connection close
smartProxy.emit('connection-close', {
connectionId: string,
duration: number,
bytesTransferred: number
});
```
2. **Add Statistics API**
```typescript
interface IProxyStats {
getActiveConnections(): number;
getConnectionsByRoute(): Map<string, number>;
getTotalConnections(): number;
getRequestsPerSecond(): number;
getThroughput(): { bytesIn: number, bytesOut: number };
}
```
3. **Expose Internal Metrics**
- Make connection pools accessible
- Expose route-level statistics
- Provide request/response metrics
### Alternative Approach
Since SmartProxy is already used with socket handlers for email routing, we could:
1. Wrap all SmartProxy socket handlers with a metrics-aware wrapper
2. Use the existing socket-handler pattern to intercept all connections
3. Track connections at the dcrouter level rather than modifying SmartProxy
## SmartDNS Adjustments
### Current State
SmartDNS (@push.rocks/smartdns) provides:
- DNS query handling via registered handlers
- Support for UDP (port 53) and DNS-over-HTTPS
- Domain pattern matching and routing
- DNSSEC support
### Missing Capabilities for Metrics
1. **No Query Tracking**
- No counters for total queries
- No breakdown by query type (A, AAAA, MX, etc.)
- No domain popularity tracking
2. **No Performance Metrics**
- No response time tracking
- No cache hit/miss statistics
- No error rate tracking
3. **No Event Emission**
- No query lifecycle events
- No cache events
- No error events
### Required Adjustments
1. **Add Query Interceptor/Middleware**
```typescript
// Wrap handler registration to add metrics
smartDns.use((query, next) => {
metricsCollector.trackQuery(query);
const startTime = Date.now();
next((response) => {
metricsCollector.trackResponse(response, Date.now() - startTime);
});
});
```
2. **Add Event Emissions**
```typescript
// Query events
smartDns.emit('query-received', {
type: query.type,
domain: query.domain,
source: 'udp' | 'https',
clientIp: string
});
smartDns.emit('query-answered', {
cached: boolean,
responseTime: number,
responseCode: string
});
```
3. **Add Statistics API**
```typescript
interface IDnsStats {
getTotalQueries(): number;
getQueriesPerSecond(): number;
getCacheStats(): { hits: number, misses: number, hitRate: number };
getTopDomains(limit: number): Array<{ domain: string, count: number }>;
getQueryTypeBreakdown(): Record<string, number>;
}
```
### Alternative Approach
Since we control the handler registration in dcrouter:
1. Create a metrics-aware handler wrapper at the dcrouter level
2. Wrap all DNS handlers before registration
3. Track metrics without modifying SmartDNS itself
## Implementation Strategy
### Option 1: Fork and Modify Dependencies
- Fork @push.rocks/smartproxy and @push.rocks/smartdns
- Add metrics capabilities directly
- Maintain custom versions
- **Pros**: Clean integration, full control
- **Cons**: Maintenance burden, divergence from upstream
### Option 2: Wrapper Approach at DcRouter Level
- Create wrapper classes that intercept all operations
- Track metrics at the application level
- No modifications to dependencies
- **Pros**: No dependency modifications, easier to maintain
- **Cons**: May miss some internal events, slightly higher overhead
### Option 3: Contribute Back to Upstream
- Submit PRs to add metrics capabilities to original packages
- Work with maintainers to add event emissions and stats APIs
- **Pros**: Benefits everyone, no fork maintenance
- **Cons**: Slower process, may not align with maintainer vision
## Recommendation
**Use Option 2 (Wrapper Approach)** for immediate implementation:
1. Create `MetricsAwareProxy` and `MetricsAwareDns` wrapper classes
2. Intercept all operations and track metrics
3. Minimal changes to existing codebase
4. Can migrate to Option 3 later if upstream accepts contributions
This approach allows us to implement comprehensive metrics collection without modifying external dependencies, maintaining compatibility and reducing maintenance burden.

View File

@ -1,351 +0,0 @@
# DCRouter OpsServer Implementation Plan
**Command to reread CLAUDE.md: `cat /home/philkunz/.claude/CLAUDE.md`**
## Overview
This document outlines the implementation plan for adding a TypedRequest-based API to the DCRouter OpsServer, following the patterns established in the cloudly project. The goal is to create a type-safe, reactive management dashboard with real-time statistics and monitoring capabilities.
## Architecture Overview
The implementation follows a clear separation of concerns:
- **Backend**: TypedRequest handlers in OpsServer
- **Frontend**: Reactive web components with Smartstate
- **Communication**: Type-safe requests via TypedRequest pattern
- **State Management**: Centralized state with reactive updates
## Implementation Phases
### Phase 1: Interface Definition ✓
Create TypeScript interfaces for all API operations:
#### Directory Structure ✓
```
ts_interfaces/
plugins.ts # TypedRequest interfaces import
data/ # Data type definitions
auth.ts # IIdentity interface
stats.ts # Server, Email, DNS, Security types
index.ts # Exports
requests/ # Request interfaces
admin.ts # Authentication requests
config.ts # Configuration management
logs.ts # Log retrieval with IVirtualStream
stats.ts # Statistics endpoints
index.ts # Exports
```
#### Key Interfaces Defined ✓
- **Server Statistics**
- [x] `IReq_GetServerStatistics` - Server metrics with history
- **Email Operations**
- [x] `IReq_GetEmailStatistics` - Email delivery stats
- [x] `IReq_GetQueueStatus` - Queue monitoring
- **DNS Management**
- [x] `IReq_GetDnsStatistics` - DNS query metrics
- **Rate Limiting**
- [x] `IReq_GetRateLimitStatus` - Rate limit info
- **Security Metrics**
- [x] `IReq_GetSecurityMetrics` - Security stats and trends
- [x] `IReq_GetActiveConnections` - Connection monitoring
- **Logging**
- [x] `IReq_GetRecentLogs` - Paginated log retrieval
- [x] `IReq_GetLogStream` - Real-time log streaming with IVirtualStream
- **Configuration**
- [x] `IReq_GetConfiguration` - Read config
- [x] `IReq_UpdateConfiguration` - Update config
- **Authentication**
- [x] `IReq_AdminLoginWithUsernameAndPassword` - Admin login
- [x] `IReq_AdminLogout` - Logout
- [x] `IReq_VerifyIdentity` - Token verification
- **Health Check**
- [x] `IReq_GetHealthStatus` - Service health monitoring
### Phase 2: Backend Implementation ✓
#### 2.1 Enhance OpsServer (`ts/opsserver/classes.opsserver.ts`) ✓
- [x] Add TypedRouter initialization
- [x] Use TypedServer's built-in typedrouter
- [x] CORS is already handled by TypedServer
- [x] Add handler registration method
```typescript
// Example structure following cloudly pattern
public typedrouter = new plugins.typedrequest.TypedRouter();
constructor(private dcRouterRef: DcRouter) {
// Add our typedrouter to the dcRouter's main typedrouter
this.dcRouterRef.typedrouter.addTypedRouter(this.typedrouter);
}
public async start() {
// TypedServer already has a built-in typedrouter at /typedrequest
this.server = new plugins.typedserver.utilityservers.UtilityWebsiteServer({
domain: 'localhost',
feedMetadata: null,
serveDir: paths.distServe,
});
// The server's typedrouter is automatically available
// Add the main dcRouter typedrouter to the server's typedrouter
this.server.typedrouter.addTypedRouter(this.dcRouterRef.typedrouter);
this.setupHandlers();
await this.server.start(3000);
}
```
**Note**: TypedServer automatically provides the `/typedrequest` endpoint with its built-in typedrouter. We just need to add our routers to it using the `addTypedRouter()` method.
#### Hierarchical TypedRouter Structure
Following cloudly's pattern, we'll use a hierarchical router structure:
```
TypedServer (built-in typedrouter at /typedrequest)
└── DcRouter.typedrouter (main router)
└── OpsServer.typedrouter (ops-specific handlers)
├── StatsHandler.typedrouter
├── ConfigHandler.typedrouter
└── SecurityHandler.typedrouter
```
This allows clean separation of concerns while keeping all handlers accessible through the single `/typedrequest` endpoint.
#### 2.2 Create Handler Classes ✓
Create modular handlers in `ts/opsserver/handlers/`:
- [x] `stats.handler.ts` - Server and performance statistics
- [x] `security.handler.ts` - Security and reputation metrics
- [x] `config.handler.ts` - Configuration management
- [x] `logs.handler.ts` - Log retrieval and streaming
- [x] `admin.handler.ts` - Authentication and session management
Each handler should:
- Have its own typedrouter that gets added to OpsServer's router
- Access the main DCRouter instance
- Register handlers using TypedHandler instances
- Format responses according to interfaces
- Handle errors gracefully
Example handler structure:
```typescript
export class StatsHandler {
public typedrouter = new plugins.typedrequest.TypedRouter();
constructor(private opsServerRef: OpsServer) {
// Add this handler's router to the parent
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
this.registerHandlers();
}
private registerHandlers() {
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<IReq_GetServerStatistics>(
'getServerStatistics',
async (dataArg, toolsArg) => {
const stats = await this.collectServerStats();
return stats;
}
)
);
}
}
```
### Phase 3: Frontend State Management ✓
#### 3.1 Set up Smartstate (`ts_web/appstate.ts`) ✓
- [x] Initialize Smartstate instance
- [x] Create state parts with appropriate persistence
- [x] Define initial state structures
```typescript
// State structure example
interface IStatsState {
serverStats: IRes_ServerStatistics | null;
emailStats: IRes_EmailStatistics | null;
dnsStats: IRes_DnsStatistics | null;
lastUpdated: number;
isLoading: boolean;
error: string | null;
}
```
#### 3.2 State Parts to Create ✓
- [x] `statsState` - Runtime statistics (soft persistence)
- [x] `configState` - Configuration data (soft persistence)
- [x] `uiState` - UI preferences (persistent)
- [x] `loginState` - Authentication state (persistent)
### Phase 4: Frontend Integration ✓
#### 4.1 API Client Setup ✓
- [x] TypedRequest instances created inline within actions
- [x] Base URL handled through relative paths
- [x] Error handling integrated in actions
- [x] Following cloudly pattern of creating requests within actions
#### 4.2 Create Actions (`ts_web/appstate.ts`) ✓
- [x] `loginAction` - Authentication with JWT
- [x] `logoutAction` - Clear authentication state
- [x] `fetchAllStatsAction` - Batch fetch all statistics
- [x] `fetchConfigurationAction` - Get configuration
- [x] `updateConfigurationAction` - Update configuration
- [x] `fetchRecentLogsAction` - Get recent logs
- [x] `toggleAutoRefreshAction` - Toggle auto-refresh
- [x] `setActiveViewAction` - Change active view
- [x] Error handling in all actions
#### 4.3 Update Dashboard Component (`ts_web/elements/ops-dashboard.ts`) ✓
- [x] Subscribe to state changes (login and UI state)
- [x] Implement reactive UI updates
- [x] Use dees-simple-login and dees-simple-appdash components
- [x] Create view components for different sections
- [x] Implement auto-refresh timer functionality
### Phase 5: Component Structure ✓
Created modular view components in `ts_web/elements/`:
- [x] `ops-view-overview.ts` - Overview with server, email, and DNS statistics
- [x] `ops-view-stats.ts` - Detailed statistics with tables and metrics
- [x] `ops-view-logs.ts` - Log viewer with filtering and search
- [x] `ops-view-config.ts` - Configuration editor with JSON editing
- [x] `ops-view-security.ts` - Security metrics and threat monitoring
- [x] `shared/ops-sectionheading.ts` - Reusable section heading component
- [x] `shared/css.ts` - Shared CSS styles
### Phase 6: Optional Enhancements
#### 6.1 Authentication ✓ (Implemented)
- [x] JWT-based authentication using `@push.rocks/smartjwt`
- [x] Guards for identity validation and admin access
- [x] Login/logout endpoints following cloudly pattern
- [ ] Login component (frontend)
- [ ] Protected route handling (frontend)
- [ ] Session persistence (frontend)
#### 6.2 Real-time Updates (future)
- [ ] WebSocket integration for live stats
- [ ] Push notifications for critical events
- [ ] Event streaming for logs
## Technical Stack
### Dependencies to Use
- `@api.global/typedserver` - Server with built-in typedrouter at `/typedrequest`
- `@api.global/typedrequest` - TypedRouter and TypedHandler classes
- `@design.estate/dees-domtools` - Frontend TypedRequest client
- `@push.rocks/smartstate` - State management
- `@design.estate/dees-element` - Web components
- `@design.estate/dees-catalog` - UI components
### Existing Dependencies to Leverage
- Current DCRouter instance and statistics
- Existing error handling patterns
- Logger infrastructure
- Security modules
## Development Workflow
1. **Start with interfaces** - Define all types first
2. **Implement one handler** - Start with server stats
3. **Create minimal frontend** - Test with one endpoint
4. **Iterate** - Add more handlers and UI components
5. **Polish** - Add error handling, loading states, etc.
## Testing Strategy
- [ ] Unit tests for handlers
- [ ] Integration tests for API endpoints
- [ ] Frontend component tests
- [ ] End-to-end testing with real DCRouter instance
## Success Criteria
- Type-safe communication between frontend and backend
- Real-time statistics display
- Responsive and reactive UI
- Clean, maintainable code structure
- Consistent with cloudly patterns
- Easy to extend with new features
## Notes
- Follow existing code conventions in the project
- Use pnpm for all package management
- Ensure all tests pass before marking complete
- Document any deviations from the plan
---
## Progress Status
### Completed ✓
- **Phase 1: Interface Definition** - All TypedRequest interfaces created following cloudly pattern
- Created proper TypedRequest interfaces with `method`, `request`, and `response` properties
- Used `IVirtualStream` for log streaming
- Added `@api.global/typedrequest-interfaces` dependency
- All interfaces compile successfully
- **Phase 2: Backend Implementation** - TypedRouter integration and handlers
- Enhanced OpsServer with hierarchical TypedRouter structure
- Created all handler classes with proper TypedHandler registration
- Implemented mock data responses for all endpoints
- Fixed all TypeScript compilation errors
- VirtualStream used for log streaming with Uint8Array encoding
- **JWT Authentication** - Following cloudly pattern:
- Added `@push.rocks/smartjwt` and `@push.rocks/smartguard` dependencies
- Updated IIdentity interface to match cloudly structure
- Implemented JWT-based authentication with RSA keypairs
- Created validIdentityGuard and adminIdentityGuard
- Added guard helpers for protecting endpoints
- Full test coverage for JWT authentication flows
- **Phase 3: Frontend State Management** - Smartstate implementation
- Initialized Smartstate with proper state parts
- Created state interfaces for all data types
- Implemented persistent vs soft state persistence
- Set up reactive subscriptions
- **Phase 4: Frontend Integration** - Complete dashboard implementation
- Created all state management actions with TypedRequest
- Implemented JWT authentication flow in frontend
- Built reactive dashboard with dees-simple-login and dees-simple-appdash
- Added auto-refresh functionality
- Fixed all interface import issues (using dist_ts_interfaces)
- **Phase 5: Component Structure** - View components
- Created all view components following cloudly patterns
- Implemented reactive data binding with state subscriptions
- Added interactive features (filtering, editing, refresh controls)
- Used @design.estate/dees-catalog components throughout
- Created shared components and styles
### Next Steps
- Write comprehensive tests for handlers and frontend components
- Implement real data sources (replace mock data)
- Add WebSocket support for real-time updates
- Enhance error handling and user feedback
- Add more detailed charts and visualizations
---
*This plan is a living document. Update it as implementation progresses.*

View File

@ -13,6 +13,7 @@ import { configureEmailStorage, configureEmailServer } from './mail/delivery/ind
import { StorageManager, type IStorageConfig } from './storage/index.js'; import { StorageManager, type IStorageConfig } from './storage/index.js';
import { OpsServer } from './opsserver/index.js'; import { OpsServer } from './opsserver/index.js';
import { MetricsManager } from './monitoring/index.js';
export interface IDcRouterOptions { export interface IDcRouterOptions {
/** /**
@ -133,6 +134,7 @@ export class DcRouter {
public emailServer?: UnifiedEmailServer; public emailServer?: UnifiedEmailServer;
public storageManager: StorageManager; public storageManager: StorageManager;
public opsServer: OpsServer; public opsServer: OpsServer;
public metricsManager?: MetricsManager;
// TypedRouter for API endpoints // TypedRouter for API endpoints
public typedrouter = new plugins.typedrequest.TypedRouter(); public typedrouter = new plugins.typedrequest.TypedRouter();
@ -160,6 +162,10 @@ export class DcRouter {
await this.opsServer.start(); await this.opsServer.start();
try { try {
// Initialize MetricsManager
this.metricsManager = new MetricsManager(this);
await this.metricsManager.start();
// Set up SmartProxy for HTTP/HTTPS and all traffic including email routes // Set up SmartProxy for HTTP/HTTPS and all traffic including email routes
await this.setupSmartProxy(); await this.setupSmartProxy();
@ -197,6 +203,14 @@ export class DcRouter {
console.log('║ DcRouter Started Successfully ║'); console.log('║ DcRouter Started Successfully ║');
console.log('╚═══════════════════════════════════════════════════════════════════╝\n'); console.log('╚═══════════════════════════════════════════════════════════════════╝\n');
// Metrics summary
if (this.metricsManager) {
console.log('📊 Metrics Service:');
console.log(' ├─ SmartMetrics: Active');
console.log(' ├─ SmartProxy Stats: Active');
console.log(' └─ Real-time tracking: Enabled');
}
// SmartProxy summary // SmartProxy summary
if (this.smartProxy) { if (this.smartProxy) {
console.log('🌐 SmartProxy Service:'); console.log('🌐 SmartProxy Service:');
@ -566,6 +580,9 @@ export class DcRouter {
try { try {
// Stop all services in parallel for faster shutdown // Stop all services in parallel for faster shutdown
await Promise.all([ await Promise.all([
// Stop metrics manager if running
this.metricsManager ? this.metricsManager.stop().catch(err => console.error('Error stopping MetricsManager:', err)) : Promise.resolve(),
// Stop unified email server if running // Stop unified email server if running
this.emailServer ? this.emailServer.stop().catch(err => console.error('Error stopping email server:', err)) : Promise.resolve(), this.emailServer ? this.emailServer.stop().catch(err => console.error('Error stopping email server:', err)) : Promise.resolve(),

View File

@ -0,0 +1,260 @@
import * as plugins from '../plugins.js';
import { DcRouter } from '../classes.dcrouter.js';
export class MetricsManager {
private logger: plugins.smartlog.Smartlog;
private smartMetrics: plugins.smartmetrics.SmartMetrics;
private dcRouter: DcRouter;
// Track email-specific metrics
private emailMetrics = {
sentToday: 0,
receivedToday: 0,
failedToday: 0,
bouncedToday: 0,
queueSize: 0,
lastResetDate: new Date().toDateString(),
};
// Track DNS-specific metrics
private dnsMetrics = {
totalQueries: 0,
cacheHits: 0,
cacheMisses: 0,
queryTypes: {} as Record<string, number>,
topDomains: new Map<string, number>(),
lastResetDate: new Date().toDateString(),
};
// Track security-specific metrics
private securityMetrics = {
blockedIPs: 0,
authFailures: 0,
spamDetected: 0,
malwareDetected: 0,
phishingDetected: 0,
lastResetDate: new Date().toDateString(),
};
constructor(dcRouter: DcRouter) {
this.dcRouter = dcRouter;
// Create a new Smartlog instance for metrics
this.logger = new plugins.smartlog.Smartlog({
logContext: {
environment: 'production',
runtime: 'node',
zone: 'dcrouter-metrics',
}
});
this.smartMetrics = new plugins.smartmetrics.SmartMetrics(this.logger, 'dcrouter');
}
public async start(): Promise<void> {
// Start SmartMetrics collection
this.smartMetrics.start();
// Reset daily counters at midnight
setInterval(() => {
const currentDate = new Date().toDateString();
if (currentDate !== this.emailMetrics.lastResetDate) {
this.emailMetrics.sentToday = 0;
this.emailMetrics.receivedToday = 0;
this.emailMetrics.failedToday = 0;
this.emailMetrics.bouncedToday = 0;
this.emailMetrics.lastResetDate = currentDate;
}
if (currentDate !== this.dnsMetrics.lastResetDate) {
this.dnsMetrics.totalQueries = 0;
this.dnsMetrics.cacheHits = 0;
this.dnsMetrics.cacheMisses = 0;
this.dnsMetrics.queryTypes = {};
this.dnsMetrics.topDomains.clear();
this.dnsMetrics.lastResetDate = currentDate;
}
if (currentDate !== this.securityMetrics.lastResetDate) {
this.securityMetrics.blockedIPs = 0;
this.securityMetrics.authFailures = 0;
this.securityMetrics.spamDetected = 0;
this.securityMetrics.malwareDetected = 0;
this.securityMetrics.phishingDetected = 0;
this.securityMetrics.lastResetDate = currentDate;
}
}, 60000); // Check every minute
this.logger.log('info', 'MetricsManager started');
}
public async stop(): Promise<void> {
this.smartMetrics.stop();
this.logger.log('info', 'MetricsManager stopped');
}
// Get server metrics from SmartMetrics and SmartProxy
public async getServerStats() {
const smartMetricsData = await this.smartMetrics.getMetrics();
const proxyStats = this.dcRouter.smartProxy ? this.dcRouter.smartProxy.getStats() : null;
return {
uptime: process.uptime(),
startTime: Date.now() - (process.uptime() * 1000),
memoryUsage: {
heapUsed: process.memoryUsage().heapUsed,
heapTotal: process.memoryUsage().heapTotal,
external: process.memoryUsage().external,
rss: process.memoryUsage().rss,
},
cpuUsage: {
user: parseFloat(smartMetricsData.cpuUsageText || '0'),
system: 0, // SmartMetrics doesn't separate user/system
},
activeConnections: proxyStats ? proxyStats.getActiveConnections() : 0,
totalConnections: proxyStats ? proxyStats.getTotalConnections() : 0,
requestsPerSecond: proxyStats ? proxyStats.getRequestsPerSecond() : 0,
throughput: proxyStats ? proxyStats.getThroughput() : { bytesIn: 0, bytesOut: 0 },
};
}
// Get email metrics
public async getEmailStats() {
return {
sentToday: this.emailMetrics.sentToday,
receivedToday: this.emailMetrics.receivedToday,
failedToday: this.emailMetrics.failedToday,
bounceRate: this.emailMetrics.bouncedToday > 0
? (this.emailMetrics.bouncedToday / this.emailMetrics.sentToday) * 100
: 0,
deliveryRate: this.emailMetrics.sentToday > 0
? ((this.emailMetrics.sentToday - this.emailMetrics.failedToday) / this.emailMetrics.sentToday) * 100
: 100,
queueSize: this.emailMetrics.queueSize,
averageDeliveryTime: 0, // TODO: Implement when delivery tracking is added
topRecipients: [], // TODO: Implement recipient tracking
recentActivity: [], // TODO: Implement activity log
};
}
// Get DNS metrics
public async getDnsStats() {
const cacheHitRate = this.dnsMetrics.totalQueries > 0
? (this.dnsMetrics.cacheHits / this.dnsMetrics.totalQueries) * 100
: 0;
const topDomains = Array.from(this.dnsMetrics.topDomains.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.map(([domain, count]) => ({ domain, count }));
return {
queriesPerSecond: 0, // TODO: Calculate based on time window
totalQueries: this.dnsMetrics.totalQueries,
cacheHits: this.dnsMetrics.cacheHits,
cacheMisses: this.dnsMetrics.cacheMisses,
cacheHitRate: cacheHitRate,
topDomains: topDomains,
queryTypes: this.dnsMetrics.queryTypes,
averageResponseTime: 0, // TODO: Implement response time tracking
activeDomains: this.dnsMetrics.topDomains.size,
};
}
// Get security metrics
public async getSecurityStats() {
return {
blockedIPs: this.securityMetrics.blockedIPs,
authFailures: this.securityMetrics.authFailures,
spamDetected: this.securityMetrics.spamDetected,
malwareDetected: this.securityMetrics.malwareDetected,
phishingDetected: this.securityMetrics.phishingDetected,
totalThreatsBlocked: this.securityMetrics.spamDetected +
this.securityMetrics.malwareDetected +
this.securityMetrics.phishingDetected,
recentIncidents: [], // TODO: Implement incident logging
};
}
// Get connection info from SmartProxy
public async getConnectionInfo() {
const proxyStats = this.dcRouter.smartProxy ? this.dcRouter.smartProxy.getStats() : null;
if (!proxyStats) {
return [];
}
const connectionsByRoute = proxyStats.getConnectionsByRoute();
const connectionInfo = [];
for (const [routeName, count] of connectionsByRoute) {
connectionInfo.push({
type: 'https',
count,
source: routeName,
lastActivity: new Date(),
});
}
return connectionInfo;
}
// Email event tracking methods
public trackEmailSent(): void {
this.emailMetrics.sentToday++;
}
public trackEmailReceived(): void {
this.emailMetrics.receivedToday++;
}
public trackEmailFailed(): void {
this.emailMetrics.failedToday++;
}
public trackEmailBounced(): void {
this.emailMetrics.bouncedToday++;
}
public updateQueueSize(size: number): void {
this.emailMetrics.queueSize = size;
}
// DNS event tracking methods
public trackDnsQuery(queryType: string, domain: string, cacheHit: boolean): void {
this.dnsMetrics.totalQueries++;
if (cacheHit) {
this.dnsMetrics.cacheHits++;
} else {
this.dnsMetrics.cacheMisses++;
}
// Track query types
this.dnsMetrics.queryTypes[queryType] = (this.dnsMetrics.queryTypes[queryType] || 0) + 1;
// Track top domains
const currentCount = this.dnsMetrics.topDomains.get(domain) || 0;
this.dnsMetrics.topDomains.set(domain, currentCount + 1);
}
// Security event tracking methods
public trackBlockedIP(): void {
this.securityMetrics.blockedIPs++;
}
public trackAuthFailure(): void {
this.securityMetrics.authFailures++;
}
public trackSpamDetected(): void {
this.securityMetrics.spamDetected++;
}
public trackMalwareDetected(): void {
this.securityMetrics.malwareDetected++;
}
public trackPhishingDetected(): void {
this.securityMetrics.phishingDetected++;
}
}

1
ts/monitoring/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './classes.metricsmanager.js';

View File

@ -1,6 +1,7 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { OpsServer } from '../classes.opsserver.js'; import type { OpsServer } from '../classes.opsserver.js';
import * as interfaces from '../../../ts_interfaces/index.js'; import * as interfaces from '../../../ts_interfaces/index.js';
import { MetricsManager } from '../../monitoring/index.js';
export class SecurityHandler { export class SecurityHandler {
public typedrouter = new plugins.typedrequest.TypedRouter(); public typedrouter = new plugins.typedrequest.TypedRouter();
@ -120,7 +121,29 @@ export class SecurityHandler {
phishing: Array<{ timestamp: number; value: number }>; phishing: Array<{ timestamp: number; value: number }>;
}; };
}> { }> {
// TODO: Implement actual security metrics collection // Get metrics from MetricsManager if available
if (this.opsServerRef.dcRouterRef.metricsManager) {
const securityStats = await this.opsServerRef.dcRouterRef.metricsManager.getSecurityStats();
return {
blockedIPs: [], // TODO: Track actual blocked IPs
reputationScores: {},
spamDetection: {
detected: securityStats.spamDetected,
falsePositives: 0,
},
malwareDetected: securityStats.malwareDetected,
phishingDetected: securityStats.phishingDetected,
authFailures: securityStats.authFailures,
suspiciousActivities: 0,
trends: {
spam: [],
malware: [],
phishing: [],
},
};
}
// Fallback if MetricsManager not available
return { return {
blockedIPs: [], blockedIPs: [],
reputationScores: {}, reputationScores: {},
@ -178,11 +201,31 @@ export class SecurityHandler {
status: 'active' | 'idle' | 'closing'; status: 'active' | 'idle' | 'closing';
}> = []; }> = [];
// TODO: Implement actual connection tracking // Get connection info from MetricsManager if available
// This would collect from: if (this.opsServerRef.dcRouterRef.metricsManager) {
// - SmartProxy connections const connectionInfo = await this.opsServerRef.dcRouterRef.metricsManager.getConnectionInfo();
// - Email server connections
// - DNS server connections // Map connection info to detailed format
// Note: Some fields will be placeholder values until more detailed tracking is implemented
connectionInfo.forEach((info, index) => {
connections.push({
id: `conn-${index}`,
type: 'http', // TODO: Determine from source/protocol
source: {
ip: '0.0.0.0', // TODO: Track actual source IPs
port: 0,
},
destination: {
ip: '0.0.0.0',
port: 443,
service: info.source,
},
startTime: info.lastActivity.getTime(),
bytesTransferred: 0, // TODO: Track bytes per connection
status: 'active',
});
});
}
return connections; return connections;
} }

View File

@ -1,6 +1,7 @@
import * as plugins from '../../plugins.js'; import * as plugins from '../../plugins.js';
import type { OpsServer } from '../classes.opsserver.js'; import type { OpsServer } from '../classes.opsserver.js';
import * as interfaces from '../../../ts_interfaces/index.js'; import * as interfaces from '../../../ts_interfaces/index.js';
import { MetricsManager } from '../../monitoring/index.js';
export class StatsHandler { export class StatsHandler {
public typedrouter = new plugins.typedrequest.TypedRouter(); public typedrouter = new plugins.typedrequest.TypedRouter();
@ -178,25 +179,30 @@ export class StatsHandler {
value: number; value: number;
}>; }>;
}> { }> {
// Get metrics from MetricsManager if available
if (this.opsServerRef.dcRouterRef.metricsManager) {
const serverStats = await this.opsServerRef.dcRouterRef.metricsManager.getServerStats();
return {
uptime: serverStats.uptime,
cpuUsage: serverStats.cpuUsage,
memoryUsage: serverStats.memoryUsage,
requestsPerSecond: serverStats.requestsPerSecond,
activeConnections: serverStats.activeConnections,
totalConnections: serverStats.totalConnections,
history: [], // TODO: Implement history tracking
};
}
// Fallback to basic stats if MetricsManager not available
const uptime = process.uptime(); const uptime = process.uptime();
const memUsage = process.memoryUsage(); const memUsage = process.memoryUsage();
const totalMem = plugins.os.totalmem();
const freeMem = plugins.os.freemem();
const usedMem = totalMem - freeMem;
// Get CPU usage (simplified - in production would use proper monitoring)
const cpuUsage = plugins.os.loadavg()[0] * 100 / plugins.os.cpus().length; const cpuUsage = plugins.os.loadavg()[0] * 100 / plugins.os.cpus().length;
// TODO: Implement proper request tracking
const requestsPerSecond = 0;
const activeConnections = 0;
const totalConnections = 0;
return { return {
uptime, uptime,
cpuUsage: { cpuUsage: {
user: cpuUsage * 0.7, // Approximate user CPU user: cpuUsage * 0.7,
system: cpuUsage * 0.3, // Approximate system CPU system: cpuUsage * 0.3,
}, },
memoryUsage: { memoryUsage: {
heapUsed: memUsage.heapUsed, heapUsed: memUsage.heapUsed,
@ -204,10 +210,10 @@ export class StatsHandler {
external: memUsage.external, external: memUsage.external,
rss: memUsage.rss, rss: memUsage.rss,
}, },
requestsPerSecond, requestsPerSecond: 0,
activeConnections, activeConnections: 0,
totalConnections, totalConnections: 0,
history: [], // TODO: Implement history tracking history: [],
}; };
} }
@ -219,7 +225,19 @@ export class StatsHandler {
queueSize: number; queueSize: number;
domainBreakdown?: { [domain: string]: interfaces.data.IEmailStats }; domainBreakdown?: { [domain: string]: interfaces.data.IEmailStats };
}> { }> {
// TODO: Implement actual email statistics collection // Get metrics from MetricsManager if available
if (this.opsServerRef.dcRouterRef.metricsManager) {
const emailStats = await this.opsServerRef.dcRouterRef.metricsManager.getEmailStats();
return {
sentToday: emailStats.sentToday,
receivedToday: emailStats.receivedToday,
bounceRate: emailStats.bounceRate,
deliveryRate: emailStats.deliveryRate,
queueSize: emailStats.queueSize,
};
}
// Fallback if MetricsManager not available
return { return {
sentToday: 0, sentToday: 0,
receivedToday: 0, receivedToday: 0,
@ -242,7 +260,21 @@ export class StatsHandler {
queryTypes: { [key: string]: number }; queryTypes: { [key: string]: number };
domainBreakdown?: { [domain: string]: interfaces.data.IDnsStats }; domainBreakdown?: { [domain: string]: interfaces.data.IDnsStats };
}> { }> {
// TODO: Implement actual DNS statistics collection // Get metrics from MetricsManager if available
if (this.opsServerRef.dcRouterRef.metricsManager) {
const dnsStats = await this.opsServerRef.dcRouterRef.metricsManager.getDnsStats();
return {
queriesPerSecond: dnsStats.queriesPerSecond,
totalQueries: dnsStats.totalQueries,
cacheHits: dnsStats.cacheHits,
cacheMisses: dnsStats.cacheMisses,
cacheHitRate: dnsStats.cacheHitRate,
topDomains: dnsStats.topDomains,
queryTypes: dnsStats.queryTypes,
};
}
// Fallback if MetricsManager not available
return { return {
queriesPerSecond: 0, queriesPerSecond: 0,
totalQueries: 0, totalQueries: 0,

View File

@ -50,6 +50,7 @@ import * as smartguard from '@push.rocks/smartguard';
import * as smartjwt from '@push.rocks/smartjwt'; import * as smartjwt from '@push.rocks/smartjwt';
import * as smartlog from '@push.rocks/smartlog'; import * as smartlog from '@push.rocks/smartlog';
import * as smartmail from '@push.rocks/smartmail'; import * as smartmail from '@push.rocks/smartmail';
import * as smartmetrics from '@push.rocks/smartmetrics';
import * as smartnetwork from '@push.rocks/smartnetwork'; import * as smartnetwork from '@push.rocks/smartnetwork';
import * as smartpath from '@push.rocks/smartpath'; import * as smartpath from '@push.rocks/smartpath';
import * as smartproxy from '@push.rocks/smartproxy'; import * as smartproxy from '@push.rocks/smartproxy';
@ -59,7 +60,7 @@ import * as smartrule from '@push.rocks/smartrule';
import * as smartrx from '@push.rocks/smartrx'; import * as smartrx from '@push.rocks/smartrx';
import * as smartunique from '@push.rocks/smartunique'; import * as smartunique from '@push.rocks/smartunique';
export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfile, smartguard, smartjwt, smartlog, smartmail, smartnetwork, smartpath, smartproxy, smartpromise, smartrequest, smartrule, smartrx, smartunique }; export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfile, smartguard, smartjwt, smartlog, smartmail, smartmetrics, smartnetwork, smartpath, smartproxy, smartpromise, smartrequest, smartrule, smartrx, smartunique };
// Define SmartLog types for use in error handling // Define SmartLog types for use in error handling
export type TLogLevel = 'error' | 'warn' | 'info' | 'success' | 'debug'; export type TLogLevel = 'error' | 'warn' | 'info' | 'success' | 'debug';