Skip to content

Fix multi file upload#2331

Open
rxtted wants to merge 2 commits into
pelican-dev:mainfrom
rxtted:fix-multi-file-upload
Open

Fix multi file upload#2331
rxtted wants to merge 2 commits into
pelican-dev:mainfrom
rxtted:fix-multi-file-upload

Conversation

@rxtted
Copy link
Copy Markdown

@rxtted rxtted commented May 9, 2026

closes #2326

multi-file uploads were 404ing after the first file. $wire.getUploadUrl() goes through livewire's request batcher, so the parallel uploadFile() loop (maxConcurrent=3) handed every concurrent caller the same jwt. wings invalidates the token after first use, so files 2+ all hit 404.

fetching the existing /api/client/servers/{uuid}/files/upload route directly bypasses the batcher, each fetch is independent so every upload gets its own jwt. the livewire helper got dropped along with its imports, nothing was calling it after the swap.

the other option was pre-fetching urls sequentially via $wire.getUploadUrl() before the parallel loop. stays in the livewire idiom and the diff is smaller, but it's one round-trip per file before any upload starts (a 100-file batch sits for like 5s before sending the first byte). went with the http route for the parallel-friendliness, i'm open to the sequential shape if people prefer.

i noticed that the upload pipeline is duplicated across file-upload.blade and list-files.blade. i didn't touch it, n just patched both.

tested on panel b35 + wings b25: parallel batches all succeed, weird filenames get handled, nested folders via drag-drop, also forced a 403 on the url fetch to confirm the failure UX still surfaces a per-file error.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

The PR migrates file upload URL generation from a server-side method to client-side API requests. The getUploadUrl() method and JWT dependencies are removed from ListFiles.php, while both Blade upload views now fetch fresh URLs dynamically via /api/client/servers/{serverUuid}/files/upload before each file upload.

Changes

Upload URL Generation Migration

Layer / File(s) Summary
Server-side method removal
app/Filament/Server/Resources/Files/Pages/ListFiles.php
Removes getUploadUrl(NodeJWTService $jwtService): string method and eliminates imports for NodeJWTService and CarbonImmutable.
Client-side URL fetching
resources/views/filament/server/pages/file-upload.blade.php, resources/views/filament/server/pages/list-files.blade.php
Both views add serverUuid state from tenant UUID and introduce async fetchUploadUrl() method that requests upload URLs via fetch() to /api/client/servers/{serverUuid}/files/upload.
Upload flow integration
resources/views/filament/server/pages/file-upload.blade.php, resources/views/filament/server/pages/list-files.blade.php
The uploadFile() methods are updated to obtain upload URLs by calling this.fetchUploadUrl() instead of $wire.getUploadUrl().

Possibly related PRs

  • pelican-dev/panel#1858: Both PRs modify the file upload flow and interact with the same ListFiles and upload Blade views, implementing opposite approaches to upload URL handling.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix multi file upload' directly addresses the main issue being resolved - fixing multi-file uploads that were failing with 404 errors.
Description check ✅ Passed The description explains the root cause (Livewire request batching reusing JWT tokens), the solution (direct API route fetch), and testing results, all directly related to the changeset.
Linked Issues check ✅ Passed The PR successfully addresses issue #2326 by ensuring each file upload receives a unique JWT token through direct API calls that bypass Livewire's request batcher.
Out of Scope Changes check ✅ Passed All changes are focused on fixing the multi-file upload issue by removing unused Livewire helper and updating upload logic to fetch URLs directly from the API endpoint.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
resources/views/filament/server/pages/list-files.blade.php (1)

14-21: ⚡ Quick win

Add response structure validation for resilience.

The function accesses (await r.json()).attributes.url without validating that these nested properties exist. If the API response structure is unexpected or malformed, this will throw a TypeError with a cryptic message that gets caught and displayed as "Failed to get upload token", making debugging difficult.

🛡️ Suggested improvement for defensive error handling
 async fetchUploadUrl() {
     const r = await fetch(`/api/client/servers/${this.serverUuid}/files/upload`, {
         headers: { Accept: 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
         credentials: 'same-origin',
     });
     if (!r.ok) throw new Error(`upload url request failed (${r.status})`);
-    return (await r.json()).attributes.url;
+    const data = await r.json();
+    if (!data?.attributes?.url) {
+        throw new Error('Invalid upload URL response format');
+    }
+    return data.attributes.url;
 },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/views/filament/server/pages/list-files.blade.php` around lines 14 -
21, The fetchUploadUrl function currently returns (await
r.json()).attributes.url without validating the response shape; update
fetchUploadUrl to parse the JSON into a variable, verify that the top-level
object is present and that json.attributes is an object and json.attributes.url
is a non-empty string (or a valid URL), and throw a clear, descriptive Error
including the parsed response when the shape is invalid so callers see why
"Failed to get upload token" happened; keep the existing r.ok check and return
the validated url when present.
resources/views/filament/server/pages/file-upload.blade.php (1)

10-17: ⚡ Quick win

Add response structure validation for resilience.

The function accesses (await r.json()).attributes.url without validating that these nested properties exist. If the API response structure is unexpected or malformed, this will throw a TypeError with a cryptic message that gets caught and displayed as "Failed to get upload token", making debugging difficult.

🛡️ Suggested improvement for defensive error handling
 async fetchUploadUrl() {
     const r = await fetch(`/api/client/servers/${this.serverUuid}/files/upload`, {
         headers: { Accept: 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
         credentials: 'same-origin',
     });
     if (!r.ok) throw new Error(`upload url request failed (${r.status})`);
-    return (await r.json()).attributes.url;
+    const data = await r.json();
+    if (!data?.attributes?.url) {
+        throw new Error('Invalid upload URL response format');
+    }
+    return data.attributes.url;
 },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/views/filament/server/pages/file-upload.blade.php` around lines 10
- 17, The fetchUploadUrl method currently assumes the JSON response contains
attributes.url; change it to parse the JSON, validate that the parsed object has
a top-level attributes object and a string url (e.g., check typeof body ===
'object' && body.attributes && typeof body.attributes.url === 'string'), and if
not throw a descriptive Error (including the actual response body or a snippet)
so callers see a clear message instead of a cryptic TypeError; keep the existing
fetch and status check around serverUuid but replace the direct return of (await
r.json()).attributes.url with this validated extraction and error path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@resources/views/filament/server/pages/file-upload.blade.php`:
- Around line 10-17: The fetchUploadUrl method currently assumes the JSON
response contains attributes.url; change it to parse the JSON, validate that the
parsed object has a top-level attributes object and a string url (e.g., check
typeof body === 'object' && body.attributes && typeof body.attributes.url ===
'string'), and if not throw a descriptive Error (including the actual response
body or a snippet) so callers see a clear message instead of a cryptic
TypeError; keep the existing fetch and status check around serverUuid but
replace the direct return of (await r.json()).attributes.url with this validated
extraction and error path.

In `@resources/views/filament/server/pages/list-files.blade.php`:
- Around line 14-21: The fetchUploadUrl function currently returns (await
r.json()).attributes.url without validating the response shape; update
fetchUploadUrl to parse the JSON into a variable, verify that the top-level
object is present and that json.attributes is an object and json.attributes.url
is a non-empty string (or a valid URL), and throw a clear, descriptive Error
including the parsed response when the shape is invalid so callers see why
"Failed to get upload token" happened; keep the existing r.ok check and return
the validated url when present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6c603892-b90f-4578-98e2-d5a6e759bea1

📥 Commits

Reviewing files that changed from the base of the PR and between 38620a9 and f7996d8.

📒 Files selected for processing (3)
  • app/Filament/Server/Resources/Files/Pages/ListFiles.php
  • resources/views/filament/server/pages/file-upload.blade.php
  • resources/views/filament/server/pages/list-files.blade.php
💤 Files with no reviewable changes (1)
  • app/Filament/Server/Resources/Files/Pages/ListFiles.php

@Boy132 Boy132 requested a review from notAreYouScared May 13, 2026 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multi-File Uploading Fails with 404

1 participant