修复本地规则管理不工作的问题
This commit is contained in:
@@ -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. 页面会正常显示屏蔽管理设置内容
|
||||
@@ -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界面自动刷新,删除的规则从列表中消失
|
||||
- 本地规则文件被正确更新,删除的规则不再存在于文件中
|
||||
@@ -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:测试修复效果
|
||||
- 启动服务器
|
||||
- 访问本地规则管理页面
|
||||
- 添加一条本地规则
|
||||
- 删除该规则
|
||||
- 验证规则是否被正确删除,页面内容是否减少
|
||||
|
||||
## 预期效果
|
||||
|
||||
修复后,本地规则管理删除规则功能将正常工作:
|
||||
- 点击删除按钮后,规则会被正确发送到服务器
|
||||
- 服务器会正确处理带有修饰符的规则
|
||||
- 本地规则标记会被正确更新
|
||||
- 规则会被持久化保存
|
||||
- 页面内容会立即减少
|
||||
|
||||
这样就能确保本地规则管理删除规则功能正常工作,提供良好的用户体验。
|
||||
@@ -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
|
||||
-10607
File diff suppressed because it is too large
Load Diff
-82585
File diff suppressed because it is too large
Load Diff
-53291
File diff suppressed because it is too large
Load Diff
-1176
File diff suppressed because it is too large
Load Diff
-549303
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
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
-118716
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+10
-4
@@ -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
|
||||
|
||||
+1
-37
@@ -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) {
|
||||
|
||||
+1
-18
@@ -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) {
|
||||
|
||||
+79
-74
@@ -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