web增加屏蔽管理和DNS查询功能
This commit is contained in:
@@ -1,313 +1,205 @@
|
||||
// DNS查询工具页面功能实现
|
||||
// DNS查询页面功能实现
|
||||
|
||||
// 初始化查询工具页面
|
||||
// 初始化查询页面
|
||||
function initQueryPage() {
|
||||
console.log('初始化DNS查询页面...');
|
||||
setupQueryEventListeners();
|
||||
|
||||
// 页面加载时自动显示一些示例数据
|
||||
setTimeout(() => {
|
||||
const mockDomain = 'example.com';
|
||||
const mockRecordType = 'A';
|
||||
displayMockQueryResult(mockDomain, mockRecordType);
|
||||
console.log('显示示例DNS查询数据');
|
||||
}, 500);
|
||||
loadQueryHistory();
|
||||
}
|
||||
|
||||
// 执行DNS查询
|
||||
async function handleDNSQuery() {
|
||||
// 尝试多种可能的DOM元素ID
|
||||
const domainInput = document.getElementById('query-domain') || document.getElementById('domain-input');
|
||||
const recordTypeSelect = document.getElementById('query-record-type') || document.getElementById('record-type');
|
||||
const domainInput = document.getElementById('dns-query-domain');
|
||||
const resultDiv = document.getElementById('query-result');
|
||||
|
||||
console.log('DOM元素查找结果:', { domainInput, recordTypeSelect, resultDiv });
|
||||
|
||||
if (!domainInput || !recordTypeSelect || !resultDiv) {
|
||||
if (!domainInput || !resultDiv) {
|
||||
console.error('找不到必要的DOM元素');
|
||||
return;
|
||||
}
|
||||
|
||||
const domain = domainInput.value.trim();
|
||||
const recordType = recordTypeSelect.value;
|
||||
|
||||
if (!domain) {
|
||||
showErrorMessage('请输入域名');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`执行DNS查询: 域名=${domain}, 记录类型=${recordType}`);
|
||||
|
||||
// 清空之前的结果
|
||||
resultDiv.innerHTML = '<div class="text-center py-4"><svg class="animate-spin mx-auto h-6 w-6 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> 查询中...</div>';
|
||||
showLoading('查询中...');
|
||||
|
||||
try {
|
||||
// 检查api对象是否存在
|
||||
if (!window.api || typeof window.api.queryDNS !== 'function') {
|
||||
console.warn('api.queryDNS不存在,使用模拟数据');
|
||||
const mockResult = generateMockDNSResult(domain, recordType);
|
||||
displayQueryResult(mockResult, domain, recordType);
|
||||
return;
|
||||
const response = await fetch(`/api/query?domain=${encodeURIComponent(domain)}`);
|
||||
if (!response.ok) {
|
||||
throw new Error('查询失败');
|
||||
}
|
||||
|
||||
// 调用API,适配不同的参数格式
|
||||
let result;
|
||||
try {
|
||||
// 尝试不同的API调用方式
|
||||
if (api.queryDNS.length === 1) {
|
||||
result = await api.queryDNS({ domain, recordType });
|
||||
} else {
|
||||
result = await api.queryDNS(domain, recordType);
|
||||
}
|
||||
} catch (apiError) {
|
||||
console.error('API调用失败,使用模拟数据:', apiError);
|
||||
const mockResult = generateMockDNSResult(domain, recordType);
|
||||
displayQueryResult(mockResult, domain, recordType);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('DNS查询API返回结果:', result);
|
||||
|
||||
// 处理API返回的数据
|
||||
if (!result || (Array.isArray(result) && result.length === 0) ||
|
||||
(typeof result === 'object' && Object.keys(result).length === 0)) {
|
||||
console.log('API返回空结果,使用模拟数据');
|
||||
const mockResult = generateMockDNSResult(domain, recordType);
|
||||
displayQueryResult(mockResult, domain, recordType);
|
||||
} else {
|
||||
displayQueryResult(result, domain, recordType);
|
||||
}
|
||||
const result = await response.json();
|
||||
displayQueryResult(result, domain);
|
||||
saveQueryHistory(domain, result);
|
||||
loadQueryHistory();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('DNS查询出错:', error);
|
||||
const mockResult = generateMockDNSResult(domain, recordType);
|
||||
displayQueryResult(mockResult, domain, recordType);
|
||||
resultDiv.innerHTML += `<div class="text-yellow-500 text-center py-2 text-sm">注意: 显示的是模拟数据</div>`;
|
||||
showErrorMessage('查询失败,请稍后重试');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 显示查询结果
|
||||
function displayQueryResult(result, domain, recordType) {
|
||||
const resultDiv = document.getElementById('query-result');
|
||||
|
||||
// 适配不同的数据结构
|
||||
let records = [];
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
// 如果是数组,直接使用
|
||||
records = result;
|
||||
} else if (typeof result === 'object' && result.length === undefined) {
|
||||
// 如果是对象,尝试转换为数组
|
||||
if (result.records) {
|
||||
records = result.records;
|
||||
} else if (result.data) {
|
||||
records = result.data;
|
||||
} else {
|
||||
// 尝试将对象转换为记录数组
|
||||
records = [result];
|
||||
}
|
||||
}
|
||||
|
||||
// 创建结果表格
|
||||
let html = `
|
||||
<div class="mb-4">
|
||||
<h3 class="text-lg font-medium text-gray-800 mb-2">查询结果: ${domain} (${recordType})</h3>
|
||||
<p class="text-sm text-gray-500 mb-3">查询时间: ${new Date().toLocaleString()}</p>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full bg-white rounded-lg overflow-hidden shadow-sm">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="py-2 px-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">类型</th>
|
||||
<th class="py-2 px-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">值</th>
|
||||
<th class="py-2 px-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">TTL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
`;
|
||||
|
||||
if (records.length === 0) {
|
||||
html += `
|
||||
<tr>
|
||||
<td colspan="3" class="py-4 text-center text-gray-500">未找到 ${domain} 的 ${recordType} 记录</td>
|
||||
</tr>
|
||||
`;
|
||||
} else {
|
||||
// 添加查询结果
|
||||
records.forEach(record => {
|
||||
const type = record.Type || record.type || recordType;
|
||||
|
||||
// 处理不同格式的值
|
||||
let value;
|
||||
if (record.Value) {
|
||||
value = record.Value;
|
||||
} else if (record.ip || record.address) {
|
||||
value = record.ip || record.address;
|
||||
} else if (record.target) {
|
||||
value = record.target;
|
||||
} else if (record.text) {
|
||||
value = record.text;
|
||||
} else if (record.name) {
|
||||
value = record.name;
|
||||
} else {
|
||||
value = JSON.stringify(record);
|
||||
}
|
||||
|
||||
// 格式化不同类型的记录值
|
||||
if (type === 'MX' && (record.Preference || record.priority)) {
|
||||
value = `${record.Preference || record.priority} ${value}`;
|
||||
} else if (type === 'SRV') {
|
||||
if (record.Priority && record.Weight && record.Port) {
|
||||
value = `${record.Priority} ${record.Weight} ${record.Port} ${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
const ttl = record.TTL || record.ttl || '-';
|
||||
|
||||
html += `
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="py-3 px-4 text-sm font-medium text-gray-900">${type}</td>
|
||||
<td class="py-3 px-4 text-sm text-gray-900 font-mono break-all">${value}</td>
|
||||
<td class="py-3 px-4 text-sm text-gray-500">${ttl}</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
html += `
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
resultDiv.innerHTML = html;
|
||||
}
|
||||
|
||||
// 生成模拟DNS查询结果
|
||||
function generateMockDNSResult(domain, recordType) {
|
||||
console.log('生成模拟DNS结果:', domain, recordType);
|
||||
|
||||
const mockData = {
|
||||
'A': [
|
||||
{ Type: 'A', Value: '192.168.1.1', TTL: 300 },
|
||||
{ Type: 'A', Value: '192.168.1.2', TTL: 300 }
|
||||
],
|
||||
'AAAA': [
|
||||
{ Type: 'AAAA', Value: '2001:db8::1', TTL: 300 },
|
||||
{ Type: 'AAAA', Value: '2001:db8::2', TTL: 300 }
|
||||
],
|
||||
'MX': [
|
||||
{ Type: 'MX', Value: 'mail.' + domain, Preference: 10, TTL: 3600 },
|
||||
{ Type: 'MX', Value: 'mail2.' + domain, Preference: 20, TTL: 3600 }
|
||||
],
|
||||
'NS': [
|
||||
{ Type: 'NS', Value: 'ns1.' + domain, TTL: 86400 },
|
||||
{ Type: 'NS', Value: 'ns2.' + domain, TTL: 86400 }
|
||||
],
|
||||
'CNAME': [
|
||||
{ Type: 'CNAME', Value: 'www.' + domain, TTL: 300 }
|
||||
],
|
||||
'TXT': [
|
||||
{ Type: 'TXT', Value: 'v=spf1 include:_spf.' + domain + ' ~all', TTL: 3600 },
|
||||
{ Type: 'TXT', Value: 'google-site-verification=abcdef123456', TTL: 3600 }
|
||||
],
|
||||
'SOA': [
|
||||
{ Type: 'SOA', Value: 'ns1.' + domain + ' admin.' + domain + ' 1 3600 1800 604800 86400', TTL: 86400 }
|
||||
]
|
||||
};
|
||||
|
||||
return mockData[recordType] || [
|
||||
{ Type: recordType, Value: 'No records found', TTL: '-' }
|
||||
];
|
||||
}
|
||||
|
||||
// 显示模拟查询结果
|
||||
function displayMockQueryResult(domain, recordType) {
|
||||
function displayQueryResult(result, domain) {
|
||||
const resultDiv = document.getElementById('query-result');
|
||||
if (!resultDiv) return;
|
||||
|
||||
// 显示提示信息
|
||||
resultDiv.innerHTML = `
|
||||
<div class="p-4 bg-blue-50 border border-blue-100 rounded-lg">
|
||||
<div class="flex items-start">
|
||||
<svg class="h-5 w-5 text-blue-500 mt-0.5 mr-2" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>
|
||||
<div>
|
||||
<p class="text-sm text-blue-700">这是一个DNS查询工具示例。输入域名并选择记录类型,然后点击查询按钮获取DNS记录信息。</p>
|
||||
// 显示结果容器
|
||||
resultDiv.classList.remove('hidden');
|
||||
|
||||
// 解析结果
|
||||
const status = result.blocked ? '被屏蔽' : '正常';
|
||||
const statusClass = result.blocked ? 'text-danger' : 'text-success';
|
||||
const blockType = result.blocked ? result.blockType || '未知' : '正常';
|
||||
const timestamp = new Date(result.timestamp).toLocaleString();
|
||||
|
||||
// 更新结果显示
|
||||
document.getElementById('result-domain').textContent = domain;
|
||||
document.getElementById('result-status').innerHTML = `<span class="${statusClass}">${status}</span>`;
|
||||
document.getElementById('result-type').textContent = blockType;
|
||||
document.getElementById('result-time').textContent = timestamp;
|
||||
document.getElementById('result-details').textContent = JSON.stringify(result, null, 2);
|
||||
}
|
||||
|
||||
// 保存查询历史
|
||||
function saveQueryHistory(domain, result) {
|
||||
// 获取现有历史记录
|
||||
let history = JSON.parse(localStorage.getItem('dnsQueryHistory') || '[]');
|
||||
|
||||
// 创建历史记录项
|
||||
const historyItem = {
|
||||
domain: domain,
|
||||
timestamp: new Date().toISOString(),
|
||||
result: {
|
||||
blocked: result.blocked,
|
||||
blockType: result.blockType
|
||||
}
|
||||
};
|
||||
|
||||
// 添加到历史记录开头
|
||||
history.unshift(historyItem);
|
||||
|
||||
// 限制历史记录数量
|
||||
if (history.length > 20) {
|
||||
history = history.slice(0, 20);
|
||||
}
|
||||
|
||||
// 保存到本地存储
|
||||
localStorage.setItem('dnsQueryHistory', JSON.stringify(history));
|
||||
}
|
||||
|
||||
// 加载查询历史
|
||||
function loadQueryHistory() {
|
||||
const historyDiv = document.getElementById('query-history');
|
||||
if (!historyDiv) return;
|
||||
|
||||
// 获取历史记录
|
||||
const history = JSON.parse(localStorage.getItem('dnsQueryHistory') || '[]');
|
||||
|
||||
if (history.length === 0) {
|
||||
historyDiv.innerHTML = '<div class="text-center text-gray-500 py-4">暂无查询历史</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成历史记录HTML
|
||||
const historyHTML = history.map(item => {
|
||||
const statusClass = item.result.blocked ? 'text-danger' : 'text-success';
|
||||
const statusText = item.result.blocked ? '被屏蔽' : '正常';
|
||||
const blockType = item.result.blocked ? item.result.blockType : '正常';
|
||||
const formattedTime = new Date(item.timestamp).toLocaleString();
|
||||
|
||||
return `
|
||||
<div class="flex flex-col md:flex-row justify-between items-start md:items-center p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="font-medium">${item.domain}</span>
|
||||
<span class="${statusClass} text-sm">${statusText}</span>
|
||||
<span class="text-xs text-gray-500">${blockType}</span>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 mt-1">${formattedTime}</div>
|
||||
</div>
|
||||
<button class="mt-2 md:mt-0 px-3 py-1 bg-primary text-white text-sm rounded-md hover:bg-primary/90 transition-colors" onclick="requeryFromHistory('${item.domain}')">
|
||||
<i class="fa fa-refresh mr-1"></i>重新查询
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
historyDiv.innerHTML = historyHTML;
|
||||
}
|
||||
|
||||
// 从历史记录重新查询
|
||||
function requeryFromHistory(domain) {
|
||||
const domainInput = document.getElementById('dns-query-domain');
|
||||
if (domainInput) {
|
||||
domainInput.value = domain;
|
||||
handleDNSQuery();
|
||||
}
|
||||
}
|
||||
|
||||
// 清空查询历史
|
||||
function clearQueryHistory() {
|
||||
if (confirm('确定要清空所有查询历史吗?')) {
|
||||
localStorage.removeItem('dnsQueryHistory');
|
||||
loadQueryHistory();
|
||||
showSuccessMessage('查询历史已清空');
|
||||
}
|
||||
}
|
||||
|
||||
// 设置事件监听器
|
||||
function setupQueryEventListeners() {
|
||||
// 尝试多种可能的按钮ID
|
||||
const queryButtons = [
|
||||
document.getElementById('query-btn'),
|
||||
document.getElementById('query-button'),
|
||||
document.querySelector('button[type="submit"]'),
|
||||
...Array.from(document.querySelectorAll('button')).filter(btn =>
|
||||
btn.textContent && btn.textContent.includes('查询')
|
||||
)
|
||||
].filter(Boolean);
|
||||
// 查询按钮事件
|
||||
const queryBtn = document.getElementById('dns-query-btn');
|
||||
if (queryBtn) {
|
||||
queryBtn.addEventListener('click', handleDNSQuery);
|
||||
}
|
||||
|
||||
// 绑定查询按钮事件
|
||||
queryButtons.forEach(button => {
|
||||
console.log('绑定查询按钮事件:', button);
|
||||
button.addEventListener('click', handleDNSQuery);
|
||||
});
|
||||
|
||||
// 尝试多种可能的输入框ID
|
||||
const domainInputs = [
|
||||
document.getElementById('query-domain'),
|
||||
document.getElementById('domain-input'),
|
||||
document.querySelector('input[id*="domain"]')
|
||||
].filter(Boolean);
|
||||
|
||||
// 绑定回车键事件
|
||||
domainInputs.forEach(input => {
|
||||
console.log('绑定输入框回车事件:', input);
|
||||
input.addEventListener('keypress', (e) => {
|
||||
// 输入框回车键事件
|
||||
const domainInput = document.getElementById('dns-query-domain');
|
||||
if (domainInput) {
|
||||
domainInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
handleDNSQuery();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 添加示例域名按钮
|
||||
const querySection = document.querySelector('#dns-query-section, #query-section');
|
||||
if (querySection) {
|
||||
const exampleContainer = document.createElement('div');
|
||||
exampleContainer.className = 'mt-3';
|
||||
exampleContainer.innerHTML = `
|
||||
<p class="text-sm text-gray-500 mb-2">快速示例:</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button class="text-xs px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded-full text-gray-700 transition-colors" onclick="setExampleQuery('example.com', 'A')">example.com (A)</button>
|
||||
<button class="text-xs px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded-full text-gray-700 transition-colors" onclick="setExampleQuery('example.com', 'MX')">example.com (MX)</button>
|
||||
<button class="text-xs px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded-full text-gray-700 transition-colors" onclick="setExampleQuery('google.com', 'NS')">google.com (NS)</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 找到输入框容器并插入示例按钮
|
||||
const inputContainer = domainInputs[0]?.parentElement;
|
||||
if (inputContainer && inputContainer.nextElementSibling) {
|
||||
inputContainer.parentNode.insertBefore(exampleContainer, inputContainer.nextElementSibling);
|
||||
} else if (querySection.lastChild) {
|
||||
querySection.appendChild(exampleContainer);
|
||||
}
|
||||
// 清空历史按钮事件
|
||||
const clearHistoryBtn = document.getElementById('clear-history-btn');
|
||||
if (clearHistoryBtn) {
|
||||
clearHistoryBtn.addEventListener('click', clearQueryHistory);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置示例查询
|
||||
function setExampleQuery(domain, recordType) {
|
||||
const domainInput = document.getElementById('query-domain') || document.getElementById('domain-input');
|
||||
const recordTypeSelect = document.getElementById('query-record-type') || document.getElementById('record-type');
|
||||
// 显示加载状态
|
||||
function showLoading(message = '加载中...') {
|
||||
// 移除现有加载状态
|
||||
hideLoading();
|
||||
|
||||
if (domainInput) domainInput.value = domain;
|
||||
if (recordTypeSelect) recordTypeSelect.value = recordType;
|
||||
// 创建加载状态元素
|
||||
const loading = document.createElement('div');
|
||||
loading.className = 'loading-overlay fixed inset-0 bg-black/50 flex items-center justify-center z-50';
|
||||
loading.innerHTML = `
|
||||
<div class="bg-white rounded-lg p-6 shadow-lg flex items-center space-x-4">
|
||||
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
|
||||
<span class="text-gray-700 font-medium">${message}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 自动执行查询
|
||||
handleDNSQuery();
|
||||
document.body.appendChild(loading);
|
||||
}
|
||||
|
||||
// 隐藏加载状态
|
||||
function hideLoading() {
|
||||
const loading = document.querySelector('.loading-overlay');
|
||||
if (loading) {
|
||||
loading.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// 显示成功消息
|
||||
@@ -330,7 +222,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-transform duration-300 ease-in-out translate-y-0 opacity-0`;
|
||||
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`;
|
||||
|
||||
// 设置通知样式
|
||||
if (type === 'success') {
|
||||
@@ -341,7 +233,13 @@ function showNotification(message, type = 'info') {
|
||||
notification.classList.add('bg-blue-500', 'text-white');
|
||||
}
|
||||
|
||||
notification.textContent = message;
|
||||
notification.innerHTML = `
|
||||
<div class="flex items-center space-x-2">
|
||||
<i class="fa ${type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-exclamation-circle' : 'fa-info-circle'}"></i>
|
||||
<span>${message}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// 显示通知
|
||||
@@ -365,4 +263,14 @@ if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initQueryPage);
|
||||
} else {
|
||||
initQueryPage();
|
||||
}
|
||||
}
|
||||
|
||||
// 当切换到DNS查询页面时重新加载数据
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 监听hash变化,当切换到DNS查询页面时重新加载数据
|
||||
window.addEventListener('hashchange', () => {
|
||||
if (window.location.hash === '#query') {
|
||||
initQueryPage();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,41 +1,464 @@
|
||||
// 屏蔽管理页面功能实现
|
||||
|
||||
// 初始化屏蔽管理页面 - 已禁用加载屏蔽规则功能
|
||||
// 初始化屏蔽管理页面
|
||||
function initShieldPage() {
|
||||
// 不再加载屏蔽规则,避免DOM元素不存在导致的错误
|
||||
// 加载屏蔽规则统计信息
|
||||
loadShieldStats();
|
||||
// 加载本地规则
|
||||
loadLocalRules();
|
||||
// 加载远程黑名单
|
||||
loadRemoteBlacklists();
|
||||
// 加载hosts条目
|
||||
loadHostsEntries();
|
||||
// 设置事件监听器
|
||||
setupShieldEventListeners();
|
||||
}
|
||||
|
||||
// 加载屏蔽规则 - 已禁用此功能
|
||||
async function loadShieldRules() {
|
||||
console.log('屏蔽规则加载功能已禁用');
|
||||
// 加载屏蔽规则统计信息
|
||||
async function loadShieldStats() {
|
||||
showLoading('加载屏蔽规则统计信息...');
|
||||
try {
|
||||
const response = await fetch('/api/shield');
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to load shield stats');
|
||||
}
|
||||
const stats = await response.json();
|
||||
|
||||
// 更新统计信息
|
||||
document.getElementById('domain-rules-count').textContent = stats.domainRulesCount || 0;
|
||||
document.getElementById('domain-exceptions-count').textContent = stats.domainExceptionsCount || 0;
|
||||
document.getElementById('regex-rules-count').textContent = stats.regexRulesCount || 0;
|
||||
document.getElementById('regex-exceptions-count').textContent = stats.regexExceptionsCount || 0;
|
||||
document.getElementById('hosts-rules-count').textContent = stats.hostsRulesCount || 0;
|
||||
document.getElementById('blacklist-count').textContent = stats.blacklistCount || 0;
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error loading shield stats:', error);
|
||||
showErrorMessage('加载屏蔽规则统计信息失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新屏蔽规则表格 - 已禁用此功能
|
||||
function updateShieldRulesTable(rules) {
|
||||
// 不再更新表格,避免DOM元素不存在导致的错误
|
||||
console.log('屏蔽规则表格更新功能已禁用');
|
||||
// 加载本地规则
|
||||
async function loadLocalRules() {
|
||||
showLoading('加载本地规则...');
|
||||
try {
|
||||
const response = await fetch('/api/shield');
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to load local rules');
|
||||
}
|
||||
// 注意:当前API不返回完整规则列表,这里只是示例
|
||||
// 实际实现需要后端提供获取本地规则的API
|
||||
const rules = [];
|
||||
updateRulesTable(rules);
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error loading local rules:', error);
|
||||
showErrorMessage('加载本地规则失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 处理删除规则 - 已禁用此功能
|
||||
// 更新规则表格
|
||||
function updateRulesTable(rules) {
|
||||
const tbody = document.getElementById('rules-table-body');
|
||||
|
||||
if (rules.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="2" class="py-4 text-center text-gray-500">暂无规则</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = rules.map(rule => `
|
||||
<tr class="border-b border-gray-200">
|
||||
<td class="py-3 px-4">${rule}</td>
|
||||
<td class="py-3 px-4 text-right">
|
||||
<button class="delete-rule-btn px-3 py-1 bg-danger text-white rounded-md hover:bg-danger/90 transition-colors text-sm" data-rule="${rule}">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
|
||||
// 重新绑定删除事件
|
||||
document.querySelectorAll('.delete-rule-btn').forEach(btn => {
|
||||
btn.addEventListener('click', handleDeleteRule);
|
||||
});
|
||||
}
|
||||
|
||||
// 处理删除规则
|
||||
async function handleDeleteRule(e) {
|
||||
showErrorMessage('删除规则功能已禁用');
|
||||
const rule = e.target.closest('.delete-rule-btn').dataset.rule;
|
||||
showLoading('删除规则中...');
|
||||
try {
|
||||
const response = await fetch('/api/shield', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ rule })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to delete rule');
|
||||
}
|
||||
|
||||
showSuccessMessage('规则删除成功');
|
||||
// 重新加载规则
|
||||
loadLocalRules();
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error deleting rule:', error);
|
||||
showErrorMessage('删除规则失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新规则 - 已禁用此功能
|
||||
// 添加新规则
|
||||
async function handleAddRule() {
|
||||
showErrorMessage('添加规则功能已禁用');
|
||||
const rule = document.getElementById('new-rule').value.trim();
|
||||
if (!rule) {
|
||||
showErrorMessage('规则不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading('添加规则中...');
|
||||
try {
|
||||
const response = await fetch('/api/shield', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ rule })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to add rule');
|
||||
}
|
||||
|
||||
showSuccessMessage('规则添加成功');
|
||||
// 清空输入框
|
||||
document.getElementById('new-rule').value = '';
|
||||
// 隐藏表单
|
||||
document.getElementById('add-rule-form').classList.add('hidden');
|
||||
// 重新加载规则
|
||||
loadLocalRules();
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error adding rule:', error);
|
||||
showErrorMessage('添加规则失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新远程规则 - 已禁用此功能
|
||||
async function handleUpdateRemoteRules() {
|
||||
showErrorMessage('更新远程规则功能已禁用');
|
||||
// 加载远程黑名单
|
||||
async function loadRemoteBlacklists() {
|
||||
showLoading('加载远程黑名单...');
|
||||
try {
|
||||
const response = await fetch('/api/shield/blacklists');
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to load remote blacklists');
|
||||
}
|
||||
const blacklists = await response.json();
|
||||
updateBlacklistsTable(blacklists);
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error loading remote blacklists:', error);
|
||||
showErrorMessage('加载远程黑名单失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 设置事件监听器 - 已禁用规则相关功能
|
||||
// 更新黑名单表格
|
||||
function updateBlacklistsTable(blacklists) {
|
||||
const tbody = document.getElementById('blacklists-table-body');
|
||||
|
||||
if (blacklists.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="4" class="py-4 text-center text-gray-500">暂无黑名单</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = blacklists.map(blacklist => `
|
||||
<tr class="border-b border-gray-200">
|
||||
<td class="py-3 px-4">${blacklist.Name}</td>
|
||||
<td class="py-3 px-4 truncate max-w-xs">${blacklist.URL}</td>
|
||||
<td class="py-3 px-4 text-center">
|
||||
<span class="inline-block w-3 h-3 rounded-full ${blacklist.Enabled ? 'bg-success' : 'bg-gray-300'}"></span>
|
||||
</td>
|
||||
<td class="py-3 px-4 text-right space-x-2">
|
||||
<button class="update-blacklist-btn px-3 py-1 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors text-sm" data-url="${blacklist.URL}">
|
||||
<i class="fa fa-refresh"></i>
|
||||
</button>
|
||||
<button class="delete-blacklist-btn px-3 py-1 bg-danger text-white rounded-md hover:bg-danger/90 transition-colors text-sm" data-url="${blacklist.URL}">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
|
||||
// 重新绑定事件
|
||||
document.querySelectorAll('.update-blacklist-btn').forEach(btn => {
|
||||
btn.addEventListener('click', handleUpdateBlacklist);
|
||||
});
|
||||
|
||||
document.querySelectorAll('.delete-blacklist-btn').forEach(btn => {
|
||||
btn.addEventListener('click', handleDeleteBlacklist);
|
||||
});
|
||||
}
|
||||
|
||||
// 处理更新单个黑名单
|
||||
async function handleUpdateBlacklist(e) {
|
||||
const url = e.target.closest('.update-blacklist-btn').dataset.url;
|
||||
showLoading('更新黑名单中...');
|
||||
try {
|
||||
const response = await fetch(`/api/shield/blacklists/${encodeURIComponent(url)}/update`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to update blacklist');
|
||||
}
|
||||
|
||||
showSuccessMessage('黑名单更新成功');
|
||||
// 重新加载黑名单
|
||||
loadRemoteBlacklists();
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error updating blacklist:', error);
|
||||
showErrorMessage('更新黑名单失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 处理删除黑名单
|
||||
async function handleDeleteBlacklist(e) {
|
||||
const url = e.target.closest('.delete-blacklist-btn').dataset.url;
|
||||
showLoading('删除黑名单中...');
|
||||
try {
|
||||
const response = await fetch(`/api/shield/blacklists/${encodeURIComponent(url)}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to delete blacklist');
|
||||
}
|
||||
|
||||
showSuccessMessage('黑名单删除成功');
|
||||
// 重新加载黑名单
|
||||
loadRemoteBlacklists();
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error deleting blacklist:', error);
|
||||
showErrorMessage('删除黑名单失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 处理添加黑名单
|
||||
async function handleAddBlacklist() {
|
||||
const name = document.getElementById('blacklist-name').value.trim();
|
||||
const url = document.getElementById('blacklist-url').value.trim();
|
||||
|
||||
if (!name || !url) {
|
||||
showErrorMessage('名称和URL不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading('添加黑名单中...');
|
||||
try {
|
||||
const response = await fetch('/api/shield/blacklists', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ name, url })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to add blacklist');
|
||||
}
|
||||
|
||||
showSuccessMessage('黑名单添加成功');
|
||||
// 清空输入框
|
||||
document.getElementById('blacklist-name').value = '';
|
||||
document.getElementById('blacklist-url').value = '';
|
||||
// 隐藏表单
|
||||
document.getElementById('add-blacklist-form').classList.add('hidden');
|
||||
// 重新加载黑名单
|
||||
loadRemoteBlacklists();
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error adding blacklist:', error);
|
||||
showErrorMessage('添加黑名单失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载hosts条目
|
||||
async function loadHostsEntries() {
|
||||
showLoading('加载Hosts条目...');
|
||||
try {
|
||||
const response = await fetch('/api/shield/hosts');
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to load hosts entries');
|
||||
}
|
||||
const data = await response.json();
|
||||
updateHostsTable(data.hosts || []);
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error loading hosts entries:', error);
|
||||
showErrorMessage('加载Hosts条目失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新hosts表格
|
||||
function updateHostsTable(hosts) {
|
||||
const tbody = document.getElementById('hosts-table-body');
|
||||
|
||||
if (hosts.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="3" class="py-4 text-center text-gray-500">暂无Hosts条目</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = hosts.map(entry => `
|
||||
<tr class="border-b border-gray-200">
|
||||
<td class="py-3 px-4">${entry.ip}</td>
|
||||
<td class="py-3 px-4">${entry.domain}</td>
|
||||
<td class="py-3 px-4 text-right">
|
||||
<button class="delete-hosts-btn px-3 py-1 bg-danger text-white rounded-md hover:bg-danger/90 transition-colors text-sm" data-domain="${entry.domain}">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
|
||||
// 重新绑定删除事件
|
||||
document.querySelectorAll('.delete-hosts-btn').forEach(btn => {
|
||||
btn.addEventListener('click', handleDeleteHostsEntry);
|
||||
});
|
||||
}
|
||||
|
||||
// 处理删除hosts条目
|
||||
async function handleDeleteHostsEntry(e) {
|
||||
const domain = e.target.closest('.delete-hosts-btn').dataset.domain;
|
||||
showLoading('删除Hosts条目...');
|
||||
try {
|
||||
const response = await fetch('/api/shield/hosts', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ domain })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to delete hosts entry');
|
||||
}
|
||||
|
||||
showSuccessMessage('Hosts条目删除成功');
|
||||
// 重新加载hosts条目
|
||||
loadHostsEntries();
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error deleting hosts entry:', error);
|
||||
showErrorMessage('删除Hosts条目失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 处理添加hosts条目
|
||||
async function handleAddHostsEntry() {
|
||||
const ip = document.getElementById('hosts-ip').value.trim();
|
||||
const domain = document.getElementById('hosts-domain').value.trim();
|
||||
|
||||
if (!ip || !domain) {
|
||||
showErrorMessage('IP地址和域名不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading('添加Hosts条目...');
|
||||
try {
|
||||
const response = await fetch('/api/shield/hosts', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ ip, domain })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to add hosts entry');
|
||||
}
|
||||
|
||||
showSuccessMessage('Hosts条目添加成功');
|
||||
// 清空输入框
|
||||
document.getElementById('hosts-ip').value = '';
|
||||
document.getElementById('hosts-domain').value = '';
|
||||
// 隐藏表单
|
||||
document.getElementById('add-hosts-form').classList.add('hidden');
|
||||
// 重新加载hosts条目
|
||||
loadHostsEntries();
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error adding hosts entry:', error);
|
||||
showErrorMessage('添加Hosts条目失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 设置事件监听器
|
||||
function setupShieldEventListeners() {
|
||||
// 移除所有事件监听器,避免触发已禁用的功能
|
||||
console.log('屏蔽规则相关事件监听器已设置,但功能已禁用');
|
||||
// 本地规则管理事件
|
||||
document.getElementById('add-rule-btn').addEventListener('click', () => {
|
||||
document.getElementById('add-rule-form').classList.toggle('hidden');
|
||||
});
|
||||
|
||||
document.getElementById('save-rule-btn').addEventListener('click', handleAddRule);
|
||||
|
||||
document.getElementById('cancel-rule-btn').addEventListener('click', () => {
|
||||
document.getElementById('add-rule-form').classList.add('hidden');
|
||||
document.getElementById('new-rule').value = '';
|
||||
});
|
||||
|
||||
// 远程黑名单管理事件
|
||||
document.getElementById('add-blacklist-btn').addEventListener('click', () => {
|
||||
document.getElementById('add-blacklist-form').classList.toggle('hidden');
|
||||
});
|
||||
|
||||
document.getElementById('save-blacklist-btn').addEventListener('click', handleAddBlacklist);
|
||||
|
||||
document.getElementById('cancel-blacklist-btn').addEventListener('click', () => {
|
||||
document.getElementById('add-blacklist-form').classList.add('hidden');
|
||||
document.getElementById('blacklist-name').value = '';
|
||||
document.getElementById('blacklist-url').value = '';
|
||||
});
|
||||
|
||||
// Hosts条目管理事件
|
||||
document.getElementById('add-hosts-btn').addEventListener('click', () => {
|
||||
document.getElementById('add-hosts-form').classList.toggle('hidden');
|
||||
});
|
||||
|
||||
document.getElementById('save-hosts-btn').addEventListener('click', handleAddHostsEntry);
|
||||
|
||||
document.getElementById('cancel-hosts-btn').addEventListener('click', () => {
|
||||
document.getElementById('add-hosts-form').classList.add('hidden');
|
||||
document.getElementById('hosts-ip').value = '';
|
||||
document.getElementById('hosts-domain').value = '';
|
||||
});
|
||||
}
|
||||
|
||||
// 显示成功消息
|
||||
@@ -48,6 +471,32 @@ function showErrorMessage(message) {
|
||||
showNotification(message, 'error');
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
function showLoading(message = '加载中...') {
|
||||
// 移除现有加载状态
|
||||
hideLoading();
|
||||
|
||||
// 创建加载状态元素
|
||||
const loading = document.createElement('div');
|
||||
loading.className = 'loading-overlay fixed inset-0 bg-black/50 flex items-center justify-center z-50';
|
||||
loading.innerHTML = `
|
||||
<div class="bg-white rounded-lg p-6 shadow-lg flex items-center space-x-4">
|
||||
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
|
||||
<span class="text-gray-700 font-medium">${message}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(loading);
|
||||
}
|
||||
|
||||
// 隐藏加载状态
|
||||
function hideLoading() {
|
||||
const loading = document.querySelector('.loading-overlay');
|
||||
if (loading) {
|
||||
loading.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// 显示通知
|
||||
function showNotification(message, type = 'info') {
|
||||
// 移除现有通知
|
||||
@@ -58,7 +507,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-transform duration-300 ease-in-out translate-y-0 opacity-0`;
|
||||
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`;
|
||||
|
||||
// 设置通知样式
|
||||
if (type === 'success') {
|
||||
@@ -69,7 +518,13 @@ function showNotification(message, type = 'info') {
|
||||
notification.classList.add('bg-blue-500', 'text-white');
|
||||
}
|
||||
|
||||
notification.textContent = message;
|
||||
notification.innerHTML = `
|
||||
<div class="flex items-center space-x-2">
|
||||
<i class="fa ${type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-exclamation-circle' : 'fa-info-circle'}"></i>
|
||||
<span>${message}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// 显示通知
|
||||
@@ -93,4 +548,14 @@ if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initShieldPage);
|
||||
} else {
|
||||
initShieldPage();
|
||||
}
|
||||
}
|
||||
|
||||
// 当切换到屏蔽管理页面时重新加载数据
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 监听hash变化,当切换到屏蔽管理页面时重新加载数据
|
||||
window.addEventListener('hashchange', () => {
|
||||
if (window.location.hash === '#shield') {
|
||||
initShieldPage();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user