远程列表web

This commit is contained in:
Alex Yang
2025-11-23 23:50:04 +08:00
parent 30795ae767
commit fcd4fc9e68
15 changed files with 84600 additions and 3811 deletions

View File

@@ -478,6 +478,45 @@
font-size: 0.875rem;
border-radius: var(--border-radius-sm);
}
.btn-edit {
background-color: var(--info-color);
color: white;
border: none;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
}
.btn-edit:hover {
background-color: #2980b9;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
}
.checkbox-label input[type="checkbox"] {
cursor: pointer;
width: 18px;
height: 18px;
}
.list-meta {
display: flex;
gap: 1rem;
font-size: 0.75rem;
color: var(--gray-500);
}
.list-meta span {
display: flex;
align-items: center;
gap: 0.25rem;
}
.empty-state {
text-align: center;
@@ -816,33 +855,36 @@
<div class="card">
<div class="card-header">
<h3 class="card-title"><i class="fas fa-globe"></i> 远程规则管理</h3>
<h3 class="card-title"><i class="fas fa-ban"></i> 黑名单管理</h3>
</div>
<div class="card-body">
<div class="mb-3">
<label for="update-interval" class="form-label">更新间隔 (秒)</label>
<input type="number" id="update-interval" min="60" max="86400" placeholder="3600">
<small class="form-text">远程规则自动更新的时间间隔建议至少60秒</small>
<small class="form-text">黑名单自动更新的时间间隔建议至少60秒</small>
</div>
<div class="mb-3">
<div class="input-group">
<input type="text" id="remote-rule-url" placeholder="输入远程规则URL">
<button id="add-remote-rule" class="btn-secondary">
<i class="fas fa-plus"></i> 添加规则
<h4>添加新黑名单</h4>
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
<input type="text" id="blacklist-name" placeholder="黑名单名称">
<input type="text" id="blacklist-url" placeholder="黑名单URL">
<button id="add-blacklist" class="btn-secondary">
<i class="fas fa-plus"></i> 添加
</button>
</div>
<small class="form-text">添加远程黑名单源支持HTTP/HTTPS链接</small>
</div>
<div class="list-container" id="remote-rules-list">
<div class="list-container" id="blacklists-list">
<div class="empty-state">
<i class="fas fa-info-circle"></i>
<p>加载中...</p>
</div>
</div>
<button id="save-remote-settings" class="btn-primary mt-3">
<i class="fas fa-save"></i> 保存远程设置
<button id="save-blacklists-settings" class="btn-primary mt-3">
<i class="fas fa-save"></i> 保存设置
</button>
</div>
</div>
@@ -1426,7 +1468,7 @@ function loadRules() {
typeText = '正则';
}
// 转义规则中的特殊字符确保在HTML和JavaScript中正确处理
// 转义规则中的特殊字符确保在HTML和JavaScript中正确处理
const escapedRule = item.rule.replace(/'/g, "\\'");
ruleItem.innerHTML = `
@@ -1504,7 +1546,19 @@ function loadRules() {
},
body: JSON.stringify({ rule: fullRule })
})
.then(response => response.json())
.then(response => {
if (!response.ok) {
throw new Error('保存失败');
}
return response.json();
})
.then(data => {
showNotification('success', '黑名单设置已保存');
})
.catch(error => {
console.error('保存黑名单设置失败:', error);
showNotification('danger', '保存失败: ' + error.message);
});
.then(data => {
// 重置按钮状态
btn.innerHTML = originalText;
@@ -1537,7 +1591,7 @@ function loadRules() {
fetch('/api/shield/hosts')
.then(response => response.json())
.then(data => {
// 注意这需要在shieldManager中添加一个获取所有hosts条目的方法
// 注意这需要在shieldManager中添加一个获取所有hosts条目的方法
// 暂时返回统计信息
const hostsCount = data.hostsCount || 0;
@@ -1766,12 +1820,12 @@ function loadRules() {
// 加载当前屏蔽设置
loadBlockSettings();
// 远程规则相关事件监听
document.getElementById('add-remote-rule').addEventListener('click', addRemoteRule);
document.getElementById('save-remote-settings').addEventListener('click', saveRemoteSettings);
// 黑名单相关事件监听
document.getElementById('add-blacklist').addEventListener('click', addBlacklist);
document.getElementById('save-blacklists-settings').addEventListener('click', saveBlacklistsSettings);
// 加载远程规则设置
loadRemoteSettings();
// 加载黑名单设置
loadBlacklistsSettings();
};
// 加载当前屏蔽设置
@@ -1835,82 +1889,104 @@ function loadRules() {
});
}
// 加载远程规则设置
function loadRemoteSettings() {
fetch('/api/config', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
// 加载黑名单设置
function loadBlacklistsSettings() {
fetch('/api/config')
.then(response => response.json())
.then(data => {
if (data.shield) {
// 设置更新间隔
document.getElementById('update-interval').value = data.shield.updateInterval || 3600;
// 加载远程规则列表
renderRemoteRulesList(data.shield.remoteRules || []);
// 加载黑名单列表
renderBlacklistsList(data.shield.blacklists || []);
}
})
.catch(error => {
console.error('加载远程规则设置失败:', error);
showNotification('danger', '加载远程规则设置失败');
renderRemoteRulesList([]);
console.error('加载黑名单设置失败:', error);
showNotification('danger', '加载黑名单设置失败');
renderBlacklistsList([]);
});
}
// 渲染远程规则列表
function renderRemoteRulesList(rules) {
const listContainer = document.getElementById('remote-rules-list');
// 渲染黑名单列表
function renderBlacklistsList(blacklists) {
const listContainer = document.getElementById('blacklists-list');
if (rules.length === 0) {
if (!blacklists || blacklists.length === 0) {
listContainer.innerHTML = `
<div class="empty-state">
<i class="fas fa-info-circle"></i>
<p>暂无远程规则</p>
<p>暂无黑名单</p>
</div>
`;
return;
}
let html = '<div class="list-container">';
rules.forEach((rule, index) => {
// 确保我们处理的是数组
const blacklistItems = Array.isArray(blacklists) ? blacklists : [];
let html = '';
blacklistItems.forEach((item, index) => {
html += `
<div class="list-item">
<div class="list-content">
<div class="list-title">远程规则 ${index + 1}</div>
<div class="list-description">${rule}</div>
<div class="list-title">
<label class="checkbox-label">
<input type="checkbox" ${item.enabled ? 'checked' : ''} class="blacklist-enabled" data-index="${index}">
<span>${item.name || '未命名黑名单'}</span>
</label>
</div>
<div class="list-description">${item.URL}</div>
<div class="list-meta">
<span>${item.ruleCount ? `规则数: ${item.ruleCount}` : '未加载规则'}</span>
${item.lastUpdateTime ? `<span>更新时间: ${item.lastUpdateTime}</span>` : ''}
</div>
</div>
<div class="list-actions">
<span class="badge badge-primary">远程</span>
<button class="btn-danger btn-sm delete-rule" data-index="${index}">
<button class="btn-edit btn-sm" data-index="${index}">
<i class="fas fa-edit"></i>
</button>
<button class="btn-danger btn-sm delete-blacklist" data-index="${index}">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</div>
`;
});
html += '</div>';
listContainer.innerHTML = html;
// 添加删除按钮事件监听
document.querySelectorAll('.delete-rule').forEach(btn => {
// 添加事件监听
document.querySelectorAll('.blacklist-enabled').forEach(checkbox => {
checkbox.addEventListener('change', function() {
const index = parseInt(this.getAttribute('data-index'));
toggleBlacklistStatus(index, this.checked);
});
});
document.querySelectorAll('.delete-blacklist').forEach(btn => {
btn.addEventListener('click', function() {
const index = parseInt(this.getAttribute('data-index'));
deleteRemoteRule(index);
deleteBlacklist(index);
});
});
document.querySelectorAll('.btn-edit').forEach(btn => {
btn.addEventListener('click', function() {
const index = parseInt(this.getAttribute('data-index'));
editBlacklist(index);
});
});
}
// 添加远程规则
function addRemoteRule() {
const urlInput = document.getElementById('remote-rule-url');
const url = urlInput.value.trim();
// 添加黑名单
function addBlacklist() {
const name = document.getElementById('blacklist-name').value.trim();
const url = document.getElementById('blacklist-url').value.trim();
if (!url) {
showNotification('warning', '请输入有效的URL');
if (!name || !url) {
showNotification('warning', '请输入黑名单名称和URL');
return;
}
@@ -1922,42 +1998,110 @@ function loadRules() {
return;
}
// 获取当前列表中的规则
const ruleItems = document.querySelectorAll('.list-item');
const rules = Array.from(ruleItems).map(item =>
item.querySelector('.list-description').textContent
);
// 获取当前黑名单列表
const listItems = document.querySelectorAll('#blacklists-list .list-item');
let blacklists = [];
// 检查是否已存在
if (rules.includes(url)) {
showNotification('warning', '该规则已存在');
listItems.forEach((item, index) => {
const enabled = item.querySelector('.blacklist-enabled').checked;
const name = item.querySelector('.list-title span').textContent;
const url = item.querySelector('.list-description').textContent;
blacklists.push({ name, URL: url, enabled });
});
// 检查URL是否已存在
if (blacklists.some(item => item.URL === url)) {
showNotification('warning', '该URL已存在');
return;
}
// 添加新规则
rules.push(url);
renderRemoteRulesList(rules);
urlInput.value = '';
// 添加新黑名单
blacklists.push({ name, URL: url, enabled: true });
renderBlacklistsList(blacklists);
showNotification('success', '规则已添加');
// 清空输入框
document.getElementById('blacklist-name').value = '';
document.getElementById('blacklist-url').value = '';
showNotification('success', '黑名单已添加');
}
// 删除远程规则
function deleteRemoteRule(index) {
const ruleItems = document.querySelectorAll('.list-item');
const rules = Array.from(ruleItems).map(item =>
item.querySelector('.list-description').textContent
);
// 编辑黑名单
function editBlacklist(index) {
const listItems = document.querySelectorAll('#blacklists-list .list-item');
const item = listItems[index];
// 移除指定索引的规则
rules.splice(index, 1);
renderRemoteRulesList(rules);
const currentName = item.querySelector('.list-title span').textContent;
const currentUrl = item.querySelector('.list-description').textContent;
showNotification('success', '规则已删除');
const newName = prompt('请输入黑名单名称:', currentName);
if (newName === null) return;
const newUrl = prompt('请输入黑名单URL:', currentUrl);
if (newUrl === null) return;
if (!newName.trim() || !newUrl.trim()) {
showNotification('warning', '名称和URL不能为空');
return;
}
// URL验证
try {
new URL(newUrl);
} catch (e) {
showNotification('warning', '请输入有效的URL格式');
return;
}
// 获取所有黑名单
let blacklists = [];
listItems.forEach((item, idx) => {
const enabled = item.querySelector('.blacklist-enabled').checked;
let name = item.querySelector('.list-title span').textContent;
let url = item.querySelector('.list-description').textContent;
if (idx === index) {
name = newName.trim();
url = newUrl.trim();
}
blacklists.push({ name, URL: url, enabled });
});
renderBlacklistsList(blacklists);
showNotification('success', '黑名单已更新');
}
// 保存远程规则设置
function saveRemoteSettings() {
// 切换黑名单状态
function toggleBlacklistStatus(index, enabled) {
showNotification('info', enabled ? '黑名单已启用' : '黑名单已禁用');
}
// 删除黑名单
function deleteBlacklist(index) {
if (!confirm('确定要删除这条黑名单吗?')) {
return;
}
// 获取所有黑名单
const listItems = document.querySelectorAll('#blacklists-list .list-item');
let blacklists = [];
listItems.forEach((item, idx) => {
if (idx !== index) {
const enabled = item.querySelector('.blacklist-enabled').checked;
const name = item.querySelector('.list-title span').textContent;
const url = item.querySelector('.list-description').textContent;
blacklists.push({ name, URL: url, enabled });
}
});
renderBlacklistsList(blacklists);
showNotification('success', '黑名单已删除');
}
// 保存黑名单设置
function saveBlacklistsSettings() {
const updateInterval = parseInt(document.getElementById('update-interval').value);
// 验证更新间隔
@@ -1966,11 +2110,16 @@ function loadRules() {
return;
}
// 获取当前列表中的规则
const ruleItems = document.querySelectorAll('.list-item');
const remoteRules = Array.from(ruleItems).map(item =>
item.querySelector('.list-description').textContent
);
// 获取当前黑名单列表
const listItems = document.querySelectorAll('#blacklists-list .list-item');
let blacklists = [];
listItems.forEach(item => {
const enabled = item.querySelector('.blacklist-enabled').checked;
const name = item.querySelector('.list-title span').textContent;
const url = item.querySelector('.list-description').textContent;
blacklists.push({ name, URL: url, enabled });
});
fetch('/api/config', {
method: 'POST',
@@ -1979,7 +2128,7 @@ function loadRules() {
},
body: JSON.stringify({
shield: {
remoteRules: remoteRules,
blacklists: blacklists,
updateInterval: updateInterval
}
})