feat: complete guest shared download flow

This commit is contained in:
OpenClaw Agent
2026-09-22 04:55:52 +08:00
parent 73c05ef112
commit d8c938a0c8
3 changed files with 100 additions and 15 deletions
+32 -9
View File
@@ -14,6 +14,7 @@ import (
_ "image/jpeg"
"image/png"
"io"
"mime"
"mime/multipart"
"net"
"net/http"
@@ -1039,6 +1040,18 @@ func (s *Server) search(w http.ResponseWriter, r *http.Request) {
writeJSON(w, map[string]any{"virtualRoot": 0, "currentMask": permString(rootMask), "files": out})
}
func sharedPath(parts ...string) string {
encoded := make([]string, 0, len(parts))
for _, part := range parts {
encoded = append(encoded, url.PathEscape(part))
}
return "/chfs/shared/" + strings.Join(encoded, "/")
}
func attachmentDisposition(name string) string {
return mime.FormatMediaType("attachment", map[string]string{"filename": name})
}
func (s *Server) shared(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/chfs/shared" {
location := "shared/"
@@ -1061,33 +1074,43 @@ func (s *Server) shared(w http.ResponseWriter, r *http.Request) {
return
}
if fi.IsDir() {
w.Header().Set("Content-Disposition", `attachment;filename="shared"`)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Cache-Control", "private, no-cache")
w.Header().Set("Last-Modified", fi.ModTime().UTC().Format(http.TimeFormat))
if r.Method == http.MethodHead {
return
}
if r.Method != http.MethodGet {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
items, err := s.Store.List(p)
if err != nil {
http.NotFound(w, r)
return
}
_, _ = io.WriteString(w, "<pre>\n")
_, _ = io.WriteString(w, "<!doctype html><meta charset=\"utf-8\"><title>CHFS Shared</title><style>body{font:15px system-ui,sans-serif;max-width:760px;margin:40px auto;padding:0 18px}a{display:block;padding:10px 0;text-decoration:none}</style><h2>共享文件</h2><p>"+html.EscapeString(p)+"</p>")
for _, item := range items {
name := item.Name()
href := url.PathEscape(name)
child := path.Join(p, item.Name())
if !account(r, s).MaskForEntry(child).HasRead() {
continue
}
href := sharedPath(strings.Split(strings.Trim(child, "/"), "/")...)
if item.IsDir() {
href += "/"
}
_, _ = fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", href, html.EscapeString(name)+func() string {
_, _ = fmt.Fprintf(w, "<a href=\"%s\">%s%s</a>", html.EscapeString(href), html.EscapeString(item.Name()), func() string {
if item.IsDir() {
return "/"
}
return ""
}())
}
_, _ = io.WriteString(w, "</pre>\n")
_, _ = io.WriteString(w, "</body></html>")
return
}
if r.Method != http.MethodGet {
http.NotFound(w, r)
if r.Method != http.MethodGet && r.Method != http.MethodHead {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
f, err := s.Store.Open(p)
@@ -1096,7 +1119,7 @@ func (s *Server) shared(w http.ResponseWriter, r *http.Request) {
return
}
defer f.Close()
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment;filename="%s"`, fi.Name()))
w.Header().Set("Content-Disposition", attachmentDisposition(fi.Name()))
http.ServeContent(w, r, fi.Name(), fi.ModTime(), f)
}