feat(ops-server): implement TypedRouter integration and modular handler classes
This commit is contained in:
344
ts/opsserver/handlers/stats.handler.ts
Normal file
344
ts/opsserver/handlers/stats.handler.ts
Normal file
@ -0,0 +1,344 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
import type { OpsServer } from '../classes.opsserver.js';
|
||||
import * as interfaces from '../../../ts_interfaces/index.js';
|
||||
|
||||
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(): void {
|
||||
// Server Statistics Handler
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetServerStatistics>(
|
||||
'getServerStatistics',
|
||||
async (dataArg, toolsArg) => {
|
||||
const stats = await this.collectServerStats();
|
||||
return {
|
||||
stats: {
|
||||
uptime: stats.uptime,
|
||||
startTime: Date.now() - (stats.uptime * 1000),
|
||||
memoryUsage: stats.memoryUsage,
|
||||
cpuUsage: stats.cpuUsage,
|
||||
activeConnections: stats.activeConnections,
|
||||
totalConnections: stats.totalConnections,
|
||||
},
|
||||
history: dataArg.includeHistory ? stats.history : undefined,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Email Statistics Handler
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetEmailStatistics>(
|
||||
'getEmailStatistics',
|
||||
async (dataArg, toolsArg) => {
|
||||
const emailServer = this.opsServerRef.dcRouterRef.emailServer;
|
||||
if (!emailServer) {
|
||||
return {
|
||||
stats: {
|
||||
sent: 0,
|
||||
received: 0,
|
||||
bounced: 0,
|
||||
queued: 0,
|
||||
failed: 0,
|
||||
averageDeliveryTime: 0,
|
||||
deliveryRate: 0,
|
||||
bounceRate: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const stats = await this.collectEmailStats();
|
||||
return {
|
||||
stats: {
|
||||
sent: stats.sentToday,
|
||||
received: stats.receivedToday,
|
||||
bounced: Math.floor(stats.sentToday * stats.bounceRate / 100),
|
||||
queued: stats.queueSize,
|
||||
failed: 0,
|
||||
averageDeliveryTime: 0,
|
||||
deliveryRate: stats.deliveryRate,
|
||||
bounceRate: stats.bounceRate,
|
||||
},
|
||||
domainBreakdown: dataArg.includeDetails ? stats.domainBreakdown : undefined,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// DNS Statistics Handler
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetDnsStatistics>(
|
||||
'getDnsStatistics',
|
||||
async (dataArg, toolsArg) => {
|
||||
const dnsServer = this.opsServerRef.dcRouterRef.dnsServer;
|
||||
if (!dnsServer) {
|
||||
return {
|
||||
stats: {
|
||||
totalQueries: 0,
|
||||
cacheHits: 0,
|
||||
cacheMisses: 0,
|
||||
cacheHitRate: 0,
|
||||
activeDomains: 0,
|
||||
averageResponseTime: 0,
|
||||
queryTypes: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const stats = await this.collectDnsStats();
|
||||
return {
|
||||
stats: {
|
||||
totalQueries: stats.totalQueries,
|
||||
cacheHits: stats.cacheHits,
|
||||
cacheMisses: stats.cacheMisses,
|
||||
cacheHitRate: stats.cacheHitRate,
|
||||
activeDomains: stats.topDomains.length,
|
||||
averageResponseTime: 0,
|
||||
queryTypes: stats.queryTypes,
|
||||
},
|
||||
domainBreakdown: dataArg.includeQueryTypes ? stats.domainBreakdown : undefined,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Queue Status Handler
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetQueueStatus>(
|
||||
'getQueueStatus',
|
||||
async (dataArg, toolsArg) => {
|
||||
const emailServer = this.opsServerRef.dcRouterRef.emailServer;
|
||||
const queues: interfaces.data.IQueueStatus[] = [];
|
||||
|
||||
if (emailServer) {
|
||||
const status = await this.getQueueStatus();
|
||||
queues.push({
|
||||
name: dataArg.queueName || 'default',
|
||||
size: status.pending,
|
||||
processing: status.active,
|
||||
failed: status.failed,
|
||||
retrying: status.retrying,
|
||||
averageProcessingTime: 0,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
queues,
|
||||
totalItems: queues.reduce((sum, q) => sum + q.size + q.processing + q.failed + q.retrying, 0),
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Health Status Handler
|
||||
this.typedrouter.addTypedHandler(
|
||||
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetHealthStatus>(
|
||||
'getHealthStatus',
|
||||
async (dataArg, toolsArg) => {
|
||||
const health = await this.checkHealthStatus();
|
||||
return {
|
||||
health: {
|
||||
healthy: health.healthy,
|
||||
uptime: process.uptime(),
|
||||
services: health.services.reduce((acc, service) => {
|
||||
acc[service.name] = {
|
||||
status: service.status,
|
||||
message: service.message,
|
||||
lastCheck: Date.now(),
|
||||
};
|
||||
return acc;
|
||||
}, {} as any),
|
||||
version: '2.12.0', // TODO: Get from package.json
|
||||
},
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private async collectServerStats(): Promise<{
|
||||
uptime: number;
|
||||
cpuUsage: {
|
||||
user: number;
|
||||
system: number;
|
||||
};
|
||||
memoryUsage: interfaces.data.IServerStats['memoryUsage'];
|
||||
requestsPerSecond: number;
|
||||
activeConnections: number;
|
||||
totalConnections: number;
|
||||
history: Array<{
|
||||
timestamp: number;
|
||||
value: number;
|
||||
}>;
|
||||
}> {
|
||||
const uptime = process.uptime();
|
||||
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;
|
||||
|
||||
// TODO: Implement proper request tracking
|
||||
const requestsPerSecond = 0;
|
||||
const activeConnections = 0;
|
||||
const totalConnections = 0;
|
||||
|
||||
return {
|
||||
uptime,
|
||||
cpuUsage: {
|
||||
user: cpuUsage * 0.7, // Approximate user CPU
|
||||
system: cpuUsage * 0.3, // Approximate system CPU
|
||||
},
|
||||
memoryUsage: {
|
||||
heapUsed: memUsage.heapUsed,
|
||||
heapTotal: memUsage.heapTotal,
|
||||
external: memUsage.external,
|
||||
rss: memUsage.rss,
|
||||
},
|
||||
requestsPerSecond,
|
||||
activeConnections,
|
||||
totalConnections,
|
||||
history: [], // TODO: Implement history tracking
|
||||
};
|
||||
}
|
||||
|
||||
private async collectEmailStats(): Promise<{
|
||||
sentToday: number;
|
||||
receivedToday: number;
|
||||
bounceRate: number;
|
||||
deliveryRate: number;
|
||||
queueSize: number;
|
||||
domainBreakdown?: { [domain: string]: interfaces.data.IEmailStats };
|
||||
}> {
|
||||
// TODO: Implement actual email statistics collection
|
||||
return {
|
||||
sentToday: 0,
|
||||
receivedToday: 0,
|
||||
bounceRate: 0,
|
||||
deliveryRate: 100,
|
||||
queueSize: 0,
|
||||
};
|
||||
}
|
||||
|
||||
private async collectDnsStats(): Promise<{
|
||||
queriesPerSecond: number;
|
||||
totalQueries: number;
|
||||
cacheHits: number;
|
||||
cacheMisses: number;
|
||||
cacheHitRate: number;
|
||||
topDomains: Array<{
|
||||
domain: string;
|
||||
count: number;
|
||||
}>;
|
||||
queryTypes: { [key: string]: number };
|
||||
domainBreakdown?: { [domain: string]: interfaces.data.IDnsStats };
|
||||
}> {
|
||||
// TODO: Implement actual DNS statistics collection
|
||||
return {
|
||||
queriesPerSecond: 0,
|
||||
totalQueries: 0,
|
||||
cacheHits: 0,
|
||||
cacheMisses: 0,
|
||||
cacheHitRate: 0,
|
||||
topDomains: [],
|
||||
queryTypes: {},
|
||||
};
|
||||
}
|
||||
|
||||
private async getQueueStatus(): Promise<{
|
||||
pending: number;
|
||||
active: number;
|
||||
failed: number;
|
||||
retrying: number;
|
||||
items: Array<{
|
||||
id: string;
|
||||
recipient: string;
|
||||
subject: string;
|
||||
status: string;
|
||||
attempts: number;
|
||||
nextRetry?: number;
|
||||
}>;
|
||||
}> {
|
||||
// TODO: Implement actual queue status collection
|
||||
return {
|
||||
pending: 0,
|
||||
active: 0,
|
||||
failed: 0,
|
||||
retrying: 0,
|
||||
items: [],
|
||||
};
|
||||
}
|
||||
|
||||
private async checkHealthStatus(): Promise<{
|
||||
healthy: boolean;
|
||||
services: Array<{
|
||||
name: string;
|
||||
status: 'healthy' | 'degraded' | 'unhealthy';
|
||||
message?: string;
|
||||
}>;
|
||||
checks: Array<{
|
||||
name: string;
|
||||
passed: boolean;
|
||||
message?: string;
|
||||
}>;
|
||||
}> {
|
||||
const services: Array<{
|
||||
name: string;
|
||||
status: 'healthy' | 'degraded' | 'unhealthy';
|
||||
message?: string;
|
||||
}> = [];
|
||||
|
||||
// Check HTTP Proxy
|
||||
if (this.opsServerRef.dcRouterRef.smartProxy) {
|
||||
services.push({
|
||||
name: 'HTTP/HTTPS Proxy',
|
||||
status: 'healthy',
|
||||
});
|
||||
}
|
||||
|
||||
// Check Email Server
|
||||
if (this.opsServerRef.dcRouterRef.emailServer) {
|
||||
services.push({
|
||||
name: 'Email Server',
|
||||
status: 'healthy',
|
||||
});
|
||||
}
|
||||
|
||||
// Check DNS Server
|
||||
if (this.opsServerRef.dcRouterRef.dnsServer) {
|
||||
services.push({
|
||||
name: 'DNS Server',
|
||||
status: 'healthy',
|
||||
});
|
||||
}
|
||||
|
||||
// Check OpsServer
|
||||
services.push({
|
||||
name: 'OpsServer',
|
||||
status: 'healthy',
|
||||
});
|
||||
|
||||
const healthy = services.every(s => s.status === 'healthy');
|
||||
|
||||
return {
|
||||
healthy,
|
||||
services,
|
||||
checks: [
|
||||
{
|
||||
name: 'Memory Usage',
|
||||
passed: process.memoryUsage().heapUsed < (plugins.os.totalmem() * 0.9),
|
||||
message: 'Memory usage within limits',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user