fix: trash integrity for TypeFile nodes — file record soft-delete, correct preview/restore

This commit is contained in:
2026-06-05 17:31:18 +08:00
parent 64e6c6f735
commit a37afd3b67
7 changed files with 452 additions and 52 deletions
+42 -3
View File
@@ -200,12 +200,25 @@ func (s *Service) Get(id string) (*Record, error) {
return scanRecord(row)
}
// ListByNode returns all files linked to a node.
// ListByNode returns all active (non-trashed) files linked to a node.
func (s *Service) ListByNode(nodeID string) ([]Record, error) {
rows, err := s.db.Query(
`SELECT id,node_id,filename,path,storage_mode,size,sha256,mime,
created_at,updated_at,last_seen_at,missing
FROM files WHERE node_id = ? ORDER BY created_at`, nodeID)
FROM files WHERE node_id = ? AND missing != 1 ORDER BY created_at`, nodeID)
if err != nil {
return nil, err
}
defer rows.Close()
return scanRecords(rows)
}
// ListTrashedByNode returns file records with missing=1 for a given node.
func (s *Service) ListTrashedByNode(nodeID string) ([]Record, error) {
rows, err := s.db.Query(
`SELECT id,node_id,filename,path,storage_mode,size,sha256,mime,
created_at,updated_at,last_seen_at,missing
FROM files WHERE node_id = ? AND missing = 1 ORDER BY created_at`, nodeID)
if err != nil {
return nil, err
}
@@ -545,6 +558,32 @@ func (s *Service) DeleteNodeAndChildren(nodeID string) error {
return nil
}
// trashRecord moves a file record's physical file to .verstak/trash/ and
// marks the record as missing=1 so it can be restored later.
func (s *Service) trashRecord(r Record) error {
if r.StorageMode == "vault" {
src, err := s.vaultPath(r.Path)
if err != nil {
return err
}
trashDir := filepath.Join(s.vaultRoot, ".verstak", "trash")
if err := os.MkdirAll(trashDir, 0o750); err != nil {
return err
}
dest := filepath.Join(trashDir, r.ID+"_"+r.Filename)
if _, err := s.vaultPath(filepath.Join(".verstak", "trash", r.ID+"_"+r.Filename)); err != nil {
return err
}
if _, statErr := os.Stat(src); os.IsNotExist(statErr) {
// File already gone — keep the DB record as-is.
} else if err := os.Rename(src, dest); err != nil {
return fmt.Errorf("move to trash: %w", err)
}
}
_, err := s.db.Exec("UPDATE files SET missing=1, updated_at=? WHERE id=?", now(), r.ID)
return err
}
func (s *Service) deleteFileRecords(nodeID string) error {
records, err := s.ListByNode(nodeID)
if err != nil {
@@ -552,7 +591,7 @@ func (s *Service) deleteFileRecords(nodeID string) error {
}
var errs []string
for _, r := range records {
if err := s.DeleteToTrash(r.ID); err != nil {
if err := s.trashRecord(r); err != nil {
errs = append(errs, fmt.Sprintf("file %s: %v", r.ID, err))
}
}