From d503e0036be0e19887d0ed2b0bc2ef96108f066b Mon Sep 17 00:00:00 2001 From: user123456 Date: Thu, 12 Jun 2025 13:43:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A6=BB=E7=BA=BF=E9=95=9C?= =?UTF-8?q?=E5=83=8F=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/public/search.html | 23 ++++++++++++++----- src/skopeo_service.go | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/src/public/search.html b/src/public/search.html index 87a1557..ad09e37 100644 --- a/src/public/search.html +++ b/src/public/search.html @@ -894,15 +894,21 @@ function createResultCard(result) { const card = document.createElement('div'); card.className = 'result-card'; - card.onclick = () => viewImageTags(result.repo_name || result.name); + card.onclick = () => viewImageTags(result.repo_name || result.name, result.is_official); const badges = []; if (result.is_official) badges.push('官方'); if (result.is_automated) badges.push('自动构建'); + // 只有真正的官方镜像才去掉 library/ 前缀 + const originalName = result.repo_name || result.name; + const displayName = (result.is_official && originalName.startsWith('library/')) + ? originalName.substring(8) + : originalName; + card.innerHTML = `
- 🐳 ${result.repo_name || result.name} + 🐳 ${displayName} ${badges.join('')}
@@ -926,7 +932,7 @@ } // 查看镜像标签 - async function viewImageTags(imageName) { + async function viewImageTags(imageName, isOfficial = false) { if (isLoading) return; isLoading = true; @@ -952,7 +958,7 @@ if (Array.isArray(data)) { allTags = data; - displayImageTags(imageName, data); + displayImageTags(imageName, data, isOfficial); elements.backToSearch.classList.add('show'); } else { showToast('获取标签失败:' + (data.error || '未知错误'), 'error'); @@ -967,12 +973,17 @@ } // 显示镜像标签 - function displayImageTags(imageName, tags) { + function displayImageTags(imageName, tags, isOfficial = false) { const fullDomain = window.location.host; + // 只有真正的官方镜像才去掉 library/ 前缀 + const displayName = (isOfficial && imageName.startsWith('library/')) + ? imageName.substring(8) + : imageName; + elements.tagInfo.innerHTML = `
- 🐳 ${imageName} + 🐳 ${displayName}
共 ${tags.length} 个标签版本 diff --git a/src/skopeo_service.go b/src/skopeo_service.go index d048cc9..fba2677 100644 --- a/src/skopeo_service.go +++ b/src/skopeo_service.go @@ -110,6 +110,9 @@ func initSkopeoRoutes(router *gin.Engine) { // 下载文件 router.GET("/api/files/:filename", serveFile) + + // 通过任务ID下载文件 + router.GET("/api/download/:taskId/file", serveFileByTaskId) // 启动清理过期文件的goroutine go cleanupTempFiles() @@ -1059,6 +1062,55 @@ func sendTaskUpdate(task *DownloadTask) { } } +// 通过任务ID提供文件下载 +func serveFileByTaskId(c *gin.Context) { + taskID := c.Param("taskId") + + tasksLock.Lock() + task, exists := tasks[taskID] + tasksLock.Unlock() + + if !exists { + c.JSON(http.StatusNotFound, gin.H{"error": "任务不存在"}) + return + } + + // 确保任务状态为已完成 + task.StatusLock.RLock() + isCompleted := task.Status == StatusCompleted + task.StatusLock.RUnlock() + + if !isCompleted { + c.JSON(http.StatusBadRequest, gin.H{"error": "任务尚未完成"}) + return + } + + // 确保所有进度都是100% + ensureTaskCompletion(task) + + // 检查文件是否存在 + filePath := task.OutputFile + if filePath == "" || !fileExists(filePath) { + c.JSON(http.StatusNotFound, gin.H{"error": "文件不存在"}) + return + } + + // 获取文件信息 + fileInfo, err := os.Stat(filePath) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "无法获取文件信息"}) + return + } + + // 设置文件名 + downloadName := filepath.Base(filePath) + c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", downloadName)) + c.Header("Content-Length", fmt.Sprintf("%d", fileInfo.Size())) + + // 返回文件 + c.File(filePath) +} + // 提供文件下载 func serveFile(c *gin.Context) { filename := c.Param("filename")