Restore the original working approach: build the prefixed filename
(e.g. Media--Dailies--clip.mxf) client-side before sending to any
upload endpoint, and pass prefix:'' to the server.
Changed in uploadFilePresigned() and uploadFileViaServer():
- Compute prefixedName from selectedPrefix with slashes converted to --
- Pass filename: prefixedName, prefix: '' to all three upload paths
(/api/presigned, /api/desktop/multipart/init, /api/upload formData)
Speed display was wildly inaccurate during parallel multipart uploads because:
1. Parallel chunk progress events caused non-monotonic byte counts
2. The 5-second smoothing window was too short for bursty uploads
3. Speed dropped to near-zero between chunk boundaries
Fixes:
- Enforce monotonic byte tracking via _peak guard
- Extend sliding window from 5s to 15s
- Cache last good speed value for display continuity
- Require >= 1s of data before calculating (was 0.5s)
Shows "XX% · Y.Y MB/s · ~Zm Zs" during uploads. Speed is smoothed
over a 5-second rolling window. Works for both small (single PUT)
and large (multipart chunked) uploads.
A 46MB file now splits into 6 chunks (all 6 streams active) instead
of 2 chunks (4 streams idle). Better saturation of available bandwidth
on high-latency or constrained links.
Large files (>32MB) now use presigned S3 multipart URLs via
/api/desktop/multipart/init, letting the browser PUT 32MB chunks
directly to S3 in 6 parallel streams. No data passes through Node.
Small files still use single presigned PUT. Updated HTTP mode
description to "HTTP multi-part upload".
Reverts accidental landing page overwrite. Files >32MB now use S3
multipart upload with 32MB chunks and 6 parallel streams per file.
Files <=32MB still use fast direct presigned PUT.
Files >32MB now use S3 multipart upload with 32MB chunks and 6 parallel
streams per file. Files <=32MB still use fast direct presigned PUT.
Fixes slow upload speeds for large files.
Files >32MB now use S3 multipart upload with 32MB chunks and 6 parallel
streams per file. Files <=32MB still use fast direct presigned PUT.
Fixes slow upload speeds for large files.
Addresses feedback from Gavin (VPM):
- Sort folders alphabetically in both upload tree and admin tree views
- Auto-select VPM as default folder on login instead of Root
- Fix S3 key construction for nested folders: convert "/" to "--" so FLX
correctly maps subfolders (e.g. Content/TEST - AMPP Demo now produces
Content--TEST - AMPP Demo--file.ext instead of Content/TEST - AMPP Demo--file.ext)
- Clarify HTTP mode note: "Files are processed 6 at a time" instead of
"up to 6 concurrent files" which implied a total file limit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Logs the first job's keys and data (truncated to 1000 chars) on each
/api/ampp/jobs request to help identify which field contains the asset name.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Browser now uploads files directly to RustFS/S3 via presigned PUT URLs.
The Node server only generates signed URLs and tracks quota — file data
never touches the server. 6 concurrent file uploads.
Falls back to server-proxied PutObjectCommand upload if presigned fails.
Server changes:
- /api/presigned now checks folder permissions, quota, and blocked files
- /api/presigned/complete endpoint for post-upload quota tracking
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add search/filter input to destination folder browser with 320px
scrollable container
- Add User Audit button showing each user's visible pages, role,
quota, and folder access permissions
- Fix admin Folders tab delete buttons (were broken due to
JSON.stringify quote conflicts in innerHTML onclick handlers)
- Add more AMPP job name field fallbacks and debug logging to
diagnose asset name display issue
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The @aws-sdk/lib-storage Upload class internally uses
CreateMultipartUploadCommand for files over the part size threshold,
which returns non-standard XML from RustFS causing UnknownError.
PutObjectCommand does a simple single PUT request that RustFS handles
correctly. Fixed in both /api/upload and /api/link/:id/upload endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The manual CreateMultipartUploadCommand/UploadPartCommand/CompleteMultipartUploadCommand
flow fails with RustFS due to non-standard XML responses (same issue as HeadBucketCommand).
Switch front-end to use /api/upload endpoint which uses @aws-sdk/lib-storage Upload class —
the same method that worked reliably in VPM-Uploader with RustFS.
Uses XMLHttpRequest for upload progress tracking.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AMPP API returns keys like 'state:jobState', 'name:text', 'type:jobType',
'creator:id', 'created:dateTime', 'job:id' — not plain 'status', 'name',
etc. Frontend was falling back to 'Job' / 'Unknown' for every entry.
Updated field lookups to read colon-namespaced keys first, with fallbacks
for compatibility. Also added 'aborted' to the failed status detection.
Split files into 32 MB chunks, POST 6 concurrently to /api/upload/chunk,
server proxies each chunk as an S3 multipart part. Up to 4 files upload
in parallel simultaneously. Achieves Aspera-class throughput over plain
HTTP with no UDP port forwarding or custom protocols required.
Same approach used by MASV under the hood.
- server.js: Add /api/upload/initiate, /chunk, /complete, /abort endpoints
- public/index.html: Replace single-PUT uploadHTTP() with parallel chunked version
Root cause: three critical bugs in the UDP upload flow:
1. Main server never registered sessions on the relay — it stored them
in its own memory but never called POST /session on the relay, so
the relay had no idea about any upload sessions.
2. Relay had no HTTP chunk endpoint — the Chrome extension sends chunks
via HTTP POST to /session/:id/chunk/:index, but the relay only had
a binary UDP listener. Added the HTTP fallback endpoint.
3. Relay had no CORS headers — browser requests from chrome-extension://
origins were blocked. Added CORS middleware.
The flow now works:
Browser → POST /api/udp/session (main server)
Main server → POST /session (relay, with s3Config)
Browser → POST /session/:id/chunk/:n (relay, via public URL)
Relay → S3 multipart upload
Browser → POST /api/udp/session/:id/complete (main server)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The relay container is reached internally via docker service name
(http://dragon-wind-relay:3001) but browsers need the public address.
Previously the internal URL was sent to clients causing UDP uploads to fail.
- Add publicRelayUrl field to relay config (server + admin UI)
- /api/udp/session now returns publicRelayUrl to browser clients
- Internal relayUrl still used for server-side health checks
- Falls back to internal URL if public not set
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The extension was saving and restoring UDP mode from storage. If UDP
was previously selected and the relay isn't configured, every upload
immediately fails with "UDP relay not configured".
- Default to HTTP mode on fresh installs (no saved mode)
- Before UDP upload, check /api/health relayConfigured flag and
auto-fall-back to HTTP with a warning if relay isn't set up
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Same fix as the Chrome extension — the web GUI's uploadHTTP() was using
item.file.type (browser-determined MIME) instead of presigned.contentType
(server-signed MIME). For broadcast formats like MXF, R3D, BRAW where
the browser returns empty or generic types, this causes an S3 signature
mismatch and the PUT fails.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>