From 1c919991b0cf0ea5b3c74a8d522ef0c1e40c436b Mon Sep 17 00:00:00 2001 From: ddCat <2695092045@qq.com> Date: Thu, 20 Aug 2026 15:08:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(media):=20=E7=A7=81=E6=9C=89=E5=AA=92?= =?UTF-8?q?=E4=BD=93=20-=20=E4=BF=AE=E5=A4=8D=E4=B8=83=E7=89=9B=E7=A7=81?= =?UTF-8?q?=E6=9C=89=E7=A9=BA=E9=97=B4=E5=9B=BE=E7=89=87=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E7=AD=BE=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/service/resource.go | 9 ++++++++ backend/internal/service/resource_test.go | 26 +++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/backend/internal/service/resource.go b/backend/internal/service/resource.go index cf89b9502..5b91125eb 100644 --- a/backend/internal/service/resource.go +++ b/backend/internal/service/resource.go @@ -128,6 +128,15 @@ func (s *Service) prepareResourceDelivery(userID string, resource *model.Resourc if err != nil { return nil, err } + if setting.Provider == qiniuKodoProvider { + // 七牛私有空间即使配置了绑定域名,也不能匿名访问;必须使用 + // Kodo 私有下载签名,否则浏览器会收到 NotSupportAnonymous。 + redirectURL, err := signedOSSObjectURL(setting, resource.ObjectKey, time.Now().Add(directResourceURLTTL)) + if err != nil { + return nil, err + } + return &ResourceDelivery{Resource: resource, RedirectURL: redirectURL}, nil + } if setting.CDNBaseURL != "" { redirectURL, err := ossCDNObjectURL(setting.CDNBaseURL, resource.ObjectKey) if err != nil { diff --git a/backend/internal/service/resource_test.go b/backend/internal/service/resource_test.go index 1786d832a..1b441963c 100644 --- a/backend/internal/service/resource_test.go +++ b/backend/internal/service/resource_test.go @@ -410,6 +410,32 @@ func TestPrepareResourceDeliveryAllowsExplicitProxyWithCDN(t *testing.T) { } } +func TestPrepareResourceDeliverySignsPrivateQiniuCDNURL(t *testing.T) { + svc := newResourceTestService(t) + settingJSON, _ := json.Marshal(ossSettingValue{ + Enabled: true, Provider: qiniuKodoProvider, Endpoint: "https://up-z0.qiniup.com", CDNBaseURL: "https://media.example.com", + Bucket: "private-bucket", AccessKeyID: "access-id", AccessKeySecret: "secret-value", + }) + if err := svc.repo.SaveSystemSetting(&model.SystemSetting{Key: ossSettingKey, ValueJSON: string(settingJSON)}); err != nil { + t.Fatal(err) + } + resource := model.Resource{ + ID: "resource-qiniu-private-cdn", UserID: "user-1", Kind: "image", Status: model.ResourceStatusReady, + Provider: qiniuKodoProvider, Endpoint: "https://up-z0.qiniup.com", Bucket: "private-bucket", + ObjectKey: "ai/users/user-1/image/private.png", MimeType: "image/png", + } + if err := svc.repo.CreateResource(&resource); err != nil { + t.Fatal(err) + } + delivery, err := svc.PrepareResourceDelivery("user-1", resource.ID, ResourceDeliveryOptions{}) + if err != nil { + t.Fatal(err) + } + if delivery.RedirectURL == "" || !strings.HasPrefix(delivery.RedirectURL, "https://media.example.com/ai/users/user-1/image/private.png?") || !strings.Contains(delivery.RedirectURL, "e=") || !strings.Contains(delivery.RedirectURL, "token=") { + t.Fatalf("PrepareResourceDelivery() = %q, want a signed Qiniu URL", delivery.RedirectURL) + } +} + func TestCurrentUserCDNSettingAppliesToHistoricalResourcesInSameStorage(t *testing.T) { t.Setenv("CANVAS_ALLOW_PRIVATE_UPSTREAMS", "true") server := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))