← back to Dear Bubbe Nextjs

archived/tests/monitoring-dashboard.html

356 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dear Bubbe - Health Monitoring Dashboard</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            min-height: 100vh;
            padding: 20px;
        }
        
        .dashboard {
            max-width: 1200px;
            margin: 0 auto;
        }
        
        .header {
            text-align: center;
            margin-bottom: 30px;
        }
        
        .header h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
        }
        
        .header p {
            font-size: 1.1rem;
            opacity: 0.9;
        }
        
        .stats-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 20px;
            margin-bottom: 30px;
        }
        
        .stat-card {
            background: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            border-radius: 15px;
            padding: 25px;
            border: 1px solid rgba(255, 255, 255, 0.2);
        }
        
        .stat-title {
            font-size: 1.2rem;
            margin-bottom: 15px;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        .status-indicator {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            display: inline-block;
        }
        
        .status-online { background: #10B981; }
        .status-warning { background: #F59E0B; }
        .status-error { background: #EF4444; }
        
        .stat-value {
            font-size: 2rem;
            font-weight: bold;
            margin-bottom: 10px;
        }
        
        .stat-detail {
            font-size: 0.9rem;
            opacity: 0.8;
        }
        
        .logs-section {
            background: rgba(0, 0, 0, 0.3);
            border-radius: 15px;
            padding: 25px;
            margin-bottom: 20px;
        }
        
        .logs-header {
            display: flex;
            justify-content: between;
            align-items: center;
            margin-bottom: 15px;
        }
        
        .logs-content {
            background: #1a1a1a;
            border-radius: 8px;
            padding: 15px;
            font-family: 'Courier New', monospace;
            max-height: 400px;
            overflow-y: auto;
            font-size: 0.85rem;
            line-height: 1.4;
        }
        
        .log-line {
            margin-bottom: 2px;
            padding: 2px 0;
        }
        
        .log-pass { color: #10B981; }
        .log-warn { color: #F59E0B; }
        .log-error { color: #EF4444; }
        .log-info { color: #60A5FA; }
        
        .refresh-btn {
            background: rgba(255, 255, 255, 0.2);
            border: none;
            color: white;
            padding: 10px 20px;
            border-radius: 8px;
            cursor: pointer;
            font-size: 0.9rem;
            transition: all 0.3s ease;
        }
        
        .refresh-btn:hover {
            background: rgba(255, 255, 255, 0.3);
        }
        
        .footer {
            text-align: center;
            margin-top: 30px;
            opacity: 0.8;
        }
        
        @media (max-width: 768px) {
            .header h1 {
                font-size: 2rem;
            }
            
            .stats-grid {
                grid-template-columns: 1fr;
            }
            
            .stat-card {
                padding: 20px;
            }
        }
    </style>
</head>
<body>
    <div class="dashboard">
        <div class="header">
            <h1>👵 Dear Bubbe Health Monitor</h1>
            <p>24/7 monitoring to keep Bubbe kvetching without interruption</p>
        </div>
        
        <div class="stats-grid">
            <div class="stat-card">
                <div class="stat-title">
                    <span class="status-indicator" id="pm2-indicator"></span>
                    PM2 Process Status
                </div>
                <div class="stat-value" id="pm2-status">Loading...</div>
                <div class="stat-detail" id="pm2-detail">Checking process health</div>
            </div>
            
            <div class="stat-card">
                <div class="stat-title">
                    <span class="status-indicator" id="api-indicator"></span>
                    API Response Time
                </div>
                <div class="stat-value" id="api-response">Loading...</div>
                <div class="stat-detail" id="api-detail">Last checked: Never</div>
            </div>
            
            <div class="stat-card">
                <div class="stat-title">
                    <span class="status-indicator" id="voice-indicator"></span>
                    Voice API Status
                </div>
                <div class="stat-value" id="voice-status">Loading...</div>
                <div class="stat-detail" id="voice-detail">ElevenLabs integration</div>
            </div>
            
            <div class="stat-card">
                <div class="stat-title">
                    <span class="status-indicator" id="memory-indicator"></span>
                    Memory Usage
                </div>
                <div class="stat-value" id="memory-usage">Loading...</div>
                <div class="stat-detail" id="memory-detail">Current consumption</div>
            </div>
            
            <div class="stat-card">
                <div class="stat-title">
                    <span class="status-indicator" id="uptime-indicator"></span>
                    System Uptime
                </div>
                <div class="stat-value" id="uptime-value">Loading...</div>
                <div class="stat-detail" id="uptime-detail">Since last restart</div>
            </div>
            
            <div class="stat-card">
                <div class="stat-title">
                    <span class="status-indicator" id="health-indicator"></span>
                    Overall Health Score
                </div>
                <div class="stat-value" id="health-score">Loading...</div>
                <div class="stat-detail" id="health-detail">Last check results</div>
            </div>
        </div>
        
        <div class="logs-section">
            <div class="logs-header">
                <h3>Recent Health Check Logs</h3>
                <button class="refresh-btn" onclick="refreshLogs()">🔄 Refresh Logs</button>
            </div>
            <div class="logs-content" id="logs-content">
                Loading recent health check logs...
            </div>
        </div>
        
        <div class="footer">
            <p>Auto-refreshes every 30 seconds • Health checks run every 5 minutes</p>
            <p>Last updated: <span id="last-update">Loading...</span></p>
        </div>
    </div>

    <script>
        // Update timestamps
        function updateTimestamp() {
            document.getElementById('last-update').textContent = new Date().toLocaleString();
        }
        
        // Fetch and display logs
        async function refreshLogs() {
            try {
                const response = await fetch('/api/health-logs');
                const logs = await response.text();
                
                const logsContent = document.getElementById('logs-content');
                const lines = logs.split('\n').slice(-50); // Last 50 lines
                
                logsContent.innerHTML = lines
                    .filter(line => line.trim())
                    .map(line => {
                        let className = 'log-info';
                        if (line.includes('✅') || line.includes('PASS')) className = 'log-pass';
                        else if (line.includes('⚠️') || line.includes('WARN')) className = 'log-warn';
                        else if (line.includes('❌') || line.includes('FAIL') || line.includes('ERROR')) className = 'log-error';
                        
                        return `<div class="log-line ${className}">${escapeHtml(line)}</div>`;
                    })
                    .join('');
                    
                // Auto-scroll to bottom
                logsContent.scrollTop = logsContent.scrollHeight;
            } catch (error) {
                console.error('Failed to fetch logs:', error);
                document.getElementById('logs-content').innerHTML = 
                    '<div class="log-line log-error">Failed to load logs. Check if health monitoring is running.</div>';
            }
        }
        
        // Fetch system stats
        async function refreshStats() {
            try {
                // PM2 Status
                const pm2Response = await fetch('/api/pm2-status');
                const pm2Data = await pm2Response.json();
                updateStatusCard('pm2', pm2Data.status, pm2Data.details);
                
                // API Response Time
                const apiStart = Date.now();
                const apiResponse = await fetch('/api/chat', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ message: 'ping', userId: 'monitor', mode: 'bubbe' })
                });
                const apiTime = Date.now() - apiStart;
                updateStatusCard('api', apiResponse.ok ? 'online' : 'error', `${apiTime}ms response`);
                
                // Voice API
                const voiceStart = Date.now();
                const voiceResponse = await fetch('/api/bubbe-voice', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ text: 'test', voiceId: '21m00Tcm4TlvDq8ikWAM' })
                });
                const voiceTime = Date.now() - voiceStart;
                updateStatusCard('voice', voiceResponse.ok ? 'online' : 'warning', `${voiceTime}ms response`);
                
                // Memory and other stats would require server endpoints
                
            } catch (error) {
                console.error('Failed to fetch stats:', error);
            }
        }
        
        // Update status card
        function updateStatusCard(type, status, details) {
            const indicator = document.getElementById(`${type}-indicator`);
            const statusElement = document.getElementById(`${type}-status`);
            const detailElement = document.getElementById(`${type}-detail`);
            
            // Update indicator color
            indicator.className = 'status-indicator';
            if (status === 'online') indicator.classList.add('status-online');
            else if (status === 'warning') indicator.classList.add('status-warning');
            else indicator.classList.add('status-error');
            
            // Update status text
            if (type === 'api' && status === 'online') {
                statusElement.textContent = details;
                detailElement.textContent = 'Chat API responding';
            } else if (type === 'voice' && status === 'online') {
                statusElement.textContent = details;
                detailElement.textContent = 'ElevenLabs responding';
            } else {
                statusElement.textContent = status.toUpperCase();
                detailElement.textContent = details;
            }
        }
        
        // Escape HTML
        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }
        
        // Initialize dashboard
        function init() {
            updateTimestamp();
            refreshLogs();
            refreshStats();
            
            // Set up auto-refresh
            setInterval(refreshLogs, 30000); // Every 30 seconds
            setInterval(refreshStats, 45000); // Every 45 seconds
            setInterval(updateTimestamp, 1000); // Every second
        }
        
        // Start dashboard when page loads
        window.addEventListener('load', init);
    </script>
</body>
</html>