Skip to content

fix: batch trash operations and prevent re-upload races - #11

Merged
LukeGus merged 3 commits into
Termix-SSH:dev-1.1.1from
deanxbox:fix/concurrent-trash-delete
Oct 22, 2026
Merged

LukeGus merged 3 commits into
Termix-SSH:dev-1.1.1from
deanxbox:fix/concurrent-trash-delete

Conversation

@deanxbox

Copy link
Copy Markdown

Bulk delete previously sent one request per file and removed rows before the server confirmed the trash operation. This could make a re-upload under the same name get trashed too. This PR sends deletes in bounded batches, keeps rows dimmed until each batch is confirmed, and makes everything that could reuse a path wait for in-flight deletes. On the measured remote host, 1,000 files were trashed in 10.13 seconds with no failures; the earlier per-item rate (about 0.46 s per file) extrapolates to about 460 seconds for the same workload.

Permanent delete and sudo retries are batched too, and Cancel now stops queued items on the server.

Overview

Bulk trash and permanent delete are faster and no longer remove rows before the server confirms them, which closes the re-upload race.

  • Added: POST /deleteItems batch endpoint (trash, or permanent with permanent: true) and POST /deleteItems/cancel
  • Updated: delete flow keeps rows dimmed until confirmed; uploads, drops, paste, rename, create, drag-move and undo wait for in-flight deletes
  • Fixed: re-uploaded files being trashed by a bulk delete that was still running
  • Fixed: Ctrl+A selecting rows that are being deleted; Cancel leaving hundreds of queued items running; the "delete permanently?" prompt covering items that were never tried

Changes Made

  • POST /deleteItems: up to 500 deduplicated paths per request, per-path results, 16 concurrent moves, trash purge after the response, session ownership check. /deleteItem is unchanged.
  • Client batching: 200 paths per batch, up to 3 batches in flight, a "Moving to Trash: n / total" toast, and Cancel.
  • Rows stay dimmed and non-interactive until their batch is confirmed. Failed or cancelled rows stay in the list. Selected children of a selected folder are skipped.
  • Uploads, folder drops, local-pane drops, paste, rename, create, drag-move and undo wait for active deletes, including ones that start while waiting. Ctrl+A skips dimmed rows.
  • Cancel calls POST /deleteItems/cancel; queued items then stop starting and report "Cancelled". State is in memory only (10-minute TTL, keyed by user, session and operation).
  • The "delete permanently?" prompt covers only items the server tried and failed.
  • Permanent delete is batched through /deleteItems with permanent: true:
    • Paths are grouped into sh -c scripts, with per-path status lines carrying a per-call random nonce so file names or error text cannot forge a result.
    • rm -f for files; rm -rf only for paths the client lists as directories.
    • Sudo retries only paths rm reported as : Permission denied (checked under LC_ALL=C), and not after Cancel.
    • Refuses /, drive roots, and any path with a . or .. segment.
    • Windows paths are removed 4 at a time with the existing PowerShell command.
  • The delete barrier stays held through the permanent-delete and sudo prompts.
  • Added the English "Deleting" and "Moving to Trash" strings. Other locales are untouched.

Verification

  • tsc and Prettier are clean. 272 Vitest tests pass in 48 files, including new /deleteItems route tests (validation, session checks, dedupe, permanent mode, cancel) and removePermanently tests (batching, nonce, sudo scope, cancel, root refusal).
  • Benchmark (one remote host, measured before the later changes): 1,000 files trashed in 10.13 s, 0 failures. The 460 s comparison is an extrapolation, not a same-run measurement.
  • Permanent delete (same host): 300 files, a file name with an apostrophe, a folder and a nonexistent path took 1.6 s with 0 errors. A request for / was refused.
  • The permission-denied classification was checked against a real shell: a file named "Permission denied" is a plain failure, and a read-only file is a denial.

Known limitations

  • The real UI (dimmed rows, Cancel, sudo dialog) was not tested end to end, and neither were 10,000 items.
  • Windows hosts are untested. The Windows delete path trusts any output containing SUCCESS.
  • Cancel cannot stop items inside a shell group that is already running, or requests already in flight.
  • Cancel state is lost if the plugin restarts.
  • The live sudo flow was not tested against a host.
  • The benchmark used one host. Up to 48 moves can be in flight (3 batches x 16), and other servers' SFTP limits are unknown.

Related Issues

None

Screenshots / Demos

Before Demo:

2026-10-20.23-28-08.mp4

After Demo:

2026-10-20.23-29-19.mp4

Checklist

  • Code follows the project style
  • Works on mobile and desktop (if it has UI)
  • Tests pass and new code has tests
  • Added the change to CHANGELOG.md (if the repo has one)
  • I have read the repo's CONTRIBUTING.md

Add a batch trash endpoint with per-path results and bounded concurrency.
Trash files in batches, keeping rows dimmed until each batch is confirmed.
Wait for in-flight deletes before uploads, drops, and paste operations.
Skip selected children when their parent folder is also selected.

Trashed 1,000 files in 10.13s with no failures on one remote host.
TypeScript checks pass; all 256 Vitest tests pass. Real UI not tested.
Copy, move, and rename can still target names being deleted; cancel
cannot stop in-flight requests, and permanent delete remains per-item.
Block rename, create, drag-move, and undo while deletes are in flight, and
exclude dimmed rows from Ctrl+A. Add server cancellation so queued delete
items stop, and offer permanent deletion only for items the server reports
as failed.

Batch permanent deletes through /deleteItems with per-call nonce status
markers. Use rm -f for files and rm -rf only for listed directories; retry
with sudo only paths reported as permission denied, and refuse roots and
paths containing a . or .. segment. Hold the delete barrier through
permanent-delete and sudo prompts.

Add route and permanent-delete unit tests.
@termix-triage-bot

termix-triage-bot Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

AI review | Looks safe to merge

Implements batched trash and permanent delete operations with concurrency limits, cancellation support, and a frontend delete barrier to prevent re-upload races. Path validation, nonce-based shell output parsing, and comprehensive tests are included. No security, rule, or quality issues found.

Reviewed 69d9031. AI can be wrong, a maintainer still decides. Comment /termix review to run it again.

@termix-triage-bot termix-triage-bot Bot moved this to Open PRs in Termix Roadmap Oct 21, 2026
@LukeGus
LukeGus changed the base branch from main to dev-1.1.1 October 22, 2026 04:59
@LukeGus
LukeGus merged commit b101721 into Termix-SSH:dev-1.1.1 Oct 22, 2026
2 checks passed
@deanxbox
deanxbox deleted the fix/concurrent-trash-delete branch October 23, 2026 17:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants