mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-14 16:52:45 -04:00
Livewire rebuilds listener names from the session team on every event call. After a team switch in another tab, open pages still listen on the old team channel, so events failed with EventHandlerDoesNotExist (500), or ran handlers that load resources scoped to the new team (404). Store the team at mount and build team channel listeners from it. Events for a team that is no longer the session team are ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
235 lines
8.7 KiB
PHP
235 lines
8.7 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Project\Application;
|
|
|
|
use App\Actions\Application\StopApplication;
|
|
use App\Actions\Docker\GetContainersStatus;
|
|
use App\Models\Application;
|
|
use App\Models\ApplicationDeploymentQueue;
|
|
use App\Traits\ListensToTeamChannel;
|
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
|
use Livewire\Component;
|
|
|
|
class Heading extends Component
|
|
{
|
|
use AuthorizesRequests;
|
|
use ListensToTeamChannel;
|
|
|
|
public Application $application;
|
|
|
|
public ?string $lastDeploymentInfo = null;
|
|
|
|
public ?string $lastDeploymentLink = null;
|
|
|
|
public array $parameters;
|
|
|
|
public string $activeRouteName = '';
|
|
|
|
protected string $deploymentUuid;
|
|
|
|
public bool $docker_cleanup = true;
|
|
|
|
public function getListeners()
|
|
{
|
|
return [
|
|
'compose_loaded' => '$refresh',
|
|
'update_links' => '$refresh',
|
|
...$this->teamChannelListeners([
|
|
'ServiceStatusChanged' => 'checkStatus',
|
|
'ServiceChecked' => '$refresh',
|
|
]),
|
|
];
|
|
}
|
|
|
|
public function mount()
|
|
{
|
|
$this->syncActiveRouteName();
|
|
$this->parameters = [
|
|
'project_uuid' => $this->application->project()->uuid,
|
|
'environment_uuid' => $this->application->environment->uuid,
|
|
'application_uuid' => $this->application->uuid,
|
|
];
|
|
$lastDeployment = $this->application->get_last_successful_deployment();
|
|
$this->lastDeploymentInfo = data_get_str($lastDeployment, 'commit')->limit(7).' '.data_get($lastDeployment, 'commit_message');
|
|
$this->lastDeploymentLink = $this->application->gitCommitLink(data_get($lastDeployment, 'commit'));
|
|
}
|
|
|
|
/**
|
|
* Keep the active tab in sync with the real page route.
|
|
* Only update when the request is a full page route (not livewire.update),
|
|
* so wire:poll re-renders do not wipe the highlighted tab.
|
|
*/
|
|
protected function syncActiveRouteName(): void
|
|
{
|
|
$routeName = request()->route()?->getName();
|
|
|
|
if (is_string($routeName) && str_starts_with($routeName, 'project.application.')) {
|
|
$this->activeRouteName = $routeName;
|
|
}
|
|
}
|
|
|
|
public function checkStatus()
|
|
{
|
|
if ($this->application->destination->server->isFunctional()) {
|
|
GetContainersStatus::dispatch($this->application->destination->server);
|
|
} else {
|
|
$this->dispatch('error', 'Server is not functional.');
|
|
}
|
|
}
|
|
|
|
public function manualCheckStatus()
|
|
{
|
|
$this->checkStatus();
|
|
}
|
|
|
|
/**
|
|
* Log-page URL of the deployment currently running for this application, so a
|
|
* "Deploying… View log" indicator can link back to it after the user navigates
|
|
* away. Re-evaluated on the heading's 10s poll. Null when nothing is running.
|
|
*/
|
|
public function getRunningDeploymentUrlProperty(): ?string
|
|
{
|
|
return ApplicationDeploymentQueue::where('application_id', $this->application->id)
|
|
->whereIn('status', ['in_progress', 'queued'])
|
|
->orderByDesc('id')
|
|
->value('deployment_url');
|
|
}
|
|
|
|
public function force_deploy_without_cache()
|
|
{
|
|
try {
|
|
$this->authorize('deploy', $this->application);
|
|
|
|
$this->deploy(force_rebuild: true);
|
|
} catch (\Throwable $e) {
|
|
return handleError($e, $this);
|
|
}
|
|
}
|
|
|
|
public function deploy(bool $force_rebuild = false)
|
|
{
|
|
try {
|
|
$this->authorize('deploy', $this->application);
|
|
|
|
if ($this->application->build_pack === 'dockercompose' && is_null($this->application->docker_compose_raw)) {
|
|
$this->dispatch('error', 'Failed to deploy', 'Please load a Compose file first.');
|
|
|
|
return;
|
|
}
|
|
if ($this->application->destination->server->isSwarm() && str($this->application->docker_registry_image_name)->isEmpty()) {
|
|
$this->dispatch('error', 'Failed to deploy.', 'To deploy to a Swarm cluster you must set a Docker image name first.');
|
|
|
|
return;
|
|
}
|
|
if (data_get($this->application, 'settings.is_build_server_enabled') && str($this->application->docker_registry_image_name)->isEmpty()) {
|
|
$this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/build-server">documentation</a>');
|
|
|
|
return;
|
|
}
|
|
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
|
|
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/multiple-servers">documentation</a>');
|
|
|
|
return;
|
|
}
|
|
$this->setDeploymentUuid();
|
|
$result = queue_application_deployment(
|
|
application: $this->application,
|
|
deployment_uuid: $this->deploymentUuid,
|
|
force_rebuild: $force_rebuild,
|
|
);
|
|
if ($result['status'] === 'queue_full') {
|
|
$this->dispatch('error', 'Deployment queue full', $result['message']);
|
|
|
|
return;
|
|
}
|
|
if ($result['status'] === 'skipped') {
|
|
$this->dispatch('error', 'Deployment skipped', $result['message']);
|
|
|
|
return;
|
|
}
|
|
|
|
return redirectRoute($this, 'project.application.deployment.show', [
|
|
'project_uuid' => $this->parameters['project_uuid'],
|
|
'application_uuid' => $this->parameters['application_uuid'],
|
|
'deployment_uuid' => $this->deploymentUuid,
|
|
'environment_uuid' => $this->parameters['environment_uuid'],
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
return handleError($e, $this);
|
|
}
|
|
}
|
|
|
|
protected function setDeploymentUuid()
|
|
{
|
|
$this->deploymentUuid = new_public_id();
|
|
$this->parameters['deployment_uuid'] = $this->deploymentUuid;
|
|
}
|
|
|
|
public function stop()
|
|
{
|
|
try {
|
|
$this->authorize('deploy', $this->application);
|
|
|
|
$this->dispatch('info', 'Gracefully stopping application.<br/>It could take a while depending on the application.');
|
|
StopApplication::dispatch($this->application, false, $this->docker_cleanup);
|
|
auditLog('ui.application.stopped', [
|
|
'team_id' => $this->application->team()?->id,
|
|
'application_uuid' => $this->application->uuid,
|
|
'application_name' => $this->application->name,
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
return handleError($e, $this);
|
|
}
|
|
}
|
|
|
|
public function restart()
|
|
{
|
|
try {
|
|
$this->authorize('deploy', $this->application);
|
|
|
|
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
|
|
$this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/multiple-servers">documentation</a>');
|
|
|
|
return;
|
|
}
|
|
|
|
$this->setDeploymentUuid();
|
|
$result = queue_application_deployment(
|
|
application: $this->application,
|
|
deployment_uuid: $this->deploymentUuid,
|
|
restart_only: true,
|
|
);
|
|
if ($result['status'] === 'queue_full') {
|
|
$this->dispatch('error', 'Deployment queue full', $result['message']);
|
|
|
|
return;
|
|
}
|
|
if ($result['status'] === 'skipped') {
|
|
$this->dispatch('success', 'Deployment skipped', $result['message']);
|
|
|
|
return;
|
|
}
|
|
|
|
return redirectRoute($this, 'project.application.deployment.show', [
|
|
'project_uuid' => $this->parameters['project_uuid'],
|
|
'application_uuid' => $this->parameters['application_uuid'],
|
|
'deployment_uuid' => $this->deploymentUuid,
|
|
'environment_uuid' => $this->parameters['environment_uuid'],
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
return handleError($e, $this);
|
|
}
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
$this->syncActiveRouteName();
|
|
|
|
return view('livewire.project.application.heading', [
|
|
'checkboxes' => [
|
|
['id' => 'docker_cleanup', 'label' => __('resource.docker_cleanup')],
|
|
],
|
|
]);
|
|
}
|
|
}
|