Small fixes before release (#5999)

* chore(version): update coolify-realtime to version 1.0.9 in docker-compose and versions files

* feat(migration): add is_sentinel_enabled column to server_settings with default true

* fix(migration): update default value handling for is_sentinel_enabled column in server_settings

* feat(seeder): dispatch StartProxy action for each server in ProductionSeeder

* feat(seeder): add CheckAndStartSentinelJob dispatch for each server in ProductionSeeder

* fix(seeder): conditionally dispatch CheckAndStartSentinelJob based on server's sentinel status

* feat(seeder): conditionally dispatch StartProxy action based on proxy check result

* refactor(ui): terminal

* refactor(ui): remove terminal header from execute-container-command view

* refactor(ui): remove unnecessary padding from deployment, backup, and logs sections
This commit is contained in:
Andras Bacsai
2025-06-17 14:37:58 +02:00
committed by GitHub
parent cabcb88adb
commit 2f6dfd0fea
16 changed files with 65 additions and 106 deletions

View File

@@ -13,8 +13,6 @@ class ExecuteContainerCommand extends Component
{
public $selected_container = 'default';
public $container;
public Collection $containers;
public $parameters;
@@ -23,13 +21,9 @@ class ExecuteContainerCommand extends Component
public string $type;
public Server $server;
public Collection $servers;
public bool $hasShell = true;
public bool $isConnecting = true;
public bool $isConnecting = false;
protected $rules = [
'server' => 'required',
@@ -78,8 +72,9 @@ class ExecuteContainerCommand extends Component
} elseif (data_get($this->parameters, 'server_uuid')) {
$this->type = 'server';
$this->resource = Server::where('uuid', $this->parameters['server_uuid'])->firstOrFail();
$this->server = $this->resource;
$this->servers = $this->servers->push($this->resource);
}
$this->servers = $this->servers->sortByDesc(fn ($server) => $server->isTerminalEnabled());
}
public function loadContainers()
@@ -97,7 +92,7 @@ class ExecuteContainerCommand extends Component
}
foreach ($containers as $container) {
// if container state is running
if (data_get($container, 'State') === 'running') {
if (data_get($container, 'State') === 'running' && $server->isTerminalEnabled()) {
$payload = [
'server' => $server,
'container' => $container,
@@ -106,7 +101,7 @@ class ExecuteContainerCommand extends Component
}
}
} elseif (data_get($this->parameters, 'database_uuid')) {
if ($this->resource->isRunning()) {
if ($this->resource->isRunning() && $server->isTerminalEnabled()) {
$this->containers = $this->containers->push([
'server' => $server,
'container' => [
@@ -116,7 +111,7 @@ class ExecuteContainerCommand extends Component
}
} elseif (data_get($this->parameters, 'service_uuid')) {
$this->resource->applications()->get()->each(function ($application) {
if ($application->isRunning()) {
if ($application->isRunning() && $this->resource->server->isTerminalEnabled()) {
$this->containers->push([
'server' => $this->resource->server,
'container' => [
@@ -137,41 +132,24 @@ class ExecuteContainerCommand extends Component
});
}
}
if ($this->containers->count() > 0) {
$this->container = $this->containers->first();
}
if ($this->containers->count() === 1) {
$this->selected_container = data_get($this->containers->first(), 'container.Names');
}
}
private function checkShellAvailability(Server $server, string $container): bool
{
$escapedContainer = escapeshellarg($container);
try {
instant_remote_process([
"docker exec {$escapedContainer} bash -c 'exit 0' 2>/dev/null || ".
"docker exec {$escapedContainer} sh -c 'exit 0' 2>/dev/null",
], $server);
return true;
} catch (\Throwable) {
return false;
}
}
#[On('connectToServer')]
public function connectToServer()
{
try {
if ($this->server->isForceDisabled()) {
$server = $this->servers->first();
if ($server->isForceDisabled()) {
throw new \RuntimeException('Server is disabled.');
}
$this->dispatch(
'send-terminal-command',
false,
data_get($this->server, 'name'),
data_get($this->server, 'uuid')
data_get($server, 'name'),
data_get($server, 'uuid')
);
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -222,11 +200,6 @@ class ExecuteContainerCommand extends Component
throw new \RuntimeException('Server ownership verification failed.');
}
$this->hasShell = $this->checkShellAvailability($server, data_get($container, 'container.Names'));
if (! $this->hasShell) {
return;
}
$this->dispatch(
'send-terminal-command',
true,

View File

@@ -45,7 +45,7 @@ class Terminal extends Component
{
$server = Server::ownedByCurrentTeam()->whereUuid($serverUuid)->firstOrFail();
if (! $server->isTerminalEnabled() || $server->isForceDisabled()) {
throw new \RuntimeException('Terminal access is disabled on this server.');
abort(403, 'Terminal access is disabled on this server.');
}
if ($isContainer) {

View File

@@ -2,6 +2,7 @@
namespace Database\Seeders;
use App\Actions\Proxy\CheckProxy;
use App\Actions\Proxy\StartProxy;
use App\Data\ServerMetadata;
use App\Enums\ProxyStatus;
@@ -124,9 +125,14 @@ class ProductionSeeder extends Seeder
$server->settings->is_reachable = true;
$server->settings->is_usable = true;
$server->settings->save();
$shouldStart = CheckProxy::run($server);
if ($shouldStart) {
StartProxy::dispatch($server);
}
if ($server->isSentinelEnabled()) {
CheckAndStartSentinelJob::dispatch($server);
}
}
if (StandaloneDocker::find(0) == null) {
StandaloneDocker::create([

View File

@@ -514,7 +514,6 @@ export function initializeTerminalComponent() {
const currentRows = this.term.rows;
if (cols !== currentCols || rows !== currentRows) {
console.log(`[Terminal] Resizing terminal: ${currentCols}x${currentRows} -> ${cols}x${rows}`);
this.term.resize(cols, rows);
this.sendMessage({
resize: { cols: cols, rows: rows }

View File

@@ -6,7 +6,7 @@
<livewire:project.shared.configuration-checker :resource="$application" />
<livewire:project.application.heading :application="$application" />
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
<div class="flex flex-col h-full gap-8 sm:flex-row">
<div class="flex flex-col items-start gap-2 min-w-fit">
<a class='menu-item' wire:current.exact="menu-item-active"
href="{{ route('project.application.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">General</a>

View File

@@ -5,7 +5,7 @@
<livewire:project.application.heading :application="$application" />
<div class="flex flex-col gap-2 pb-10"
@if (!$skip) wire:poll.5000ms='reload_deployments' @endif>
<div class="flex items-end gap-2 pt-4">
<div class="flex items-end gap-2">
<h2>Deployments <span class="text-xs">({{ $deployments_count }})</span></h2>
@if ($deployments_count > 0)
<x-forms.button disabled="{{ !$show_prev }}" wire:click="previous_page('{{ $default_take }}')">

View File

@@ -1,4 +1,4 @@
<nav wire:poll.10000ms="checkStatus">
<nav wire:poll.10000ms="checkStatus" class="pb-6">
<x-resources.breadcrumbs :resource="$application" :parameters="$parameters" :title="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
<div class="navbar-main">
<nav class="flex shrink-0 gap-6 items-center whitespace-nowrap scrollbar min-h-10">

View File

@@ -5,7 +5,7 @@
<h1>Backups</h1>
<livewire:project.shared.configuration-checker :resource="$database" />
<livewire:project.database.heading :database="$database" />
<div class="pt-6">
<div>
<div class="flex gap-2">
<h2 class="pb-4">Scheduled Backups</h2>
<x-modal-input buttonTitle="+ Add" title="New Scheduled Backup">

View File

@@ -5,7 +5,7 @@
<h1>Configuration</h1>
<livewire:project.shared.configuration-checker :resource="$database" />
<livewire:project.database.heading :database="$database" />
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
<div class="flex flex-col h-full gap-8 sm:flex-row">
<div class="flex flex-col items-start gap-2 min-w-fit">
<a class='menu-item' wire:current.exact="menu-item-active"
href="{{ route('project.database.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">General</a>

View File

@@ -1,4 +1,4 @@
<nav wire:poll.10000ms="checkStatus">
<nav wire:poll.10000ms="checkStatus" class="pb-6">
<x-resources.breadcrumbs :resource="$database" :parameters="$parameters" />
<x-slide-over @startdatabase.window="slideOverOpen = true" closeWithX fullScreen>
<x-slot:title>Database Startup</x-slot:title>

View File

@@ -4,7 +4,7 @@
</x-slot>
<livewire:project.service.heading :service="$service" :parameters="$parameters" :query="$query" />
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
<div class="flex flex-col h-full gap-8 sm:flex-row">
<div class="flex flex-col items-start gap-2 min-w-fit">
<a class="menu-item sm:min-w-fit" target="_blank" href="{{ $service->documentation() }}">Documentation
<x-external-link /></a>

View File

@@ -1,4 +1,4 @@
<div wire:poll.10000ms="checkStatus">
<div wire:poll.10000ms="checkStatus" class="pb-6">
<livewire:project.shared.configuration-checker :resource="$service" />
<x-slide-over @startservice.window="slideOverOpen = true" closeWithX fullScreen>
<x-slot:title>Service Startup</x-slot:title>

View File

@@ -1,6 +1,6 @@
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }">
<livewire:project.service.heading :service="$service" :parameters="$parameters" :query="$query" />
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
<div class="flex flex-col h-full gap-8 sm:flex-row">
<div class="flex flex-col items-start gap-2 min-w-fit">
<a class="menu-item"
class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-active' : '' }}"
@@ -35,7 +35,7 @@
</div>
@if ($serviceDatabase?->isBackupSolutionAvailable() || $serviceDatabase?->is_migrated)
<div x-cloak x-show="activeTab === 'backups'">
<div class="flex gap-2 ">
<div class="flex gap-2">
<h2 class="pb-4">Scheduled Backups</h2>
@if (filled($serviceDatabase->custom_type) || !$serviceDatabase->is_migrated)
<x-modal-input buttonTitle="+ Add" title="New Scheduled Backup">

View File

@@ -13,50 +13,15 @@
@elseif ($type === 'service')
<livewire:project.shared.configuration-checker :resource="$resource" />
<livewire:project.service.heading :service="$resource" :parameters="$parameters" title="Terminal" />
@elseif ($type === 'server')
<livewire:server.navbar :server="$server" />
@endif
@if (!$hasShell)
<div class="flex items-center justify-center w-full py-4 mx-auto">
<div class="p-4 w-full rounded border dark:bg-coolgray-100 dark:border-coolgray-300">
<div class="flex flex-col items-center justify-center space-y-4">
<svg class="w-12 h-12 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<div class="text-center">
<h3 class="text-lg font-medium">Terminal Not Available</h3>
<p class="mt-2 text-sm text-gray-500">No shell (bash/sh) is available in this container. Please
ensure either bash or sh is installed to use the terminal.</p>
</div>
</div>
</div>
</div>
@else
@if ($type === 'server')
<form class="w-full flex gap-2 items-start" wire:submit="$dispatchSelf('connectToServer')"
wire:init="$dispatchSelf('connectToServer')">
@if ($type === 'application' || $type === 'database' || $type === 'service')
<h2 class="pb-4">Terminal</h2>
<x-forms.button :disabled="$isConnecting"
type="submit">{{ $isConnecting ? 'Connecting...' : 'Reconnect' }}</x-forms.button>
</form>
<div class="mx-auto w-full">
<livewire:project.shared.terminal />
</div>
@else
@if (count($containers) === 0)
<div class="pt-4">No containers are running.</div>
<div>No containers are running or terminal access is disabled on this server.</div>
@else
@if (count($containers) === 1)
<form class="w-full flex gap-2 items-start pt-4" wire:submit="$dispatchSelf('connectToContainer')"
wire:init="$dispatchSelf('connectToContainer')">
<h2 class="pb-4">Terminal</h2>
<x-forms.button :disabled="$isConnecting"
type="submit">{{ $isConnecting ? 'Connecting...' : 'Reconnect' }}</x-forms.button>
</form>
@else
<form class="w-full pt-4 flex gap-2 flex-col" wire:submit="$dispatchSelf('connectToContainer')">
@foreach ($containers as $container)
<form class="w-full flex gap-2 items-end" wire:submit="$dispatchSelf('connectToContainer')">
<x-forms.select label="Container" id="container" required wire:model="selected_container">
@foreach ($containers as $container)
@if ($loop->first)
@@ -68,14 +33,30 @@
</option>
@endforeach
</x-forms.select>
<x-forms.button :disabled="$isConnecting" class="w-full"
<x-forms.button :disabled="$isConnecting"
type="submit">{{ $isConnecting ? 'Connecting...' : 'Connect' }}</x-forms.button>
</form>
@endif
@endforeach
<div class="mx-auto w-full">
<livewire:project.shared.terminal />
</div>
@endif
@endif
@if ($type === 'server')
<livewire:server.navbar :server="$servers->first()" />
@if ($servers->first()->isTerminalEnabled())
<form class="w-full flex gap-2 items-start" wire:submit="$dispatchSelf('connectToServer')"
wire:init="$dispatchSelf('connectToServer')">
<h2 class="pb-4">Terminal</h2>
<x-forms.button :disabled="$isConnecting"
type="submit">{{ $isConnecting ? 'Connecting...' : 'Connect' }}</x-forms.button>
</form>
<div class="mx-auto w-full">
<livewire:project.shared.terminal />
</div>
@else
<div>Terminal access is disabled on this server.</div>
@endif
@endif
</div>

View File

@@ -6,7 +6,7 @@
@if ($type === 'application')
<h1>Logs</h1>
<livewire:project.application.heading :application="$resource" />
<div class="pt-4">
<div>
<h2>Logs</h2>
@if (str($status)->contains('exited'))
<div class="pt-2">The resource is not running.</div>
@@ -41,7 +41,7 @@
@elseif ($type === 'database')
<h1>Logs</h1>
<livewire:project.database.heading :database="$resource" />
<div class="pt-4">
<div>
<h2>Logs</h2>
@if (str($status)->contains('exited'))
<div class="pt-2">The resource is not running.</div>
@@ -65,7 +65,7 @@
</div>
@elseif ($type === 'service')
<livewire:project.service.heading :service="$resource" :parameters="$parameters" :query="$query" title="Logs" />
<div class="pt-4">
<div>
<h2>Logs</h2>
@if (str($status)->contains('exited'))
<div class="pt-2">The resource is not running.</div>

View File

@@ -55,7 +55,7 @@
<div class="subtitle">{{ data_get($server, 'name') }}</div>
<div class="navbar-main">
<nav
class="flex items-center gap-6 overflow-x-scroll sm:overflow-x-hidden scrollbar min-h-10 whitespace-nowrap">
class="flex items-center gap-6 overflow-x-scroll sm:overflow-x-hidden scrollbar min-h-10 whitespace-nowrap pt-2">
<a class="{{ request()->routeIs('server.show') ? 'dark:text-white' : '' }}"
href="{{ route('server.show', [
'server_uuid' => data_get($server, 'uuid'),