diff --git a/src/Http/Middleware/CP/HandleInertiaRequests.php b/src/Http/Middleware/CP/HandleInertiaRequests.php index a8b2ab69546..19a75c61777 100644 --- a/src/Http/Middleware/CP/HandleInertiaRequests.php +++ b/src/Http/Middleware/CP/HandleInertiaRequests.php @@ -3,9 +3,13 @@ namespace Statamic\Http\Middleware\CP; use Illuminate\Http\Request; +use Inertia\Inertia; use Inertia\Middleware; use Statamic\CP\Toasts\Manager; use Statamic\Statamic; +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\StreamedResponse; use function Statamic\trans as __; @@ -38,6 +42,15 @@ public function share(Request $request): array ]); } + public function onEmptyResponse(Request $request, Response $response): Response + { + if ($response instanceof StreamedResponse || $response instanceof BinaryFileResponse) { + return Inertia::location($request->fullUrl()); + } + + return parent::onEmptyResponse($request, $response); + } + private function logos() { if (! Statamic::pro()) { diff --git a/tests/Http/Middleware/HandleInertiaRequestsTest.php b/tests/Http/Middleware/HandleInertiaRequestsTest.php new file mode 100644 index 00000000000..8dfd56eb5d8 --- /dev/null +++ b/tests/Http/Middleware/HandleInertiaRequestsTest.php @@ -0,0 +1,78 @@ +streamDownload(fn () => print 'id,name', 'data.csv'); + }); + + Route::get('file-download-test', function () { + return response()->download(__FILE__); + }); + + Route::get('empty-response-test', function () { + return response()->noContent(200); + }); + }); + } + + #[Test] + public function it_converts_streamed_downloads_into_location_visits_for_inertia_requests() + { + // Inertia requests are made over XHR, which can't trigger downloads. Returning + // 409 + X-Inertia-Location makes the client do a full browser visit instead, + // which lets the browser handle the download natively. + + $this + ->actingAs(User::make()->makeSuper()) + ->get('/cp/streamed-download-test', ['X-Inertia' => 'true']) + ->assertStatus(409) + ->assertHeader('X-Inertia-Location', 'http://localhost/cp/streamed-download-test'); + } + + #[Test] + public function it_converts_file_downloads_into_location_visits_for_inertia_requests() + { + $this + ->actingAs(User::make()->makeSuper()) + ->get('/cp/file-download-test', ['X-Inertia' => 'true']) + ->assertStatus(409) + ->assertHeader('X-Inertia-Location', 'http://localhost/cp/file-download-test'); + } + + #[Test] + public function it_leaves_downloads_untouched_for_non_inertia_requests() + { + $this + ->actingAs(User::make()->makeSuper()) + ->get('/cp/streamed-download-test') + ->assertOk() + ->assertDownload('data.csv'); + } + + #[Test] + public function it_redirects_back_for_empty_inertia_responses() + { + $this + ->actingAs(User::make()->makeSuper()) + ->from('/cp/utilities') + ->get('/cp/empty-response-test', ['X-Inertia' => 'true']) + ->assertRedirect('/cp/utilities'); + } +}