dashboard.ts•7.98 kB
/**
* Simple monitoring dashboard for health checks and metrics
*/
import { metricsCollector } from './metrics-collector.js';
import { healthChecker } from './health-checker.js';
import { logger } from '../logging/logger.js';
export interface DashboardData {
health: any;
metrics: {
system: any;
operations: any[];
summary: any;
};
timestamp: string;
}
export class MonitoringDashboard {
/**
* Get dashboard data
*/
public async getDashboardData(): Promise<DashboardData> {
try {
const [health, systemMetrics, operationMetrics] = await Promise.all([
healthChecker.runChecks(),
Promise.resolve(metricsCollector.getSystemMetrics()),
Promise.resolve(metricsCollector.getOperationMetrics())
]);
return {
health,
metrics: {
system: systemMetrics,
operations: operationMetrics,
summary: metricsCollector.getMetricsSummary()
},
timestamp: new Date().toISOString()
};
} catch (error) {
logger.error('Failed to get dashboard data', error as Error);
throw error;
}
}
/**
* Get health status only
*/
public async getHealthStatus(): Promise<any> {
try {
return await healthChecker.runChecks();
} catch (error) {
logger.error('Failed to get health status', error as Error);
return {
status: 'unhealthy',
timestamp: new Date().toISOString(),
uptime: process.uptime() * 1000,
version: process.env.npm_package_version || '1.0.0',
checks: [],
summary: { healthy: 0, degraded: 0, unhealthy: 1 },
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
/**
* Get metrics only
*/
public getMetrics(): any {
try {
return {
system: metricsCollector.getSystemMetrics(),
operations: metricsCollector.getOperationMetrics(),
summary: metricsCollector.getMetricsSummary(),
timestamp: new Date().toISOString()
};
} catch (error) {
logger.error('Failed to get metrics', error as Error);
throw error;
}
}
/**
* Generate simple HTML dashboard
*/
public async generateHtmlDashboard(): Promise<string> {
try {
const data = await this.getDashboardData();
return `
<!DOCTYPE html>
<html>
<head>
<title>Google Drive MCP Server - Monitoring</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
.container { max-width: 1200px; margin: 0 auto; }
.card { background: white; padding: 20px; margin: 10px 0; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.status { padding: 10px; border-radius: 4px; font-weight: bold; text-align: center; margin: 10px 0; }
.healthy { background: #d4edda; color: #155724; }
.degraded { background: #fff3cd; color: #856404; }
.unhealthy { background: #f8d7da; color: #721c24; }
.metric { display: inline-block; margin: 10px; padding: 10px; background: #e9ecef; border-radius: 4px; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }
h1, h2 { color: #333; }
.timestamp { color: #666; font-size: 0.9em; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
th { background-color: #f8f9fa; }
</style>
</head>
<body>
<div class="container">
<h1>Google Drive MCP Server - Monitoring Dashboard</h1>
<p class="timestamp">Last updated: ${data.timestamp}</p>
<div class="grid">
<div class="card">
<h2>System Health</h2>
<div class="status ${data.health.status}">
Status: ${data.health.status.toUpperCase()}
</div>
<p>Uptime: ${Math.round(data.health.uptime / 1000 / 60)} minutes</p>
<p>Version: ${data.health.version}</p>
<p>Checks: ${data.health.summary.healthy} healthy, ${data.health.summary.degraded} degraded, ${data.health.summary.unhealthy} unhealthy</p>
</div>
<div class="card">
<h2>System Metrics</h2>
<div class="metric">Memory: ${(data.metrics.system.memory.used / 1024 / 1024).toFixed(2)}MB (${data.metrics.system.memory.percentage.toFixed(2)}%)</div>
<div class="metric">CPU: ${data.metrics.system.cpu.usage.toFixed(2)}%</div>
<div class="metric">Active Requests: ${data.metrics.system.requests.active}</div>
<div class="metric">Cache Hit Rate: ${data.metrics.system.cache.hitRate.toFixed(2)}%</div>
</div>
</div>
<div class="card">
<h2>Health Checks</h2>
<table>
<thead>
<tr>
<th>Check</th>
<th>Status</th>
<th>Message</th>
<th>Duration</th>
<th>Last Checked</th>
</tr>
</thead>
<tbody>
${data.health.checks.map((check: any) => `
<tr>
<td>${check.name}</td>
<td><span class="status ${check.status}">${check.status}</span></td>
<td>${check.message}</td>
<td>${check.duration}ms</td>
<td>${new Date(check.lastChecked).toLocaleString()}</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
${data.metrics.operations.length > 0 ? `
<div class="card">
<h2>Operation Metrics</h2>
<table>
<thead>
<tr>
<th>Operation</th>
<th>Count</th>
<th>Avg Duration</th>
<th>Success Rate</th>
<th>Last Executed</th>
</tr>
</thead>
<tbody>
${data.metrics.operations.map((op: any) => `
<tr>
<td>${op.operation}</td>
<td>${op.count}</td>
<td>${op.averageDuration.toFixed(2)}ms</td>
<td>${op.successRate.toFixed(2)}%</td>
<td>${new Date(op.lastExecuted).toLocaleString()}</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
` : ''}
<div class="card">
<h2>Request Metrics</h2>
<div class="metric">Total Requests: ${data.metrics.system.requests.total}</div>
<div class="metric">Average Response Time: ${data.metrics.system.requests.averageResponseTime.toFixed(2)}ms</div>
<div class="metric">Error Rate: ${data.metrics.system.requests.errorRate.toFixed(2)}%</div>
</div>
</div>
<script>
// Auto-refresh every 30 seconds
setTimeout(() => {
window.location.reload();
}, 30000);
</script>
</body>
</html>`;
} catch (error) {
logger.error('Failed to generate HTML dashboard', error as Error);
return `
<!DOCTYPE html>
<html>
<head><title>Monitoring Dashboard - Error</title></head>
<body>
<h1>Error</h1>
<p>Failed to load dashboard data: ${error instanceof Error ? error.message : 'Unknown error'}</p>
</body>
</html>`;
}
}
}
// Export singleton instance
export const monitoringDashboard = new MonitoringDashboard();