fix: align CHFS API response compatibility

This commit is contained in:
OpenClaw Agent
2026-09-21 17:53:13 +08:00
parent d771c3c521
commit 3be697af9c
+49 -20
View File
@@ -45,6 +45,7 @@ type fileInfo struct {
Mask string `json:"mask"` Mask string `json:"mask"`
GuestMask string `json:"guestMask"` GuestMask string `json:"guestMask"`
HasSubDir bool `json:"hasSubDir"` HasSubDir bool `json:"hasSubDir"`
Icon string `json:"icon"`
} }
func (s *Server) Handler() http.Handler { func (s *Server) Handler() http.Handler {
@@ -104,7 +105,7 @@ func clientIPString(r *http.Request) string {
} }
func writeJSON(w http.ResponseWriter, v any) { func writeJSON(w http.ResponseWriter, v any) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(v) json.NewEncoder(w).Encode(v)
} }
@@ -128,6 +129,33 @@ func permString(p auth.PermMask) string {
} }
return "" return ""
} }
func iconFor(fi os.FileInfo) string {
if fi.IsDir() {
return "asset/icons/folder.svg"
}
ext := strings.ToLower(filepath.Ext(fi.Name()))
switch ext {
case ".txt", ".md", ".log", ".json", ".xml", ".yaml", ".yml", ".ini", ".conf", ".cfg":
return "asset/icons/txt.svg"
case ".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg":
return "asset/icons/image.svg"
case ".mp3", ".wav", ".flac", ".aac", ".m4a", ".ogg":
return "asset/icons/audio.svg"
case ".mp4", ".mkv", ".avi", ".mov", ".webm":
return "asset/icons/video.svg"
case ".zip", ".rar", ".7z", ".tar", ".gz":
return "asset/icons/archive.svg"
case ".pdf":
return "asset/icons/pdf.svg"
default:
return "asset/icons/unknown.svg"
}
}
func originalTime(t time.Time) string { return t.Format("2006-01-02 15:04:05") }
func writeCreated(w http.ResponseWriter) { w.WriteHeader(http.StatusCreated) }
func (s *Server) files(w http.ResponseWriter, r *http.Request) { func (s *Server) files(w http.ResponseWriter, r *http.Request) {
a := account(r, s) a := account(r, s)
p := qpath(r) p := qpath(r)
@@ -150,7 +178,7 @@ func (s *Server) files(w http.ResponseWriter, r *http.Request) {
sub = len(xs) > 0 sub = len(xs) > 0
} }
} }
out = append(out, fileInfo{Name: fi.Name(), Path: fp, Size: fi.Size(), Modified: fi.ModTime().Format(time.RFC3339), Dir: fi.IsDir(), Mask: permString(a.MaskFor(fp)), GuestMask: permString(s.Auth.Guest.MaskFor(fp)), HasSubDir: sub}) out = append(out, fileInfo{Name: fi.Name(), Path: fp, Size: fi.Size(), Modified: originalTime(fi.ModTime()), Dir: fi.IsDir(), Mask: permString(a.MaskFor(fp)), GuestMask: permString(s.Auth.Guest.MaskFor(fp)), HasSubDir: sub, Icon: iconFor(fi)})
} }
virtual := p == "" && s.MultiRoot virtual := p == "" && s.MultiRoot
writeJSON(w, map[string]any{"files": out, "currentMask": permString(m), "virtualRoot": virtual}) writeJSON(w, map[string]any{"files": out, "currentMask": permString(m), "virtualRoot": virtual})
@@ -182,7 +210,7 @@ func (s *Server) newdir(w http.ResponseWriter, r *http.Request) {
return return
} }
s.logOp(r, fmt.Sprintf("created directory '%s'", p)) s.logOp(r, fmt.Sprintf("created directory '%s'", p))
writeJSON(w, map[string]any{"ok": true}) writeCreated(w)
} }
func (s *Server) rename(w http.ResponseWriter, r *http.Request) { func (s *Server) rename(w http.ResponseWriter, r *http.Request) {
a := account(r, s) a := account(r, s)
@@ -197,7 +225,7 @@ func (s *Server) rename(w http.ResponseWriter, r *http.Request) {
return return
} }
s.logOp(r, fmt.Sprintf("renamed '%s' to '%s'", old, neu)) s.logOp(r, fmt.Sprintf("renamed '%s' to '%s'", old, neu))
writeJSON(w, map[string]any{"ok": true}) writeCreated(w)
} }
func (s *Server) remove(w http.ResponseWriter, r *http.Request) { func (s *Server) remove(w http.ResponseWriter, r *http.Request) {
a := account(r, s) a := account(r, s)
@@ -219,7 +247,7 @@ func (s *Server) remove(w http.ResponseWriter, r *http.Request) {
} }
s.logOp(r, fmt.Sprintf("removed '%s'", strings.Trim(p, "/"))) s.logOp(r, fmt.Sprintf("removed '%s'", strings.Trim(p, "/")))
} }
writeJSON(w, map[string]any{"ok": true}) w.WriteHeader(http.StatusNoContent)
} }
func (s *Server) removePath(p string) error { func (s *Server) removePath(p string) error {
mode := s.RemoveMode mode := s.RemoveMode
@@ -249,8 +277,13 @@ func (s *Server) removePath(p string) error {
func (s *Server) exist(w http.ResponseWriter, r *http.Request) { func (s *Server) exist(w http.ResponseWriter, r *http.Request) {
p := strings.Trim(strings.ReplaceAll(r.URL.Query().Get("file"), "\\", "/"), "/") p := strings.Trim(strings.ReplaceAll(r.URL.Query().Get("file"), "\\", "/"), "/")
_, e := s.Store.Stat(p) _, e := s.Store.Stat(p)
writeJSON(w, map[string]any{"exist": e == nil}) if e != nil {
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusCreated)
} }
func (s *Server) session(w http.ResponseWriter, r *http.Request) { func (s *Server) session(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodDelete { if r.Method == http.MethodDelete {
jwtValue, _ := r.Cookie("JWT") jwtValue, _ := r.Cookie("JWT")
@@ -317,7 +350,7 @@ func (s *Server) newtext(w http.ResponseWriter, r *http.Request) {
defer f.Close() defer f.Close()
io.WriteString(f, r.FormValue("text")) io.WriteString(f, r.FormValue("text"))
s.logOp(r, fmt.Sprintf("create a text file:'%s'", p)) s.logOp(r, fmt.Sprintf("create a text file:'%s'", p))
writeJSON(w, map[string]any{"ok": true}) writeCreated(w)
} }
func (s *Server) updatetext(w http.ResponseWriter, r *http.Request) { func (s *Server) updatetext(w http.ResponseWriter, r *http.Request) {
a := account(r, s) a := account(r, s)
@@ -339,7 +372,7 @@ func (s *Server) updatetext(w http.ResponseWriter, r *http.Request) {
return return
} }
s.logOp(r, fmt.Sprintf("update text file:'%s'", p)) s.logOp(r, fmt.Sprintf("update text file:'%s'", p))
writeJSON(w, map[string]any{"ok": true}) writeCreated(w)
} }
func (s *Server) upload(w http.ResponseWriter, r *http.Request) { func (s *Server) upload(w http.ResponseWriter, r *http.Request) {
if e := r.ParseMultipartForm(64 << 20); e != nil { if e := r.ParseMultipartForm(64 << 20); e != nil {
@@ -370,8 +403,7 @@ func (s *Server) upload(w http.ResponseWriter, r *http.Request) {
for _, name := range saved { for _, name := range saved {
s.logOp(r, fmt.Sprintf("uploaded '%s'", filepath.Base(name))) s.logOp(r, fmt.Sprintf("uploaded '%s'", filepath.Base(name)))
} }
w.WriteHeader(http.StatusCreated) writeCreated(w)
writeJSON(w, map[string]any{"ok": true, "files": saved})
} }
func savePart(st storage.Storage, target string, h *multipart.FileHeader) error { func savePart(st storage.Storage, target string, h *multipart.FileHeader) error {
in, e := h.Open() in, e := h.Open()
@@ -415,21 +447,17 @@ func (s *Server) upload2(w http.ResponseWriter, r *http.Request) {
saved = append(saved, path.Join(dir, name)) saved = append(saved, path.Join(dir, name))
} }
} }
w.WriteHeader(http.StatusCreated) writeCreated(w)
writeJSON(w, map[string]any{"ok": true, "files": saved})
} }
func (s *Server) search(w http.ResponseWriter, r *http.Request) { func (s *Server) search(w http.ResponseWriter, r *http.Request) {
a := account(r, s) a := account(r, s)
if !a.MaskFor("").HasRead() { rootMask := a.MaskFor("")
if !rootMask.HasRead() {
http.Error(w, "forbidden", 403) http.Error(w, "forbidden", 403)
return return
} }
needle := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("str"))) needle := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("str")))
if needle == "" {
writeJSON(w, map[string]any{"files": []fileInfo{}})
return
}
var out []fileInfo var out []fileInfo
var walk func(string) error var walk func(string) error
walk = func(dir string) error { walk = func(dir string) error {
@@ -442,8 +470,8 @@ func (s *Server) search(w http.ResponseWriter, r *http.Request) {
if !a.MaskFor(p).HasRead() { if !a.MaskFor(p).HasRead() {
continue continue
} }
if strings.Contains(strings.ToLower(fi.Name()), needle) { if needle == "" || strings.Contains(strings.ToLower(fi.Name()), needle) {
out = append(out, fileInfo{Name: fi.Name(), Path: p, Size: fi.Size(), Modified: fi.ModTime().Format(time.RFC3339), Dir: fi.IsDir(), Mask: permString(a.MaskFor(p)), GuestMask: permString(s.Auth.Guest.MaskFor(p))}) out = append(out, fileInfo{Name: fi.Name(), Path: "/" + strings.Trim(p, "/"), Size: fi.Size(), Modified: originalTime(fi.ModTime()), Dir: fi.IsDir(), Mask: permString(a.MaskFor(p)), GuestMask: permString(s.Auth.Guest.MaskFor(p)), HasSubDir: false, Icon: iconFor(fi)})
} }
if fi.IsDir() { if fi.IsDir() {
if err := walk(p); err != nil { if err := walk(p); err != nil {
@@ -457,8 +485,9 @@ func (s *Server) search(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
} }
writeJSON(w, map[string]any{"files": out}) writeJSON(w, map[string]any{"virtualRoot": 0, "currentMask": permString(rootMask), "files": out})
} }
func (s *Server) shared(w http.ResponseWriter, r *http.Request) { func (s *Server) shared(w http.ResponseWriter, r *http.Request) {
file := strings.Trim(r.URL.Query().Get("file"), "/") file := strings.Trim(r.URL.Query().Get("file"), "/")
if file == "" { if file == "" {