refactor: FilterReadMeScripts (#8154 close #8150)

* refactor: FilterReadMeScripts

* .
This commit is contained in:
j2rong4cn 2025-03-18 22:02:33 +08:00 committed by GitHub
parent 3499c4db87
commit b4e6ab12d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 51 additions and 98 deletions

View File

@ -316,7 +316,7 @@ func (d *Quqi) Put(ctx context.Context, dstDir model.Obj, stream model.FileStrea
// if the file already exists in Quqi server, there is no need to actually upload it // if the file already exists in Quqi server, there is no need to actually upload it
if uploadInitResp.Data.Exist { if uploadInitResp.Data.Exist {
// the file name returned by Quqi does not include the extension name // the file name returned by Quqi does not include the extension name
nodeName, nodeExt := uploadInitResp.Data.NodeName, rawExt(stream.GetName()) nodeName, nodeExt := uploadInitResp.Data.NodeName, utils.Ext(stream.GetName())
if nodeExt != "" { if nodeExt != "" {
nodeName = nodeName + "." + nodeExt nodeName = nodeName + "." + nodeExt
} }
@ -432,7 +432,7 @@ func (d *Quqi) Put(ctx context.Context, dstDir model.Obj, stream model.FileStrea
return nil, err return nil, err
} }
// the file name returned by Quqi does not include the extension name // the file name returned by Quqi does not include the extension name
nodeName, nodeExt := uploadFinishResp.Data.NodeName, rawExt(stream.GetName()) nodeName, nodeExt := uploadFinishResp.Data.NodeName, utils.Ext(stream.GetName())
if nodeExt != "" { if nodeExt != "" {
nodeName = nodeName + "." + nodeExt nodeName = nodeName + "." + nodeExt
} }

View File

@ -9,7 +9,6 @@ import (
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
stdpath "path"
"strings" "strings"
"time" "time"
@ -115,16 +114,6 @@ func (d *Quqi) checkLogin() bool {
return true return true
} }
// rawExt 保留扩展名大小写
func rawExt(name string) string {
ext := stdpath.Ext(name)
if strings.HasPrefix(ext, ".") {
ext = ext[1:]
}
return ext
}
// decryptKey 获取密码 // decryptKey 获取密码
func decryptKey(encodeKey string) []byte { func decryptKey(encodeKey string) []byte {
// 移除非法字符 // 移除非法字符

View File

@ -8,9 +8,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"path"
"strconv" "strconv"
"strings"
"github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
@ -151,7 +149,7 @@ func (d *Vtencent) ApplyUploadUGC(signature string, stream model.FileStreamer) (
form := base.Json{ form := base.Json{
"signature": signature, "signature": signature,
"videoName": stream.GetName(), "videoName": stream.GetName(),
"videoType": strings.ReplaceAll(path.Ext(stream.GetName()), ".", ""), "videoType": utils.Ext(stream.GetName()),
"videoSize": stream.GetSize(), "videoSize": stream.GetSize(),
} }
var resps RspApplyUploadUGC var resps RspApplyUploadUGC

View File

@ -619,10 +619,9 @@ type Buf struct {
// NewBuf is a buffer that can have 1 read & 1 write at the same time. // NewBuf is a buffer that can have 1 read & 1 write at the same time.
// when read is faster write, immediately feed data to read after written // when read is faster write, immediately feed data to read after written
func NewBuf(ctx context.Context, maxSize int) *Buf { func NewBuf(ctx context.Context, maxSize int) *Buf {
d := make([]byte, 0, maxSize)
return &Buf{ return &Buf{
ctx: ctx, ctx: ctx,
buffer: bytes.NewBuffer(d), buffer: bytes.NewBuffer(make([]byte, 0, maxSize)),
size: maxSize, size: maxSize,
} }
} }
@ -677,5 +676,5 @@ func (br *Buf) Write(p []byte) (n int, err error) {
} }
func (br *Buf) Close() { func (br *Buf) Close() {
br.buffer.Reset() br.buffer = nil
} }

View File

@ -45,7 +45,7 @@ func IsSubPath(path string, subPath string) bool {
func Ext(path string) string { func Ext(path string) string {
ext := stdpath.Ext(path) ext := stdpath.Ext(path)
if strings.HasPrefix(ext, ".") { if len(ext) > 0 && ext[0] == '.' {
ext = ext[1:] ext = ext[1:]
} }
return strings.ToLower(ext) return strings.ToLower(ext)

View File

@ -1,97 +1,25 @@
package common package common
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"strconv"
"strings" "strings"
"github.com/alist-org/alist/v3/internal/conf" "maps"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/net" "github.com/alist-org/alist/v3/internal/net"
"github.com/alist-org/alist/v3/internal/setting"
"github.com/alist-org/alist/v3/internal/stream" "github.com/alist-org/alist/v3/internal/stream"
"github.com/alist-org/alist/v3/pkg/http_range" "github.com/alist-org/alist/v3/pkg/http_range"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
"github.com/microcosm-cc/bluemonday"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/yuin/goldmark"
) )
func processMarkdown(content []byte) ([]byte, error) {
var buf bytes.Buffer
if err := goldmark.New().Convert(content, &buf); err != nil {
return nil, fmt.Errorf("markdown conversion failed: %w", err)
}
return bluemonday.UGCPolicy().SanitizeBytes(buf.Bytes()), nil
}
func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.Obj) error { func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.Obj) error {
//优先处理md文件
if utils.Ext(file.GetName()) == "md" && setting.GetBool(conf.FilterReadMeScripts) {
var markdownContent []byte
var err error
if link.MFile != nil {
defer link.MFile.Close()
attachHeader(w, file)
markdownContent, err = io.ReadAll(link.MFile)
if err != nil {
return fmt.Errorf("failed to read markdown content: %w", err)
}
} else if link.RangeReadCloser != nil {
attachHeader(w, file)
rrc, err := link.RangeReadCloser.RangeRead(r.Context(), http_range.Range{Start: 0, Length: -1})
if err != nil {
return err
}
defer rrc.Close()
markdownContent, err = io.ReadAll(rrc)
if err != nil {
return fmt.Errorf("failed to read markdown content: %w", err)
}
} else {
header := net.ProcessHeader(r.Header, link.Header)
res, err := net.RequestHttp(r.Context(), r.Method, header, link.URL)
if err != nil {
return err
}
defer res.Body.Close()
for h, v := range res.Header {
w.Header()[h] = v
}
w.WriteHeader(res.StatusCode)
if r.Method == http.MethodHead {
return nil
}
markdownContent, err = io.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("failed to read markdown content: %w", err)
}
}
safeHTML, err := processMarkdown(markdownContent)
if err != nil {
return err
}
safeHTMLReader := bytes.NewReader(safeHTML)
w.Header().Set("Content-Length", strconv.FormatInt(int64(len(safeHTML)), 10))
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, err = utils.CopyWithBuffer(w, safeHTMLReader)
if err != nil {
return err
}
return nil
}
if link.MFile != nil { if link.MFile != nil {
defer link.MFile.Close() defer link.MFile.Close()
attachHeader(w, file) attachHeader(w, file)
@ -152,9 +80,7 @@ func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.
} }
defer res.Body.Close() defer res.Body.Close()
for h, v := range res.Header { maps.Copy(w.Header(), res.Header)
w.Header()[h] = v
}
w.WriteHeader(res.StatusCode) w.WriteHeader(res.StatusCode)
if r.Method == http.MethodHead { if r.Method == http.MethodHead {
return nil return nil

View File

@ -1,9 +1,12 @@
package handles package handles
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"net/http"
stdpath "path" stdpath "path"
"strconv"
"strings" "strings"
"github.com/alist-org/alist/v3/internal/conf" "github.com/alist-org/alist/v3/internal/conf"
@ -15,7 +18,9 @@ import (
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
"github.com/alist-org/alist/v3/server/common" "github.com/alist-org/alist/v3/server/common"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/microcosm-cc/bluemonday"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/yuin/goldmark"
) )
func Down(c *gin.Context) { func Down(c *gin.Context) {
@ -124,7 +129,34 @@ func localProxy(c *gin.Context, link *model.Link, file model.Obj, proxyRange boo
if proxyRange { if proxyRange {
common.ProxyRange(link, file.GetSize()) common.ProxyRange(link, file.GetSize())
} }
//优先处理md文件
if utils.Ext(file.GetName()) == "md" && setting.GetBool(conf.FilterReadMeScripts) {
w := c.Writer
buf := bytes.NewBuffer(make([]byte, 0, file.GetSize()))
err = common.Proxy(&proxyResponseWriter{ResponseWriter: w, Writer: buf}, c.Request, link, file)
if err == nil && buf.Len() > 0 {
if w.Status() < 200 || w.Status() > 300 {
w.Write(buf.Bytes())
return
}
var html bytes.Buffer
if err = goldmark.Convert(buf.Bytes(), &html); err != nil {
err = fmt.Errorf("markdown conversion failed: %w", err)
} else {
buf.Reset()
err = bluemonday.UGCPolicy().SanitizeReaderToWriter(&html, buf)
if err == nil {
w.Header().Set("Content-Length", strconv.FormatInt(int64(buf.Len()), 10))
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, err = utils.CopyWithBuffer(c.Writer, buf)
}
}
}
} else {
err = common.Proxy(c.Writer, c.Request, link, file) err = common.Proxy(c.Writer, c.Request, link, file)
}
if err != nil { if err != nil {
common.ErrorResp(c, err, 500, true) common.ErrorResp(c, err, 500, true)
return return
@ -150,3 +182,12 @@ func canProxy(storage driver.Driver, filename string) bool {
} }
return false return false
} }
type proxyResponseWriter struct {
http.ResponseWriter
io.Writer
}
func (pw *proxyResponseWriter) Write(p []byte) (int, error) {
return pw.Writer.Write(p)
}