fix: resolve ghost conflicts by assigning unique paths to trashed files
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m2s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m2s
This commit is contained in:
parent
c9f84fc257
commit
4c8e6b814c
1 changed files with 48 additions and 14 deletions
|
|
@ -529,8 +529,15 @@ func (h *FSHandler) Delete(c *fiber.Ctx) error {
|
|||
resolvedPath := c.Locals("resolvedPath").(string)
|
||||
relativePath := c.Locals("relativePath").(string)
|
||||
|
||||
// Soft-delete: move to .trash directory
|
||||
trashPath := filepath.Join(h.Config.TrashDir, relativePath)
|
||||
// Generate unique suffix for the trash path to prevent collisions
|
||||
trashSuffix := fmt.Sprintf("_%d", time.Now().UnixNano())
|
||||
trashName := filepath.Base(relativePath) + trashSuffix
|
||||
trashDir := filepath.Dir(relativePath)
|
||||
if trashDir == "." {
|
||||
trashDir = ""
|
||||
}
|
||||
trashedRelativePath := filepath.Join(trashDir, trashName)
|
||||
trashPath := filepath.Join(h.Config.TrashDir, trashedRelativePath)
|
||||
|
||||
// Check if permanent
|
||||
isPermanent := c.Query("permanent") == "true"
|
||||
|
|
@ -582,18 +589,27 @@ func (h *FSHandler) Delete(c *fiber.Ctx) error {
|
|||
|
||||
// Update DB
|
||||
cleanPath := filepath.ToSlash(relativePath)
|
||||
cleanTrashPath := filepath.ToSlash(trashedRelativePath)
|
||||
|
||||
h.DB.Exec(
|
||||
`UPDATE files SET is_trashed = 1, trashed_at = CURRENT_TIMESTAMP, original_path = ?, is_dir = ?, size = ? WHERE path = ?`,
|
||||
cleanPath, isDir, size, cleanPath,
|
||||
`UPDATE files SET is_trashed = 1, trashed_at = CURRENT_TIMESTAMP, original_path = ?, is_dir = ?, size = ?, path = ? WHERE path = ?`,
|
||||
cleanPath, isDir, size, cleanTrashPath, cleanPath,
|
||||
)
|
||||
|
||||
if isDir == 1 {
|
||||
h.DB.Exec(
|
||||
`UPDATE files SET path = REPLACE(path, ?, ?) WHERE path LIKE ?`,
|
||||
cleanPath+"/", cleanTrashPath+"/", cleanPath+"/%",
|
||||
)
|
||||
}
|
||||
|
||||
// Also insert if not tracked
|
||||
h.DB.Exec(
|
||||
`INSERT OR IGNORE INTO files (path, name, is_trashed, trashed_at, original_path, is_dir, size) VALUES (?, ?, 1, CURRENT_TIMESTAMP, ?, ?, ?)`,
|
||||
cleanPath, filepath.Base(cleanPath), cleanPath, isDir, size,
|
||||
cleanTrashPath, filepath.Base(cleanTrashPath), cleanPath, isDir, size,
|
||||
)
|
||||
|
||||
h.DB.AddAuditLog("deleted", cleanPath, c.IP())
|
||||
h.DB.AddAuditLog("deleted", cleanTrashPath, c.IP())
|
||||
|
||||
return c.JSON(fiber.Map{"message": "moved to trash"})
|
||||
}
|
||||
|
|
@ -657,21 +673,39 @@ func (h *FSHandler) RestoreFromTrash(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
trashPath := filepath.Join(h.Config.TrashDir, filepath.FromSlash(body.Path))
|
||||
restorePath := filepath.Join(h.Config.StorageDir, filepath.FromSlash(originalPath))
|
||||
destFull := filepath.Join(h.Config.StorageDir, filepath.FromSlash(originalPath))
|
||||
|
||||
// Check if originalPath already exists, assign new name if so
|
||||
if _, err := os.Stat(destFull); err == nil {
|
||||
uniqueName := GetUniquePath(filepath.Dir(destFull), filepath.Base(destFull))
|
||||
originalPath = filepath.ToSlash(filepath.Join(filepath.Dir(originalPath), uniqueName))
|
||||
destFull = filepath.Join(filepath.Dir(destFull), uniqueName)
|
||||
}
|
||||
|
||||
// Ensure parent directory exists
|
||||
if err := os.MkdirAll(filepath.Dir(restorePath), 0755); err != nil {
|
||||
if err := os.MkdirAll(filepath.Dir(destFull), 0755); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to prepare restore location"})
|
||||
}
|
||||
|
||||
if err := os.Rename(trashPath, restorePath); err != nil {
|
||||
if err := os.Rename(trashPath, destFull); err != nil {
|
||||
if err := copyFile(trashPath, destFull); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to restore file"})
|
||||
}
|
||||
os.RemoveAll(trashPath)
|
||||
}
|
||||
|
||||
h.DB.Exec("UPDATE files SET path = ?, name = ?, is_trashed = 0, trashed_at = NULL WHERE path = ?", originalPath, filepath.Base(originalPath), body.Path)
|
||||
|
||||
var isDir int
|
||||
h.DB.QueryRow("SELECT is_dir FROM files WHERE path = ?", originalPath).Scan(&isDir)
|
||||
if isDir == 1 {
|
||||
h.DB.Exec(
|
||||
"UPDATE files SET is_trashed = 0, trashed_at = NULL WHERE path = ?",
|
||||
body.Path,
|
||||
`UPDATE files SET path = REPLACE(path, ?, ?) WHERE path LIKE ?`,
|
||||
body.Path+"/", originalPath+"/", body.Path+"/%",
|
||||
)
|
||||
}
|
||||
|
||||
h.DB.AddAuditLog("restored", originalPath, c.IP())
|
||||
|
||||
h.DB.AddAuditLog("restored", fmt.Sprintf("Restored '%s' from trash", originalPath), c.IP())
|
||||
|
||||
|
|
|
|||
Reference in a new issue