Compare commits
2 Commits
dnssever1.
...
Qimeng-DNS
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
356310ae75 | ||
|
|
fe77539da1 |
15
CHANGELOG.md
15
CHANGELOG.md
@@ -2,6 +2,21 @@
|
|||||||
|
|
||||||
所有对本项目的显著更改都将记录在此文件中。
|
所有对本项目的显著更改都将记录在此文件中。
|
||||||
|
|
||||||
|
## [1.2.0] - 2025-12-24
|
||||||
|
|
||||||
|
### 添加
|
||||||
|
- 在查询日志详情的域名左侧添加DNSSEC状态锁图标和跟踪器状态图标
|
||||||
|
- 实现跟踪器状态显示(匹配tracker/trackers.json数据库)
|
||||||
|
- 添加跟踪器详情浮窗(鼠标悬停在眼睛图标上时显示跟踪器名称、类别、URL、来源等信息)
|
||||||
|
- 实现日志页面页码跳转功能(输入框+"前往"按钮)
|
||||||
|
- 实现日志页面显示数量选择功能(下拉框)
|
||||||
|
|
||||||
|
### 修改
|
||||||
|
- 异步加载跟踪器数据库并缓存,优化性能
|
||||||
|
- 将日志渲染逻辑改为支持异步操作的for...of循环
|
||||||
|
- 修复跟踪器浮窗CSS样式语法错误
|
||||||
|
- 在后端添加/tracker目录静态文件服务路由
|
||||||
|
|
||||||
## [1.1.4] - 2025-12-21
|
## [1.1.4] - 2025-12-21
|
||||||
|
|
||||||
### 修复
|
### 修复
|
||||||
|
|||||||
@@ -35,6 +35,9 @@
|
|||||||
],
|
],
|
||||||
"microsoft.com": [
|
"microsoft.com": [
|
||||||
"4.2.2.1:53"
|
"4.2.2.1:53"
|
||||||
|
],
|
||||||
|
"steam": [
|
||||||
|
"4.2.2.1:53"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"noDNSSECDomains": [
|
"noDNSSECDomains": [
|
||||||
|
|||||||
@@ -154,6 +154,17 @@ func (s *Server) Start() error {
|
|||||||
http.ServeFile(w, r, "./static/login.html")
|
http.ServeFile(w, r, "./static/login.html")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Tracker目录静态文件服务
|
||||||
|
trackerFileServer := http.FileServer(http.Dir("./tracker"))
|
||||||
|
mux.HandleFunc("/tracker/", s.loginRequired(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// 添加Cache-Control头,禁用浏览器缓存
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
w.Header().Set("Pragma", "no-cache")
|
||||||
|
w.Header().Set("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
|
||||||
|
// 使用StripPrefix处理路径
|
||||||
|
http.StripPrefix("/tracker", trackerFileServer).ServeHTTP(w, r)
|
||||||
|
}))
|
||||||
|
|
||||||
// 其他静态文件需要登录
|
// 其他静态文件需要登录
|
||||||
mux.HandleFunc("/", s.loginRequired(func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/", s.loginRequired(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// 添加Cache-Control头,禁用浏览器缓存
|
// 添加Cache-Control头,禁用浏览器缓存
|
||||||
|
|||||||
8670
server.log
8670
server.log
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
|||||||
132708
|
|
||||||
@@ -1064,3 +1064,78 @@ tr:hover {
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 跟踪器状态图标容器 */
|
||||||
|
.tracker-icon-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 跟踪器浮窗样式 */
|
||||||
|
.tracker-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
left: 100%;
|
||||||
|
margin-left: 10px;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
padding: 12px;
|
||||||
|
min-width: 250px;
|
||||||
|
z-index: 50;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
/* 添加箭头 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 浮窗箭头 */
|
||||||
|
.tracker-tooltip::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
left: -10px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 10px solid transparent;
|
||||||
|
border-bottom: 10px solid transparent;
|
||||||
|
border-right: 10px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracker-tooltip::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
left: -11px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 10px solid transparent;
|
||||||
|
border-bottom: 10px solid transparent;
|
||||||
|
border-right: 10px solid #e2e8f0;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 浮窗标题 */
|
||||||
|
.tracker-tooltip .font-semibold {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2d3748;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 浮窗内容项 */
|
||||||
|
.tracker-tooltip > div {
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 浮窗链接样式 */
|
||||||
|
.tracker-tooltip a {
|
||||||
|
color: #3182ce;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tracker-tooltip a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
|
|
||||||
<!-- 底部信息 -->
|
<!-- 底部信息 -->
|
||||||
<div class="p-4 border-t border-gray-200 text-center text-gray-500 text-sm">
|
<div class="p-4 border-t border-gray-200 text-center text-gray-500 text-sm">
|
||||||
<p>DNS服务器 v1.0.0</p>
|
<p>DNS服务器 v1.2.0</p>
|
||||||
<p class="mt-1" id="uptime">正常运行中</p>
|
<p class="mt-1" id="uptime">正常运行中</p>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
@@ -995,13 +995,32 @@
|
|||||||
<div class="text-sm text-gray-500">
|
<div class="text-sm text-gray-500">
|
||||||
显示 <span id="logs-current-page">1</span> / <span id="logs-total-pages">1</span> 页
|
显示 <span id="logs-current-page">1</span> / <span id="logs-total-pages">1</span> 页
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-2">
|
<div class="flex items-center space-x-4">
|
||||||
<button id="logs-prev-page" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:border-transparent" disabled>
|
<div class="flex items-center space-x-2">
|
||||||
<i class="fa fa-chevron-left"></i>
|
<span class="text-sm text-gray-500">每页显示:</span>
|
||||||
</button>
|
<select id="logs-per-page" class="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
|
||||||
<button id="logs-next-page" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:border-transparent" disabled>
|
<option value="10">10条</option>
|
||||||
<i class="fa fa-chevron-right"></i>
|
<option value="20">20条</option>
|
||||||
</button>
|
<option value="30" selected>30条</option>
|
||||||
|
<option value="50">50条</option>
|
||||||
|
<option value="100">100条</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<span class="text-sm text-gray-500">页码:</span>
|
||||||
|
<input type="number" id="logs-page-input" min="1" max="1" value="1" class="w-16 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-center">
|
||||||
|
<button id="logs-go-page" class="px-4 py-2 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors">
|
||||||
|
前往
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<button id="logs-prev-page" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:border-transparent" disabled>
|
||||||
|
<i class="fa fa-chevron-left"></i>
|
||||||
|
</button>
|
||||||
|
<button id="logs-next-page" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:border-transparent" disabled>
|
||||||
|
<i class="fa fa-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,10 +14,83 @@ let currentSortDirection = 'desc'; // 默认降序
|
|||||||
let ipGeolocationCache = {};
|
let ipGeolocationCache = {};
|
||||||
const GEOLOCATION_CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 缓存有效期24小时
|
const GEOLOCATION_CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 缓存有效期24小时
|
||||||
|
|
||||||
|
// 跟踪器数据库缓存
|
||||||
|
let trackersDatabase = null;
|
||||||
|
let trackersLoaded = false;
|
||||||
|
let trackersLoading = false;
|
||||||
|
|
||||||
// WebSocket连接和重连计时器
|
// WebSocket连接和重连计时器
|
||||||
let logsWsConnection = null;
|
let logsWsConnection = null;
|
||||||
let logsWsReconnectTimer = null;
|
let logsWsReconnectTimer = null;
|
||||||
|
|
||||||
|
// 加载跟踪器数据库
|
||||||
|
async function loadTrackersDatabase() {
|
||||||
|
if (trackersLoaded) return trackersDatabase;
|
||||||
|
if (trackersLoading) {
|
||||||
|
// 等待正在进行的加载完成
|
||||||
|
while (trackersLoading) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
}
|
||||||
|
return trackersDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
trackersLoading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/tracker/trackers.json');
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error('加载跟踪器数据库失败:', response.statusText);
|
||||||
|
trackersDatabase = { trackers: {} };
|
||||||
|
return trackersDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
trackersDatabase = await response.json();
|
||||||
|
trackersLoaded = true;
|
||||||
|
return trackersDatabase;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载跟踪器数据库失败:', error);
|
||||||
|
trackersDatabase = { trackers: {} };
|
||||||
|
return trackersDatabase;
|
||||||
|
} finally {
|
||||||
|
trackersLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查域名是否在跟踪器数据库中,并返回跟踪器信息
|
||||||
|
async function isDomainInTrackerDatabase(domain) {
|
||||||
|
if (!trackersDatabase || !trackersLoaded) {
|
||||||
|
await loadTrackersDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!trackersDatabase || !trackersDatabase.trackers) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查域名是否直接作为跟踪器键存在
|
||||||
|
if (trackersDatabase.trackers.hasOwnProperty(domain)) {
|
||||||
|
return trackersDatabase.trackers[domain];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查域名是否在跟踪器URL中
|
||||||
|
for (const trackerKey in trackersDatabase.trackers) {
|
||||||
|
if (trackersDatabase.trackers.hasOwnProperty(trackerKey)) {
|
||||||
|
const tracker = trackersDatabase.trackers[trackerKey];
|
||||||
|
if (tracker && tracker.url) {
|
||||||
|
try {
|
||||||
|
const trackerUrl = new URL(tracker.url);
|
||||||
|
if (trackerUrl.hostname === domain || trackerUrl.hostname.includes(domain)) {
|
||||||
|
return tracker;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 忽略无效URL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化查询日志页面
|
// 初始化查询日志页面
|
||||||
function initLogsPage() {
|
function initLogsPage() {
|
||||||
console.log('初始化查询日志页面');
|
console.log('初始化查询日志页面');
|
||||||
@@ -122,6 +195,34 @@ function bindLogsEvents() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 页码跳转
|
||||||
|
const pageInput = document.getElementById('logs-page-input');
|
||||||
|
const goBtn = document.getElementById('logs-go-page');
|
||||||
|
|
||||||
|
if (pageInput) {
|
||||||
|
// 页码输入框回车事件
|
||||||
|
pageInput.addEventListener('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
const page = parseInt(pageInput.value);
|
||||||
|
if (page >= 1 && page <= totalPages) {
|
||||||
|
currentPage = page;
|
||||||
|
loadLogs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (goBtn) {
|
||||||
|
// 前往按钮点击事件
|
||||||
|
goBtn.addEventListener('click', () => {
|
||||||
|
const page = parseInt(pageInput.value);
|
||||||
|
if (page >= 1 && page <= totalPages) {
|
||||||
|
currentPage = page;
|
||||||
|
loadLogs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 时间范围切换
|
// 时间范围切换
|
||||||
const timeRangeBtns = document.querySelectorAll('.time-range-btn');
|
const timeRangeBtns = document.querySelectorAll('.time-range-btn');
|
||||||
timeRangeBtns.forEach(btn => {
|
timeRangeBtns.forEach(btn => {
|
||||||
@@ -304,7 +405,7 @@ function loadLogs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新日志表格
|
// 更新日志表格
|
||||||
function updateLogsTable(logs) {
|
async function updateLogsTable(logs) {
|
||||||
const tableBody = document.getElementById('logs-table-body');
|
const tableBody = document.getElementById('logs-table-body');
|
||||||
if (!tableBody) return;
|
if (!tableBody) return;
|
||||||
|
|
||||||
@@ -325,7 +426,7 @@ function updateLogsTable(logs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 填充表格
|
// 填充表格
|
||||||
logs.forEach(log => {
|
for (const log of logs) {
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
row.className = 'border-b border-gray-100 hover:bg-gray-50 transition-colors';
|
row.className = 'border-b border-gray-100 hover:bg-gray-50 transition-colors';
|
||||||
|
|
||||||
@@ -386,37 +487,76 @@ function updateLogsTable(logs) {
|
|||||||
statusClass = '';
|
statusClass = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建行内容 - 两行显示,时间列显示时间和日期,请求列显示域名和类型状态
|
// 检查域名是否在跟踪器数据库中
|
||||||
// 添加缓存状态显示
|
const trackerInfo = await isDomainInTrackerDatabase(log.Domain);
|
||||||
const cacheStatusClass = log.FromCache ? 'text-primary' : 'text-gray-500';
|
const isTracker = trackerInfo !== null;
|
||||||
const cacheStatusText = log.FromCache ? '缓存' : '非缓存';
|
|
||||||
|
|
||||||
// 检查域名是否被拦截
|
// 构建行内容 - 两行显示,时间列显示时间和日期,请求列显示域名和类型状态
|
||||||
const isBlocked = log.Result === 'blocked';
|
// 添加缓存状态显示
|
||||||
|
const cacheStatusClass = log.FromCache ? 'text-primary' : 'text-gray-500';
|
||||||
|
const cacheStatusText = log.FromCache ? '缓存' : '非缓存';
|
||||||
|
|
||||||
row.innerHTML = `
|
// 检查域名是否被拦截
|
||||||
<td class="py-3 px-4">
|
const isBlocked = log.Result === 'blocked';
|
||||||
<div class="text-sm font-medium">${formattedTime}</div>
|
|
||||||
<div class="text-xs text-gray-500 mt-1">${formattedDate}</div>
|
// 构建跟踪器浮窗内容
|
||||||
</td>
|
const trackerTooltip = isTracker ? `
|
||||||
<td class="py-3 px-4 text-sm">
|
<div class="tracker-tooltip absolute z-50 bg-white shadow-lg rounded-md border p-3 min-w-64 text-sm">
|
||||||
<div class="font-medium">${log.ClientIP}</div>
|
<div class="font-semibold mb-1">已知跟踪器</div>
|
||||||
<div class="text-xs text-gray-500 mt-1">${log.Location || '未知 未知'}</div>
|
<div class="mb-1">名称: ${trackerInfo.name}</div>
|
||||||
</td>
|
<div class="mb-1">类别: ${trackersDatabase.categories[trackerInfo.categoryId] || '未知'}</div>
|
||||||
<td class="py-3 px-4 text-sm">
|
${trackerInfo.url ? `<div class="mb-1">URL: <a href="${trackerInfo.url}" target="_blank" class="text-blue-500 hover:underline">${trackerInfo.url}</a></div>` : ''}
|
||||||
<div class="font-medium">${log.Domain}</div>
|
${trackerInfo.source ? `<div class="mb-1">源: ${trackerInfo.source}</div>` : ''}
|
||||||
<div class="text-xs text-gray-500 mt-1">类型: ${log.QueryType}, <span class="${statusClass}">${statusText}</span>, <span class="${cacheStatusClass}">${log.FromCache ? '缓存' : '实时'}</span>${log.DNSSEC ? ', <span class="text-green-500"><i class="fa fa-lock"></i> DNSSEC</span>' : ''}${log.EDNS ? ', <span class="text-blue-500"><i class="fa fa-exchange"></i> EDNS</span>' : ''}</div>
|
</div>
|
||||||
<div class="text-xs text-gray-500 mt-1">DNS 服务器: ${log.DNSServer || '无'}, DNSSEC专用: ${log.DNSSECServer || '无'}</div>
|
` : '';
|
||||||
</td>
|
|
||||||
<td class="py-3 px-4 text-sm">${log.ResponseTime}ms</td>
|
row.innerHTML = `
|
||||||
<td class="py-3 px-4 text-sm text-gray-500">${log.BlockRule || '-'}</td>
|
<td class="py-3 px-4">
|
||||||
<td class="py-3 px-4 text-sm text-center">
|
<div class="text-sm font-medium">${formattedTime}</div>
|
||||||
${isBlocked ?
|
<div class="text-xs text-gray-500 mt-1">${formattedDate}</div>
|
||||||
`<button class="unblock-btn px-3 py-1 bg-green-500 text-white rounded-md hover:bg-green-600 transition-colors text-xs" data-domain="${log.Domain}">放行</button>` :
|
</td>
|
||||||
`<button class="block-btn px-3 py-1 bg-red-500 text-white rounded-md hover:bg-red-600 transition-colors text-xs" data-domain="${log.Domain}">拦截</button>`
|
<td class="py-3 px-4 text-sm">
|
||||||
|
<div class="font-medium">${log.ClientIP}</div>
|
||||||
|
<div class="text-xs text-gray-500 mt-1">${log.Location || '未知 未知'}</div>
|
||||||
|
</td>
|
||||||
|
<td class="py-3 px-4 text-sm">
|
||||||
|
<div class="font-medium flex items-center relative">
|
||||||
|
${log.DNSSEC ? '<i class="fa fa-lock text-green-500 mr-1" title="DNSSEC已启用"></i>' : ''}
|
||||||
|
<div class="tracker-icon-container relative">
|
||||||
|
${isTracker ? '<i class="fa fa-eye text-red-500 mr-1"></i>' : '<i class="fa fa-eye-slash text-gray-300 mr-1"></i>'}
|
||||||
|
${trackerTooltip}
|
||||||
|
</div>
|
||||||
|
${log.Domain}
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-gray-500 mt-1">类型: ${log.QueryType}, <span class="${statusClass}">${statusText}</span>, <span class="${cacheStatusClass}">${log.FromCache ? '缓存' : '实时'}</span>${log.DNSSEC ? ', <span class="text-green-500"><i class="fa fa-lock"></i> DNSSEC</span>' : ''}${log.EDNS ? ', <span class="text-blue-500"><i class="fa fa-exchange"></i> EDNS</span>' : ''}</div>
|
||||||
|
<div class="text-xs text-gray-500 mt-1">DNS 服务器: ${log.DNSServer || '无'}, DNSSEC专用: ${log.DNSSECServer || '无'}</div>
|
||||||
|
</td>
|
||||||
|
<td class="py-3 px-4 text-sm">${log.ResponseTime}ms</td>
|
||||||
|
<td class="py-3 px-4 text-sm text-gray-500">${log.BlockRule || '-'}</td>
|
||||||
|
<td class="py-3 px-4 text-sm text-center">
|
||||||
|
${isBlocked ?
|
||||||
|
`<button class="unblock-btn px-3 py-1 bg-green-500 text-white rounded-md hover:bg-green-600 transition-colors text-xs" data-domain="${log.Domain}">放行</button>` :
|
||||||
|
`<button class="block-btn px-3 py-1 bg-red-500 text-white rounded-md hover:bg-red-600 transition-colors text-xs" data-domain="${log.Domain}">拦截</button>`
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 添加跟踪器图标悬停事件
|
||||||
|
if (isTracker) {
|
||||||
|
const iconContainer = row.querySelector('.tracker-icon-container');
|
||||||
|
const tooltip = iconContainer.querySelector('.tracker-tooltip');
|
||||||
|
if (iconContainer && tooltip) {
|
||||||
|
tooltip.style.display = 'none';
|
||||||
|
|
||||||
|
iconContainer.addEventListener('mouseenter', () => {
|
||||||
|
tooltip.style.display = 'block';
|
||||||
|
});
|
||||||
|
|
||||||
|
iconContainer.addEventListener('mouseleave', () => {
|
||||||
|
tooltip.style.display = 'none';
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</td>
|
}
|
||||||
`;
|
|
||||||
|
|
||||||
// 绑定按钮事件
|
// 绑定按钮事件
|
||||||
const blockBtn = row.querySelector('.block-btn');
|
const blockBtn = row.querySelector('.block-btn');
|
||||||
@@ -438,7 +578,7 @@ function updateLogsTable(logs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tableBody.appendChild(row);
|
tableBody.appendChild(row);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新分页信息
|
// 更新分页信息
|
||||||
@@ -447,6 +587,13 @@ function updateLogsPagination() {
|
|||||||
document.getElementById('logs-current-page').textContent = currentPage;
|
document.getElementById('logs-current-page').textContent = currentPage;
|
||||||
document.getElementById('logs-total-pages').textContent = totalPages;
|
document.getElementById('logs-total-pages').textContent = totalPages;
|
||||||
|
|
||||||
|
// 更新页码输入框
|
||||||
|
const pageInput = document.getElementById('logs-page-input');
|
||||||
|
if (pageInput) {
|
||||||
|
pageInput.max = totalPages;
|
||||||
|
pageInput.value = currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
// 更新按钮状态
|
// 更新按钮状态
|
||||||
const prevBtn = document.getElementById('logs-prev-page');
|
const prevBtn = document.getElementById('logs-prev-page');
|
||||||
const nextBtn = document.getElementById('logs-next-page');
|
const nextBtn = document.getElementById('logs-next-page');
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
{
|
|
||||||
"dns": {
|
|
||||||
"port": 5353,
|
|
||||||
"upstreamDNS": [
|
|
||||||
"223.5.5.5:53",
|
|
||||||
"223.6.6.6:53",
|
|
||||||
"117.50.10.10:53",
|
|
||||||
"10.35.10.200:53"
|
|
||||||
],
|
|
||||||
"dnssecUpstreamDNS": [
|
|
||||||
"117.50.10.10:53",
|
|
||||||
"101.226.4.6:53",
|
|
||||||
"218.30.118.6:53",
|
|
||||||
"208.67.220.220:53",
|
|
||||||
"208.67.222.222:53"
|
|
||||||
],
|
|
||||||
"timeout": 5000,
|
|
||||||
"statsFile": "data/stats.json",
|
|
||||||
"saveInterval": 300,
|
|
||||||
"cacheTTL": 30,
|
|
||||||
"enableDNSSEC": true,
|
|
||||||
"queryMode": "parallel",
|
|
||||||
"domainSpecificDNS": {
|
|
||||||
"amazehome.xyz": ["10.35.10.200:53"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http": {
|
|
||||||
"port": 8081,
|
|
||||||
"host": "0.0.0.0",
|
|
||||||
"enableAPI": true,
|
|
||||||
"username": "admin",
|
|
||||||
"password": "admin"
|
|
||||||
},
|
|
||||||
"shield": {
|
|
||||||
"localRulesFile": "data/rules.txt",
|
|
||||||
"blacklists": [],
|
|
||||||
"updateInterval": 3600,
|
|
||||||
"hostsFile": "data/hosts.txt",
|
|
||||||
"blockMethod": "NXDOMAIN",
|
|
||||||
"customBlockIP": "",
|
|
||||||
"statsFile": "./data/shield_stats.json",
|
|
||||||
"statsSaveInterval": 60,
|
|
||||||
"remoteRulesCacheDir": "data/remote_rules"
|
|
||||||
},
|
|
||||||
"log": {
|
|
||||||
"file": "logs/dns-server-5353.log",
|
|
||||||
"level": "debug",
|
|
||||||
"maxSize": 100,
|
|
||||||
"maxBackups": 10,
|
|
||||||
"maxAge": 30
|
|
||||||
}
|
|
||||||
}
|
|
||||||
25333
tracker/trackers.json
Normal file
25333
tracker/trackers.json
Normal file
File diff suppressed because it is too large
Load Diff
25333
tracker/trackers.json.bak
Normal file
25333
tracker/trackers.json.bak
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user