← back to Melanie Project

public/agents-app.js

386 lines

// Agent Dashboard Client
class AgentDashboard {
    constructor() {
        this.agents = new Map();
        this.logs = [];
        this.settings = this.loadSettings();
        this.ws = null;
        this.startTime = Date.now();

        this.init();
    }

    init() {
        this.setupNavigation();
        this.connectWebSocket();
        this.startPolling();
        this.updateSystemStatus();

        // Add sample initial logs
        this.addLog('info', 'server-health', 'Agent dashboard initialized');
        this.addLog('info', 'api-monitor', 'Monitoring Anthropic API health');
        this.addLog('info', 'upload-guardian', 'File upload monitoring active');
        this.addLog('info', 'ssl-cert', 'SSL certificate monitoring active');
    }

    setupNavigation() {
        const tabs = document.querySelectorAll('.nav-tab');
        tabs.forEach(tab => {
            tab.addEventListener('click', () => {
                const view = tab.dataset.view;
                this.switchView(view);

                // Update active tab
                tabs.forEach(t => t.classList.remove('active'));
                tab.classList.add('active');
            });
        });
    }

    switchView(view) {
        const views = document.querySelectorAll('.view-container');
        views.forEach(v => v.classList.remove('active'));

        const targetView = document.getElementById(`${view}View`);
        if (targetView) {
            targetView.classList.add('active');

            // Refresh data for the view
            if (view === 'monitoring') {
                this.updateMonitoringCharts();
            } else if (view === 'logs') {
                this.renderLogs();
            }
        }
    }

    connectWebSocket() {
        const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
        const wsUrl = `${protocol}//${window.location.host}`;

        try {
            this.ws = new WebSocket(wsUrl);

            this.ws.onopen = () => {
                console.log('WebSocket connected');
                this.addLog('info', 'system', 'Real-time monitoring connected');
            };

            this.ws.onmessage = (event) => {
                const data = JSON.parse(event.data);
                this.handleWebSocketMessage(data);
            };

            this.ws.onerror = () => {
                console.log('WebSocket not available, using polling');
                this.addLog('warning', 'system', 'Real-time updates unavailable, using polling');
            };

            this.ws.onclose = () => {
                setTimeout(() => this.connectWebSocket(), 5000);
            };
        } catch (error) {
            console.log('WebSocket connection failed, using polling');
        }
    }

    handleWebSocketMessage(data) {
        if (data.type === 'agent-update') {
            this.updateAgentMetrics(data.agent, data.metrics);
        } else if (data.type === 'log') {
            this.addLog(data.level, data.agent, data.message);
        } else if (data.type === 'system-status') {
            this.updateSystemStatus(data.status);
        }
    }

    startPolling() {
        // Poll for updates every 5 seconds
        setInterval(() => {
            this.fetchAgentStatus();
            this.updateUptime();
        }, 5000);

        // Initial fetch
        this.fetchAgentStatus();
    }

    async fetchAgentStatus() {
        try {
            const response = await fetch('/api/agents/status');
            if (response.ok) {
                const data = await response.json();
                this.updateAllAgents(data);
            }
        } catch (error) {
            console.error('Failed to fetch agent status:', error);
            this.addLog('error', 'system', 'Failed to fetch agent status');
        }
    }

    updateAllAgents(data) {
        if (data.serverHealth) {
            this.updateAgentMetrics('server-health', {
                uptime: this.formatUptime(Date.now() - this.startTime),
                lastCheck: 'Just now',
                restarts: data.serverHealth.restarts || 0
            });
        }

        if (data.apiMonitor) {
            this.updateAgentMetrics('api-monitor', {
                successRate: data.apiMonitor.successRate || '100%',
                avgResponse: data.apiMonitor.avgResponse || '0ms',
                autoFixes: data.apiMonitor.autoFixes || 0
            });
        }

        if (data.uploadGuardian) {
            this.updateAgentMetrics('upload-guardian', {
                uploadCount: data.uploadGuardian.uploadCount || 0,
                uploadFailed: data.uploadGuardian.uploadFailed || 0,
                uploadFixed: data.uploadGuardian.uploadFixed || 0
            });
        }

        if (data.sslCert) {
            this.updateAgentMetrics('ssl-cert', {
                sslExpiry: data.sslCert.expiresIn || 'Unknown',
                sslStatus: data.sslCert.status || 'Valid',
                sslRenewals: data.sslCert.renewals || 0
            });
        }
    }

    updateAgentMetrics(agentId, metrics) {
        for (const [key, value] of Object.entries(metrics)) {
            const element = document.getElementById(`${agentId.replace('-health', '')}-${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`);
            if (element) {
                element.textContent = value;
            }
        }
    }

    updateUptime() {
        const uptime = Date.now() - this.startTime;
        const uptimeElement = document.getElementById('server-uptime');
        if (uptimeElement) {
            uptimeElement.textContent = this.formatUptime(uptime);
        }
    }

    formatUptime(ms) {
        const seconds = Math.floor(ms / 1000);
        const minutes = Math.floor(seconds / 60);
        const hours = Math.floor(minutes / 60);
        const days = Math.floor(hours / 24);

        if (days > 0) return `${days}d ${hours % 24}h`;
        if (hours > 0) return `${hours}h ${minutes % 60}m`;
        if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
        return `${seconds}s`;
    }

    updateSystemStatus(status = 'healthy') {
        const statusElement = document.getElementById('systemStatus');
        const indicator = statusElement.querySelector('.status-indicator');
        const text = statusElement.querySelector('.status-text');

        indicator.className = 'status-indicator';

        if (status === 'healthy') {
            text.textContent = 'All Systems Operational';
            // Green indicator (default)
        } else if (status === 'warning') {
            text.textContent = 'Minor Issues Detected';
            indicator.classList.add('warning');
        } else if (status === 'error') {
            text.textContent = 'Critical Issues';
            indicator.classList.add('error');
        }
    }

    addLog(level, agent, message) {
        const logEntry = {
            timestamp: new Date().toISOString(),
            level,
            agent,
            message
        };

        this.logs.unshift(logEntry);

        // Keep only last 1000 logs
        if (this.logs.length > 1000) {
            this.logs = this.logs.slice(0, 1000);
        }

        // Update logs view if it's active
        if (document.getElementById('logsView').classList.contains('active')) {
            this.renderLogs();
        }
    }

    renderLogs() {
        const container = document.getElementById('logsContainer');
        const agentFilter = document.getElementById('logFilter').value;
        const levelFilter = document.getElementById('logLevel').value;

        let filteredLogs = this.logs;

        if (agentFilter !== 'all') {
            filteredLogs = filteredLogs.filter(log => log.agent === agentFilter);
        }

        if (levelFilter !== 'all') {
            filteredLogs = filteredLogs.filter(log => log.level === levelFilter);
        }

        if (filteredLogs.length === 0) {
            container.innerHTML = '<p style="color: var(--text-secondary);">No logs to display</p>';
            return;
        }

        container.innerHTML = filteredLogs.map(log => `
            <div class="log-entry ${log.level}">
                <span class="log-timestamp">${new Date(log.timestamp).toLocaleTimeString()}</span>
                <span class="log-agent">[${log.agent}]</span>
                <span class="log-message">${log.message}</span>
            </div>
        `).join('');
    }

    updateMonitoringCharts() {
        // Placeholder for charts - would integrate with Chart.js in production
        document.getElementById('total-requests').textContent = this.logs.length;
        document.getElementById('total-fixes').textContent =
            this.logs.filter(log => log.level === 'fix').length;
        document.getElementById('total-uptime').textContent = '99.9%';
        document.getElementById('avg-response').textContent = '234ms';
    }

    loadSettings() {
        const defaultSettings = {
            autoRestart: true,
            autoRetry: true,
            autoRenewSSL: true,
            autoCompressUploads: true,
            serverCheckInterval: 30,
            apiCheckInterval: 60,
            sslCheckInterval: 24
        };

        try {
            const saved = localStorage.getItem('agentSettings');
            return saved ? { ...defaultSettings, ...JSON.parse(saved) } : defaultSettings;
        } catch {
            return defaultSettings;
        }
    }

    saveSettings() {
        const settings = {
            autoRestart: document.getElementById('autoRestart').checked,
            autoRetry: document.getElementById('autoRetry').checked,
            autoRenewSSL: document.getElementById('autoRenewSSL').checked,
            autoCompressUploads: document.getElementById('autoCompressUploads').checked,
            serverCheckInterval: parseInt(document.getElementById('serverCheckInterval').value),
            apiCheckInterval: parseInt(document.getElementById('apiCheckInterval').value),
            sslCheckInterval: parseInt(document.getElementById('sslCheckInterval').value)
        };

        localStorage.setItem('agentSettings', JSON.stringify(settings));
        this.settings = settings;

        // Send to backend
        fetch('/api/agents/settings', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(settings)
        });

        this.addLog('info', 'system', 'Settings saved successfully');
        alert('Settings saved successfully!');
    }
}

// Global functions for button actions
let dashboard;

window.addEventListener('DOMContentLoaded', () => {
    dashboard = new AgentDashboard();
});

function restartAgent(agentId) {
    dashboard.addLog('info', agentId, 'Manual restart triggered');
    fetch(`/api/agents/${agentId}/restart`, { method: 'POST' })
        .then(() => dashboard.addLog('fix', agentId, 'Agent restarted successfully'))
        .catch(() => dashboard.addLog('error', agentId, 'Failed to restart agent'));
}

function viewLogs(agentId) {
    document.getElementById('logFilter').value = agentId;
    dashboard.switchView('logs');
    document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active'));
    document.querySelector('[data-view="logs"]').classList.add('active');
    dashboard.renderLogs();
}

function testAPI() {
    dashboard.addLog('info', 'api-monitor', 'Running API health check...');
    fetch('/api/agents/test-api', { method: 'POST' })
        .then(res => res.json())
        .then(data => {
            if (data.success) {
                dashboard.addLog('fix', 'api-monitor', `API test successful - ${data.responseTime}ms`);
            } else {
                dashboard.addLog('error', 'api-monitor', `API test failed: ${data.error}`);
            }
        })
        .catch(() => dashboard.addLog('error', 'api-monitor', 'API test failed'));
}

function testUpload() {
    dashboard.addLog('info', 'upload-guardian', 'Running upload test...');
    // Simulate upload test
    setTimeout(() => {
        dashboard.addLog('fix', 'upload-guardian', 'Upload test completed successfully');
    }, 1000);
}

function checkSSL() {
    dashboard.addLog('info', 'ssl-cert', 'Checking SSL certificate...');
    fetch('/api/agents/check-ssl', { method: 'POST' })
        .then(res => res.json())
        .then(data => {
            dashboard.addLog('fix', 'ssl-cert', `SSL valid until: ${data.expiresIn}`);
        })
        .catch(() => dashboard.addLog('error', 'ssl-cert', 'SSL check failed'));
}

function clearLogs() {
    if (confirm('Are you sure you want to clear all logs?')) {
        dashboard.logs = [];
        dashboard.renderLogs();
        dashboard.addLog('info', 'system', 'Logs cleared');
    }
}

function saveSettings() {
    dashboard.saveSettings();
}

// Setup log filter listeners
document.addEventListener('DOMContentLoaded', () => {
    const logFilter = document.getElementById('logFilter');
    const logLevel = document.getElementById('logLevel');

    if (logFilter) {
        logFilter.addEventListener('change', () => dashboard.renderLogs());
    }

    if (logLevel) {
        logLevel.addEventListener('change', () => dashboard.renderLogs());
    }
});