class SystemMonitor { constructor() { this.cpuValueEl = document.getElementById('cpu-value'); this.cpuBarEl = document.getElementById('cpu-bar'); this.memoryValueEl = document.getElementById('memory-value'); this.memoryBarEl = document.getElementById('memory-bar'); this.serverStatusEl = document.getElementById('server-status'); this.lastUpdateEl = document.getElementById('last-update'); this.updateInterval = 1000; this.isOnline = false; this.cpuApiUrl = 'https://dreamlife.indevs.in:61207/api/4/cpu'; this.memoryApiUrl = 'https://dreamlife.indevs.in:61207/api/4/mem'; this.init(); } init() { this.fetchSystemInfo(); setInterval(() => this.fetchSystemInfo(), this.updateInterval); } async fetchSystemInfo() { try { const [cpuResponse, memoryResponse] = await Promise.all([ fetch(this.cpuApiUrl, { method: 'GET', headers: { 'Accept': 'application/json' }, signal: AbortSignal.timeout(5000) }), fetch(this.memoryApiUrl, { method: 'GET', headers: { 'Accept': 'application/json' }, signal: AbortSignal.timeout(5000) }) ]); if (!cpuResponse.ok || !memoryResponse.ok) { throw new Error('API 请求失败'); } const cpuData = await cpuResponse.json(); const memoryData = await memoryResponse.json(); this.updateUI(cpuData, memoryData); this.setOnlineStatus(true); } catch (error) { console.error('获取系统信息失败:', error); this.setOnlineStatus(false); this.showMockData(); } } updateUI(cpuData, memoryData) { // Glances CPU API: // { "total": 12.3, ... } const cpuPercent = Number(cpuData.total || 0).toFixed(2); // Glances MEM API: // { "percent": 45.6, ... } const memoryPercent = Number(memoryData.percent || 0).toFixed(2); this.cpuValueEl.textContent = `${cpuPercent}%`; this.cpuBarEl.style.width = `${Math.round(cpuPercent)}%`; this.memoryValueEl.textContent = `${memoryPercent}%`; this.memoryBarEl.style.width = `${Math.round(memoryPercent)}%`; const now = new Date(); const timeString = now.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', timeZoneName: 'short' }); this.lastUpdateEl.textContent = timeString; } setOnlineStatus(online) { if (this.isOnline !== online) { this.isOnline = online; if (online) { this.serverStatusEl.textContent = '在线'; this.serverStatusEl.className = 'info-value status-online'; } else { this.serverStatusEl.textContent = '离线'; this.serverStatusEl.className = 'info-value status-offline'; } } } showMockData() { const mockCpu = Math.floor(Math.random() * 40) + 20; const mockMemory = Math.floor(Math.random() * 30) + 40; this.cpuValueEl.textContent = `${mockCpu}%`; this.cpuBarEl.style.width = `${mockCpu}%`; this.memoryValueEl.textContent = `${mockMemory}%`; this.memoryBarEl.style.width = `${mockMemory}%`; const now = new Date(); this.lastUpdateEl.textContent = now.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', timeZoneName: 'short' }); } } let isProtocolMenuOpen = false; function toggleProtocolMenu() { const selector = document.getElementById('protocol-selector'); isProtocolMenuOpen = !isProtocolMenuOpen; if (isProtocolMenuOpen) { selector.classList.add('open'); } else { selector.classList.remove('open'); } } function selectProtocol(protocol) { const ports = { 'tcp': 11010, 'udp': 11010, 'wg': 11011, 'ws':11011, 'wss':11012, 'quic':11012, 'faketcp':11013, 'http':80, 'https':443, 'txt':null, 'srv':null }; const protocolNames = { 'tcp': 'TCP', 'udp': 'UDP', 'wg': 'WG', 'ws': 'WS', 'wss': 'WSS', 'quic': 'QUIC', 'faketcp': 'FakeTCP', 'http': 'HTTP', 'https': 'HTTPS', 'txt': 'TXT', 'srv': 'SRV' }; document.getElementById('current-protocol').textContent = protocolNames[protocol]; const addressCode = document.getElementById('node-address'); if (ports[protocol] === null) { addressCode.firstChild.textContent = `${protocol}://dreamlife.indevs.in\n`; } else { addressCode.firstChild.textContent = `${protocol}://dreamlife.indevs.in:${ports[protocol]}\n`; } document.querySelectorAll('.protocol-option').forEach(opt => { opt.classList.remove('active'); }); document.querySelector(`.protocol-option[data-protocol="${protocol}"]`).classList.add('active'); toggleProtocolMenu(); } function copyAddress() { const address = document.getElementById('node-address').textContent; navigator.clipboard.writeText(address).then(() => { const copyIcon = document.getElementById('copy-icon'); const checkIcon = document.getElementById('check-icon'); copyIcon.style.display = 'none'; checkIcon.style.display = 'block'; setTimeout(() => { copyIcon.style.display = 'block'; checkIcon.style.display = 'none'; }, 2000); }).catch(err => { console.error('复制失败:', err); alert('复制失败,请手动复制'); }); } document.addEventListener('click', function(event) { const selector = document.getElementById('protocol-selector'); if (!selector.contains(event.target) && isProtocolMenuOpen) { selector.classList.remove('open'); isProtocolMenuOpen = false; } }); async function testLatency() { const latencyText = document.querySelector('.latency-text'); const testBtn = document.getElementById('latency-test-btn'); testBtn.disabled = true; testBtn.textContent = '测试中...'; latencyText.textContent = '--'; latencyText.className = 'latency-text'; try { const startTime = performance.now(); await fetch('https://dreamlife.indevs.in:61207/api/4/cpu', { method: 'GET', signal: AbortSignal.timeout(5000) }); const endTime = performance.now(); const latency = Math.round(endTime - startTime); latencyText.textContent = `${latency}ms`; if (latency < 100) { latencyText.classList.add('latency-good'); } else if (latency < 300) { latencyText.classList.add('latency-medium'); } else { latencyText.classList.add('latency-bad'); } testBtn.textContent = '重测'; } catch (error) { console.error('延迟测试失败:', error); latencyText.textContent = '超时'; latencyText.classList.add('latency-bad'); testBtn.textContent = '重测'; } finally { testBtn.disabled = false; } } document.addEventListener('DOMContentLoaded', () => { new SystemMonitor(); setTimeout(() => { testLatency(); }, 500); });