295 lines
8.7 KiB
JavaScript
295 lines
8.7 KiB
JavaScript
// 配置管理页面功能实现
|
||
|
||
// 工具函数:安全获取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 {
|
||
showLoading(true);
|
||
const config = await api.getConfig();
|
||
populateConfigForm(config);
|
||
} catch (error) {
|
||
showErrorMessage('加载配置失败: ' + error.message);
|
||
} finally {
|
||
showLoading(false);
|
||
}
|
||
}
|
||
|
||
// 填充配置表单
|
||
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-upstream-servers', getSafeArray(dnsServerConfig.UpstreamServers).join(', '));
|
||
setElementValue('dns-timeout', getSafeValue(dnsServerConfig.Timeout, 5));
|
||
setElementValue('dns-stats-file', getSafeValue(dnsServerConfig.StatsFile, 'data/stats.json'));
|
||
setElementValue('dns-save-interval', getSafeValue(dnsServerConfig.SaveInterval, 300));
|
||
|
||
// 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'));
|
||
setElementValue('shield-block-method', getSafeValue(shieldConfig.BlockMethod, '0.0.0.0'));
|
||
}
|
||
|
||
// 工具函数:安全设置元素值
|
||
function setElementValue(elementId, value) {
|
||
const element = document.getElementById(elementId);
|
||
if (element && element.tagName === 'INPUT') {
|
||
element.value = value;
|
||
} else if (!element) {
|
||
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 {
|
||
showLoading(true);
|
||
await api.saveConfig(formData);
|
||
showSuccessMessage('配置保存成功');
|
||
} catch (error) {
|
||
showErrorMessage('保存配置失败: ' + error.message);
|
||
} finally {
|
||
showLoading(false);
|
||
}
|
||
}
|
||
|
||
// 重启服务
|
||
async function handleRestartService() {
|
||
if (!confirm('确定要重启DNS服务吗?重启期间服务可能会短暂不可用。')) return;
|
||
|
||
try {
|
||
showLoading(true);
|
||
await api.restartService();
|
||
showSuccessMessage('服务重启成功');
|
||
} catch (error) {
|
||
showErrorMessage('重启服务失败: ' + error.message);
|
||
} finally {
|
||
showLoading(false);
|
||
}
|
||
}
|
||
|
||
// 收集表单数据并验证
|
||
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(',').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;
|
||
|
||
return {
|
||
DNSServer: {
|
||
Port: dnsPort,
|
||
UpstreamServers: upstreamServers,
|
||
Timeout: timeout,
|
||
StatsFile: getElementValue('dns-stats-file') || './data/stats.json',
|
||
SaveInterval: saveInterval
|
||
},
|
||
HTTPServer: {
|
||
Port: httpPort
|
||
},
|
||
Shield: {
|
||
LocalRulesFile: getElementValue('shield-local-rules-file') || './data/rules.txt',
|
||
UpdateInterval: updateInterval,
|
||
HostsFile: getElementValue('shield-hosts-file') || './data/hosts.txt',
|
||
BlockMethod: getElementValue('shield-block-method') || '0.0.0.0'
|
||
}
|
||
};
|
||
}
|
||
|
||
// 工具函数:安全获取元素值
|
||
function getElementValue(elementId) {
|
||
const element = document.getElementById(elementId);
|
||
if (element && element.tagName === 'INPUT') {
|
||
return element.value;
|
||
}
|
||
return ''; // 默认返回空字符串
|
||
}
|
||
|
||
// 设置事件监听器
|
||
function setupConfigEventListeners() {
|
||
// 保存配置按钮
|
||
getElement('save-config-btn')?.addEventListener('click', handleSaveConfig);
|
||
|
||
// 重启服务按钮
|
||
getElement('restart-service-btn')?.addEventListener('click', handleRestartService);
|
||
}
|
||
|
||
// 显示加载状态
|
||
function showLoading(show) {
|
||
const loadingElement = document.getElementById('loading-overlay');
|
||
if (show) {
|
||
if (!loadingElement) {
|
||
const overlay = document.createElement('div');
|
||
overlay.id = 'loading-overlay';
|
||
overlay.style.cssText = `
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0,0,0,0.5);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 1000;
|
||
color: white;
|
||
font-size: 18px;
|
||
`;
|
||
overlay.innerHTML = '<div>处理中...</div>';
|
||
document.body.appendChild(overlay);
|
||
}
|
||
} else {
|
||
loadingElement?.remove();
|
||
}
|
||
}
|
||
|
||
// 显示成功消息
|
||
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 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`;
|
||
|
||
// 设置通知样式(兼容Tailwind和原生CSS)
|
||
notification.style.cssText += `
|
||
position: fixed;
|
||
bottom: 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';
|
||
} else if (type === 'error') {
|
||
notification.style.backgroundColor = '#ef4444';
|
||
notification.style.color = 'white';
|
||
} else {
|
||
notification.style.backgroundColor = '#3b82f6';
|
||
notification.style.color = 'white';
|
||
}
|
||
|
||
notification.textContent = message;
|
||
document.body.appendChild(notification);
|
||
|
||
// 显示通知
|
||
setTimeout(() => {
|
||
notification.style.opacity = '1';
|
||
}, 10);
|
||
|
||
// 3秒后隐藏通知
|
||
setTimeout(() => {
|
||
notification.style.opacity = '0';
|
||
setTimeout(() => {
|
||
notification.remove();
|
||
}, 300);
|
||
}, 3000);
|
||
}
|
||
|
||
// 页面加载完成后初始化
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', initConfigPage);
|
||
} else {
|
||
initConfigPage();
|
||
} |