mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 21:18:11 -04:00
feat(ui): move maintenance mode to its own page with a confirmed toggle
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
53e21abe97
commit
17a9fa624e
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'],
|
||||
];
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<livewire:project.application.general :application="$application" />
|
||||
@elseif ($currentRoute === 'project.application.domains')
|
||||
<livewire:project.application.domains :application="$application" />
|
||||
@elseif ($currentRoute === 'project.application.maintenance')
|
||||
<livewire:project.shared.maintenance :resource="$application" />
|
||||
@elseif ($currentRoute === 'project.application.swarm' && $application->destination->server->isSwarm())
|
||||
<livewire:project.application.swarm :application="$application" />
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
:lazy="(bool) ($service->server ?? $service->destination?->server)?->isTrafficAnalyticsEnabled()" />
|
||||
@elseif ($currentRoute === 'project.service.domains')
|
||||
<livewire:project.service.domains :service="$service" />
|
||||
@elseif ($currentRoute === 'project.service.maintenance')
|
||||
<livewire:project.shared.maintenance :resource="$service" />
|
||||
@elseif ($currentRoute === 'project.service.environment-variables')
|
||||
<livewire:project.shared.environment-variable.all :resource="$service" />
|
||||
|
||||
@@ -3,14 +3,33 @@
|
||||
<x-application.settings-section id="maintenance-section" title="Maintenance mode"
|
||||
helper="Show a maintenance page with a 503 status on all domains of this resource. The containers keep running, and the change applies without a redeploy.">
|
||||
@if ($this->isSupported)
|
||||
<x-slot:badges>
|
||||
{{-- The inline wrapper puts the badge text on the same line as the title text. --}}
|
||||
<span>
|
||||
@if ($isMaintenanceEnabled)
|
||||
<x-status-badge label="On" type="success" />
|
||||
@else
|
||||
<x-status-badge label="Off" type="neutral" />
|
||||
@endif
|
||||
</span>
|
||||
</x-slot:badges>
|
||||
@can('update', $resource)
|
||||
<x-slot:actions>
|
||||
<x-modal-confirmation wire:key="maintenance-toggle-{{ $isMaintenanceEnabled ? 'on' : 'off' }}"
|
||||
:ignoreWire="false" submitAction="toggleMaintenance" :confirmWithText="false"
|
||||
:confirmWithPassword="false" :isErrorButton="! $isMaintenanceEnabled"
|
||||
:title="$isMaintenanceEnabled ? 'Disable maintenance mode?' : 'Enable maintenance mode?'"
|
||||
:buttonTitle="$isMaintenanceEnabled ? 'Disable maintenance' : 'Enable maintenance'"
|
||||
:step2ButtonText="$isMaintenanceEnabled ? 'Disable maintenance' : 'Enable maintenance'"
|
||||
:warningMessage="$isMaintenanceEnabled
|
||||
? 'Visitors can reach this resource again right away.'
|
||||
: 'Visitors cannot reach this resource until you disable maintenance mode.'"
|
||||
:actions="$isMaintenanceEnabled
|
||||
? ['All domains of this resource serve the resource again.']
|
||||
: ['All domains of this resource show the maintenance page with a 503 status.', 'The containers keep running.']" />
|
||||
</x-slot:actions>
|
||||
@endcan
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="grid gap-4 lg:grid-cols-2">
|
||||
<x-forms.listbox canGate="update" :canResource="$resource" id="isMaintenanceEnabled"
|
||||
label="Maintenance mode" onChange="instantSaveMaintenance" :options="[
|
||||
['value' => false, 'label' => 'Off: serve the resource'],
|
||||
['value' => true, 'label' => 'On: show the maintenance page'],
|
||||
]" />
|
||||
</div>
|
||||
<x-forms.textarea canGate="update" :canResource="$resource" id="maintenancePage" rows="10" monospace
|
||||
label="Custom maintenance page (HTML)"
|
||||
placeholder="Leave empty to use the Coolify default maintenance page."
|
||||
|
||||
@@ -424,6 +424,7 @@ Route::middleware(['auth', 'verified'])->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');
|
||||
|
||||
@@ -262,8 +262,7 @@ it('lets an admin enable maintenance and save a custom page', function () {
|
||||
->set('maintenancePage', '<h1>Back soon</h1>')
|
||||
->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', '<h1>Hacked</h1>')
|
||||
->call('submit')
|
||||
|
||||
Reference in New Issue
Block a user