v4.0.0-beta.415 (#5722)

* feat(README): add InterviewPal sponsorship link and corresponding SVG icon

* chore(versions): update coolify version to 4.0.0-beta.413 and nightly version to 4.0.0-beta.414 in configuration files

* fix(terminal): enhance WebSocket client verification with authorized IPs in terminal server

* chore(versions): update realtime version to 1.0.8 in versions.json

* chore(versions): update realtime version to 1.0.8 in versions.json

* chore(docker): update soketi image version to 1.0.8 in production configuration files

* chore(versions): update coolify version to 4.0.0-beta.414 and nightly version to 4.0.0-beta.415 in configuration files

* fix(ApplicationDeploymentJob): ensure source is an object before checking GitHub app properties

* fix(ui): Disable livewire navigate feature (causing spam of setInterval())

* fix(ui): Remove required attribute from image input in service application view

* fix(ui): Change application image validation to be nullable in service application view

* fix(Server): Correct proxy path formatting for Traefik proxy type
This commit is contained in:
Andras Bacsai
2025-04-29 10:54:30 +02:00
committed by GitHub
parent 43e965010f
commit 1227448e53
45 changed files with 238 additions and 279 deletions

View File

@@ -9,30 +9,27 @@
<div class="flex flex-col h-full gap-8 pt-6 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]) }}"
wire:navigate>General</a>
href="{{ route('project.application.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">General</a>
<a class='menu-item' wire:current.exact="menu-item-active"
href="{{ route('project.application.advanced', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Advanced</a>
href="{{ route('project.application.advanced', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Advanced</a>
@if ($application->destination->server->isSwarm())
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.swarm', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Swarm Configuration</a>
href="{{ route('project.application.swarm', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Swarm
Configuration</a>
@endif
<a class='menu-item' wire:current.exact="menu-item-active"
href="{{ route('project.application.environment-variables', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Environment Variables</a>
href="{{ route('project.application.environment-variables', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Environment
Variables</a>
<a class='menu-item' wire:current.exact="menu-item-active"
href="{{ route('project.application.persistent-storage', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Persistent Storage</a>
href="{{ route('project.application.persistent-storage', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Persistent
Storage</a>
@if ($application->git_based())
<a class='menu-item' wire:current.exact="menu-item-active"
href="{{ route('project.application.source', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Git Source</a>
href="{{ route('project.application.source', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Git
Source</a>
@endif
<a class="menu-item flex items-center gap-2" wire:current.exact="menu-item-active"
href="{{ route('project.application.servers', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Servers
href="{{ route('project.application.servers', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Servers
@if (str($application->status)->contains('degraded'))
<span title="Some servers are unavailable">
<svg class="w-4 h-4 text-error" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
@@ -51,36 +48,32 @@
@endif
</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.scheduled-tasks.show', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Scheduled Tasks</a>
href="{{ route('project.application.scheduled-tasks.show', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Scheduled
Tasks</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.webhooks', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Webhooks</a>
href="{{ route('project.application.webhooks', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Webhooks</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.preview-deployments', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Preview Deployments</a>
href="{{ route('project.application.preview-deployments', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Preview
Deployments</a>
@if ($application->build_pack !== 'dockercompose')
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.healthcheck', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Healthcheck</a>
href="{{ route('project.application.healthcheck', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Healthcheck</a>
@endif
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.rollback', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Rollback</a>
href="{{ route('project.application.rollback', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Rollback</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.resource-limits', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Resource Limits</a>
href="{{ route('project.application.resource-limits', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Resource
Limits</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Resource Operations</a>
href="{{ route('project.application.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Resource
Operations</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Metrics</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.tags', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Tags</a>
href="{{ route('project.application.tags', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Tags</a>
<a class="menu-item" wire:current.exact="menu-item-active"
href="{{ route('project.application.danger', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
wire:navigate>Danger Zone</a>
href="{{ route('project.application.danger', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Danger
Zone</a>
</div>
<div class="w-full">
@if ($currentRoute === 'project.application.configuration')

View File

@@ -3,18 +3,21 @@
<h1>Deployments</h1>
<livewire:project.shared.configuration-checker :resource="$application" />
<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 flex-col gap-2 pb-10"
@if (!$skip) wire:poll.5000ms='reload_deployments' @endif>
<div class="flex items-end gap-2 pt-4">
<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 }}')">
<svg class="w-6 h-6" viewBox="0 0 24 24">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m14 6l-6 6l6 6z" />
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="m14 6l-6 6l6 6z" />
</svg>
</x-forms.button>
<x-forms.button disabled="{{ !$show_next }}" wire:click="next_page('{{ $default_take }}')">
<svg class="w-6 h-6" viewBox="0 0 24 24">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m10 18l6-6l-6-6z" />
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="m10 18l6-6l-6-6z" />
</svg>
</x-forms.button>
@endif
@@ -28,44 +31,57 @@
@forelse ($deployments as $deployment)
<div @class([
'p-2 border-l-2 bg-white dark:bg-coolgray-100',
'border-blue-500/50 border-dashed' => data_get($deployment, 'status') === 'in_progress',
'border-purple-500/50 border-dashed' => data_get($deployment, 'status') === 'queued',
'border-white border-dashed' => data_get($deployment, 'status') === 'cancelled-by-user',
'border-blue-500/50 border-dashed' =>
data_get($deployment, 'status') === 'in_progress',
'border-purple-500/50 border-dashed' =>
data_get($deployment, 'status') === 'queued',
'border-white border-dashed' =>
data_get($deployment, 'status') === 'cancelled-by-user',
'border-error' => data_get($deployment, 'status') === 'failed',
'border-success' => data_get($deployment, 'status') === 'finished',
])>
<a href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}" wire:navigate class="block">
<a href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}" class="block">
<div class="flex flex-col">
<div class="flex items-center gap-2 mb-2">
<span @class([
'px-3 py-1 rounded-md text-xs font-medium shadow-sm',
'bg-blue-100/80 text-blue-700 dark:bg-blue-500/20 dark:text-blue-300' => data_get($deployment, 'status') === 'in_progress',
'bg-purple-100/80 text-purple-700 dark:bg-purple-500/20 dark:text-purple-300' => data_get($deployment, 'status') === 'queued',
'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-200' => data_get($deployment, 'status') === 'failed',
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => data_get($deployment, 'status') === 'finished',
'bg-gray-100 text-gray-700 dark:bg-gray-600/30 dark:text-gray-300' => data_get($deployment, 'status') === 'cancelled-by-user',
'bg-blue-100/80 text-blue-700 dark:bg-blue-500/20 dark:text-blue-300' =>
data_get($deployment, 'status') === 'in_progress',
'bg-purple-100/80 text-purple-700 dark:bg-purple-500/20 dark:text-purple-300' =>
data_get($deployment, 'status') === 'queued',
'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-200' =>
data_get($deployment, 'status') === 'failed',
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' =>
data_get($deployment, 'status') === 'finished',
'bg-gray-100 text-gray-700 dark:bg-gray-600/30 dark:text-gray-300' =>
data_get($deployment, 'status') === 'cancelled-by-user',
])>
@php
$statusText = match(data_get($deployment, 'status')) {
$statusText = match (data_get($deployment, 'status')) {
'finished' => 'Success',
'in_progress' => 'In Progress',
'cancelled-by-user' => 'Cancelled',
'queued' => 'Queued',
default => ucfirst(data_get($deployment, 'status'))
default => ucfirst(data_get($deployment, 'status')),
};
@endphp
{{ $statusText }}
</span>
</div>
@if(data_get($deployment, 'status') !== 'queued')
@if (data_get($deployment, 'status') !== 'queued')
<div class="text-gray-600 dark:text-gray-400 text-sm">
Started: {{ formatDateInServerTimezone(data_get($deployment, 'created_at'), data_get($application, 'destination.server')) }}
@if($deployment->status !== 'in_progress' && $deployment->status !== 'cancelled-by-user')
<br>Ended: {{ formatDateInServerTimezone(data_get($deployment, 'finished_at'), data_get($application, 'destination.server')) }}
<br>Duration: {{ calculateDuration(data_get($deployment, 'created_at'), data_get($deployment, 'finished_at')) }}
<br>Finished {{ \Carbon\Carbon::parse(data_get($deployment, 'finished_at'))->diffForHumans() }}
Started:
{{ formatDateInServerTimezone(data_get($deployment, 'created_at'), data_get($application, 'destination.server')) }}
@if ($deployment->status !== 'in_progress' && $deployment->status !== 'cancelled-by-user')
<br>Ended:
{{ formatDateInServerTimezone(data_get($deployment, 'finished_at'), data_get($application, 'destination.server')) }}
<br>Duration:
{{ calculateDuration(data_get($deployment, 'created_at'), data_get($deployment, 'finished_at')) }}
<br>Finished
{{ \Carbon\Carbon::parse(data_get($deployment, 'finished_at'))->diffForHumans() }}
@elseif($deployment->status === 'in_progress')
<br>Running for: {{ calculateDuration(data_get($deployment, 'created_at'), now()) }}
<br>Running for:
{{ calculateDuration(data_get($deployment, 'created_at'), now()) }}
@endif
</div>
@endif
@@ -75,14 +91,14 @@
<div x-data="{ expanded: false }">
<div class="flex items-center gap-2">
<span class="font-medium">Commit:</span>
<a wire:navigate.prevent
href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}"
target="_blank"
class="underline">
<a .prevent
href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}"
target="_blank" class="underline">
{{ substr(data_get($deployment, 'commit'), 0, 7) }}
</a>
@if (!$deployment->commitMessage())
<span class="bg-gray-200/70 dark:bg-gray-600/20 px-2 py-0.5 rounded-md text-xs text-gray-800 dark:text-gray-100 border border-gray-400/30">
<span
class="bg-gray-200/70 dark:bg-gray-600/20 px-2 py-0.5 rounded-md text-xs text-gray-800 dark:text-gray-100 border border-gray-400/30">
@if (data_get($deployment, 'is_webhook'))
Webhook
@if (data_get($deployment, 'pull_request_id'))
@@ -101,21 +117,25 @@
@endif
@if ($deployment->commitMessage())
<span class="text-gray-600 dark:text-gray-400">-</span>
<a wire:navigate.prevent
href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}"
target="_blank"
class="text-gray-600 dark:text-gray-400 truncate max-w-md underline">
<a .prevent
href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}"
target="_blank"
class="text-gray-600 dark:text-gray-400 truncate max-w-md underline">
{{ Str::before($deployment->commitMessage(), "\n") }}
</a>
@if ($deployment->commitMessage() !== Str::before($deployment->commitMessage(), "\n"))
<button @click="expanded = !expanded"
class="text-gray-600 dark:text-gray-400 flex items-center gap-1">
<svg x-bind:class="{'rotate-180': expanded}" class="w-4 h-4 transition-transform" viewBox="0 0 24 24">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m6 9l6 6l6-6"/>
<svg x-bind:class="{ 'rotate-180': expanded }"
class="w-4 h-4 transition-transform" viewBox="0 0 24 24">
<path fill="none" stroke="currentColor"
stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="m6 9l6 6l6-6" />
</svg>
</button>
@endif
<span class="bg-gray-200/70 dark:bg-gray-600/20 px-2 py-0.5 rounded-md text-xs text-gray-800 dark:text-gray-100 border border-gray-400/30">
<span
class="bg-gray-200/70 dark:bg-gray-600/20 px-2 py-0.5 rounded-md text-xs text-gray-800 dark:text-gray-100 border border-gray-400/30">
@if (data_get($deployment, 'is_webhook'))
Webhook
@if (data_get($deployment, 'pull_request_id'))
@@ -134,11 +154,10 @@
@endif
</div>
@if ($deployment->commitMessage())
<div x-show="expanded"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 transform -translate-y-2"
x-transition:enter-end="opacity-100 transform translate-y-0"
class="mt-2 ml-4 text-gray-600 dark:text-gray-400">
<div x-show="expanded" x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 transform -translate-y-2"
x-transition:enter-end="opacity-100 transform translate-y-0"
class="mt-2 ml-4 text-gray-600 dark:text-gray-400">
{{ Str::after($deployment->commitMessage(), "\n") }}
</div>
@endif

View File

@@ -3,20 +3,20 @@
<div class="navbar-main">
<nav class="flex flex-shrink-0 gap-6 items-center whitespace-nowrap scrollbar min-h-10">
<a class="{{ request()->routeIs('project.application.configuration') ? 'dark:text-white' : '' }}"
wire:navigate href="{{ route('project.application.configuration', $parameters) }}">
href="{{ route('project.application.configuration', $parameters) }}">
Configuration
</a>
<a class="{{ request()->routeIs('project.application.deployment.index') ? 'dark:text-white' : '' }}"
wire:navigate href="{{ route('project.application.deployment.index', $parameters) }}">
href="{{ route('project.application.deployment.index', $parameters) }}">
<button>Deployments</button>
</a>
<a class="{{ request()->routeIs('project.application.logs') ? 'dark:text-white' : '' }}"
wire:navigate href="{{ route('project.application.logs', $parameters) }}">
href="{{ route('project.application.logs', $parameters) }}">
<button>Logs</button>
</a>
@if (!$application->destination->server->isSwarm())
<a class="{{ request()->routeIs('project.application.command') ? 'dark:text-white' : '' }}"
href="{{ route('project.application.command', $parameters) }}">
href="{{ route('project.application.command', $parameters) }}">
<button>Terminal</button>
</a>
@endif