From 4eca3f8935458429b4817676fd2fbd80f76cf7d6 Mon Sep 17 00:00:00 2001 From: NewName Date: Tue, 20 May 2025 18:49:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=90=9C=E7=B4=A2=E9=95=9C=E5=83=8F=E5=AE=8C?= =?UTF-8?q?=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ghproxy/public/search.html | 74 +++++++++++++++++++++++++++++++------- ghproxy/search.go | 59 +++++++++++++++--------------- 2 files changed, 93 insertions(+), 40 deletions(-) diff --git a/ghproxy/public/search.html b/ghproxy/public/search.html index 2eaf62e..71c2d8e 100644 --- a/ghproxy/public/search.html +++ b/ghproxy/public/search.html @@ -512,16 +512,70 @@ prevButton.disabled = currentPage <= 1; nextButton.disabled = currentPage >= totalPages; - // 添加页码显示 + // 添加页码显示和快速跳转 const paginationDiv = document.querySelector('.pagination'); - const pageInfo = document.getElementById('pageInfo'); + let pageInfo = document.getElementById('pageInfo'); if (!pageInfo) { - const infoSpan = document.createElement('span'); - infoSpan.id = 'pageInfo'; - infoSpan.style.margin = '0 10px'; - paginationDiv.insertBefore(infoSpan, nextButton); + const container = document.createElement('div'); + container.id = 'pageInfo'; + container.style.margin = '0 10px'; + container.style.display = 'flex'; + container.style.alignItems = 'center'; + container.style.gap = '10px'; + + // 页码显示 + const pageText = document.createElement('span'); + pageText.id = 'pageText'; + + // 跳转输入框 + const jumpInput = document.createElement('input'); + jumpInput.type = 'number'; + jumpInput.min = '1'; + jumpInput.id = 'jumpPage'; + jumpInput.style.width = '60px'; + jumpInput.style.padding = '4px'; + jumpInput.style.borderRadius = '4px'; + jumpInput.style.border = '1px solid var(--border-color)'; + jumpInput.style.backgroundColor = 'var(--inputcolor)'; + jumpInput.style.color = 'var(--inputcolor-font)'; + + // 跳转按钮 + const jumpButton = document.createElement('button'); + jumpButton.textContent = '跳转'; + jumpButton.className = 'btn search-button'; + jumpButton.style.padding = '4px 8px'; + jumpButton.onclick = () => { + const page = parseInt(jumpInput.value); + if (page && page >= 1 && page <= totalPages) { + currentPage = page; + performSearch(); + } else { + showToast('请输入有效的页码'); + } + }; + + container.appendChild(pageText); + container.appendChild(jumpInput); + container.appendChild(jumpButton); + + // 插入到分页区域 + paginationDiv.insertBefore(container, nextButton); + pageInfo = container; } - document.getElementById('pageInfo').textContent = `第 ${currentPage} / ${totalPages} 页`; + + // 更新页码显示 + const pageText = document.getElementById('pageText'); + pageText.textContent = `第 ${currentPage} / ${totalPages || 1} 页 共 ${totalPages || 1} 页`; + + // 更新跳转输入框 + const jumpInput = document.getElementById('jumpPage'); + if (jumpInput) { + jumpInput.max = totalPages; + jumpInput.value = currentPage; + } + + // 显示或隐藏分页区域 + paginationDiv.style.display = totalPages > 1 ? 'flex' : 'none'; } function showSearchResults() { @@ -560,11 +614,7 @@ console.log('搜索响应:', data); // 更新总页数和分页状态 - if (typeof data.count === 'number') { - totalPages = Math.ceil(data.count / 25); - } else { - totalPages = data.results ? Math.ceil(data.results.length / 25) : 1; - } + totalPages = Math.ceil(data.count / 25); updatePagination(); displayResults(data.results); diff --git a/ghproxy/search.go b/ghproxy/search.go index 90093ae..43a484f 100644 --- a/ghproxy/search.go +++ b/ghproxy/search.go @@ -223,18 +223,45 @@ func searchDockerHub(ctx context.Context, query string, page, pageSize int) (*Se var result *SearchResult var lastErr error + // 判断是否是用户/仓库格式的搜索 + isUserRepo := strings.Contains(query, "/") + for retries := 3; retries > 0; retries-- { - result, lastErr = trySearchDockerHub(ctx, query, page, pageSize) + if isUserRepo { + // 对于用户/仓库格式,尝试精确搜索和模糊搜索 + parts := strings.Split(query, "/") + if len(parts) == 2 { + // 先尝试精确搜索 + result, lastErr = trySearchDockerHub(ctx, query, page, pageSize) + if lastErr == nil && len(result.Results) == 0 { + // 如果精确搜索没有结果,尝试模糊搜索 + result, lastErr = trySearchDockerHub(ctx, parts[1], page, pageSize) + if lastErr == nil { + // 过滤出属于指定用户的结果 + filteredResults := make([]Repository, 0) + for _, repo := range result.Results { + if strings.EqualFold(repo.Namespace, parts[0]) || + strings.EqualFold(repo.RepoOwner, parts[0]) { + filteredResults = append(filteredResults, repo) + } + } + result.Results = filteredResults + result.Count = len(filteredResults) + } + } + } + } else { + result, lastErr = trySearchDockerHub(ctx, query, page, pageSize) + } + if lastErr == nil { break } - // 判断是否需要重试 if !isRetryableError(lastErr) { return nil, fmt.Errorf("搜索失败: %v", lastErr) } - // 等待后重试 time.Sleep(time.Second * time.Duration(4-retries)) } @@ -316,7 +343,6 @@ func trySearchDockerHub(ctx context.Context, query string, page, pageSize int) ( // 检查响应状态码 if resp.StatusCode != http.StatusOK { - // 特殊处理常见错误 switch resp.StatusCode { case http.StatusTooManyRequests: return nil, fmt.Errorf("请求过于频繁,请稍后重试") @@ -342,7 +368,6 @@ func trySearchDockerHub(ctx context.Context, query string, page, pageSize int) ( if !strings.Contains(result.Results[i].Name, "/") { result.Results[i].Name = "library/" + result.Results[i].Name } - // 设置命名空间为 library result.Results[i].Namespace = "library" } else { // 从 repo_name 中提取 namespace @@ -350,7 +375,7 @@ func trySearchDockerHub(ctx context.Context, query string, page, pageSize int) ( if len(parts) > 1 { result.Results[i].Namespace = parts[0] result.Results[i].Name = parts[1] - } else { + } else if result.Results[i].RepoOwner != "" { result.Results[i].Namespace = result.Results[i].RepoOwner } } @@ -452,11 +477,6 @@ func RegisterSearchRoute(r *gin.Engine) { fmt.Sscanf(ps, "%d", &pageSize) } - // 如果是搜索官方镜像 - if !strings.Contains(query, "/") { - query = strings.ToLower(query) - } - fmt.Printf("搜索请求: query=%s, page=%d, pageSize=%d\n", query, page, pageSize) result, err := searchDockerHub(c.Request.Context(), query, page, pageSize) @@ -466,23 +486,6 @@ func RegisterSearchRoute(r *gin.Engine) { return } - // 过滤搜索结果,只保留相关的镜像 - filteredResults := make([]Repository, 0) - searchTerm := strings.ToLower(strings.TrimPrefix(query, "library/")) - - for _, repo := range result.Results { - repoName := strings.ToLower(repo.Name) - // 如果是精确匹配或者以搜索词开头,或者包含 "searchTerm/searchTerm" - if repoName == searchTerm || - strings.HasPrefix(repoName, searchTerm+"/") || - strings.Contains(repoName, "/"+searchTerm) { - filteredResults = append(filteredResults, repo) - } - } - - result.Results = filteredResults - result.Count = len(filteredResults) - c.JSON(http.StatusOK, result) })