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>
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>
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>
- Add CORS middleware to server.js allowing chrome-extension:// origins
so the popup can make authenticated API requests without browser blocking
- Fix popup.js saveSettings(): require password on save, call login() directly
instead of tryConnect() to avoid password-not-found loop
- Fix init(): open settings panel automatically if no saved token, so users
know they need to enter credentials after first install or session expiry
- Don't persist password to chrome.storage (security), use remove('token')
instead of set({token:null}) to properly clear the old session
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
S3:
- Endpoint is now optional (leave blank = AWS S3, custom = MinIO/R2/Backblaze/etc.)
- forcePathStyle only applied when a custom endpoint is set (harmless on AWS)
- initS3() no longer requires endpoint to be present
- Updated form hint to explain AWS vs generic S3 usage
Nav tabs:
- Switched admin tab active-state matching from fragile array-index to data-tab attribute
- Added user-select:none to prevent text selection on click for both nav and admin tabs
- Admin tabs now flex-wrap for narrow viewports
Logo:
- Removed wilddragon-logo.png wordmark from splash, login, and header
- Single dragon-icon.png used throughout — no CSS invert hack needed
- Cleaner header: icon + "Dragon Wind" badge only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GET/PUT /api/ampp/config — store base URL + API key in db.json (no restart needed)
- POST /api/ampp/test — authenticate against AMPP token endpoint and report result
- Refactored AMPP_BASE/AMPP_API_KEY constants to dynamic getAmppBase()/getAmppApiKey()
functions so live config changes take effect immediately
- Admin → AMPP tab: base URL field, masked API key field, Test Connection + Save buttons
- Cached token invalidated automatically on config save
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>