0.37.2: stream folder zip-downloads to fix 504 on large folders
A big folder hit a 504 Gateway Timeout: the zip was built into a temp
file *before* any response was sent, so for large folders the backend
stayed silent past nginx's proxy_read_timeout.
Now the zip is streamed as it's built, end to end:
- file_service.open_archive() returns (filename, byte iterator); _iter_zip
walks the dir and yields zip bytes incrementally via a small drain
buffer, writing each file in 1 MiB chunks (bounded memory, valid CRCs).
Same hardening as before — only real regular files; FIFOs/sockets/
devices/symlinks skipped without open(); per-file read errors skipped.
- /api/files/download and /agent/files/download return a StreamingResponse
(no temp file). The agent proxy streams the agent response straight
through (agent_service.stream_download), pulling the first chunk eagerly
so an offline/bad-token agent still yields a clean status before 200.
- Files page: streamed downloads have no Content-Length, so the progress
bar shows the running downloaded byte count ("Downloading … 12.3 MB")
instead of a percentage, after the initial "Preparing …".
Verified end to end via TestClient (200, application/zip, valid zip,
2 MiB file intact, FIFO skipped, no hang).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
0dc430bb2a
commit
5ac9f15de4
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "stackpilot-frontend",
|
||||
"private": true,
|
||||
"version": "0.37.1",
|
||||
"version": "0.37.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -88,6 +88,7 @@ export function Files() {
|
||||
pct: number;
|
||||
kind: "upload" | "download";
|
||||
indeterminate?: boolean;
|
||||
loaded?: number;
|
||||
} | null>(null);
|
||||
const fileInput = useRef<HTMLInputElement>(null);
|
||||
const folderInput = useRef<HTMLInputElement>(null);
|
||||
@@ -222,6 +223,7 @@ export function Files() {
|
||||
pct: total ? (loaded / total) * 100 : 0,
|
||||
kind: "download",
|
||||
indeterminate: !total,
|
||||
loaded,
|
||||
}),
|
||||
)
|
||||
.catch((err) => toast.error(apiErrorMessage(err)))
|
||||
@@ -329,14 +331,18 @@ export function Files() {
|
||||
)}
|
||||
<span className="truncate">
|
||||
{progress.kind === "download"
|
||||
? progress.indeterminate
|
||||
? progress.indeterminate && !progress.loaded
|
||||
? `Preparing ${progress.label}…`
|
||||
: `Downloading ${progress.label}`
|
||||
: `Uploading ${progress.label}`}
|
||||
</span>
|
||||
</span>
|
||||
<span className="tabular-nums">
|
||||
{progress.indeterminate ? "" : `${Math.round(progress.pct)}%`}
|
||||
{progress.indeterminate
|
||||
? progress.loaded
|
||||
? formatBytes(progress.loaded)
|
||||
: ""
|
||||
: `${Math.round(progress.pct)}%`}
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-2 w-full overflow-hidden rounded-full bg-slate-200 dark:bg-slate-700">
|
||||
|
||||
Reference in New Issue
Block a user