Stop the 503 page appearing during an update
Reported with the network panel open: `update` and `state` both answering 503, then the overlay, then Laravel's 503 page, then a reload. Three different screens for one event. Livewire's default on a failed request is to render the response body. So a wire:poll that happened to fire while the application was in maintenance mode swapped the whole console for the 503 page — the panel the operator was watching, replaced by an error page, for something that is not an error. A deployment IS maintenance mode; that is the event, not a fault. Three changes, and they are three because the request came from three places: The status endpoint is exempt from maintenance mode. It is the deployment's own status, read by the overlay every three seconds, and behind maintenance mode it answered 503 for the entire run — so the overlay could never name the step it was on. Safe to exempt: version, commit and a step name, behind the console's host and network guards, and it writes nothing. Livewire swallows a 503 instead of rendering it. For every request, not only the poll: any action taken in that window did the same thing. And the settings card stops polling for the duration. The overlay's own watcher is a plain fetch with no component state behind it, which is why it survives the restart; this poll cannot, and every attempt was a request that answered 503. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>feature/betriebsmodus v1.3.24
parent
98afd1d7e0
commit
82f95df04c
|
|
@ -21,6 +21,21 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
// The one endpoint that must answer WHILE the application is down.
|
||||
//
|
||||
// It is the deployment's own status, read by the overlay every three
|
||||
// seconds. Behind maintenance mode it answered 503 for the whole run,
|
||||
// so the overlay had nothing to show but "still waiting" and could not
|
||||
// name the step it was on — and any Livewire request that fired in the
|
||||
// same window swapped the console for Laravel's 503 page.
|
||||
//
|
||||
// Safe to exempt: it reports version, commit and a step name, it is
|
||||
// behind the console's host and network guards, and it writes nothing.
|
||||
$middleware->preventRequestsDuringMaintenance(except: [
|
||||
'update/state',
|
||||
'*/update/state',
|
||||
]);
|
||||
|
||||
$middleware->alias([
|
||||
'admin' => EnsureAdmin::class,
|
||||
'admin.host' => RestrictAdminHost::class,
|
||||
|
|
|
|||
|
|
@ -134,6 +134,23 @@ function recoverFrom419() {
|
|||
document.addEventListener('livewire:init', () => {
|
||||
window.Livewire.hook('request', ({ fail }) => {
|
||||
fail(({ status, preventDefault }) => {
|
||||
// 503: the application is in maintenance mode, which during a
|
||||
// deployment is not a fault — it is the event being watched.
|
||||
//
|
||||
// Livewire's default is to replace the document with the response
|
||||
// body, so a wire:poll that happened to fire mid-update swapped the
|
||||
// console for Laravel's 503 page: the operator saw the overlay,
|
||||
// then the error page, then the reload. Reported as "the 503 must
|
||||
// not appear at all during the update", and it must not.
|
||||
//
|
||||
// Swallowed for every Livewire request, not just the poll: any
|
||||
// action taken in that window would do the same thing.
|
||||
if (status === 503) {
|
||||
preventDefault();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (status !== 419) return;
|
||||
|
||||
preventDefault();
|
||||
|
|
|
|||
|
|
@ -34,8 +34,13 @@
|
|||
open. wire:poll below is unrelated and stays: it is Livewire's
|
||||
own refresh of this card's own details (log tail, elapsed time)
|
||||
for as long as a Livewire connection exists. --}}
|
||||
{{-- The poll stops while a run is in flight. The overlay's own
|
||||
watcher is what follows a deployment — a plain fetch with no
|
||||
component state behind it, which is why it survives the
|
||||
restart. This one cannot, and every attempt during maintenance
|
||||
mode was a Livewire request answering 503. --}}
|
||||
<div class="rounded-lg border border-line bg-surface p-6 shadow-xs animate-rise"
|
||||
wire:poll.{{ ($update['running'] || $update['checking']) ? '3s' : '30s' }}>
|
||||
@if (! $update['running']) wire:poll.{{ $update['checking'] ? '3s' : '30s' }} @endif>
|
||||
<div class="flex flex-wrap items-start justify-between gap-4">
|
||||
<div class="min-w-0">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
|
|
|
|||
|
|
@ -951,3 +951,29 @@ it('runs the workers as the same user as the web process', function () {
|
|||
"\nnormalise_ownership\n",
|
||||
))->toBe(2);
|
||||
});
|
||||
|
||||
it('keeps answering its own status while the application is down', function () {
|
||||
// The deployment's status is the one thing that must be readable DURING
|
||||
// the deployment. Behind maintenance mode it answered 503 for the whole
|
||||
// run, so the overlay had nothing to show but "still waiting" and could
|
||||
// never name the step it was on.
|
||||
expect(Illuminate\Support\Facades\File::get(base_path('bootstrap/app.php')))
|
||||
->toContain("preventRequestsDuringMaintenance(except: [")
|
||||
->toContain("'update/state'")
|
||||
->toContain("'*/update/state'");
|
||||
});
|
||||
|
||||
it('does not let a Livewire request during maintenance replace the page', function () {
|
||||
// Reported with the network panel open: `update` and `state` both 503, and
|
||||
// Livewire's default on a failed request is to render the response body —
|
||||
// so a wire:poll that happened to fire mid-deployment swapped the console
|
||||
// for Laravel's 503 page. Overlay, then error page, then reload.
|
||||
//
|
||||
// 503 is not a fault here. It IS the event being watched.
|
||||
$app = Illuminate\Support\Facades\File::get(resource_path('js/app.js'));
|
||||
|
||||
expect($app)->toContain('if (status === 503) {')
|
||||
// And the poll that used to fire it stops for the duration.
|
||||
->and(Illuminate\Support\Facades\File::get(resource_path('views/livewire/admin/settings.blade.php')))
|
||||
->toContain("@if (! \$update['running']) wire:poll");
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue