diff --git a/app/Livewire/Project/Shared/Maintenance.php b/app/Livewire/Project/Shared/Maintenance.php
index 29c2263e5a..b79f9d1ff3 100644
--- a/app/Livewire/Project/Shared/Maintenance.php
+++ b/app/Livewire/Project/Shared/Maintenance.php
@@ -38,13 +38,17 @@ class Maintenance extends Component
&& in_array($server->proxyType(), [ProxyTypes::TRAEFIK->value, ProxyTypes::CADDY->value], true));
}
- public function instantSaveMaintenance(): void
+ /**
+ * Turn maintenance mode on or off. The header button calls this after the user confirms.
+ */
+ public function toggleMaintenance(): void
{
try {
$this->authorize('update', $this->resource);
- $this->resource->is_maintenance_enabled = $this->isMaintenanceEnabled;
+ $this->resource->is_maintenance_enabled = ! $this->resource->is_maintenance_enabled;
$this->resource->save();
$this->resource->syncMaintenancePage();
+ $this->isMaintenanceEnabled = (bool) $this->resource->is_maintenance_enabled;
$this->dispatch('success', $this->isMaintenanceEnabled ? 'Maintenance mode enabled.' : 'Maintenance mode disabled.');
} catch (\Throwable $e) {
$this->isMaintenanceEnabled = (bool) $this->resource->fresh()?->is_maintenance_enabled;
diff --git a/resources/views/components/application/configuration-sidebar.blade.php b/resources/views/components/application/configuration-sidebar.blade.php
index f94f010b3d..c049effcc4 100644
--- a/resources/views/components/application/configuration-sidebar.blade.php
+++ b/resources/views/components/application/configuration-sidebar.blade.php
@@ -119,6 +119,11 @@
'route' => 'project.application.resource-operations',
'active' => $currentRoute === 'project.application.resource-operations',
],
+ [
+ 'label' => 'Maintenance Mode',
+ 'route' => 'project.application.maintenance',
+ 'active' => $currentRoute === 'project.application.maintenance',
+ ],
[
'label' => 'Metrics',
'route' => 'project.application.metrics',
@@ -175,6 +180,7 @@
'Rollback' => 'time-back',
'Resource Limits' => 'cpu',
'Resource Operations' => 'server-update',
+ 'Maintenance Mode' => 'bandage',
'Metrics' => 'graph',
'Analytics' => 'analytics',
'Security' => 'shield-alert',
@@ -188,7 +194,7 @@
'Observe & troubleshoot' => ['Runtime Logs', 'Deployment Logs', 'Terminal', 'Files', 'Metrics', 'Analytics', 'Security'],
'Deploy' => ['Git Source', 'Servers', 'Preview Deployments'],
'Automation' => ['Scheduled Tasks', 'Webhooks', 'Backups'],
- 'Operations' => ['Resource Operations', 'Resource Limits', 'Rollback', 'Tags'],
+ 'Operations' => ['Resource Operations', 'Maintenance Mode', 'Resource Limits', 'Rollback', 'Tags'],
'Danger Zone' => ['Danger Zone'],
];
$groupedMenuItems = collect($menuGroups)
diff --git a/resources/views/components/service/configuration-sidebar.blade.php b/resources/views/components/service/configuration-sidebar.blade.php
index 258b48abd1..56961a6a8c 100644
--- a/resources/views/components/service/configuration-sidebar.blade.php
+++ b/resources/views/components/service/configuration-sidebar.blade.php
@@ -22,6 +22,7 @@
['label' => 'Scheduled Tasks', 'route' => 'project.service.scheduled-tasks.show', 'icon' => 'calendar'],
['label' => 'Webhooks', 'route' => 'project.service.webhooks', 'icon' => 'webhook'],
['label' => 'Resource Operations', 'route' => 'project.service.resource-operations', 'icon' => 'server-update'],
+ ['label' => 'Maintenance Mode', 'route' => 'project.service.maintenance', 'icon' => 'bandage'],
['label' => 'Security', 'route' => 'project.service.vulnerabilities', 'icon' => 'shield-alert', 'visible' => $imageScanningAllowed,
'security' => $imageScanningAllowed ? \App\Services\ImageScan\SecurityStatus::stored($service)->status->value : null],
['label' => 'Tags', 'route' => 'project.service.tags', 'icon' => 'tags'],
@@ -40,7 +41,7 @@
'Settings' => ['General', 'Domains', 'Environment Variables', 'Persistent Storage'],
'Observe & troubleshoot' => ['Runtime Logs', 'Terminal', 'Files', 'Analytics', 'Security'],
'Automation' => ['Scheduled Tasks', 'Webhooks', 'Backups'],
- 'Operations' => ['Resource Operations', 'Tags'],
+ 'Operations' => ['Resource Operations', 'Maintenance Mode', 'Tags'],
'Danger Zone' => ['Danger Zone'],
];
diff --git a/resources/views/livewire/project/application/configuration.blade.php b/resources/views/livewire/project/application/configuration.blade.php
index 0d5c61e8bf..26445d410d 100644
--- a/resources/views/livewire/project/application/configuration.blade.php
+++ b/resources/views/livewire/project/application/configuration.blade.php
@@ -14,6 +14,7 @@
@elseif ($currentRoute === 'project.application.domains')
+ @elseif ($currentRoute === 'project.application.maintenance')
@elseif ($currentRoute === 'project.application.swarm' && $application->destination->server->isSwarm())
diff --git a/resources/views/livewire/project/service/configuration.blade.php b/resources/views/livewire/project/service/configuration.blade.php
index c0cb85f513..b5ad51065e 100644
--- a/resources/views/livewire/project/service/configuration.blade.php
+++ b/resources/views/livewire/project/service/configuration.blade.php
@@ -81,6 +81,7 @@
:lazy="(bool) ($service->server ?? $service->destination?->server)?->isTrafficAnalyticsEnabled()" />
@elseif ($currentRoute === 'project.service.domains')
+ @elseif ($currentRoute === 'project.service.maintenance')
@elseif ($currentRoute === 'project.service.environment-variables')
diff --git a/resources/views/livewire/project/shared/maintenance.blade.php b/resources/views/livewire/project/shared/maintenance.blade.php
index 822d672f47..378c4dab4f 100644
--- a/resources/views/livewire/project/shared/maintenance.blade.php
+++ b/resources/views/livewire/project/shared/maintenance.blade.php
@@ -3,14 +3,33 @@
@if ($this->isSupported)
+
+ {{-- The inline wrapper puts the badge text on the same line as the title text. --}}
+
+ @if ($isMaintenanceEnabled)
+
+ @else
+
+ @endif
+
+
+ @can('update', $resource)
+
+
+
+ @endcan
-
-
-
group(function () {
Route::prefix('project/{project_uuid}/environment/{environment_uuid}/application/{application_uuid}')->group(function () {
Route::get('/', ApplicationConfiguration::class)->name('project.application.configuration');
Route::get('/domains', ApplicationConfiguration::class)->name('project.application.domains');
+ Route::get('/maintenance', ApplicationConfiguration::class)->name('project.application.maintenance');
Route::get('/swarm', ApplicationConfiguration::class)->name('project.application.swarm');
Route::get('/advanced', ApplicationConfiguration::class)->name('project.application.advanced');
Route::get('/environment-variables', ApplicationConfiguration::class)->name('project.application.environment-variables');
@@ -490,6 +491,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::prefix('project/{project_uuid}/environment/{environment_uuid}/service/{service_uuid}')->group(function () {
Route::get('/', ServiceConfiguration::class)->name('project.service.configuration');
Route::get('/domains', ServiceConfiguration::class)->name('project.service.domains');
+ Route::get('/maintenance', ServiceConfiguration::class)->name('project.service.maintenance');
Route::get('/logs', Logs::class)->name('project.service.logs');
Route::get('/analytics', ServiceConfiguration::class)->name('project.service.analytics');
Route::get('/vulnerabilities', ServiceConfiguration::class)->name('project.service.vulnerabilities');
diff --git a/tests/Feature/Proxy/MaintenanceModeTest.php b/tests/Feature/Proxy/MaintenanceModeTest.php
index e0791cbc63..fb2eb30faa 100644
--- a/tests/Feature/Proxy/MaintenanceModeTest.php
+++ b/tests/Feature/Proxy/MaintenanceModeTest.php
@@ -262,8 +262,7 @@ it('lets an admin enable maintenance and save a custom page', function () {
->set('maintenancePage', 'Back soon
')
->call('submit')
->assertDispatched('success')
- ->set('isMaintenanceEnabled', true)
- ->call('instantSaveMaintenance')
+ ->call('toggleMaintenance')
->assertDispatched('success');
$application->refresh();
@@ -292,8 +291,7 @@ it('does not let a member or another team change maintenance mode', function (st
session(['currentTeam' => $team]);
Livewire::test(Maintenance::class, ['resource' => $service])
- ->set('isMaintenanceEnabled', true)
- ->call('instantSaveMaintenance')
+ ->call('toggleMaintenance')
->assertDispatched('error')
->set('maintenancePage', 'Hacked
')
->call('submit')