From 871e72277cc4619254226f3aa26a42182bf05469 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Mon, 21 Sep 2026 18:08:35 +0800 Subject: [PATCH] fix: match CHFS preview and listing behavior --- chfs-ng/cmd/chfs/main.go | 2 +- chfs-ng/go.mod | 1 + chfs-ng/go.sum | 2 ++ chfs-ng/internal/api/server.go | 44 ++++++++++++++++++++++++----- chfs-ng/internal/storage/storage.go | 8 +++++- 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/chfs-ng/cmd/chfs/main.go b/chfs-ng/cmd/chfs/main.go index 2b8a90a..2b288c4 100644 --- a/chfs-ng/cmd/chfs/main.go +++ b/chfs-ng/cmd/chfs/main.go @@ -89,7 +89,7 @@ func main() { if len(cfg.Path) == 1 { store = storage.Local{Root: root} } - srv := &api.Server{Store: store, Auth: mgr, Sessions: map[string]*auth.Account{}, SessionExpiry: map[string]time.Time{}, SessionTimeout: cfg.SessionTimeout, WebDAVRoot: root, Allow: cfg.Allow, MultiRoot: len(cfg.Path) > 1, RemoveMode: cfg.FileRemove, Log: opslog.New(cfg.Log)} + srv := &api.Server{Store: store, Auth: mgr, Sessions: map[string]*auth.Account{}, SessionExpiry: map[string]time.Time{}, SessionTimeout: cfg.SessionTimeout, WebDAVRoot: root, Allow: cfg.Allow, MultiRoot: len(cfg.Path) > 1, RemoveMode: cfg.FileRemove, ImagePreview: cfg.ImagePreview, Log: opslog.New(cfg.Log)} addr := ":" + itoa(cfg.Port) log.Printf("chfs-ng listening on %s root=%s removeMode=%d", addr, filepath.Clean(root), cfg.FileRemove) if cfg.SSLCert != "" && cfg.SSLKey != "" { diff --git a/chfs-ng/go.mod b/chfs-ng/go.mod index 03b3c93..bef9fda 100644 --- a/chfs-ng/go.mod +++ b/chfs-ng/go.mod @@ -4,6 +4,7 @@ go 1.22 require ( github.com/Bios-Marcel/wastebasket v0.0.3 + github.com/anthonynsimon/bild v0.14.0 github.com/emersion/go-webdav v0.7.0 github.com/golang-jwt/jwt/v4 v4.5.2 ) diff --git a/chfs-ng/go.sum b/chfs-ng/go.sum index e92bb1f..ab668a5 100644 --- a/chfs-ng/go.sum +++ b/chfs-ng/go.sum @@ -1,5 +1,7 @@ github.com/Bios-Marcel/wastebasket v0.0.3 h1:gyuMcoDav6n+sdynn9TDl1NN7GOAnoy227JErVSW3RM= github.com/Bios-Marcel/wastebasket v0.0.3/go.mod h1:FChzXi1izqzdPb6BiNZmcZLGyTYiT61iGx9Rxx9GNeI= +github.com/anthonynsimon/bild v0.14.0 h1:IFRkmKdNdqmexXHfEU7rPlAmdUZ8BDZEGtGHDnGWync= +github.com/anthonynsimon/bild v0.14.0/go.mod h1:hcvEAyBjTW69qkKJTfpcDQ83sSZHxwOunsseDfeQhUs= github.com/emersion/go-ical v0.0.0-20240127095438-fc1c9d8fb2b6/go.mod h1:BEksegNspIkjCQfmzWgsgbu6KdeJ/4LwUZs7DMBzjzw= github.com/emersion/go-vcard v0.0.0-20230815062825-8fda7d206ec9/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM= github.com/emersion/go-webdav v0.7.0 h1:cp6aBWXBf8Sjzguka9VJarr4XTkGc2IHxXI1Gq3TKpA= diff --git a/chfs-ng/internal/api/server.go b/chfs-ng/internal/api/server.go index 94e5c29..c3e8ac1 100644 --- a/chfs-ng/internal/api/server.go +++ b/chfs-ng/internal/api/server.go @@ -2,9 +2,14 @@ package api import ( "archive/zip" + "bytes" "context" "encoding/json" "fmt" + "image" + _ "image/gif" + _ "image/jpeg" + "image/png" "io" "mime/multipart" "net" @@ -19,6 +24,7 @@ import ( "chfs-ng/internal/auth" "chfs-ng/internal/opslog" "chfs-ng/internal/storage" + "github.com/anthonynsimon/bild/transform" "github.com/emersion/go-webdav" ) @@ -34,6 +40,7 @@ type Server struct { Allow string MultiRoot bool RemoveMode int + ImagePreview bool Log *opslog.Logger } type fileInfo struct { @@ -463,7 +470,7 @@ func (s *Server) search(w http.ResponseWriter, r *http.Request) { return } needle := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("str"))) - var out []fileInfo + out := make([]fileInfo, 0) var walk func(string) error walk = func(dir string) error { items, err := s.Store.List(dir) @@ -502,16 +509,39 @@ func (s *Server) shared(w http.ResponseWriter, r *http.Request) { } func (s *Server) preview(w http.ResponseWriter, r *http.Request) { - if !account(r, s).MaskFor(strings.Trim(r.URL.Query().Get("file"), "/")).HasRead() { - http.Error(w, "forbidden", 403) - return - } - file := strings.Trim(r.URL.Query().Get("file"), "/") + file := strings.Trim(strings.ReplaceAll(r.URL.Query().Get("file"), "\\", "/"), "/") if file == "" { http.Error(w, "file required", 400) return } - s.serveFile(w, r, file, false) + if !account(r, s).MaskFor(file).HasRead() { + http.Error(w, "forbidden", 403) + return + } + if !s.ImagePreview { + writeJSONError(w, http.StatusInternalServerError, "Open image file fail!") + return + } + f, err := s.Store.Open(file) + if err != nil { + writeJSONError(w, http.StatusInternalServerError, "Open image file fail!") + return + } + defer f.Close() + src, _, err := image.Decode(f) + if err != nil { + writeJSONError(w, http.StatusInternalServerError, "Open image file fail!") + return + } + thumb := transform.Resize(src, 64, 64, transform.Linear) + var buf bytes.Buffer + if err := png.Encode(&buf, thumb); err != nil { + writeJSONError(w, http.StatusInternalServerError, "Open image file fail!") + return + } + w.Header().Set("Cache-Control", "max-age=2592000") + w.Header().Set("Content-Type", "image/png") + _, _ = w.Write(buf.Bytes()) } func (s *Server) fileHandler(w http.ResponseWriter, r *http.Request) { diff --git a/chfs-ng/internal/storage/storage.go b/chfs-ng/internal/storage/storage.go index e260cb4..e78bb05 100644 --- a/chfs-ng/internal/storage/storage.go +++ b/chfs-ng/internal/storage/storage.go @@ -7,6 +7,7 @@ import ( "io/fs" "os" "path/filepath" + "sort" "strings" "syscall" "time" @@ -258,7 +259,12 @@ func (s Local) List(path string) ([]os.FileInfo, error) { return nil, e } defer f.Close() - return f.Readdir(-1) + items, err := f.Readdir(-1) + if err != nil { + return nil, err + } + sort.Slice(items, func(i, j int) bool { return items[i].Name() < items[j].Name() }) + return items, nil } var _ fs.FS