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) {}))