86 lines
2.2 KiB
Go
86 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type treeEntry struct {
|
|
Name string `json:"name"`
|
|
Kind string `json:"kind"`
|
|
}
|
|
|
|
type treeResponse struct {
|
|
Path string `json:"path"`
|
|
Entries []treeEntry `json:"entries"`
|
|
}
|
|
|
|
// handleTree responds with a JSON listing of the immediate children of the
|
|
// folder at fsPath. Hidden entries and `index.md` are filtered. Files are not
|
|
// descended — the client lazy-loads children on expand.
|
|
func (h *handler) handleTree(w http.ResponseWriter, r *http.Request, urlPath, fsPath string) {
|
|
info, err := os.Stat(fsPath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
http.Error(w, "stat failed", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if !info.IsDir() {
|
|
http.Error(w, "not a folder", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
entries, err := listTreeEntries(fsPath)
|
|
if err != nil {
|
|
http.Error(w, "read failed", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
resp := treeResponse{Path: canonicalTreePath(urlPath), Entries: entries}
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
_ = json.NewEncoder(w).Encode(resp)
|
|
}
|
|
|
|
// canonicalTreePath returns the URL path in the form used by the picker:
|
|
// "/" for root, otherwise stripped of any trailing slash.
|
|
func canonicalTreePath(urlPath string) string {
|
|
if urlPath == "" || urlPath == "/" {
|
|
return "/"
|
|
}
|
|
return "/" + strings.Trim(urlPath, "/")
|
|
}
|
|
|
|
// listTreeEntries returns the immediate children of fsPath, filtering hidden
|
|
// entries and index.md. Folders are listed before files; both groups are
|
|
// sorted alphabetically.
|
|
func listTreeEntries(fsPath string) ([]treeEntry, error) {
|
|
raw, err := os.ReadDir(fsPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var folders, files []treeEntry
|
|
for _, e := range raw {
|
|
name := e.Name()
|
|
if strings.HasPrefix(name, ".") {
|
|
continue
|
|
}
|
|
if e.IsDir() {
|
|
folders = append(folders, treeEntry{Name: name, Kind: "folder"})
|
|
} else {
|
|
if name == "index.md" {
|
|
continue
|
|
}
|
|
files = append(files, treeEntry{Name: name, Kind: "file"})
|
|
}
|
|
}
|
|
sort.Slice(folders, func(i, j int) bool { return folders[i].Name < folders[j].Name })
|
|
sort.Slice(files, func(i, j int) bool { return files[i].Name < files[j].Name })
|
|
return append(folders, files...), nil
|
|
}
|