// 配置管理页面功能实现 // 工具函数:安全获取DOM元素 function getElement(id) { const element = document.getElementById(id); if (!element) { console.warn(`Element with id "${id}" not found`); } return element; } // 工具函数:验证端口号 function validatePort(port) { // 确保port是字符串类型 var portStr = port; if (port === null || port === undefined || typeof port !== 'string') { return null; } // 去除前后空白并验证是否为纯数字 portStr = port.trim(); if (!/^\d+$/.test(portStr)) { return null; } const num = parseInt(portStr, 10); return num >= 1 && num <= 65535 ? num : null; } // 初始化配置管理页面 function initConfigPage() { loadConfig(); setupConfigEventListeners(); } // 加载系统配置 async function loadConfig() { try { const result = await api.getConfig(); // 检查API返回的错误 if (result && result.error) { showErrorMessage('加载配置失败: ' + result.error); return; } populateConfigForm(result); } catch (error) { // 捕获可能的异常(虽然apiRequest不应该再抛出异常) showErrorMessage('加载配置失败: ' + (error.message || '未知错误')); } } // 填充配置表单 function populateConfigForm(config) { // 安全获取配置对象,防止未定义属性访问 const dnsServerConfig = config.DNSServer || {}; const httpServerConfig = config.HTTPServer || {}; const shieldConfig = config.Shield || {}; // DNS配置 - 使用函数安全设置值,避免 || 操作符可能的错误处理 setElementValue('dns-port', getSafeValue(dnsServerConfig.Port, 53)); 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-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) { 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 { console.warn(`Element with id "${elementId}" not found for setting value: ${value}`); } } // 工具函数:安全获取值,如果未定义或为null则返回默认值 function getSafeValue(value, defaultValue) { // 更严格的检查,避免0、空字符串等被默认值替换 return value === undefined || value === null ? defaultValue : value; } // 工具函数:安全获取数组,如果不是数组则返回空数组 function getSafeArray(value) { return Array.isArray(value) ? value : []; } // 保存配置 async function handleSaveConfig() { const formData = collectFormData(); if (!formData) return; try { const result = await api.saveConfig(formData); // 检查API返回的错误 if (result && result.error) { showErrorMessage('保存配置失败: ' + result.error); return; } showSuccessMessage('配置保存成功'); } catch (error) { // 捕获可能的异常(虽然apiRequest不应该再抛出异常) showErrorMessage('保存配置失败: ' + (error.message || '未知错误')); } } // 重启服务 async function handleRestartService() { if (!confirm('确定要重启DNS服务吗?重启期间服务可能会短暂不可用。')) return; try { const result = await api.restartService(); // 检查API返回的错误 if (result && result.error) { showErrorMessage('服务重启失败: ' + result.error); return; } showSuccessMessage('服务重启成功'); } catch (error) { // 捕获可能的异常(虽然apiRequest不应该再抛出异常) showErrorMessage('重启服务失败: ' + (error.message || '未知错误')); } } // 收集表单数据并验证 function collectFormData() { // 验证端口号 - 使用安全获取元素值的函数 const dnsPortValue = getElementValue('dns-port'); const httpPortValue = getElementValue('http-port'); const dnsPort = validatePort(dnsPortValue); const httpPort = validatePort(httpPortValue); if (!dnsPort) { showErrorMessage('DNS端口号无效(必须是1-65535之间的整数)'); return null; } if (!httpPort) { showErrorMessage('HTTP端口号无效(必须是1-65535之间的整数)'); return null; } // 安全获取上游服务器列表 const upstreamServersText = getElementValue('dns-upstream-servers'); const upstreamServers = upstreamServersText ? upstreamServersText.split('\n').map(function(s) { return s.trim(); }).filter(function(s) { return s !== ''; }) : []; const dnssecUpstreamServersText = getElementValue('dns-dnssec-upstream-servers'); const dnssecUpstreamServers = dnssecUpstreamServersText ? dnssecUpstreamServersText.split('\n').map(function(s) { return s.trim(); }).filter(function(s) { return s !== ''; }) : []; // 安全获取并转换整数值 const timeoutValue = getElementValue('dns-timeout'); const timeout = timeoutValue ? parseInt(timeoutValue, 10) : 5; const saveIntervalValue = getElementValue('dns-save-interval'); const saveInterval = saveIntervalValue ? parseInt(saveIntervalValue, 10) : 300; 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'), cacheMode: getElementValue('dns-cache-mode') || 'memory', cacheSize: cacheSize, maxCacheTTL: maxCacheTTL, minCacheTTL: minCacheTTL }, httpserver: { port: httpPort }, shield: { updateInterval: updateInterval, blockMethod: getElementValue('shield-block-method') || 'NXDOMAIN', customBlockIP: getElementValue('shield-custom-block-ip') } }; } // 工具函数:安全获取元素值 function getElementValue(elementId) { const element = document.getElementById(elementId); 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() { const saveConfigBtn = getElement('save-config-btn'); if (saveConfigBtn) { saveConfigBtn.addEventListener('click', handleSaveConfig); } 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); } } // 显示成功消息 function showSuccessMessage(message) { showNotification(message, 'success'); } // 显示错误消息 function showErrorMessage(message) { showNotification(message, 'error'); } // 显示通知 function showNotification(message, type = 'info') { // 移除现有通知 const existingNotification = document.querySelector('.notification'); if (existingNotification) { existingNotification.remove(); } // 创建新通知 const notification = document.createElement('div'); 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; top: 16px; right: 16px; padding: 16px 24px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1000; transition: all 0.3s ease; opacity: 0; `; if (type === 'success') { notification.style.backgroundColor = '#10b981'; notification.style.color = 'white'; notification.innerHTML = `${message}`; } else if (type === 'error') { notification.style.backgroundColor = '#ef4444'; notification.style.color = 'white'; notification.innerHTML = `${message}`; } else { notification.style.backgroundColor = '#3b82f6'; notification.style.color = 'white'; notification.innerHTML = `${message}`; } document.body.appendChild(notification); // 显示通知 setTimeout(() => { notification.style.opacity = '1'; }, 10); // 3秒后隐藏通知 setTimeout(() => { notification.style.opacity = '0'; setTimeout(() => { notification.remove(); }, 300); }, 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); } else { initConfigPage(); }