2026-05-27 19:03:20 +08:00

264 lines
7.8 KiB
JavaScript

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);
});