feat: add mobile transfer and drag upload
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/skip2/go-qrcode"
|
||||
"html"
|
||||
"image"
|
||||
_ "image/gif"
|
||||
@@ -85,6 +86,8 @@ func (s *Server) Handler() http.Handler {
|
||||
mux.HandleFunc("/chfs/upload2", s.upload2)
|
||||
mux.HandleFunc("/chfs/search", s.search)
|
||||
mux.HandleFunc("/chfs/downloaddir/", s.downloadDir)
|
||||
mux.HandleFunc("/chfs/downloadzip", s.downloadSelectedZip)
|
||||
mux.HandleFunc("/chfs/qrcode", s.qrcode)
|
||||
mux.HandleFunc("/chfs/shared", s.shared)
|
||||
mux.HandleFunc("/chfs/shared/", s.shared)
|
||||
mux.HandleFunc("/chfs/preview", s.preview)
|
||||
@@ -1093,6 +1096,100 @@ func (s *Server) serveFile(w http.ResponseWriter, r *http.Request, p string, att
|
||||
http.ServeContent(w, r, fi.Name(), fi.ModTime(), f)
|
||||
}
|
||||
|
||||
func (s *Server) qrcode(w http.ResponseWriter, r *http.Request) {
|
||||
u := r.URL.Query().Get("url")
|
||||
if u == "" || len(u) > 4096 {
|
||||
writeJSONError(w, http.StatusBadRequest, "invalid url")
|
||||
return
|
||||
}
|
||||
data, err := qrcode.Encode(u, qrcode.Medium, 320)
|
||||
if err != nil {
|
||||
writeJSONError(w, http.StatusInternalServerError, "qrcode failed")
|
||||
return
|
||||
}
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
_, _ = w.Write(data)
|
||||
}
|
||||
|
||||
func (s *Server) downloadSelectedZip(w http.ResponseWriter, r *http.Request) {
|
||||
a := account(r, s)
|
||||
paths := r.URL.Query()["path"]
|
||||
if len(paths) == 0 || len(paths) > 100 {
|
||||
writeJSONError(w, http.StatusBadRequest, "invalid selection")
|
||||
return
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for i, raw := range paths {
|
||||
p := strings.Trim(strings.ReplaceAll(raw, "\\", "/"), "/")
|
||||
if p == "" || unsafePath(p) || seen[p] {
|
||||
writeJSONError(w, http.StatusBadRequest, "invalid selection")
|
||||
return
|
||||
}
|
||||
seen[p] = true
|
||||
paths[i] = p
|
||||
if !a.MaskFor(p).HasRead() {
|
||||
writeJSONError(w, http.StatusForbidden, "has no privilege")
|
||||
return
|
||||
}
|
||||
if _, err := s.Store.Stat(p); err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/zip")
|
||||
w.Header().Set("Content-Disposition", `attachment; filename="download.zip"`)
|
||||
zw := zip.NewWriter(w)
|
||||
defer zw.Close()
|
||||
added := map[string]bool{}
|
||||
var walk func(string, string) error
|
||||
walk = func(p, prefix string) error {
|
||||
fi, err := s.Store.Stat(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
items, err := s.Store.List(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, it := range items {
|
||||
child := path.Join(p, it.Name())
|
||||
if !a.MaskFor(child).HasRead() {
|
||||
continue
|
||||
}
|
||||
if err := walk(child, path.Join(prefix, it.Name())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if added[prefix] {
|
||||
return nil
|
||||
}
|
||||
added[prefix] = true
|
||||
f, err := s.Store.Open(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
h := &zip.FileHeader{Name: prefix, Method: zip.Deflate}
|
||||
h.SetModTime(fi.ModTime())
|
||||
wz, err := zw.CreateHeader(h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(wz, f)
|
||||
return err
|
||||
}
|
||||
for _, p := range paths {
|
||||
if err := walk(p, path.Base(p)); err != nil {
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) downloadDir(w http.ResponseWriter, r *http.Request) {
|
||||
a := account(r, s)
|
||||
rawPath := strings.TrimPrefix(r.URL.Path, "/chfs/downloaddir/")
|
||||
|
||||
Reference in New Issue
Block a user