diff --git a/chfs-ng/internal/api/server.go b/chfs-ng/internal/api/server.go index a662119..6479e09 100644 --- a/chfs-ng/internal/api/server.go +++ b/chfs-ng/internal/api/server.go @@ -45,6 +45,7 @@ type fileInfo struct { Mask string `json:"mask"` GuestMask string `json:"guestMask"` HasSubDir bool `json:"hasSubDir"` + Icon string `json:"icon"` } func (s *Server) Handler() http.Handler { @@ -104,7 +105,7 @@ func clientIPString(r *http.Request) string { } 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) } @@ -128,6 +129,33 @@ func permString(p auth.PermMask) string { } 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) { a := account(r, s) p := qpath(r) @@ -150,7 +178,7 @@ func (s *Server) files(w http.ResponseWriter, r *http.Request) { 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 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 } 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) { a := account(r, s) @@ -197,7 +225,7 @@ func (s *Server) rename(w http.ResponseWriter, r *http.Request) { return } 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) { 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, "/"))) } - writeJSON(w, map[string]any{"ok": true}) + w.WriteHeader(http.StatusNoContent) } func (s *Server) removePath(p string) error { mode := s.RemoveMode @@ -249,8 +277,13 @@ func (s *Server) removePath(p string) error { func (s *Server) exist(w http.ResponseWriter, r *http.Request) { p := strings.Trim(strings.ReplaceAll(r.URL.Query().Get("file"), "\\", "/"), "/") _, 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) { if r.Method == http.MethodDelete { jwtValue, _ := r.Cookie("JWT") @@ -317,7 +350,7 @@ func (s *Server) newtext(w http.ResponseWriter, r *http.Request) { defer f.Close() io.WriteString(f, r.FormValue("text")) 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) { a := account(r, s) @@ -339,7 +372,7 @@ func (s *Server) updatetext(w http.ResponseWriter, r *http.Request) { return } 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) { 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 { s.logOp(r, fmt.Sprintf("uploaded '%s'", filepath.Base(name))) } - w.WriteHeader(http.StatusCreated) - writeJSON(w, map[string]any{"ok": true, "files": saved}) + writeCreated(w) } func savePart(st storage.Storage, target string, h *multipart.FileHeader) error { 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)) } } - w.WriteHeader(http.StatusCreated) - writeJSON(w, map[string]any{"ok": true, "files": saved}) + writeCreated(w) } func (s *Server) search(w http.ResponseWriter, r *http.Request) { a := account(r, s) - if !a.MaskFor("").HasRead() { + rootMask := a.MaskFor("") + if !rootMask.HasRead() { http.Error(w, "forbidden", 403) return } needle := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("str"))) - if needle == "" { - writeJSON(w, map[string]any{"files": []fileInfo{}}) - return - } var out []fileInfo var walk func(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() { continue } - if 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))}) + if needle == "" || strings.Contains(strings.ToLower(fi.Name()), needle) { + 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 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) 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) { file := strings.Trim(r.URL.Query().Get("file"), "/") if file == "" {