feat: implement configurable delete modes
This commit is contained in:
@@ -67,9 +67,9 @@ func main() {
|
|||||||
if len(cfg.Path) == 1 {
|
if len(cfg.Path) == 1 {
|
||||||
store = storage.Local{Root: root}
|
store = storage.Local{Root: root}
|
||||||
}
|
}
|
||||||
srv := &api.Server{Store: store, Auth: mgr, Sessions: map[string]*auth.Account{}, WebDAVRoot: root, Allow: cfg.Allow, MultiRoot: len(cfg.Path) > 1}
|
srv := &api.Server{Store: store, Auth: mgr, Sessions: map[string]*auth.Account{}, WebDAVRoot: root, Allow: cfg.Allow, MultiRoot: len(cfg.Path) > 1, RemoveMode: cfg.FileRemove}
|
||||||
addr := ":" + itoa(cfg.Port)
|
addr := ":" + itoa(cfg.Port)
|
||||||
log.Printf("chfs-ng listening on %s root=%s", addr, filepath.Clean(root))
|
log.Printf("chfs-ng listening on %s root=%s removeMode=%d", addr, filepath.Clean(root), cfg.FileRemove)
|
||||||
if cfg.SSLCert != "" && cfg.SSLKey != "" {
|
if cfg.SSLCert != "" && cfg.SSLKey != "" {
|
||||||
log.Fatal(http.ListenAndServeTLS(addr, cfg.SSLCert, cfg.SSLKey, srv.Handler()))
|
log.Fatal(http.ListenAndServeTLS(addr, cfg.SSLCert, cfg.SSLKey, srv.Handler()))
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -2,4 +2,9 @@ module chfs-ng
|
|||||||
|
|
||||||
go 1.22
|
go 1.22
|
||||||
|
|
||||||
require github.com/emersion/go-webdav v0.7.0
|
require (
|
||||||
|
github.com/Bios-Marcel/wastebasket v0.0.3
|
||||||
|
github.com/emersion/go-webdav v0.7.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.3.0 // indirect
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
github.com/Bios-Marcel/wastebasket v0.0.3 h1:gyuMcoDav6n+sdynn9TDl1NN7GOAnoy227JErVSW3RM=
|
||||||
|
github.com/Bios-Marcel/wastebasket v0.0.3/go.mod h1:FChzXi1izqzdPb6BiNZmcZLGyTYiT61iGx9Rxx9GNeI=
|
||||||
github.com/emersion/go-ical v0.0.0-20240127095438-fc1c9d8fb2b6/go.mod h1:BEksegNspIkjCQfmzWgsgbu6KdeJ/4LwUZs7DMBzjzw=
|
github.com/emersion/go-ical v0.0.0-20240127095438-fc1c9d8fb2b6/go.mod h1:BEksegNspIkjCQfmzWgsgbu6KdeJ/4LwUZs7DMBzjzw=
|
||||||
github.com/emersion/go-vcard v0.0.0-20230815062825-8fda7d206ec9/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM=
|
github.com/emersion/go-vcard v0.0.0-20230815062825-8fda7d206ec9/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM=
|
||||||
github.com/emersion/go-webdav v0.7.0 h1:cp6aBWXBf8Sjzguka9VJarr4XTkGc2IHxXI1Gq3TKpA=
|
github.com/emersion/go-webdav v0.7.0 h1:cp6aBWXBf8Sjzguka9VJarr4XTkGc2IHxXI1Gq3TKpA=
|
||||||
github.com/emersion/go-webdav v0.7.0/go.mod h1:mI8iBx3RAODwX7PJJ7qzsKAKs/vY429YfS2/9wKnDbQ=
|
github.com/emersion/go-webdav v0.7.0/go.mod h1:mI8iBx3RAODwX7PJJ7qzsKAKs/vY429YfS2/9wKnDbQ=
|
||||||
github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4=
|
github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4=
|
||||||
|
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -25,6 +26,7 @@ type Server struct {
|
|||||||
WebDAVRoot string
|
WebDAVRoot string
|
||||||
Allow string
|
Allow string
|
||||||
MultiRoot bool
|
MultiRoot bool
|
||||||
|
RemoveMode int
|
||||||
}
|
}
|
||||||
type fileInfo struct {
|
type fileInfo struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -156,6 +158,7 @@ func (s *Server) rename(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
func (s *Server) remove(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) remove(w http.ResponseWriter, r *http.Request) {
|
||||||
a := account(r, s)
|
a := account(r, s)
|
||||||
|
_ = r.ParseMultipartForm(16 << 20)
|
||||||
_ = r.ParseForm()
|
_ = r.ParseForm()
|
||||||
items := r.Form["filepath"]
|
items := r.Form["filepath"]
|
||||||
if len(items) == 0 {
|
if len(items) == 0 {
|
||||||
@@ -166,13 +169,38 @@ func (s *Server) remove(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "forbidden", 403)
|
http.Error(w, "forbidden", 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if e := s.Store.Remove(p); e != nil {
|
if e := s.removePath(p); e != nil {
|
||||||
http.Error(w, e.Error(), 500)
|
http.Error(w, e.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeJSON(w, map[string]any{"ok": true})
|
writeJSON(w, map[string]any{"ok": true})
|
||||||
}
|
}
|
||||||
|
func (s *Server) removePath(p string) error {
|
||||||
|
mode := s.RemoveMode
|
||||||
|
if mode == 0 {
|
||||||
|
mode = 1
|
||||||
|
}
|
||||||
|
t, ok := s.Store.(storage.Trashable)
|
||||||
|
switch mode {
|
||||||
|
case 1:
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("storage does not support system trash")
|
||||||
|
}
|
||||||
|
return t.Trash(p)
|
||||||
|
case 2:
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return t.MoveToTrash(p, filepath.Join(home, ".chfs_trashbin"))
|
||||||
|
case 3:
|
||||||
|
return s.Store.Remove(p)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid file.remove mode: %d", mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) exist(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) exist(w http.ResponseWriter, r *http.Request) {
|
||||||
p := strings.Trim(strings.ReplaceAll(r.URL.Query().Get("file"), "\\", "/"), "/")
|
p := strings.Trim(strings.ReplaceAll(r.URL.Query().Get("file"), "\\", "/"), "/")
|
||||||
_, e := s.Store.Stat(p)
|
_, e := s.Store.Stat(p)
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func ParseRules(values []string) (*Manager, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if m.Guest == nil {
|
if m.Guest == nil {
|
||||||
m.Guest = &Account{Mask: Read | Write}
|
m.Guest = &Account{Mask: Read | Write | Delete}
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,31 @@ func (m Multi) Remove(p string) error {
|
|||||||
}
|
}
|
||||||
return r.Remove(rel)
|
return r.Remove(rel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m Multi) Trash(p string) error {
|
||||||
|
r, rel, ok := m.find(p)
|
||||||
|
if !ok {
|
||||||
|
return os.ErrNotExist
|
||||||
|
}
|
||||||
|
return r.Trash(rel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Multi) MoveToTrash(p, trashRoot string) error {
|
||||||
|
r, rel, ok := m.find(p)
|
||||||
|
if !ok {
|
||||||
|
return os.ErrNotExist
|
||||||
|
}
|
||||||
|
return r.MoveToTrash(rel, filepath.Join(trashRoot, m.RootName(r)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Multi) RootName(r Local) string {
|
||||||
|
for i := range m.Roots {
|
||||||
|
if m.Roots[i].Root == r.Root {
|
||||||
|
return m.Names[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "root"
|
||||||
|
}
|
||||||
func (m Multi) Rename(a, b string) error {
|
func (m Multi) Rename(a, b string) error {
|
||||||
ra, aa, oka := m.find(a)
|
ra, aa, oka := m.find(a)
|
||||||
rb, bb, okb := m.find(b)
|
rb, bb, okb := m.find(b)
|
||||||
|
|||||||
@@ -2,12 +2,20 @@ package storage
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Bios-Marcel/wastebasket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Trashable interface {
|
||||||
|
Trash(path string) error
|
||||||
|
MoveToTrash(path, trashRoot string) error
|
||||||
|
}
|
||||||
|
|
||||||
type Storage interface {
|
type Storage interface {
|
||||||
Stat(path string) (os.FileInfo, error)
|
Stat(path string) (os.FileInfo, error)
|
||||||
Open(path string) (*os.File, error)
|
Open(path string) (*os.File, error)
|
||||||
@@ -67,6 +75,33 @@ func (s Local) Remove(path string) error {
|
|||||||
}
|
}
|
||||||
return os.RemoveAll(p)
|
return os.RemoveAll(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Local) Trash(path string) error {
|
||||||
|
p, e := s.real(path)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return wastebasket.Trash(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Local) MoveToTrash(path, trashRoot string) error {
|
||||||
|
p, e := s.real(path)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if e = os.MkdirAll(trashRoot, 0755); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
name := filepath.Base(p)
|
||||||
|
target := filepath.Join(trashRoot, name)
|
||||||
|
for i := 1; ; i++ {
|
||||||
|
if _, e = os.Lstat(target); os.IsNotExist(e) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
target = filepath.Join(trashRoot, fmt.Sprintf("%s.%d", name, i))
|
||||||
|
}
|
||||||
|
return os.Rename(p, target)
|
||||||
|
}
|
||||||
func (s Local) Rename(a, b string) error {
|
func (s Local) Rename(a, b string) error {
|
||||||
pa, e := s.real(a)
|
pa, e := s.real(a)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user