feat: restore configurable root page and api docs
This commit is contained in:
@@ -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 != "" {
|
||||
|
||||
@@ -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: "`<strong>Hello</strong>`"}
|
||||
h := s.Handler()
|
||||
r := httptest.NewRecorder()
|
||||
h.ServeHTTP(r, httptest.NewRequest(http.MethodGet, "/", nil))
|
||||
if r.Code != 200 || !strings.Contains(r.Body.String(), "<title>My Files</title>") || !strings.Contains(r.Body.String(), "<strong>Hello</strong>") {
|
||||
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: `<script>alert(1)</script>`}
|
||||
r := httptest.NewRecorder()
|
||||
s.Handler().ServeHTTP(r, httptest.NewRequest(http.MethodGet, "/", nil))
|
||||
if strings.Contains(r.Body.String(), `<script>alert(1)</script>`) {
|
||||
t.Fatal("plain notice was not escaped")
|
||||
}
|
||||
}
|
||||
@@ -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, `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>%s</title></head><body><main><h1>%s</h1>%s<p><a href="/chfs/files?filepath=">Open file API</a> · <a href="/asset/api.html">API documentation</a></p></main></body></html>`, 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 = `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>CHFS API</title></head><body><h1>CHFS API</h1><p>Compatibility API implemented by chfs-ng.</p><ul><li>/chfs/files</li><li>/chfs/session</li><li>/chfs/upload</li><li>/chfs/newdir</li><li>/chfs/rename</li><li>/chfs/rmfiles</li><li>/chfs/search</li><li>/chfs/downloaddir/<path></li><li>/chfs/shared/<path></li><li>/chfs/preview</li><li>/webdav</li></ul></body></html>`
|
||||
_, _ = w.Write([]byte(doc))
|
||||
}
|
||||
|
||||
func (s *Server) serveFile(w http.ResponseWriter, r *http.Request, p string, attachment bool) {
|
||||
|
||||
Reference in New Issue
Block a user