Commit graph

31 commits

Author SHA1 Message Date
Zac
89291a4baf Fix S3 test: always merge with saved config, add debug logging 2026-04-07 00:32:59 -04:00
Zac
ae4360e85a Fix S3 test (use HeadBucket like VPM-Uploader), replace UDP button with grayed Electron App 2026-04-07 00:30:28 -04:00
Zac
db25255472 Fix S3 save, remove UDP relay entirely, remove Extension tab 2026-04-07 00:26:43 -04:00
Zac
f3aa44104c Remove Extension tab, fix S3 save, update footer, add Electron uploader coming soon to UDP Relay 2026-04-07 00:21:31 -04:00
2b713fd169 fix: remove duplicate require causing SyntaxError on startup
CreateMultipartUploadCommand etc. were already declared at top of file.
Appended block re-declared them causing 'Identifier already declared' crash.
2026-04-06 23:32:08 -04:00
172250b279 fix: AMPP Monitor field mapping for Grassvalley API colon-namespaced keys
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.
2026-04-06 23:30:00 -04:00
8ec43c299e Add parallel chunked HTTP upload (Option 4 — Aspera-class speed)
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
2026-04-06 23:22:51 -04:00
a3d1446060 fix: add missing UDP Relay admin tab button and HTTP/UDP mode toggle buttons on upload page 2026-04-06 22:46:52 -04:00
efdaa48cb6 fix: UDP upload was completely broken — relay never received sessions or chunks
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>
2026-04-06 22:12:22 -04:00
42fead82aa fix: split relay URL into internal + public for browser clients
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>
2026-04-06 22:02:14 -04:00
b3f669afe4 fix: extension defaulting to UDP mode causing upload failures
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>
2026-04-06 22:00:20 -04:00
9e1e25ac97 fix: web GUI upload Content-Type mismatch with presigned URL
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>
2026-04-06 21:56:03 -04:00
3b39803b49 fix: extension upload uses server-signed content type + better error logging
The presigned URL is signed with a specific Content-Type (determined by
the server's MIME map). If the browser's file.type doesn't match (common
for broadcast formats like MXF, R3D, BRAW), S3 rejects the PUT with a
signature mismatch. Now the extension uses presigned.contentType from the
server response instead of item.file.type.

Also added console logging for upload requests and detailed error
messages from S3 responses on failure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 21:54:38 -04:00
8bb9cf7ce9 debug: add console logging and 10s timeout to extension API calls
- apiFetch() now logs method+URL on start and status code on response
- Added 10s AbortController timeout to prevent infinite hangs
- Added try/catch wrapper in saveSettings() around login() call
- This helps diagnose the "stuck on connecting" issue

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 21:50:28 -04:00
7c2aa0d739 fix: extension Save & Connect stuck on connecting state
- Login failures (wrong password) were caught by the 401 handler in
  apiFetch() which threw "Session expired" instead of returning the
  actual error message. Now /api/login 401 responses pass through
  so the real "Invalid credentials" error is shown.
- Settings panel now shows error messages from login() failures
  instead of staying stuck on "Saved — connecting…" forever.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 21:45:08 -04:00
e588a69a78 fix: Chrome extension MV3 CSP violations and missing alarms permission
- Add "alarms" permission to manifest — chrome.alarms.create() was
  throwing "Cannot read properties of undefined" because the permission
  was missing; also removed invalid "sockets" permission (not a valid MV3 perm)
- Remove all inline event handlers from popup.html (onclick, ondragover,
  ondragleave, ondrop, onchange) — MV3 CSP blocks inline JS entirely;
  all handlers moved to popup.js addEventListener() calls
- Replace inline onclick="removeFile(i)" on dynamically generated remove
  buttons with data-idx attribute + delegated click listener on the list
  container — same CSP fix for runtime-generated elements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 21:38:50 -04:00
fa206ba21a fix: AMPP monitor duplicate ID, not-configured UX, and auto-refresh
- Fix duplicate id="ampp-status" collision between the monitor page and
  admin AMPP panel — admin panel now uses id="ampp-cfg-status" so the
  monitor status messages are no longer silently hijacked
- AMPP monitor now shows a clear "not configured" card with a direct
  button to Admin → AMPP instead of a cryptic error when the API key
  hasn't been entered yet
- Improve job field mapping to handle AMPP response variations
  (jobStatus, displayName, jobType, results array vs items array)
- Add 30-second auto-refresh while on the Monitor tab; timer is cleared
  when navigating away to avoid ghost requests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 21:32:09 -04:00
978d447b3d fix: add CORS headers for Chrome extension + fix Save & Connect button
- 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>
2026-04-06 21:30:17 -04:00
08c138ca46 Fix extension icons and AMPP autofill
- Generate missing chrome-extension/images/ icons (16, 48, 128px)
  that were absent from repo, causing 'Could not load manifest' error
- Fix AMPP API key field: add readonly+onfocus to block Bitwarden/browser autofill

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 21:05:46 -04:00
017d73f48c Fix S3 config form: autofill, region default, secret field
- Add readonly+onfocus trick to prevent browser autofilling secret key with garbage
- Set region value="us-east-1" so it always has a real default, not just placeholder
- loadS3Config always clears secret field and sets contextual placeholder
- Secret hint now clearly shows saved vs not-saved state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 20:39:22 -04:00
d0525eb718 Fix extension 404, simplify UDP/relay UI
- Add chrome-extension/ to Dockerfile COPY (was missing, caused 404 on download)
- Remove UDP Relay tab from admin panel (relay is server-side, no user config needed)
- Remove upload mode toggle buttons, replace with clean inline status bar
- Extension panel: drop relay port-forwarding note, simplify to 'extension required' message
- UDP hint shown inline only when extension is detected in browser

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 20:32:25 -04:00
b393eca960 Redesign folder UI, VPM branding, and auto theme
- Replace chip-pill folder selector with clean vertical tree list
- VPM text wordmark replaces vpm-logo.png in login + header (no PNG/invert hack)
- Wild Dragon logo/icon retained only on favicon and splash animation
- Auto-detect prefers-color-scheme on first load (no longer defaults to dark)
- System theme changes update UI if user hasn't manually toggled
- Remove dragon icon from login card and app header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 20:26:51 -04:00
c03b7ef491 Add per-user upload quotas/permissions and share link system
- Per-user quota tracking (MB limit + uploadedBytes counter)
- Per-user allowed folders restriction (empty = all folders allowed)
- Admin permissions modal: quota config, folder checkboxes, usage reset
- Share links: create tokenized upload URLs with expiry, max-uses, folder
- Public share.html upload page (no auth required) with drag-drop + progress
- Backend routes: GET/PUT /api/users/:u/permissions, POST .../quota/reset
- Backend routes: GET/POST /api/sharelinks, DELETE .../token, GET /share/:token
- migrateData() ensures existing user records gain new fields on startup
- Frontend JS: loadUsers quota column, openPermissions modal, loadShareLinks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 19:58:29 -04:00
12441b7bf4 feat: VPM branding + Built by footer
- Added VPM logo (vpm-logo.png) to login card and header left side
  (subtle, opacity 50%, auto-inverted in light mode)
- Footer at bottom of app: "Built by Zac Gaetano & Wild Dragon LLC
  · In partnership with Broadcast Management Group"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 19:41:07 -04:00
cde70d386d fix: extension download using wrong auth header (Authorization vs x-auth-token)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 21:25:46 -04:00
e2c6db7113 fix: S3 generic compat, nav tab reliability, single logo
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>
2026-04-05 20:42:21 -04:00
895145e1ed feat: Chrome extension download + install guide in admin panel
- GET /api/extension/download — streams chrome-extension/ as a .zip using archiver
- Admin → 🧩 Extension tab with:
  - One-click download button (fetches zip via auth'd API, triggers browser save)
  - 5-step install guide: unzip, chrome://extensions, developer mode, load unpacked, configure
  - UDP port-forwarding reminder note
- Added archiver dependency to package.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 20:33:56 -04:00
fbec32dcc4 feat: randomized ports via setup.sh + .env on first run
- setup.sh generates .env with random ports (10000-59999) on first run,
  ensuring WEB_PORT, RELAY_TCP_PORT, and RELAY_UDP_PORT are all distinct
- .env.example documents all available env vars with defaults
- docker-compose.yml already reads from .env via ${VAR:-default} syntax
- Run `bash setup.sh` to generate ports, `bash setup.sh --start` to also
  bring up the stack in one step

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 20:29:35 -04:00
155c821ef8 feat: AMPP configuration in admin settings
- 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>
2026-04-05 20:22:51 -04:00
6b6efc61ba feat: Wild Dragon branding — splash animation + logo assets
- Full cinematic splash screen with animated dragon GIF, 18 wind streaks,
  pulsing glow rings, shimmer subtitle, and accelerating progress bar
- Wild Dragon wordmark (wilddragon-logo.png) in header and login, CSS-inverted for dark mode
- Dragon circular icon (dragon-icon.png) in header and login card
- Dragon animation GIF (dragon-anim.gif) as splash hero
- Blue (#1e4bd8) primary accent matching Wild Dragon brand; orange reserved for UDP mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 20:19:57 -04:00
641701edf8 feat: Dragon Wind v1.0 — dual-mode broadcast uploader
- Full VPM Uploader feature set (auth, users, folders, AMPP monitor)
- HTTP upload via presigned S3 URLs with XHR progress tracking
- UDP upload mode with relay server (WebRTC DataChannel + HTTP fallback)
- S3 Admin settings with live Test Connection (upload+delete verify)
- UDP Relay Admin settings with health check
- Standalone UDP relay server (Node.js + Docker) with multipart S3 assembly
- Chrome Extension (Manifest v3): popup, background, content script
- Dynamic S3 client — reconfigures on save without restart
- Dark/light theme, full AMPP job monitor
- docker-compose.yml with dragon-wind + udp-relay services

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 20:05:34 -04:00