diff --git a/chfs-ng/cmd/chfs/main.go b/chfs-ng/cmd/chfs/main.go
index 65087e4..0a9cc48 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, ImagePreview: cfg.ImagePreview, FolderDownload: cfg.FolderDownload, 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, FolderDownload: cfg.FolderDownload, HTMLTitle: cfg.HTMLTitle, HTMLNotice: cfg.HTMLNotice, 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/internal/api/root_handler_test.go b/chfs-ng/internal/api/root_handler_test.go
new file mode 100644
index 0000000..ddf4b6d
--- /dev/null
+++ b/chfs-ng/internal/api/root_handler_test.go
@@ -0,0 +1,35 @@
+package api
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "chfs-ng/internal/auth"
+ "chfs-ng/internal/storage"
+)
+
+func TestRootAndAPIDoc(t *testing.T) {
+ s := &Server{Store: storage.Local{Root: t.TempDir()}, Auth: &auth.Manager{Guest: &auth.Account{Mask: auth.Read}}, HTMLTitle: "My Files", HTMLNotice: "`Hello`"}
+ h := s.Handler()
+ r := httptest.NewRecorder()
+ h.ServeHTTP(r, httptest.NewRequest(http.MethodGet, "/", nil))
+ if r.Code != 200 || !strings.Contains(r.Body.String(), "
My Files") || !strings.Contains(r.Body.String(), "Hello") {
+ t.Fatalf("root: %d %s", r.Code, r.Body.String())
+ }
+ r = httptest.NewRecorder()
+ h.ServeHTTP(r, httptest.NewRequest(http.MethodGet, "/asset/api.html", nil))
+ if r.Code != 200 || !strings.Contains(r.Body.String(), "/chfs/files") {
+ t.Fatalf("api doc: %d %s", r.Code, r.Body.String())
+ }
+}
+
+func TestRootEscapesPlainNotice(t *testing.T) {
+ s := &Server{Store: storage.Local{Root: t.TempDir()}, Auth: &auth.Manager{Guest: &auth.Account{Mask: auth.Read}}, HTMLNotice: ``}
+ r := httptest.NewRecorder()
+ s.Handler().ServeHTTP(r, httptest.NewRequest(http.MethodGet, "/", nil))
+ if strings.Contains(r.Body.String(), ``) {
+ t.Fatal("plain notice was not escaped")
+ }
+}
diff --git a/chfs-ng/internal/api/server.go b/chfs-ng/internal/api/server.go
index c224e91..a6f262c 100644
--- a/chfs-ng/internal/api/server.go
+++ b/chfs-ng/internal/api/server.go
@@ -44,6 +44,8 @@ type Server struct {
RemoveMode int
ImagePreview bool
FolderDownload string
+ HTMLTitle string
+ HTMLNotice string
Log *opslog.Logger
}
type fileInfo struct {
@@ -76,6 +78,7 @@ func (s *Server) Handler() http.Handler {
mux.HandleFunc("/chfs/shared", s.shared)
mux.HandleFunc("/chfs/shared/", s.shared)
mux.HandleFunc("/chfs/preview", s.preview)
+ mux.HandleFunc("/asset/api.html", s.apiDoc)
mux.HandleFunc("/", s.fileHandler)
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Go's ServeMux canonicalizes a subtree route before dispatching.
@@ -767,7 +770,32 @@ func (s *Server) preview(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) fileHandler(w http.ResponseWriter, r *http.Request) {
- http.NotFound(w, r)
+ if r.URL.Path != "/" {
+ http.NotFound(w, r)
+ return
+ }
+ title := s.HTMLTitle
+ if title == "" {
+ title = "CuteHttpFileServer"
+ }
+ notice := s.HTMLNotice
+ if strings.HasPrefix(notice, "`") && strings.HasSuffix(notice, "`") && len(notice) >= 2 {
+ notice = notice[1 : len(notice)-1]
+ } else {
+ notice = html.EscapeString(notice)
+ }
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ _, _ = fmt.Fprintf(w, `%s%s
%sOpen file API ยท API documentation
`, html.EscapeString(title), html.EscapeString(title), notice)
+}
+
+func (s *Server) apiDoc(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != "/asset/api.html" {
+ http.NotFound(w, r)
+ return
+ }
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ const doc = `CHFS APICHFS API
Compatibility API implemented by chfs-ng.
- /chfs/files
- /chfs/session
- /chfs/upload
- /chfs/newdir
- /chfs/rename
- /chfs/rmfiles
- /chfs/search
- /chfs/downloaddir/<path>
- /chfs/shared/<path>
- /chfs/preview
- /webdav
`
+ _, _ = w.Write([]byte(doc))
}
func (s *Server) serveFile(w http.ResponseWriter, r *http.Request, p string, attachment bool) {