-
-
-
这是一个DNS查询工具示例。输入域名并选择记录类型,然后点击查询按钮获取DNS记录信息。
+ // 显示结果容器
+ 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 = `
${status}`;
+ 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 = '
暂无查询历史
';
+ 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 `
+
+
+
+ ${item.domain}
+ ${statusText}
+ ${blockType}
+
+
${formattedTime}
+
-
- `;
+ `;
+ }).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 = `
-
快速示例:
-
-
-
-
-
- `;
-
- // 找到输入框容器并插入示例按钮
- 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 = `
+
+ `;
- // 自动执行查询
- 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 = `
+
+
+ ${message}
+
+ `;
+
document.body.appendChild(notification);
// 显示通知
@@ -365,4 +263,14 @@ if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initQueryPage);
} else {
initQueryPage();
-}
\ No newline at end of file
+}
+
+// 当切换到DNS查询页面时重新加载数据
+document.addEventListener('DOMContentLoaded', () => {
+ // 监听hash变化,当切换到DNS查询页面时重新加载数据
+ window.addEventListener('hashchange', () => {
+ if (window.location.hash === '#query') {
+ initQueryPage();
+ }
+ });
+});
\ No newline at end of file
diff --git a/static/js/shield.js b/static/js/shield.js
index 5ee51a1..eac77b8 100644
--- a/static/js/shield.js
+++ b/static/js/shield.js
@@ -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 = '
| 暂无规则 |
';
+ return;
+ }
+
+ tbody.innerHTML = rules.map(rule => `
+
+ | ${rule} |
+
+
+ |
+
+ `).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 = '
| 暂无黑名单 |
';
+ return;
+ }
+
+ tbody.innerHTML = blacklists.map(blacklist => `
+
+ | ${blacklist.Name} |
+ ${blacklist.URL} |
+
+
+ |
+
+
+
+ |
+
+ `).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 = '
| 暂无Hosts条目 |
';
+ return;
+ }
+
+ tbody.innerHTML = hosts.map(entry => `
+
+ | ${entry.ip} |
+ ${entry.domain} |
+
+
+ |
+
+ `).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 = `
+
+ `;
+
+ 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 = `
+
+
+ ${message}
+
+ `;
+
document.body.appendChild(notification);
// 显示通知
@@ -93,4 +548,14 @@ if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initShieldPage);
} else {
initShieldPage();
-}
\ No newline at end of file
+}
+
+// 当切换到屏蔽管理页面时重新加载数据
+document.addEventListener('DOMContentLoaded', () => {
+ // 监听hash变化,当切换到屏蔽管理页面时重新加载数据
+ window.addEventListener('hashchange', () => {
+ if (window.location.hash === '#shield') {
+ initShieldPage();
+ }
+ });
+});
\ No newline at end of file