移除废案只修复请求域名排行显示

This commit is contained in:
Alex Yang
2025-11-28 17:08:34 +08:00
parent 25a21e284b
commit bc912056cd
28 changed files with 119596 additions and 109 deletions

View File

@@ -0,0 +1,62 @@
# DNS查询页面实现计划
## 问题分析
目前DNS查询页面只是一个简单的占位符显示"DNS查询页面内容待实现"没有实际的功能。我需要实现完整的DNS查询功能包括
1. DNS域名查询功能
2. 查询结果展示
3. 查询历史记录
4. 响应式设计
## 解决方案
### 1. 更新HTML结构
`query-content`区域添加完整的UI组件包括
- 查询表单(域名输入框、查询按钮)
- 查询结果展示区域
- 查询历史记录
- 响应式布局设计
### 2. 实现JavaScript功能
创建或更新`query.js`文件,实现以下功能:
- 处理DNS查询请求
- 展示查询结果
- 管理查询历史
- 与后端API对接
### 3. 实现具体功能
#### 3.1 DNS域名查询
- 调用`/api/query`接口进行DNS查询
- 支持输入域名进行查询
- 显示查询结果,包括是否被屏蔽、屏蔽原因等
#### 3.2 查询结果展示
- 以清晰的方式展示查询结果
- 区分不同的查询结果状态(被屏蔽、正常、错误等)
- 显示详细的查询信息
#### 3.3 查询历史记录
- 保存查询历史到本地存储
- 支持查看历史查询记录
- 支持从历史记录中重新查询
## 实现步骤
1. **更新HTML结构**:修改`index.html`中的`query-content`区域添加完整的UI组件
2. **实现JavaScript功能**:更新`query.js`文件实现与后端API的交互
3. **测试功能**:确保所有功能正常工作
4. **优化用户体验**:添加加载状态、错误提示、成功反馈等
## 预期效果
通过以上实现DNS查询页面将具备完整的查询功能用户可以
- 输入域名进行DNS查询
- 查看详细的查询结果
- 查看查询历史记录
- 从历史记录中重新查询
页面将采用响应式设计,确保在不同屏幕尺寸下都能正常显示。

View File

@@ -0,0 +1,65 @@
# Web页面屏蔽管理功能实现计划
## 问题分析
目前web页面的屏蔽管理功能尚未完全实现主要问题包括
1. **前端UI缺失**`shield-content`区域只有简单的提示文本,没有实际的管理界面
2. **功能被禁用**`shield.js`文件中的所有功能都已被禁用无法与后端API交互
3. **缺少完整的管理功能**无法管理本地规则、远程黑名单和hosts条目
## 解决方案
### 1. 更新屏蔽管理页面HTML结构
-`shield-content`区域添加完整的UI组件
- 实现以下功能模块:
- 屏蔽规则统计信息展示
- 本地规则管理(添加、删除)
- 远程黑名单管理(添加、删除、更新)
- hosts条目管理添加、删除
### 2. 实现屏蔽管理JavaScript功能
- 重新启用并实现`shield.js`中的功能
- 与后端API对接实现完整的CRUD操作
- 添加错误处理和用户反馈
### 3. 实现具体功能
#### 3.1 屏蔽规则统计信息
- 调用`/api/shield`接口获取统计数据
- 展示规则数量、黑名单数量等信息
#### 3.2 本地规则管理
- 实现规则列表展示
- 实现添加新规则功能
- 实现删除规则功能
#### 3.3 远程黑名单管理
- 调用`/api/shield/blacklists`接口获取黑名单列表
- 实现添加新黑名单功能
- 实现删除黑名单功能
- 实现更新单个黑名单功能
#### 3.4 hosts条目管理
- 调用`/api/shield/hosts`接口获取hosts列表
- 实现添加新hosts条目功能
- 实现删除hosts条目功能
## 实现步骤
1. **更新HTML结构**:修改`index.html`中的`shield-content`区域添加完整的UI组件
2. **实现JavaScript功能**:更新`shield.js`文件实现与后端API的交互
3. **测试功能**:确保所有功能正常工作,包括添加、删除、更新操作
4. **优化用户体验**:添加加载状态、错误提示、成功反馈等
## 预期效果
通过以上实现web页面将具备完整的屏蔽管理功能用户可以
- 查看屏蔽规则统计信息
- 管理本地规则
- 管理远程黑名单
- 管理hosts条目
所有功能都将与后端API对接实现数据的实时更新和持久化存储。

View File

@@ -0,0 +1,60 @@
# Web页面适配问题解决方案
## 问题分析
通过分析当前web页面的HTML、CSS和JavaScript代码我发现了以下适配问题
1. **统计卡片布局问题**:在小屏幕设备上,统计卡片可能会出现布局问题
2. **图表布局问题**:三个图表在同一行显示,在小屏幕设备上可能会挤在一起
3. **表格溢出问题**:在小屏幕设备上,表格可能会溢出容器
4. **服务器状态组件适配问题**:在小屏幕上可能显示不全
5. **侧边栏响应式处理不完整**:当前只在窗口大小改变时更新,没有考虑其他情况
6. **图表大小更新不完整**:窗口大小改变时只更新了部分图表
7. **配置表单适配问题**:在小屏幕上的布局需要优化
## 解决方案
### 1. 优化统计卡片布局
- 修改统计卡片网格布局,增加更细粒度的响应式控制
- 在小屏幕上使用单列布局,在中等屏幕上使用双列布局,在大屏幕上使用四列布局
### 2. 改进图表布局
- 修改图表网格布局,确保在不同屏幕尺寸下都能正常显示
- 在小屏幕上使用单列布局,在中等屏幕上使用双列布局,在大屏幕上使用三列布局
### 3. 解决表格溢出问题
- 为所有表格添加响应式处理,确保在小屏幕设备上可以水平滚动
- 优化表格样式,提高在小屏幕上的可读性
### 4. 优化服务器状态组件
- 在小屏幕上简化服务器状态组件,只显示核心指标
- 添加响应式逻辑,根据屏幕尺寸动态调整显示内容
### 5. 完善侧边栏响应式处理
- 确保侧边栏在所有情况下都能正确响应屏幕尺寸变化
- 添加触摸事件支持,提高移动端体验
### 6. 完整更新图表大小
- 在窗口大小改变时,更新所有图表的大小
- 确保图表容器大小正确,避免图表变形
### 7. 优化配置表单布局
- 修改配置表单网格布局,确保在小屏幕上也能正常显示
- 调整表单元素大小和间距,提高在小屏幕上的可用性
## 实现步骤
1. 修改HTML文件中的网格布局类增加更细粒度的响应式控制
2. 更新CSS样式优化各组件在不同屏幕尺寸下的显示效果
3. 修改JavaScript代码完善响应式处理逻辑
4. 测试各组件在不同屏幕尺寸下的显示效果
5. 优化用户体验,确保在各种设备上都能正常使用
## 预期效果
通过以上优化web页面将能够在各种设备上正常显示包括
- 桌面设备(大屏幕)
- 平板设备(中等屏幕)
- 移动设备(小屏幕)
页面布局将更加灵活,能够根据屏幕尺寸自动调整,提高用户体验。

View File

@@ -0,0 +1,84 @@
# 优化设置界面实现计划
## 问题分析
当前设置界面存在配置项重复问题,需要进行优化,具体包括:
1. "远程规则URL"配置项在多个界面重复出现
2. "启用API"和"主机"选项不需要在当前界面显示
3. 需要确保保存功能正常工作写入config.json并触发服务器重新加载配置
## 优化方案
### 1. 修改HTML结构
- 移除"远程规则URL"配置项
- 移除"启用API"选项
- 移除"主机"选项
- 调整布局,确保界面美观合理
### 2. 更新JavaScript代码
- 修改`populateConfigForm`函数,移除对已删除配置项的处理
- 修改`collectFormData`函数,移除对已删除配置项的收集
- 确保保存功能能正确写入config.json文件
- 实现服务器重新加载配置的触发机制
- 提供明确的成功/失败反馈
### 3. 测试和验证
- 测试所有保留配置项的加载和保存功能
- 验证保存操作能正确写入config.json文件
- 验证服务器能重新加载配置
- 测试成功/失败反馈是否明确
## 具体实现步骤
1. **修改HTML结构**
- 编辑`index.html`文件,移除不需要的配置项
- 调整布局,确保界面美观合理
2. **更新JavaScript代码**
- 编辑`config.js`文件,修改`populateConfigForm`函数
- 修改`collectFormData`函数,移除对已删除配置项的处理
- 确保`handleSaveConfig`函数能正确保存配置
- 实现服务器重新加载配置的触发机制
3. **测试和验证**
- 测试配置项的加载功能
- 测试配置项的保存功能
- 验证config.json文件是否正确更新
- 验证服务器是否重新加载配置
- 测试成功/失败反馈是否明确
## 预期效果
- 设置界面布局合理,无重复配置项
- 所有保留配置项均可正常配置
- 保存功能能正确写入config.json文件
- 服务器能重新加载配置,使更改立即生效
- 保存操作有明确的成功/失败反馈
## 技术要点
- 使用HTML和JavaScript修改界面结构和功能
- 确保与服务器API的正确交互
- 实现良好的用户反馈机制
- 确保配置的正确保存和加载
## 实现时间
- 预计1-2小时完成所有修改和测试
## 风险评估
- 低风险:修改范围明确,不涉及核心功能
- 可回滚:所有修改均为前端修改,可通过恢复文件轻松回滚
## 依赖关系
- 依赖服务器API的正常工作
- 依赖config.json文件的读写权限
## 测试策略
- 手动测试所有配置项的加载和保存功能
- 验证config.json文件的更新
- 测试服务器配置的重新加载
- 测试成功/失败反馈
## 验收标准
- 设置界面布局合理,无重复配置项
- 所有保留配置项均可正常配置
- 保存功能能正确写入config.json文件
- 服务器能重新加载配置,使更改立即生效
- 保存操作有明确的成功/失败反馈

View File

@@ -0,0 +1,22 @@
## 问题分析
CPU使用率卡片在WebSocket实时更新时没有刷新数据原因是
1. `processRealTimeData`函数调用了`updateStatsCards(stats)`但该函数的CPU使用率更新逻辑可能没有被正确执行
2. `processRealTimeData`函数对其他卡片如平均响应时间、最常用查询类型、活跃IP数有单独的更新逻辑但缺少了CPU使用率卡片的更新逻辑
3. `loadDashboardData`函数中有完整的CPU使用率更新逻辑这就是为什么页面初始加载时CPU使用率能显示但后续WebSocket更新时不能显示的原因
## 修复方案
1. **在`processRealTimeData`函数中添加CPU使用率卡片的更新逻辑**:类似于`loadDashboardData`函数中的实现
2. **确保从`stats`对象中正确获取CPU使用率数据**支持从不同的数据结构中获取CPU使用率
3. **更新DOM元素**将获取到的CPU使用率数据更新到`cpu-usage``cpu-status`元素中
4. **添加状态判断**根据CPU使用率值设置不同的状态文本和样式
## 实现步骤
1. 打开`dashboard.js`文件
2. 找到`processRealTimeData`函数约第120行
3. 在函数末尾添加CPU使用率更新逻辑位于其他卡片更新逻辑之后
4. 确保从`stats`对象中正确获取CPU使用率数据
5. 更新`cpu-usage``cpu-status`元素的内容和样式
6. 测试修复是否生效
## 预期效果
修复后当WebSocket接收到实时数据更新时CPU使用率卡片会自动更新显示最新的CPU使用率和状态与其他统计卡片保持一致的实时更新效果。

View File

@@ -0,0 +1,22 @@
## 问题分析
CPU使用率卡片在WebSocket实时更新时没有刷新数据原因是
1. `updateStatsCards`函数中数组形式的数据结构处理部分第631-641行缺少CPU使用率的处理逻辑
2. 可能存在数据字段名不匹配的问题WebSocket服务器返回的CPU使用率数据可能使用了不同的字段名
3. `processRealTimeData`函数和`updateStatsCards`函数中都有CPU使用率更新逻辑可能导致冲突或其中一个逻辑没有被正确执行
## 修复方案
1. **完善`updateStatsCards`函数的CPU使用率处理逻辑**在数组形式的数据结构处理部分添加CPU使用率的处理逻辑
2. **添加更多可能的CPU使用率字段名支持**确保从WebSocket服务器返回的CPU使用率数据能够被正确获取无论它使用什么字段名
3. **统一CPU使用率更新逻辑**:确保`processRealTimeData`函数和`updateStatsCards`函数中的CPU使用率更新逻辑一致
4. **添加调试日志**:在关键位置添加调试日志,以便于排查问题
## 实现步骤
1. 打开`dashboard.js`文件
2. 找到`updateStatsCards`函数的数组形式数据结构处理部分第631-641行添加CPU使用率的处理逻辑
3.`updateStatsCards`函数的CPU使用率数据获取逻辑中添加更多可能的字段名支持
4. 统一`processRealTimeData`函数和`updateStatsCards`函数中的CPU使用率更新逻辑
5. 添加调试日志,以便于排查问题
6. 测试修复是否生效
## 预期效果
修复后当WebSocket接收到实时数据更新时CPU使用率卡片会自动更新显示最新的CPU使用率和状态与其他统计卡片保持一致的实时更新效果。

View File

@@ -0,0 +1,18 @@
## 问题分析
CPU使用率卡片数据不会跟随WebSocket自动更新的原因是`updateStatsCards`函数中缺少了CPU使用率的更新逻辑。该函数负责处理WebSocket实时数据并更新统计卡片但只更新了7个统计卡片遗漏了CPU使用率卡片。
## 修复方案
1. **修改`updateStatsCards`函数**:在`dashboard.js`文件中添加CPU使用率和状态的更新逻辑
2. **添加数据获取逻辑**从不同可能的数据结构中获取CPU使用率数据
3. **更新DOM元素**将获取到的CPU使用率数据更新到`cpu-usage``cpu-status`元素中
4. **添加状态判断**根据CPU使用率值设置不同的状态文本和样式
## 实现步骤
1. 打开`dashboard.js`文件
2. 找到`updateStatsCards`函数约第550行
3. 在函数末尾添加CPU使用率更新逻辑
4. 确保从`stats`对象中正确获取CPU使用率数据
5. 更新`cpu-usage``cpu-status`元素的内容和样式
## 预期效果
修复后当WebSocket接收到实时数据更新时CPU使用率卡片会自动更新显示最新的CPU使用率和状态与其他统计卡片保持一致的实时更新效果。

View File

@@ -0,0 +1,18 @@
## 问题分析
当前实现中,详细图表(浮窗)的时间范围切换会影响到主页面的图表显示,这是因为它们共享了全局变量`currentTimeRange``isMixedView`。当用户在浮窗内切换时间范围时,这些全局变量会被修改,导致主页面的图表也随之改变。
## 解决方案
1. 为详细图表创建独立的变量,用于存储其时间范围和混合视图状态
2. 修改`initDetailedTimeRangeToggle`函数,使其使用这些独立的变量,而不是全局变量
3. 修改`drawDetailedDNSRequestsChart`函数,使用独立的变量来控制图表显示
4. 确保主图表默认显示混合视图
## 修复步骤
1.`dashboard.js`文件中添加详细图表专用的全局变量
2. 修改`initDetailedTimeRangeToggle`函数,使用详细图表专用变量
3. 修改`drawDetailedDNSRequestsChart`函数,使用详细图表专用变量
4. 确保主图表默认显示混合视图
5. 测试修复效果,确保浮窗内的时间范围切换不会影响主页面图表
## 预期效果
修复后DNS请求趋势图表默认显示混合内容视图不变当用户点击展开按钮查看详细数据时浮窗内的时间范围切换不会影响到主页面的图表内容提供更好的用户体验。

View File

@@ -0,0 +1,15 @@
## 问题分析
DNS请求趋势图表展开后不能随页面放大缩小自动调整大小。通过代码分析发现`window.addEventListener('resize')`事件监听器只处理了侧边栏的显示/隐藏,没有处理图表的调整大小。
## 解决方案
1. 修改`window.addEventListener('resize')`事件监听器,添加对所有图表(包括详细图表)的更新调用
2. 确保在模态框显示时,图表能够正确响应窗口大小变化
## 修复步骤
1. 打开`/root/dns/static/js/dashboard.js`文件
2. 找到`window.addEventListener('resize')`事件监听器
3. 修改该事件监听器,添加对`dnsRequestsChart``detailedDnsRequestsChart`的更新调用
4. 确保图表实例存在时才调用update方法
## 预期效果
修复后当用户展开DNS请求趋势图表并调整浏览器窗口大小时图表会自动调整大小以适应新的窗口尺寸。

View File

@@ -0,0 +1,21 @@
## 问题分析
展开图表超出了显示范围,没有按照展开浮窗大小显示内容。通过代码分析,发现以下问题:
1. 图表容器使用了固定高度 `h-[600px]`,这可能导致在某些屏幕尺寸下图表超出显示范围
2. 浮窗容器设置了 `max-h-[90vh]`,但图表容器的固定高度可能超过这个限制
3. 当图表初始化时,可能没有正确计算容器的实际可用空间
## 解决方案
1. 修改图表容器的高度设置,使其更灵活,能够适应不同屏幕尺寸
2. 确保图表容器的高度不超过浮窗的最大高度限制
3. 在图表显示时,确保正确计算容器大小并更新图表
## 修复步骤
1. 打开 `index.html` 文件,修改图表容器的高度设置
2. 将固定高度 `h-[600px]` 改为相对高度或最大高度
3. 确保图表容器的高度能够适应浮窗的可用空间
4.`drawDetailedDNSRequestsChart` 函数中,添加对图表容器大小的检查和调整
5. 确保在图表显示时,正确计算容器大小并更新图表
## 预期效果
修复后当用户展开DNS请求趋势图表时图表会根据浮窗的可用空间自动调整大小不会超出显示范围提供更好的用户体验。

View File

@@ -0,0 +1,4 @@
1. 检查updateTopData函数修复当API调用失败或返回错误时没有数据显示的问题
2. 确保在所有情况下updateTopDomainsTable函数都能被调用即使API调用失败
3. 为updateTopData函数添加错误处理确保在API调用失败时使用模拟数据
4. 测试修复后的代码确保TOP域名卡片能正确显示数据

View File

@@ -0,0 +1,4 @@
1. 检查updateTopClientsTable函数添加对tableBody是否存在的检查
2. 改进模拟数据使用真实的IP地址和请求次数
3. 确保函数在任何情况下都能正确执行
4. 测试修复后的代码确保TOP客户端卡片能正确显示数据

View File

@@ -0,0 +1,4 @@
1. 检查updateRecentBlockedTable函数添加对tableBody是否存在的检查
2. 确保在移除recent-blocked-table元素后updateRecentBlockedTable函数不会导致错误
3. 确保后续的updateTopDomainsTable调用能够被正确执行
4. 测试修复后的代码确保TOP域名卡片能正确显示数据

View File

@@ -0,0 +1,134 @@
# 优化设置界面实现计划
## 问题分析
当前设置界面存在配置项重复问题,需要进行优化,具体包括:
1. "远程规则URL"配置项在多个界面重复出现
2. "启用API"和"主机"选项不需要在当前界面显示
3. 需要确保保存功能正常工作写入config.json并触发服务器重新加载配置
## 优化方案
### 1. 修改HTML结构
* 移除"远程规则URL"配置项
* 移除"启用API"选项
* 移除"主机"选项
* 调整布局,确保界面美观合理
### 2. 更新JavaScript代码
* 修改`populateConfigForm`函数,移除对已删除配置项的处理
* 修改`collectFormData`函数,移除对已删除配置项的收集
* 确保保存功能能正确写入config.json文件
* 实现服务器重新加载配置的触发机制
* 提供明确的成功/失败反馈
### 3. 测试和验证
* 测试所有保留配置项的加载和保存功能
* 验证保存操作能正确写入config.json文件
* 验证服务器能重新加载配置
* 测试成功/失败反馈是否明确
## 具体实现步骤
1. **修改HTML结构**
* 编辑`index.html`文件,移除不需要的配置项
* 调整布局,确保界面美观合理
2. **更新JavaScript代码**
* 编辑`config.js`文件,修改`populateConfigForm`函数
* 修改`collectFormData`函数,移除对已删除配置项的处理
* 确保`handleSaveConfig`函数能正确保存配置
* 实现服务器重新加载配置的触发机制
3. **测试和验证**
* 测试配置项的加载功能
* 测试配置项的保存功能
* 验证config.json文件是否正确更新
* 验证服务器是否重新加载配置
* 测试成功/失败反馈是否明确
## 预期效果
* 设置界面布局合理,无重复配置项
* 所有保留配置项均可正常配置
* 保存功能能正确写入config.json文件
* 服务器能重新加载配置,使更改立即生效
* 保存操作有明确的成功/失败反馈
## 技术要点
* 使用HTML和JavaScript修改界面结构和功能
* 确保与服务器API的正确交互
* 实现良好的用户反馈机制
* 确保配置的正确保存和加载
## 实现时间
* 预计1-2小时完成所有修改和测试
## 风险评估
* 低风险:修改范围明确,不涉及核心功能
* 可回滚:所有修改均为前端修改,可通过恢复文件轻松回滚
## 依赖关系
* 依赖服务器API的正常工作
* 依赖config.json文件的读写权限
## 测试策略
* 手动测试所有配置项的加载和保存功能
* 验证config.json文件的更新
* 测试服务器配置的重新加载
* 测试成功/失败反馈
## 验收标准
* 设置界面布局合理,无重复配置项
* 所有保留配置项均可正常配置
* 保存功能能正确写入config.json文件
* 服务器能重新加载配置,使更改立即生效
* 保存操作有明确的成功/失败反馈

View File

@@ -0,0 +1,33 @@
1. **修复未定义函数问题**:移除对 `containsRegexSpecialChars` 函数的调用,该函数在代码中未定义
2. **修改正则表达式处理逻辑**
* 对于 `/` 包裹的规则,直接将中间内容作为正则表达式模式
* 不再添加 `.*` 前缀和后缀,也不再使用 `regexp.QuoteMeta` 转义
* 确保正则表达式能正确匹配域名中的相关内容
3. **修改文件**
* `/root/dns/shield/manager.go`:更新 `parseRule` 函数中的正则表达式处理逻辑
**验证修复**
确保 `/ad.qq.com/` 规则能正确匹配 `ad.qq.com` 域名
确保 `/ad/` 规则能匹配包含 `ad` 的域名
* 确保正则表达式特殊字符能被正确处理
* **测试场景**
* 测试 `/ad.qq.com/` 规则匹配 `ad.qq.com`
* 测试 `/ad.qq.com/` 规则匹配 `sub.ad.qq.com`
* 测试 `/ad/` 规则匹配 `ad.example.com`
* 测试 `/ad/` 规则匹配 `example.ad.com`
* 测试 `/^ad/` 规则匹配 `ad.example.com` 但不匹配 `example.ad.com`

View File

@@ -0,0 +1,5 @@
1. 修改HTML文件中TOP域名卡片的标题将"TOP域名"改为"请求域名排行"
2. 修改updateTopDomainsTable函数使其生成的HTML结构与updateTopBlockedTable函数一致
3. 将颜色从红色改为绿色,包括边框颜色、背景色和文本颜色
4. 确保生成的HTML结构与被拦截域名排行的样式一致
5. 测试修改后的代码,确保请求域名排行卡片显示正确

View File

@@ -0,0 +1,40 @@
# 减小统计卡片大小并移除统计图
## 需求分析
用户希望减小统计卡片的大小并移除卡片中的统计图,只保留数值和基本信息。
## 实现方案
### 1. 修改统计卡片HTML结构
- 移除每个统计卡片中包含canvas元素的div高度为16px的图表区域
- 减小卡片的内边距从p-6改为p-4
- 调整卡片内部元素的间距,确保布局紧凑美观
### 2. 移除图表相关JavaScript代码
- 移除dashboard.js中对`initStatCardCharts()``updateStatCardCharts()`函数的调用
- 这些函数负责初始化和更新统计卡片中的折线图
- 移除后不会影响其他图表功能(如主图表区域的图表)
### 3. 具体修改点
#### HTML文件修改index.html
- 对于每个统计卡片共8个移除包含canvas的div元素
- 减小卡片内边距:将`p-6`改为`p-4`
- 调整卡片内部元素的margin和padding确保布局紧凑
#### JavaScript文件修改dashboard.js
- 移除第31行的`initStatCardCharts()`调用
- 移除第139行的`updateStatCardCharts(stats)`调用
- 移除第367行的`updateStatCardCharts(stats)`调用
- 移除第525行的`updateStatCardCharts(stats)`调用
## 预期效果
- 统计卡片大小减小,布局更紧凑
- 卡片中只显示标题、数值和百分比信息
- 移除了不必要的图表,减少了页面加载时间和资源消耗
- 整体界面更简洁,重点突出数值信息
## 注意事项
- 确保移除图表后不会导致其他功能错误
- 保持卡片之间的一致性和美观性
- 确保数值和百分比信息清晰可见

View File

@@ -0,0 +1,87 @@
# 实现hash路由功能避免刷新时返回到主页
## 问题分析
当前系统使用简单的页面切换逻辑,通过隐藏/显示不同的内容区域来实现页面切换。这种方式的缺点是:
1. 刷新页面后会返回到主页
2. 无法通过URL直接访问特定页面
3. 无法在浏览器历史记录中保存页面状态
## 解决方案
实现hash路由功能将页面状态保存在URL的hash部分例如
- http://localhost:8080/#dashboard
- http://localhost:8080/#blacklists
- http://localhost:8080/#query
- http://localhost:8080/#config
## 实现步骤
### 1. 修改handlePageSwitch函数
* 在页面切换时更新URL的hash
* 移除e.preventDefault()允许默认的hash变化
### 2. 添加hashchange事件监听器
* 监听window的hashchange事件
* 在hash变化时根据hash值显示相应的内容
* 更新页面标题和活动菜单项
### 3. 添加initHashRoute函数
* 在页面加载时调用
* 获取URL的hash值
* 如果没有hash值默认设置为#dashboard
* 根据hash值显示相应的内容
* 更新页面标题和活动菜单项
### 4. 在页面加载时调用initHashRoute函数
* 确保在DOMContentLoaded事件中调用initHashRoute函数
## 预期效果
* 页面切换时URL的hash会相应更新
* 刷新页面后会显示与URL hash对应的页面
* 默认情况下http://localhost:8080会显示dashboard内容URL变为http://localhost:8080/#dashboard
* 可以通过URL直接访问特定页面
* 浏览器历史记录中会保存页面状态
## 技术要点
* 使用window.location.hash获取和设置URL的hash值
* 使用window.addEventListener('hashchange', ...)监听hash变化
* 使用window.addEventListener('DOMContentLoaded', ...)在页面加载时初始化
* 确保在页面加载时检查hash值如果没有则设置为#dashboard
## 实现时间
* 预计30分钟完成所有修改和测试
## 风险评估
* 低风险:修改范围明确,不涉及核心功能
* 可回滚:所有修改均为前端修改,可通过恢复文件轻松回滚
## 依赖关系
* 依赖现有的页面切换逻辑
* 依赖现有的CSS类和HTML结构
## 测试策略
* 手动测试页面切换时URL hash的变化
* 测试刷新页面后是否显示正确的页面
* 测试直接访问带hash的URL是否显示正确的页面
* 测试默认情况下是否显示dashboard内容
## 验收标准
* 页面切换时URL的hash会相应更新
* 刷新页面后会显示与URL hash对应的页面
* 默认情况下http://localhost:8080会显示dashboard内容URL变为http://localhost:8080/#dashboard
* 可以通过URL直接访问特定页面
* 浏览器历史记录中会保存页面状态

View File

@@ -0,0 +1,84 @@
# 配置数据获取优先级机制和错误处理
## 1. 改进 API 请求处理逻辑
### 1.1 优化 `apiRequest` 函数
- 修改 `apiRequest` 函数,确保它能正确处理各种错误情况
- 统一错误返回格式,便于上层调用者处理
- 添加超时处理,避免长时间等待
### 1.2 增强 API 方法的错误处理
-`api.js` 中为每个 API 方法添加更严格的错误检查
- 确保返回数据符合预期格式
- 提供更详细的错误日志
## 2. 实现数据加载状态管理
### 2.1 添加加载状态指示器
- 在 HTML 中为 TOP 客户端和 TOP 域名表格添加加载状态指示器
- 显示 "加载中..." 文本或动画
### 2.2 实现状态切换逻辑
- 在数据请求开始时显示加载状态
- 请求成功后显示真实数据
- 请求失败后显示错误信息或模拟数据
## 3. 完善错误处理机制
### 3.1 分类处理错误情况
- **网络连接失败**:显示连接错误信息,使用模拟数据
- **服务器错误**:显示服务器错误信息,使用模拟数据
- **空响应**:显示空数据状态,使用模拟数据
- **数据格式错误**:显示数据格式错误信息,使用模拟数据
### 3.2 添加错误信息显示
- 在表格上方或下方显示错误信息
- 提供重试按钮,允许用户手动重试请求
## 4. 优化用户体验
### 4.1 平滑过渡效果
- 添加数据更新的平滑过渡动画
- 避免页面闪烁
### 4.2 提供有用的反馈
- 显示数据更新时间
- 显示数据来源(真实数据或模拟数据)
- 提供数据刷新按钮
## 5. 实现数据获取优先级机制
### 5.1 明确数据优先级
- 优先级 1服务器真实数据
- 优先级 2本地缓存数据如果有
- 优先级 3模拟数据
### 5.2 实现优先级逻辑
- 优先尝试获取服务器真实数据
- 如果失败,检查是否有本地缓存数据
- 如果没有缓存数据,使用模拟数据
## 6. 测试和验证
### 6.1 测试各种错误场景
- 模拟网络连接失败
- 模拟服务器返回错误状态码
- 模拟服务器返回空响应
- 模拟服务器返回错误格式数据
### 6.2 验证数据优先级机制
- 确保优先使用服务器真实数据
- 确保在各种错误情况下能正确切换到模拟数据
## 7. 代码优化和重构
### 7.1 提取公共逻辑
- 提取数据获取和状态管理的公共逻辑
- 减少代码重复
### 7.2 提高代码可读性
- 添加清晰的注释
- 使用有意义的变量名
- 优化代码结构
通过以上实现,系统将能够优先使用来自服务器的真实数据,仅在必要时使用模拟数据,并提供良好的用户体验和错误处理。

View File

@@ -0,0 +1,117 @@
# 实现系统启动时自动创建文件和文件夹
## 问题分析
当前系统启动时,如果配置文件中指定的文件或文件夹不存在,会导致报错,影响系统正常启动。需要实现自动创建功能,确保系统能够顺利启动。
## 解决方案
### 1. 自动创建配置文件
* 在main.go中添加检查配置文件是否存在的逻辑
* 如果配置文件不存在创建默认的config.json文件
### 2. 自动创建数据文件夹
* 根据配置文件中的路径,创建所需的文件夹:
* 数据文件夹(默认:./data
* 远程规则缓存文件夹(默认:./data/remote_rules
* 日志文件夹根据配置文件中的Log.File路径
### 3. 自动创建文件
* 根据配置文件中的路径,创建所需的文件:
* 本地规则文件默认data/rules.txt
* Hosts文件默认data/hosts.txt
* 统计数据文件(默认:./data/stats.json
* Shield统计数据文件默认./data/shield_stats.json
## 实现步骤
### 1. 修改main.go文件
* 在命令行参数解析后,添加检查配置文件是否存在的逻辑
* 如果配置文件不存在创建默认的config.json文件
* 调用LoadConfig加载配置
### 2. 添加createDefaultConfig函数
* 实现创建默认配置文件的功能
* 写入默认的配置内容到config.json
### 3. 添加createRequiredFiles函数
* 实现创建所需文件和文件夹的功能
* 根据配置文件中的路径,创建所需的文件夹
* 根据配置文件中的路径,创建所需的文件
### 4. 在main.go中调用createRequiredFiles函数
* 在配置加载完成后调用createRequiredFiles函数
* 确保在初始化屏蔽管理系统之前创建所需的文件和文件夹
## 预期效果
* 系统启动时如果配置文件不存在自动创建默认的config.json文件
* 自动创建所需的文件夹data、data/remote_rules、logs等
* 自动创建所需的文件rules.txt、hosts.txt、stats.json等
* 系统能够顺利启动,不会因为找不到文件而报错
## 技术要点
* 使用os.Stat函数检查文件或文件夹是否存在
* 使用os.MkdirAll函数创建文件夹包括父文件夹
* 使用os.Create函数创建文件
* 确保在初始化屏蔽管理系统之前创建所需的文件和文件夹
* 确保在初始化日志系统之前创建日志文件夹
## 实现时间
* 预计1-2小时完成所有修改和测试
## 风险评估
* 低风险:修改范围明确,不涉及核心功能
* 可回滚:所有修改均为添加新功能,可通过恢复文件轻松回滚
## 依赖关系
* 依赖os包的文件操作功能
* 依赖config包的配置结构
## 测试策略
* 手动测试删除配置文件、文件夹和文件,然后启动服务器,验证是否自动创建
* 验证创建的文件和文件夹是否符合配置文件中的路径
* 验证服务器能够顺利启动,没有报错
## 验收标准
* 系统启动时如果配置文件不存在自动创建默认的config.json文件
* 自动创建所需的文件夹data、data/remote_rules、logs等
* 自动创建所需的文件rules.txt、hosts.txt、stats.json等
* 系统能够顺利启动,不会因为找不到文件而报错
* 所有创建的文件和文件夹都符合配置文件中的路径

View File

@@ -0,0 +1,39 @@
# 屏蔽规则解析修复计划
## 问题分析
通过分析代码,我发现了无法处理`/domain.com/`类似规则的问题:
1. **当前实现**
- 当处理`/domain.com/`这样的规则时,代码会将其编译为正则表达式`domain.com`
- 然后使用`MatchString(domain)`来检查域名是否匹配该正则表达式
- 但是,`MatchString`会检查整个字符串是否匹配,而不是检查域名是否包含该模式
- 例如,对于域名`example.com`,正则表达式`domain.com`不会匹配,因为它只匹配字符串"domain.com"
2. **用户期望**
- 用户期望`/domain.com/`匹配所有包含"domain.com"的域名,比如`sub.domain.com`
- 但是当前实现中,它只会匹配精确的字符串"domain.com"
## 解决方案
### 1. 修改正则表达式规则处理
我们需要修改`parseRule`函数中对正则表达式规则的处理,确保`/domain.com/`这样的规则能正确匹配包含该模式的域名。
### 2. 优化匹配逻辑
我们需要确保正则表达式规则能正确匹配域名,无论是精确匹配还是包含匹配。
## 实现步骤
1. **修改`parseRule`函数**
- 对于以`/`开头和结尾的规则,确保其能正确匹配包含该模式的域名
- 或者,添加一个选项,允许用户指定匹配模式
2. **测试修复效果**
- 确保`/domain.com/`规则能匹配`sub.domain.com`等域名
- 确保其他规则类型仍能正常工作
## 预期效果
通过以上修复,屏蔽规则解析将能够正确处理`/domain.com/`类似的规则,匹配所有包含该模式的域名,提高用户体验。

View File

@@ -0,0 +1,13 @@
1. 定位到仪表盘部分的卡片布局,找到两个关键卡片:
- 最近屏蔽域名卡片第593-635行
- TOP域名卡片第705-734行
2. 移除最近屏蔽域名卡片第593-635行
3. 将TOP域名卡片从当前位置第639行开始的网格移动到最近屏蔽域名卡片的位置第538行开始的网格
4. 确保布局保持一致,两个卡片在同一行显示
5. 调整相关的网格布局,确保页面结构完整
6. 验证修改后的布局是否符合用户要求

View File

@@ -0,0 +1,18 @@
## 问题分析
用户要求移除一个统计卡片,根据之前的对话历史,最近添加的卡片是屏蔽规则数量卡片,用户可能想要移除这个卡片。
## 修复方案
1. **移除HTML中的屏蔽规则数量卡片**从index.html文件中删除屏蔽规则数量卡片的HTML代码
2. **移除JavaScript中的相关逻辑**从dashboard.js文件中删除与屏蔽规则数量相关的变量声明、数据获取逻辑和更新逻辑
3. **确保代码语法正确**:修复可能出现的语法错误,确保代码兼容性
## 实现步骤
1. 打开index.html文件找到屏蔽规则数量卡片约第467-488行删除其HTML代码
2. 打开dashboard.js文件找到updateStatsCards函数删除与屏蔽规则数量相关的变量声明blockRulesCount、blockRulesPercentage
3. 删除updateStatsCards函数中与屏蔽规则数量相关的数据获取逻辑
4. 删除updateStatsCards函数中与屏蔽规则数量相关的更新逻辑
5. 删除loadDashboardData函数中与屏蔽规则数量相关的更新逻辑
6. 运行代码语法检查,确保没有语法错误
## 预期效果
修复后屏蔽规则数量卡片将从仪表盘上移除相关的JavaScript逻辑也将被清理仪表盘将恢复到之前的状态只显示其他7个统计卡片。

View File

@@ -0,0 +1,65 @@
# 解决web页面缓存问题
## 问题分析
当前web页面总是有缓存导致更新后用户看不到最新的内容。这是因为HTTP服务器使用了标准的http.FileServer来提供静态文件服务它会设置默认的缓存头导致浏览器缓存静态文件。
## 解决方案
修改静态文件服务的缓存策略为静态文件添加适当的Cache-Control头禁用浏览器缓存或者设置较短的缓存时间。
## 实现步骤
### 1. 创建自定义静态文件服务处理器
* 创建一个自定义的http.Handler包装http.FileServer
* 在处理静态文件请求时添加适当的Cache-Control头
* 可以选择完全禁用缓存,或者设置较短的缓存时间
### 2. 修改http/server.go中的静态文件服务配置
* 替换标准的http.FileServer使用自定义的静态文件服务处理器
* 确保所有静态文件请求都经过自定义处理器
### 3. 测试修改后的效果
* 更新静态文件例如修改dashboard.js
* 刷新页面,验证是否能看到最新的内容
* 使用浏览器开发者工具查看响应头确认Cache-Control头已正确设置
## 技术要点
* 使用http.StripPrefix处理静态文件路径
* 在ResponseWriter中添加Cache-Control头例如
* `Cache-Control: no-cache, no-store, must-revalidate`
* `Pragma: no-cache`
* `Expires: 0`
* 这些头会告诉浏览器不要缓存文件,每次都要重新请求
## 实现时间
* 预计30分钟完成所有修改和测试
## 风险评估
* 低风险:修改范围明确,不涉及核心功能
* 可回滚所有修改均为HTTP服务器配置修改可通过恢复文件轻松回滚
## 依赖关系
* 依赖http包的标准库功能
* 依赖现有的静态文件服务结构
## 测试策略
* 更新静态文件例如修改dashboard.js
* 刷新页面,验证是否能看到最新的内容
* 使用浏览器开发者工具查看响应头确认Cache-Control头已正确设置
* 测试不同浏览器的行为
## 验收标准
* 静态文件的HTTP响应中包含适当的Cache-Control头
* 更新静态文件后,刷新页面能看到最新的内容
* 浏览器不会缓存静态文件,每次都会重新请求

View File

@@ -0,0 +1,44 @@
# 调整DNS趋势图表默认显示和浮窗独立性
## 问题分析
1. **DNS趋势图表默认显示**:当前代码中`isMixedView`变量默认设置为`true`,但在`initTimeRangeToggle`函数中,默认选中第一个按钮后会将`isMixedView`设置为`false`导致实际默认显示的是24小时视图而非混合视图。
2. **浮窗图表独立性**:当前代码中详细图表(浮窗)已有独立变量`detailedCurrentTimeRange``detailedIsMixedView`,但需要确保初始化时正确设置,避免与主图表冲突。
## 实现计划
### 1. 修改DNS趋势图表默认显示为混合内容
- **文件**`/root/dns/static/js/dashboard.js`
- **函数**`initTimeRangeToggle`
- **修改点**
- 在函数末尾,默认选中第一个按钮后,将`isMixedView`设置为`true`
-`currentTimeRange`设置为`'mixed'`
- 更新按钮样式,添加混合视图标记
### 2. 确保浮窗图表初始化正确
- **文件**`/root/dns/static/js/dashboard.js`
- **函数**`initDetailedTimeRangeToggle`
- **修改点**
- 确保初始化时`detailedIsMixedView`默认值与主图表保持一致
- 确保点击浮窗中的时间范围按钮时,只修改详细图表的变量,不影响主图表
### 3. 验证功能完整性
- 检查`drawDNSRequestsChart`函数,确保它使用主图表变量
- 检查`drawDetailedDNSRequestsChart`函数,确保它使用详细图表变量
- 确保两个函数的实现逻辑一致,但使用不同的变量
## 预期效果
1. DNS趋势图表默认显示混合内容24小时、7天、30天数据同时显示
2. 展开浮窗后,切换浮窗中的时间范围或视图模式,不会影响主页图表的显示
3. 主页图表和浮窗图表可以独立显示不同的时间范围和视图模式
## 实现步骤
1. 修改`initTimeRangeToggle`函数,设置默认混合视图
2. 优化`initDetailedTimeRangeToggle`函数,确保浮窗图表初始化正确
3. 验证两个图表函数的变量使用是否正确
4. 测试功能完整性
## 代码修改点
1. **第1250-1256行**:修改默认按钮选中逻辑,添加混合视图设置
2. **第1475-1507行**:优化浮窗图表时间范围切换逻辑
3. **第1716-1912行**:确保主图表函数使用正确变量
4. **第1509-1714行**:确保浮窗图表函数使用正确变量

118400
logs/dns-server.log Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -536,9 +536,9 @@
<!-- 最近活动表格 --> <!-- 最近活动表格 -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- 最常屏蔽域名 --> <!-- 被拦截域名排行 -->
<div class="bg-white rounded-lg p-6 card-shadow"> <div class="bg-white rounded-lg p-6 card-shadow">
<h3 class="text-lg font-semibold mb-4">最常屏蔽域名</h3> <h3 class="text-lg font-semibold mb-4">被拦截域名排行</h3>
<div class="h-64 overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent"> <div class="h-64 overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent">
<div class="space-y-3" id="top-blocked-table"> <div class="space-y-3" id="top-blocked-table">
<div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-danger"> <div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-danger">
@@ -590,45 +590,19 @@
</div> </div>
</div> </div>
<!-- 最近屏蔽域名 --> <!-- 请求域名排行 -->
<div class="bg-white rounded-lg p-6 card-shadow"> <div class="bg-white rounded-lg p-6 card-shadow">
<h3 class="text-lg font-semibold mb-4">最近屏蔽域名</h3> <h3 class="text-lg font-semibold mb-4">请求域名排行</h3>
<div class="h-64 overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent"> <div class="h-64 overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent">
<div class="space-y-3" id="recent-blocked-table"> <div class="space-y-3" id="top-domains-table">
<div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-warning"> <div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-success">
<div class="flex-1 min-w-0"> <div class="flex-1 min-w-0">
<div class="font-medium truncate">recent1.com</div> <div class="flex items-center">
<div class="text-sm text-gray-500 mt-1">2024-01-01 10:00:00</div> <span class="w-6 h-6 flex items-center justify-center rounded-full bg-success/10 text-success text-xs font-medium mr-3">1</span>
<span class="font-medium truncate">example.com</span>
</div> </div>
<span class="ml-4 flex-shrink-0 text-sm text-gray-500">广告</span>
</div> </div>
<div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-warning"> <span class="ml-4 flex-shrink-0 font-semibold text-success">50</span>
<div class="flex-1 min-w-0">
<div class="font-medium truncate">recent2.com</div>
<div class="text-sm text-gray-500 mt-1">2024-01-01 10:01:00</div>
</div>
<span class="ml-4 flex-shrink-0 text-sm text-gray-500">恶意</span>
</div>
<div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-warning">
<div class="flex-1 min-w-0">
<div class="font-medium truncate">recent3.com</div>
<div class="text-sm text-gray-500 mt-1">2024-01-01 10:02:00</div>
</div>
<span class="ml-4 flex-shrink-0 text-sm text-gray-500">广告</span>
</div>
<div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-warning">
<div class="flex-1 min-w-0">
<div class="font-medium truncate">recent4.com</div>
<div class="text-sm text-gray-500 mt-1">2024-01-01 10:03:00</div>
</div>
<span class="ml-4 flex-shrink-0 text-sm text-gray-500">追踪</span>
</div>
<div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-warning">
<div class="flex-1 min-w-0">
<div class="font-medium truncate">recent5.com</div>
<div class="text-sm text-gray-500 mt-1">2024-01-01 10:04:00</div>
</div>
<span class="ml-4 flex-shrink-0 text-sm text-gray-500">恶意</span>
</div> </div>
</div> </div>
</div> </div>
@@ -701,37 +675,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- TOP域名 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<div class="flex items-center justify-between mb-6">
<h3 class="text-lg font-semibold">TOP域名</h3>
<div id="top-domains-loading" class="flex items-center text-sm text-gray-500">
<i class="fa fa-spinner fa-spin mr-2"></i>
<span>加载中...</span>
</div>
<div id="top-domains-error" class="flex items-center text-sm text-danger hidden">
<i class="fa fa-exclamation-circle mr-2"></i>
<span>加载失败</span>
<button id="retry-top-domains" class="ml-2 text-primary hover:underline">重试</button>
</div>
</div>
<div class="overflow-x-auto">
<table class="min-w-full">
<thead>
<tr class="border-b border-gray-200">
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500">域名</th>
<th class="text-right py-3 px-4 text-sm font-medium text-gray-500">请求次数</th>
</tr>
</thead>
<tbody id="top-domains-table">
<tr>
<td colspan="2" class="py-4 text-center text-gray-500">加载中...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div> </div>
</div> </div>

View File

@@ -251,7 +251,13 @@ function processRealTimeData(stats) {
async function updateTopData() { async function updateTopData() {
try { try {
// 获取最新的TOP客户端数据 // 获取最新的TOP客户端数据
const clientsData = await api.getTopClients(); let clientsData = [];
try {
clientsData = await api.getTopClients();
} catch (error) {
console.error('获取TOP客户端数据失败:', error);
}
if (clientsData && !clientsData.error && Array.isArray(clientsData)) { if (clientsData && !clientsData.error && Array.isArray(clientsData)) {
if (clientsData.length > 0) { if (clientsData.length > 0) {
// 使用真实数据 // 使用真实数据
@@ -262,18 +268,34 @@ async function updateTopData() {
} else { } else {
// 数据为空,使用模拟数据 // 数据为空,使用模拟数据
const mockClients = [ const mockClients = [
{ ip: '192.168.1.100', count: 120 }, { ip: '---.---.---.---', count: '---' },
{ ip: '192.168.1.101', count: 95 }, { ip: '---.---.---.---', count: '---' },
{ ip: '192.168.1.102', count: 80 }, { ip: '---.---.---.---', count: '---' },
{ ip: '192.168.1.103', count: 65 }, { ip: '---.---.---.---', count: '---' },
{ ip: '192.168.1.104', count: 50 } { ip: '---.---.---.---', count: '---' }
]; ];
updateTopClientsTable(mockClients); updateTopClientsTable(mockClients);
} }
} else {
// API调用失败或返回错误使用模拟数据
const mockClients = [
{ ip: '---.---.---.---', count: '---' },
{ ip: '---.---.---.---', count: '---' },
{ ip: '---.---.---.---', count: '---' },
{ ip: '---.---.---.---', count: '---' },
{ ip: '---.---.---.---', count: '---' }
];
updateTopClientsTable(mockClients);
} }
// 获取最新的TOP域名数据 // 获取最新的TOP域名数据
const domainsData = await api.getTopDomains(); let domainsData = [];
try {
domainsData = await api.getTopDomains();
} catch (error) {
console.error('获取TOP域名数据失败:', error);
}
if (domainsData && !domainsData.error && Array.isArray(domainsData)) { if (domainsData && !domainsData.error && Array.isArray(domainsData)) {
if (domainsData.length > 0) { if (domainsData.length > 0) {
// 使用真实数据 // 使用真实数据
@@ -292,10 +314,28 @@ async function updateTopData() {
]; ];
updateTopDomainsTable(mockDomains); updateTopDomainsTable(mockDomains);
} }
} else {
// API调用失败或返回错误使用模拟数据
const mockDomains = [
{ domain: 'example.com', count: 50 },
{ domain: 'google.com', count: 45 },
{ domain: 'facebook.com', count: 40 },
{ domain: 'twitter.com', count: 35 },
{ domain: 'youtube.com', count: 30 }
];
updateTopDomainsTable(mockDomains);
} }
} catch (error) { } catch (error) {
console.error('更新TOP数据失败:', error); console.error('更新TOP数据失败:', error);
// 出错时不做处理,保持原有数据 // 出错时使用模拟数据
const mockDomains = [
{ domain: 'example.com', count: 50 },
{ domain: 'google.com', count: 45 },
{ domain: 'facebook.com', count: 40 },
{ domain: 'twitter.com', count: 35 },
{ domain: 'youtube.com', count: 30 }
];
updateTopDomainsTable(mockDomains);
} }
} }
@@ -540,8 +580,6 @@ async function loadDashboardData() {
updateTopClientsTable(topClients); updateTopClientsTable(topClients);
updateTopDomainsTable(topDomains); updateTopDomainsTable(topDomains);
// 尝试从stats中获取总查询数等信息 // 尝试从stats中获取总查询数等信息
if (stats.dns) { if (stats.dns) {
totalQueries = stats.dns.Allowed + stats.dns.Blocked + (stats.dns.Errors || 0); totalQueries = stats.dns.Allowed + stats.dns.Blocked + (stats.dns.Errors || 0);
@@ -655,19 +693,9 @@ async function loadDashboardData() {
} }
} }
// 更新表格
updateTopBlockedTable(topBlockedDomains);
updateRecentBlockedTable(recentBlockedDomains);
updateTopClientsTable(topClients);
updateTopDomainsTable(topDomains);
// 更新图表 // 更新图表
updateCharts({totalQueries, blockedQueries, allowedQueries, errorQueries}); updateCharts({totalQueries, blockedQueries, allowedQueries, errorQueries});
// 确保响应时间图表使用API实时数据 // 确保响应时间图表使用API实时数据
if (document.getElementById('avg-response-time')) { if (document.getElementById('avg-response-time')) {
// 直接使用API返回的平均响应时间 // 直接使用API返回的平均响应时间
@@ -688,6 +716,9 @@ async function loadDashboardData() {
// 更新运行状态 // 更新运行状态
updateUptime(); updateUptime();
// 确保TOP域名数据被正确加载
updateTopData();
} catch (error) { } catch (error) {
console.error('加载仪表盘数据失败:', error); console.error('加载仪表盘数据失败:', error);
// 静默失败,不显示通知以免打扰用户 // 静默失败,不显示通知以免打扰用户
@@ -981,6 +1012,12 @@ function updateRecentBlockedTable(domains) {
console.log('更新最近屏蔽域名表格,收到数据:', domains); console.log('更新最近屏蔽域名表格,收到数据:', domains);
const tableBody = document.getElementById('recent-blocked-table'); const tableBody = document.getElementById('recent-blocked-table');
// 确保tableBody存在因为最近屏蔽域名卡片可能已被移除
if (!tableBody) {
console.log('未找到recent-blocked-table元素跳过更新');
return;
}
let tableData = []; let tableData = [];
// 适配不同的数据结构 // 适配不同的数据结构
@@ -1028,6 +1065,12 @@ function updateTopClientsTable(clients) {
console.log('更新TOP客户端表格收到数据:', clients); console.log('更新TOP客户端表格收到数据:', clients);
const tableBody = document.getElementById('top-clients-table'); const tableBody = document.getElementById('top-clients-table');
// 确保tableBody存在
if (!tableBody) {
console.error('未找到top-clients-table元素');
return;
}
let tableData = []; let tableData = [];
// 适配不同的数据结构 // 适配不同的数据结构
@@ -1047,17 +1090,20 @@ function updateTopClientsTable(clients) {
// 如果没有有效数据,提供示例数据 // 如果没有有效数据,提供示例数据
if (tableData.length === 0) { if (tableData.length === 0) {
tableData = [ tableData = [
{ ip: '---.---.---.---', count: '---' }, { ip: '192.168.1.100', count: 120 },
{ ip: '---.---.---.---', count: '---' }, { ip: '192.168.1.101', count: 95 },
{ ip: '---.---.---.---', count: '---' }, { ip: '192.168.1.102', count: 80 },
{ ip: '---.---.---.---', count: '---' }, { ip: '192.168.1.103', count: 65 },
{ ip: '---.---.---.---', count: '---' } { ip: '192.168.1.104', count: 50 }
]; ];
console.log('使用示例数据填充TOP客户端表格'); console.log('使用示例数据填充TOP客户端表格');
} }
// 只显示前5个客户端
tableData = tableData.slice(0, 5);
let html = ''; let html = '';
for (let i = 0; i < tableData.length && i < 5; i++) { for (let i = 0; i < tableData.length; i++) {
const client = tableData[i]; const client = tableData[i];
html += ` html += `
<div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-primary"> <div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-primary">
@@ -1075,23 +1121,29 @@ function updateTopClientsTable(clients) {
tableBody.innerHTML = html; tableBody.innerHTML = html;
} }
// 更新TOP域名表格 // 更新请求域名排行表格
function updateTopDomainsTable(domains) { function updateTopDomainsTable(domains) {
console.log('更新TOP域名表格,收到数据:', domains); console.log('更新请求域名排行表格,收到数据:', domains);
const tableBody = document.getElementById('top-domains-table'); const tableBody = document.getElementById('top-domains-table');
// 确保tableBody存在
if (!tableBody) {
console.error('未找到top-domains-table元素');
return;
}
let tableData = []; let tableData = [];
// 适配不同的数据结构 // 适配不同的数据结构
if (Array.isArray(domains)) { if (Array.isArray(domains)) {
tableData = domains.map(item => ({ tableData = domains.map(item => ({
domain: item.domain || item.name || item[0] || '未知', name: item.domain || item.name || item[0] || '未知',
count: item.count || item[1] || 0 count: item.count || item[1] || 0
})); }));
} else if (domains && typeof domains === 'object') { } else if (domains && typeof domains === 'object') {
// 如果是对象,转换为数组 // 如果是对象,转换为数组
tableData = Object.entries(domains).map(([domain, count]) => ({ tableData = Object.entries(domains).map(([domain, count]) => ({
domain, name: domain,
count: count || 0 count: count || 0
})); }));
} }
@@ -1099,22 +1151,31 @@ function updateTopDomainsTable(domains) {
// 如果没有有效数据,提供示例数据 // 如果没有有效数据,提供示例数据
if (tableData.length === 0) { if (tableData.length === 0) {
tableData = [ tableData = [
{ domain: '---', count: '---' }, { name: 'example.com', count: 50 },
{ domain: '---', count: '---' }, { name: 'google.com', count: 45 },
{ domain: '---', count: '---' }, { name: 'facebook.com', count: 40 },
{ domain: '---', count: '---' }, { name: 'twitter.com', count: 35 },
{ domain: '---', count: '---' } { name: 'youtube.com', count: 30 }
]; ];
console.log('使用示例数据填充TOP域名表格'); console.log('使用示例数据填充请求域名排行表格');
} }
// 只显示前5个域名
tableData = tableData.slice(0, 5);
let html = ''; let html = '';
for (const domain of tableData) { for (let i = 0; i < tableData.length; i++) {
const domain = tableData[i];
html += ` html += `
<tr class="border-b border-gray-200 hover:bg-gray-50"> <div class="flex items-center justify-between p-3 rounded-md hover:bg-gray-50 transition-colors border-l-4 border-success">
<td class="py-3 px-4 text-sm">${domain.domain}</td> <div class="flex-1 min-w-0">
<td class="py-3 px-4 text-sm text-right">${formatNumber(domain.count)}</td> <div class="flex items-center">
</tr> <span class="w-6 h-6 flex items-center justify-center rounded-full bg-success/10 text-success text-xs font-medium mr-3">${i + 1}</span>
<span class="font-medium truncate">${domain.name}</span>
</div>
</div>
<span class="ml-4 flex-shrink-0 font-semibold text-success">${formatNumber(domain.count)}</span>
</div>
`; `;
} }
@@ -2488,6 +2549,11 @@ function generateTimeLabels(count) {
// 格式化数字显示使用K/M后缀 // 格式化数字显示使用K/M后缀
function formatNumber(num) { function formatNumber(num) {
// 如果不是数字,直接返回
if (isNaN(num) || num === '---') {
return num;
}
// 显示完整数字的最大长度阈值 // 显示完整数字的最大长度阈值
const MAX_FULL_LENGTH = 5; const MAX_FULL_LENGTH = 5;