修复本地规则管理不工作的问题
This commit is contained in:
90
.trae/documents/修复web屏蔽管理页面一直显示处理中遮罩的问题.md
Normal file
90
.trae/documents/修复web屏蔽管理页面一直显示处理中遮罩的问题.md
Normal file
@@ -0,0 +1,90 @@
|
||||
## 问题分析
|
||||
|
||||
通过代码分析,我发现web屏蔽管理页面点击后一直显示处理中灰色遮罩的原因有两个:
|
||||
|
||||
1. **JavaScript错误导致`hideLoading()`不被调用**:
|
||||
- `loadLocalRules()`函数尝试访问`document.getElementById('local-rules-count')`,但该元素在HTML中不存在
|
||||
- `loadRemoteRules()`函数尝试访问`document.getElementById('remote-rules-count')`,但该元素在HTML中不存在
|
||||
- `setupShieldEventListeners()`函数尝试访问`document.getElementById('view-local-rules-btn')`和`document.getElementById('view-remote-rules-btn')`,但这些元素在HTML中不存在
|
||||
- 这些JavaScript错误会导致函数执行中断,从而使`hideLoading()`不被调用,加载遮罩一直显示
|
||||
|
||||
2. **竞态条件问题**:
|
||||
- `initShieldPage()`函数并行调用三个异步函数:`loadShieldStats()`、`loadLocalRules()`和`loadRemoteBlacklists()`
|
||||
- 每个异步函数都会调用`showLoading()`,而`showLoading()`会先调用`hideLoading()`来移除现有加载状态,然后创建一个新的加载状态
|
||||
- 这种并行调用可能导致竞态条件,使最后一个加载遮罩无法被正确销毁
|
||||
|
||||
## 修复计划
|
||||
|
||||
1. **修改`loadLocalRules()`函数**:添加元素存在性检查,避免JavaScript错误
|
||||
2. **修改`loadRemoteRules()`函数**:添加元素存在性检查,避免JavaScript错误
|
||||
3. **修改`setupShieldEventListeners()`函数**:添加元素存在性检查,避免JavaScript错误
|
||||
4. **修改`initShieldPage()`函数**:确保只创建一个加载遮罩,并在所有异步函数完成后销毁它
|
||||
|
||||
## 修复步骤
|
||||
|
||||
### 步骤1:修改`loadLocalRules()`函数
|
||||
在`shield.js`文件中,修改`loadLocalRules()`函数,添加元素存在性检查:
|
||||
|
||||
```javascript
|
||||
// 更新本地规则数量显示
|
||||
if (document.getElementById('local-rules-count')) {
|
||||
document.getElementById('local-rules-count').textContent = data.localRulesCount || 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤2:修改`loadRemoteRules()`函数
|
||||
在`shield.js`文件中,修改`loadRemoteRules()`函数,添加元素存在性检查:
|
||||
|
||||
```javascript
|
||||
// 更新远程规则数量显示
|
||||
if (document.getElementById('remote-rules-count')) {
|
||||
document.getElementById('remote-rules-count').textContent = data.remoteRulesCount || 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤3:修改`setupShieldEventListeners()`函数
|
||||
在`shield.js`文件中,修改`setupShieldEventListeners()`函数,添加元素存在性检查:
|
||||
|
||||
```javascript
|
||||
// 添加切换查看本地规则和远程规则的事件监听
|
||||
if (document.getElementById('view-local-rules-btn')) {
|
||||
document.getElementById('view-local-rules-btn').addEventListener('click', loadLocalRules);
|
||||
}
|
||||
|
||||
if (document.getElementById('view-remote-rules-btn')) {
|
||||
document.getElementById('view-remote-rules-btn').addEventListener('click', loadRemoteRules);
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤4:修改`initShieldPage()`函数
|
||||
在`shield.js`文件中,修改`initShieldPage()`函数,确保只创建一个加载遮罩,并在所有异步函数完成后销毁它:
|
||||
|
||||
```javascript
|
||||
// 初始化屏蔽管理页面
|
||||
async function initShieldPage() {
|
||||
showLoading('加载屏蔽管理数据...');
|
||||
try {
|
||||
// 并行加载所有数据
|
||||
await Promise.all([
|
||||
loadShieldStats(),
|
||||
loadLocalRules(),
|
||||
loadRemoteBlacklists()
|
||||
]);
|
||||
// 设置事件监听器
|
||||
setupShieldEventListeners();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤5:修改异步函数,移除内部的`showLoading()`和`hideLoading()`调用
|
||||
在`shield.js`文件中,修改`loadShieldStats()`、`loadLocalRules()`和`loadRemoteBlacklists()`函数,移除内部的`showLoading()`和`hideLoading()`调用,只保留数据加载逻辑。
|
||||
|
||||
## 预期效果
|
||||
|
||||
修复后,web屏蔽管理页面点击后:
|
||||
1. 只会显示一个加载遮罩
|
||||
2. 不会出现JavaScript错误
|
||||
3. 所有数据加载完成后,加载遮罩会被正确隐藏
|
||||
4. 页面会正常显示屏蔽管理设置内容
|
||||
41
.trae/documents/修复本地规则管理不工作的问题.md
Normal file
41
.trae/documents/修复本地规则管理不工作的问题.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# 问题分析
|
||||
|
||||
1. **问题现象**:用户点击删除按钮删除本地规则时,通过API发送数据到服务器,但Web界面没有自动刷新本地规则列表。
|
||||
|
||||
2. **问题根源**:
|
||||
- 后端`RemoveRule`方法存在缺陷,当删除规则时没有正确更新所有相关映射
|
||||
- 前端代码虽然调用了重新加载规则列表的函数,但由于后端数据不一致,导致刷新后列表没有变化
|
||||
|
||||
3. **具体问题**:
|
||||
- 在`shield/manager.go`的`RemoveRule`方法中,删除域名规则时只从`domainRules`或`domainExceptions`映射中删除了规则,但没有更新`domainRulesIsLocal`、`domainExceptionsIsLocal`、`domainRulesSource`和`domainExceptionsSource`映射
|
||||
- 处理正则表达式规则时,使用了错误的比较方式(`re.pattern.String() != pattern`),而应该使用原始规则字符串进行比较
|
||||
|
||||
# 修复方案
|
||||
|
||||
1. **修复`RemoveRule`方法**:
|
||||
- 当删除域名规则时,同时更新所有相关映射
|
||||
- 当删除排除规则时,同时更新所有相关映射
|
||||
- 修复正则表达式规则的比较方式,使用原始规则字符串进行比较
|
||||
|
||||
2. **验证前端代码**:
|
||||
- 确认前端代码在删除规则后正确调用了重新加载规则列表的函数
|
||||
- 验证前端代码处理API响应的逻辑是否正确
|
||||
|
||||
# 实现步骤
|
||||
|
||||
1. 修改`shield/manager.go`文件中的`RemoveRule`方法:
|
||||
- 在删除域名规则时,添加删除`domainRulesIsLocal`和`domainRulesSource`映射的代码
|
||||
- 在删除排除规则时,添加删除`domainExceptionsIsLocal`和`domainExceptionsSource`映射的代码
|
||||
- 修复正则表达式规则的比较方式
|
||||
|
||||
2. 测试修复后的功能:
|
||||
- 启动DNS服务器
|
||||
- 访问Web界面,添加几条本地规则
|
||||
- 点击删除按钮删除其中一条规则
|
||||
- 验证规则列表是否自动刷新,且删除的规则不再显示
|
||||
|
||||
# 预期结果
|
||||
|
||||
- 用户点击删除按钮后,规则被成功删除
|
||||
- Web界面自动刷新,删除的规则从列表中消失
|
||||
- 本地规则文件被正确更新,删除的规则不再存在于文件中
|
||||
51
.trae/documents/修复本地规则管理删除规则功能.md
Normal file
51
.trae/documents/修复本地规则管理删除规则功能.md
Normal file
@@ -0,0 +1,51 @@
|
||||
## 问题分析
|
||||
|
||||
通过代码分析,我发现本地规则管理删除规则功能失效的原因有两个:
|
||||
|
||||
1. **规则格式不匹配**:
|
||||
- 前端显示的规则带有修饰符,例如:`||example.com^`
|
||||
- 服务器端实际存储的是裸域名,例如:`example.com`
|
||||
- `RemoveRule` 函数在处理规则时,虽然尝试了多种格式变体,但没有正确处理前端发送的带有修饰符的规则
|
||||
|
||||
2. **本地规则标记未更新**:
|
||||
- `RemoveRule` 函数没有考虑 `m.domainRulesIsLocal` 和 `m.domainExceptionsIsLocal` 映射,这些映射用于标记哪些规则是本地规则
|
||||
- 删除规则后,没有更新这些映射,导致规则删除不彻底
|
||||
|
||||
## 修复计划
|
||||
|
||||
1. **修改 `RemoveRule` 函数**:
|
||||
- 改进规则处理逻辑,确保能正确处理带有修饰符的规则
|
||||
- 更新 `domainRulesIsLocal` 和 `domainExceptionsIsLocal` 映射,确保本地规则被正确删除
|
||||
|
||||
2. **修改 `GetLocalRules` 函数**:
|
||||
- 确保返回的规则格式与 `RemoveRule` 函数期望的格式一致
|
||||
|
||||
3. **添加调试日志**:
|
||||
- 在关键位置添加日志,便于调试和监控规则删除过程
|
||||
|
||||
## 修复步骤
|
||||
|
||||
### 步骤1:修改 `RemoveRule` 函数
|
||||
在 `shield/manager.go` 文件中,修改 `RemoveRule` 函数,改进规则处理逻辑:
|
||||
|
||||
1. 确保正确处理带有修饰符的规则
|
||||
2. 更新 `domainRulesIsLocal` 和 `domainExceptionsIsLocal` 映射
|
||||
3. 添加调试日志
|
||||
|
||||
### 步骤2:测试修复效果
|
||||
- 启动服务器
|
||||
- 访问本地规则管理页面
|
||||
- 添加一条本地规则
|
||||
- 删除该规则
|
||||
- 验证规则是否被正确删除,页面内容是否减少
|
||||
|
||||
## 预期效果
|
||||
|
||||
修复后,本地规则管理删除规则功能将正常工作:
|
||||
- 点击删除按钮后,规则会被正确发送到服务器
|
||||
- 服务器会正确处理带有修饰符的规则
|
||||
- 本地规则标记会被正确更新
|
||||
- 规则会被持久化保存
|
||||
- 页面内容会立即减少
|
||||
|
||||
这样就能确保本地规则管理删除规则功能正常工作,提供良好的用户体验。
|
||||
81
ReadMe.md
81
ReadMe.md
@@ -1,81 +0,0 @@
|
||||
# DNS服务器项目介绍
|
||||
## 项目概述
|
||||
这是一个基于Go语言开发的高性能DNS服务器,具备域名屏蔽、Hosts管理、统计分析和远程规则管理等功能。服务器支持通过Web界面进行管理配置,同时能够自动更新和缓存远程规则列表。
|
||||
|
||||
## 技术架构
|
||||
### 核心组件
|
||||
1. DNS服务模块 ( `server.go` )
|
||||
- 基于 github.com/miekg/dns 库实现高性能DNS查询处理
|
||||
- 支持配置上游DNS服务器进行递归查询
|
||||
- 实现域名屏蔽、统计数据收集等核心功能
|
||||
|
||||
2. 屏蔽管理系统 ( `manager.go` )
|
||||
- 管理本地和远程屏蔽规则
|
||||
- 支持规则缓存、自动更新和统计
|
||||
- 实现域名和正则表达式规则的解析和匹配
|
||||
|
||||
3. HTTP控制台 ( `server.go` )
|
||||
- 提供Web管理界面
|
||||
- 实现REST API用于配置管理和数据查询
|
||||
|
||||
4. 配置管理 ( `config.go` )
|
||||
- 定义配置结构和加载功能
|
||||
- 支持JSON格式配置文件
|
||||
|
||||
## 主要功能特性
|
||||
### 1. 域名屏蔽系统
|
||||
- 支持本地规则文件和远程规则URL
|
||||
- 多种屏蔽方式:NXDOMAIN、refused、emptyIP、customIP
|
||||
- 支持域名精确匹配和正则表达式匹配
|
||||
- 远程规则自动缓存和更新机制
|
||||
### 2. Hosts管理
|
||||
- 支持自定义Hosts映射
|
||||
- 提供Web界面管理Hosts条目
|
||||
- 自动保存Hosts配置
|
||||
### 3. 统计分析功能
|
||||
- 记录屏蔽域名统计信息
|
||||
- 记录解析域名统计信息
|
||||
- 提供按小时统计的屏蔽数据
|
||||
- 支持查询最常屏蔽和解析的域名
|
||||
### 4. 远程规则管理
|
||||
- 支持添加多个远程规则URL
|
||||
- 自动定期更新远程规则
|
||||
- 本地缓存机制确保规则可用性
|
||||
- Web界面可视化管理
|
||||
### 5. 管理界面
|
||||
- 提供直观的Web控制台
|
||||
- 支持查看服务器状态和统计信息
|
||||
- 规则管理和配置修改
|
||||
- DNS查询测试工具
|
||||
## 项目结构
|
||||
```
|
||||
/root/dns/
|
||||
├── config/ # 配置管理
|
||||
├── data/ # 数据目录(包含缓存和统计)
|
||||
│ └── remote_rules/ # 远程规则缓存
|
||||
├── dns/ # DNS服务器核心
|
||||
├── http/ # HTTP控制台
|
||||
├── logger/ # 日志系统
|
||||
├── shield/ # 屏蔽规则管理
|
||||
├── static/ # 静态Web文件
|
||||
├── main.go # 程序入口
|
||||
└── config.json # 配置文件
|
||||
```
|
||||
## 配置项说明
|
||||
主要配置文件 `config.json` 包含以下部分:
|
||||
|
||||
- DNS配置 :端口、上游DNS服务器、超时设置等
|
||||
- HTTP配置 :控制台端口、主机绑定等
|
||||
- 屏蔽配置 :规则文件路径、远程规则URL、更新间隔等
|
||||
- 日志配置 :日志文件路径、级别设置等
|
||||
## 使用场景
|
||||
1. 网络内容过滤(广告、恶意网站屏蔽)
|
||||
2. 本地DNS缓存加速
|
||||
3. 企业/家庭网络DNS管理
|
||||
4. 开发测试环境DNS重定向
|
||||
## 技术栈
|
||||
- 语言 :Go
|
||||
- DNS库 :github.com/miekg/dns
|
||||
- 日志库 :github.com/sirupsen/logrus
|
||||
- Web前端 :HTML/CSS/JavaScript
|
||||
该DNS服务器具有高性能、功能全面、易于配置等特点,适用于需要精确控制DNS查询结果的各种网络环境。
|
||||
@@ -1,7 +0,0 @@
|
||||
# DNS Server Hosts File
|
||||
# Generated by DNS Server
|
||||
|
||||
127.0.0.1 localhost
|
||||
::1 localhost
|
||||
|
||||
127.0.0.1 example.com
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
/example.com/
|
||||
/adjust.com/
|
||||
/adjust.c/
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"blockedDomainsCount": {},
|
||||
"resolvedDomainsCount": {},
|
||||
"lastSaved": "2025-11-28T18:01:32.204427496+08:00"
|
||||
}
|
||||
3282
data/stats.json
3282
data/stats.json
File diff suppressed because it is too large
Load Diff
BIN
dns-server
BIN
dns-server
Binary file not shown.
118716
logs/dns-server.log
118716
logs/dns-server.log
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -804,6 +804,8 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
domain := strings.TrimPrefix(format, "@@||")
|
||||
if _, exists := m.domainExceptions[domain]; exists {
|
||||
delete(m.domainExceptions, domain)
|
||||
delete(m.domainExceptionsIsLocal, domain)
|
||||
delete(m.domainExceptionsSource, domain)
|
||||
removed = true
|
||||
break
|
||||
}
|
||||
@@ -812,6 +814,8 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
domain := strings.TrimPrefix(format, "||")
|
||||
if _, exists := m.domainRules[domain]; exists {
|
||||
delete(m.domainRules, domain)
|
||||
delete(m.domainRulesIsLocal, domain)
|
||||
delete(m.domainRulesSource, domain)
|
||||
removed = true
|
||||
break
|
||||
}
|
||||
@@ -819,11 +823,15 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
// 尝试直接作为域名删除
|
||||
if _, exists := m.domainRules[format]; exists {
|
||||
delete(m.domainRules, format)
|
||||
delete(m.domainRulesIsLocal, format)
|
||||
delete(m.domainRulesSource, format)
|
||||
removed = true
|
||||
break
|
||||
}
|
||||
if _, exists := m.domainExceptions[format]; exists {
|
||||
delete(m.domainExceptions, format)
|
||||
delete(m.domainExceptionsIsLocal, format)
|
||||
delete(m.domainExceptionsSource, format)
|
||||
removed = true
|
||||
break
|
||||
}
|
||||
@@ -832,12 +840,10 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
|
||||
// 处理正则表达式规则
|
||||
if !removed && strings.HasPrefix(cleanRule, "/") && strings.HasSuffix(cleanRule, "/") {
|
||||
pattern := strings.TrimPrefix(strings.TrimSuffix(cleanRule, "/"), "/")
|
||||
|
||||
// 检查是否在正则表达式规则中
|
||||
newRegexRules := []regexRule{}
|
||||
for _, re := range m.regexRules {
|
||||
if re.pattern.String() != pattern {
|
||||
if re.original != rule && re.original != cleanRule {
|
||||
newRegexRules = append(newRegexRules, re)
|
||||
} else {
|
||||
removed = true
|
||||
@@ -849,7 +855,7 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
if !removed {
|
||||
newRegexExceptions := []regexRule{}
|
||||
for _, re := range m.regexExceptions {
|
||||
if re.pattern.String() != pattern {
|
||||
if re.original != rule && re.original != cleanRule {
|
||||
newRegexExceptions = append(newRegexExceptions, re)
|
||||
} else {
|
||||
removed = true
|
||||
|
||||
@@ -36,7 +36,6 @@ function initConfigPage() {
|
||||
// 加载系统配置
|
||||
async function loadConfig() {
|
||||
try {
|
||||
showLoading(true);
|
||||
const result = await api.getConfig();
|
||||
|
||||
// 检查API返回的错误
|
||||
@@ -49,8 +48,6 @@ async function loadConfig() {
|
||||
} catch (error) {
|
||||
// 捕获可能的异常(虽然apiRequest不应该再抛出异常)
|
||||
showErrorMessage('加载配置失败: ' + (error.message || '未知错误'));
|
||||
} finally {
|
||||
showLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +103,6 @@ async function handleSaveConfig() {
|
||||
if (!formData) return;
|
||||
|
||||
try {
|
||||
showLoading(true);
|
||||
const result = await api.saveConfig(formData);
|
||||
|
||||
// 检查API返回的错误
|
||||
@@ -119,8 +115,6 @@ async function handleSaveConfig() {
|
||||
} catch (error) {
|
||||
// 捕获可能的异常(虽然apiRequest不应该再抛出异常)
|
||||
showErrorMessage('保存配置失败: ' + (error.message || '未知错误'));
|
||||
} finally {
|
||||
showLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +123,6 @@ async function handleRestartService() {
|
||||
if (!confirm('确定要重启DNS服务吗?重启期间服务可能会短暂不可用。')) return;
|
||||
|
||||
try {
|
||||
showLoading(true);
|
||||
const result = await api.restartService();
|
||||
|
||||
// 检查API返回的错误
|
||||
@@ -142,8 +135,6 @@ async function handleRestartService() {
|
||||
} catch (error) {
|
||||
// 捕获可能的异常(虽然apiRequest不应该再抛出异常)
|
||||
showErrorMessage('重启服务失败: ' + (error.message || '未知错误'));
|
||||
} finally {
|
||||
showLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,34 +211,7 @@ function setupConfigEventListeners() {
|
||||
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) {
|
||||
|
||||
@@ -446,18 +446,7 @@ async function loadDashboardData() {
|
||||
];
|
||||
}
|
||||
|
||||
// 实现数据加载状态管理
|
||||
function showLoading(elementId) {
|
||||
const loadingElement = document.getElementById(elementId + '-loading');
|
||||
const errorElement = document.getElementById(elementId + '-error');
|
||||
if (loadingElement) loadingElement.classList.remove('hidden');
|
||||
if (errorElement) errorElement.classList.add('hidden');
|
||||
}
|
||||
|
||||
function hideLoading(elementId) {
|
||||
const loadingElement = document.getElementById(elementId + '-loading');
|
||||
if (loadingElement) loadingElement.classList.add('hidden');
|
||||
}
|
||||
|
||||
function showError(elementId) {
|
||||
const loadingElement = document.getElementById(elementId + '-loading');
|
||||
@@ -468,7 +457,6 @@ async function loadDashboardData() {
|
||||
|
||||
// 尝试获取TOP客户端,优先使用真实数据,失败时使用模拟数据
|
||||
let topClients = [];
|
||||
showLoading('top-clients');
|
||||
try {
|
||||
const clientsData = await api.getTopClients();
|
||||
console.log('TOP客户端:', clientsData);
|
||||
@@ -513,13 +501,10 @@ async function loadDashboardData() {
|
||||
{ ip: '192.168.1.104', count: 50 }
|
||||
];
|
||||
showError('top-clients');
|
||||
} finally {
|
||||
hideLoading('top-clients');
|
||||
}
|
||||
|
||||
// 尝试获取TOP域名,优先使用真实数据,失败时使用模拟数据
|
||||
let topDomains = [];
|
||||
showLoading('top-domains');
|
||||
try {
|
||||
const domainsData = await api.getTopDomains();
|
||||
console.log('TOP域名:', domainsData);
|
||||
@@ -564,8 +549,6 @@ async function loadDashboardData() {
|
||||
{ domain: 'youtube.com', count: 30 }
|
||||
];
|
||||
showError('top-domains');
|
||||
} finally {
|
||||
hideLoading('top-domains');
|
||||
}
|
||||
|
||||
// 更新统计卡片
|
||||
|
||||
@@ -23,8 +23,6 @@ async function handleDNSQuery() {
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading('查询中...');
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/query?domain=${encodeURIComponent(domain)}`);
|
||||
if (!response.ok) {
|
||||
@@ -35,11 +33,9 @@ async function handleDNSQuery() {
|
||||
displayQueryResult(result, domain);
|
||||
saveQueryHistory(domain, result);
|
||||
loadQueryHistory();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('DNS查询出错:', error);
|
||||
showErrorMessage('查询失败,请稍后重试');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,31 +225,7 @@ function setupQueryEventListeners() {
|
||||
}
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
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 showSuccessMessage(message) {
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
// 屏蔽管理页面功能实现
|
||||
|
||||
// 初始化屏蔽管理页面
|
||||
function initShieldPage() {
|
||||
// 加载屏蔽规则统计信息
|
||||
loadShieldStats();
|
||||
// 加载本地规则
|
||||
loadLocalRules();
|
||||
// 加载远程黑名单
|
||||
loadRemoteBlacklists();
|
||||
async function initShieldPage() {
|
||||
// 并行加载所有数据
|
||||
await Promise.all([
|
||||
loadShieldStats(),
|
||||
loadLocalRules(),
|
||||
loadRemoteBlacklists()
|
||||
]);
|
||||
// 设置事件监听器
|
||||
setupShieldEventListeners();
|
||||
}
|
||||
|
||||
// 加载屏蔽规则统计信息
|
||||
async function loadShieldStats() {
|
||||
showLoading('加载屏蔽规则统计信息...');
|
||||
try {
|
||||
const response = await fetch('/api/shield');
|
||||
|
||||
@@ -40,18 +39,14 @@ async function loadShieldStats() {
|
||||
element.textContent = item.value || 0;
|
||||
}
|
||||
});
|
||||
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('加载屏蔽规则统计信息失败:', error);
|
||||
showErrorMessage('加载屏蔽规则统计信息失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载本地规则
|
||||
async function loadLocalRules() {
|
||||
showLoading('加载本地规则...');
|
||||
try {
|
||||
const response = await fetch('/api/shield/localrules');
|
||||
|
||||
@@ -89,17 +84,14 @@ async function loadLocalRules() {
|
||||
}
|
||||
|
||||
updateRulesTable(rules);
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('加载本地规则失败:', error);
|
||||
showErrorMessage('加载本地规则失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载远程规则
|
||||
async function loadRemoteRules() {
|
||||
showLoading('加载远程规则...');
|
||||
try {
|
||||
// 设置当前规则类型
|
||||
currentRulesType = 'remote';
|
||||
@@ -136,11 +128,9 @@ async function loadRemoteRules() {
|
||||
}
|
||||
|
||||
updateRulesTable(rules);
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('加载远程规则失败:', error);
|
||||
showErrorMessage('加载远程规则失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,8 +169,19 @@ function updateRulesTable(rules) {
|
||||
const deleteBtn = document.createElement('button');
|
||||
deleteBtn.className = 'delete-rule-btn px-3 py-1 bg-danger text-white rounded-md hover:bg-danger/90 transition-colors text-sm';
|
||||
deleteBtn.dataset.rule = rule;
|
||||
deleteBtn.innerHTML = '<i class="fa fa-trash"></i>';
|
||||
deleteBtn.addEventListener('click', handleDeleteRule);
|
||||
|
||||
// 创建删除图标
|
||||
const deleteIcon = document.createElement('i');
|
||||
deleteIcon.className = 'fa fa-trash';
|
||||
deleteIcon.style.pointerEvents = 'none'; // 防止图标拦截点击事件
|
||||
|
||||
deleteBtn.appendChild(deleteIcon);
|
||||
|
||||
// 使用普通函数,确保this指向按钮元素
|
||||
deleteBtn.onclick = function(e) {
|
||||
e.stopPropagation(); // 阻止事件冒泡
|
||||
handleDeleteRule(e);
|
||||
};
|
||||
|
||||
tdAction.appendChild(deleteBtn);
|
||||
tr.appendChild(tdRule);
|
||||
@@ -201,9 +202,36 @@ function updateRulesTable(rules) {
|
||||
|
||||
// 处理删除规则
|
||||
async function handleDeleteRule(e) {
|
||||
const rule = e.target.closest('.delete-rule-btn').dataset.rule;
|
||||
showLoading('删除规则中...');
|
||||
console.log('Delete button clicked');
|
||||
let deleteBtn;
|
||||
|
||||
// 尝试从事件目标获取按钮元素
|
||||
deleteBtn = e.target.closest('.delete-rule-btn');
|
||||
console.log('Delete button from event target:', deleteBtn);
|
||||
|
||||
// 尝试从this获取按钮元素(备用方案)
|
||||
if (!deleteBtn && this && typeof this.classList === 'object' && this.classList) {
|
||||
if (this.classList.contains('delete-rule-btn')) {
|
||||
deleteBtn = this;
|
||||
console.log('Delete button from this:', deleteBtn);
|
||||
}
|
||||
}
|
||||
|
||||
if (!deleteBtn) {
|
||||
console.error('Delete button not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const rule = deleteBtn.dataset.rule;
|
||||
console.log('Rule to delete:', rule);
|
||||
|
||||
if (!rule) {
|
||||
console.error('Rule not found in data-rule attribute');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('Sending DELETE request to /api/shield');
|
||||
const response = await fetch('/api/shield', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
@@ -212,24 +240,32 @@ async function handleDeleteRule(e) {
|
||||
body: JSON.stringify({ rule })
|
||||
});
|
||||
|
||||
console.log('Response status:', response.status);
|
||||
console.log('Response ok:', response.ok);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to delete rule');
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Failed to delete rule: ${response.status} ${errorText}`);
|
||||
}
|
||||
|
||||
const responseData = await response.json();
|
||||
console.log('Response data:', responseData);
|
||||
|
||||
showSuccessMessage('规则删除成功');
|
||||
console.log('Current rules type:', currentRulesType);
|
||||
// 根据当前显示的规则类型重新加载对应的规则列表
|
||||
if (currentRulesType === 'local') {
|
||||
console.log('Reloading local rules');
|
||||
loadLocalRules();
|
||||
} else {
|
||||
console.log('Reloading remote rules');
|
||||
loadRemoteRules();
|
||||
}
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error deleting rule:', error);
|
||||
showErrorMessage('删除规则失败');
|
||||
hideLoading();
|
||||
showErrorMessage('删除规则失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +277,6 @@ async function handleAddRule() {
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading('添加规则中...');
|
||||
try {
|
||||
const response = await fetch('/api/shield', {
|
||||
method: 'POST',
|
||||
@@ -262,17 +297,14 @@ async function handleAddRule() {
|
||||
loadLocalRules();
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error adding rule:', error);
|
||||
showErrorMessage('添加规则失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载远程黑名单
|
||||
async function loadRemoteBlacklists() {
|
||||
showLoading('加载远程黑名单...');
|
||||
try {
|
||||
const response = await fetch('/api/shield/blacklists');
|
||||
|
||||
@@ -285,11 +317,9 @@ async function loadRemoteBlacklists() {
|
||||
// 确保blacklists是数组
|
||||
const blacklistArray = Array.isArray(blacklists) ? blacklists : [];
|
||||
updateBlacklistsTable(blacklistArray);
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('加载远程黑名单失败:', error);
|
||||
showErrorMessage('加载远程黑名单失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +456,6 @@ async function handleUpdateBlacklist(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading('更新黑名单中...');
|
||||
try {
|
||||
const response = await fetch(`/api/shield/blacklists/${encodeURIComponent(url)}/update`, {
|
||||
method: 'POST',
|
||||
@@ -448,8 +477,6 @@ async function handleUpdateBlacklist(e) {
|
||||
} catch (error) {
|
||||
console.error('更新黑名单失败:', error);
|
||||
showToast('更新黑名单失败: ' + error.message, 'error');
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,7 +494,6 @@ async function handleDeleteBlacklist(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading('删除黑名单中...');
|
||||
try {
|
||||
const response = await fetch(`/api/shield/blacklists/${encodeURIComponent(url)}`, {
|
||||
method: 'DELETE',
|
||||
@@ -489,8 +515,6 @@ async function handleDeleteBlacklist(e) {
|
||||
} catch (error) {
|
||||
console.error('删除黑名单失败:', error);
|
||||
showToast('删除黑名单失败: ' + error.message, 'error');
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,7 +545,6 @@ async function handleAddBlacklist(event) {
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading('添加黑名单中...');
|
||||
try {
|
||||
const response = await fetch('/api/shield/blacklists', {
|
||||
method: 'POST',
|
||||
@@ -553,11 +576,9 @@ async function handleAddBlacklist(event) {
|
||||
loadRemoteBlacklists();
|
||||
// 重新加载统计信息
|
||||
loadShieldStats();
|
||||
hideLoading();
|
||||
} catch (error) {
|
||||
console.error('Error adding blacklist:', error);
|
||||
showErrorMessage(error.message || '添加黑名单失败');
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,18 +590,26 @@ let currentRulesType = 'local';
|
||||
// 设置事件监听器
|
||||
function setupShieldEventListeners() {
|
||||
// 本地规则管理事件
|
||||
document.getElementById('save-rule-btn').addEventListener('click', handleAddRule);
|
||||
|
||||
// 远程黑名单管理事件
|
||||
document.getElementById('save-blacklist-btn').addEventListener('click', handleAddBlacklist);
|
||||
|
||||
// 添加切换查看本地规则和远程规则的事件监听
|
||||
if (document.getElementById('view-local-rules-btn')) {
|
||||
document.getElementById('view-local-rules-btn').addEventListener('click', loadLocalRules);
|
||||
const saveRuleBtn = document.getElementById('save-rule-btn');
|
||||
if (saveRuleBtn) {
|
||||
saveRuleBtn.addEventListener('click', handleAddRule);
|
||||
}
|
||||
|
||||
if (document.getElementById('view-remote-rules-btn')) {
|
||||
document.getElementById('view-remote-rules-btn').addEventListener('click', loadRemoteRules);
|
||||
// 远程黑名单管理事件
|
||||
const saveBlacklistBtn = document.getElementById('save-blacklist-btn');
|
||||
if (saveBlacklistBtn) {
|
||||
saveBlacklistBtn.addEventListener('click', handleAddBlacklist);
|
||||
}
|
||||
|
||||
// 添加切换查看本地规则和远程规则的事件监听
|
||||
const viewLocalRulesBtn = document.getElementById('view-local-rules-btn');
|
||||
if (viewLocalRulesBtn) {
|
||||
viewLocalRulesBtn.addEventListener('click', loadLocalRules);
|
||||
}
|
||||
|
||||
const viewRemoteRulesBtn = document.getElementById('view-remote-rules-btn');
|
||||
if (viewRemoteRulesBtn) {
|
||||
viewRemoteRulesBtn.addEventListener('click', loadRemoteRules);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,31 +623,7 @@ 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') {
|
||||
|
||||
Reference in New Issue
Block a user