Add trash: deleted files go to trash, restore or empty

This commit is contained in:
2026-05-22 21:12:29 +02:00
parent 88eebf6944
commit 1433890a4c
4 changed files with 134 additions and 5 deletions
+38
View File
@@ -286,3 +286,41 @@ func (s *Server) handleSharedFiles(w http.ResponseWriter, r *http.Request) {
// For now return empty list
writeJSON(w, 200, []files.FileInfo{})
}
func (s *Server) handleListTrash(w http.ResponseWriter, r *http.Request) {
userID := getUserID(r)
items, err := files.ListTrash(s.dataDir, userID)
if err != nil {
writeJSON(w, 500, map[string]string{"error": "failed to list trash"})
return
}
if items == nil {
items = []files.FileInfo{}
}
writeJSON(w, 200, items)
}
func (s *Server) handleRestoreTrash(w http.ResponseWriter, r *http.Request) {
var req struct {
Name string `json:"name"`
}
if err := decodeBody(r, &req); err != nil || req.Name == "" {
writeJSON(w, 400, map[string]string{"error": "name required"})
return
}
userID := getUserID(r)
if err := files.RestoreFromTrash(s.dataDir, userID, req.Name); err != nil {
writeJSON(w, 500, map[string]string{"error": "restore failed"})
return
}
writeJSON(w, 200, map[string]string{"status": "restored"})
}
func (s *Server) handleEmptyTrash(w http.ResponseWriter, r *http.Request) {
userID := getUserID(r)
if err := files.EmptyTrash(s.dataDir, userID); err != nil {
writeJSON(w, 500, map[string]string{"error": "empty trash failed"})
return
}
writeJSON(w, 200, map[string]string{"status": "emptied"})
}
+3
View File
@@ -36,6 +36,9 @@ func NewRouter(db *sql.DB, dataDir, secret string) http.Handler {
mux.HandleFunc("POST /api/files/create-folder", s.requireAuth(s.handleCreateFolder))
mux.HandleFunc("POST /api/files/delete", s.requireAuth(s.handleDeleteFile))
mux.HandleFunc("POST /api/files/move", s.requireAuth(s.handleMoveFile))
mux.HandleFunc("POST /api/files/trash", s.requireAuth(s.handleListTrash))
mux.HandleFunc("POST /api/files/trash/restore", s.requireAuth(s.handleRestoreTrash))
mux.HandleFunc("POST /api/files/trash/empty", s.requireAuth(s.handleEmptyTrash))
mux.HandleFunc("POST /api/files/search", s.requireAuth(s.handleSearchFiles))
mux.HandleFunc("POST /api/files/shared", s.requireAuth(s.handleListSharedFiles))
+49 -2
View File
@@ -1,6 +1,7 @@
package files
import (
"fmt"
"os"
"path/filepath"
"strings"
@@ -54,13 +55,56 @@ func CreateFolder(dataDir, userID, relPath string) error {
return os.MkdirAll(p, 0755)
}
// DeleteFile removes a file or folder for a user.
// DeleteFile moves a file or folder to trash.
func DeleteFile(dataDir, userID, relPath string) error {
p := safePath(dataDir, userID, relPath)
if p == "" {
return os.ErrPermission
}
return os.RemoveAll(p)
trashDir := filepath.Join(UserDir(dataDir, userID), ".trash")
if err := os.MkdirAll(trashDir, 0755); err != nil {
return err
}
dest := filepath.Join(trashDir, filepath.Base(relPath))
// If already exists in trash, add timestamp
if _, err := os.Stat(dest); err == nil {
dest = dest + "." + fmt.Sprintf("%d", os.Getpid())
}
return os.Rename(p, dest)
}
// ListTrash returns files in the user's trash.
func ListTrash(dataDir, userID string) ([]FileInfo, error) {
trashDir := filepath.Join(UserDir(dataDir, userID), ".trash")
if err := os.MkdirAll(trashDir, 0755); err != nil {
return nil, err
}
entries, err := os.ReadDir(trashDir)
if err != nil {
return nil, err
}
var result []FileInfo
for _, e := range entries {
result = append(result, FileInfo{Name: e.Name(), Path: e.Name(), IsDir: e.IsDir()})
}
return result, nil
}
// RestoreFromTrash moves a file from trash back to the user's root.
func RestoreFromTrash(dataDir, userID, name string) error {
trashDir := filepath.Join(UserDir(dataDir, userID), ".trash")
src := filepath.Join(trashDir, name)
dest := filepath.Join(UserDir(dataDir, userID), name)
if strings.Contains(name, "..") {
return os.ErrPermission
}
return os.Rename(src, dest)
}
// EmptyTrash permanently deletes all files in trash.
func EmptyTrash(dataDir, userID string) error {
trashDir := filepath.Join(UserDir(dataDir, userID), ".trash")
return os.RemoveAll(trashDir)
}
// MoveFile moves a file or folder to a new path.
@@ -93,6 +137,9 @@ func listDir(base, rel string) ([]FileInfo, error) {
}
var result []FileInfo
for _, e := range entries {
if e.Name() == ".trash" || e.Name() == ".git" {
continue
}
entryRel := filepath.Join(rel, e.Name())
info := FileInfo{
Name: e.Name(),