264 lines
7.8 KiB
JavaScript
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);
|
|
}); |