From 9f81b449f1ecf770dcc2a74c20de224b5585bc09 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Mon, 24 Nov 2025 23:50:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- http/server.go | 143 +++++++++++++++++++++++++++++++++++++ static/js/modules/rules.js | 23 ++++-- 2 files changed, 159 insertions(+), 7 deletions(-) diff --git a/http/server.go b/http/server.go index 5b2548f..15e2839 100644 --- a/http/server.go +++ b/http/server.go @@ -200,6 +200,12 @@ func (s *Server) handleShield(w http.ResponseWriter, r *http.Request) { return } + // 处理远程黑名单管理子路由 + if strings.HasPrefix(r.URL.Path, "/api/shield/blacklists") { + s.handleShieldBlacklists(w, r) + return + } + switch r.Method { case http.MethodGet: // 获取完整规则列表 @@ -255,6 +261,143 @@ func (s *Server) handleShield(w http.ResponseWriter, r *http.Request) { } } +// handleShieldBlacklists 处理远程黑名单管理请求 +func (s *Server) handleShieldBlacklists(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + // 处理更新单个黑名单 + if strings.Contains(r.URL.Path, "/update") { + if r.Method == http.MethodPost { + // 提取黑名单ID + parts := strings.Split(r.URL.Path, "/") + var id string + for i, part := range parts { + if part == "blacklists" && i+1 < len(parts) && parts[i+1] != "update" { + id = parts[i+1] + break + } + } + + // 获取黑名单列表 + blacklists := s.shieldManager.GetBlacklists() + var targetIndex = -1 + for i, list := range blacklists { + if list.URL == id || list.Name == id { + targetIndex = i + break + } + } + + if targetIndex == -1 { + http.Error(w, "黑名单不存在", http.StatusNotFound) + return + } + + // 尝试更新该黑名单 + if err := s.shieldManager.fetchRemoteRules(blacklists[targetIndex].URL); err != nil { + // 更新失败,但不返回错误,而是更新状态 + blacklists[targetIndex].LastUpdateTime = time.Now().Format(time.RFC3339) + } else { + // 更新成功 + blacklists[targetIndex].LastUpdateTime = time.Now().Format(time.RFC3339) + // 重新加载规则 + s.shieldManager.LoadRules() + } + + s.shieldManager.UpdateBlacklist(blacklists) + json.NewEncoder(w).Encode(map[string]string{"status": "success"}) + return + } + } + + // 处理删除黑名单 + parts := strings.Split(r.URL.Path, "/") + if len(parts) > 4 && parts[3] == "blacklists" && parts[4] != "" && r.Method == http.MethodDelete { + id := parts[4] + blacklists := s.shieldManager.GetBlacklists() + var newBlacklists []config.BlacklistEntry + + for _, list := range blacklists { + if list.URL != id && list.Name != id { + newBlacklists = append(newBlacklists, list) + } + } + + s.shieldManager.UpdateBlacklist(newBlacklists) + json.NewEncoder(w).Encode(map[string]string{"status": "success"}) + return + } + + switch r.Method { + case http.MethodGet: + // 获取远程黑名单列表 + blacklists := s.shieldManager.GetBlacklists() + json.NewEncoder(w).Encode(blacklists) + + case http.MethodPost: + // 添加远程黑名单 + var req struct { + Name string `json:"name"` + URL string `json:"url"` + } + + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + if req.Name == "" || req.URL == "" { + http.Error(w, "Name and URL are required", http.StatusBadRequest) + return + } + + // 获取现有黑名单 + blacklists := s.shieldManager.GetBlacklists() + + // 检查是否已存在 + for _, list := range blacklists { + if list.URL == req.URL { + http.Error(w, "黑名单URL已存在", http.StatusConflict) + return + } + } + + // 添加新黑名单 + newEntry := config.BlacklistEntry{ + Name: req.Name, + URL: req.URL, + Enabled: true, + } + + blacklists = append(blacklists, newEntry) + s.shieldManager.UpdateBlacklist(blacklists) + + // 尝试获取规则 + s.shieldManager.fetchRemoteRules(req.URL) + s.shieldManager.LoadRules() + + json.NewEncoder(w).Encode(map[string]string{"status": "success"}) + + case http.MethodPut: + // 更新所有远程黑名单 + blacklists := s.shieldManager.GetBlacklists() + for i := range blacklists { + // 尝试更新每个黑名单 + s.shieldManager.fetchRemoteRules(blacklists[i].URL) + blacklists[i].LastUpdateTime = time.Now().Format(time.RFC3339) + } + + s.shieldManager.UpdateBlacklist(blacklists) + // 重新加载所有规则 + s.shieldManager.LoadRules() + + json.NewEncoder(w).Encode(map[string]string{"status": "success"}) + + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + // handleShieldHosts 处理hosts管理请求 func (s *Server) handleShieldHosts(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") diff --git a/static/js/modules/rules.js b/static/js/modules/rules.js index ab304dd..94ec160 100644 --- a/static/js/modules/rules.js +++ b/static/js/modules/rules.js @@ -40,11 +40,20 @@ async function loadRules() { const rulesPanel = document.getElementById('rules-panel'); showLoading(rulesPanel); - // 更新API路径,使用完整路径 + // 更新API路径,使用正确的API路径 const data = await apiRequest('/shield', 'GET'); - // 处理不同格式的响应数据 - rules = Array.isArray(data) ? data : (data.rules || []); + // 处理后端返回的复杂对象数据格式 + let allRules = []; + if (data && typeof data === 'object') { + // 合并所有类型的规则到一个数组 + if (Array.isArray(data.domainRules)) allRules = allRules.concat(data.domainRules); + if (Array.isArray(data.domainExceptions)) allRules = allRules.concat(data.domainExceptions); + if (Array.isArray(data.regexRules)) allRules = allRules.concat(data.regexRules); + if (Array.isArray(data.regexExceptions)) allRules = allRules.concat(data.regexExceptions); + } + + rules = allRules; filteredRules = [...rules]; currentPage = 1; // 重置为第一页 renderRulesList(); @@ -197,7 +206,7 @@ async function addNewRule() { // 预处理规则,支持AdGuardHome格式 const processedRule = preprocessRule(rule); - // 更新API路径 + // 使用正确的API路径 const response = await apiRequest('/shield', 'POST', { rule: processedRule }); // 处理不同的响应格式 @@ -248,7 +257,7 @@ async function deleteRule(index) { rowElement.style.transform = 'translateX(-20px)'; } - // 更新API路径 + // 使用正确的API路径 const response = await apiRequest('/shield', 'DELETE', { rule }); // 处理不同的响应格式 @@ -310,8 +319,8 @@ async function reloadRules() { const rulesPanel = document.getElementById('rules-panel'); showLoading(rulesPanel); - // 确保使用正确的API路径 - await apiRequest('/shield/refresh', 'POST'); + // 使用正确的API路径和方法 - PUT请求到/shield + await apiRequest('/shield', 'PUT'); // 重新加载规则列表 await loadRules();