feat(files): streaming ZIP download in the project explorer
Nightly Build / build (push) Successful in 8m52s

Each row of the project Files tab gains a download action, and the toolbar
gains a Download ZIP button scoped to the folder being browsed (at the root,
the whole project). Visible to read-only members too: download is a read.

Single files need no new backend: they reuse GET /api/file?force_download.
Directories go through the new GET /api/file/download, which builds the ZIP
on the fly: an async task walks the tree and async_zip (Astral's maintained
rs-async-zip fork) streams entries into a bounded duplex stream backing the
response body — no temp file, no whole-archive buffer, backpressure for free,
and the task dies with the client. Compression is per entry: Deflate at
maximum level, except files whose magic bytes name an already-compressed
format (media/PDF via the shared sniffer, the ZIP family, gzip/zstd/7z/rar,
compressed audio), which are Stored. Entries are prefixed with the folder
name, empty folders and unix permission bits survive, symlinks are never
followed into the archive, and containment stays fail-closed under the
resolved root. Covered by a round-trip test read back with the crate's own
reader (and verified against unzip/python's zipfile).
This commit is contained in:
2026-08-07 19:49:30 +01:00
parent c96ceee037
commit 8013022321
9 changed files with 315 additions and 8 deletions
+2
View File
@@ -55,6 +55,8 @@ If you have write access you can also, from the toolbar or each row:
Read-only members see the same explorer and can open every file, but the write actions are hidden (and refused by the server anyway).
**Downloading.** Every member can download what they see: each row has a download icon, and the toolbar has a **Download ZIP** button that always applies to the folder you are currently browsing (at the project root, that's the whole project). A single file downloads as-is; a folder downloads as a ZIP archive, built on the fly on the server. Inside the archive the folder keeps its name, and files that are already compressed (photos, videos, PDFs, other archives) are stored as-is so the download stays fast.
## The Sharing tab
Lists every member with their access level. The owner and any read & write member can: