更新版本到2.0

This commit is contained in:
Alex Yang
2026-01-25 16:13:52 +08:00
parent ecbc20d89f
commit a5dc5841fb
54 changed files with 76520 additions and 2914 deletions

View File

@@ -31,11 +31,10 @@ async function apiRequest(endpoint, method = 'GET', data = null) {
// 竞争:请求或超时
const response = await Promise.race([fetch(url, options), timeoutPromise]);
// 获取响应文本,用于调试和错误处理
// 获取响应文本
const responseText = await response.text();
if (!response.ok) {
// 优化错误响应处理
console.warn(`API请求失败: ${response.status}`);
// 处理401未授权错误重定向到登录页面
@@ -45,67 +44,28 @@ async function apiRequest(endpoint, method = 'GET', data = null) {
return { error: '未授权访问' };
}
// 尝试解析JSON,但如果失败,直接使用原始文本作为错误信息
// 尝试解析JSON错误响应
try {
const errorData = JSON.parse(responseText);
return { error: errorData.error || responseText || `请求失败: ${response.status}` };
} catch (parseError) {
// 当响应不是有效的JSON时(如中文错误信息),直接使用原始文本
console.warn('非JSON格式错误响应:', responseText);
// 当响应不是有效的JSON时直接使用原始文本作为错误信息
return { error: responseText || `请求失败: ${response.status}` };
}
}
// 尝试解析成功响应
// 如果响应文本为空返回null
if (!responseText || responseText.trim() === '') {
return null;
}
// 尝试解析JSON响应
try {
// 首先检查响应文本是否为空
if (!responseText || responseText.trim() === '') {
console.warn('空响应文本');
return null; // 返回null表示空响应
}
// 尝试解析JSON
const parsedData = JSON.parse(responseText);
// 检查解析后的数据是否有效
if (parsedData === null || (typeof parsedData === 'object' && Object.keys(parsedData).length === 0)) {
console.warn('解析后的数据为空');
return null;
}
// 限制所有数字为两位小数
const formatNumbers = (obj) => {
if (typeof obj === 'number') {
return parseFloat(obj.toFixed(2));
} else if (Array.isArray(obj)) {
return obj.map(formatNumbers);
} else if (obj && typeof obj === 'object') {
const formattedObj = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
formattedObj[key] = formatNumbers(obj[key]);
}
}
return formattedObj;
}
return obj;
};
const formattedData = formatNumbers(parsedData);
return formattedData;
return parsedData;
} catch (parseError) {
// 详细记录错误信息和响应内容
console.error('JSON解析错误:', parseError);
console.error('原始响应文本:', responseText);
console.error('响应长度:', responseText.length);
console.error('响应前100字符:', responseText.substring(0, 100));
// 如果是位置66附近的错误特别标记
if (parseError.message.includes('position 66')) {
console.error('位置66附近的字符:', responseText.substring(60, 75));
}
// 返回错误对象,让上层处理
return { error: 'JSON解析错误' };
}
} catch (error) {
@@ -150,66 +110,6 @@ const api = {
// 获取查询类型统计
getQueryTypeStats: () => apiRequest('/query/type?t=' + Date.now()),
// 获取屏蔽规则 - 已禁用
getShieldRules: () => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({}); // 返回空对象而非API调用
},
// 添加屏蔽规则 - 已禁用
addShieldRule: (rule) => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 删除屏蔽规则 - 已禁用
deleteShieldRule: (rule) => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 更新远程规则 - 已禁用
updateRemoteRules: () => {
console.log('屏蔽规则功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 获取黑名单列表 - 已禁用
getBlacklists: () => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve([]); // 返回空数组而非API调用
},
// 添加黑名单 - 已禁用
addBlacklist: (url) => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 删除黑名单 - 已禁用
deleteBlacklist: (url) => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 获取Hosts内容 - 已禁用
getHosts: () => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ content: '' }); // 返回空内容而非API调用
},
// 保存Hosts内容 - 已禁用
saveHosts: (content) => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 刷新Hosts - 已禁用
refreshHosts: () => {
console.log('屏蔽规则相关功能已禁用');
return Promise.resolve({ error: '屏蔽规则功能已禁用' });
},
// 查询DNS记录 - 兼容多种参数格式
queryDNS: async function(domain, recordType) {
try {
@@ -225,36 +125,22 @@ const api = {
params = { domain, recordType };
}
// 尝试不同的API端点
const endpoints = ['/api/dns/query', '/dns/query', '/api/query', '/query'];
let lastError;
// 使用正确的API端点
const response = await fetch('/api/query', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
for (const endpoint of endpoints) {
try {
console.log(`尝试API端点: ${endpoint}`);
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
if (response.ok) {
const data = await response.json();
console.log('DNS查询成功:', data);
return data;
} else {
lastError = new Error(`HTTP error! status: ${response.status} for endpoint: ${endpoint}`);
}
} catch (error) {
lastError = error;
console.log(`端点 ${endpoint} 调用失败,尝试下一个`);
}
if (response.ok) {
const data = await response.json();
console.log('DNS查询成功:', data);
return data;
} else {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 如果所有端点都失败,抛出最后一个错误
throw lastError || new Error('所有API端点调用失败');
} catch (error) {
console.error('DNS查询API调用失败:', error);

View File

@@ -60,34 +60,60 @@ function populateConfigForm(config) {
// DNS配置 - 使用函数安全设置值,避免 || 操作符可能的错误处理
setElementValue('dns-port', getSafeValue(dnsServerConfig.Port, 53));
setElementValue('dns-upstream-servers', getSafeArray(dnsServerConfig.UpstreamServers).join(', '));
setElementValue('dns-dnssec-upstream-servers', getSafeArray(dnsServerConfig.DNSSECUpstreamServers).join(', '));
setElementValue('dns-timeout', getSafeValue(dnsServerConfig.Timeout, 5));
setElementValue('dns-run-mode', getSafeValue(dnsServerConfig.QueryMode, 'parallel'));
setElementValue('dns-upstream-servers', getSafeArray(dnsServerConfig.UpstreamServers).join('\n'));
setElementValue('dns-dnssec-upstream-servers', getSafeArray(dnsServerConfig.DNSSECUpstreamServers).join('\n'));
//setElementValue('dns-stats-file', getSafeValue(dnsServerConfig.StatsFile, 'data/stats.json'));
setElementValue('dns-save-interval', getSafeValue(dnsServerConfig.SaveInterval, 30));
//setElementValue('dns-cache-ttl', getSafeValue(dnsServerConfig.CacheTTL, 10));
setElementValue('dns-enable-ipv6', getSafeValue(dnsServerConfig.EnableIPv6, false));
setElementValue('dns-save-interval', getSafeValue(dnsServerConfig.saveInterval, 30));
// 缓存配置
setElementValue('dns-cache-mode', getSafeValue(dnsServerConfig.CacheMode, 'memory'));
setElementValue('dns-cache-size', getSafeValue(dnsServerConfig.CacheSize, 100));
setElementValue('dns-max-cache-ttl', getSafeValue(dnsServerConfig.MaxCacheTTL, 120));
setElementValue('dns-min-cache-ttl', getSafeValue(dnsServerConfig.MinCacheTTL, 5));
setElementValue('dns-enable-ipv6', getSafeValue(dnsServerConfig.enableIPv6, false));
// HTTP配置
setElementValue('http-port', getSafeValue(httpServerConfig.Port, 8080));
// 屏蔽配置
// 屏蔽配置
//setElementValue('shield-local-rules-file', getSafeValue(shieldConfig.LocalRulesFile, 'data/rules.txt'));
setElementValue('shield-update-interval', getSafeValue(shieldConfig.UpdateInterval, 3600));
//setElementValue('shield-hosts-file', getSafeValue(shieldConfig.HostsFile, 'data/hosts.txt'));
// 使用服务器端接受的屏蔽方法值默认使用NXDOMAIN, 可选值: NXDOMAIN, NULL, REFUSED
setElementValue('shield-block-method', getSafeValue(shieldConfig.BlockMethod, 'NXDOMAIN'));
setElementValue('shield-custom-block-ip', getSafeValue(shieldConfig.CustomBlockIP, ''));
// 初始加载时更新自定义屏蔽IP输入框的可见性
updateCustomBlockIpVisibility();
}
// 工具函数:安全设置元素值
function setElementValue(elementId, value) {
const element = document.getElementById(elementId);
if (element && element.tagName === 'INPUT') {
if (element.type === 'checkbox') {
element.checked = value;
} else {
if (element) {
if (element.tagName === 'INPUT') {
if (element.type === 'checkbox') {
element.checked = value;
} else {
element.value = value;
}
} else if (element.tagName === 'TEXTAREA') {
element.value = value;
} else if (element.tagName === 'SELECT') {
element.value = value;
} else if (element.tagName === 'BUTTON' && element.classList.contains('toggle-btn')) {
const icon = element.querySelector('i');
if (icon) {
if (value) {
element.classList.remove('bg-gray-300', 'hover:bg-gray-400');
element.classList.add('bg-success', 'hover:bg-success/90');
icon.className = 'fa fa-toggle-on';
} else {
element.classList.remove('bg-success', 'hover:bg-success/90');
element.classList.add('bg-gray-300', 'hover:bg-gray-400');
icon.className = 'fa fa-toggle-off';
}
}
}
} else if (!element) {
} else {
console.warn(`Element with id "${elementId}" not found for setting value: ${value}`);
}
}
@@ -166,13 +192,12 @@ function collectFormData() {
// 安全获取上游服务器列表
const upstreamServersText = getElementValue('dns-upstream-servers');
const upstreamServers = upstreamServersText ?
upstreamServersText.split(',').map(function(s) { return s.trim(); }).filter(function(s) { return s !== ''; }) :
upstreamServersText.split('\n').map(function(s) { return s.trim(); }).filter(function(s) { return s !== ''; }) :
[];
// 安全获取DNSSEC上游服务器列表
const dnssecUpstreamServersText = getElementValue('dns-dnssec-upstream-servers');
const dnssecUpstreamServers = dnssecUpstreamServersText ?
dnssecUpstreamServersText.split(',').map(function(s) { return s.trim(); }).filter(function(s) { return s !== ''; }) :
dnssecUpstreamServersText.split('\n').map(function(s) { return s.trim(); }).filter(function(s) { return s !== ''; }) :
[];
// 安全获取并转换整数值
@@ -185,21 +210,37 @@ function collectFormData() {
const updateIntervalValue = getElementValue('shield-update-interval');
const updateInterval = updateIntervalValue ? parseInt(updateIntervalValue, 10) : 3600;
// 安全获取并转换缓存配置值
const cacheSizeValue = getElementValue('dns-cache-size');
const cacheSize = cacheSizeValue ? parseInt(cacheSizeValue, 10) : 100;
const maxCacheTTLValue = getElementValue('dns-max-cache-ttl');
const maxCacheTTL = maxCacheTTLValue ? parseInt(maxCacheTTLValue, 10) : 120;
const minCacheTTLValue = getElementValue('dns-min-cache-ttl');
const minCacheTTL = minCacheTTLValue ? parseInt(minCacheTTLValue, 10) : 5;
return {
dnsserver: {
port: dnsPort,
queryMode: getElementValue('dns-run-mode') || 'parallel',
upstreamServers: upstreamServers,
dnssecUpstreamServers: dnssecUpstreamServers,
timeout: timeout,
saveInterval: saveInterval,
enableIPv6: getElementValue('dns-enable-ipv6')
enableIPv6: getElementValue('dns-enable-ipv6'),
cacheMode: getElementValue('dns-cache-mode') || 'memory',
cacheSize: cacheSize,
maxCacheTTL: maxCacheTTL,
minCacheTTL: minCacheTTL
},
httpserver: {
port: httpPort
},
shield: {
updateInterval: updateInterval,
blockMethod: getElementValue('shield-block-method') || 'NXDOMAIN'
blockMethod: getElementValue('shield-block-method') || 'NXDOMAIN',
customBlockIP: getElementValue('shield-custom-block-ip')
}
};
}
@@ -207,22 +248,51 @@ function collectFormData() {
// 工具函数:安全获取元素值
function getElementValue(elementId) {
const element = document.getElementById(elementId);
if (element && element.tagName === 'INPUT') {
if (element.type === 'checkbox') {
return element.checked;
if (element) {
if (element.tagName === 'INPUT') {
if (element.type === 'checkbox') {
return element.checked;
}
return element.value;
} else if (element.tagName === 'TEXTAREA') {
return element.value;
} else if (element.tagName === 'BUTTON' && element.classList.contains('toggle-btn')) {
// 处理按钮式开关
return element.classList.contains('bg-success');
}
return element.value;
}
return ''; // 默认返回空字符串
}
// 更新自定义屏蔽IP输入框的可见性
function updateCustomBlockIpVisibility() {
const blockMethod = getElementValue('shield-block-method');
const customBlockIpContainer = document.getElementById('custom-block-ip-container');
if (blockMethod === 'customIP') {
customBlockIpContainer.style.display = 'block';
} else {
customBlockIpContainer.style.display = 'none';
}
}
// 设置事件监听器
function setupConfigEventListeners() {
// 保存配置按钮
getElement('save-config-btn')?.addEventListener('click', handleSaveConfig);
const saveConfigBtn = getElement('save-config-btn');
if (saveConfigBtn) {
saveConfigBtn.addEventListener('click', handleSaveConfig);
}
// 重启服务按钮
getElement('restart-service-btn')?.addEventListener('click', handleRestartService);
const restartServiceBtn = getElement('restart-service-btn');
if (restartServiceBtn) {
restartServiceBtn.addEventListener('click', handleRestartService);
}
// 监听屏蔽方法选择变化
const blockMethodSelect = document.getElementById('shield-block-method');
if (blockMethodSelect) {
blockMethodSelect.addEventListener('change', updateCustomBlockIpVisibility);
}
}
@@ -247,12 +317,12 @@ function showNotification(message, type = 'info') {
// 创建新通知
const notification = document.createElement('div');
notification.className = `notification fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-transform duration-300 ease-in-out translate-y-0 opacity-0`;
notification.className = `notification fixed top-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-transform duration-300 ease-in-out translate-y-0 opacity-0`;
// 设置通知样式兼容Tailwind和原生CSS
notification.style.cssText += `
position: fixed;
bottom: 16px;
top: 16px;
right: 16px;
padding: 16px 24px;
border-radius: 8px;
@@ -265,15 +335,17 @@ function showNotification(message, type = 'info') {
if (type === 'success') {
notification.style.backgroundColor = '#10b981';
notification.style.color = 'white';
notification.innerHTML = `<i class="fa fa-check-circle mr-2"></i>${message}`;
} else if (type === 'error') {
notification.style.backgroundColor = '#ef4444';
notification.style.color = 'white';
notification.innerHTML = `<i class="fa fa-exclamation-circle mr-2"></i>${message}`;
} else {
notification.style.backgroundColor = '#3b82f6';
notification.style.color = 'white';
notification.innerHTML = `<i class="fa fa-info-circle mr-2"></i>${message}`;
}
notification.textContent = message;
document.body.appendChild(notification);
// 显示通知
@@ -290,6 +362,177 @@ function showNotification(message, type = 'info') {
}, 3000);
}
// GFWList管理页面功能实现
// 初始化GFWList管理页面
function initGFWListPage() {
loadGFWListConfig();
setupGFWListEventListeners();
}
// 加载GFWList配置
async function loadGFWListConfig() {
try {
const result = await api.getConfig();
if (result && result.error) {
showErrorMessage('加载配置失败: ' + result.error);
return;
}
populateGFWListForm(result);
} catch (error) {
showErrorMessage('加载配置失败: ' + (error.message || '未知错误'));
}
}
// 填充GFWList配置表单
function populateGFWListForm(config) {
const gfwListConfig = config.gfwList || {};
const enabled = getSafeValue(gfwListConfig.enabled, false);
setElementValue('gfwlist-enabled', enabled);
setElementValue('gfwlist-target-ip', getSafeValue(gfwListConfig.ip, ''));
setElementValue('gfwlist-google', getSafeValue(config.allowGoogle, false));
setElementValue('gfwlist-youtube', getSafeValue(config.allowYouTube, false));
setElementValue('gfwlist-facebook', getSafeValue(config.allowFacebook, false));
setElementValue('gfwlist-twitter', getSafeValue(config.allowTwitter, false));
setElementValue('gfwlist-amazon', getSafeValue(config.allowAmazon, false));
setElementValue('gfwlist-bbc', getSafeValue(config.allowBBC, false));
setElementValue('gfwlist-discord', getSafeValue(config.allowDiscord, false));
setElementValue('gfwlist-dropbox', getSafeValue(config.allowDropbox, false));
setElementValue('gfwlist-microsoft', getSafeValue(config.allowMicrosoft, false));
setElementValue('gfwlist-steam', getSafeValue(config.allowSteam, false));
setElementValue('gfwlist-telegram', getSafeValue(config.allowTelegram, false));
setElementValue('gfwlist-tiktok', getSafeValue(config.allowTikTok, false));
setElementValue('gfwlist-v2ex', getSafeValue(config.allowV2EX, false));
setElementValue('gfwlist-wikimedia', getSafeValue(config.allowWikimedia, false));
setElementValue('gfwlist-yahoo', getSafeValue(config.allowYahoo, false));
// 更新通行网站部分的显示效果
updateAllowedSitesSection(enabled);
}
// 保存GFWList配置
async function handleSaveGFWListConfig() {
const formData = collectGFWListFormData();
if (!formData) return;
try {
const result = await api.saveConfig(formData);
if (result && result.error) {
showErrorMessage('保存配置失败: ' + result.error);
return;
}
showSuccessMessage('配置保存成功');
} catch (error) {
showErrorMessage('保存配置失败: ' + (error.message || '未知错误'));
}
}
// 收集GFWList表单数据
function collectGFWListFormData() {
const targetIP = getElementValue('gfwlist-target-ip');
return {
gfwList: {
ip: targetIP,
enabled: getElementValue('gfwlist-enabled'),
content: '/root/dns/data/gfwlist.txt' // 保持默认路径
},
allowGoogle: getElementValue('gfwlist-google'),
allowYouTube: getElementValue('gfwlist-youtube'),
allowFacebook: getElementValue('gfwlist-facebook'),
allowTwitter: getElementValue('gfwlist-twitter'),
allowAmazon: getElementValue('gfwlist-amazon'),
allowBBC: getElementValue('gfwlist-bbc'),
allowDiscord: getElementValue('gfwlist-discord'),
allowDropbox: getElementValue('gfwlist-dropbox'),
allowMicrosoft: getElementValue('gfwlist-microsoft'),
allowSteam: getElementValue('gfwlist-steam'),
allowTelegram: getElementValue('gfwlist-telegram'),
allowTikTok: getElementValue('gfwlist-tiktok'),
allowV2EX: getElementValue('gfwlist-v2ex'),
allowWikimedia: getElementValue('gfwlist-wikimedia'),
allowYahoo: getElementValue('gfwlist-yahoo')
};
}
// 更新通行网站部分的显示效果
function updateAllowedSitesSection(enabled) {
const section = document.getElementById('allowed-sites-section');
if (!section) return;
const siteCards = section.querySelectorAll('.bg-gray-50');
const siteLabels = section.querySelectorAll('.text-gray-700');
const siteToggles = section.querySelectorAll('.toggle-btn');
if (enabled) {
// GFWList已启用显示彩色且可点击
section.classList.remove('opacity-50');
siteCards.forEach(card => {
card.style.filter = 'grayscale(0%)';
});
siteToggles.forEach(toggle => {
toggle.disabled = false;
toggle.classList.remove('cursor-not-allowed');
});
} else {
// GFWList已禁用显示灰色且不可点击
section.classList.add('opacity-50');
siteCards.forEach(card => {
card.style.filter = 'grayscale(100%)';
});
siteToggles.forEach(toggle => {
toggle.disabled = true;
toggle.classList.add('cursor-not-allowed');
});
}
}
// 重启GFWList服务
async function handleRestartGFWListService() {
if (!confirm('确定要重启DNS服务吗重启期间服务可能会短暂不可用。')) return;
try {
const result = await api.restartService();
if (result && result.error) {
showErrorMessage('服务重启失败: ' + result.error);
return;
}
showSuccessMessage('服务重启成功');
} catch (error) {
showErrorMessage('重启服务失败: ' + (error.message || '未知错误'));
}
}
// 设置GFWList事件监听器
function setupGFWListEventListeners() {
const saveBtn = getElement('gfwlist-save-btn');
if (saveBtn) {
saveBtn.addEventListener('click', handleSaveGFWListConfig);
}
// 为所有按钮式开关添加点击事件监听器
const toggleBtns = document.querySelectorAll('.toggle-btn');
toggleBtns.forEach(btn => {
btn.addEventListener('click', function() {
// 切换按钮状态
const currentState = this.classList.contains('bg-success');
setElementValue(this.id, !currentState);
// 如果是GFWList启用开关更新通行网站部分的显示效果
if (this.id === 'gfwlist-enabled') {
updateAllowedSitesSection(!currentState);
}
});
});
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initConfigPage);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +1,5 @@
// main.js - 主脚本文件
// 页面导航功能
function setupNavigation() {
// 侧边栏菜单项
const menuItems = document.querySelectorAll('nav a');
const contentSections = [
document.getElementById('dashboard-content'),
document.getElementById('shield-content'),
document.getElementById('hosts-content'),
document.getElementById('query-content'),
document.getElementById('logs-content'),
document.getElementById('config-content')
];
const pageTitle = document.getElementById('page-title');
menuItems.forEach((item, index) => {
item.addEventListener('click', (e) => {
// 允许浏览器自动更新地址栏中的hash不阻止默认行为
// 移动端点击菜单项后自动关闭侧边栏
if (window.innerWidth < 768) {
closeSidebar();
}
});
});
// 移动端侧边栏切换
const toggleSidebar = document.getElementById('toggle-sidebar');
const closeSidebarBtn = document.getElementById('close-sidebar');
const sidebar = document.getElementById('mobile-sidebar');
const sidebarOverlay = document.getElementById('sidebar-overlay');
// 打开侧边栏函数
function openSidebar() {
console.log('Opening sidebar...');
if (sidebar) {
sidebar.classList.remove('-translate-x-full');
sidebar.classList.add('translate-x-0');
}
if (sidebarOverlay) {
sidebarOverlay.classList.remove('hidden');
sidebarOverlay.classList.add('block');
}
// 防止页面滚动
document.body.style.overflow = 'hidden';
console.log('Sidebar opened successfully');
}
// 关闭侧边栏函数
function closeSidebar() {
console.log('Closing sidebar...');
if (sidebar) {
sidebar.classList.add('-translate-x-full');
sidebar.classList.remove('translate-x-0');
}
if (sidebarOverlay) {
sidebarOverlay.classList.add('hidden');
sidebarOverlay.classList.remove('block');
}
// 恢复页面滚动
document.body.style.overflow = '';
console.log('Sidebar closed successfully');
}
// 切换侧边栏函数
function toggleSidebarVisibility() {
console.log('Toggling sidebar visibility...');
console.log('Current sidebar classes:', sidebar ? sidebar.className : 'sidebar not found');
if (sidebar && sidebar.classList.contains('-translate-x-full')) {
console.log('Sidebar is hidden, opening...');
openSidebar();
} else {
console.log('Sidebar is visible, closing...');
closeSidebar();
}
}
// 绑定切换按钮事件
if (toggleSidebar) {
toggleSidebar.addEventListener('click', toggleSidebarVisibility);
}
// 绑定关闭按钮事件
if (closeSidebarBtn) {
closeSidebarBtn.addEventListener('click', closeSidebar);
}
// 绑定遮罩层点击事件
if (sidebarOverlay) {
sidebarOverlay.addEventListener('click', closeSidebar);
}
// 移动端点击菜单项后自动关闭侧边栏
menuItems.forEach(item => {
item.addEventListener('click', () => {
// 检查是否是移动设备视图
if (window.innerWidth < 768) {
closeSidebar();
}
});
});
// 添加键盘事件监听按ESC键关闭侧边栏
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeSidebar();
}
});
}
// 页面初始化函数 - 根据当前hash值初始化对应页面
function initPageByHash() {
const hash = window.location.hash.substring(1);
@@ -118,6 +9,7 @@ function initPageByHash() {
document.getElementById('dashboard-content'),
document.getElementById('shield-content'),
document.getElementById('hosts-content'),
document.getElementById('gfwlist-content'),
document.getElementById('query-content'),
document.getElementById('logs-content'),
document.getElementById('config-content')
@@ -142,6 +34,7 @@ function initPageByHash() {
'dashboard': '仪表盘',
'shield': '屏蔽管理',
'hosts': 'Hosts管理',
'gfwlist': 'GFWList管理',
'query': 'DNS屏蔽查询',
'logs': '查询日志',
'config': '系统设置'
@@ -162,6 +55,12 @@ function initPageByHash() {
initHostsPage();
}
}, 0);
} else if (hash === 'gfwlist') {
setTimeout(() => {
if (typeof initGFWListPage === 'function') {
initGFWListPage();
}
}, 0);
} else if (hash === 'logs') {
setTimeout(() => {
if (typeof initLogsPage === 'function') {
@@ -177,41 +76,6 @@ function initPageByHash() {
}
}
// 初始化函数
function init() {
// 设置导航
setupNavigation();
// 初始化页面
initPageByHash();
// 添加hashchange事件监听处理浏览器前进/后退按钮
window.addEventListener('hashchange', initPageByHash);
// 定期更新系统状态
setInterval(updateSystemStatus, 5000);
}
// 更新系统状态
function updateSystemStatus() {
fetch('/api/status')
.then(response => response.json())
.then(data => {
const uptimeElement = document.getElementById('uptime');
if (uptimeElement) {
uptimeElement.textContent = `正常运行中 | ${formatUptime(data.uptime)}`;
}
})
.catch(error => {
console.error('更新系统状态失败:', error);
const uptimeElement = document.getElementById('uptime');
if (uptimeElement) {
uptimeElement.textContent = '连接异常';
uptimeElement.classList.add('text-danger');
}
});
}
// 格式化运行时间
function formatUptime(milliseconds) {
// 简化版的格式化实际使用时需要根据API返回的数据格式调整
@@ -231,6 +95,29 @@ function formatUptime(milliseconds) {
}
}
// 更新系统状态
function updateSystemStatus() {
api.getStatus()
.then(data => {
if (data.error) {
throw new Error(data.error);
}
const uptimeElement = document.getElementById('uptime');
if (uptimeElement) {
uptimeElement.textContent = `正常运行中 | ${formatUptime(data.uptime)}`;
uptimeElement.classList.remove('text-danger');
}
})
.catch(error => {
console.error('更新系统状态失败:', error);
const uptimeElement = document.getElementById('uptime');
if (uptimeElement) {
uptimeElement.textContent = '连接异常';
uptimeElement.classList.add('text-danger');
}
});
}
// 账户功能 - 下拉菜单、注销和修改密码
function setupAccountFeatures() {
// 下拉菜单功能
@@ -383,6 +270,107 @@ function setupAccountFeatures() {
}
}
// 页面导航功能
function setupNavigation() {
// 侧边栏菜单项
const menuItems = document.querySelectorAll('nav a');
const pageTitle = document.getElementById('page-title');
menuItems.forEach((item, index) => {
item.addEventListener('click', (e) => {
// 允许浏览器自动更新地址栏中的hash不阻止默认行为
// 移动端点击菜单项后自动关闭侧边栏
if (window.innerWidth < 768) {
closeSidebar();
}
});
});
// 移动端侧边栏切换
const toggleSidebar = document.getElementById('toggle-sidebar');
const closeSidebarBtn = document.getElementById('close-sidebar');
const sidebar = document.getElementById('mobile-sidebar');
const sidebarOverlay = document.getElementById('sidebar-overlay');
// 打开侧边栏函数
function openSidebar() {
console.log('Opening sidebar...');
if (sidebar) {
sidebar.classList.remove('-translate-x-full');
sidebar.classList.add('translate-x-0');
}
if (sidebarOverlay) {
sidebarOverlay.classList.remove('hidden');
sidebarOverlay.classList.add('block');
// 防止页面滚动
document.body.style.overflow = 'hidden';
}
console.log('Sidebar opened successfully');
}
// 关闭侧边栏函数
function closeSidebar() {
console.log('Closing sidebar...');
if (sidebar) {
sidebar.classList.add('-translate-x-full');
sidebar.classList.remove('translate-x-0');
}
if (sidebarOverlay) {
sidebarOverlay.classList.add('hidden');
sidebarOverlay.classList.remove('block');
// 恢复页面滚动
document.body.style.overflow = '';
}
console.log('Sidebar closed successfully');
}
// 切换侧边栏函数
function toggleSidebarVisibility() {
console.log('Toggling sidebar visibility...');
console.log('Current sidebar classes:', sidebar ? sidebar.className : 'sidebar not found');
if (sidebar && sidebar.classList.contains('-translate-x-full')) {
console.log('Sidebar is hidden, opening...');
openSidebar();
} else {
console.log('Sidebar is visible, closing...');
closeSidebar();
}
}
// 绑定切换按钮事件
if (toggleSidebar) {
toggleSidebar.addEventListener('click', toggleSidebarVisibility);
}
// 绑定关闭按钮事件
if (closeSidebarBtn) {
closeSidebarBtn.addEventListener('click', closeSidebar);
}
// 绑定遮罩层点击事件
if (sidebarOverlay) {
sidebarOverlay.addEventListener('click', closeSidebar);
}
// 移动端点击菜单项后自动关闭侧边栏
menuItems.forEach(item => {
item.addEventListener('click', () => {
// 检查是否是移动设备视图
if (window.innerWidth < 768) {
closeSidebar();
}
});
});
// 添加键盘事件监听按ESC键关闭侧边栏
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeSidebar();
}
});
}
// 初始化函数
function init() {
// 设置导航

View File

@@ -1248,7 +1248,7 @@ function showNotification(message, type = 'info') {
// 创建新通知
const notification = document.createElement('div');
notification.className = `notification fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-all duration-300 ease-in-out translate-y-0 opacity-0`;
notification.className = `notification fixed top-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transform transition-all duration-300 ease-in-out translate-y-0 opacity-0`;
// 设置通知样式
if (type === 'success') {