feat(ts_interfaces): add TypedRequest interfaces for admin and configuration requests
fix(dependencies): include @api.global/typedrequest-interfaces in package.json chore(docs): create OpsServer implementation plan in readme.opsserver.md
This commit is contained in:
@ -25,6 +25,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@api.global/typedrequest": "^3.0.19",
|
||||
"@api.global/typedrequest-interfaces": "^3.0.19",
|
||||
"@api.global/typedserver": "^3.0.74",
|
||||
"@api.global/typedsocket": "^3.0.0",
|
||||
"@apiclient.xyz/cloudflare": "^6.4.1",
|
||||
|
7
pnpm-lock.yaml
generated
7
pnpm-lock.yaml
generated
@ -11,6 +11,9 @@ importers:
|
||||
'@api.global/typedrequest':
|
||||
specifier: ^3.0.19
|
||||
version: 3.1.10
|
||||
'@api.global/typedrequest-interfaces':
|
||||
specifier: ^3.0.19
|
||||
version: 3.0.19
|
||||
'@api.global/typedserver':
|
||||
specifier: ^3.0.74
|
||||
version: 3.0.74
|
||||
@ -5353,8 +5356,10 @@ snapshots:
|
||||
'@push.rocks/taskbuffer': 3.1.7
|
||||
transitivePeerDependencies:
|
||||
- '@nuxt/kit'
|
||||
- bufferutil
|
||||
- react
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
- vue
|
||||
|
||||
'@hapi/hoek@9.3.0': {}
|
||||
@ -5695,7 +5700,6 @@ snapshots:
|
||||
- '@mongodb-js/zstd'
|
||||
- '@nuxt/kit'
|
||||
- aws-crt
|
||||
- bufferutil
|
||||
- encoding
|
||||
- gcp-metadata
|
||||
- kerberos
|
||||
@ -5704,7 +5708,6 @@ snapshots:
|
||||
- snappy
|
||||
- socks
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
- vue
|
||||
|
||||
'@push.rocks/smartarchive@3.0.8':
|
||||
|
@ -1,494 +0,0 @@
|
||||
# DCRouter Email Configuration Example
|
||||
|
||||
This document provides a comprehensive example of configuring the UnifiedEmailServer in DCRouter for various email routing scenarios.
|
||||
|
||||
## Basic Configuration
|
||||
|
||||
Here's a complete example of a email configuration for the UnifiedEmailServer:
|
||||
|
||||
```typescript
|
||||
import { IEmailConfig, EmailProcessingMode, IDomainRule } from './ts/config/email.config.js';
|
||||
|
||||
const emailConfig: IEmailConfig = {
|
||||
// Basic server settings
|
||||
useEmail: true,
|
||||
behindSmartProxy: false,
|
||||
hostname: "mail.example.com",
|
||||
ports: [25, 587, 465],
|
||||
|
||||
// Default processing settings
|
||||
defaultMode: "forward" as EmailProcessingMode,
|
||||
defaultServer: "smtp.internal-relay.example.com",
|
||||
defaultPort: 25,
|
||||
defaultTls: true,
|
||||
|
||||
// TLS configuration
|
||||
tls: {
|
||||
certPath: "/path/to/tls/certificate.pem",
|
||||
keyPath: "/path/to/tls/key.pem",
|
||||
caPath: "/path/to/tls/ca.pem",
|
||||
minVersion: "TLSv1.2",
|
||||
ciphers: "HIGH:!aNULL:!MD5:!RC4"
|
||||
},
|
||||
|
||||
// Email size and connection limits
|
||||
maxMessageSize: 25 * 1024 * 1024, // 25MB
|
||||
|
||||
// Authentication settings
|
||||
auth: {
|
||||
required: true,
|
||||
methods: ["PLAIN", "LOGIN"],
|
||||
users: [
|
||||
{ username: "user1", password: "securepassword1" },
|
||||
{ username: "user2", password: "securepassword2" }
|
||||
]
|
||||
},
|
||||
|
||||
// Domain routing rules
|
||||
domainRules: [
|
||||
// Process emails for your primary domain via MTA
|
||||
{
|
||||
pattern: "*@example.com",
|
||||
mode: "mta" as EmailProcessingMode,
|
||||
mtaOptions: {
|
||||
domain: "example.com",
|
||||
dkimSign: true,
|
||||
dkimOptions: {
|
||||
domainName: "example.com",
|
||||
keySelector: "mail"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Forward support emails to help desk
|
||||
{
|
||||
pattern: "*@support.example.com",
|
||||
mode: "forward" as EmailProcessingMode,
|
||||
target: {
|
||||
server: "helpdesk.example.com",
|
||||
port: 25,
|
||||
useTls: true
|
||||
}
|
||||
},
|
||||
|
||||
// Scan marketing emails for content and attachments
|
||||
{
|
||||
pattern: "*@marketing.example.com",
|
||||
mode: "process" as EmailProcessingMode,
|
||||
contentScanning: true,
|
||||
scanners: [
|
||||
{
|
||||
type: "attachment",
|
||||
action: "reject",
|
||||
blockedExtensions: [".exe", ".zip", ".js", ".vbs", ".bat"]
|
||||
},
|
||||
{
|
||||
type: "spam",
|
||||
threshold: 5.0,
|
||||
action: "tag"
|
||||
}
|
||||
],
|
||||
transformations: [
|
||||
{
|
||||
type: "addHeader",
|
||||
header: "X-Scanned-By",
|
||||
value: "DCRouter Content Scanner"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Forward all other emails to a backup server
|
||||
{
|
||||
pattern: "*@*",
|
||||
mode: "forward" as EmailProcessingMode,
|
||||
target: {
|
||||
server: "backup-smtp.example.com",
|
||||
port: 587,
|
||||
useTls: true
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
// Queue configuration
|
||||
queue: {
|
||||
storageType: "memory",
|
||||
ttl: 86400000, // 24 hours
|
||||
maxItems: 10000,
|
||||
checkInterval: 60000 // 1 minute
|
||||
},
|
||||
|
||||
// Template configuration
|
||||
templateConfig: {
|
||||
from: "noreply@example.com",
|
||||
replyTo: "support@example.com",
|
||||
footerHtml: "<p>This is an automated message from Example Inc.</p>",
|
||||
footerText: "This is an automated message from Example Inc."
|
||||
},
|
||||
|
||||
// IP warmup configuration
|
||||
serverConfig: {
|
||||
delivery: {
|
||||
concurrency: 10,
|
||||
rateLimit: {
|
||||
rate: 100,
|
||||
interval: 60000 // 1 minute
|
||||
},
|
||||
retries: {
|
||||
max: 3,
|
||||
delay: 300000, // 5 minutes
|
||||
useBackoff: true
|
||||
}
|
||||
},
|
||||
security: {
|
||||
useDkim: true,
|
||||
verifyDkim: true,
|
||||
verifySpf: true,
|
||||
verifyDmarc: true,
|
||||
enforceDmarc: true,
|
||||
useTls: true,
|
||||
requireValidCerts: true,
|
||||
securityLogLevel: "info",
|
||||
checkIPReputation: true,
|
||||
scanContent: true,
|
||||
maliciousContentAction: "tag",
|
||||
threatScoreThreshold: 7.0
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Initializing the UnifiedEmailServer
|
||||
|
||||
Here's how to initialize the UnifiedEmailServer with the configuration:
|
||||
|
||||
```typescript
|
||||
import { UnifiedEmailServer, IUnifiedEmailServerOptions } from './ts/mail/routing/classes.unified.email.server.js';
|
||||
|
||||
// Convert EmailConfig to UnifiedEmailServerOptions
|
||||
const serverOptions: IUnifiedEmailServerOptions = {
|
||||
ports: emailConfig.ports || [25],
|
||||
hostname: emailConfig.hostname || 'localhost',
|
||||
banner: `${emailConfig.hostname} ESMTP DCRouter`,
|
||||
|
||||
// Authentication
|
||||
auth: emailConfig.auth,
|
||||
|
||||
// TLS
|
||||
tls: emailConfig.tls,
|
||||
|
||||
// Message limits
|
||||
maxMessageSize: emailConfig.maxMessageSize || 10 * 1024 * 1024, // 10MB default
|
||||
maxClients: 100,
|
||||
|
||||
// Domain routing
|
||||
domainRules: emailConfig.domainRules || [],
|
||||
defaultMode: emailConfig.defaultMode || 'forward',
|
||||
defaultServer: emailConfig.defaultServer,
|
||||
defaultPort: emailConfig.defaultPort,
|
||||
defaultTls: emailConfig.defaultTls,
|
||||
|
||||
// Deliverability options
|
||||
ipWarmupConfig: {
|
||||
enabled: true,
|
||||
ipAddresses: ['198.51.100.1', '198.51.100.2', '198.51.100.3'],
|
||||
targetDomains: ['gmail.com', 'yahoo.com', 'outlook.com'],
|
||||
allocationPolicy: 'balanced'
|
||||
},
|
||||
|
||||
reputationMonitorConfig: {
|
||||
enabled: true,
|
||||
domains: ['example.com', 'marketing.example.com'],
|
||||
alertThresholds: {
|
||||
bounceRate: 0.05, // 5%
|
||||
complaintRate: 0.001 // 0.1%
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create and start the server
|
||||
const emailServer = new UnifiedEmailServer(serverOptions);
|
||||
emailServer.start().then(() => {
|
||||
console.log('UnifiedEmailServer started successfully');
|
||||
}).catch((error) => {
|
||||
console.error('Failed to start UnifiedEmailServer:', error);
|
||||
});
|
||||
```
|
||||
|
||||
## Use Case Examples
|
||||
|
||||
### 1. Forwarding Email Gateway (SMTP Proxy)
|
||||
|
||||
Configure DCRouter to forward emails for specific domains to internal mail servers:
|
||||
|
||||
```typescript
|
||||
const forwardingRules: IDomainRule[] = [
|
||||
// Main corporate domain - forward to Exchange
|
||||
{
|
||||
pattern: "*@corp.example.com",
|
||||
mode: "forward" as EmailProcessingMode,
|
||||
target: {
|
||||
server: "exchange.internal",
|
||||
port: 25,
|
||||
useTls: true
|
||||
}
|
||||
},
|
||||
// Marketing domain - forward to Marketing mail server
|
||||
{
|
||||
pattern: "*@marketing.example.com",
|
||||
mode: "forward" as EmailProcessingMode,
|
||||
target: {
|
||||
server: "marketing-mail.internal",
|
||||
port: 25,
|
||||
useTls: false
|
||||
}
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
### 2. Outbound MTA with DKIM Signing and IP Warmup
|
||||
|
||||
Configure DCRouter as an outbound mail transfer agent with DKIM signing and IP warmup:
|
||||
|
||||
```typescript
|
||||
// MTA configuration with DKIM signing
|
||||
const mtaRule: IDomainRule = {
|
||||
pattern: "*@outbound.example.com",
|
||||
mode: "mta" as EmailProcessingMode,
|
||||
mtaOptions: {
|
||||
domain: "outbound.example.com",
|
||||
dkimSign: true,
|
||||
dkimOptions: {
|
||||
domainName: "outbound.example.com",
|
||||
keySelector: "mail2023"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// IP Warmup configuration
|
||||
const ipWarmupConfig = {
|
||||
enabled: true,
|
||||
ipAddresses: ['203.0.113.1', '203.0.113.2'],
|
||||
targetDomains: ['gmail.com', 'yahoo.com', 'hotmail.com', 'aol.com'],
|
||||
allocationPolicy: 'progressive',
|
||||
fallbackPercentage: 20
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Content Scanning and Security Gateway
|
||||
|
||||
Configure DCRouter to scan emails for malicious content and enforce security policies:
|
||||
|
||||
```typescript
|
||||
const securityRule: IDomainRule = {
|
||||
pattern: "*@*",
|
||||
mode: "process" as EmailProcessingMode,
|
||||
contentScanning: true,
|
||||
scanners: [
|
||||
// Scan for malicious attachments
|
||||
{
|
||||
type: "attachment",
|
||||
action: "reject",
|
||||
blockedExtensions: [".exe", ".dll", ".bat", ".vbs", ".js", ".cmd", ".scr", ".com", ".pif"]
|
||||
},
|
||||
// Scan for spam
|
||||
{
|
||||
type: "spam",
|
||||
threshold: 6.0,
|
||||
action: "tag"
|
||||
},
|
||||
// Scan for viruses
|
||||
{
|
||||
type: "virus",
|
||||
action: "reject"
|
||||
}
|
||||
],
|
||||
transformations: [
|
||||
// Add scanning headers
|
||||
{
|
||||
type: "addHeader",
|
||||
header: "X-Security-Scanned",
|
||||
value: "DCRouter Security Gateway"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Configure security settings
|
||||
const securityConfig = {
|
||||
useDkim: true,
|
||||
verifyDkim: true,
|
||||
verifySpf: true,
|
||||
verifyDmarc: true,
|
||||
enforceDmarc: true,
|
||||
checkIPReputation: true,
|
||||
scanContent: true,
|
||||
maliciousContentAction: "quarantine",
|
||||
threatScoreThreshold: 5.0,
|
||||
rejectHighRiskIPs: true,
|
||||
securityLogLevel: "warn"
|
||||
};
|
||||
```
|
||||
|
||||
### 4. Multi-Tenant Email Server
|
||||
|
||||
Configure DCRouter to handle emails for multiple domains with different processing rules:
|
||||
|
||||
```typescript
|
||||
const multiTenantRules: IDomainRule[] = [
|
||||
// Tenant 1: Process locally
|
||||
{
|
||||
pattern: "*@tenant1.example.org",
|
||||
mode: "mta" as EmailProcessingMode,
|
||||
mtaOptions: {
|
||||
domain: "tenant1.example.org",
|
||||
dkimSign: true,
|
||||
dkimOptions: {
|
||||
domainName: "tenant1.example.org",
|
||||
keySelector: "t1mail"
|
||||
}
|
||||
}
|
||||
},
|
||||
// Tenant 2: Forward to their server
|
||||
{
|
||||
pattern: "*@tenant2.example.org",
|
||||
mode: "forward" as EmailProcessingMode,
|
||||
target: {
|
||||
server: "mail.tenant2.com",
|
||||
port: 587,
|
||||
useTls: true
|
||||
}
|
||||
},
|
||||
// Tenant 3: Process with content scanning
|
||||
{
|
||||
pattern: "*@tenant3.example.org",
|
||||
mode: "process" as EmailProcessingMode,
|
||||
contentScanning: true,
|
||||
scanners: [
|
||||
{
|
||||
type: "attachment",
|
||||
action: "tag",
|
||||
blockedExtensions: [".zip", ".rar", ".7z"]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
## Using the Bounce Management System
|
||||
|
||||
DCRouter includes a sophisticated bounce management system. Here's how to use it:
|
||||
|
||||
```typescript
|
||||
// Get an instance of the UnifiedEmailServer
|
||||
const emailServer = new UnifiedEmailServer(serverOptions);
|
||||
|
||||
// Check if an email is on the suppression list
|
||||
const isSuppressed = emailServer.isEmailSuppressed('user@example.com');
|
||||
|
||||
// Get suppression information
|
||||
const suppressionInfo = emailServer.getSuppressionInfo('user@example.com');
|
||||
if (suppressionInfo) {
|
||||
console.log(`Suppressed due to: ${suppressionInfo.reason}`);
|
||||
console.log(`Suppressed since: ${new Date(suppressionInfo.timestamp)}`);
|
||||
if (suppressionInfo.expiresAt) {
|
||||
console.log(`Suppression expires: ${new Date(suppressionInfo.expiresAt)}`);
|
||||
} else {
|
||||
console.log('Suppression is permanent');
|
||||
}
|
||||
}
|
||||
|
||||
// Add an email to the suppression list
|
||||
emailServer.addToSuppressionList(
|
||||
'problem-user@example.com',
|
||||
'Manual suppression due to user request',
|
||||
Date.now() + (30 * 24 * 60 * 60 * 1000) // Expires in 30 days
|
||||
);
|
||||
|
||||
// Remove an email from the suppression list
|
||||
emailServer.removeFromSuppressionList('reactivated-user@example.com');
|
||||
|
||||
// Get all suppressed emails
|
||||
const suppressionList = emailServer.getSuppressionList();
|
||||
console.log(`There are ${suppressionList.length} suppressed email addresses`);
|
||||
```
|
||||
|
||||
## Using the IP Warmup System
|
||||
|
||||
DCRouter's IP warmup system helps gradually build sender reputation for new IP addresses:
|
||||
|
||||
```typescript
|
||||
// Add a new IP to warmup
|
||||
emailServer.addIPToWarmup('198.51.100.4');
|
||||
|
||||
// Get warmup status for all IPs
|
||||
const warmupStatus = emailServer.getIPWarmupStatus();
|
||||
console.log('IP Warmup Status:', warmupStatus);
|
||||
|
||||
// Get warmup status for a specific IP
|
||||
const specificIPStatus = emailServer.getIPWarmupStatus('198.51.100.1');
|
||||
console.log(`Warmup day: ${specificIPStatus.day}`);
|
||||
console.log(`Daily limit: ${specificIPStatus.dailyLimit}`);
|
||||
console.log(`Emails sent today: ${specificIPStatus.sentToday}`);
|
||||
|
||||
// Update IP metrics based on feedback from mailbox providers
|
||||
emailServer.updateIPWarmupMetrics('198.51.100.1', {
|
||||
openRate: 0.25, // 25% open rate
|
||||
bounceRate: 0.02, // 2% bounce rate
|
||||
complaintRate: 0.001 // 0.1% complaint rate
|
||||
});
|
||||
|
||||
// Check if an IP can send more emails today
|
||||
if (emailServer.canIPSendMoreToday('198.51.100.1')) {
|
||||
console.log('IP can send more emails today');
|
||||
} else {
|
||||
console.log('IP has reached its daily sending limit');
|
||||
}
|
||||
|
||||
// Change the IP allocation policy
|
||||
emailServer.setIPAllocationPolicy('volume-based');
|
||||
```
|
||||
|
||||
## Sender Reputation Monitoring
|
||||
|
||||
Monitor and manage your domain reputation with these features:
|
||||
|
||||
```typescript
|
||||
// Add a domain to monitoring
|
||||
emailServer.addDomainToMonitoring('newdomain.example.com');
|
||||
|
||||
// Get reputation data for a specific domain
|
||||
const reputationData = emailServer.getDomainReputationData('example.com');
|
||||
console.log(`Reputation score: ${reputationData.score}`);
|
||||
console.log(`Bounce rate: ${reputationData.bounceRate}`);
|
||||
console.log(`Complaint rate: ${reputationData.complaintRate}`);
|
||||
|
||||
// Get a summary of all domains
|
||||
const summary = emailServer.getReputationSummary();
|
||||
console.log('Domain reputation summary:', summary);
|
||||
|
||||
// Record a custom engagement event
|
||||
emailServer.recordReputationEvent('example.com', {
|
||||
type: 'open',
|
||||
count: 15
|
||||
});
|
||||
```
|
||||
|
||||
## Updating Configuration at Runtime
|
||||
|
||||
DCRouter allows updating configurations dynamically without restarting:
|
||||
|
||||
```typescript
|
||||
// Update domain rules
|
||||
const newRules: IDomainRule[] = [
|
||||
// New rules here
|
||||
];
|
||||
emailServer.updateDomainRules(newRules);
|
||||
|
||||
// Update server options
|
||||
emailServer.updateOptions({
|
||||
maxMessageSize: 50 * 1024 * 1024, // 50MB
|
||||
maxClients: 200,
|
||||
defaultServer: 'new-relay.example.com'
|
||||
});
|
||||
```
|
||||
|
||||
This example configuration covers a wide range of use cases from simple forwarding to advanced security scanning, IP warmup, and reputation management, showcasing DCRouter's versatility as an email routing and processing solution.
|
308
readme.opsserver.md
Normal file
308
readme.opsserver.md
Normal file
@ -0,0 +1,308 @@
|
||||
# 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`)
|
||||
|
||||
- [ ] Add TypedRouter initialization
|
||||
- [ ] Use TypedServer's built-in typedrouter
|
||||
- [ ] CORS is already handled by TypedServer
|
||||
- [ ] 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/`:
|
||||
|
||||
- [ ] `stats.handler.ts` - Server and performance statistics
|
||||
- [ ] `email.handler.ts` - Email-related operations
|
||||
- [ ] `dns.handler.ts` - DNS management statistics
|
||||
- [ ] `security.handler.ts` - Security and reputation metrics
|
||||
- [ ] `config.handler.ts` - Configuration 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`)
|
||||
|
||||
- [ ] Initialize Smartstate instance
|
||||
- [ ] Create state parts with appropriate persistence
|
||||
- [ ] 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
|
||||
|
||||
- [ ] `statsState` - Runtime statistics (soft persistence)
|
||||
- [ ] `configState` - Configuration data (soft persistence)
|
||||
- [ ] `uiState` - UI preferences (persistent)
|
||||
- [ ] `loginState` - Authentication state (persistent) *if needed*
|
||||
|
||||
### Phase 4: Frontend Integration
|
||||
|
||||
#### 4.1 API Client Setup (`ts_web/api/clients.ts`)
|
||||
|
||||
- [ ] Create TypedRequest instances for each endpoint
|
||||
- [ ] Configure base URL handling
|
||||
- [ ] Add error interceptors
|
||||
- [ ] Implement retry logic
|
||||
|
||||
#### 4.2 Create Actions (`ts_web/state/actions.ts`)
|
||||
|
||||
- [ ] `fetchAllStatsAction` - Batch fetch all statistics
|
||||
- [ ] `refreshServerStatsAction` - Update server stats only
|
||||
- [ ] `refreshEmailStatsAction` - Update email stats only
|
||||
- [ ] `setAutoRefreshAction` - Toggle auto-refresh
|
||||
- [ ] Error handling actions
|
||||
|
||||
#### 4.3 Update Dashboard Component (`ts_web/elements/ops-dashboard.ts`)
|
||||
|
||||
- [ ] Subscribe to state changes
|
||||
- [ ] Implement reactive UI updates
|
||||
- [ ] Add refresh controls
|
||||
- [ ] Create sub-components for different stat types
|
||||
- [ ] Implement auto-refresh timer
|
||||
|
||||
### Phase 5: Component Structure
|
||||
|
||||
Create modular components in `ts_web/elements/components/`:
|
||||
|
||||
- [ ] `server-stats.ts` - Server statistics display
|
||||
- [ ] `email-stats.ts` - Email metrics visualization
|
||||
- [ ] `dns-stats.ts` - DNS statistics
|
||||
- [ ] `rate-limit-display.ts` - Rate limiting status
|
||||
- [ ] `security-metrics.ts` - Security dashboard
|
||||
- [ ] `log-viewer.ts` - Real-time log display
|
||||
|
||||
### Phase 6: Optional Enhancements
|
||||
|
||||
#### 6.1 Authentication (if required)
|
||||
- [ ] Simple token-based authentication
|
||||
- [ ] Login component
|
||||
- [ ] Protected route handling
|
||||
- [ ] Session management
|
||||
|
||||
#### 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
|
||||
|
||||
### Next Steps
|
||||
- Phase 2: Backend Implementation - Enhance OpsServer and create handlers
|
||||
- Phase 3: Frontend State Management - Set up Smartstate
|
||||
- Phase 4: Frontend Integration - Create API clients and update dashboard
|
||||
|
||||
---
|
||||
|
||||
*This plan is a living document. Update it as implementation progresses.*
|
@ -0,0 +1,9 @@
|
||||
export * from './plugins.js';
|
||||
|
||||
// Data types
|
||||
import * as data from './data/index.js';
|
||||
export { data };
|
||||
|
||||
// Request interfaces
|
||||
import * as requests from './requests/index.js';
|
||||
export { requests };
|
6
ts_interfaces/plugins.ts
Normal file
6
ts_interfaces/plugins.ts
Normal file
@ -0,0 +1,6 @@
|
||||
// @apiglobal scope
|
||||
import * as typedrequestInterfaces from '@api.global/typedrequest-interfaces';
|
||||
|
||||
export {
|
||||
typedrequestInterfaces
|
||||
}
|
46
ts_interfaces/requests/admin.ts
Normal file
46
ts_interfaces/requests/admin.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import * as authInterfaces from '../data/auth.js';
|
||||
|
||||
// Admin Login
|
||||
export interface IReq_AdminLoginWithUsernameAndPassword extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_AdminLoginWithUsernameAndPassword
|
||||
> {
|
||||
method: 'adminLoginWithUsernameAndPassword';
|
||||
request: {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
response: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
};
|
||||
}
|
||||
|
||||
// Admin Logout
|
||||
export interface IReq_AdminLogout extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_AdminLogout
|
||||
> {
|
||||
method: 'adminLogout';
|
||||
request: {
|
||||
identity: authInterfaces.IIdentity;
|
||||
};
|
||||
response: {
|
||||
success: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// Verify Identity
|
||||
export interface IReq_VerifyIdentity extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_VerifyIdentity
|
||||
> {
|
||||
method: 'verifyIdentity';
|
||||
request: {
|
||||
identity: authInterfaces.IIdentity;
|
||||
};
|
||||
response: {
|
||||
valid: boolean;
|
||||
identity?: authInterfaces.IIdentity;
|
||||
};
|
||||
}
|
35
ts_interfaces/requests/config.ts
Normal file
35
ts_interfaces/requests/config.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import * as authInterfaces from '../data/auth.js';
|
||||
|
||||
// Get Configuration
|
||||
export interface IReq_GetConfiguration extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_GetConfiguration
|
||||
> {
|
||||
method: 'getConfiguration';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
section?: string;
|
||||
};
|
||||
response: {
|
||||
config: any;
|
||||
section?: string;
|
||||
};
|
||||
}
|
||||
|
||||
// Update Configuration
|
||||
export interface IReq_UpdateConfiguration extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_UpdateConfiguration
|
||||
> {
|
||||
method: 'updateConfiguration';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
section: string;
|
||||
config: any;
|
||||
};
|
||||
response: {
|
||||
updated: boolean;
|
||||
config: any;
|
||||
};
|
||||
}
|
4
ts_interfaces/requests/index.ts
Normal file
4
ts_interfaces/requests/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './admin.js';
|
||||
export * from './config.js';
|
||||
export * from './logs.js';
|
||||
export * from './stats.js';
|
44
ts_interfaces/requests/logs.ts
Normal file
44
ts_interfaces/requests/logs.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import * as authInterfaces from '../data/auth.js';
|
||||
import * as statsInterfaces from '../data/stats.js';
|
||||
|
||||
// Get Recent Logs
|
||||
export interface IReq_GetRecentLogs extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_GetRecentLogs
|
||||
> {
|
||||
method: 'getRecentLogs';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
level?: 'debug' | 'info' | 'warn' | 'error';
|
||||
category?: 'smtp' | 'dns' | 'security' | 'system' | 'email';
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
search?: string;
|
||||
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
||||
};
|
||||
response: {
|
||||
logs: statsInterfaces.ILogEntry[];
|
||||
total: number;
|
||||
hasMore: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// Get Log Stream
|
||||
export interface IReq_GetLogStream extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_GetLogStream
|
||||
> {
|
||||
method: 'getLogStream';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
follow?: boolean;
|
||||
filters?: {
|
||||
level?: string[];
|
||||
category?: string[];
|
||||
};
|
||||
};
|
||||
response: {
|
||||
logStream: plugins.typedrequestInterfaces.IVirtualStream;
|
||||
};
|
||||
}
|
162
ts_interfaces/requests/stats.ts
Normal file
162
ts_interfaces/requests/stats.ts
Normal file
@ -0,0 +1,162 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import * as authInterfaces from '../data/auth.js';
|
||||
import * as statsInterfaces from '../data/stats.js';
|
||||
|
||||
// Server Statistics
|
||||
export interface IReq_GetServerStatistics extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_GetServerStatistics
|
||||
> {
|
||||
method: 'getServerStatistics';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
includeHistory?: boolean;
|
||||
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
||||
};
|
||||
response: {
|
||||
stats: statsInterfaces.IServerStats;
|
||||
history?: Array<{
|
||||
timestamp: number;
|
||||
value: number;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
// Email Statistics
|
||||
export interface IReq_GetEmailStatistics extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_GetEmailStatistics
|
||||
> {
|
||||
method: 'getEmailStatistics';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
||||
domain?: string;
|
||||
includeDetails?: boolean;
|
||||
};
|
||||
response: {
|
||||
stats: statsInterfaces.IEmailStats;
|
||||
domainBreakdown?: {
|
||||
[domain: string]: statsInterfaces.IEmailStats;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// DNS Statistics
|
||||
export interface IReq_GetDnsStatistics extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_GetDnsStatistics
|
||||
> {
|
||||
method: 'getDnsStatistics';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
||||
domain?: string;
|
||||
includeQueryTypes?: boolean;
|
||||
};
|
||||
response: {
|
||||
stats: statsInterfaces.IDnsStats;
|
||||
domainBreakdown?: {
|
||||
[domain: string]: statsInterfaces.IDnsStats;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Rate Limit Status
|
||||
export interface IReq_GetRateLimitStatus extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_GetRateLimitStatus
|
||||
> {
|
||||
method: 'getRateLimitStatus';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
domain?: string;
|
||||
ip?: string;
|
||||
includeBlocked?: boolean;
|
||||
};
|
||||
response: {
|
||||
limits: statsInterfaces.IRateLimitInfo[];
|
||||
globalLimit?: {
|
||||
current: number;
|
||||
limit: number;
|
||||
remaining: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Security Metrics
|
||||
export interface IReq_GetSecurityMetrics extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_GetSecurityMetrics
|
||||
> {
|
||||
method: 'getSecurityMetrics';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
timeRange?: '1h' | '6h' | '24h' | '7d' | '30d';
|
||||
includeDetails?: boolean;
|
||||
};
|
||||
response: {
|
||||
metrics: statsInterfaces.ISecurityMetrics;
|
||||
trends?: {
|
||||
spam: Array<{ timestamp: number; value: number }>;
|
||||
malware: Array<{ timestamp: number; value: number }>;
|
||||
phishing: Array<{ timestamp: number; value: number }>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Active Connections
|
||||
export interface IReq_GetActiveConnections extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_GetActiveConnections
|
||||
> {
|
||||
method: 'getActiveConnections';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
protocol?: 'smtp' | 'smtps' | 'http' | 'https';
|
||||
state?: string;
|
||||
};
|
||||
response: {
|
||||
connections: statsInterfaces.IConnectionInfo[];
|
||||
summary: {
|
||||
total: number;
|
||||
byProtocol: {
|
||||
[protocol: string]: number;
|
||||
};
|
||||
byState: {
|
||||
[state: string]: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Queue Status
|
||||
export interface IReq_GetQueueStatus extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_GetQueueStatus
|
||||
> {
|
||||
method: 'getQueueStatus';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
queueName?: string;
|
||||
};
|
||||
response: {
|
||||
queues: statsInterfaces.IQueueStatus[];
|
||||
totalItems: number;
|
||||
};
|
||||
}
|
||||
|
||||
// Health Check
|
||||
export interface IReq_GetHealthStatus extends plugins.typedrequestInterfaces.implementsTR<
|
||||
plugins.typedrequestInterfaces.ITypedRequest,
|
||||
IReq_GetHealthStatus
|
||||
> {
|
||||
method: 'getHealthStatus';
|
||||
request: {
|
||||
identity?: authInterfaces.IIdentity;
|
||||
detailed?: boolean;
|
||||
};
|
||||
response: {
|
||||
health: statsInterfaces.IHealthStatus;
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user