feat(service): enhance service status handling and UI updates

This commit is contained in:
Andras Bacsai
2025-06-24 12:52:22 +02:00
parent 3643a388bf
commit 8fc79022f3
6 changed files with 36 additions and 8 deletions

View File

@@ -41,6 +41,6 @@ class StartService
} }
} }
return remote_process($commands, $service->server, type_uuid: $service->uuid); return remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged');
} }
} }

View File

@@ -6,7 +6,6 @@ use App\Actions\Docker\GetContainersStatus;
use App\Actions\Service\StartService; use App\Actions\Service\StartService;
use App\Actions\Service\StopService; use App\Actions\Service\StopService;
use App\Enums\ProcessStatus; use App\Enums\ProcessStatus;
use App\Events\ServiceStatusChanged;
use App\Models\Service; use App\Models\Service;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Livewire\Component; use Livewire\Component;
@@ -96,7 +95,7 @@ class Heading extends Component
public function start() public function start()
{ {
$activity = StartService::run($this->service, pullLatestImages: true); $activity = StartService::run($this->service, pullLatestImages: true);
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class); $this->dispatch('activityMonitor', $activity->id);
} }
public function forceDeploy() public function forceDeploy()
@@ -112,7 +111,7 @@ class Heading extends Component
$activity->save(); $activity->save();
} }
$activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true); $activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true);
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class); $this->dispatch('activityMonitor', $activity->id);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->dispatch('error', $e->getMessage()); $this->dispatch('error', $e->getMessage());
} }
@@ -136,7 +135,7 @@ class Heading extends Component
return; return;
} }
$activity = StartService::run($this->service, stopBeforeStart: true); $activity = StartService::run($this->service, stopBeforeStart: true);
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class); $this->dispatch('activityMonitor', $activity->id);
} }
public function pullAndRestartEvent() public function pullAndRestartEvent()
@@ -148,7 +147,7 @@ class Heading extends Component
return; return;
} }
$activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true); $activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true);
$this->dispatch('activityMonitor', $activity->id, ServiceStatusChanged::class); $this->dispatch('activityMonitor', $activity->id);
} }
public function render() public function render()

View File

@@ -2,6 +2,7 @@
namespace App\Models; namespace App\Models;
use App\Enums\ProcessStatus;
use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
@@ -9,6 +10,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
use Spatie\Activitylog\Models\Activity;
use Spatie\Url\Url; use Spatie\Url\Url;
use Visus\Cuid2\Cuid2; use Visus\Cuid2\Cuid2;
@@ -116,6 +118,18 @@ class Service extends BaseModel
return (bool) str($this->status)->contains('exited'); return (bool) str($this->status)->contains('exited');
} }
public function isStarting(): bool
{
try {
$activity = Activity::where('properties->type_uuid', $this->uuid)->latest()->first();
$status = data_get($activity, 'properties.status');
return $status === ProcessStatus::QUEUED->value || $status === ProcessStatus::IN_PROGRESS->value;
} catch (\Throwable) {
return false;
}
}
public function type() public function type()
{ {
return 'service'; return 'service';
@@ -159,6 +173,10 @@ class Service extends BaseModel
public function getStatusAttribute() public function getStatusAttribute()
{ {
if ($this->isStarting()) {
return 'starting:unhealthy';
}
$applications = $this->applications; $applications = $this->applications;
$databases = $this->databases; $databases = $this->databases;

View File

@@ -1,5 +1,7 @@
@if (str($complexStatus)->contains('running')) @if (str($complexStatus)->contains('running'))
<x-status.running :status="$complexStatus" /> <x-status.running :status="$complexStatus" />
@elseif(str($complexStatus)->contains('starting'))
<x-status.restarting :status="$complexStatus" />
@elseif(str($complexStatus)->contains('restarting')) @elseif(str($complexStatus)->contains('restarting'))
<x-status.restarting :status="$complexStatus" /> <x-status.restarting :status="$complexStatus" />
@elseif(str($complexStatus)->contains('degraded')) @elseif(str($complexStatus)->contains('degraded'))

View File

@@ -72,6 +72,9 @@
<template x-if="item.status.startsWith('exited')"> <template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge-dashboard"></div> <div title="exited" class="bg-error badge-dashboard"></div>
</template> </template>
<template x-if="item.status.startsWith('starting')">
<div title="starting" class="bg-warning badge-dashboard"></div>
</template>
<template x-if="item.status.startsWith('restarting')"> <template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge-dashboard"></div> <div title="restarting" class="bg-warning badge-dashboard"></div>
</template> </template>
@@ -118,6 +121,9 @@
<template x-if="item.status.startsWith('exited')"> <template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge-dashboard"></div> <div title="exited" class="bg-error badge-dashboard"></div>
</template> </template>
<template x-if="item.status.startsWith('starting')">
<div title="starting" class="bg-warning badge-dashboard"></div>
</template>
<template x-if="item.status.startsWith('restarting')"> <template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge-dashboard"></div> <div title="restarting" class="bg-warning badge-dashboard"></div>
</template> </template>
@@ -164,6 +170,9 @@
<template x-if="item.status.startsWith('exited')"> <template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge-dashboard"></div> <div title="exited" class="bg-error badge-dashboard"></div>
</template> </template>
<template x-if="item.status.startsWith('starting')">
<div title="starting" class="bg-warning badge-dashboard"></div>
</template>
<template x-if="item.status.startsWith('restarting')"> <template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge-dashboard"></div> <div title="restarting" class="bg-warning badge-dashboard"></div>
</template> </template>

View File

@@ -136,7 +136,7 @@
<script> <script>
$wire.$on('stopEvent', () => { $wire.$on('stopEvent', () => {
$wire.$dispatch('info', $wire.$dispatch('info',
'Gracefully stopping service.<br/>It could take a while depending on the service.'); 'Gracefully stopping service.<br/><br/>It could take a while depending on the service.');
$wire.$call('stop'); $wire.$call('stop');
}); });
$wire.$on('startEvent', async () => { $wire.$on('startEvent', async () => {
@@ -163,7 +163,7 @@
return; return;
} }
$wire.$dispatch('info', $wire.$dispatch('info',
'Gracefully stopping service.<br/>It could take a while depending on the service.'); 'Gracefully stopping service.<br/><br/>It could take a while depending on the service.');
window.dispatchEvent(new CustomEvent('startservice')); window.dispatchEvent(new CustomEvent('startservice'));
$wire.$call('restart'); $wire.$call('restart');
}); });