diff --git a/internal/http/handlers/image.go b/internal/http/handlers/image.go index 9fe58cf..ef3f0a9 100644 --- a/internal/http/handlers/image.go +++ b/internal/http/handlers/image.go @@ -218,7 +218,7 @@ func ListImages(c *gin.Context) { result := []gin.H{} for _, img := range images { - result = append(result, formatMeta(&img)) + result = append(result, formatMetaSummary(&img)) } c.JSON(http.StatusOK, result) } @@ -300,6 +300,49 @@ func serveLocal(c *gin.Context, key string, etag string, maxAge int) { io.Copy(c.Writer, reader) } +func formatMetaSummary(img *model.Image) gin.H { + cfg := config.GetConfig() + + // 找到最小的变体(Size 最小) + var smallest *model.ImageVariant + for i := range img.Variants { + v := &img.Variants[i] + if smallest == nil || v.Size < smallest.Size { + smallest = v + } + } + + variants := []gin.H{} + if smallest != nil { + url := smallest.PublicURL + if url == "" && cfg.API.Mode == "redirect" && img.URLBase != "" { + url = fmt.Sprintf("https://www.bing.com%s_%s.jpg", img.URLBase, smallest.Variant) + } else if cfg.API.Mode == "local" || url == "" { + url = fmt.Sprintf("%s/api/v1/image/date/%s?variant=%s&format=%s&mkt=%s", cfg.Server.BaseURL, img.Date, smallest.Variant, smallest.Format, img.Mkt) + } + variants = append(variants, gin.H{ + "variant": smallest.Variant, + "format": smallest.Format, + "size": smallest.Size, + "url": url, + "storage_key": smallest.StorageKey, + }) + } + + return gin.H{ + "date": img.Date, + "mkt": img.Mkt, + "title": img.Title, + "copyright": img.Copyright, + "copyrightlink": img.CopyrightLink, + "quiz": img.Quiz, + "startdate": img.StartDate, + "fullstartdate": img.FullStartDate, + "hsh": img.HSH, + "variants": variants, + } +} + func formatMeta(img *model.Image) gin.H { cfg := config.GetConfig() variants := []gin.H{} diff --git a/internal/http/handlers/image_test.go b/internal/http/handlers/image_test.go index f10d98b..1617caa 100644 --- a/internal/http/handlers/image_test.go +++ b/internal/http/handlers/image_test.go @@ -66,6 +66,21 @@ func TestHandleImageResponseRedirect(t *testing.T) { assert.Contains(t, variants[0]["url"].(string), "myserver.com") assert.Contains(t, variants[0]["url"].(string), "/api/v1/image/date/") }) + + t.Run("FormatMetaSummary should only return the smallest variant", func(t *testing.T) { + imgWithMultipleVariants := &model.Image{ + Date: "2026-01-26", + Variants: []model.ImageVariant{ + {Variant: "UHD", Size: 1000, Format: "jpg"}, + {Variant: "640x480", Size: 200, Format: "jpg"}, + {Variant: "1920x1080", Size: 500, Format: "jpg"}, + }, + } + meta := formatMetaSummary(imgWithMultipleVariants) + variants := meta["variants"].([]gin.H) + assert.Equal(t, 1, len(variants)) + assert.Equal(t, "640x480", variants[0]["variant"]) + }) } func TestGetRegions(t *testing.T) { diff --git a/webapp/src/lib/api-config.ts b/webapp/src/lib/api-config.ts index 9a6e907..289b16b 100644 --- a/webapp/src/lib/api-config.ts +++ b/webapp/src/lib/api-config.ts @@ -35,6 +35,26 @@ export const buildApiUrl = (endpoint: string): string => { return `${API_BASE_URL}${normalizedEndpoint}` } +/** + * 标准化图片 URL + * 当后端返回相对路径且配置了绝对 API 基础地址时,自动拼接完整域名 + */ +export const normalizeImageUrl = (url: string | undefined): string => { + if (!url) return '' + if (url.startsWith('http')) return url + + // 处理相对路径问题:如果配置了绝对 API 基础地址,则拼接 Origin + if (API_BASE_URL.startsWith('http')) { + try { + const origin = new URL(API_BASE_URL).origin + return url.startsWith('/') ? origin + url : origin + '/' + url + } catch (e) { + // 解析失败则返回原样 + } + } + return url +} + /** * HTTP 状态码枚举 */ diff --git a/webapp/src/views/Home.vue b/webapp/src/views/Home.vue index 9e3b2fb..04ddb3e 100644 --- a/webapp/src/views/Home.vue +++ b/webapp/src/views/Home.vue @@ -156,7 +156,7 @@ -