mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-12 15:53:46 -04:00
feat(server): move management toggle to connection header and show connection first
Replace the dev-only management ownership section with an Enable/Disable management button in the Connection section header, and place the Connection section above the Server overview. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
2e67b70eec
commit
15909fa641
@@ -63,143 +63,6 @@
|
||||
<x-unsaved-bar action="submit"
|
||||
targets="name,description,ip,user,port,connectionTimeout,serverTimezone,wildcardDomain" />
|
||||
|
||||
<x-application.settings-section id="server-overview-section" title="Server overview"
|
||||
helper="Connection health, provider state, operating system, and hardware details.">
|
||||
<x-slot:actions>
|
||||
@if ($provider)
|
||||
<x-status-badge :label="$provider . ($providerStatus ? ' · ' . ucfirst($providerStatus) : '')"
|
||||
:type="$providerStatusType" />
|
||||
@if ($provider === 'Hetzner')
|
||||
<x-forms.button type="button" class="size-8! px-0!"
|
||||
wire:click.prevent="checkHetznerServerStatus(true)"
|
||||
title="Refresh provider status">
|
||||
<x-reicon name="refresh" class="size-3.5" />
|
||||
</x-forms.button>
|
||||
@elseif ($provider === 'DigitalOcean')
|
||||
<x-forms.button type="button" class="size-8! px-0!"
|
||||
wire:click.prevent="checkDigitalOceanDropletStatus(true)"
|
||||
title="Refresh provider status">
|
||||
<x-reicon name="refresh" class="size-3.5" />
|
||||
</x-forms.button>
|
||||
@elseif ($provider === 'Vultr')
|
||||
<x-forms.button type="button" class="size-8! px-0!"
|
||||
wire:click.prevent="checkVultrInstanceStatus(true)"
|
||||
title="Refresh provider status">
|
||||
<x-reicon name="refresh" class="size-3.5" />
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@if ($server->cloudProviderToken)
|
||||
@if ($provider === 'Hetzner' && !$server->isFunctional() && $hetznerServerStatus === 'off')
|
||||
<x-forms.button type="button" wire:click.prevent="startHetznerServer" isHighlighted
|
||||
canGate="update" :canResource="$server">
|
||||
Power On
|
||||
</x-forms.button>
|
||||
@elseif ($provider === 'DigitalOcean' && $digitalOceanDropletStatus === 'off')
|
||||
<x-forms.button type="button" wire:click.prevent="startDigitalOceanDroplet"
|
||||
isHighlighted canGate="update" :canResource="$server">
|
||||
Power On
|
||||
</x-forms.button>
|
||||
@elseif ($provider === 'Vultr' && $vultrInstanceStatus === 'stopped')
|
||||
<x-forms.button type="button" wire:click.prevent="startVultrInstance" isHighlighted
|
||||
canGate="update" :canResource="$server">
|
||||
Power On
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@endif
|
||||
@endif
|
||||
@if ($server->server_metadata)
|
||||
<x-forms.button type="button" class="size-8! px-0!"
|
||||
wire:click="refreshServerMetadata" title="Refresh server details">
|
||||
<x-reicon name="refresh" class="size-3.5" />
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@if ($server->isTransferredAway())
|
||||
<x-status-badge label="Transferred away" type="warning" />
|
||||
@else
|
||||
<x-status-badge :label="$server->isFunctional() ? 'Ready' : 'Validation required'"
|
||||
:type="$server->isFunctional() ? 'success' : 'warning'" />
|
||||
@endif
|
||||
</x-slot:actions>
|
||||
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="flex size-9 shrink-0 items-center justify-center rounded-lg bg-neutral-100 text-neutral-600 dark:bg-white/[0.06] dark:text-fg-dim">
|
||||
<x-reicon name="servers" class="size-4.5" />
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<p class="truncate text-sm font-medium text-neutral-950 dark:text-fg">
|
||||
{{ $server->name }}
|
||||
</p>
|
||||
<p class="mt-1 text-xs leading-5 text-neutral-500 dark:text-fg-dim">
|
||||
@if ($server->isTransferredAway())
|
||||
This server was migrated away from this Coolify instance and cannot be managed here.
|
||||
@elseif ($server->isFunctional())
|
||||
The server is reachable, validated, and ready to host resources.
|
||||
@else
|
||||
Validate the SSH connection before using this server.
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($server->server_metadata)
|
||||
@include('livewire.server.partials.server-details', ['server' => $server])
|
||||
@else
|
||||
<div class="mt-4 border-t border-neutral-200 pt-4 dark:border-white/[0.08]">
|
||||
<x-forms.button type="button" wire:click="refreshServerMetadata">
|
||||
<x-reicon name="refresh" class="size-3.5" />
|
||||
Fetch server details
|
||||
</x-forms.button>
|
||||
</div>
|
||||
@endif
|
||||
</x-application.settings-section>
|
||||
|
||||
@if (isDev())
|
||||
<x-application.settings-section id="server-management-ownership-section"
|
||||
title="Management ownership (Dev)"
|
||||
helper="Choose whether this Coolify instance can control this server.">
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div class="min-w-0">
|
||||
<div class="text-sm font-medium text-neutral-950 dark:text-fg">
|
||||
Manage this server from this instance
|
||||
</div>
|
||||
<p class="mt-1 max-w-2xl text-xs leading-5 text-neutral-500 dark:text-fg-dim">
|
||||
@if ($server->isTransferredAway())
|
||||
Automations are disabled here. Existing workloads continue to run on the server.
|
||||
@else
|
||||
This instance runs deployments, webhooks, scheduled tasks, backups, proxy updates,
|
||||
and monitoring for this server.
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button type="button" role="switch"
|
||||
aria-checked="{{ $server->isTransferredAway() ? 'false' : 'true' }}"
|
||||
wire:click="toggleManagement"
|
||||
wire:loading.attr="disabled"
|
||||
wire:target="toggleManagement"
|
||||
wire:confirm="{{ $server->isTransferredAway()
|
||||
? 'Take ownership of this server? Confirm that no other Coolify instance manages it.'
|
||||
: 'Stop managing this server from this instance? Deployments, webhooks, scheduled tasks, backups, proxy updates, and monitoring will be disabled here.' }}"
|
||||
@cannot('update', $server) disabled @endcannot
|
||||
class="relative inline-flex h-7 w-12 shrink-0 items-center rounded-full border transition-colors disabled:cursor-not-allowed disabled:opacity-50 {{ $server->isTransferredAway()
|
||||
? 'border-neutral-300 bg-neutral-200 dark:border-white/[0.14] dark:bg-white/[0.08]'
|
||||
: 'border-coollabs bg-coollabs dark:border-warning dark:bg-warning' }}">
|
||||
<span class="sr-only">Manage this server from this instance</span>
|
||||
<span aria-hidden="true"
|
||||
class="inline-block size-5 rounded-full bg-white shadow-sm transition-transform dark:bg-black {{ $server->isTransferredAway() ? 'translate-x-1' : 'translate-x-6' }}"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if ($server->isTransferredAway())
|
||||
<x-callout type="warning" title="Management is disabled on this instance" class="mt-4">
|
||||
Before you enable management, disable this server on every other Coolify instance. Two
|
||||
active instances can cause conflicting deployments, proxy changes, backups, and restarts.
|
||||
</x-callout>
|
||||
@endif
|
||||
</x-application.settings-section>
|
||||
@endif
|
||||
|
||||
<x-application.settings-section id="server-connection-section" title="Connection"
|
||||
helper="Configure how Coolify identifies, reaches, and validates this server.">
|
||||
<x-slot:actions>
|
||||
@@ -260,6 +123,25 @@
|
||||
{{ $server->isFunctional() ? 'Revalidate connection' : 'Validate connection' }}
|
||||
</x-forms.button>
|
||||
</x-process-dialog>
|
||||
@if (isDev())
|
||||
<x-forms.button type="button" wire:click="toggleManagement"
|
||||
wire:confirm="{{ $server->isTransferredAway()
|
||||
? 'Enable management? Confirm that no other Coolify instance manages this server. Two active instances can cause conflicting deployments, proxy changes, backups, and restarts.'
|
||||
: 'Disable management? Deployments, webhooks, scheduled tasks, backups, proxy updates, and monitoring will stop on this instance. Existing workloads continue to run.' }}"
|
||||
title="{{ $server->isTransferredAway()
|
||||
? 'Let this instance run deployments, webhooks, scheduled tasks, backups, proxy updates, and monitoring for this server'
|
||||
: 'Stop deployments, webhooks, scheduled tasks, backups, proxy updates, and monitoring for this server on this instance' }}"
|
||||
canGate="update" :canResource="$server">
|
||||
<x-reicon name="{{ $server->isTransferredAway() ? 'play-circle' : 'stop-circle' }}" class="size-3.5" />
|
||||
{{ $server->isTransferredAway() ? 'Enable management' : 'Disable management' }}
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@if ($server->isTransferredAway())
|
||||
<x-status-badge label="Transferred away" type="warning" />
|
||||
@else
|
||||
<x-status-badge :label="$server->isFunctional() ? 'Ready' : 'Validation required'"
|
||||
:type="$server->isFunctional() ? 'success' : 'warning'" />
|
||||
@endif
|
||||
</x-slot:actions>
|
||||
|
||||
@if ($server->isTransferredAway())
|
||||
@@ -335,6 +217,91 @@
|
||||
@endif
|
||||
</x-application.settings-section>
|
||||
|
||||
<x-application.settings-section id="server-overview-section" title="Server overview"
|
||||
helper="Provider state, operating system, and hardware details.">
|
||||
<x-slot:actions>
|
||||
@if ($provider)
|
||||
<x-status-badge :label="$provider . ($providerStatus ? ' · ' . ucfirst($providerStatus) : '')"
|
||||
:type="$providerStatusType" />
|
||||
@if ($provider === 'Hetzner')
|
||||
<x-forms.button type="button" class="size-8! px-0!"
|
||||
wire:click.prevent="checkHetznerServerStatus(true)"
|
||||
title="Refresh provider status">
|
||||
<x-reicon name="refresh" class="size-3.5" />
|
||||
</x-forms.button>
|
||||
@elseif ($provider === 'DigitalOcean')
|
||||
<x-forms.button type="button" class="size-8! px-0!"
|
||||
wire:click.prevent="checkDigitalOceanDropletStatus(true)"
|
||||
title="Refresh provider status">
|
||||
<x-reicon name="refresh" class="size-3.5" />
|
||||
</x-forms.button>
|
||||
@elseif ($provider === 'Vultr')
|
||||
<x-forms.button type="button" class="size-8! px-0!"
|
||||
wire:click.prevent="checkVultrInstanceStatus(true)"
|
||||
title="Refresh provider status">
|
||||
<x-reicon name="refresh" class="size-3.5" />
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@if ($server->cloudProviderToken)
|
||||
@if ($provider === 'Hetzner' && !$server->isFunctional() && $hetznerServerStatus === 'off')
|
||||
<x-forms.button type="button" wire:click.prevent="startHetznerServer" isHighlighted
|
||||
canGate="update" :canResource="$server">
|
||||
Power On
|
||||
</x-forms.button>
|
||||
@elseif ($provider === 'DigitalOcean' && $digitalOceanDropletStatus === 'off')
|
||||
<x-forms.button type="button" wire:click.prevent="startDigitalOceanDroplet"
|
||||
isHighlighted canGate="update" :canResource="$server">
|
||||
Power On
|
||||
</x-forms.button>
|
||||
@elseif ($provider === 'Vultr' && $vultrInstanceStatus === 'stopped')
|
||||
<x-forms.button type="button" wire:click.prevent="startVultrInstance" isHighlighted
|
||||
canGate="update" :canResource="$server">
|
||||
Power On
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@endif
|
||||
@endif
|
||||
@if ($server->server_metadata)
|
||||
<x-forms.button type="button" class="size-8! px-0!"
|
||||
wire:click="refreshServerMetadata" title="Refresh server details">
|
||||
<x-reicon name="refresh" class="size-3.5" />
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</x-slot:actions>
|
||||
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="flex size-9 shrink-0 items-center justify-center rounded-lg bg-neutral-100 text-neutral-600 dark:bg-white/[0.06] dark:text-fg-dim">
|
||||
<x-reicon name="servers" class="size-4.5" />
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<p class="truncate text-sm font-medium text-neutral-950 dark:text-fg">
|
||||
{{ $server->name }}
|
||||
</p>
|
||||
<p class="mt-1 text-xs leading-5 text-neutral-500 dark:text-fg-dim">
|
||||
@if ($server->isTransferredAway())
|
||||
This server was migrated away from this Coolify instance and cannot be managed here.
|
||||
@elseif ($server->isFunctional())
|
||||
The server is reachable, validated, and ready to host resources.
|
||||
@else
|
||||
Validate the SSH connection before using this server.
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($server->server_metadata)
|
||||
@include('livewire.server.partials.server-details', ['server' => $server])
|
||||
@else
|
||||
<div class="mt-4 border-t border-neutral-200 pt-4 dark:border-white/[0.08]">
|
||||
<x-forms.button type="button" wire:click="refreshServerMetadata">
|
||||
<x-reicon name="refresh" class="size-3.5" />
|
||||
Fetch server details
|
||||
</x-forms.button>
|
||||
</div>
|
||||
@endif
|
||||
</x-application.settings-section>
|
||||
|
||||
@if ($server->validation_logs)
|
||||
<x-application.settings-section title="Previous validation output"
|
||||
helper="The latest output produced while checking this server.">
|
||||
|
||||
@@ -144,18 +144,20 @@ test('team members cannot change server management ownership', function () {
|
||||
expect($this->server->fresh()->isTransferredAway())->toBeFalse();
|
||||
});
|
||||
|
||||
test('server overview explains the management ownership switch', function () {
|
||||
test('server overview shows a management button that matches the ownership state', function () {
|
||||
Livewire::test(Show::class, ['server_uuid' => $this->server->uuid])
|
||||
->assertSee('Manage this server from this instance')
|
||||
->assertSee('This instance runs deployments, webhooks, scheduled tasks, backups, proxy updates,')
|
||||
->assertSee('and monitoring for this server.');
|
||||
->assertSee('Disable management')
|
||||
->assertDontSee('Enable management')
|
||||
->call('toggleManagement')
|
||||
->assertSee('Enable management')
|
||||
->assertDontSee('Disable management');
|
||||
});
|
||||
|
||||
test('management ownership is not available outside development', function () {
|
||||
config(['app.env' => 'production']);
|
||||
|
||||
Livewire::test(Show::class, ['server_uuid' => $this->server->uuid])
|
||||
->assertDontSee('Manage this server from this instance')
|
||||
->assertDontSee('Disable management')
|
||||
->call('toggleManagement')
|
||||
->assertNotFound();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user