feat: restore files from trash

This commit is contained in:
2026-06-28 22:18:44 +08:00
parent e5bdaec0aa
commit cc0c050985
11 changed files with 505 additions and 24 deletions
+43
View File
@@ -987,6 +987,49 @@ func (a *App) ListVaultTrash(pluginID string) ([]corefiles.TrashEntry, string) {
return entries, ""
}
// RestoreVaultTrash restores a file or folder from internal trash for a plugin with files.delete and files.write.
func (a *App) RestoreVaultTrash(pluginID, trashID string, options corefiles.RestoreOptions) (string, string) {
if _, err := a.requirePluginAccess(pluginID, "files.delete"); err != nil {
return "", err.Error()
}
if _, err := a.requirePluginAccess(pluginID, "files.write"); err != nil {
return "", err.Error()
}
if a.files == nil {
return "", "files service not initialized"
}
entries, err := a.files.ListTrashEntries()
if err != nil {
return "", err.Error()
}
var entry corefiles.TrashEntry
for _, candidate := range entries {
if candidate.TrashID == trashID {
entry = candidate
break
}
}
if entry.TrashID == "" {
return "", "not-found: trash entry " + trashID
}
restoredPath, err := a.files.RestoreTrashEntry(trashID, options)
if err != nil {
return "", err.Error()
}
if err := a.recordFileSyncOp(syncEntityTypeForFileType(entry.OriginalType), restoredPath, syncsvc.OpCreate, map[string]string{
"path": restoredPath,
}); err != nil {
return "", err.Error()
}
a.publishFileActivity("file.changed", pluginID, restoredPath, map[string]interface{}{
"operation": syncsvc.OpCreate,
"type": string(entry.OriginalType),
"restored": true,
"trashId": trashID,
})
return restoredPath, ""
}
// OpenVaultPathExternal opens a vault-relative file or folder in the OS default app.
func (a *App) OpenVaultPathExternal(pluginID, relativePath string) string {
if _, err := a.requirePluginAccess(pluginID, "files.openExternal"); err != nil {
+29
View File
@@ -395,6 +395,17 @@ func TestFilesBridgeReadWriteListMoveTrash(t *testing.T) {
if len(trashEntries) != 1 || trashEntries[0].OriginalPath != "Docs/two.txt" || trashEntries[0].TrashID != trash.TrashID {
t.Fatalf("trash entries = %+v, want Docs/two.txt", trashEntries)
}
restored, errStr := app.RestoreVaultTrash("files.plugin", trash.TrashID, corefiles.RestoreOptions{})
if errStr != "" {
t.Fatalf("RestoreVaultTrash: %s", errStr)
}
if restored != "Docs/two.txt" {
t.Fatalf("restored path = %q, want Docs/two.txt", restored)
}
if _, err := os.Stat(filepath.Join(root, "Docs", "two.txt")); err != nil {
t.Fatalf("restored file missing: %v", err)
}
}
func TestFilesBridgeWritePublishesFileChangedActivityEvent(t *testing.T) {
@@ -610,6 +621,24 @@ func TestFilesBridgePermissions(t *testing.T) {
call: func(app *App) string { _, errStr := app.TrashVaultPath("files.plugin", "one.txt"); return errStr },
wantPhrase: "files.delete",
},
{
name: "restore requires delete",
perms: []string{"files.read", "files.write"},
call: func(app *App) string {
_, errStr := app.RestoreVaultTrash("files.plugin", "missing", corefiles.RestoreOptions{})
return errStr
},
wantPhrase: "files.delete",
},
{
name: "restore requires write",
perms: []string{"files.read", "files.delete"},
call: func(app *App) string {
_, errStr := app.RestoreVaultTrash("files.plugin", "missing", corefiles.RestoreOptions{})
return errStr
},
wantPhrase: "files.write",
},
{
name: "open external requires openExternal",
perms: []string{"files.read", "files.write", "files.delete"},