feat(attach): 附件可配置限额、流式落盘与分阶段上传

管理端可配类型/体积;上传改流式避免整文件进内存;发帖先选再传并拦截未上传提交;提高 Next proxy 体积上限。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-17 07:33:16 +08:00
parent 6cc434f103
commit 61bf9fb48c
24 changed files with 2513 additions and 307 deletions

View File

@@ -0,0 +1,70 @@
package service
import (
"testing"
)
func TestNormalizeAttachmentExts(t *testing.T) {
got, err := NormalizeAttachmentExts([]string{".PDF", "msi", "PDF", " zip "})
if err != nil {
t.Fatal(err)
}
want := []string{"pdf", "msi", "zip"}
if len(got) != len(want) {
t.Fatalf("len=%d want %d: %v", len(got), len(want), got)
}
for i := range want {
if got[i] != want[i] {
t.Fatalf("got[%d]=%s want %s", i, got[i], want[i])
}
}
}
func TestNormalizeAttachmentExtsInvalid(t *testing.T) {
_, err := NormalizeAttachmentExts([]string{"bad_ext"})
if err != ErrInvalidSiteSetting {
t.Fatalf("want ErrInvalidSiteSetting, got %v", err)
}
}
func TestResolveAttachmentMIMEActive(t *testing.T) {
m := ResolveAttachmentMIME(".html", []byte("<!DOCTYPE html><html></html>"))
if m != "application/octet-stream" {
t.Fatalf("html mime=%s", m)
}
}
func TestResolveAttachmentMIMEDisguisedPNG(t *testing.T) {
m := ResolveAttachmentMIME(".png", []byte("<svg xmlns='http://www.w3.org/2000/svg'></svg>"))
if m != "application/octet-stream" {
t.Fatalf("disguised png mime=%s", m)
}
}
func TestIsActiveContentExt(t *testing.T) {
if !IsActiveContentExt(".JS") {
t.Fatal("js should be active")
}
if IsActiveContentExt(".pdf") {
t.Fatal("pdf should not be active")
}
}
func TestSanitizeFilenameRTL(t *testing.T) {
name := sanitizeFilename("evil\u202epdf.exe")
if name != "evil_pdf.exe" && name != "evil_.exe" {
// Map replaces U+202E with _
if !containsRune(name, '_') {
t.Fatalf("expected RTL stripped: %q", name)
}
}
}
func containsRune(s string, r rune) bool {
for _, c := range s {
if c == r {
return true
}
}
return false
}