diff --git a/static/index.html b/static/index.html
index 47c3115..f29d8d3 100644
--- a/static/index.html
+++ b/static/index.html
@@ -260,6 +260,30 @@
+
+
diff --git a/static/js/server-status.js b/static/js/server-status.js
index 1698321..b7c6f31 100644
--- a/static/js/server-status.js
+++ b/static/js/server-status.js
@@ -1,4 +1,4 @@
-// server-status.js - 服务器状态组件功能实现
+// 服务器状态组件 - 显示CPU使用率和查询统计
// 全局变量
let serverStatusUpdateTimer = null;
@@ -9,208 +9,284 @@ let previousServerData = {
// 初始化服务器状态组件
function initServerStatusWidget() {
- console.log('初始化服务器状态组件');
-
- // 确保组件存在
- if (!document.getElementById('server-status-widget')) {
- console.warn('服务器状态组件不存在');
- return;
+ // 确保DOM元素存在
+ const widget = document.getElementById('server-status-widget');
+ if (!widget) return;
+
+ // 初始化页面类型检测
+ updateWidgetDisplayByPageType();
+
+ // 设置页面切换事件监听
+ handlePageSwitchEvents();
+
+ // 设置WebSocket监听(如果可用)
+ setupWebSocketListeners();
+
+ // 立即加载一次数据
+ loadServerStatusData();
+
+ // 设置定时更新(每5秒更新一次)
+ serverStatusUpdateTimer = setInterval(loadServerStatusData, 5000);
+}
+
+// 判断当前页面是否为仪表盘
+function isCurrentPageDashboard() {
+ // 方法1:检查侧边栏激活状态
+ const dashboardLink = document.querySelector('.sidebar a[href="#dashboard"]');
+ if (dashboardLink && dashboardLink.classList.contains('active')) {
+ return true;
+ }
+
+ // 方法2:检查仪表盘特有元素
+ const dashboardElements = [
+ '#dashboard-container',
+ '.dashboard-summary',
+ '#dashboard-stats'
+ ];
+
+ for (const selector of dashboardElements) {
+ if (document.querySelector(selector)) {
+ return true;
}
-
- // 监听页面切换事件
- handlePageSwitchEvents();
-
- // 初始加载数据
- loadServerStatusData();
-
- // 监听WebSocket连接事件
- monitorWebSocketConnection();
+ }
+
+ // 方法3:检查URL哈希值
+ if (window.location.hash === '#dashboard' || window.location.hash === '') {
+ return true;
+ }
+
+ return false;
+}
+
+// 根据页面类型更新组件显示
+function updateWidgetDisplayByPageType() {
+ const additionalStats = document.getElementById('server-additional-stats');
+ if (!additionalStats) return;
+
+ // 如果当前页面是仪表盘,隐藏额外统计指标
+ if (isCurrentPageDashboard()) {
+ additionalStats.classList.add('hidden');
+ } else {
+ // 非仪表盘页面,显示额外统计指标
+ additionalStats.classList.remove('hidden');
+ }
}
// 处理页面切换事件
function handlePageSwitchEvents() {
- // 监听导航点击事件
- document.querySelectorAll('.sidebar-link').forEach(link => {
- link.addEventListener('click', () => {
- // 页面切换后重新初始化组件
- setTimeout(() => {
- if (document.getElementById('server-status-widget')) {
- loadServerStatusData();
- }
- }, 300);
- });
+ // 监听哈希变化(导航切换)
+ window.addEventListener('hashchange', updateWidgetDisplayByPageType);
+
+ // 监听侧边栏点击事件
+ const sidebarLinks = document.querySelectorAll('.sidebar a');
+ sidebarLinks.forEach(link => {
+ link.addEventListener('click', function() {
+ // 延迟检查,确保页面已切换
+ setTimeout(updateWidgetDisplayByPageType, 100);
});
+ });
+
+ // 监听导航菜单点击事件
+ const navLinks = document.querySelectorAll('nav a');
+ navLinks.forEach(link => {
+ link.addEventListener('click', function() {
+ setTimeout(updateWidgetDisplayByPageType, 100);
+ });
+ });
}
-// 监听WebSocket连接
+// 监控WebSocket连接状态
function monitorWebSocketConnection() {
- // 检查全局WebSocket连接
- if (window.wsConnection && window.wsConnection.readyState === WebSocket.OPEN) {
- setupWebSocketListeners();
- } else {
- // 如果WebSocket未连接,尝试监听其连接状态
- const originalOnOpen = window.wsConnection?.onopen;
- if (window.wsConnection) {
- window.wsConnection.onopen = function(event) {
- if (originalOnOpen) originalOnOpen(event);
- setupWebSocketListeners();
- };
+ // 如果存在WebSocket连接,监听消息
+ if (window.socket) {
+ window.socket.addEventListener('message', function(event) {
+ try {
+ const data = JSON.parse(event.data);
+ if (data.type === 'status_update') {
+ updateServerStatusWidget(data.payload);
}
- }
+ } catch (error) {
+ console.error('解析WebSocket消息失败:', error);
+ }
+ });
+ }
}
// 设置WebSocket监听器
function setupWebSocketListeners() {
- // 保存原始的onmessage函数
- const originalOnMessage = window.wsConnection.onmessage;
-
- // 重写onmessage函数
- window.wsConnection.onmessage = function(event) {
- // 调用原始函数
- if (originalOnMessage) originalOnMessage(event);
-
- // 处理服务器状态数据
- try {
- const data = JSON.parse(event.data);
- if (data.type === 'initial_data' || data.type === 'stats_update') {
- updateServerStatusWidget(data.data);
- }
- } catch (error) {
- console.error('处理服务器状态WebSocket消息失败:', error);
- }
- };
+ // 如果WebSocket已经存在
+ if (window.socket) {
+ monitorWebSocketConnection();
+ } else {
+ // 监听socket初始化事件
+ window.addEventListener('socketInitialized', function() {
+ monitorWebSocketConnection();
+ });
+ }
}
// 加载服务器状态数据
async function loadServerStatusData() {
- try {
- // 使用现有的API获取系统状态
- const statusData = await window.api?.getStatus();
- if (statusData && !statusData.error) {
- updateServerStatusWidget(statusData);
- }
- } catch (error) {
- console.error('加载服务器状态数据失败:', error);
+ try {
+ // 使用现有的API获取系统状态
+ const api = window.api || {};
+ const getStatusFn = api.getStatus || function() { return Promise.resolve({}); };
+ const statusData = await getStatusFn();
+ if (statusData && !statusData.error) {
+ updateServerStatusWidget(statusData);
}
+ } catch (error) {
+ console.error('加载服务器状态数据失败:', error);
+ }
}
// 更新服务器状态组件
function updateServerStatusWidget(stats) {
- // 确保组件存在
- const widget = document.getElementById('server-status-widget');
- if (!widget) return;
+ // 确保组件存在
+ const widget = document.getElementById('server-status-widget');
+ if (!widget) return;
+
+ // 确保stats存在
+ stats = stats || {};
+
+ // 提取CPU使用率
+ let cpuUsage = 0;
+ if (stats.system && typeof stats.system.cpu === 'number') {
+ cpuUsage = stats.system.cpu;
+ } else if (typeof stats.cpuUsage === 'number') {
+ cpuUsage = stats.cpuUsage;
+ }
+
+ // 提取查询统计数据
+ let totalQueries = 0;
+ let blockedQueries = 0;
+ let allowedQueries = 0;
+
+ if (stats.dns) {
+ const allowed = typeof stats.dns.Allowed === 'number' ? stats.dns.Allowed : 0;
+ const blocked = typeof stats.dns.Blocked === 'number' ? stats.dns.Blocked : 0;
+ const errors = typeof stats.dns.Errors === 'number' ? stats.dns.Errors : 0;
+ totalQueries = allowed + blocked + errors;
+ blockedQueries = blocked;
+ allowedQueries = allowed;
+ } else {
+ totalQueries = typeof stats.totalQueries === 'number' ? stats.totalQueries : 0;
+ blockedQueries = typeof stats.blockedQueries === 'number' ? stats.blockedQueries : 0;
+ allowedQueries = typeof stats.allowedQueries === 'number' ? stats.allowedQueries : 0;
+ }
+
+ // 更新CPU使用率
+ const cpuValueElement = document.getElementById('server-cpu-value');
+ if (cpuValueElement) {
+ cpuValueElement.textContent = cpuUsage.toFixed(1) + '%';
+ }
+
+ const cpuBarElement = document.getElementById('server-cpu-bar');
+ if (cpuBarElement) {
+ cpuBarElement.style.width = Math.min(cpuUsage, 100) + '%';
- // 提取CPU使用率
- let cpuUsage = 0;
- if (stats.system && stats.system.cpu) {
- cpuUsage = stats.system.cpu;
- } else if (stats.cpuUsage) {
- cpuUsage = stats.cpuUsage;
+ // 根据CPU使用率改变颜色
+ if (cpuUsage > 80) {
+ cpuBarElement.className = 'h-full bg-danger rounded-full';
+ } else if (cpuUsage > 50) {
+ cpuBarElement.className = 'h-full bg-warning rounded-full';
+ } else {
+ cpuBarElement.className = 'h-full bg-success rounded-full';
}
-
- // 提取查询量
- let queryCount = 0;
- if (stats.dns) {
- queryCount = (stats.dns.Allowed || 0) + (stats.dns.Blocked || 0) + (stats.dns.Errors || 0);
- } else if (stats.totalQueries) {
- queryCount = stats.totalQueries;
+ }
+
+ // 更新查询量
+ const queriesValueElement = document.getElementById('server-queries-value');
+ if (queriesValueElement) {
+ queriesValueElement.textContent = formatNumber(totalQueries);
+ }
+
+ // 计算查询量百分比(假设最大查询量为10000)
+ const queryPercentage = Math.min((totalQueries / 10000) * 100, 100);
+ const queriesBarElement = document.getElementById('server-queries-bar');
+ if (queriesBarElement) {
+ queriesBarElement.style.width = queryPercentage + '%';
+ }
+
+ // 更新额外统计指标
+ const totalQueriesElement = document.getElementById('server-total-queries');
+ if (totalQueriesElement) {
+ totalQueriesElement.textContent = formatNumber(totalQueries);
+ }
+
+ const blockedQueriesElement = document.getElementById('server-blocked-queries');
+ if (blockedQueriesElement) {
+ blockedQueriesElement.textContent = formatNumber(blockedQueries);
+ }
+
+ const allowedQueriesElement = document.getElementById('server-allowed-queries');
+ if (allowedQueriesElement) {
+ allowedQueriesElement.textContent = formatNumber(allowedQueries);
+ }
+
+ // 添加光晕提示效果
+ if (previousServerData.cpu !== cpuUsage || previousServerData.queries !== totalQueries) {
+ addGlowEffect();
+ }
+
+ // 更新服务器状态指示器
+ const statusIndicator = document.getElementById('server-status-indicator');
+ if (statusIndicator) {
+ // 检查系统状态
+ if (stats.system && stats.system.status === 'error') {
+ statusIndicator.className = 'inline-block w-2 h-2 bg-danger rounded-full';
+ } else {
+ statusIndicator.className = 'inline-block w-2 h-2 bg-success rounded-full';
}
-
- // 更新CPU使用率
- if (document.getElementById('server-cpu-value')) {
- document.getElementById('server-cpu-value').textContent = cpuUsage.toFixed(1) + '%';
- }
- if (document.getElementById('server-cpu-bar')) {
- document.getElementById('server-cpu-bar').style.width = Math.min(cpuUsage, 100) + '%';
-
- // 根据CPU使用率改变颜色
- const cpuBar = document.getElementById('server-cpu-bar');
- if (cpuUsage > 80) {
- cpuBar.className = 'h-full bg-danger rounded-full';
- } else if (cpuUsage > 50) {
- cpuBar.className = 'h-full bg-warning rounded-full';
- } else {
- cpuBar.className = 'h-full bg-success rounded-full';
- }
- }
-
- // 更新查询量
- if (document.getElementById('server-queries-value')) {
- document.getElementById('server-queries-value').textContent = formatNumber(queryCount);
- }
-
- // 计算查询量百分比(假设最大查询量为10000)
- const queryPercentage = Math.min((queryCount / 10000) * 100, 100);
- if (document.getElementById('server-queries-bar')) {
- document.getElementById('server-queries-bar').style.width = queryPercentage + '%';
- }
-
- // 添加光晕提示效果
- if (previousServerData.cpu !== cpuUsage || previousServerData.queries !== queryCount) {
- addGlowEffect();
- }
-
- // 更新服务器状态指示器
- if (document.getElementById('server-status-indicator')) {
- const indicator = document.getElementById('server-status-indicator');
-
- // 检查系统状态
- if (stats.system && stats.system.status === 'error') {
- indicator.className = 'inline-block w-2 h-2 bg-danger rounded-full';
- } else {
- indicator.className = 'inline-block w-2 h-2 bg-success rounded-full';
- }
- }
-
- // 保存当前数据用于下次比较
- previousServerData = {
- cpu: cpuUsage,
- queries: queryCount
- };
+ }
+
+ // 保存当前数据用于下次比较
+ previousServerData = {
+ cpu: cpuUsage,
+ queries: totalQueries
+ };
}
// 添加光晕提示效果
function addGlowEffect() {
- const widget = document.getElementById('server-status-widget');
- if (!widget) return;
-
- // 添加光晕类
- widget.classList.add('glow-effect');
-
- // 2秒后移除光晕
- setTimeout(() => {
- widget.classList.remove('glow-effect');
- }, 2000);
+ const widget = document.getElementById('server-status-widget');
+ if (!widget) return;
+
+ // 添加光晕类
+ widget.classList.add('glow-effect');
+
+ // 2秒后移除光晕
+ setTimeout(function() {
+ widget.classList.remove('glow-effect');
+ }, 2000);
}
-// 格式化数字(添加千位分隔符)
+// 格式化数字
function formatNumber(num) {
- if (num >= 1000000) {
- return (num / 1000000).toFixed(1) + 'M';
- } else if (num >= 1000) {
- return (num / 1000).toFixed(1) + 'K';
- }
- return num.toString();
+ if (num >= 1000000) {
+ return (num / 1000000).toFixed(1) + 'M';
+ } else if (num >= 1000) {
+ return (num / 1000).toFixed(1) + 'K';
+ }
+ return num.toString();
}
// 在DOM加载完成后初始化
-window.addEventListener('DOMContentLoaded', () => {
- // 延迟初始化,确保页面完全加载
- setTimeout(initServerStatusWidget, 500);
-
- // 监听页面切换事件(已在handlePageSwitchEvents中处理)
+window.addEventListener('DOMContentLoaded', function() {
+ // 延迟初始化,确保页面完全加载
+ setTimeout(initServerStatusWidget, 500);
});
// 在页面卸载时清理资源
-window.addEventListener('beforeunload', () => {
- if (serverStatusUpdateTimer) {
- clearInterval(serverStatusUpdateTimer);
- serverStatusUpdateTimer = null;
- }
+window.addEventListener('beforeunload', function() {
+ if (serverStatusUpdateTimer) {
+ clearInterval(serverStatusUpdateTimer);
+ serverStatusUpdateTimer = null;
+ }
});
// 导出函数供其他模块使用
window.serverStatusWidget = {
- init: initServerStatusWidget,
- update: updateServerStatusWidget
+ init: initServerStatusWidget,
+ update: updateServerStatusWidget
};
\ No newline at end of file