fix(media): 资源交付 - 按内容识别上传类型并统一重试与附件交付 (#678)

Co-authored-by: ddcat666 <26564566+ddcat666@users.noreply.github.com>
This commit is contained in:
Catlaxy AI
2026-10-06 09:35:50 +08:00
committed by GitHub
parent 9597a9b4ed
commit 3698249254
10 changed files with 322 additions and 39 deletions

View File

@@ -0,0 +1,39 @@
package handler
import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gin-gonic/gin"
"infinite-canvas/backend/internal/assets"
"infinite-canvas/backend/internal/model"
"infinite-canvas/backend/internal/service"
)
func TestNonMediaDeliveryAttachment(t *testing.T) {
gin.SetMode(gin.TestMode)
for _, mimeType := range []string{"text/html", "image/svg+xml", "application/pdf", "application/octet-stream", "image/png"} {
t.Run(mimeType, func(t *testing.T) {
router := gin.New()
router.GET("/file", func(c *gin.Context) {
r := &model.Resource{ID: "file", MimeType: mimeType}
serveResourceDelivery(c, &service.ResourceDelivery{Resource: r, Access: &assets.ResourceAccess{Delivery: assets.DeliveryLocal}, Stream: &service.ResourceStream{Resource: r, Body: io.NopCloser(strings.NewReader("content")), StatusCode: 200, ContentLength: 7}}, "", "inline")
})
w := httptest.NewRecorder()
router.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/file", nil))
if w.Code != 200 {
t.Fatalf("status=%d", w.Code)
}
if mimeType == "image/png" {
if w.Header().Get("Content-Disposition") != "inline" {
t.Fatal("media no longer inline")
}
} else if !strings.HasPrefix(w.Header().Get("Content-Disposition"), "attachment") || !strings.Contains(w.Header().Get("Content-Security-Policy"), "sandbox") {
t.Fatalf("unsafe non-media headers: %v", w.Header())
}
})
}
}

View File

@@ -3,6 +3,7 @@ package handler
import (
"fmt"
"io"
"mime"
"net/http"
"strings"
@@ -69,6 +70,12 @@ func serveResourceDelivery(c *gin.Context, delivery *service.ResourceDelivery, c
c.Header("Content-Disposition", disposition)
}
resource := stream.Resource
if !assets.InlineMediaType(resource.MimeType) {
if dispositionType, _, _ := mime.ParseMediaType(disposition); dispositionType != "attachment" {
c.Header("Content-Disposition", "attachment")
}
c.Header("Content-Security-Policy", "sandbox; default-src 'none'")
}
mimeType := resource.MimeType
if mimeType == "" {
mimeType = "application/octet-stream"