@@ -4,3 +4,7 @@ APP_KEY=
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
DB_PASSWORD=
 | 
					DB_PASSWORD=
 | 
				
			||||||
REDIS_PASSWORD=
 | 
					REDIS_PASSWORD=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PUSHER_APP_ID=
 | 
				
			||||||
 | 
					PUSHER_APP_KEY=
 | 
				
			||||||
 | 
					PUSHER_APP_SECRET=
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								app/Events/TestEvent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/Events/TestEvent.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Events;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Broadcasting\Channel;
 | 
				
			||||||
 | 
					use Illuminate\Broadcasting\InteractsWithSockets;
 | 
				
			||||||
 | 
					use Illuminate\Broadcasting\PresenceChannel;
 | 
				
			||||||
 | 
					use Illuminate\Broadcasting\PrivateChannel;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Events\Dispatchable;
 | 
				
			||||||
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestEvent implements ShouldBroadcast
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use Dispatchable, InteractsWithSockets, SerializesModels;
 | 
				
			||||||
 | 
					    public $teamId;
 | 
				
			||||||
 | 
					    public function __construct()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->teamId = auth()->user()->currentTeam()->id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function broadcastOn(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            new PrivateChannel("custom.{$this->teamId}"),
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,7 +10,6 @@ class Dashboard extends Component
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public $projects = [];
 | 
					    public $projects = [];
 | 
				
			||||||
    public $servers = [];
 | 
					    public $servers = [];
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->servers = Server::ownedByCurrentTeam()->get();
 | 
					        $this->servers = Server::ownedByCurrentTeam()->get();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										29
									
								
								app/Http/Livewire/Modal/EditCompose.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								app/Http/Livewire/Modal/EditCompose.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Http\Livewire\Modal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Service;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					use LivewireUI\Modal\ModalComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EditCompose extends ModalComponent
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public Service $service;
 | 
				
			||||||
 | 
					    public $serviceId;
 | 
				
			||||||
 | 
					    protected $rules = [
 | 
				
			||||||
 | 
					        'service.docker_compose_raw' => 'required',
 | 
				
			||||||
 | 
					        'service.docker_compose' => 'required',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    public function mount() {
 | 
				
			||||||
 | 
					        $this->service = Service::find($this->serviceId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return view('livewire.modal.edit-compose');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function submit() {
 | 
				
			||||||
 | 
					        $this->emit('warning', "Saving new docker compose...");
 | 
				
			||||||
 | 
					        $this->emit('saveCompose', $this->service->docker_compose_raw);
 | 
				
			||||||
 | 
					        $this->closeModal();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										110
									
								
								app/Http/Livewire/Project/Application/Command.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								app/Http/Livewire/Project/Application/Command.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Http\Livewire\Project\Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Application;
 | 
				
			||||||
 | 
					use App\Models\Server;
 | 
				
			||||||
 | 
					use App\Models\Service;
 | 
				
			||||||
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
 | 
					use App\Models\StandalonePostgresql;
 | 
				
			||||||
 | 
					use App\Models\StandaloneRedis;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Command extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public string $command;
 | 
				
			||||||
 | 
					    public string $container;
 | 
				
			||||||
 | 
					    public $containers;
 | 
				
			||||||
 | 
					    public $parameters;
 | 
				
			||||||
 | 
					    public $resource;
 | 
				
			||||||
 | 
					    public string $type;
 | 
				
			||||||
 | 
					    public string $workDir = '';
 | 
				
			||||||
 | 
					    public Server $server;
 | 
				
			||||||
 | 
					    public $servers = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $rules = [
 | 
				
			||||||
 | 
					        'server' => 'required',
 | 
				
			||||||
 | 
					        'container' => 'required',
 | 
				
			||||||
 | 
					        'command' => 'required',
 | 
				
			||||||
 | 
					        'workDir' => 'nullable',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->containers = collect();
 | 
				
			||||||
 | 
					        $this->parameters = get_route_parameters();
 | 
				
			||||||
 | 
					        if (data_get($this->parameters, 'application_uuid')) {
 | 
				
			||||||
 | 
					            $this->type = 'application';
 | 
				
			||||||
 | 
					            $this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
 | 
				
			||||||
 | 
					            $this->server = $this->resource->destination->server;
 | 
				
			||||||
 | 
					            $containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
 | 
				
			||||||
 | 
					            if ($containers->count() > 0) {
 | 
				
			||||||
 | 
					                $containers->each(function ($container) {
 | 
				
			||||||
 | 
					                    $this->containers->push(str_replace('/', '', $container['Names']));
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else if (data_get($this->parameters, 'database_uuid')) {
 | 
				
			||||||
 | 
					            $this->type = 'database';
 | 
				
			||||||
 | 
					            $resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
 | 
				
			||||||
 | 
					            if (is_null($resource)) {
 | 
				
			||||||
 | 
					                $resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
 | 
				
			||||||
 | 
					                if (is_null($resource)) {
 | 
				
			||||||
 | 
					                    $resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
 | 
				
			||||||
 | 
					                    if (is_null($resource)) {
 | 
				
			||||||
 | 
					                        $resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
 | 
				
			||||||
 | 
					                        if (is_null($resource)) {
 | 
				
			||||||
 | 
					                            $resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
 | 
				
			||||||
 | 
					                            if (is_null($resource)) {
 | 
				
			||||||
 | 
					                                abort(404);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->resource = $resource;
 | 
				
			||||||
 | 
					            $this->server = $this->resource->destination->server;
 | 
				
			||||||
 | 
					            $this->container = $this->resource->uuid;
 | 
				
			||||||
 | 
					            $this->containers->push($this->container);
 | 
				
			||||||
 | 
					        } else if (data_get($this->parameters, 'service_uuid')) {
 | 
				
			||||||
 | 
					            $this->type = 'service';
 | 
				
			||||||
 | 
					            $this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
 | 
				
			||||||
 | 
					            $this->resource->applications()->get()->each(function ($application) {
 | 
				
			||||||
 | 
					                if (str(data_get($application, 'status'))->contains('running')) {
 | 
				
			||||||
 | 
					                    $this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            $this->resource->databases()->get()->each(function ($database) {
 | 
				
			||||||
 | 
					                if (str(data_get($database, 'status'))->contains('running')) {
 | 
				
			||||||
 | 
					                    $this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->server = $this->resource->server;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($this->containers->count() > 1) {
 | 
				
			||||||
 | 
					            $this->container = $this->containers->first();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function runCommand()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->validate();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (!empty($this->workDir)) {
 | 
				
			||||||
 | 
					                $exec = "docker exec -w {$this->workDir} {$this->container} {$this->command}";
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $exec = "docker exec {$this->container} {$this->command}";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $activity = remote_process([$exec], $this->server, ignore_errors: true);
 | 
				
			||||||
 | 
					            $this->emit('newMonitorActivity', $activity->id);
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return view('livewire.project.shared.execute-container-command');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -26,7 +26,7 @@ class Configuration extends Component
 | 
				
			|||||||
            return redirect()->route('dashboard');
 | 
					            return redirect()->route('dashboard');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->application = $application;
 | 
					        $this->application = $application;
 | 
				
			||||||
        $mainServer = $application->destination->server;
 | 
					        $mainServer = $this->application->destination->server;
 | 
				
			||||||
        $servers = Server::ownedByCurrentTeam()->get();
 | 
					        $servers = Server::ownedByCurrentTeam()->get();
 | 
				
			||||||
        $this->servers = $servers->filter(function ($server) use ($mainServer) {
 | 
					        $this->servers = $servers->filter(function ($server) use ($mainServer) {
 | 
				
			||||||
            return $server->id != $mainServer->id;
 | 
					            return $server->id != $mainServer->id;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,6 @@ class DeploymentNavbar extends Component
 | 
				
			|||||||
    public Server $server;
 | 
					    public Server $server;
 | 
				
			||||||
    public bool $is_debug_enabled = false;
 | 
					    public bool $is_debug_enabled = false;
 | 
				
			||||||
    protected $listeners = ['deploymentFinished'];
 | 
					    protected $listeners = ['deploymentFinished'];
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->application = Application::find($this->application_deployment_queue->application_id);
 | 
					        $this->application = Application::find($this->application_deployment_queue->application_id);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,8 +29,6 @@ class General extends Component
 | 
				
			|||||||
    public ?string $initialDockerComposeLocation = null;
 | 
					    public ?string $initialDockerComposeLocation = null;
 | 
				
			||||||
    public ?string $initialDockerComposePrLocation = null;
 | 
					    public ?string $initialDockerComposePrLocation = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public bool $is_static;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public $parsedServices = [];
 | 
					    public $parsedServices = [];
 | 
				
			||||||
    public $parsedServiceDomains = [];
 | 
					    public $parsedServiceDomains = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -124,6 +122,10 @@ class General extends Component
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->application->settings->save();
 | 
					        $this->application->settings->save();
 | 
				
			||||||
        $this->emit('success', 'Settings saved.');
 | 
					        $this->emit('success', 'Settings saved.');
 | 
				
			||||||
 | 
					        $this->application->refresh();
 | 
				
			||||||
 | 
					        if ($this->ports_exposes !== $this->application->ports_exposes) {
 | 
				
			||||||
 | 
					            $this->resetDefaultLabels(false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function loadComposeFile($isInit = false)
 | 
					    public function loadComposeFile($isInit = false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -156,7 +158,7 @@ class General extends Component
 | 
				
			|||||||
    public function updatedApplicationBuildPack()
 | 
					    public function updatedApplicationBuildPack()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->application->build_pack !== 'nixpacks') {
 | 
					        if ($this->application->build_pack !== 'nixpacks') {
 | 
				
			||||||
            $this->application->settings->is_static = $this->is_static = false;
 | 
					            $this->application->settings->is_static = false;
 | 
				
			||||||
            $this->application->settings->save();
 | 
					            $this->application->settings->save();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if ($this->application->build_pack === 'dockercompose') {
 | 
					        if ($this->application->build_pack === 'dockercompose') {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,26 @@ class Heading extends Component
 | 
				
			|||||||
        $this->deploy(force_rebuild: true);
 | 
					        $this->deploy(force_rebuild: true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function deployNew()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->application->build_pack === 'dockercompose' && is_null($this->application->docker_compose_raw)) {
 | 
				
			||||||
 | 
					            $this->emit('error', 'Please load a Compose file first.');
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $this->setDeploymentUuid();
 | 
				
			||||||
 | 
					        queue_application_deployment(
 | 
				
			||||||
 | 
					            application_id: $this->application->id,
 | 
				
			||||||
 | 
					            deployment_uuid: $this->deploymentUuid,
 | 
				
			||||||
 | 
					            force_rebuild: false,
 | 
				
			||||||
 | 
					            is_new_deployment: true,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        return redirect()->route('project.application.deployment', [
 | 
				
			||||||
 | 
					            'project_uuid' => $this->parameters['project_uuid'],
 | 
				
			||||||
 | 
					            'application_uuid' => $this->parameters['application_uuid'],
 | 
				
			||||||
 | 
					            'deployment_uuid' => $this->deploymentUuid,
 | 
				
			||||||
 | 
					            'environment_name' => $this->parameters['environment_name'],
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function deploy(bool $force_rebuild = false)
 | 
					    public function deploy(bool $force_rebuild = false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->application->build_pack === 'dockercompose' && is_null($this->application->docker_compose_raw)) {
 | 
					        if ($this->application->build_pack === 'dockercompose' && is_null($this->application->docker_compose_raw)) {
 | 
				
			||||||
@@ -72,6 +92,22 @@ class Heading extends Component
 | 
				
			|||||||
        $this->application->save();
 | 
					        $this->application->save();
 | 
				
			||||||
        $this->application->refresh();
 | 
					        $this->application->refresh();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function restartNew()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->setDeploymentUuid();
 | 
				
			||||||
 | 
					        queue_application_deployment(
 | 
				
			||||||
 | 
					            application_id: $this->application->id,
 | 
				
			||||||
 | 
					            deployment_uuid: $this->deploymentUuid,
 | 
				
			||||||
 | 
					            restart_only: true,
 | 
				
			||||||
 | 
					            is_new_deployment: true,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        return redirect()->route('project.application.deployment', [
 | 
				
			||||||
 | 
					            'project_uuid' => $this->parameters['project_uuid'],
 | 
				
			||||||
 | 
					            'application_uuid' => $this->parameters['application_uuid'],
 | 
				
			||||||
 | 
					            'deployment_uuid' => $this->deploymentUuid,
 | 
				
			||||||
 | 
					            'environment_name' => $this->parameters['environment_name'],
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function restart()
 | 
					    public function restart()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->setDeploymentUuid();
 | 
					        $this->setDeploymentUuid();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace App\Http\Livewire\Project\Service;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use Livewire\Component;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ComposeModal extends Component
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public ?string $raw = null;
 | 
					 | 
				
			||||||
    public ?string $actual = null;
 | 
					 | 
				
			||||||
    public function render()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return view('livewire.project.service.compose-modal');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    public function submit() {
 | 
					 | 
				
			||||||
        $this->emit('warning', "Saving new docker compose...");
 | 
					 | 
				
			||||||
        $this->emit('saveCompose', $this->raw);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										110
									
								
								app/Http/Livewire/Project/Shared/ExecuteContainerCommand.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								app/Http/Livewire/Project/Shared/ExecuteContainerCommand.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Http\Livewire\Project\Shared;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Application;
 | 
				
			||||||
 | 
					use App\Models\Server;
 | 
				
			||||||
 | 
					use App\Models\Service;
 | 
				
			||||||
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
 | 
					use App\Models\StandalonePostgresql;
 | 
				
			||||||
 | 
					use App\Models\StandaloneRedis;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ExecuteContainerCommand extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public string $command;
 | 
				
			||||||
 | 
					    public string $container;
 | 
				
			||||||
 | 
					    public $containers;
 | 
				
			||||||
 | 
					    public $parameters;
 | 
				
			||||||
 | 
					    public $resource;
 | 
				
			||||||
 | 
					    public string $type;
 | 
				
			||||||
 | 
					    public string $workDir = '';
 | 
				
			||||||
 | 
					    public Server $server;
 | 
				
			||||||
 | 
					    public $servers = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $rules = [
 | 
				
			||||||
 | 
					        'server' => 'required',
 | 
				
			||||||
 | 
					        'container' => 'required',
 | 
				
			||||||
 | 
					        'command' => 'required',
 | 
				
			||||||
 | 
					        'workDir' => 'nullable',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->containers = collect();
 | 
				
			||||||
 | 
					        $this->parameters = get_route_parameters();
 | 
				
			||||||
 | 
					        if (data_get($this->parameters, 'application_uuid')) {
 | 
				
			||||||
 | 
					            $this->type = 'application';
 | 
				
			||||||
 | 
					            $this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
 | 
				
			||||||
 | 
					            $this->server = $this->resource->destination->server;
 | 
				
			||||||
 | 
					            $containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
 | 
				
			||||||
 | 
					            if ($containers->count() > 0) {
 | 
				
			||||||
 | 
					                $containers->each(function ($container) {
 | 
				
			||||||
 | 
					                    $this->containers->push(str_replace('/', '', $container['Names']));
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else if (data_get($this->parameters, 'database_uuid')) {
 | 
				
			||||||
 | 
					            $this->type = 'database';
 | 
				
			||||||
 | 
					            $resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
 | 
				
			||||||
 | 
					            if (is_null($resource)) {
 | 
				
			||||||
 | 
					                $resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
 | 
				
			||||||
 | 
					                if (is_null($resource)) {
 | 
				
			||||||
 | 
					                    $resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
 | 
				
			||||||
 | 
					                    if (is_null($resource)) {
 | 
				
			||||||
 | 
					                        $resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
 | 
				
			||||||
 | 
					                        if (is_null($resource)) {
 | 
				
			||||||
 | 
					                            $resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
 | 
				
			||||||
 | 
					                            if (is_null($resource)) {
 | 
				
			||||||
 | 
					                                abort(404);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->resource = $resource;
 | 
				
			||||||
 | 
					            $this->server = $this->resource->destination->server;
 | 
				
			||||||
 | 
					            $this->container = $this->resource->uuid;
 | 
				
			||||||
 | 
					            $this->containers->push($this->container);
 | 
				
			||||||
 | 
					        } else if (data_get($this->parameters, 'service_uuid')) {
 | 
				
			||||||
 | 
					            $this->type = 'service';
 | 
				
			||||||
 | 
					            $this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
 | 
				
			||||||
 | 
					            $this->resource->applications()->get()->each(function ($application) {
 | 
				
			||||||
 | 
					                if (str(data_get($application, 'status'))->contains('running')) {
 | 
				
			||||||
 | 
					                    $this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            $this->resource->databases()->get()->each(function ($database) {
 | 
				
			||||||
 | 
					                if (str(data_get($database, 'status'))->contains('running')) {
 | 
				
			||||||
 | 
					                    $this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->server = $this->resource->server;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($this->containers->count() > 0) {
 | 
				
			||||||
 | 
					            $this->container = $this->containers->first();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function runCommand()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->validate();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (!empty($this->workDir)) {
 | 
				
			||||||
 | 
					                $exec = "docker exec -w {$this->workDir} {$this->container} {$this->command}";
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $exec = "docker exec {$this->container} {$this->command}";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $activity = remote_process([$exec], $this->server, ignore_errors: true);
 | 
				
			||||||
 | 
					            $this->emit('newMonitorActivity', $activity->id);
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return view('livewire.project.shared.execute-container-command');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -44,6 +44,7 @@ class Form extends Component
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->wildcard_domain = $this->server->settings->wildcard_domain;
 | 
					        $this->wildcard_domain = $this->server->settings->wildcard_domain;
 | 
				
			||||||
        $this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage;
 | 
					        $this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage;
 | 
				
			||||||
 | 
					        $this->validateServer();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function serverRefresh($install = true)
 | 
					    public function serverRefresh($install = true)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,17 @@ use Livewire\Component;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Sponsorship extends Component
 | 
					class Sponsorship extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    public function getListeners()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $teamId = auth()->user()->currentTeam()->id;
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            "echo-private:custom.{$teamId},TestEvent" => 'testEvent',
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function testEvent()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->emit('success', 'Realtime events configured!');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function disable()
 | 
					    public function disable()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        auth()->user()->update(['is_notification_sponsorship_enabled' => false]);
 | 
					        auth()->user()->update(['is_notification_sponsorship_enabled' => false]);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1094,6 +1094,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $health_check_port = $this->application->health_check_port;
 | 
					            $health_check_port = $this->application->health_check_port;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
 | 
				
			||||||
 | 
					            $health_check_port = 80;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if ($this->application->health_check_path) {
 | 
					        if ($this->application->health_check_path) {
 | 
				
			||||||
            $this->full_healthcheck_url = "{$this->application->health_check_method}: {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path}";
 | 
					            $this->full_healthcheck_url = "{$this->application->health_check_method}: {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path}";
 | 
				
			||||||
            $generated_healthchecks_commands = [
 | 
					            $generated_healthchecks_commands = [
 | 
				
			||||||
@@ -1248,11 +1251,17 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
 | 
				
			|||||||
                [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true],
 | 
					                [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true],
 | 
				
			||||||
                [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
 | 
					                [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($this->docker_compose_location) {
 | 
				
			||||||
 | 
					                $this->execute_remote_command(
 | 
				
			||||||
 | 
					                    [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), "hidden" => true],
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                $this->execute_remote_command(
 | 
					                $this->execute_remote_command(
 | 
				
			||||||
                    [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
 | 
					                    [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $this->application_deployment_queue->addLogEntry("New container started.");
 | 
					        $this->application_deployment_queue->addLogEntry("New container started.");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										180
									
								
								app/Jobs/ApplicationDeploymentNewJob.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								app/Jobs/ApplicationDeploymentNewJob.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Jobs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Enums\ApplicationDeploymentStatus;
 | 
				
			||||||
 | 
					use App\Models\Application;
 | 
				
			||||||
 | 
					use App\Models\ApplicationDeploymentQueue;
 | 
				
			||||||
 | 
					use App\Models\Server;
 | 
				
			||||||
 | 
					use App\Traits\ExecuteRemoteCommand;
 | 
				
			||||||
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			||||||
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					use RuntimeException;
 | 
				
			||||||
 | 
					use Throwable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ApplicationDeploymentNewJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ExecuteRemoteCommand;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $timeout = 3600;
 | 
				
			||||||
 | 
					    public $tries = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static int $batch_counter = 0;
 | 
				
			||||||
 | 
					    public Server $mainServer;
 | 
				
			||||||
 | 
					    public $servers;
 | 
				
			||||||
 | 
					    public string $basedir;
 | 
				
			||||||
 | 
					    public string $workdir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public string $deploymentUuid;
 | 
				
			||||||
 | 
					    public int $pullRequestId = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Git related
 | 
				
			||||||
 | 
					    public string $gitImportCommands;
 | 
				
			||||||
 | 
					    public ?string $gitType = null;
 | 
				
			||||||
 | 
					    public string $gitRepository;
 | 
				
			||||||
 | 
					    public string $gitBranch;
 | 
				
			||||||
 | 
					    public int $gitPort;
 | 
				
			||||||
 | 
					    public string $gitFullRepoUrl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(public ApplicationDeploymentQueue $deployment, public Application $application)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->mainServer = data_get($this->application, 'destination.server');
 | 
				
			||||||
 | 
					        $this->deploymentUuid = data_get($this->deployment, 'deployment_uuid');
 | 
				
			||||||
 | 
					        $this->pullRequestId = data_get($this->deployment, 'pull_request_id', 0);
 | 
				
			||||||
 | 
					        $this->gitType = data_get($this->deployment, 'git_type');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->basedir = $this->application->generateBaseDir($this->deploymentUuid);
 | 
				
			||||||
 | 
					        $this->workdir = $this->basedir . rtrim($this->application->base_directory, '/');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            ray()->clearAll();
 | 
				
			||||||
 | 
					            $this->deployment->setStatus(ApplicationDeploymentStatus::IN_PROGRESS->value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $hostIpMappings = $this->mainServer->getHostIPMappings($this->application->destination->network);
 | 
				
			||||||
 | 
					            if ($this->application->dockerfile_target_build) {
 | 
				
			||||||
 | 
					                $buildTarget = " --target {$this->application->dockerfile_target_build} ";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Get the git repository and port (custom port or default port)
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                'repository' => $this->gitRepository,
 | 
				
			||||||
 | 
					                'port' => $this->gitPort
 | 
				
			||||||
 | 
					            ] = $this->application->customRepository();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Get the git branch and git import commands
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                'commands' => $this->gitImportCommands,
 | 
				
			||||||
 | 
					                'branch' => $this->gitBranch,
 | 
				
			||||||
 | 
					                'fullRepoUrl' => $this->gitFullRepoUrl
 | 
				
			||||||
 | 
					            ] = $this->application->generateGitImportCommands($this->deploymentUuid, $this->pullRequestId, $this->gitType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->servers = $this->application->servers();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($this->deployment->restart_only) {
 | 
				
			||||||
 | 
					                if ($this->application->build_pack === 'dockerimage') {
 | 
				
			||||||
 | 
					                    throw new \Exception('Restart only is not supported for docker image based deployments');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $this->deployment->addLogEntry("Starting deployment of {$this->application->name}.");
 | 
				
			||||||
 | 
					                $this->servers->each(function ($server) {
 | 
				
			||||||
 | 
					                    $this->deployment->addLogEntry("Restarting {$this->application->name} on {$server->name}.");
 | 
				
			||||||
 | 
					                    $this->restartOnly($server);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->next(ApplicationDeploymentStatus::FINISHED->value);
 | 
				
			||||||
 | 
					        } catch (Throwable $exception) {
 | 
				
			||||||
 | 
					            $this->fail($exception);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            $this->servers->each(function ($server) {
 | 
				
			||||||
 | 
					                $this->deployment->addLogEntry("Cleaning up temporary containers on {$server->name}.");
 | 
				
			||||||
 | 
					                $server->executeRemoteCommand(
 | 
				
			||||||
 | 
					                    commands: collect([])->push([
 | 
				
			||||||
 | 
					                        "command" => "docker rm -f {$this->deploymentUuid}",
 | 
				
			||||||
 | 
					                        "hidden" => true,
 | 
				
			||||||
 | 
					                        "ignoreErrors" => true,
 | 
				
			||||||
 | 
					                    ]),
 | 
				
			||||||
 | 
					                    loggingModel: $this->deployment
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function restartOnly(Server $server)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $server->executeRemoteCommand(
 | 
				
			||||||
 | 
					            commands: $this->application->prepareHelperImage($this->deploymentUuid),
 | 
				
			||||||
 | 
					            loggingModel: $this->deployment
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $privateKey = data_get($this->application, 'private_key.private_key', null);
 | 
				
			||||||
 | 
					        $gitLsRemoteCommand = collect([]);
 | 
				
			||||||
 | 
					        if ($privateKey) {
 | 
				
			||||||
 | 
					            $privateKey = base64_decode($privateKey);
 | 
				
			||||||
 | 
					            $gitLsRemoteCommand
 | 
				
			||||||
 | 
					                ->push([
 | 
				
			||||||
 | 
					                    "command" => executeInDocker($this->deploymentUuid, "mkdir -p /root/.ssh")
 | 
				
			||||||
 | 
					                ])
 | 
				
			||||||
 | 
					                ->push([
 | 
				
			||||||
 | 
					                    "command" => executeInDocker($this->deploymentUuid, "echo '{$privateKey}' | base64 -d > /root/.ssh/id_rsa")
 | 
				
			||||||
 | 
					                ])
 | 
				
			||||||
 | 
					                ->push([
 | 
				
			||||||
 | 
					                    "command" => executeInDocker($this->deploymentUuid, "chmod 600 /root/.ssh/id_rsa")
 | 
				
			||||||
 | 
					                ])
 | 
				
			||||||
 | 
					                ->push([
 | 
				
			||||||
 | 
					                    "name" => "git_commit_sha",
 | 
				
			||||||
 | 
					                    "command" => executeInDocker($this->deploymentUuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->gitPort} -o Port={$this->gitPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->gitFullRepoUrl} {$this->gitBranch}"),
 | 
				
			||||||
 | 
					                    "hidden" => true,
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $gitLsRemoteCommand->push([
 | 
				
			||||||
 | 
					                "name" => "git_commit_sha",
 | 
				
			||||||
 | 
					                "command" => executeInDocker($this->deploymentUuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->gitPort} -o Port={$this->gitPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->gitFullRepoUrl} {$this->gitBranch}"),
 | 
				
			||||||
 | 
					                "hidden" => true,
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $this->deployment->addLogEntry("Checking if there is any new commit on {$this->gitBranch} branch.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $server->executeRemoteCommand(
 | 
				
			||||||
 | 
					            commands: $gitLsRemoteCommand,
 | 
				
			||||||
 | 
					            loggingModel: $this->deployment
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        $commit = str($this->deployment->getOutput('git_commit_sha'))->before("\t");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            'productionImageName' => $productionImageName
 | 
				
			||||||
 | 
					        ] = $this->application->generateImageNames($commit, $this->pullRequestId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->deployment->addLogEntry("Checking if the image {$productionImageName} already exists.");
 | 
				
			||||||
 | 
					        $server->checkIfDockerImageExists($productionImageName, $this->deployment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (str($this->deployment->getOutput('local_image_found'))->isNotEmpty()) {
 | 
				
			||||||
 | 
					            $this->deployment->addLogEntry("Image {$productionImageName} already exists. Skipping the build.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $server->createWorkDirForDeployment($this->workdir, $this->deployment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->application->generateDockerComposeFile($server, $this->deployment, $this->workdir);
 | 
				
			||||||
 | 
					            $this->application->rollingUpdateApplication($server, $this->deployment, $this->workdir);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        throw new RuntimeException('Cannot find image anywhere. Please redeploy the application.');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function failed(Throwable $exception): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ray($exception);
 | 
				
			||||||
 | 
					        $this->next(ApplicationDeploymentStatus::FAILED->value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    private function next(string $status)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // If the deployment is cancelled by the user, don't update the status
 | 
				
			||||||
 | 
					        if ($this->deployment->status !== ApplicationDeploymentStatus::CANCELLED_BY_USER->value) {
 | 
				
			||||||
 | 
					            $this->deployment->update([
 | 
				
			||||||
 | 
					                'status' => $status,
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        queue_next_deployment($this->application, isNew: true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,6 +4,7 @@ namespace App\Models;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
					use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
				
			||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
 | 
					use Illuminate\Database\Eloquent\Relations\HasMany;
 | 
				
			||||||
 | 
					use Illuminate\Support\Collection;
 | 
				
			||||||
use Spatie\Activitylog\Models\Activity;
 | 
					use Spatie\Activitylog\Models\Activity;
 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
use RuntimeException;
 | 
					use RuntimeException;
 | 
				
			||||||
@@ -48,6 +49,65 @@ class Application extends BaseModel
 | 
				
			|||||||
            $application->environment_variables_preview()->delete();
 | 
					            $application->environment_variables_preview()->delete();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    // Build packs / deployment types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function servers(): Collection
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $mainServer = data_get($this, 'destination.server');
 | 
				
			||||||
 | 
					        $additionalDestinations = data_get($this, 'additional_destinations', null);
 | 
				
			||||||
 | 
					        $additionalServers = collect([]);
 | 
				
			||||||
 | 
					        if ($this->isMultipleServerDeployment()) {
 | 
				
			||||||
 | 
					            ray('asd');
 | 
				
			||||||
 | 
					            if (str($additionalDestinations)->isNotEmpty()) {
 | 
				
			||||||
 | 
					                $additionalDestinations = str($additionalDestinations)->explode(',');
 | 
				
			||||||
 | 
					                foreach ($additionalDestinations as $destinationId) {
 | 
				
			||||||
 | 
					                    $destination = StandaloneDocker::find($destinationId)->whereNot('id', $mainServer->id)->first();
 | 
				
			||||||
 | 
					                    $server = data_get($destination, 'server');
 | 
				
			||||||
 | 
					                    $additionalServers->push($server);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return collect([$mainServer])->merge($additionalServers);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function generateImageNames(string $commit, int $pullRequestId)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->dockerfile) {
 | 
				
			||||||
 | 
					            if ($this->docker_registry_image_name) {
 | 
				
			||||||
 | 
					                $buildImageName = Str::lower("{$this->docker_registry_image_name}:build");
 | 
				
			||||||
 | 
					                $productionImageName = Str::lower("{$this->docker_registry_image_name}:latest");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $buildImageName = Str::lower("{$this->uuid}:build");
 | 
				
			||||||
 | 
					                $productionImageName = Str::lower("{$this->uuid}:latest");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else if ($this->build_pack === 'dockerimage') {
 | 
				
			||||||
 | 
					            $productionImageName = Str::lower("{$this->docker_registry_image_name}:{$this->docker_registry_image_tag}");
 | 
				
			||||||
 | 
					        } else if ($pullRequestId === 0) {
 | 
				
			||||||
 | 
					            $dockerImageTag = str($commit)->substr(0, 128);
 | 
				
			||||||
 | 
					            if ($this->docker_registry_image_name) {
 | 
				
			||||||
 | 
					                $buildImageName = Str::lower("{$this->docker_registry_image_name}:{$dockerImageTag}-build");
 | 
				
			||||||
 | 
					                $productionImageName = Str::lower("{$this->docker_registry_image_name}:{$dockerImageTag}");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $buildImageName = Str::lower("{$this->uuid}:{$dockerImageTag}-build");
 | 
				
			||||||
 | 
					                $productionImageName = Str::lower("{$this->uuid}:{$dockerImageTag}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else if ($pullRequestId !== 0) {
 | 
				
			||||||
 | 
					            if ($this->docker_registry_image_name) {
 | 
				
			||||||
 | 
					                $buildImageName = Str::lower("{$this->docker_registry_image_name}:pr-{$pullRequestId}-build");
 | 
				
			||||||
 | 
					                $productionImageName = Str::lower("{$this->docker_registry_image_name}:pr-{$pullRequestId}");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $buildImageName = Str::lower("{$this->uuid}:pr-{$pullRequestId}-build");
 | 
				
			||||||
 | 
					                $productionImageName = Str::lower("{$this->uuid}:pr-{$pullRequestId}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            'buildImageName' => $buildImageName,
 | 
				
			||||||
 | 
					            'productionImageName' => $productionImageName,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // End of build packs / deployment types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function link()
 | 
					    public function link()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (data_get($this, 'environment.project.uuid')) {
 | 
					        if (data_get($this, 'environment.project.uuid')) {
 | 
				
			||||||
@@ -385,9 +445,7 @@ class Application extends BaseModel
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    public function isMultipleServerDeployment()
 | 
					    public function isMultipleServerDeployment()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (isDev()) {
 | 
					        return false;
 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (data_get($this, 'additional_destinations') && data_get($this, 'docker_registry_image_name')) {
 | 
					        if (data_get($this, 'additional_destinations') && data_get($this, 'docker_registry_image_name')) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -431,6 +489,294 @@ class Application extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return "/artifacts/{$uuid}";
 | 
					        return "/artifacts/{$uuid}";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    function generateHealthCheckCommands()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->dockerfile || $this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
 | 
				
			||||||
 | 
					            // TODO: disabled HC because there are several ways to hc a simple docker image, hard to figure out a good way. Like some docker images (pocketbase) does not have curl.
 | 
				
			||||||
 | 
					            return 'exit 0';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!$this->health_check_port) {
 | 
				
			||||||
 | 
					            $health_check_port = $this->ports_exposes_array[0];
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $health_check_port = $this->health_check_port;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($this->health_check_path) {
 | 
				
			||||||
 | 
					            $this->full_healthcheck_url = "{$this->health_check_method}: {$this->health_check_scheme}://{$this->health_check_host}:{$health_check_port}{$this->health_check_path}";
 | 
				
			||||||
 | 
					            $generated_healthchecks_commands = [
 | 
				
			||||||
 | 
					                "curl -s -X {$this->health_check_method} -f {$this->health_check_scheme}://{$this->health_check_host}:{$health_check_port}{$this->health_check_path} > /dev/null"
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->full_healthcheck_url = "{$this->health_check_method}: {$this->health_check_scheme}://{$this->health_check_host}:{$health_check_port}/";
 | 
				
			||||||
 | 
					            $generated_healthchecks_commands = [
 | 
				
			||||||
 | 
					                "curl -s -X {$this->health_check_method} -f {$this->health_check_scheme}://{$this->health_check_host}:{$health_check_port}/"
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return implode(' ', $generated_healthchecks_commands);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    function generateLocalPersistentVolumes(int $pullRequestId)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $persistentStorages = [];
 | 
				
			||||||
 | 
					        $volumeNames = [];
 | 
				
			||||||
 | 
					        foreach ($this->persistentStorages as $persistentStorage) {
 | 
				
			||||||
 | 
					            $volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
 | 
				
			||||||
 | 
					            if ($pullRequestId !== 0) {
 | 
				
			||||||
 | 
					                $volume_name = $volume_name . '-pr-' . $pullRequestId;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $persistentStorages[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($persistentStorage->host_path) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $name = $persistentStorage->name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($pullRequestId !== 0) {
 | 
				
			||||||
 | 
					                $name = $name . '-pr-' . $pullRequestId;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $volumeNames[$name] = [
 | 
				
			||||||
 | 
					                'name' => $name,
 | 
				
			||||||
 | 
					                'external' => false,
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            'persistentStorages' => $persistentStorages,
 | 
				
			||||||
 | 
					            'volumeNames' => $volumeNames,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function generateEnvironmentVariables($ports)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $environmentVariables = collect();
 | 
				
			||||||
 | 
					        // ray('Generate Environment Variables')->green();
 | 
				
			||||||
 | 
					        if ($this->pull_request_id === 0) {
 | 
				
			||||||
 | 
					            // ray($this->runtime_environment_variables)->green();
 | 
				
			||||||
 | 
					            foreach ($this->runtime_environment_variables as $env) {
 | 
				
			||||||
 | 
					                $environmentVariables->push("$env->key=$env->value");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            foreach ($this->nixpacks_environment_variables as $env) {
 | 
				
			||||||
 | 
					                $environmentVariables->push("$env->key=$env->value");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // ray($this->runtime_environment_variables_preview)->green();
 | 
				
			||||||
 | 
					            foreach ($this->runtime_environment_variables_preview as $env) {
 | 
				
			||||||
 | 
					                $environmentVariables->push("$env->key=$env->value");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            foreach ($this->nixpacks_environment_variables_preview as $env) {
 | 
				
			||||||
 | 
					                $environmentVariables->push("$env->key=$env->value");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Add PORT if not exists, use the first port as default
 | 
				
			||||||
 | 
					        if ($environmentVariables->filter(fn ($env) => Str::of($env)->contains('PORT'))->isEmpty()) {
 | 
				
			||||||
 | 
					            $environmentVariables->push("PORT={$ports[0]}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return $environmentVariables->all();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    function generateDockerComposeFile(Server $server, ApplicationDeploymentQueue $deployment, string $workdir)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $pullRequestId = $deployment->pull_request_id;
 | 
				
			||||||
 | 
					        $ports = $this->settings->is_static ? [80] : $this->ports_exposes_array;
 | 
				
			||||||
 | 
					        $container_name = generateApplicationContainerName($this, $this->pull_request_id);
 | 
				
			||||||
 | 
					        $commit = str($deployment->getOutput('git_commit_sha'))->before("\t");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            'productionImageName' => $productionImageName
 | 
				
			||||||
 | 
					        ] = $this->generateImageNames($commit, $pullRequestId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            'persistentStorages' => $persistentStorages,
 | 
				
			||||||
 | 
					            'volumeNames' => $volumeNames
 | 
				
			||||||
 | 
					        ] = $this->generateLocalPersistentVolumes($pullRequestId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $environmentVariables = $this->generateEnvironmentVariables($ports);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (data_get($this, 'custom_labels')) {
 | 
				
			||||||
 | 
					            $labels = collect(str($this->custom_labels)->explode(','));
 | 
				
			||||||
 | 
					            $labels = $labels->filter(function ($value, $key) {
 | 
				
			||||||
 | 
					                return !Str::startsWith($value, 'coolify.');
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            $this->custom_labels = $labels->implode(',');
 | 
				
			||||||
 | 
					            $this->save();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $labels = collect(generateLabelsApplication($this, $this->preview));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($this->pull_request_id !== 0) {
 | 
				
			||||||
 | 
					            $labels = collect(generateLabelsApplication($this, $this->preview));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $labels = $labels->merge(defaultLabels($this->id, $this->uuid, $this->pull_request_id))->toArray();
 | 
				
			||||||
 | 
					        $docker_compose = [
 | 
				
			||||||
 | 
					            'version' => '3.8',
 | 
				
			||||||
 | 
					            'services' => [
 | 
				
			||||||
 | 
					                $container_name => [
 | 
				
			||||||
 | 
					                    'image' => $productionImageName,
 | 
				
			||||||
 | 
					                    'container_name' => $container_name,
 | 
				
			||||||
 | 
					                    'restart' => RESTART_MODE,
 | 
				
			||||||
 | 
					                    'environment' => $environmentVariables,
 | 
				
			||||||
 | 
					                    'expose' => $ports,
 | 
				
			||||||
 | 
					                    'networks' => [
 | 
				
			||||||
 | 
					                        $this->destination->network,
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'healthcheck' => [
 | 
				
			||||||
 | 
					                        'test' => [
 | 
				
			||||||
 | 
					                            'CMD-SHELL',
 | 
				
			||||||
 | 
					                            $this->generateHealthCheckCommands()
 | 
				
			||||||
 | 
					                        ],
 | 
				
			||||||
 | 
					                        'interval' => $this->health_check_interval . 's',
 | 
				
			||||||
 | 
					                        'timeout' => $this->health_check_timeout . 's',
 | 
				
			||||||
 | 
					                        'retries' => $this->health_check_retries,
 | 
				
			||||||
 | 
					                        'start_period' => $this->health_check_start_period . 's'
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'mem_limit' => $this->limits_memory,
 | 
				
			||||||
 | 
					                    'memswap_limit' => $this->limits_memory_swap,
 | 
				
			||||||
 | 
					                    'mem_swappiness' => $this->limits_memory_swappiness,
 | 
				
			||||||
 | 
					                    'mem_reservation' => $this->limits_memory_reservation,
 | 
				
			||||||
 | 
					                    'cpus' => (int) $this->limits_cpus,
 | 
				
			||||||
 | 
					                    'cpuset' => $this->limits_cpuset,
 | 
				
			||||||
 | 
					                    'cpu_shares' => $this->limits_cpu_shares,
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            'networks' => [
 | 
				
			||||||
 | 
					                $this->destination->network => [
 | 
				
			||||||
 | 
					                    'external' => true,
 | 
				
			||||||
 | 
					                    'name' => $this->destination->network,
 | 
				
			||||||
 | 
					                    'attachable' => true
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					        if ($server->isSwarm()) {
 | 
				
			||||||
 | 
					            data_forget($docker_compose, 'services.' . $container_name . '.container_name');
 | 
				
			||||||
 | 
					            data_forget($docker_compose, 'services.' . $container_name . '.expose');
 | 
				
			||||||
 | 
					            data_forget($docker_compose, 'services.' . $container_name . '.restart');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            data_forget($docker_compose, 'services.' . $container_name . '.mem_limit');
 | 
				
			||||||
 | 
					            data_forget($docker_compose, 'services.' . $container_name . '.memswap_limit');
 | 
				
			||||||
 | 
					            data_forget($docker_compose, 'services.' . $container_name . '.mem_swappiness');
 | 
				
			||||||
 | 
					            data_forget($docker_compose, 'services.' . $container_name . '.mem_reservation');
 | 
				
			||||||
 | 
					            data_forget($docker_compose, 'services.' . $container_name . '.cpus');
 | 
				
			||||||
 | 
					            data_forget($docker_compose, 'services.' . $container_name . '.cpuset');
 | 
				
			||||||
 | 
					            data_forget($docker_compose, 'services.' . $container_name . '.cpu_shares');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['deploy'] = [
 | 
				
			||||||
 | 
					                'placement' => [
 | 
				
			||||||
 | 
					                    'constraints' => [
 | 
				
			||||||
 | 
					                        'node.role == worker'
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                'mode' => 'replicated',
 | 
				
			||||||
 | 
					                'replicas' => 1,
 | 
				
			||||||
 | 
					                'update_config' => [
 | 
				
			||||||
 | 
					                    'order' => 'start-first'
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                'rollback_config' => [
 | 
				
			||||||
 | 
					                    'order' => 'start-first'
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                'labels' => $labels,
 | 
				
			||||||
 | 
					                'resources' => [
 | 
				
			||||||
 | 
					                    'limits' => [
 | 
				
			||||||
 | 
					                        'cpus' => $this->limits_cpus,
 | 
				
			||||||
 | 
					                        'memory' => $this->limits_memory,
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'reservations' => [
 | 
				
			||||||
 | 
					                        'cpus' => $this->limits_cpus,
 | 
				
			||||||
 | 
					                        'memory' => $this->limits_memory,
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['labels'] = $labels;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($server->isLogDrainEnabled() && $this->isLogDrainEnabled()) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['logging'] = [
 | 
				
			||||||
 | 
					                'driver' => 'fluentd',
 | 
				
			||||||
 | 
					                'options' => [
 | 
				
			||||||
 | 
					                    'fluentd-address' => "tcp://127.0.0.1:24224",
 | 
				
			||||||
 | 
					                    'fluentd-async' => "true",
 | 
				
			||||||
 | 
					                    'fluentd-sub-second-precision' => "true",
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($this->settings->is_gpu_enabled) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['deploy']['resources']['reservations']['devices'] = [
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    'driver' => data_get($this, 'settings.gpu_driver', 'nvidia'),
 | 
				
			||||||
 | 
					                    'capabilities' => ['gpu'],
 | 
				
			||||||
 | 
					                    'options' => data_get($this, 'settings.gpu_options', [])
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					            if (data_get($this, 'settings.gpu_count')) {
 | 
				
			||||||
 | 
					                $count = data_get($this, 'settings.gpu_count');
 | 
				
			||||||
 | 
					                if ($count === 'all') {
 | 
				
			||||||
 | 
					                    $docker_compose['services'][$container_name]['deploy']['resources']['reservations']['devices'][0]['count'] = $count;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $docker_compose['services'][$container_name]['deploy']['resources']['reservations']['devices'][0]['count'] = (int) $count;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else if (data_get($this, 'settings.gpu_device_ids')) {
 | 
				
			||||||
 | 
					                $docker_compose['services'][$container_name]['deploy']['resources']['reservations']['devices'][0]['ids'] = data_get($this, 'settings.gpu_device_ids');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($this->isHealthcheckDisabled()) {
 | 
				
			||||||
 | 
					            data_forget($docker_compose, 'services.' . $container_name . '.healthcheck');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (count($this->ports_mappings_array) > 0 && $this->pull_request_id === 0) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['ports'] = $this->ports_mappings_array;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (count($persistentStorages) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['volumes'] = $persistentStorages;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (count($volumeNames) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['volumes'] = $volumeNames;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $docker_compose['services'][$this->uuid] = $docker_compose['services'][$container_name];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data_forget($docker_compose, 'services.' . $container_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $docker_compose = Yaml::dump($docker_compose, 10);
 | 
				
			||||||
 | 
					        $docker_compose_base64 = base64_encode($docker_compose);
 | 
				
			||||||
 | 
					        $server->executeRemoteCommand(
 | 
				
			||||||
 | 
					            commands: collect([])->push([
 | 
				
			||||||
 | 
					                'command' => executeInDocker($deployment->deployment_uuid, "echo '{$docker_compose_base64}' | base64 -d > {$workdir}/docker-compose.yml"),
 | 
				
			||||||
 | 
					                'hidden' => true,
 | 
				
			||||||
 | 
					                'ignoreErrors' => true
 | 
				
			||||||
 | 
					            ]),
 | 
				
			||||||
 | 
					            loggingModel: $deployment
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    function rollingUpdateApplication(Server $server, ApplicationDeploymentQueue $deployment, string $workdir)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $pullRequestId = $deployment->pull_request_id;
 | 
				
			||||||
 | 
					        $containerName = generateApplicationContainerName($this, $pullRequestId);
 | 
				
			||||||
 | 
					        // if (count($this->ports_mappings_array) > 0) {
 | 
				
			||||||
 | 
					        // $deployment->addLogEntry('Application has ports mapped to the host system, rolling update is not supported.');
 | 
				
			||||||
 | 
					        $containers = getCurrentApplicationContainerStatus($server, $this->id, $pullRequestId);
 | 
				
			||||||
 | 
					        ray($containers);
 | 
				
			||||||
 | 
					        // if ($pullRequestId === 0) {
 | 
				
			||||||
 | 
					        //     $containers = $containers->filter(function ($container) use ($containerName) {
 | 
				
			||||||
 | 
					        //         return data_get($container, 'Names') !== $containerName;
 | 
				
			||||||
 | 
					        //     });
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					        $containers->each(function ($container) use ($server, $deployment) {
 | 
				
			||||||
 | 
					            $removingContainerName = data_get($container, 'Names');
 | 
				
			||||||
 | 
					            $server->executeRemoteCommand(
 | 
				
			||||||
 | 
					                commands: collect([])->push([
 | 
				
			||||||
 | 
					                    'command' => "docker rm -f $removingContainerName",
 | 
				
			||||||
 | 
					                    'hidden' => true,
 | 
				
			||||||
 | 
					                    'ignoreErrors' => true
 | 
				
			||||||
 | 
					                ]),
 | 
				
			||||||
 | 
					                loggingModel: $deployment
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					        $server->executeRemoteCommand(
 | 
				
			||||||
 | 
					            commands: collect([])->push([
 | 
				
			||||||
 | 
					                'command' => executeInDocker($deployment->deployment_uuid, "docker compose --project-directory {$workdir} up --build -d"),
 | 
				
			||||||
 | 
					                'hidden' => true,
 | 
				
			||||||
 | 
					                'ignoreErrors' => true
 | 
				
			||||||
 | 
					            ]),
 | 
				
			||||||
 | 
					            loggingModel: $deployment
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        $deployment->addLogEntry("New container started.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    function setGitImportSettings(string $deployment_uuid, string $git_clone_command)
 | 
					    function setGitImportSettings(string $deployment_uuid, string $git_clone_command)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $baseDir = $this->generateBaseDir($deployment_uuid);
 | 
					        $baseDir = $this->generateBaseDir($deployment_uuid);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,11 @@ class ApplicationDeploymentQueue extends Model
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    protected $guarded = [];
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function setStatus(string $status) {
 | 
				
			||||||
 | 
					        $this->update([
 | 
				
			||||||
 | 
					            'status' => $status,
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function getOutput($name)
 | 
					    public function getOutput($name)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (!$this->logs) {
 | 
					        if (!$this->logs) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,29 +16,16 @@ class ApplicationSetting extends Model
 | 
				
			|||||||
        'is_git_submodules_enabled' => 'boolean',
 | 
					        'is_git_submodules_enabled' => 'boolean',
 | 
				
			||||||
        'is_git_lfs_enabled' => 'boolean',
 | 
					        'is_git_lfs_enabled' => 'boolean',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    protected $fillable = [
 | 
					    protected $guarded = [];
 | 
				
			||||||
        'application_id',
 | 
					 | 
				
			||||||
        'is_static',
 | 
					 | 
				
			||||||
        'is_auto_deploy_enabled',
 | 
					 | 
				
			||||||
        'is_force_https_enabled',
 | 
					 | 
				
			||||||
        'is_debug_enabled',
 | 
					 | 
				
			||||||
        'is_preview_deployments_enabled',
 | 
					 | 
				
			||||||
        'is_git_submodules_enabled',
 | 
					 | 
				
			||||||
        'is_git_lfs_enabled',
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function isStatic(): Attribute
 | 
					    public function isStatic(): Attribute
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return Attribute::make(
 | 
					        return Attribute::make(
 | 
				
			||||||
            set: function ($value) {
 | 
					            set: function ($value) {
 | 
				
			||||||
                if (is_null($this->application->ports_exposes)) {
 | 
					 | 
				
			||||||
                if ($value) {
 | 
					                if ($value) {
 | 
				
			||||||
                        $this->application->ports_exposes = '80';
 | 
					                    $this->application->ports_exposes = 80;
 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        $this->application->ports_exposes = '3000';
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                $this->application->save();
 | 
					                $this->application->save();
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return $value;
 | 
					                return $value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,7 +67,7 @@ class Server extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $teamId = currentTeam()->id;
 | 
					        $teamId = currentTeam()->id;
 | 
				
			||||||
        $selectArray = collect($select)->concat(['id']);
 | 
					        $selectArray = collect($select)->concat(['id']);
 | 
				
			||||||
        return Server::whereTeamId($teamId)->with('settings','swarmDockers','standaloneDockers')->select($selectArray->all())->orderBy('name');
 | 
					        return Server::whereTeamId($teamId)->with('settings', 'swarmDockers', 'standaloneDockers')->select($selectArray->all())->orderBy('name');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static public function isUsable()
 | 
					    static public function isUsable()
 | 
				
			||||||
@@ -86,7 +86,8 @@ class Server extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->hasOne(ServerSetting::class);
 | 
					        return $this->hasOne(ServerSetting::class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function addInitialNetwork() {
 | 
					    public function addInitialNetwork()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        if ($this->id === 0) {
 | 
					        if ($this->id === 0) {
 | 
				
			||||||
            if ($this->isSwarm()) {
 | 
					            if ($this->isSwarm()) {
 | 
				
			||||||
                SwarmDocker::create([
 | 
					                SwarmDocker::create([
 | 
				
			||||||
@@ -381,17 +382,17 @@ class Server extends BaseModel
 | 
				
			|||||||
    public function validateConnection()
 | 
					    public function validateConnection()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $server = Server::find($this->id);
 | 
					        $server = Server::find($this->id);
 | 
				
			||||||
        if ($this->skipServer()) {
 | 
					        if ($server->skipServer()) {
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $uptime = instant_remote_process(['uptime'], $this, false);
 | 
					        $uptime = instant_remote_process(['uptime'], $server, false);
 | 
				
			||||||
        if (!$uptime) {
 | 
					        if (!$uptime) {
 | 
				
			||||||
            $this->settings()->update([
 | 
					            $server->settings()->update([
 | 
				
			||||||
                'is_reachable' => false,
 | 
					                'is_reachable' => false,
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $this->settings()->update([
 | 
					            $server->settings()->update([
 | 
				
			||||||
                'is_reachable' => true,
 | 
					                'is_reachable' => true,
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
            $server->update([
 | 
					            $server->update([
 | 
				
			||||||
@@ -399,8 +400,8 @@ class Server extends BaseModel
 | 
				
			|||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (data_get($this, 'unreachable_notification_sent') === true) {
 | 
					        if (data_get($server, 'unreachable_notification_sent') === true) {
 | 
				
			||||||
            $this->team->notify(new Revived($this));
 | 
					            $server->team->notify(new Revived($server));
 | 
				
			||||||
            $server->update(['unreachable_notification_sent' => false]);
 | 
					            $server->update(['unreachable_notification_sent' => false]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -536,4 +537,74 @@ class Server extends BaseModel
 | 
				
			|||||||
            );
 | 
					            );
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function getHostIPMappings($network)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $addHosts = null;
 | 
				
			||||||
 | 
					        $allContainers = instant_remote_process(["docker network inspect {$network} -f '{{json .Containers}}' "], $this);
 | 
				
			||||||
 | 
					        if (!is_null($allContainers)) {
 | 
				
			||||||
 | 
					            $allContainers = format_docker_command_output_to_json($allContainers);
 | 
				
			||||||
 | 
					            $ips = collect([]);
 | 
				
			||||||
 | 
					            if (count($allContainers) > 0) {
 | 
				
			||||||
 | 
					                $allContainers = $allContainers[0];
 | 
				
			||||||
 | 
					                foreach ($allContainers as $container) {
 | 
				
			||||||
 | 
					                    $containerName = data_get($container, 'Name');
 | 
				
			||||||
 | 
					                    if ($containerName === 'coolify-proxy') {
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $containerIp = data_get($container, 'IPv4Address');
 | 
				
			||||||
 | 
					                    if ($containerName && $containerIp) {
 | 
				
			||||||
 | 
					                        $containerIp = str($containerIp)->before('/');
 | 
				
			||||||
 | 
					                        $ips->put($containerName, $containerIp->value());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $addHosts = $ips->map(function ($ip, $name) {
 | 
				
			||||||
 | 
					                return "--add-host $name:$ip";
 | 
				
			||||||
 | 
					            })->implode(' ');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return $addHosts;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function checkIfDockerImageExists(string $imageName, ApplicationDeploymentQueue $deployment)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->executeRemoteCommand(
 | 
				
			||||||
 | 
					            commands: collect([
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    "name" => "local_image_found",
 | 
				
			||||||
 | 
					                    "command" => "docker images -q {$imageName} 2>/dev/null",
 | 
				
			||||||
 | 
					                    "hidden" => true,
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ]),
 | 
				
			||||||
 | 
					            loggingModel: $deployment
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        if (str($deployment->getOutput('local_image_found'))->isEmpty()) {
 | 
				
			||||||
 | 
					            $this->executeRemoteCommand(
 | 
				
			||||||
 | 
					                commands: collect([
 | 
				
			||||||
 | 
					                    [
 | 
				
			||||||
 | 
					                        "command" => "docker pull {$imageName} 2>/dev/null",
 | 
				
			||||||
 | 
					                        "ignoreErrors" => true,
 | 
				
			||||||
 | 
					                        "hidden" => true
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    [
 | 
				
			||||||
 | 
					                        "name" => "local_image_found",
 | 
				
			||||||
 | 
					                        "command" => "docker images -q {$imageName} 2>/dev/null",
 | 
				
			||||||
 | 
					                        "hidden" => true,
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                ]),
 | 
				
			||||||
 | 
					                loggingModel: $deployment
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function createWorkDirForDeployment(string $workdir, ApplicationDeploymentQueue $deployment)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->executeRemoteCommand(
 | 
				
			||||||
 | 
					            commands: collect([
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    "command" => executeInDocker($deployment->deployment_uuid, "mkdir -p {$workdir}"),
 | 
				
			||||||
 | 
					                    "ignoreErrors" => true,
 | 
				
			||||||
 | 
					                    "hidden" => true
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					            ]),
 | 
				
			||||||
 | 
					            loggingModel: $deployment
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,28 +8,15 @@ use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class EventServiceProvider extends ServiceProvider
 | 
					class EventServiceProvider extends ServiceProvider
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The event to listener mappings for the application.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @var array<class-string, array<int, class-string>>
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected $listen = [
 | 
					    protected $listen = [
 | 
				
			||||||
        Registered::class => [
 | 
					        // Registered::class => [
 | 
				
			||||||
            SendEmailVerificationNotification::class,
 | 
					        //     SendEmailVerificationNotification::class,
 | 
				
			||||||
        ],
 | 
					        // ],
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Register any events for your application.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function boot(): void
 | 
					    public function boot(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Determine if events and listeners should be automatically discovered.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function shouldDiscoverEvents(): bool
 | 
					    public function shouldDiscoverEvents(): bool
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use App\Jobs\ApplicationDeployDockerImageJob;
 | 
					use App\Jobs\ApplicationDeployDockerImageJob;
 | 
				
			||||||
use App\Jobs\ApplicationDeploymentJob;
 | 
					use App\Jobs\ApplicationDeploymentJob;
 | 
				
			||||||
 | 
					use App\Jobs\ApplicationDeploymentNewJob;
 | 
				
			||||||
use App\Jobs\ApplicationDeploySimpleDockerfileJob;
 | 
					use App\Jobs\ApplicationDeploySimpleDockerfileJob;
 | 
				
			||||||
use App\Jobs\ApplicationRestartJob;
 | 
					use App\Jobs\ApplicationRestartJob;
 | 
				
			||||||
use App\Jobs\MultipleApplicationDeploymentJob;
 | 
					use App\Jobs\MultipleApplicationDeploymentJob;
 | 
				
			||||||
@@ -11,7 +12,7 @@ use App\Models\ApplicationPreview;
 | 
				
			|||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
use Symfony\Component\Yaml\Yaml;
 | 
					use Symfony\Component\Yaml\Yaml;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function queue_application_deployment(int $application_id, string $deployment_uuid, int | null $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null)
 | 
					function queue_application_deployment(int $application_id, string $deployment_uuid, int | null $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null, bool $is_new_deployment = false)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    $deployment = ApplicationDeploymentQueue::create([
 | 
					    $deployment = ApplicationDeploymentQueue::create([
 | 
				
			||||||
        'application_id' => $application_id,
 | 
					        'application_id' => $application_id,
 | 
				
			||||||
@@ -36,20 +37,33 @@ function queue_application_deployment(int $application_id, string $deployment_uu
 | 
				
			|||||||
    if ($running_deployments->count() > 0) {
 | 
					    if ($running_deployments->count() > 0) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if ($is_new_deployment) {
 | 
				
			||||||
 | 
					        dispatch(new ApplicationDeploymentNewJob(
 | 
				
			||||||
 | 
					            deployment: $deployment,
 | 
				
			||||||
 | 
					            application: Application::find($application_id)
 | 
				
			||||||
 | 
					        ))->onConnection('long-running')->onQueue('long-running');
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
        dispatch(new ApplicationDeploymentJob(
 | 
					        dispatch(new ApplicationDeploymentJob(
 | 
				
			||||||
            application_deployment_queue_id: $deployment->id,
 | 
					            application_deployment_queue_id: $deployment->id,
 | 
				
			||||||
        ))->onConnection('long-running')->onQueue('long-running');
 | 
					        ))->onConnection('long-running')->onQueue('long-running');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function queue_next_deployment(Application $application)
 | 
					function queue_next_deployment(Application $application, bool $isNew = false)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    $next_found = ApplicationDeploymentQueue::where('application_id', $application->id)->where('status', 'queued')->first();
 | 
					    $next_found = ApplicationDeploymentQueue::where('application_id', $application->id)->where('status', 'queued')->first();
 | 
				
			||||||
    if ($next_found) {
 | 
					    if ($next_found) {
 | 
				
			||||||
 | 
					        if ($isNew) {
 | 
				
			||||||
 | 
					            dispatch(new ApplicationDeploymentNewJob(
 | 
				
			||||||
 | 
					                deployment: $next_found,
 | 
				
			||||||
 | 
					                application: $application
 | 
				
			||||||
 | 
					            ))->onConnection('long-running')->onQueue('long-running');
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
            dispatch(new ApplicationDeploymentJob(
 | 
					            dispatch(new ApplicationDeploymentJob(
 | 
				
			||||||
                application_deployment_queue_id: $next_found->id,
 | 
					                application_deployment_queue_id: $next_found->id,
 | 
				
			||||||
            ))->onConnection('long-running')->onQueue('long-running');
 | 
					            ))->onConnection('long-running')->onQueue('long-running');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
// Deployment things
 | 
					// Deployment things
 | 
				
			||||||
function generateHostIpMapping(Server $server, string $network)
 | 
					function generateHostIpMapping(Server $server, string $network)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1124,7 +1124,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
                    $serviceVolumes = $serviceVolumes->map(function ($volume) use ($resource, $pull_request_id, $topLevelVolumes) {
 | 
					                    $serviceVolumes = $serviceVolumes->map(function ($volume) use ($resource, $pull_request_id, $topLevelVolumes) {
 | 
				
			||||||
                        if (is_string($volume)) {
 | 
					                        if (is_string($volume)) {
 | 
				
			||||||
                            $volume = str($volume);
 | 
					                            $volume = str($volume);
 | 
				
			||||||
                            if ($volume->contains(':')) {
 | 
					                            if ($volume->contains(':') && !$volume->startsWith('/')) {
 | 
				
			||||||
                                $name = $volume->before(':');
 | 
					                                $name = $volume->before(':');
 | 
				
			||||||
                                $mount = $volume->after(':');
 | 
					                                $mount = $volume->after(':');
 | 
				
			||||||
                                $newName = $resource->uuid . "-{$name}-pr-$pull_request_id";
 | 
					                                $newName = $resource->uuid . "-{$name}-pr-$pull_request_id";
 | 
				
			||||||
@@ -1138,13 +1138,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
                            if ($source) {
 | 
					                            if ($source) {
 | 
				
			||||||
                                $newSource = $resource->uuid . "-{$source}-pr-$pull_request_id";
 | 
					                                $newSource = $resource->uuid . "-{$source}-pr-$pull_request_id";
 | 
				
			||||||
                                data_set($volume, 'source', $newSource);
 | 
					                                data_set($volume, 'source', $newSource);
 | 
				
			||||||
 | 
					                                if (!str($source)->startsWith('/')) {
 | 
				
			||||||
                                    $topLevelVolumes->put($newSource, [
 | 
					                                    $topLevelVolumes->put($newSource, [
 | 
				
			||||||
                                        'name' => $newSource,
 | 
					                                        'name' => $newSource,
 | 
				
			||||||
                                    ]);
 | 
					                                    ]);
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
                        return $volume->value();
 | 
					                        return $volume->value();
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    data_set($service, 'volumes', $serviceVolumes->toArray());
 | 
					                    data_set($service, 'volumes', $serviceVolumes->toArray());
 | 
				
			||||||
@@ -1154,7 +1154,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
                    $serviceVolumes = $serviceVolumes->map(function ($volume) use ($resource, $topLevelVolumes) {
 | 
					                    $serviceVolumes = $serviceVolumes->map(function ($volume) use ($resource, $topLevelVolumes) {
 | 
				
			||||||
                        if (is_string($volume)) {
 | 
					                        if (is_string($volume)) {
 | 
				
			||||||
                            $volume = str($volume);
 | 
					                            $volume = str($volume);
 | 
				
			||||||
                            if ($volume->contains(':')) {
 | 
					                            if ($volume->contains(':') && !$volume->startsWith('/')) {
 | 
				
			||||||
                                $name = $volume->before(':');
 | 
					                                $name = $volume->before(':');
 | 
				
			||||||
                                $mount = $volume->after(':');
 | 
					                                $mount = $volume->after(':');
 | 
				
			||||||
                                if ($name->startsWith('.') || $name->startsWith('~')) {
 | 
					                                if ($name->startsWith('.') || $name->startsWith('~')) {
 | 
				
			||||||
@@ -1175,7 +1175,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
                        } else if (is_array($volume)) {
 | 
					                        } else if (is_array($volume)) {
 | 
				
			||||||
                            $source = data_get($volume, 'source');
 | 
					                            $source = data_get($volume, 'source');
 | 
				
			||||||
                            if ($source) {
 | 
					                            if ($source) {
 | 
				
			||||||
                                if (str($source, '.') || str($source, '~')) {
 | 
					                                if ((str($source)->startsWith('.') || str($source)->startsWith('~')) && !str($source)->startsWith('/')) {
 | 
				
			||||||
                                    $dir = base_configuration_dir() . '/applications/' . $resource->uuid;
 | 
					                                    $dir = base_configuration_dir() . '/applications/' . $resource->uuid;
 | 
				
			||||||
                                    if (str($source, '.')) {
 | 
					                                    if (str($source, '.')) {
 | 
				
			||||||
                                        $source = str('.', $dir, $source);
 | 
					                                        $source = str('.', $dir, $source);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,7 @@
 | 
				
			|||||||
        "nubs/random-name-generator": "^2.2",
 | 
					        "nubs/random-name-generator": "^2.2",
 | 
				
			||||||
        "phpseclib/phpseclib": "~3.0",
 | 
					        "phpseclib/phpseclib": "~3.0",
 | 
				
			||||||
        "poliander/cron": "^3.0",
 | 
					        "poliander/cron": "^3.0",
 | 
				
			||||||
 | 
					        "pusher/pusher-php-server": "^7.2",
 | 
				
			||||||
        "resend/resend-laravel": "^0.5.0",
 | 
					        "resend/resend-laravel": "^0.5.0",
 | 
				
			||||||
        "sentry/sentry-laravel": "^3.4",
 | 
					        "sentry/sentry-laravel": "^3.4",
 | 
				
			||||||
        "spatie/laravel-activitylog": "^4.7.3",
 | 
					        "spatie/laravel-activitylog": "^4.7.3",
 | 
				
			||||||
@@ -38,6 +39,7 @@
 | 
				
			|||||||
        "stripe/stripe-php": "^12.0",
 | 
					        "stripe/stripe-php": "^12.0",
 | 
				
			||||||
        "symfony/yaml": "^6.2",
 | 
					        "symfony/yaml": "^6.2",
 | 
				
			||||||
        "visus/cuid2": "^2.0.0",
 | 
					        "visus/cuid2": "^2.0.0",
 | 
				
			||||||
 | 
					        "wire-elements/modal": "^1.0",
 | 
				
			||||||
        "yosymfony/toml": "^1.0"
 | 
					        "yosymfony/toml": "^1.0"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "require-dev": {
 | 
					    "require-dev": {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1279
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1279
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -194,7 +194,7 @@ return [
 | 
				
			|||||||
        App\Providers\AppServiceProvider::class,
 | 
					        App\Providers\AppServiceProvider::class,
 | 
				
			||||||
        App\Providers\FortifyServiceProvider::class,
 | 
					        App\Providers\FortifyServiceProvider::class,
 | 
				
			||||||
        App\Providers\AuthServiceProvider::class,
 | 
					        App\Providers\AuthServiceProvider::class,
 | 
				
			||||||
        // App\Providers\BroadcastServiceProvider::class,
 | 
					        App\Providers\BroadcastServiceProvider::class,
 | 
				
			||||||
        App\Providers\EventServiceProvider::class,
 | 
					        App\Providers\EventServiceProvider::class,
 | 
				
			||||||
        App\Providers\HorizonServiceProvider::class,
 | 
					        App\Providers\HorizonServiceProvider::class,
 | 
				
			||||||
        App\Providers\RouteServiceProvider::class,
 | 
					        App\Providers\RouteServiceProvider::class,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ return [
 | 
				
			|||||||
    |
 | 
					    |
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'default' => env('BROADCAST_DRIVER', 'null'),
 | 
					    'default' => env('BROADCAST_DRIVER', 'pusher'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
    |--------------------------------------------------------------------------
 | 
					    |--------------------------------------------------------------------------
 | 
				
			||||||
@@ -32,13 +32,13 @@ return [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        'pusher' => [
 | 
					        'pusher' => [
 | 
				
			||||||
            'driver' => 'pusher',
 | 
					            'driver' => 'pusher',
 | 
				
			||||||
            'key' => env('PUSHER_APP_KEY'),
 | 
					            'key' => env('PUSHER_APP_KEY', 'coolify'),
 | 
				
			||||||
            'secret' => env('PUSHER_APP_SECRET'),
 | 
					            'secret' => env('PUSHER_APP_SECRET', 'coolify'),
 | 
				
			||||||
            'app_id' => env('PUSHER_APP_ID'),
 | 
					            'app_id' => env('PUSHER_APP_ID', 'coolify'),
 | 
				
			||||||
            'options' => [
 | 
					            'options' => [
 | 
				
			||||||
                'host' => env('PUSHER_HOST') ?: 'api-' . env('PUSHER_APP_CLUSTER', 'mt1') . '.pusher.com',
 | 
					                'host' => env('PUSHER_BACKEND_HOST', 'coolify-soketi'),
 | 
				
			||||||
                'port' => env('PUSHER_PORT', 443),
 | 
					                'port' => env('PUSHER_BACKEND_PORT', 6001),
 | 
				
			||||||
                'scheme' => env('PUSHER_SCHEME', 'https'),
 | 
					                'scheme' => env('PUSHER_SCHEME', 'http'),
 | 
				
			||||||
                'encrypted' => true,
 | 
					                'encrypted' => true,
 | 
				
			||||||
                'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
 | 
					                'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										52
									
								
								config/livewire-ui-modal.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								config/livewire-ui-modal.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					    |--------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    | Include CSS
 | 
				
			||||||
 | 
					    |--------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    |
 | 
				
			||||||
 | 
					    | The modal uses TailwindCSS, if you don't use TailwindCSS you will need
 | 
				
			||||||
 | 
					    | to set this parameter to true. This includes the modern-normalize css.
 | 
				
			||||||
 | 
					    |
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    'include_css' => false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					    |--------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    | Include JS
 | 
				
			||||||
 | 
					    |--------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    |
 | 
				
			||||||
 | 
					    | Livewire UI will inject the required Javascript in your blade template.
 | 
				
			||||||
 | 
					    | If you want to bundle the required Javascript you can set this to false
 | 
				
			||||||
 | 
					    | and add `require('vendor/wire-elements/modal/resources/js/modal');`
 | 
				
			||||||
 | 
					    | to your script bundler like webpack.
 | 
				
			||||||
 | 
					    |
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    'include_js' => false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					    |--------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    | Modal Component Defaults
 | 
				
			||||||
 | 
					    |--------------------------------------------------------------------------
 | 
				
			||||||
 | 
					    |
 | 
				
			||||||
 | 
					    | Configure the default properties for a modal component.
 | 
				
			||||||
 | 
					    |
 | 
				
			||||||
 | 
					    | Supported modal_max_width
 | 
				
			||||||
 | 
					    | 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl'
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    'component_defaults' => [
 | 
				
			||||||
 | 
					        'modal_max_width' => '7xl',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        'close_modal_on_click_away' => true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        'close_modal_on_escape' => true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        'close_modal_on_escape_is_forceful' => true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        'dispatch_close_event' => false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        'destroy_on_close' => false,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
@@ -7,7 +7,7 @@ return [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // The release version of your application
 | 
					    // The release version of your application
 | 
				
			||||||
    // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
 | 
					    // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
 | 
				
			||||||
    'release' => '4.0.0-beta.153',
 | 
					    'release' => '4.0.0-beta.154',
 | 
				
			||||||
    // When left empty or `null` the Laravel environment will be used
 | 
					    // When left empty or `null` the Laravel environment will be used
 | 
				
			||||||
    'environment' => config('app.env'),
 | 
					    'environment' => config('app.env'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,3 @@
 | 
				
			|||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return '4.0.0-beta.153';
 | 
					return '4.0.0-beta.154';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,11 +13,11 @@ class StandaloneDockerSeeder extends Seeder
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function run(): void
 | 
					    public function run(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // StandaloneDocker::create([
 | 
					        StandaloneDocker::create([
 | 
				
			||||||
        //     'id' => 0,
 | 
					            'id' => 0,
 | 
				
			||||||
        //     'name' => 'Standalone Docker 1',
 | 
					            'name' => 'Standalone Docker 1',
 | 
				
			||||||
        //     'network' => 'coolify',
 | 
					            'network' => 'coolify',
 | 
				
			||||||
        //     'server_id' => 0,
 | 
					            'server_id' => 0,
 | 
				
			||||||
        // ]);
 | 
					        ]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,12 @@ services:
 | 
				
			|||||||
      SSL_MODE: "off"
 | 
					      SSL_MODE: "off"
 | 
				
			||||||
      AUTORUN_LARAVEL_STORAGE_LINK: "false"
 | 
					      AUTORUN_LARAVEL_STORAGE_LINK: "false"
 | 
				
			||||||
      AUTORUN_LARAVEL_MIGRATION: "false"
 | 
					      AUTORUN_LARAVEL_MIGRATION: "false"
 | 
				
			||||||
 | 
					      PUSHER_HOST: "${PUSHER_HOST}"
 | 
				
			||||||
 | 
					      PUSHER_PORT: "${PUSHER_PORT:-6001}"
 | 
				
			||||||
 | 
					      PUSHER_SCHEME: "${PUSHER_SCHEME:-http}"
 | 
				
			||||||
 | 
					      PUSHER_APP_ID: "${PUSHER_APP_ID:-coolify}"
 | 
				
			||||||
 | 
					      PUSHER_APP_KEY: "${PUSHER_APP_KEY:-coolify}"
 | 
				
			||||||
 | 
					      PUSHER_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}"
 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - .:/var/www/html/:cached
 | 
					      - .:/var/www/html/:cached
 | 
				
			||||||
  postgres:
 | 
					  postgres:
 | 
				
			||||||
@@ -44,9 +50,21 @@ services:
 | 
				
			|||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - /data/coolify/_volumes/redis/:/data
 | 
					      - /data/coolify/_volumes/redis/:/data
 | 
				
			||||||
      # - coolify-redis-data-dev:/data
 | 
					      # - coolify-redis-data-dev:/data
 | 
				
			||||||
 | 
					  soketi:
 | 
				
			||||||
 | 
					    env_file:
 | 
				
			||||||
 | 
					      - .env
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - "${FORWARD_SOKETI_PORT:-6001}:6001"
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      SOKETI_DEBUG: "false"
 | 
				
			||||||
 | 
					      SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID:-coolify}"
 | 
				
			||||||
 | 
					      SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY:-coolify}"
 | 
				
			||||||
 | 
					      SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}"
 | 
				
			||||||
  vite:
 | 
					  vite:
 | 
				
			||||||
    image: node:20
 | 
					    image: node:20
 | 
				
			||||||
    working_dir: /var/www/html
 | 
					    working_dir: /var/www/html
 | 
				
			||||||
 | 
					    # environment:
 | 
				
			||||||
 | 
					    # VITE_PUSHER_APP_KEY: "${PUSHER_APP_KEY:-coolify}"
 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - "${VITE_PORT:-5173}:${VITE_PORT:-5173}"
 | 
					      - "${VITE_PORT:-5173}:${VITE_PORT:-5173}"
 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
@@ -60,14 +78,14 @@ services:
 | 
				
			|||||||
      - /var/run/docker.sock:/var/run/docker.sock
 | 
					      - /var/run/docker.sock:/var/run/docker.sock
 | 
				
			||||||
      - /data/coolify/:/data/coolify
 | 
					      - /data/coolify/:/data/coolify
 | 
				
			||||||
      # - coolify-data-dev:/data/coolify
 | 
					      # - coolify-data-dev:/data/coolify
 | 
				
			||||||
  remote-host:
 | 
					      # remote-host:
 | 
				
			||||||
    <<: *testing-host-base
 | 
					      #   <<: *testing-host-base
 | 
				
			||||||
    container_name: coolify-remote-host
 | 
					      #   container_name: coolify-remote-host
 | 
				
			||||||
    volumes:
 | 
					      #   volumes:
 | 
				
			||||||
      - /:/host
 | 
					      #     - /:/host
 | 
				
			||||||
      - /var/run/docker.sock:/var/run/docker.sock
 | 
					      #     - /var/run/docker.sock:/var/run/docker.sock
 | 
				
			||||||
      - /data/coolify/:/data/coolify
 | 
					      #     - /data/coolify/:/data/coolify
 | 
				
			||||||
      # - coolify-data-dev:/data/coolify
 | 
					      #     # - coolify-data-dev:/data/coolify
 | 
				
			||||||
  mailpit:
 | 
					  mailpit:
 | 
				
			||||||
    image: "axllent/mailpit:latest"
 | 
					    image: "axllent/mailpit:latest"
 | 
				
			||||||
    container_name: coolify-mail
 | 
					    container_name: coolify-mail
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
version: '3.8'
 | 
					version: '3.8'
 | 
				
			||||||
services:
 | 
					services:
 | 
				
			||||||
  coolify:
 | 
					  coolify:
 | 
				
			||||||
    image: "ghcr.io/coollabsio/coolify:${LATEST_IMAGE:-4.0.0-beta.20}"
 | 
					    image: "ghcr.io/coollabsio/coolify:${LATEST_IMAGE:-4.0.0-beta.153}"
 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - type: bind
 | 
					      - type: bind
 | 
				
			||||||
        source: /data/coolify/source/.env
 | 
					        source: /data/coolify/source/.env
 | 
				
			||||||
@@ -35,6 +35,14 @@ services:
 | 
				
			|||||||
      - PHP_PM_START_SERVERS=1
 | 
					      - PHP_PM_START_SERVERS=1
 | 
				
			||||||
      - PHP_PM_MIN_SPARE_SERVERS=1
 | 
					      - PHP_PM_MIN_SPARE_SERVERS=1
 | 
				
			||||||
      - PHP_PM_MAX_SPARE_SERVERS=10
 | 
					      - PHP_PM_MAX_SPARE_SERVERS=10
 | 
				
			||||||
 | 
					      - PUSHER_HOST
 | 
				
			||||||
 | 
					      - PUSHER_BACKEND_HOST
 | 
				
			||||||
 | 
					      - PUSHER_PORT
 | 
				
			||||||
 | 
					      - PUSHER_BACKEND_PORT
 | 
				
			||||||
 | 
					      - PUSHER_SCHEME
 | 
				
			||||||
 | 
					      - PUSHER_APP_ID
 | 
				
			||||||
 | 
					      - PUSHER_APP_KEY
 | 
				
			||||||
 | 
					      - PUSHER_APP_SECRET
 | 
				
			||||||
      - SELF_HOSTED
 | 
					      - SELF_HOSTED
 | 
				
			||||||
      - WAITLIST
 | 
					      - WAITLIST
 | 
				
			||||||
      - SUBSCRIPTION_PROVIDER
 | 
					      - SUBSCRIPTION_PROVIDER
 | 
				
			||||||
@@ -74,8 +82,8 @@ services:
 | 
				
			|||||||
      - "${APP_PORT:-8000}"
 | 
					      - "${APP_PORT:-8000}"
 | 
				
			||||||
    healthcheck:
 | 
					    healthcheck:
 | 
				
			||||||
      test: curl --fail http://localhost:80/api/health || exit 1
 | 
					      test: curl --fail http://localhost:80/api/health || exit 1
 | 
				
			||||||
      interval: 4s
 | 
					      interval: 5s
 | 
				
			||||||
      retries: 5
 | 
					      retries: 10
 | 
				
			||||||
      timeout: 2s
 | 
					      timeout: 2s
 | 
				
			||||||
    depends_on:
 | 
					    depends_on:
 | 
				
			||||||
      postgres:
 | 
					      postgres:
 | 
				
			||||||
@@ -97,8 +105,8 @@ services:
 | 
				
			|||||||
          "-d",
 | 
					          "-d",
 | 
				
			||||||
          "${DB_DATABASE:-coolify}"
 | 
					          "${DB_DATABASE:-coolify}"
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
      interval: 2s
 | 
					      interval: 5s
 | 
				
			||||||
      retries: 5
 | 
					      retries: 10
 | 
				
			||||||
      timeout: 2s
 | 
					      timeout: 2s
 | 
				
			||||||
  redis:
 | 
					  redis:
 | 
				
			||||||
    command: redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD}
 | 
					    command: redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD}
 | 
				
			||||||
@@ -108,8 +116,21 @@ services:
 | 
				
			|||||||
      - coolify-redis:/data
 | 
					      - coolify-redis:/data
 | 
				
			||||||
    healthcheck:
 | 
					    healthcheck:
 | 
				
			||||||
      test: redis-cli ping
 | 
					      test: redis-cli ping
 | 
				
			||||||
      interval: 2s
 | 
					      interval: 5s
 | 
				
			||||||
      retries: 5
 | 
					      retries: 10
 | 
				
			||||||
 | 
					      timeout: 2s
 | 
				
			||||||
 | 
					  soketi:
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - "${SOKETI_PORT:-6001}:6001"
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      SOKETI_DEBUG: "${SOKETI_DEBUG:-false}"
 | 
				
			||||||
 | 
					      SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID}"
 | 
				
			||||||
 | 
					      SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY}"
 | 
				
			||||||
 | 
					      SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET}"
 | 
				
			||||||
 | 
					    healthcheck:
 | 
				
			||||||
 | 
					      test: wget -qO- http://localhost:6001/ready || exit 1
 | 
				
			||||||
 | 
					      interval: 5s
 | 
				
			||||||
 | 
					      retries: 10
 | 
				
			||||||
      timeout: 2s
 | 
					      timeout: 2s
 | 
				
			||||||
volumes:
 | 
					volumes:
 | 
				
			||||||
  coolify-db:
 | 
					  coolify-db:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,12 @@ services:
 | 
				
			|||||||
        restart: always
 | 
					        restart: always
 | 
				
			||||||
        networks:
 | 
					        networks:
 | 
				
			||||||
            - coolify
 | 
					            - coolify
 | 
				
			||||||
 | 
					    soketi:
 | 
				
			||||||
 | 
					        image: 'quay.io/soketi/soketi:1.6-16-alpine'
 | 
				
			||||||
 | 
					        container_name: coolify-soketi
 | 
				
			||||||
 | 
					        restart: always
 | 
				
			||||||
 | 
					        networks:
 | 
				
			||||||
 | 
					            - coolify
 | 
				
			||||||
networks:
 | 
					networks:
 | 
				
			||||||
    coolify:
 | 
					    coolify:
 | 
				
			||||||
        name: coolify
 | 
					        name: coolify
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										385
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										385
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -5,20 +5,24 @@
 | 
				
			|||||||
    "packages": {
 | 
					    "packages": {
 | 
				
			||||||
        "": {
 | 
					        "": {
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
 | 
					                "@alpinejs/focus": "^3.13.3",
 | 
				
			||||||
                "@tailwindcss/typography": "0.5.10",
 | 
					                "@tailwindcss/typography": "0.5.10",
 | 
				
			||||||
                "alpinejs": "3.13.2",
 | 
					                "alpinejs": "3.13.3",
 | 
				
			||||||
                "daisyui": "4.3.1",
 | 
					                "daisyui": "4.4.19",
 | 
				
			||||||
 | 
					                "ioredis": "5.3.2",
 | 
				
			||||||
                "tailwindcss-scrollbar": "0.1.0"
 | 
					                "tailwindcss-scrollbar": "0.1.0"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "devDependencies": {
 | 
					            "devDependencies": {
 | 
				
			||||||
                "@vitejs/plugin-vue": "4.5.0",
 | 
					                "@vitejs/plugin-vue": "4.5.1",
 | 
				
			||||||
                "autoprefixer": "10.4.16",
 | 
					                "autoprefixer": "10.4.16",
 | 
				
			||||||
                "axios": "1.6.2",
 | 
					                "axios": "1.6.2",
 | 
				
			||||||
 | 
					                "laravel-echo": "1.15.3",
 | 
				
			||||||
                "laravel-vite-plugin": "0.8.1",
 | 
					                "laravel-vite-plugin": "0.8.1",
 | 
				
			||||||
                "postcss": "8.4.31",
 | 
					                "postcss": "8.4.32",
 | 
				
			||||||
                "tailwindcss": "3.3.5",
 | 
					                "pusher-js": "8.4.0-rc2",
 | 
				
			||||||
                "vite": "4.5.0",
 | 
					                "tailwindcss": "3.3.6",
 | 
				
			||||||
                "vue": "3.3.8"
 | 
					                "vite": "4.5.1",
 | 
				
			||||||
 | 
					                "vue": "3.3.10"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@alloc/quick-lru": {
 | 
					        "node_modules/@alloc/quick-lru": {
 | 
				
			||||||
@@ -32,10 +36,19 @@
 | 
				
			|||||||
                "url": "https://github.com/sponsors/sindresorhus"
 | 
					                "url": "https://github.com/sponsors/sindresorhus"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/@alpinejs/focus": {
 | 
				
			||||||
 | 
					            "version": "3.13.3",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/@alpinejs/focus/-/focus-3.13.3.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-fTRX/9wOfysyZ1PJ4gHeUnmiNTIgqBDIqKxeP5iMvj1UHD3TFLDXllvoIKH3ezqcsyQZqxd/q1MFM7dlIhkmeg==",
 | 
				
			||||||
 | 
					            "dependencies": {
 | 
				
			||||||
 | 
					                "focus-trap": "^6.9.4",
 | 
				
			||||||
 | 
					                "tabbable": "^5.3.3"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/@babel/parser": {
 | 
					        "node_modules/@babel/parser": {
 | 
				
			||||||
            "version": "7.23.3",
 | 
					            "version": "7.23.5",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz",
 | 
				
			||||||
            "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==",
 | 
					            "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "bin": {
 | 
					            "bin": {
 | 
				
			||||||
                "parser": "bin/babel-parser.js"
 | 
					                "parser": "bin/babel-parser.js"
 | 
				
			||||||
@@ -396,6 +409,11 @@
 | 
				
			|||||||
                "node": ">=12"
 | 
					                "node": ">=12"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/@ioredis/commands": {
 | 
				
			||||||
 | 
					            "version": "1.2.0",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/@jridgewell/gen-mapping": {
 | 
					        "node_modules/@jridgewell/gen-mapping": {
 | 
				
			||||||
            "version": "0.3.3",
 | 
					            "version": "0.3.3",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
 | 
				
			||||||
@@ -503,9 +521,9 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vitejs/plugin-vue": {
 | 
					        "node_modules/@vitejs/plugin-vue": {
 | 
				
			||||||
            "version": "4.5.0",
 | 
					            "version": "4.5.1",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.5.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.5.1.tgz",
 | 
				
			||||||
            "integrity": "sha512-a2WSpP8X8HTEww/U00bU4mX1QpLINNuz/2KMNpLsdu3BzOpak3AGI1CJYBTXcc4SPhaD0eNRUp7IyQK405L5dQ==",
 | 
					            "integrity": "sha512-DaUzYFr+2UGDG7VSSdShKa9sIWYBa1LL8KC0MNOf2H5LjcTPjob0x8LbkqXWmAtbANJCkpiQTj66UVcQkN2s3g==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "engines": {
 | 
					            "engines": {
 | 
				
			||||||
                "node": "^14.18.0 || >=16.0.0"
 | 
					                "node": "^14.18.0 || >=16.0.0"
 | 
				
			||||||
@@ -516,77 +534,77 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/compiler-core": {
 | 
					        "node_modules/@vue/compiler-core": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
 | 
					            "integrity": "sha512-doe0hODR1+i1menPkRzJ5MNR6G+9uiZHIknK3Zn5OcIztu6GGw7u0XUzf3AgB8h/dfsZC9eouzoLo3c3+N/cVA==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@babel/parser": "^7.23.0",
 | 
					                "@babel/parser": "^7.23.5",
 | 
				
			||||||
                "@vue/shared": "3.3.8",
 | 
					                "@vue/shared": "3.3.10",
 | 
				
			||||||
                "estree-walker": "^2.0.2",
 | 
					                "estree-walker": "^2.0.2",
 | 
				
			||||||
                "source-map-js": "^1.0.2"
 | 
					                "source-map-js": "^1.0.2"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/compiler-core/node_modules/@vue/shared": {
 | 
					        "node_modules/@vue/compiler-core/node_modules/@vue/shared": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
					            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/compiler-dom": {
 | 
					        "node_modules/@vue/compiler-dom": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
 | 
					            "integrity": "sha512-NCrqF5fm10GXZIK0GrEAauBqdy+F2LZRt3yNHzrYjpYBuRssQbuPLtSnSNjyR9luHKkWSH8we5LMB3g+4z2HvA==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@vue/compiler-core": "3.3.8",
 | 
					                "@vue/compiler-core": "3.3.10",
 | 
				
			||||||
                "@vue/shared": "3.3.8"
 | 
					                "@vue/shared": "3.3.10"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/compiler-dom/node_modules/@vue/shared": {
 | 
					        "node_modules/@vue/compiler-dom/node_modules/@vue/shared": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
					            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/compiler-sfc": {
 | 
					        "node_modules/@vue/compiler-sfc": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
 | 
					            "integrity": "sha512-xpcTe7Rw7QefOTRFFTlcfzozccvjM40dT45JtrE3onGm/jBLZ0JhpKu3jkV7rbDFLeeagR/5RlJ2Y9SvyS0lAg==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@babel/parser": "^7.23.0",
 | 
					                "@babel/parser": "^7.23.5",
 | 
				
			||||||
                "@vue/compiler-core": "3.3.8",
 | 
					                "@vue/compiler-core": "3.3.10",
 | 
				
			||||||
                "@vue/compiler-dom": "3.3.8",
 | 
					                "@vue/compiler-dom": "3.3.10",
 | 
				
			||||||
                "@vue/compiler-ssr": "3.3.8",
 | 
					                "@vue/compiler-ssr": "3.3.10",
 | 
				
			||||||
                "@vue/reactivity-transform": "3.3.8",
 | 
					                "@vue/reactivity-transform": "3.3.10",
 | 
				
			||||||
                "@vue/shared": "3.3.8",
 | 
					                "@vue/shared": "3.3.10",
 | 
				
			||||||
                "estree-walker": "^2.0.2",
 | 
					                "estree-walker": "^2.0.2",
 | 
				
			||||||
                "magic-string": "^0.30.5",
 | 
					                "magic-string": "^0.30.5",
 | 
				
			||||||
                "postcss": "^8.4.31",
 | 
					                "postcss": "^8.4.32",
 | 
				
			||||||
                "source-map-js": "^1.0.2"
 | 
					                "source-map-js": "^1.0.2"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
 | 
					        "node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
					            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/compiler-ssr": {
 | 
					        "node_modules/@vue/compiler-ssr": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
 | 
					            "integrity": "sha512-12iM4jA4GEbskwXMmPcskK5wImc2ohKm408+o9iox3tfN9qua8xL0THIZtoe9OJHnXP4eOWZpgCAAThEveNlqQ==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@vue/compiler-dom": "3.3.8",
 | 
					                "@vue/compiler-dom": "3.3.10",
 | 
				
			||||||
                "@vue/shared": "3.3.8"
 | 
					                "@vue/shared": "3.3.10"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
 | 
					        "node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
					            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/reactivity": {
 | 
					        "node_modules/@vue/reactivity": {
 | 
				
			||||||
@@ -598,83 +616,83 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/reactivity-transform": {
 | 
					        "node_modules/@vue/reactivity-transform": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
 | 
					            "integrity": "sha512-0xBdk+CKHWT+Gev8oZ63Tc0qFfj935YZx+UAynlutnrDZ4diFCVFMWixn65HzjE3S1iJppWOo6Tt1OzASH7VEg==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@babel/parser": "^7.23.0",
 | 
					                "@babel/parser": "^7.23.5",
 | 
				
			||||||
                "@vue/compiler-core": "3.3.8",
 | 
					                "@vue/compiler-core": "3.3.10",
 | 
				
			||||||
                "@vue/shared": "3.3.8",
 | 
					                "@vue/shared": "3.3.10",
 | 
				
			||||||
                "estree-walker": "^2.0.2",
 | 
					                "estree-walker": "^2.0.2",
 | 
				
			||||||
                "magic-string": "^0.30.5"
 | 
					                "magic-string": "^0.30.5"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/reactivity-transform/node_modules/@vue/shared": {
 | 
					        "node_modules/@vue/reactivity-transform/node_modules/@vue/shared": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
					            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/runtime-core": {
 | 
					        "node_modules/@vue/runtime-core": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
 | 
					            "integrity": "sha512-DZ0v31oTN4YHX9JEU5VW1LoIVgFovWgIVb30bWn9DG9a7oA415idcwsRNNajqTx8HQJyOaWfRKoyuP2P2TYIag==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@vue/reactivity": "3.3.8",
 | 
					                "@vue/reactivity": "3.3.10",
 | 
				
			||||||
                "@vue/shared": "3.3.8"
 | 
					                "@vue/shared": "3.3.10"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/runtime-core/node_modules/@vue/reactivity": {
 | 
					        "node_modules/@vue/runtime-core/node_modules/@vue/reactivity": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
 | 
					            "integrity": "sha512-H5Z7rOY/JLO+e5a6/FEXaQ1TMuOvY4LDVgT+/+HKubEAgs9qeeZ+NhADSeEtrNQeiKLDuzeKc8v0CUFpB6Pqgw==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@vue/shared": "3.3.8"
 | 
					                "@vue/shared": "3.3.10"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/runtime-core/node_modules/@vue/shared": {
 | 
					        "node_modules/@vue/runtime-core/node_modules/@vue/shared": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
					            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/runtime-dom": {
 | 
					        "node_modules/@vue/runtime-dom": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
 | 
					            "integrity": "sha512-c/jKb3ny05KJcYk0j1m7Wbhrxq7mZYr06GhKykDMNRRR9S+/dGT8KpHuNQjv3/8U4JshfkAk6TpecPD3B21Ijw==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@vue/runtime-core": "3.3.8",
 | 
					                "@vue/runtime-core": "3.3.10",
 | 
				
			||||||
                "@vue/shared": "3.3.8",
 | 
					                "@vue/shared": "3.3.10",
 | 
				
			||||||
                "csstype": "^3.1.2"
 | 
					                "csstype": "^3.1.2"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
 | 
					        "node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
					            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/server-renderer": {
 | 
					        "node_modules/@vue/server-renderer": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
 | 
					            "integrity": "sha512-0i6ww3sBV3SKlF3YTjSVqKQ74xialMbjVYGy7cOTi7Imd8ediE7t72SK3qnvhrTAhOvlQhq6Bk6nFPdXxe0sAg==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@vue/compiler-ssr": "3.3.8",
 | 
					                "@vue/compiler-ssr": "3.3.10",
 | 
				
			||||||
                "@vue/shared": "3.3.8"
 | 
					                "@vue/shared": "3.3.10"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "peerDependencies": {
 | 
					            "peerDependencies": {
 | 
				
			||||||
                "vue": "3.3.8"
 | 
					                "vue": "3.3.10"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/server-renderer/node_modules/@vue/shared": {
 | 
					        "node_modules/@vue/server-renderer/node_modules/@vue/shared": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
					            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@vue/shared": {
 | 
					        "node_modules/@vue/shared": {
 | 
				
			||||||
@@ -683,9 +701,9 @@
 | 
				
			|||||||
            "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
 | 
					            "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/alpinejs": {
 | 
					        "node_modules/alpinejs": {
 | 
				
			||||||
            "version": "3.13.2",
 | 
					            "version": "3.13.3",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.3.tgz",
 | 
				
			||||||
            "integrity": "sha512-WzojeeN082kLZznGI1HAuP8yFJSWqJ1fGdz2mUjj45H4y0XwToE7fFqtI3mCPRR+BpcSbxT/NL+FyPnYAWSltw==",
 | 
					            "integrity": "sha512-WZ6WQjkAOl+WdW/jukzNHq9zHFDNKmkk/x6WF7WdyNDD6woinrfXCVsZXm0galjbco+pEpYmJLtwlZwcOfIVdg==",
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@vue/reactivity": "~3.1.1"
 | 
					                "@vue/reactivity": "~3.1.1"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -896,6 +914,14 @@
 | 
				
			|||||||
                "node": ">= 6"
 | 
					                "node": ">= 6"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/cluster-key-slot": {
 | 
				
			||||||
 | 
					            "version": "1.1.2",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
 | 
				
			||||||
 | 
					            "engines": {
 | 
				
			||||||
 | 
					                "node": ">=0.10.0"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/combined-stream": {
 | 
					        "node_modules/combined-stream": {
 | 
				
			||||||
            "version": "1.0.8",
 | 
					            "version": "1.0.8",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
 | 
				
			||||||
@@ -956,9 +982,9 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/daisyui": {
 | 
					        "node_modules/daisyui": {
 | 
				
			||||||
            "version": "4.3.1",
 | 
					            "version": "4.4.19",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.3.1.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.4.19.tgz",
 | 
				
			||||||
            "integrity": "sha512-dCi91VD+57lkoBd10CjdW4wPOeOPYvvzQbxti6xmyQbDMbCeCXwNq2KdoU798I4OsCcD5B+n7yVG7HAgYW+cvw==",
 | 
					            "integrity": "sha512-IjOLWwnndD4N7Ut5CDxbUsaVtbqXPeVHM92IcgxGFxpuOd3CCKW/PAXZH6JoBTHFRaN57vB9XqEhdWm5yC+bPA==",
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "css-selector-tokenizer": "^0.8",
 | 
					                "css-selector-tokenizer": "^0.8",
 | 
				
			||||||
                "culori": "^3",
 | 
					                "culori": "^3",
 | 
				
			||||||
@@ -973,6 +999,22 @@
 | 
				
			|||||||
                "url": "https://opencollective.com/daisyui"
 | 
					                "url": "https://opencollective.com/daisyui"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/debug": {
 | 
				
			||||||
 | 
					            "version": "4.3.4",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
 | 
				
			||||||
 | 
					            "dependencies": {
 | 
				
			||||||
 | 
					                "ms": "2.1.2"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "engines": {
 | 
				
			||||||
 | 
					                "node": ">=6.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					                "supports-color": {
 | 
				
			||||||
 | 
					                    "optional": true
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/delayed-stream": {
 | 
					        "node_modules/delayed-stream": {
 | 
				
			||||||
            "version": "1.0.0",
 | 
					            "version": "1.0.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
 | 
				
			||||||
@@ -982,6 +1024,14 @@
 | 
				
			|||||||
                "node": ">=0.4.0"
 | 
					                "node": ">=0.4.0"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/denque": {
 | 
				
			||||||
 | 
					            "version": "2.1.0",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
 | 
				
			||||||
 | 
					            "engines": {
 | 
				
			||||||
 | 
					                "node": ">=0.10"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/didyoumean": {
 | 
					        "node_modules/didyoumean": {
 | 
				
			||||||
            "version": "1.2.2",
 | 
					            "version": "1.2.2",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
 | 
				
			||||||
@@ -1100,6 +1150,14 @@
 | 
				
			|||||||
                "node": ">=8"
 | 
					                "node": ">=8"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/focus-trap": {
 | 
				
			||||||
 | 
					            "version": "6.9.4",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.9.4.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-v2NTsZe2FF59Y+sDykKY+XjqZ0cPfhq/hikWVL88BqLivnNiEffAsac6rP6H45ff9wG9LL5ToiDqrLEP9GX9mw==",
 | 
				
			||||||
 | 
					            "dependencies": {
 | 
				
			||||||
 | 
					                "tabbable": "^5.3.3"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/follow-redirects": {
 | 
					        "node_modules/follow-redirects": {
 | 
				
			||||||
            "version": "1.15.2",
 | 
					            "version": "1.15.2",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
 | 
				
			||||||
@@ -1233,6 +1291,29 @@
 | 
				
			|||||||
            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
 | 
				
			||||||
            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
 | 
					            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/ioredis": {
 | 
				
			||||||
 | 
					            "version": "5.3.2",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==",
 | 
				
			||||||
 | 
					            "dependencies": {
 | 
				
			||||||
 | 
					                "@ioredis/commands": "^1.1.1",
 | 
				
			||||||
 | 
					                "cluster-key-slot": "^1.1.0",
 | 
				
			||||||
 | 
					                "debug": "^4.3.4",
 | 
				
			||||||
 | 
					                "denque": "^2.1.0",
 | 
				
			||||||
 | 
					                "lodash.defaults": "^4.2.0",
 | 
				
			||||||
 | 
					                "lodash.isarguments": "^3.1.0",
 | 
				
			||||||
 | 
					                "redis-errors": "^1.2.0",
 | 
				
			||||||
 | 
					                "redis-parser": "^3.0.0",
 | 
				
			||||||
 | 
					                "standard-as-callback": "^2.1.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "engines": {
 | 
				
			||||||
 | 
					                "node": ">=12.22.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "funding": {
 | 
				
			||||||
 | 
					                "type": "opencollective",
 | 
				
			||||||
 | 
					                "url": "https://opencollective.com/ioredis"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/is-binary-path": {
 | 
					        "node_modules/is-binary-path": {
 | 
				
			||||||
            "version": "2.1.0",
 | 
					            "version": "2.1.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
 | 
				
			||||||
@@ -1290,6 +1371,15 @@
 | 
				
			|||||||
                "jiti": "bin/jiti.js"
 | 
					                "jiti": "bin/jiti.js"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/laravel-echo": {
 | 
				
			||||||
 | 
					            "version": "1.15.3",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/laravel-echo/-/laravel-echo-1.15.3.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-SRXzccaat6w4qKgZ4/rjFKr3nJfVxB+ly4V0MEJNIF1/TpERNXepo3uk7NnOjBGsiV/np1fl2XitAzW4Sa1s/w==",
 | 
				
			||||||
 | 
					            "dev": true,
 | 
				
			||||||
 | 
					            "engines": {
 | 
				
			||||||
 | 
					                "node": ">=10"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/laravel-vite-plugin": {
 | 
					        "node_modules/laravel-vite-plugin": {
 | 
				
			||||||
            "version": "0.8.1",
 | 
					            "version": "0.8.1",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.8.1.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.8.1.tgz",
 | 
				
			||||||
@@ -1324,6 +1414,16 @@
 | 
				
			|||||||
            "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="
 | 
					            "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/lodash.defaults": {
 | 
				
			||||||
 | 
					            "version": "4.2.0",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/lodash.isarguments": {
 | 
				
			||||||
 | 
					            "version": "3.1.0",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/lodash.isplainobject": {
 | 
					        "node_modules/lodash.isplainobject": {
 | 
				
			||||||
            "version": "4.0.6",
 | 
					            "version": "4.0.6",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
 | 
				
			||||||
@@ -1398,6 +1498,11 @@
 | 
				
			|||||||
                "node": "*"
 | 
					                "node": "*"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/ms": {
 | 
				
			||||||
 | 
					            "version": "2.1.2",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/mz": {
 | 
					        "node_modules/mz": {
 | 
				
			||||||
            "version": "2.7.0",
 | 
					            "version": "2.7.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
 | 
				
			||||||
@@ -1409,9 +1514,9 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/nanoid": {
 | 
					        "node_modules/nanoid": {
 | 
				
			||||||
            "version": "3.3.6",
 | 
					            "version": "3.3.7",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
 | 
				
			||||||
            "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
 | 
					            "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
 | 
				
			||||||
            "funding": [
 | 
					            "funding": [
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    "type": "github",
 | 
					                    "type": "github",
 | 
				
			||||||
@@ -1518,9 +1623,9 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/postcss": {
 | 
					        "node_modules/postcss": {
 | 
				
			||||||
            "version": "8.4.31",
 | 
					            "version": "8.4.32",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
 | 
				
			||||||
            "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
 | 
					            "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
 | 
				
			||||||
            "funding": [
 | 
					            "funding": [
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    "type": "opencollective",
 | 
					                    "type": "opencollective",
 | 
				
			||||||
@@ -1536,7 +1641,7 @@
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "nanoid": "^3.3.6",
 | 
					                "nanoid": "^3.3.7",
 | 
				
			||||||
                "picocolors": "^1.0.0",
 | 
					                "picocolors": "^1.0.0",
 | 
				
			||||||
                "source-map-js": "^1.0.2"
 | 
					                "source-map-js": "^1.0.2"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@@ -1647,6 +1752,15 @@
 | 
				
			|||||||
            "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
 | 
					            "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/pusher-js": {
 | 
				
			||||||
 | 
					            "version": "8.4.0-rc2",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-8.4.0-rc2.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-d87GjOEEl9QgO5BWmViSqW0LOzPvybvX6WA9zLUstNdB57jVJuR27zHkRnrav2a3+zAMlHbP2Og8wug+rG8T+g==",
 | 
				
			||||||
 | 
					            "dev": true,
 | 
				
			||||||
 | 
					            "dependencies": {
 | 
				
			||||||
 | 
					                "tweetnacl": "^1.0.3"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/queue-microtask": {
 | 
					        "node_modules/queue-microtask": {
 | 
				
			||||||
            "version": "1.2.3",
 | 
					            "version": "1.2.3",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
 | 
				
			||||||
@@ -1685,6 +1799,25 @@
 | 
				
			|||||||
                "node": ">=8.10.0"
 | 
					                "node": ">=8.10.0"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/redis-errors": {
 | 
				
			||||||
 | 
					            "version": "1.2.0",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
 | 
				
			||||||
 | 
					            "engines": {
 | 
				
			||||||
 | 
					                "node": ">=4"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/redis-parser": {
 | 
				
			||||||
 | 
					            "version": "3.0.0",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
 | 
				
			||||||
 | 
					            "dependencies": {
 | 
				
			||||||
 | 
					                "redis-errors": "^1.0.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "engines": {
 | 
				
			||||||
 | 
					                "node": ">=4"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/resolve": {
 | 
					        "node_modules/resolve": {
 | 
				
			||||||
            "version": "1.22.2",
 | 
					            "version": "1.22.2",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
 | 
				
			||||||
@@ -1775,6 +1908,11 @@
 | 
				
			|||||||
                "node": ">=0.10.0"
 | 
					                "node": ">=0.10.0"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/standard-as-callback": {
 | 
				
			||||||
 | 
					            "version": "2.1.0",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/sucrase": {
 | 
					        "node_modules/sucrase": {
 | 
				
			||||||
            "version": "3.32.0",
 | 
					            "version": "3.32.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
 | 
				
			||||||
@@ -1807,10 +1945,15 @@
 | 
				
			|||||||
                "url": "https://github.com/sponsors/ljharb"
 | 
					                "url": "https://github.com/sponsors/ljharb"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/tabbable": {
 | 
				
			||||||
 | 
					            "version": "5.3.3",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA=="
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/tailwindcss": {
 | 
					        "node_modules/tailwindcss": {
 | 
				
			||||||
            "version": "3.3.5",
 | 
					            "version": "3.3.6",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.6.tgz",
 | 
				
			||||||
            "integrity": "sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==",
 | 
					            "integrity": "sha512-AKjF7qbbLvLaPieoKeTjG1+FyNZT6KaJMJPFeQyLfIp7l82ggH1fbHJSsYIvnbTFQOlkh+gBYpyby5GT1LIdLw==",
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@alloc/quick-lru": "^5.2.0",
 | 
					                "@alloc/quick-lru": "^5.2.0",
 | 
				
			||||||
                "arg": "^5.0.2",
 | 
					                "arg": "^5.0.2",
 | 
				
			||||||
@@ -1886,6 +2029,12 @@
 | 
				
			|||||||
            "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
 | 
				
			||||||
            "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
 | 
					            "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "node_modules/tweetnacl": {
 | 
				
			||||||
 | 
					            "version": "1.0.3",
 | 
				
			||||||
 | 
					            "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
 | 
				
			||||||
 | 
					            "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
 | 
				
			||||||
 | 
					            "dev": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "node_modules/update-browserslist-db": {
 | 
					        "node_modules/update-browserslist-db": {
 | 
				
			||||||
            "version": "1.0.13",
 | 
					            "version": "1.0.13",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
 | 
				
			||||||
@@ -1922,9 +2071,9 @@
 | 
				
			|||||||
            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
 | 
					            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/vite": {
 | 
					        "node_modules/vite": {
 | 
				
			||||||
            "version": "4.5.0",
 | 
					            "version": "4.5.1",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.1.tgz",
 | 
				
			||||||
            "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==",
 | 
					            "integrity": "sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "esbuild": "^0.18.10",
 | 
					                "esbuild": "^0.18.10",
 | 
				
			||||||
@@ -1987,16 +2136,16 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/vue": {
 | 
					        "node_modules/vue": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
 | 
					            "integrity": "sha512-zg6SIXZdTBwiqCw/1p+m04VyHjLfwtjwz8N57sPaBhEex31ND0RYECVOC1YrRwMRmxFf5T1dabl6SGUbMKKuVw==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@vue/compiler-dom": "3.3.8",
 | 
					                "@vue/compiler-dom": "3.3.10",
 | 
				
			||||||
                "@vue/compiler-sfc": "3.3.8",
 | 
					                "@vue/compiler-sfc": "3.3.10",
 | 
				
			||||||
                "@vue/runtime-dom": "3.3.8",
 | 
					                "@vue/runtime-dom": "3.3.10",
 | 
				
			||||||
                "@vue/server-renderer": "3.3.8",
 | 
					                "@vue/server-renderer": "3.3.10",
 | 
				
			||||||
                "@vue/shared": "3.3.8"
 | 
					                "@vue/shared": "3.3.10"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "peerDependencies": {
 | 
					            "peerDependencies": {
 | 
				
			||||||
                "typescript": "*"
 | 
					                "typescript": "*"
 | 
				
			||||||
@@ -2008,9 +2157,9 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/vue/node_modules/@vue/shared": {
 | 
					        "node_modules/vue/node_modules/@vue/shared": {
 | 
				
			||||||
            "version": "3.3.8",
 | 
					            "version": "3.3.10",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
				
			||||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
					            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
				
			||||||
            "dev": true
 | 
					            "dev": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/wrappy": {
 | 
					        "node_modules/wrappy": {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								package.json
									
									
									
									
									
								
							@@ -6,19 +6,23 @@
 | 
				
			|||||||
        "build": "vite build"
 | 
					        "build": "vite build"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
        "@vitejs/plugin-vue": "4.5.0",
 | 
					        "@vitejs/plugin-vue": "4.5.1",
 | 
				
			||||||
        "autoprefixer": "10.4.16",
 | 
					        "autoprefixer": "10.4.16",
 | 
				
			||||||
        "axios": "1.6.2",
 | 
					        "axios": "1.6.2",
 | 
				
			||||||
 | 
					        "laravel-echo": "1.15.3",
 | 
				
			||||||
        "laravel-vite-plugin": "0.8.1",
 | 
					        "laravel-vite-plugin": "0.8.1",
 | 
				
			||||||
        "postcss": "8.4.31",
 | 
					        "postcss": "8.4.32",
 | 
				
			||||||
        "tailwindcss": "3.3.5",
 | 
					        "pusher-js": "8.4.0-rc2",
 | 
				
			||||||
        "vite": "4.5.0",
 | 
					        "tailwindcss": "3.3.6",
 | 
				
			||||||
        "vue": "3.3.8"
 | 
					        "vite": "4.5.1",
 | 
				
			||||||
 | 
					        "vue": "3.3.10"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "dependencies": {
 | 
					    "dependencies": {
 | 
				
			||||||
 | 
					        "@alpinejs/focus": "^3.13.3",
 | 
				
			||||||
        "@tailwindcss/typography": "0.5.10",
 | 
					        "@tailwindcss/typography": "0.5.10",
 | 
				
			||||||
        "alpinejs": "3.13.2",
 | 
					        "alpinejs": "3.13.3",
 | 
				
			||||||
        "daisyui": "4.3.1",
 | 
					        "daisyui": "4.4.19",
 | 
				
			||||||
 | 
					        "ioredis": "5.3.2",
 | 
				
			||||||
        "tailwindcss-scrollbar": "0.1.0"
 | 
					        "tailwindcss-scrollbar": "0.1.0"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								public/js/echo.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								public/js/echo.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										10
									
								
								public/js/pusher.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								public/js/pusher.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								public/vendor/horizon/app.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/vendor/horizon/app.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								public/vendor/horizon/mix-manifest.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/vendor/horizon/mix-manifest.json
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,5 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "/app.js": "/app.js?id=ff1533ec4a7afad65c5bd7bcc2cc7d7b",
 | 
					    "/app.js": "/app.js?id=79bae40dcb18de9ca1b5d0008c577471",
 | 
				
			||||||
    "/app-dark.css": "/app-dark.css?id=15c72df05e2b1147fa3e4b0670cfb435",
 | 
					    "/app-dark.css": "/app-dark.css?id=15c72df05e2b1147fa3e4b0670cfb435",
 | 
				
			||||||
    "/app.css": "/app.css?id=4d6a1a7fe095eedc2cb2a4ce822ea8a5",
 | 
					    "/app.css": "/app.css?id=4d6a1a7fe095eedc2cb2a4ce822ea8a5",
 | 
				
			||||||
    "/img/favicon.png": "/img/favicon.png?id=1542bfe8a0010dcbee710da13cce367f",
 | 
					    "/img/favicon.png": "/img/favicon.png?id=1542bfe8a0010dcbee710da13cce367f",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,9 @@ button[isError] {
 | 
				
			|||||||
.main {
 | 
					.main {
 | 
				
			||||||
    @apply pt-4 pl-24 pr-10 mx-auto;
 | 
					    @apply pt-4 pl-24 pr-10 mx-auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.custom-modal {
 | 
				
			||||||
 | 
					    @apply flex flex-col gap-2 px-8 py-4 border bg-base-100 border-coolgray-200;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
.label-text,
 | 
					.label-text,
 | 
				
			||||||
label {
 | 
					label {
 | 
				
			||||||
    @apply text-neutral-400;
 | 
					    @apply text-neutral-400;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,11 @@
 | 
				
			|||||||
import Alpine from "alpinejs";
 | 
					import Alpine from "alpinejs";
 | 
				
			||||||
 | 
					import focus from '@alpinejs/focus';
 | 
				
			||||||
import { createApp } from "vue";
 | 
					import { createApp } from "vue";
 | 
				
			||||||
import MagicBar from "./components/MagicBar.vue";
 | 
					import MagicBar from "./components/MagicBar.vue";
 | 
				
			||||||
import Toaster from "../../vendor/masmerise/livewire-toaster/resources/js";
 | 
					import Toaster from "../../vendor/masmerise/livewire-toaster/resources/js";
 | 
				
			||||||
 | 
					import "../../vendor/wire-elements/modal/resources/js/modal";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Alpine.plugin(focus);
 | 
				
			||||||
Alpine.plugin(Toaster);
 | 
					Alpine.plugin(Toaster);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.Alpine = Alpine;
 | 
					window.Alpine = Alpine;
 | 
				
			||||||
@@ -12,3 +14,6 @@ Alpine.start();
 | 
				
			|||||||
const app = createApp({});
 | 
					const app = createApp({});
 | 
				
			||||||
app.component("magic-bar", MagicBar);
 | 
					app.component("magic-bar", MagicBar);
 | 
				
			||||||
app.mount("#vue");
 | 
					app.mount("#vue");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,14 +3,18 @@
 | 
				
			|||||||
        href="{{ route('project.application.configuration', $parameters) }}">
 | 
					        href="{{ route('project.application.configuration', $parameters) }}">
 | 
				
			||||||
        <button>Configuration</button>
 | 
					        <button>Configuration</button>
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
    <a class="{{ request()->routeIs('project.application.deployments') ? 'text-white' : '' }}"
 | 
					    <a class="{{ request()->routeIs('project.application.command') ? 'text-white' : '' }}"
 | 
				
			||||||
        href="{{ route('project.application.deployments', $parameters) }}">
 | 
					        href="{{ route('project.application.command', $parameters) }}">
 | 
				
			||||||
        <button>Deployments</button>
 | 
					        <button>Execute Command</button>
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
    <a class="{{ request()->routeIs('project.application.logs') ? 'text-white' : '' }}"
 | 
					    <a class="{{ request()->routeIs('project.application.logs') ? 'text-white' : '' }}"
 | 
				
			||||||
        href="{{ route('project.application.logs', $parameters) }}">
 | 
					        href="{{ route('project.application.logs', $parameters) }}">
 | 
				
			||||||
        <button>Logs</button>
 | 
					        <button>Logs</button>
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
 | 
					    <a class="{{ request()->routeIs('project.application.deployments') ? 'text-white' : '' }}"
 | 
				
			||||||
 | 
					        href="{{ route('project.application.deployments', $parameters) }}">
 | 
				
			||||||
 | 
					        <button>Deployments</button>
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
    <x-applications.links :application="$application" />
 | 
					    <x-applications.links :application="$application" />
 | 
				
			||||||
    <div class="flex-1"></div>
 | 
					    <div class="flex-1"></div>
 | 
				
			||||||
    @if ($application->build_pack === 'dockercompose' && is_null($application->docker_compose_raw))
 | 
					    @if ($application->build_pack === 'dockercompose' && is_null($application->docker_compose_raw))
 | 
				
			||||||
@@ -45,6 +49,19 @@
 | 
				
			|||||||
                    </svg>
 | 
					                    </svg>
 | 
				
			||||||
                    Restart
 | 
					                    Restart
 | 
				
			||||||
                </button>
 | 
					                </button>
 | 
				
			||||||
 | 
					                @if (isDev())
 | 
				
			||||||
 | 
					                    <button title="Restart without rebuilding" wire:click='restartNew'
 | 
				
			||||||
 | 
					                        class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
 | 
				
			||||||
 | 
					                        <svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					                            <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
 | 
				
			||||||
 | 
					                                stroke-width="2">
 | 
				
			||||||
 | 
					                                <path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
 | 
				
			||||||
 | 
					                                <path d="M20 4v5h-5" />
 | 
				
			||||||
 | 
					                            </g>
 | 
				
			||||||
 | 
					                        </svg>
 | 
				
			||||||
 | 
					                        Restart (new)
 | 
				
			||||||
 | 
					                    </button>
 | 
				
			||||||
 | 
					                @endif
 | 
				
			||||||
            @endif
 | 
					            @endif
 | 
				
			||||||
            <button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
 | 
					            <button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
 | 
				
			||||||
                <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
 | 
					                <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
 | 
				
			||||||
@@ -66,6 +83,18 @@
 | 
				
			|||||||
                </svg>
 | 
					                </svg>
 | 
				
			||||||
                Deploy
 | 
					                Deploy
 | 
				
			||||||
            </button>
 | 
					            </button>
 | 
				
			||||||
 | 
					            @if (isDev())
 | 
				
			||||||
 | 
					                <button wire:click='deployNew'
 | 
				
			||||||
 | 
					                    class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
 | 
				
			||||||
 | 
					                    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24"
 | 
				
			||||||
 | 
					                        stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
 | 
				
			||||||
 | 
					                        stroke-linejoin="round">
 | 
				
			||||||
 | 
					                        <path stroke="none" d="M0 0h24v24H0z" fill="none" />
 | 
				
			||||||
 | 
					                        <path d="M7 4v16l13 -8z" />
 | 
				
			||||||
 | 
					                    </svg>
 | 
				
			||||||
 | 
					                    Deploy (new)
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					            @endif
 | 
				
			||||||
        @endif
 | 
					        @endif
 | 
				
			||||||
    @endif
 | 
					    @endif
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,10 @@
 | 
				
			|||||||
        href="{{ route('project.database.configuration', $parameters) }}">
 | 
					        href="{{ route('project.database.configuration', $parameters) }}">
 | 
				
			||||||
        <button>Configuration</button>
 | 
					        <button>Configuration</button>
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
 | 
					    <a class="{{ request()->routeIs('project.database.command') ? 'text-white' : '' }}"
 | 
				
			||||||
 | 
					        href="{{ route('project.database.command', $parameters) }}">
 | 
				
			||||||
 | 
					        <button>Execute Command</button>
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
    <a class="{{ request()->routeIs('project.database.logs') ? 'text-white' : '' }}"
 | 
					    <a class="{{ request()->routeIs('project.database.logs') ? 'text-white' : '' }}"
 | 
				
			||||||
        href="{{ route('project.database.logs', $parameters) }}">
 | 
					        href="{{ route('project.database.logs', $parameters) }}">
 | 
				
			||||||
        <button>Logs</button>
 | 
					        <button>Logs</button>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,8 @@
 | 
				
			|||||||
        </button>
 | 
					        </button>
 | 
				
			||||||
    @endif
 | 
					    @endif
 | 
				
			||||||
    @if (serviceStatus($service) === 'exited')
 | 
					    @if (serviceStatus($service) === 'exited')
 | 
				
			||||||
        <button wire:click='stop(true)' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
 | 
					        <button wire:click='stop(true)'
 | 
				
			||||||
 | 
					            class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
 | 
				
			||||||
            <svg class="w-5 h-5 " viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
 | 
					            <svg class="w-5 h-5 " viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
                <path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
 | 
					                <path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
 | 
				
			||||||
                <path fill="red"
 | 
					                <path fill="red"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
@props([
 | 
					@props([
 | 
				
			||||||
    'status' => 'Degraded',
 | 
					    'status' => 'Degraded',
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
<x-loading wire:loading.delay />
 | 
					<x-loading wire:loading.delay.longer />
 | 
				
			||||||
<div class="flex items-center gap-2" wire:loading.remove.delay.longer>
 | 
					<div class="flex items-center gap-2" wire:loading.remove.delay.longer>
 | 
				
			||||||
    <div class="badge badge-warning badge-xs"></div>
 | 
					    <div class="badge badge-warning badge-xs"></div>
 | 
				
			||||||
    <div class="text-xs font-medium tracking-wide text-warning">{{ Str::headline($status) }}</div>
 | 
					    <div class="text-xs font-medium tracking-wide text-warning">{{ Str::headline($status) }}</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
@props([
 | 
					@props([
 | 
				
			||||||
    'status' => 'Restarting',
 | 
					    'status' => 'Restarting',
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
<x-loading wire:loading.delay />
 | 
					<x-loading wire:loading.delay.longer />
 | 
				
			||||||
<div class="flex items-center gap-2" wire:loading.remove.delay.longer>
 | 
					<div class="flex items-center gap-2" wire:loading.remove.delay.longer>
 | 
				
			||||||
    <div class="badge badge-warning badge-xs"></div>
 | 
					    <div class="badge badge-warning badge-xs"></div>
 | 
				
			||||||
    <div class="text-xs font-medium tracking-wide text-warning">{{ Str::headline($status) }}</div>
 | 
					    <div class="text-xs font-medium tracking-wide text-warning">{{ Str::headline($status) }}</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
@section('body')
 | 
					@section('body')
 | 
				
			||||||
    @parent
 | 
					    @parent
 | 
				
			||||||
    <x-navbar />
 | 
					    <x-navbar />
 | 
				
			||||||
    <div class="fixed z-50 top-[4.5rem] left-4" id="vue">
 | 
					    <div class="fixed z-30 top-[4.5rem] left-4" id="vue">
 | 
				
			||||||
        <magic-bar></magic-bar>
 | 
					        <magic-bar></magic-bar>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <livewire:sponsorship />
 | 
					    <livewire:sponsorship />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,11 +24,20 @@
 | 
				
			|||||||
    @if (config('app.name') == 'Coolify Cloud')
 | 
					    @if (config('app.name') == 'Coolify Cloud')
 | 
				
			||||||
        <script defer data-domain="app.coolify.io" src="https://analytics.coollabs.io/js/plausible.js"></script>
 | 
					        <script defer data-domain="app.coolify.io" src="https://analytics.coollabs.io/js/plausible.js"></script>
 | 
				
			||||||
    @endif
 | 
					    @endif
 | 
				
			||||||
 | 
					    @auth
 | 
				
			||||||
 | 
					        <script src="https://cdnjs.cloudflare.com/ajax/libs/laravel-echo/1.15.3/echo.iife.min.js"
 | 
				
			||||||
 | 
					            integrity="sha512-aPAh2oRUr3ALz2MwVWkd6lmdgBQC0wSr0R++zclNjXZreT/JrwDPZQwA/p6R3wOCTcXKIHgA9pQGEQBWQmdLaA=="
 | 
				
			||||||
 | 
					            crossorigin="anonymous" referrerpolicy="no-referrer"></script>
 | 
				
			||||||
 | 
					        <script src="https://cdnjs.cloudflare.com/ajax/libs/pusher/8.3.0/pusher.min.js"
 | 
				
			||||||
 | 
					            integrity="sha512-tXL5mrkSoP49uQf2jO0LbvzMyFgki//znmq0wYXGq94gVF6TU0QlrSbwGuPpKTeN1mIjReeqKZ4/NJPjHN1d2Q=="
 | 
				
			||||||
 | 
					            crossorigin="anonymous" referrerpolicy="no-referrer"></script>
 | 
				
			||||||
 | 
					    @endauth
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
@section('body')
 | 
					@section('body')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <body>
 | 
					    <body>
 | 
				
			||||||
        @livewireScripts
 | 
					        @livewireScripts
 | 
				
			||||||
 | 
					        @livewire('livewire-ui-modal')
 | 
				
			||||||
        <dialog id="help" class="modal">
 | 
					        <dialog id="help" class="modal">
 | 
				
			||||||
            <livewire:help />
 | 
					            <livewire:help />
 | 
				
			||||||
            <form method="dialog" class="modal-backdrop">
 | 
					            <form method="dialog" class="modal-backdrop">
 | 
				
			||||||
@@ -37,7 +46,47 @@
 | 
				
			|||||||
        </dialog>
 | 
					        </dialog>
 | 
				
			||||||
        <x-toaster-hub />
 | 
					        <x-toaster-hub />
 | 
				
			||||||
        <x-version class="fixed left-2 bottom-1" />
 | 
					        <x-version class="fixed left-2 bottom-1" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <script>
 | 
					        <script>
 | 
				
			||||||
 | 
					            @auth
 | 
				
			||||||
 | 
					            window.Pusher = Pusher;
 | 
				
			||||||
 | 
					            window.Echo = new Echo({
 | 
				
			||||||
 | 
					                broadcaster: 'pusher',
 | 
				
			||||||
 | 
					                cluster: "{{ env('PUSHER_HOST') }}" || window.location.hostname,
 | 
				
			||||||
 | 
					                key: "{{ env('PUSHER_APP_KEY') }}" || 'coolify',
 | 
				
			||||||
 | 
					                wsHost: "{{ env('PUSHER_HOST') }}" || window.location.hostname,
 | 
				
			||||||
 | 
					                wsPort: "{{ env('PUSHER_PORT') }}" || 6001,
 | 
				
			||||||
 | 
					                wssPort: "{{ env('PUSHER_PORT') }}" || 6001,
 | 
				
			||||||
 | 
					                forceTLS: false,
 | 
				
			||||||
 | 
					                encrypted: true,
 | 
				
			||||||
 | 
					                enableStats: false,
 | 
				
			||||||
 | 
					                enableLogging: true,
 | 
				
			||||||
 | 
					                enabledTransports: ['ws', 'wss'],
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ("{{ auth()->user()->id }}" == 0) {
 | 
				
			||||||
 | 
					                let checkPusherInterval = null;
 | 
				
			||||||
 | 
					                let checkNumber = 0;
 | 
				
			||||||
 | 
					                let errorMessage = "Coolify could not connect to the new realtime service introduced in beta.154.<br>Please check the related <a href='https://coolify.io/docs/cloudflare-tunnels' target='_blank'>documentation</a> or get help on <a href='https://coollabs.io/discord' target='_blank'>Discord</a>.";
 | 
				
			||||||
 | 
					                checkPusherInterval = setInterval(() => {
 | 
				
			||||||
 | 
					                    if (window.Echo) {
 | 
				
			||||||
 | 
					                        if (window.Echo.connector.pusher.connection.state !== 'connected') {
 | 
				
			||||||
 | 
					                            checkNumber++;
 | 
				
			||||||
 | 
					                            if (checkNumber > 5) {
 | 
				
			||||||
 | 
					                                clearInterval(checkPusherInterval);
 | 
				
			||||||
 | 
					                                Livewire.emit('error', errorMessage);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                           console.log('Coolify is now connected to the new realtime service introduced in beta.154.');
 | 
				
			||||||
 | 
					                            clearInterval(checkPusherInterval);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        clearInterval(checkPusherInterval);
 | 
				
			||||||
 | 
					                        Livewire.emit('error', errorMessage);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }, 2000);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            @endauth
 | 
				
			||||||
            let checkHealthInterval = null;
 | 
					            let checkHealthInterval = null;
 | 
				
			||||||
            let checkIfIamDeadInterval = null;
 | 
					            let checkIfIamDeadInterval = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										32
									
								
								resources/views/livewire/modal/edit-compose.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								resources/views/livewire/modal/edit-compose.blade.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					<form x-data="{ raw: true }" class="custom-modal" wire:submit.prevent='submit'>
 | 
				
			||||||
 | 
					    <div class="flex items-end gap-2">
 | 
				
			||||||
 | 
					        <h1>Docker Compose</h1>
 | 
				
			||||||
 | 
					        <div x-cloak x-show="raw">
 | 
				
			||||||
 | 
					            <x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable Compose</x-forms.button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div x-cloak x-show="raw === false">
 | 
				
			||||||
 | 
					            <x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
 | 
				
			||||||
 | 
					                Compose</x-forms.button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div>Volume names are updated upon save. The service UUID will be added as a prefix to all volumes, to prevent
 | 
				
			||||||
 | 
					        name collision. <br>To see the actual volume names, check the Deployable Compose file, or go to Storage
 | 
				
			||||||
 | 
					        menu.</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div x-cloak x-show="raw" class="font-mono">
 | 
				
			||||||
 | 
					        <x-forms.textarea rows="20" id="service.docker_compose_raw">
 | 
				
			||||||
 | 
					        </x-forms.textarea>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div x-cloak x-show="raw === false" class="font-mono">
 | 
				
			||||||
 | 
					        <x-forms.textarea rows="20" readonly id="service.docker_compose">
 | 
				
			||||||
 | 
					        </x-forms.textarea>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="flex justify-end w-full gap-2">
 | 
				
			||||||
 | 
					        <x-forms.button class="w-64" type="submit">
 | 
				
			||||||
 | 
					            Save
 | 
				
			||||||
 | 
					        </x-forms.button>
 | 
				
			||||||
 | 
					        <x-forms.button wire:click='closeModal'>
 | 
				
			||||||
 | 
					            Close
 | 
				
			||||||
 | 
					        </x-forms.button>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
@@ -1,30 +0,0 @@
 | 
				
			|||||||
<dialog id="composeModal" class="modal" x-data="{ raw: true }" wire:ignore.self>
 | 
					 | 
				
			||||||
    <form method="dialog" class="flex flex-col gap-2 rounded max-w-7xl modal-box" wire:submit.prevent='submit'>
 | 
					 | 
				
			||||||
        <div class="flex items-end gap-2">
 | 
					 | 
				
			||||||
        <h1>Docker Compose</h1>
 | 
					 | 
				
			||||||
        <div x-cloak x-show="raw">
 | 
					 | 
				
			||||||
            <x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable Compose</x-forms.button>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div x-cloak x-show="raw === false">
 | 
					 | 
				
			||||||
            <x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
 | 
					 | 
				
			||||||
                Compose</x-forms.button>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
        <div>Volume names are updated upon save. The service UUID will be added as a prefix to all volumes, to prevent name collision. <br>To see the actual volume names, check the Deployable Compose file, or go to Storage menu.</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <div x-cloak x-show="raw">
 | 
					 | 
				
			||||||
            <x-forms.textarea rows="20" id="raw">
 | 
					 | 
				
			||||||
            </x-forms.textarea>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div x-cloak x-show="raw === false">
 | 
					 | 
				
			||||||
            <x-forms.textarea rows="20" readonly id="actual">
 | 
					 | 
				
			||||||
            </x-forms.textarea>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <x-forms.button onclick="composeModal.close()" type="submit">
 | 
					 | 
				
			||||||
            Save
 | 
					 | 
				
			||||||
        </x-forms.button>
 | 
					 | 
				
			||||||
    </form>
 | 
					 | 
				
			||||||
    <form method="dialog" class="modal-backdrop">
 | 
					 | 
				
			||||||
        <button>close</button>
 | 
					 | 
				
			||||||
    </form>
 | 
					 | 
				
			||||||
</dialog>
 | 
					 | 
				
			||||||
@@ -1,6 +1,5 @@
 | 
				
			|||||||
<div x-data="{ raw: true, activeTab: window.location.hash ? window.location.hash.substring(1) : 'service-stack' }" x-init="$wire.checkStatus" wire:poll.10000ms="checkStatus">
 | 
					<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'service-stack' }" x-init="$wire.checkStatus" wire:poll.10000ms="checkStatus">
 | 
				
			||||||
    <livewire:project.service.navbar :service="$service" :parameters="$parameters" :query="$query" />
 | 
					    <livewire:project.service.navbar :service="$service" :parameters="$parameters" :query="$query" />
 | 
				
			||||||
    <livewire:project.service.compose-modal :raw="$service->docker_compose_raw" :actual="$service->docker_compose" />
 | 
					 | 
				
			||||||
    <div class="flex h-full pt-6">
 | 
					    <div class="flex h-full pt-6">
 | 
				
			||||||
        <div class="flex flex-col items-start gap-4 min-w-fit">
 | 
					        <div class="flex flex-col items-start gap-4 min-w-fit">
 | 
				
			||||||
            <a target="_blank" href="{{ $service->documentation() }}">Documentation <x-external-link /></a>
 | 
					            <a target="_blank" href="{{ $service->documentation() }}">Documentation <x-external-link /></a>
 | 
				
			||||||
@@ -8,6 +7,10 @@
 | 
				
			|||||||
                @click.prevent="activeTab = 'service-stack';
 | 
					                @click.prevent="activeTab = 'service-stack';
 | 
				
			||||||
                window.location.hash = 'service-stack'"
 | 
					                window.location.hash = 'service-stack'"
 | 
				
			||||||
                href="#">Service Stack</a>
 | 
					                href="#">Service Stack</a>
 | 
				
			||||||
 | 
					            <a :class="activeTab === 'execute-command' && 'text-white'"
 | 
				
			||||||
 | 
					                @click.prevent="activeTab = 'execute-command';
 | 
				
			||||||
 | 
					                window.location.hash = 'execute-command'"
 | 
				
			||||||
 | 
					                href="#">Execute Command</a>
 | 
				
			||||||
            <a :class="activeTab === 'storages' && 'text-white'"
 | 
					            <a :class="activeTab === 'storages' && 'text-white'"
 | 
				
			||||||
                @click.prevent="activeTab = 'storages';
 | 
					                @click.prevent="activeTab = 'storages';
 | 
				
			||||||
                window.location.hash = 'storages'"
 | 
					                window.location.hash = 'storages'"
 | 
				
			||||||
@@ -113,6 +116,9 @@
 | 
				
			|||||||
            <div x-cloak x-show="activeTab === 'webhooks'">
 | 
					            <div x-cloak x-show="activeTab === 'webhooks'">
 | 
				
			||||||
                <livewire:project.shared.webhooks :resource="$service" />
 | 
					                <livewire:project.shared.webhooks :resource="$service" />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div x-cloak x-show="activeTab === 'execute-command'">
 | 
				
			||||||
 | 
					                <livewire:project.shared.execute-container-command :resource="$service" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
            <div x-cloak x-show="activeTab === 'environment-variables'">
 | 
					            <div x-cloak x-show="activeTab === 'environment-variables'">
 | 
				
			||||||
                <div x-cloak x-show="activeTab === 'environment-variables'">
 | 
					                <div x-cloak x-show="activeTab === 'environment-variables'">
 | 
				
			||||||
                    <livewire:project.shared.environment-variable.all :resource="$service" />
 | 
					                    <livewire:project.shared.environment-variable.all :resource="$service" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,9 @@
 | 
				
			|||||||
            <div>Configuration</div>
 | 
					            <div>Configuration</div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <x-forms.button type="submit">Save</x-forms.button>
 | 
					        <x-forms.button type="submit">Save</x-forms.button>
 | 
				
			||||||
        <x-forms.button class="w-64" onclick="composeModal.showModal()">Edit Compose
 | 
					        <x-forms.button class="w-64"
 | 
				
			||||||
 | 
					            onclick="Livewire.emit('openModal', 'modals.edit-compose',{{ json_encode(['serviceId' => $service->id]) }})">Edit
 | 
				
			||||||
 | 
					            Compose
 | 
				
			||||||
            File</x-forms.button>
 | 
					            File</x-forms.button>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="flex gap-2">
 | 
					    <div class="flex gap-2">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,8 @@
 | 
				
			|||||||
            server <span class="px-1 text-warning">{{ data_get($resource, 'destination.server.name') }}</span>
 | 
					            server <span class="px-1 text-warning">{{ data_get($resource, 'destination.server.name') }}</span>
 | 
				
			||||||
            in <span class="px-1 text-warning"> {{ data_get($resource, 'destination.network') }} </span> network.</a>
 | 
					            in <span class="px-1 text-warning"> {{ data_get($resource, 'destination.network') }} </span> network.</a>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    {{-- {{$resource->additional_destinations}} --}}
 | 
					    {{-- Additonal Destinations:
 | 
				
			||||||
 | 
					    {{$resource->additional_destinations}} --}}
 | 
				
			||||||
    {{-- @if (count($servers) > 0)
 | 
					    {{-- @if (count($servers) > 0)
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
            <h3>Additional Servers</h3>
 | 
					            <h3>Additional Servers</h3>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@
 | 
				
			|||||||
                    class="font-bold text-warning">({{ $env->key }})</span>?</p>
 | 
					                    class="font-bold text-warning">({{ $env->key }})</span>?</p>
 | 
				
			||||||
        </x-slot:modalBody>
 | 
					        </x-slot:modalBody>
 | 
				
			||||||
    </x-modal>
 | 
					    </x-modal>
 | 
				
			||||||
    <form wire:submit.prevent='submit' class="flex flex-col items-center gap-2 xl:flex-row">
 | 
					    <form wire:submit.prevent='submit' class="flex flex-col gap-2 p-4 m-2 border lg:items-center border-coolgray-300 lg:m-0 lg:p-0 lg:border-0 lg:flex-row">
 | 
				
			||||||
        @if ($isLocked)
 | 
					        @if ($isLocked)
 | 
				
			||||||
            <svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
 | 
					            <svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
                <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
 | 
					                <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					<div>
 | 
				
			||||||
 | 
					    @if ($type === 'application')
 | 
				
			||||||
 | 
					        <h1>Execute Command</h1>
 | 
				
			||||||
 | 
					        <livewire:project.application.heading :application="$resource" />
 | 
				
			||||||
 | 
					    @elseif ($type === 'database')
 | 
				
			||||||
 | 
					        <h1>Execute Command</h1>
 | 
				
			||||||
 | 
					        <livewire:project.database.heading :database="$resource" />
 | 
				
			||||||
 | 
					    @elseif ($type === 'service')
 | 
				
			||||||
 | 
					        <h2>Execute Command</h2>
 | 
				
			||||||
 | 
					    @endif
 | 
				
			||||||
 | 
					    @if (count($containers) > 0)
 | 
				
			||||||
 | 
					        <form class="flex flex-col gap-2 pt-4" wire:submit.prevent='runCommand'>
 | 
				
			||||||
 | 
					            <div class="flex gap-2">
 | 
				
			||||||
 | 
					                <x-forms.input placeholder="ls -l" autofocus id="command" label="Command" required />
 | 
				
			||||||
 | 
					                <x-forms.input id="workDir" label="Working directory" />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <x-forms.select label="Container" id="container" required>
 | 
				
			||||||
 | 
					                <option disabled selected>Select container</option>
 | 
				
			||||||
 | 
					                @foreach ($containers as $container)
 | 
				
			||||||
 | 
					                    <option value="{{ $container }}">{{ $container }}</option>
 | 
				
			||||||
 | 
					                @endforeach
 | 
				
			||||||
 | 
					            </x-forms.select>
 | 
				
			||||||
 | 
					            <x-forms.button type="submit">Run</x-forms.button>
 | 
				
			||||||
 | 
					        </form>
 | 
				
			||||||
 | 
					    @else
 | 
				
			||||||
 | 
					        <div class="pt-4">No containers are not running.</div>
 | 
				
			||||||
 | 
					    @endif
 | 
				
			||||||
 | 
					    <div class="container w-full pt-10 mx-auto">
 | 
				
			||||||
 | 
					        <livewire:activity-monitor header="Command output" />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
							
								
								
									
										57
									
								
								resources/views/vendor/livewire-ui-modal/modal.blade.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								resources/views/vendor/livewire-ui-modal/modal.blade.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					<div>
 | 
				
			||||||
 | 
					    @isset($jsPath)
 | 
				
			||||||
 | 
					        <script>{!! file_get_contents($jsPath) !!}</script>
 | 
				
			||||||
 | 
					    @endisset
 | 
				
			||||||
 | 
					    @isset($cssPath)
 | 
				
			||||||
 | 
					        <style>{!! file_get_contents($cssPath) !!}</style>
 | 
				
			||||||
 | 
					    @endisset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					            x-data="LivewireUIModal()"
 | 
				
			||||||
 | 
					            x-init="init()"
 | 
				
			||||||
 | 
					            x-on:close.stop="setShowPropertyTo(false)"
 | 
				
			||||||
 | 
					            x-on:keydown.escape.window="closeModalOnEscape()"
 | 
				
			||||||
 | 
					            x-show="show"
 | 
				
			||||||
 | 
					            class="fixed inset-0 z-40 overflow-y-auto"
 | 
				
			||||||
 | 
					            style="display: none;"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					        <div class="flex items-end justify-center px-4 pt-4 pb-10 text-center sm:block sm:p-0">
 | 
				
			||||||
 | 
					            <div
 | 
				
			||||||
 | 
					                    x-show="show"
 | 
				
			||||||
 | 
					                    x-on:click="closeModalOnClickAway()"
 | 
				
			||||||
 | 
					                    x-transition:enter="ease-out duration-300"
 | 
				
			||||||
 | 
					                    x-transition:enter-start="opacity-0"
 | 
				
			||||||
 | 
					                    x-transition:enter-end="opacity-100"
 | 
				
			||||||
 | 
					                    x-transition:leave="ease-in duration-200"
 | 
				
			||||||
 | 
					                    x-transition:leave-start="opacity-100"
 | 
				
			||||||
 | 
					                    x-transition:leave-end="opacity-0"
 | 
				
			||||||
 | 
					                    class="fixed inset-0 transition-all transform"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <div class="absolute inset-0 bg-black opacity-70"></div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {{-- <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> --}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <div
 | 
				
			||||||
 | 
					                    x-show="show && showActiveComponent"
 | 
				
			||||||
 | 
					                    x-transition:enter="ease-out duration-200"
 | 
				
			||||||
 | 
					                    x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
 | 
				
			||||||
 | 
					                    x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
 | 
				
			||||||
 | 
					                    x-transition:leave="ease-in duration-200"
 | 
				
			||||||
 | 
					                    x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
 | 
				
			||||||
 | 
					                    x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
 | 
				
			||||||
 | 
					                    class="w-full overflow-hidden text-left align-bottom transition-all transform sm:my-8 sm:align-middle sm:w-full"
 | 
				
			||||||
 | 
					                    id="modal-container"
 | 
				
			||||||
 | 
					                    x-trap.noscroll.inert="show && showActiveComponent"
 | 
				
			||||||
 | 
					                    aria-modal="true"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                @forelse($components as $id => $component)
 | 
				
			||||||
 | 
					                    <div class="sm:mx-20" x-show.immediate="activeComponent == '{{ $id }}'" x-ref="{{ $id }}" wire:key="{{ $id }}">
 | 
				
			||||||
 | 
					                        @livewire($component['name'], $component['attributes'], key($id))
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                @empty
 | 
				
			||||||
 | 
					                @endforelse
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -11,6 +11,13 @@
 | 
				
			|||||||
|
 | 
					|
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
 | 
					use App\Models\Application;
 | 
				
			||||||
//     return (int) $user->id === (int) $id;
 | 
					use App\Models\User;
 | 
				
			||||||
// });
 | 
					use Illuminate\Support\Facades\Broadcast;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Broadcast::channel('custom.{teamId}', function (User $user, int $teamId) {
 | 
				
			||||||
 | 
					    if ($user->teams->pluck('id')->contains($teamId)) {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ use App\Http\Livewire\Project\Service\Show as ServiceShow;
 | 
				
			|||||||
use App\Http\Livewire\Dev\Compose as Compose;
 | 
					use App\Http\Livewire\Dev\Compose as Compose;
 | 
				
			||||||
use App\Http\Livewire\Dashboard;
 | 
					use App\Http\Livewire\Dashboard;
 | 
				
			||||||
use App\Http\Livewire\Project\CloneProject;
 | 
					use App\Http\Livewire\Project\CloneProject;
 | 
				
			||||||
 | 
					use App\Http\Livewire\Project\Shared\ExecuteContainerCommand;
 | 
				
			||||||
use App\Http\Livewire\Project\Shared\Logs;
 | 
					use App\Http\Livewire\Project\Shared\Logs;
 | 
				
			||||||
use App\Http\Livewire\Security\ApiTokens;
 | 
					use App\Http\Livewire\Security\ApiTokens;
 | 
				
			||||||
use App\Http\Livewire\Server\All;
 | 
					use App\Http\Livewire\Server\All;
 | 
				
			||||||
@@ -42,6 +43,16 @@ use Laravel\Fortify\Fortify;
 | 
				
			|||||||
if (isDev()) {
 | 
					if (isDev()) {
 | 
				
			||||||
    Route::get('/dev/compose', Compose::class)->name('dev.compose');
 | 
					    Route::get('/dev/compose', Compose::class)->name('dev.compose');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Route::get('/api/v1/test/realtime', function () {
 | 
				
			||||||
 | 
					    if (auth()->user()?->currentTeam()->id !== 0) {
 | 
				
			||||||
 | 
					        return redirect('/');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    event(new \App\Events\TestEvent());
 | 
				
			||||||
 | 
					    return 'Look at your other tab.';
 | 
				
			||||||
 | 
					})->middleware('auth');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Route::post('/forgot-password', function (Request $request) {
 | 
					Route::post('/forgot-password', function (Request $request) {
 | 
				
			||||||
    if (is_transactional_emails_active()) {
 | 
					    if (is_transactional_emails_active()) {
 | 
				
			||||||
        $arrayOfRequest = $request->only(Fortify::email());
 | 
					        $arrayOfRequest = $request->only(Fortify::email());
 | 
				
			||||||
@@ -111,18 +122,21 @@ Route::middleware(['auth', 'verified'])->group(function () {
 | 
				
			|||||||
    )->name('project.application.deployment');
 | 
					    )->name('project.application.deployment');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/logs', Logs::class)->name('project.application.logs');
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/logs', Logs::class)->name('project.application.logs');
 | 
				
			||||||
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/command', ExecuteContainerCommand::class)->name('project.application.command');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Databases
 | 
					    // Databases
 | 
				
			||||||
    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}', [DatabaseController::class, 'configuration'])->name('project.database.configuration');
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}', [DatabaseController::class, 'configuration'])->name('project.database.configuration');
 | 
				
			||||||
    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups', [DatabaseController::class, 'backups'])->name('project.database.backups.all');
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups', [DatabaseController::class, 'backups'])->name('project.database.backups.all');
 | 
				
			||||||
    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups/{backup_uuid}', [DatabaseController::class, 'executions'])->name('project.database.backups.executions');
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups/{backup_uuid}', [DatabaseController::class, 'executions'])->name('project.database.backups.executions');
 | 
				
			||||||
    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/logs', Logs::class)->name('project.database.logs');
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/logs', Logs::class)->name('project.database.logs');
 | 
				
			||||||
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/command', ExecuteContainerCommand::class)->name('project.database.command');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Services
 | 
					    // Services
 | 
				
			||||||
    Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}', ServiceIndex::class)->name('project.service.configuration');
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}', ServiceIndex::class)->name('project.service.configuration');
 | 
				
			||||||
    Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/{service_name}', ServiceShow::class)->name('project.service.show');
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/{service_name}', ServiceShow::class)->name('project.service.show');
 | 
				
			||||||
    Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/{service_name}/logs', Logs::class)->name('project.service.logs');
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/{service_name}/logs', Logs::class)->name('project.service.logs');
 | 
				
			||||||
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/command', ExecuteContainerCommand::class)->name('project.service.command');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Route::middleware(['auth'])->group(function () {
 | 
					Route::middleware(['auth'])->group(function () {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -137,6 +137,9 @@ if [ ! -f /data/coolify/source/.env ]; then
 | 
				
			|||||||
    sed -i "s|APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|g" /data/coolify/source/.env
 | 
					    sed -i "s|APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|g" /data/coolify/source/.env
 | 
				
			||||||
    sed -i "s|DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env
 | 
					    sed -i "s|DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env
 | 
				
			||||||
    sed -i "s|REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env
 | 
					    sed -i "s|REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env
 | 
				
			||||||
 | 
					    sed -i "s|PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|g" /data/coolify/source/.env
 | 
				
			||||||
 | 
					    sed -i "s|PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|g" /data/coolify/source/.env
 | 
				
			||||||
 | 
					    sed -i "s|PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|g" /data/coolify/source/.env
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Merge .env and .env.production. New values will be added to .env
 | 
					# Merge .env and .env.production. New values will be added to .env
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,7 @@
 | 
				
			|||||||
#!/bin/bash
 | 
					#!/bin/bash
 | 
				
			||||||
## Do not modify this file. You will lost the ability to autoupdate!
 | 
					## Do not modify this file. You will lost the ability to autoupdate!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###########
 | 
					VERSION="1.0.4"
 | 
				
			||||||
## Always run "php artisan app:sync-to-bunny-cdn --env=secrets" if you update this file.
 | 
					 | 
				
			||||||
###########
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VERSION="1.0.1"
 | 
					 | 
				
			||||||
CDN="https://cdn.coollabs.io/coolify"
 | 
					CDN="https://cdn.coollabs.io/coolify"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml
 | 
					curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml
 | 
				
			||||||
@@ -13,10 +9,28 @@ curl -fsSL $CDN/docker-compose.prod.yml -o /data/coolify/source/docker-compose.p
 | 
				
			|||||||
curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
 | 
					curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Merge .env and .env.production. New values will be added to .env
 | 
					# Merge .env and .env.production. New values will be added to .env
 | 
				
			||||||
sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' > /data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env
 | 
					sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' >/data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Check if PUSHER_APP_ID or PUSHER_APP_KEY or PUSHER_APP_SECRET is empty in /data/coolify/source/.env
 | 
				
			||||||
 | 
					if grep -q "PUSHER_APP_ID=$" /data/coolify/source/.env; then
 | 
				
			||||||
 | 
					    sed -i "s|PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|g" /data/coolify/source/.env
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if grep -q "PUSHER_APP_KEY=$" /data/coolify/source/.env; then
 | 
				
			||||||
 | 
					    sed -i "s|PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|g" /data/coolify/source/.env
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if grep -q "PUSHER_APP_SECRET=$" /data/coolify/source/.env; then
 | 
				
			||||||
 | 
					    sed -i "s|PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|g" /data/coolify/source/.env
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Make sure coolify network exists
 | 
					# Make sure coolify network exists
 | 
				
			||||||
docker network create --attachable coolify 2>/dev/null
 | 
					docker network create --attachable coolify 2>/dev/null
 | 
				
			||||||
# docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null
 | 
					# docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
docker run --pull always -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --pull always --remove-orphans --force-recreate"
 | 
					if [ -f /data/coolify/source/docker-compose.custom.yml ]; then
 | 
				
			||||||
 | 
					    echo "docker-compose.custom.yml detected."
 | 
				
			||||||
 | 
					    docker run --pull always -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --pull always --remove-orphans --force-recreate"
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					    docker run --pull always -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --pull always --remove-orphans --force-recreate"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
/** @type {import('tailwindcss').Config} */
 | 
					/** @type {import('tailwindcss').Config} */
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
    content: [
 | 
					    content: [
 | 
				
			||||||
 | 
					        './vendor/wire-elements/modal/resources/views/*.blade.php',
 | 
				
			||||||
 | 
					        './storage/framework/views/*.php',
 | 
				
			||||||
        "./resources/**/*.blade.php",
 | 
					        "./resources/**/*.blade.php",
 | 
				
			||||||
        "./app/**/*.php",
 | 
					        "./app/**/*.php",
 | 
				
			||||||
        "./resources/**/*.js",
 | 
					        "./resources/**/*.js",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
            "version": "3.12.36"
 | 
					            "version": "3.12.36"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "v4": {
 | 
					        "v4": {
 | 
				
			||||||
            "version": "4.0.0-beta.153"
 | 
					            "version": "4.0.0-beta.154"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user