Allow sendCommand on Starting or Running Servers (#1061)

* Replace `string` with `enum`

* Add title

* Allow sendCommand on `Starting` or `Running` servers

* refactor: Use Filament interfaces

* Use `getLabel` instead of `str->headline`

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>

---------

Co-authored-by: Boy132 <Boy132@users.noreply.github.com>
This commit is contained in:
MartinOscar 2025-03-06 15:55:00 +01:00 committed by GitHub
parent a9e4495c91
commit 1fdc428f3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 70 additions and 79 deletions

View File

@ -2,7 +2,11 @@
namespace App\Enums;
enum ContainerStatus: string
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;
enum ContainerStatus: string implements HasColor, HasIcon, HasLabel
{
// Docker Based
case Created = 'created';
@ -19,7 +23,7 @@ enum ContainerStatus: string
// HTTP Based
case Missing = 'missing';
public function icon(): string
public function getIcon(): string
{
return match ($this) {
@ -36,8 +40,17 @@ enum ContainerStatus: string
};
}
public function color(): string
public function getColor(bool $hex = false): string
{
if ($hex) {
return match ($this) {
self::Created, self::Restarting => '#2563EB',
self::Starting, self::Paused, self::Removing, self::Stopping => '#D97706',
self::Running => '#22C55E',
self::Exited, self::Missing, self::Dead, self::Offline => '#EF4444',
};
}
return match ($this) {
self::Created => 'primary',
self::Starting => 'warning',
@ -53,14 +66,19 @@ enum ContainerStatus: string
};
}
public function colorHex(): string
public function getLabel(): string
{
return match ($this) {
self::Created, self::Restarting => '#2563EB',
self::Starting, self::Paused, self::Removing, self::Stopping => '#D97706',
self::Running => '#22C55E',
self::Exited, self::Missing, self::Dead, self::Offline => '#EF4444',
};
return str($this->value)->title();
}
public function isOffline(): bool
{
return in_array($this, [ContainerStatus::Offline, ContainerStatus::Missing]);
}
public function isStartingOrRunning(): bool
{
return in_array($this, [ContainerStatus::Starting, ContainerStatus::Running]);
}
public function isStartingOrStopping(): bool

View File

@ -2,7 +2,11 @@
namespace App\Enums;
enum ServerState: string
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;
enum ServerState: string implements HasColor, HasIcon, HasLabel
{
case Normal = 'normal';
case Installing = 'installing';
@ -11,7 +15,7 @@ enum ServerState: string
case Suspended = 'suspended';
case RestoringBackup = 'restoring_backup';
public function icon(): string
public function getIcon(): string
{
return match ($this) {
self::Normal => 'tabler-heart',
@ -23,7 +27,7 @@ enum ServerState: string
};
}
public function color(): string
public function getColor(): string
{
return match ($this) {
self::Normal => 'primary',
@ -34,4 +38,9 @@ enum ServerState: string
self::RestoringBackup => 'primary',
};
}
public function getLabel(): string
{
return str($this->value)->headline();
}
}

View File

@ -2,8 +2,6 @@
namespace App\Filament\Admin\Resources\ServerResource\Pages;
use App\Enums\ContainerStatus;
use App\Enums\ServerState;
use App\Enums\SuspendAction;
use App\Filament\Admin\Resources\ServerResource;
use App\Filament\Admin\Resources\ServerResource\RelationManagers\AllocationsRelationManager;
@ -127,16 +125,9 @@ class EditServer extends EditRecord
ToggleButtons::make('condition')
->label(trans('admin/server.server_status'))
->formatStateUsing(fn (Server $server) => $server->condition)
->options(fn ($state) => collect(array_merge(ContainerStatus::cases(), ServerState::cases()))
->filter(fn ($condition) => $condition->value === $state)
->mapWithKeys(fn ($state) => [$state->value => str($state->value)->replace('_', ' ')->ucwords()])
)
->colors(collect(array_merge(ContainerStatus::cases(), ServerState::cases()))->mapWithKeys(
fn ($status) => [$status->value => $status->color()]
))
->icons(collect(array_merge(ContainerStatus::cases(), ServerState::cases()))->mapWithKeys(
fn ($status) => [$status->value => $status->icon()]
))
->options(fn ($state) => [$state->value => $state->getLabel()])
->colors(fn ($state) => [$state->value => $state->getColor()])
->icons(fn ($state) => [$state->value => $state->getIcon()])
->columnSpan([
'default' => 2,
'sm' => 1,

View File

@ -34,8 +34,8 @@ class ListServers extends ListRecords
->label(trans('admin/server.condition'))
->default('unknown')
->badge()
->icon(fn (Server $server) => $server->conditionIcon())
->color(fn (Server $server) => $server->conditionColor()),
->icon(fn (Server $server) => $server->condition->getIcon())
->color(fn (Server $server) => $server->condition->getColor()),
TextColumn::make('uuid')
->hidden()
->label('UUID')

View File

@ -75,7 +75,7 @@ class ServerConsole extends Widget
protected function canSendCommand(): bool
{
return $this->authorizeSendCommand() && !$this->server->isInConflictState() && $this->server->retrieveStatus() === 'running';
return $this->authorizeSendCommand() && !$this->server->isInConflictState() && $this->server->retrieveStatus()->isStartingOrRunning();
}
public function up(): void

View File

@ -9,7 +9,6 @@ use App\Models\Server;
use Carbon\CarbonInterface;
use Filament\Widgets\StatsOverviewWidget;
use Illuminate\Support\Number;
use Illuminate\Support\Str;
class ServerOverview extends StatsOverviewWidget
{
@ -38,7 +37,7 @@ class ServerOverview extends StatsOverviewWidget
private function status(): string
{
$status = Str::title($this->server->condition);
$status = $this->server->condition->getLabel();
$uptime = collect(cache()->get("servers.{$this->server->id}.uptime"))->last() ?? 0;
if ($uptime === 0) {
@ -52,10 +51,10 @@ class ServerOverview extends StatsOverviewWidget
public function cpuUsage(): string
{
$status = ContainerStatus::tryFrom($this->server->retrieveStatus());
$status = $this->server->retrieveStatus();
if ($status === ContainerStatus::Offline || $status === ContainerStatus::Missing) {
return 'Offline';
if ($status->isOffline()) {
return ContainerStatus::Offline->getLabel();
}
$data = collect(cache()->get("servers.{$this->server->id}.cpu_absolute"))->last(default: 0);
@ -66,10 +65,10 @@ class ServerOverview extends StatsOverviewWidget
public function memoryUsage(): string
{
$status = ContainerStatus::tryFrom($this->server->retrieveStatus());
$status = $this->server->retrieveStatus();
if ($status === ContainerStatus::Offline || $status === ContainerStatus::Missing) {
return 'Offline';
if ($status->isOffline()) {
return ContainerStatus::Offline->getLabel();
}
$latestMemoryUsed = collect(cache()->get("servers.{$this->server->id}.memory_bytes"))->last(default: 0);

View File

@ -114,7 +114,7 @@ use App\Services\Subusers\SubuserDeletionService;
*
* @property string[]|null $docker_labels
* @property string|null $ports
* @property-read mixed $condition
* @property-read ContainerStatus|ServerState $condition
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\EggVariable> $eggVariables
* @property-read int|null $egg_variables_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ServerVariable> $serverVariables
@ -438,17 +438,17 @@ class Server extends Model implements Validatable
])->toPsrResponse();
}
public function retrieveStatus(): string
public function retrieveStatus(): ContainerStatus
{
$status = cache()->get("servers.$this->uuid.container.status");
if ($status) {
return $status;
if ($status === null) {
$this->node->serverStatuses();
$status = cache()->get("servers.$this->uuid.container.status");
}
$this->node->serverStatuses();
return cache()->get("servers.$this->uuid.container.status") ?? 'missing';
return ContainerStatus::tryFrom($status) ?? ContainerStatus::Missing;
}
/**
@ -474,7 +474,7 @@ class Server extends Model implements Validatable
return 'Suspended';
}
if ($resourceAmount === 0) {
return 'Offline';
return ContainerStatus::Offline->getLabel();
}
return now()->subMillis($resourceAmount)->diffForHumans(syntax: CarbonInterface::DIFF_ABSOLUTE, short: true, parts: 4);
@ -499,34 +499,7 @@ class Server extends Model implements Validatable
public function condition(): Attribute
{
return Attribute::make(
get: fn () => $this->isSuspended() ? ServerState::Suspended->value : $this->status->value ?? $this->retrieveStatus(),
get: fn () => $this->isSuspended() ? ServerState::Suspended : $this->status ?? $this->retrieveStatus(),
);
}
public function conditionIcon(): string
{
if ($this->status === null) {
$containerStatus = ContainerStatus::from($this->retrieveStatus());
return $containerStatus->icon();
}
return $this->status->icon();
}
public function conditionColor(): string
{
if ($this->status === null) {
$containerStatus = ContainerStatus::from($this->retrieveStatus());
return $containerStatus->color();
}
return $this->status->color();
}
public function conditionColorHex(): string
{
return ContainerStatus::from($this->retrieveStatus())->colorHex();
}
}

View File

@ -2,6 +2,7 @@
namespace App\Services\Schedules;
use App\Enums\ContainerStatus;
use App\Models\Task;
use Exception;
use App\Models\Schedule;
@ -41,10 +42,10 @@ class ProcessScheduleService
// Check that the server is currently in a starting or running state before executing
// this schedule if this option has been set.
try {
$details = $this->serverRepository->setServer($schedule->server)->getDetails();
$state = $details['state'] ?? 'offline';
$state = fluent($this->serverRepository->setServer($schedule->server)->getDetails())->get('state') ?? ContainerStatus::Offline;
// If the server is stopping or offline just do nothing with this task.
if (in_array($state, ['offline', 'stopping'])) {
if ($state->isOffline()) {
$job->failed();
return;

View File

@ -18,7 +18,7 @@
<!-- Status Strip Outside the Box -->
<div
class="absolute left-0 top-1 bottom-0 w-1 rounded-lg"
style="background-color: {{ $server->conditionColorHex() }};">
style="background-color: {{ $server->condition->getColor(true) }};">
</div>
<!-- Card Component -->
@ -26,9 +26,9 @@
<!-- Header -->
<div class="flex items-center mb-5 gap-2">
<x-filament::icon-button
:icon="$server->conditionIcon()"
:color="$server->conditionColor()"
:tooltip="\Illuminate\Support\Str::title($server->condition)"
:icon="$server->condition->getIcon()"
:color="$server->condition->getColor()"
:tooltip="$server->condition->getLabel()"
size="xl"
/>
<h2 class="text-xl font-bold">