日志查询界面增加操作列,修复责增加删除规则不生效的问题,修复自定义规则优先级问题

This commit is contained in:
Alex Yang
2025-12-21 21:18:17 +08:00
parent b67a0fad8e
commit 1f8bd6b97f
14 changed files with 1467 additions and 169 deletions

View File

@@ -1138,12 +1138,12 @@
},
"/shield/localrules": {
"get": {
"summary": "获取本地规则",
"description": "获取Shield的本地规则列表。",
"summary": "获取自定义规则",
"description": "获取Shield的自定义规则列表。"
"tags": ["shield"],
"responses": {
"200": {
"description": "成功获取本地规则",
"description": "成功获取自定义规则",
"content": {
"application/json": {
"schema": {
@@ -1166,8 +1166,8 @@
}
},
"post": {
"summary": "添加本地规则",
"description": "添加新的本地规则。",
"summary": "添加自定义规则",
"description": "添加新的自定义规则。",
"tags": ["shield"],
"requestBody": {
"required": true,
@@ -1205,8 +1205,8 @@
}
},
"delete": {
"summary": "删除本地规则",
"description": "删除指定ID的本地规则。",
"summary": "删除自定义规则",
"description": "删除指定ID的自定义规则。",
"tags": ["shield"],
"parameters": [
{
@@ -1500,7 +1500,7 @@
"blocked": true,
"blockRule": "example.com",
"blockRuleType": "exact_domain",
"blocksource": "本地规则",
"blocksource": "自定义规则",
"excluded": false,
"excludeRule": "",
"excludeRuleType": "",

View File

@@ -630,9 +630,9 @@
</div>
</div>
<!-- 本地规则管理 -->
<!-- 自定义规则管理 -->
<div class="bg-white rounded-lg p-6 card-shadow">
<h3 class="text-lg font-semibold mb-6">本地规则管理</h3>
<h3 class="text-lg font-semibold mb-6">自定义规则管理</h3>
<!-- 添加规则表单 -->
<div id="add-rule-form" class="mb-6 bg-gray-50 p-4 rounded-lg">
@@ -976,11 +976,12 @@
</th>
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500">响应时间</th>
<th class="text-left py-3 px-4 text-sm font-medium text-gray-500">屏蔽规则</th>
<th class="text-center py-3 px-4 text-sm font-medium text-gray-500">操作</th>
</tr>
</thead>
<tbody id="logs-table-body">
<tr>
<td colspan="5" class="py-8 text-center text-gray-500 border-b border-gray-100">
<td colspan="6" class="py-8 text-center text-gray-500 border-b border-gray-100">
<i class="fa fa-file-text-o text-4xl mb-2 text-gray-300"></i>
<div>暂无查询日志</div>
</td>
@@ -1056,7 +1057,7 @@
<h4 class="text-md font-medium mb-4">屏蔽配置</h4>
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 gap-6">
<div>
<label for="shield-local-rules-file" class="block text-sm font-medium text-gray-700 mb-1">本地规则文件</label>
<label for="shield-local-rules-file" class="block text-sm font-medium text-gray-700 mb-1">自定义规则文件</label>
<input type="text" id="shield-local-rules-file" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" placeholder="./rules.txt">
</div>
<div>

View File

@@ -282,6 +282,9 @@ function loadLogs() {
// 更新日志表格
updateLogsTable(logs);
// 绑定操作按钮事件
bindActionButtonsEvents();
// 更新分页信息
updateLogsPagination();
@@ -388,6 +391,9 @@ function updateLogsTable(logs) {
const cacheStatusClass = log.FromCache ? 'text-primary' : 'text-gray-500';
const cacheStatusText = log.FromCache ? '缓存' : '非缓存';
// 检查域名是否被拦截
const isBlocked = log.Result === 'blocked';
row.innerHTML = `
<td class="py-3 px-4">
<div class="text-sm font-medium">${formattedTime}</div>
@@ -400,10 +406,36 @@ function updateLogsTable(logs) {
<td class="py-3 px-4 text-sm">
<div class="font-medium">${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>
`;
// 绑定按钮事件
const blockBtn = row.querySelector('.block-btn');
if (blockBtn) {
blockBtn.addEventListener('click', (e) => {
e.preventDefault();
const domain = e.currentTarget.dataset.domain;
blockDomain(domain);
});
}
const unblockBtn = row.querySelector('.unblock-btn');
if (unblockBtn) {
unblockBtn.addEventListener('click', (e) => {
e.preventDefault();
const domain = e.currentTarget.dataset.domain;
unblockDomain(domain);
});
}
tableBody.appendChild(row);
});
@@ -613,6 +645,146 @@ function updateLogsStatsFromWebSocket(stats) {
}
}
// 拦截域名
async function blockDomain(domain) {
try {
console.log(`开始拦截域名: ${domain}`);
// 创建拦截规则使用AdBlock Plus格式
const blockRule = `||${domain}^`;
console.log(`创建的拦截规则: ${blockRule}`);
// 调用API添加拦截规则
console.log(`调用API添加拦截规则路径: /shield, 方法: POST`);
const response = await apiRequest('/shield', 'POST', { rule: blockRule });
console.log(`API响应:`, response);
// 处理不同的响应格式
if (response && (response.success || response.status === 'success')) {
// 重新加载日志,显示更新后的状态
loadLogs();
// 刷新规则列表
refreshRulesList();
// 显示成功通知
if (typeof window.showNotification === 'function') {
window.showNotification(`已成功拦截域名: ${domain}`, 'success');
}
} else {
const errorMsg = response ? (response.message || '添加拦截规则失败') : '添加拦截规则失败: 无效的API响应';
console.error(`拦截域名失败: ${errorMsg}`);
throw new Error(errorMsg);
}
} catch (error) {
console.error('拦截域名失败:', error);
// 显示错误通知
if (typeof window.showNotification === 'function') {
window.showNotification(`拦截域名失败: ${error.message}`, 'danger');
}
}
}
// 绑定操作按钮事件
function bindActionButtonsEvents() {
// 绑定拦截按钮事件
const blockBtns = document.querySelectorAll('.block-btn');
blockBtns.forEach(btn => {
btn.addEventListener('click', async (e) => {
e.preventDefault();
const domain = e.currentTarget.dataset.domain;
await blockDomain(domain);
});
});
// 绑定放行按钮事件
const unblockBtns = document.querySelectorAll('.unblock-btn');
unblockBtns.forEach(btn => {
btn.addEventListener('click', async (e) => {
e.preventDefault();
const domain = e.currentTarget.dataset.domain;
await unblockDomain(domain);
});
});
}
// 刷新规则列表
async function refreshRulesList() {
try {
// 调用API重新加载规则
const response = await apiRequest('/shield', 'GET');
if (response) {
// 处理规则列表响应
let allRules = [];
if (response && typeof response === 'object') {
// 合并所有类型的规则到一个数组
if (Array.isArray(response.domainRules)) allRules = allRules.concat(response.domainRules);
if (Array.isArray(response.domainExceptions)) allRules = allRules.concat(response.domainExceptions);
if (Array.isArray(response.regexRules)) allRules = allRules.concat(response.regexRules);
if (Array.isArray(response.regexExceptions)) allRules = allRules.concat(response.regexExceptions);
}
// 更新规则列表
if (window.rules) {
rules = allRules;
filteredRules = [...rules];
// 更新规则数量统计
if (window.updateRulesCount && typeof window.updateRulesCount === 'function') {
window.updateRulesCount(rules.length);
}
}
}
} catch (error) {
console.error('刷新规则列表失败:', error);
}
}
// 放行域名
async function unblockDomain(domain) {
try {
console.log(`开始放行域名: ${domain}`);
// 创建放行规则使用AdBlock Plus格式
const allowRule = `@@||${domain}^`;
console.log(`创建的放行规则: ${allowRule}`);
// 调用API添加放行规则
console.log(`调用API添加放行规则路径: /shield, 方法: POST`);
const response = await apiRequest('/shield', 'POST', { rule: allowRule });
console.log(`API响应:`, response);
// 处理不同的响应格式
if (response && (response.success || response.status === 'success')) {
// 重新加载日志,显示更新后的状态
loadLogs();
// 刷新规则列表
refreshRulesList();
// 显示成功通知
if (typeof window.showNotification === 'function') {
window.showNotification(`已成功放行域名: ${domain}`, 'success');
}
} else {
const errorMsg = response ? (response.message || '添加放行规则失败') : '添加放行规则失败: 无效的API响应';
console.error(`放行域名失败: ${errorMsg}`);
throw new Error(errorMsg);
}
} catch (error) {
console.error('放行域名失败:', error);
// 显示错误通知
if (typeof window.showNotification === 'function') {
window.showNotification(`放行域名失败: ${error.message}`, 'danger');
}
}
}
// 定期更新日志统计数据(备用方案)
setInterval(() => {
// 只有在查询日志页面时才更新

View File

@@ -231,7 +231,7 @@ async function loadShieldStats() {
}
}
// 加载本地规则
// 加载自定义规则
async function loadLocalRules() {
try {
const response = await fetch('/api/shield/localrules');
@@ -242,7 +242,7 @@ async function loadLocalRules() {
const data = await response.json();
// 更新本地规则数量显示
// 更新自定义规则数量显示
if (document.getElementById('local-rules-count')) {
document.getElementById('local-rules-count').textContent = data.localRulesCount || 0;
}
@@ -250,7 +250,7 @@ async function loadLocalRules() {
// 设置当前规则类型
currentRulesType = 'local';
// 合并所有本地规则
// 合并所有自定义规则
let rules = [];
// 添加域名规则
if (Array.isArray(data.domainRules)) {
@@ -271,8 +271,8 @@ async function loadLocalRules() {
updateRulesTable(rules);
} catch (error) {
console.error('加载本地规则失败:', error);
showNotification('加载本地规则失败', 'error');
console.error('加载自定义规则失败:', error);
showNotification('加载自定义规则失败', 'error');
}
}
@@ -1202,7 +1202,7 @@ let currentRulesType = 'local';
// 设置事件监听器
function setupShieldEventListeners() {
// 本地规则管理事件
// 自定义规则管理事件
const saveRuleBtn = document.getElementById('save-rule-btn');
if (saveRuleBtn) {
saveRuleBtn.addEventListener('click', handleAddRule);
@@ -1214,7 +1214,7 @@ function setupShieldEventListeners() {
saveBlacklistBtn.addEventListener('click', handleAddBlacklist);
}
// 添加切换查看本地规则和远程规则的事件监听
// 添加切换查看自定义规则和远程规则的事件监听
const viewLocalRulesBtn = document.getElementById('view-local-rules-btn');
if (viewLocalRulesBtn) {
viewLocalRulesBtn.addEventListener('click', loadLocalRules);