@@ -4,3 +4,7 @@ APP_KEY=
 | 
			
		||||
 | 
			
		||||
DB_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 $servers = [];
 | 
			
		||||
 | 
			
		||||
    public function mount()
 | 
			
		||||
    {
 | 
			
		||||
        $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');
 | 
			
		||||
        }
 | 
			
		||||
        $this->application = $application;
 | 
			
		||||
        $mainServer = $application->destination->server;
 | 
			
		||||
        $mainServer = $this->application->destination->server;
 | 
			
		||||
        $servers = Server::ownedByCurrentTeam()->get();
 | 
			
		||||
        $this->servers = $servers->filter(function ($server) use ($mainServer) {
 | 
			
		||||
            return $server->id != $mainServer->id;
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,6 @@ class DeploymentNavbar extends Component
 | 
			
		||||
    public Server $server;
 | 
			
		||||
    public bool $is_debug_enabled = false;
 | 
			
		||||
    protected $listeners = ['deploymentFinished'];
 | 
			
		||||
 | 
			
		||||
    public function mount()
 | 
			
		||||
    {
 | 
			
		||||
        $this->application = Application::find($this->application_deployment_queue->application_id);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,8 +29,6 @@ class General extends Component
 | 
			
		||||
    public ?string $initialDockerComposeLocation = null;
 | 
			
		||||
    public ?string $initialDockerComposePrLocation = null;
 | 
			
		||||
 | 
			
		||||
    public bool $is_static;
 | 
			
		||||
 | 
			
		||||
    public $parsedServices = [];
 | 
			
		||||
    public $parsedServiceDomains = [];
 | 
			
		||||
 | 
			
		||||
@@ -124,6 +122,10 @@ class General extends Component
 | 
			
		||||
    {
 | 
			
		||||
        $this->application->settings->save();
 | 
			
		||||
        $this->emit('success', 'Settings saved.');
 | 
			
		||||
        $this->application->refresh();
 | 
			
		||||
        if ($this->ports_exposes !== $this->application->ports_exposes) {
 | 
			
		||||
            $this->resetDefaultLabels(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    public function loadComposeFile($isInit = false)
 | 
			
		||||
    {
 | 
			
		||||
@@ -156,7 +158,7 @@ class General extends Component
 | 
			
		||||
    public function updatedApplicationBuildPack()
 | 
			
		||||
    {
 | 
			
		||||
        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();
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->application->build_pack === 'dockercompose') {
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,26 @@ class Heading extends Component
 | 
			
		||||
        $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)
 | 
			
		||||
    {
 | 
			
		||||
        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->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()
 | 
			
		||||
    {
 | 
			
		||||
        $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->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage;
 | 
			
		||||
        $this->validateServer();
 | 
			
		||||
    }
 | 
			
		||||
    public function serverRefresh($install = true)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,17 @@ use Livewire\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()
 | 
			
		||||
    {
 | 
			
		||||
        auth()->user()->update(['is_notification_sponsorship_enabled' => false]);
 | 
			
		||||
 
 | 
			
		||||
@@ -1094,6 +1094,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
			
		||||
        } else {
 | 
			
		||||
            $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) {
 | 
			
		||||
            $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 = [
 | 
			
		||||
@@ -1249,9 +1252,15 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
 | 
			
		||||
                [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->execute_remote_command(
 | 
			
		||||
                [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
 | 
			
		||||
            );
 | 
			
		||||
            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 {
 | 
			
		||||
                $this->execute_remote_command(
 | 
			
		||||
                    [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $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\Relations\HasMany;
 | 
			
		||||
use Illuminate\Support\Collection;
 | 
			
		||||
use Spatie\Activitylog\Models\Activity;
 | 
			
		||||
use Illuminate\Support\Str;
 | 
			
		||||
use RuntimeException;
 | 
			
		||||
@@ -48,6 +49,65 @@ class Application extends BaseModel
 | 
			
		||||
            $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()
 | 
			
		||||
    {
 | 
			
		||||
        if (data_get($this, 'environment.project.uuid')) {
 | 
			
		||||
@@ -385,9 +445,7 @@ class Application extends BaseModel
 | 
			
		||||
    }
 | 
			
		||||
    public function isMultipleServerDeployment()
 | 
			
		||||
    {
 | 
			
		||||
        if (isDev()) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        if (data_get($this, 'additional_destinations') && data_get($this, 'docker_registry_image_name')) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
@@ -431,6 +489,294 @@ class Application extends BaseModel
 | 
			
		||||
    {
 | 
			
		||||
        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)
 | 
			
		||||
    {
 | 
			
		||||
        $baseDir = $this->generateBaseDir($deployment_uuid);
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,11 @@ class ApplicationDeploymentQueue extends Model
 | 
			
		||||
{
 | 
			
		||||
    protected $guarded = [];
 | 
			
		||||
 | 
			
		||||
    public function setStatus(string $status) {
 | 
			
		||||
        $this->update([
 | 
			
		||||
            'status' => $status,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
    public function getOutput($name)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$this->logs) {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,29 +16,16 @@ class ApplicationSetting extends Model
 | 
			
		||||
        'is_git_submodules_enabled' => 'boolean',
 | 
			
		||||
        'is_git_lfs_enabled' => 'boolean',
 | 
			
		||||
    ];
 | 
			
		||||
    protected $fillable = [
 | 
			
		||||
        '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',
 | 
			
		||||
    ];
 | 
			
		||||
    protected $guarded = [];
 | 
			
		||||
 | 
			
		||||
    public function isStatic(): Attribute
 | 
			
		||||
    {
 | 
			
		||||
        return Attribute::make(
 | 
			
		||||
            set: function ($value) {
 | 
			
		||||
                if (is_null($this->application->ports_exposes)) {
 | 
			
		||||
                    if ($value) {
 | 
			
		||||
                        $this->application->ports_exposes = '80';
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $this->application->ports_exposes = '3000';
 | 
			
		||||
                    }
 | 
			
		||||
                    $this->application->save();
 | 
			
		||||
                if ($value) {
 | 
			
		||||
                    $this->application->ports_exposes = 80;
 | 
			
		||||
                }
 | 
			
		||||
                $this->application->save();
 | 
			
		||||
                return $value;
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,7 @@ class Server extends BaseModel
 | 
			
		||||
    {
 | 
			
		||||
        $teamId = currentTeam()->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()
 | 
			
		||||
@@ -86,7 +86,8 @@ class Server extends BaseModel
 | 
			
		||||
    {
 | 
			
		||||
        return $this->hasOne(ServerSetting::class);
 | 
			
		||||
    }
 | 
			
		||||
    public function addInitialNetwork() {
 | 
			
		||||
    public function addInitialNetwork()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->id === 0) {
 | 
			
		||||
            if ($this->isSwarm()) {
 | 
			
		||||
                SwarmDocker::create([
 | 
			
		||||
@@ -381,17 +382,17 @@ class Server extends BaseModel
 | 
			
		||||
    public function validateConnection()
 | 
			
		||||
    {
 | 
			
		||||
        $server = Server::find($this->id);
 | 
			
		||||
        if ($this->skipServer()) {
 | 
			
		||||
        if ($server->skipServer()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        $uptime = instant_remote_process(['uptime'], $this, false);
 | 
			
		||||
        $uptime = instant_remote_process(['uptime'], $server, false);
 | 
			
		||||
        if (!$uptime) {
 | 
			
		||||
            $this->settings()->update([
 | 
			
		||||
            $server->settings()->update([
 | 
			
		||||
                'is_reachable' => false,
 | 
			
		||||
            ]);
 | 
			
		||||
            return false;
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->settings()->update([
 | 
			
		||||
            $server->settings()->update([
 | 
			
		||||
                'is_reachable' => true,
 | 
			
		||||
            ]);
 | 
			
		||||
            $server->update([
 | 
			
		||||
@@ -399,8 +400,8 @@ class Server extends BaseModel
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (data_get($this, 'unreachable_notification_sent') === true) {
 | 
			
		||||
            $this->team->notify(new Revived($this));
 | 
			
		||||
        if (data_get($server, 'unreachable_notification_sent') === true) {
 | 
			
		||||
            $server->team->notify(new Revived($server));
 | 
			
		||||
            $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
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * The event to listener mappings for the application.
 | 
			
		||||
     *
 | 
			
		||||
     * @var array<class-string, array<int, class-string>>
 | 
			
		||||
     */
 | 
			
		||||
    protected $listen = [
 | 
			
		||||
        Registered::class => [
 | 
			
		||||
            SendEmailVerificationNotification::class,
 | 
			
		||||
        ],
 | 
			
		||||
        // Registered::class => [
 | 
			
		||||
        //     SendEmailVerificationNotification::class,
 | 
			
		||||
        // ],
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Register any events for your application.
 | 
			
		||||
     */
 | 
			
		||||
    public function boot(): void
 | 
			
		||||
    {
 | 
			
		||||
        //
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determine if events and listeners should be automatically discovered.
 | 
			
		||||
     */
 | 
			
		||||
    public function shouldDiscoverEvents(): bool
 | 
			
		||||
    {
 | 
			
		||||
        return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
use App\Jobs\ApplicationDeployDockerImageJob;
 | 
			
		||||
use App\Jobs\ApplicationDeploymentJob;
 | 
			
		||||
use App\Jobs\ApplicationDeploymentNewJob;
 | 
			
		||||
use App\Jobs\ApplicationDeploySimpleDockerfileJob;
 | 
			
		||||
use App\Jobs\ApplicationRestartJob;
 | 
			
		||||
use App\Jobs\MultipleApplicationDeploymentJob;
 | 
			
		||||
@@ -11,7 +12,7 @@ use App\Models\ApplicationPreview;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
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([
 | 
			
		||||
        'application_id' => $application_id,
 | 
			
		||||
@@ -36,19 +37,32 @@ function queue_application_deployment(int $application_id, string $deployment_uu
 | 
			
		||||
    if ($running_deployments->count() > 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    dispatch(new ApplicationDeploymentJob(
 | 
			
		||||
        application_deployment_queue_id: $deployment->id,
 | 
			
		||||
    ))->onConnection('long-running')->onQueue('long-running');
 | 
			
		||||
 | 
			
		||||
    if ($is_new_deployment) {
 | 
			
		||||
        dispatch(new ApplicationDeploymentNewJob(
 | 
			
		||||
            deployment: $deployment,
 | 
			
		||||
            application: Application::find($application_id)
 | 
			
		||||
        ))->onConnection('long-running')->onQueue('long-running');
 | 
			
		||||
    } else {
 | 
			
		||||
        dispatch(new ApplicationDeploymentJob(
 | 
			
		||||
            application_deployment_queue_id: $deployment->id,
 | 
			
		||||
        ))->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();
 | 
			
		||||
    if ($next_found) {
 | 
			
		||||
        dispatch(new ApplicationDeploymentJob(
 | 
			
		||||
            application_deployment_queue_id: $next_found->id,
 | 
			
		||||
        ))->onConnection('long-running')->onQueue('long-running');
 | 
			
		||||
        if ($isNew) {
 | 
			
		||||
            dispatch(new ApplicationDeploymentNewJob(
 | 
			
		||||
                deployment: $next_found,
 | 
			
		||||
                application: $application
 | 
			
		||||
            ))->onConnection('long-running')->onQueue('long-running');
 | 
			
		||||
        } else {
 | 
			
		||||
            dispatch(new ApplicationDeploymentJob(
 | 
			
		||||
                application_deployment_queue_id: $next_found->id,
 | 
			
		||||
            ))->onConnection('long-running')->onQueue('long-running');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
// Deployment things
 | 
			
		||||
 
 | 
			
		||||
@@ -1124,7 +1124,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
			
		||||
                    $serviceVolumes = $serviceVolumes->map(function ($volume) use ($resource, $pull_request_id, $topLevelVolumes) {
 | 
			
		||||
                        if (is_string($volume)) {
 | 
			
		||||
                            $volume = str($volume);
 | 
			
		||||
                            if ($volume->contains(':')) {
 | 
			
		||||
                            if ($volume->contains(':') && !$volume->startsWith('/')) {
 | 
			
		||||
                                $name = $volume->before(':');
 | 
			
		||||
                                $mount = $volume->after(':');
 | 
			
		||||
                                $newName = $resource->uuid . "-{$name}-pr-$pull_request_id";
 | 
			
		||||
@@ -1138,13 +1138,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
			
		||||
                            if ($source) {
 | 
			
		||||
                                $newSource = $resource->uuid . "-{$source}-pr-$pull_request_id";
 | 
			
		||||
                                data_set($volume, 'source', $newSource);
 | 
			
		||||
                                $topLevelVolumes->put($newSource, [
 | 
			
		||||
                                    'name' => $newSource,
 | 
			
		||||
                                ]);
 | 
			
		||||
                                if (!str($source)->startsWith('/')) {
 | 
			
		||||
                                    $topLevelVolumes->put($newSource, [
 | 
			
		||||
                                        'name' => $newSource,
 | 
			
		||||
                                    ]);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        return $volume->value();
 | 
			
		||||
                    });
 | 
			
		||||
                    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) {
 | 
			
		||||
                        if (is_string($volume)) {
 | 
			
		||||
                            $volume = str($volume);
 | 
			
		||||
                            if ($volume->contains(':')) {
 | 
			
		||||
                            if ($volume->contains(':') && !$volume->startsWith('/')) {
 | 
			
		||||
                                $name = $volume->before(':');
 | 
			
		||||
                                $mount = $volume->after(':');
 | 
			
		||||
                                if ($name->startsWith('.') || $name->startsWith('~')) {
 | 
			
		||||
@@ -1175,7 +1175,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
			
		||||
                        } else if (is_array($volume)) {
 | 
			
		||||
                            $source = data_get($volume, '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;
 | 
			
		||||
                                    if (str($source, '.')) {
 | 
			
		||||
                                        $source = str('.', $dir, $source);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@
 | 
			
		||||
        "nubs/random-name-generator": "^2.2",
 | 
			
		||||
        "phpseclib/phpseclib": "~3.0",
 | 
			
		||||
        "poliander/cron": "^3.0",
 | 
			
		||||
        "pusher/pusher-php-server": "^7.2",
 | 
			
		||||
        "resend/resend-laravel": "^0.5.0",
 | 
			
		||||
        "sentry/sentry-laravel": "^3.4",
 | 
			
		||||
        "spatie/laravel-activitylog": "^4.7.3",
 | 
			
		||||
@@ -38,6 +39,7 @@
 | 
			
		||||
        "stripe/stripe-php": "^12.0",
 | 
			
		||||
        "symfony/yaml": "^6.2",
 | 
			
		||||
        "visus/cuid2": "^2.0.0",
 | 
			
		||||
        "wire-elements/modal": "^1.0",
 | 
			
		||||
        "yosymfony/toml": "^1.0"
 | 
			
		||||
    },
 | 
			
		||||
    "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\FortifyServiceProvider::class,
 | 
			
		||||
        App\Providers\AuthServiceProvider::class,
 | 
			
		||||
        // App\Providers\BroadcastServiceProvider::class,
 | 
			
		||||
        App\Providers\BroadcastServiceProvider::class,
 | 
			
		||||
        App\Providers\EventServiceProvider::class,
 | 
			
		||||
        App\Providers\HorizonServiceProvider::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' => [
 | 
			
		||||
            'driver' => 'pusher',
 | 
			
		||||
            'key' => env('PUSHER_APP_KEY'),
 | 
			
		||||
            'secret' => env('PUSHER_APP_SECRET'),
 | 
			
		||||
            'app_id' => env('PUSHER_APP_ID'),
 | 
			
		||||
            'key' => env('PUSHER_APP_KEY', 'coolify'),
 | 
			
		||||
            'secret' => env('PUSHER_APP_SECRET', 'coolify'),
 | 
			
		||||
            'app_id' => env('PUSHER_APP_ID', 'coolify'),
 | 
			
		||||
            'options' => [
 | 
			
		||||
                'host' => env('PUSHER_HOST') ?: 'api-' . env('PUSHER_APP_CLUSTER', 'mt1') . '.pusher.com',
 | 
			
		||||
                'port' => env('PUSHER_PORT', 443),
 | 
			
		||||
                'scheme' => env('PUSHER_SCHEME', 'https'),
 | 
			
		||||
                'host' => env('PUSHER_BACKEND_HOST', 'coolify-soketi'),
 | 
			
		||||
                'port' => env('PUSHER_BACKEND_PORT', 6001),
 | 
			
		||||
                'scheme' => env('PUSHER_SCHEME', 'http'),
 | 
			
		||||
                'encrypted' => true,
 | 
			
		||||
                '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
 | 
			
		||||
    // 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
 | 
			
		||||
    'environment' => config('app.env'),
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,3 @@
 | 
			
		||||
<?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
 | 
			
		||||
    {
 | 
			
		||||
        // StandaloneDocker::create([
 | 
			
		||||
        //     'id' => 0,
 | 
			
		||||
        //     'name' => 'Standalone Docker 1',
 | 
			
		||||
        //     'network' => 'coolify',
 | 
			
		||||
        //     'server_id' => 0,
 | 
			
		||||
        // ]);
 | 
			
		||||
        StandaloneDocker::create([
 | 
			
		||||
            'id' => 0,
 | 
			
		||||
            'name' => 'Standalone Docker 1',
 | 
			
		||||
            'network' => 'coolify',
 | 
			
		||||
            'server_id' => 0,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,12 @@ services:
 | 
			
		||||
      SSL_MODE: "off"
 | 
			
		||||
      AUTORUN_LARAVEL_STORAGE_LINK: "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:
 | 
			
		||||
      - .:/var/www/html/:cached
 | 
			
		||||
  postgres:
 | 
			
		||||
@@ -44,9 +50,21 @@ services:
 | 
			
		||||
    volumes:
 | 
			
		||||
      - /data/coolify/_volumes/redis/:/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:
 | 
			
		||||
    image: node:20
 | 
			
		||||
    working_dir: /var/www/html
 | 
			
		||||
    # environment:
 | 
			
		||||
    # VITE_PUSHER_APP_KEY: "${PUSHER_APP_KEY:-coolify}"
 | 
			
		||||
    ports:
 | 
			
		||||
      - "${VITE_PORT:-5173}:${VITE_PORT:-5173}"
 | 
			
		||||
    volumes:
 | 
			
		||||
@@ -60,14 +78,14 @@ services:
 | 
			
		||||
      - /var/run/docker.sock:/var/run/docker.sock
 | 
			
		||||
      - /data/coolify/:/data/coolify
 | 
			
		||||
      # - coolify-data-dev:/data/coolify
 | 
			
		||||
  remote-host:
 | 
			
		||||
    <<: *testing-host-base
 | 
			
		||||
    container_name: coolify-remote-host
 | 
			
		||||
    volumes:
 | 
			
		||||
      - /:/host
 | 
			
		||||
      - /var/run/docker.sock:/var/run/docker.sock
 | 
			
		||||
      - /data/coolify/:/data/coolify
 | 
			
		||||
      # - coolify-data-dev:/data/coolify
 | 
			
		||||
      # remote-host:
 | 
			
		||||
      #   <<: *testing-host-base
 | 
			
		||||
      #   container_name: coolify-remote-host
 | 
			
		||||
      #   volumes:
 | 
			
		||||
      #     - /:/host
 | 
			
		||||
      #     - /var/run/docker.sock:/var/run/docker.sock
 | 
			
		||||
      #     - /data/coolify/:/data/coolify
 | 
			
		||||
      #     # - coolify-data-dev:/data/coolify
 | 
			
		||||
  mailpit:
 | 
			
		||||
    image: "axllent/mailpit:latest"
 | 
			
		||||
    container_name: coolify-mail
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
version: '3.8'
 | 
			
		||||
services:
 | 
			
		||||
  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:
 | 
			
		||||
      - type: bind
 | 
			
		||||
        source: /data/coolify/source/.env
 | 
			
		||||
@@ -35,6 +35,14 @@ services:
 | 
			
		||||
      - PHP_PM_START_SERVERS=1
 | 
			
		||||
      - PHP_PM_MIN_SPARE_SERVERS=1
 | 
			
		||||
      - 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
 | 
			
		||||
      - WAITLIST
 | 
			
		||||
      - SUBSCRIPTION_PROVIDER
 | 
			
		||||
@@ -74,8 +82,8 @@ services:
 | 
			
		||||
      - "${APP_PORT:-8000}"
 | 
			
		||||
    healthcheck:
 | 
			
		||||
      test: curl --fail http://localhost:80/api/health || exit 1
 | 
			
		||||
      interval: 4s
 | 
			
		||||
      retries: 5
 | 
			
		||||
      interval: 5s
 | 
			
		||||
      retries: 10
 | 
			
		||||
      timeout: 2s
 | 
			
		||||
    depends_on:
 | 
			
		||||
      postgres:
 | 
			
		||||
@@ -97,8 +105,8 @@ services:
 | 
			
		||||
          "-d",
 | 
			
		||||
          "${DB_DATABASE:-coolify}"
 | 
			
		||||
        ]
 | 
			
		||||
      interval: 2s
 | 
			
		||||
      retries: 5
 | 
			
		||||
      interval: 5s
 | 
			
		||||
      retries: 10
 | 
			
		||||
      timeout: 2s
 | 
			
		||||
  redis:
 | 
			
		||||
    command: redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD}
 | 
			
		||||
@@ -108,8 +116,21 @@ services:
 | 
			
		||||
      - coolify-redis:/data
 | 
			
		||||
    healthcheck:
 | 
			
		||||
      test: redis-cli ping
 | 
			
		||||
      interval: 2s
 | 
			
		||||
      retries: 5
 | 
			
		||||
      interval: 5s
 | 
			
		||||
      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
 | 
			
		||||
volumes:
 | 
			
		||||
  coolify-db:
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,12 @@ services:
 | 
			
		||||
        restart: always
 | 
			
		||||
        networks:
 | 
			
		||||
            - coolify
 | 
			
		||||
    soketi:
 | 
			
		||||
        image: 'quay.io/soketi/soketi:1.6-16-alpine'
 | 
			
		||||
        container_name: coolify-soketi
 | 
			
		||||
        restart: always
 | 
			
		||||
        networks:
 | 
			
		||||
            - coolify
 | 
			
		||||
networks:
 | 
			
		||||
    coolify:
 | 
			
		||||
        name: coolify
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										385
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										385
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -5,20 +5,24 @@
 | 
			
		||||
    "packages": {
 | 
			
		||||
        "": {
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@alpinejs/focus": "^3.13.3",
 | 
			
		||||
                "@tailwindcss/typography": "0.5.10",
 | 
			
		||||
                "alpinejs": "3.13.2",
 | 
			
		||||
                "daisyui": "4.3.1",
 | 
			
		||||
                "alpinejs": "3.13.3",
 | 
			
		||||
                "daisyui": "4.4.19",
 | 
			
		||||
                "ioredis": "5.3.2",
 | 
			
		||||
                "tailwindcss-scrollbar": "0.1.0"
 | 
			
		||||
            },
 | 
			
		||||
            "devDependencies": {
 | 
			
		||||
                "@vitejs/plugin-vue": "4.5.0",
 | 
			
		||||
                "@vitejs/plugin-vue": "4.5.1",
 | 
			
		||||
                "autoprefixer": "10.4.16",
 | 
			
		||||
                "axios": "1.6.2",
 | 
			
		||||
                "laravel-echo": "1.15.3",
 | 
			
		||||
                "laravel-vite-plugin": "0.8.1",
 | 
			
		||||
                "postcss": "8.4.31",
 | 
			
		||||
                "tailwindcss": "3.3.5",
 | 
			
		||||
                "vite": "4.5.0",
 | 
			
		||||
                "vue": "3.3.8"
 | 
			
		||||
                "postcss": "8.4.32",
 | 
			
		||||
                "pusher-js": "8.4.0-rc2",
 | 
			
		||||
                "tailwindcss": "3.3.6",
 | 
			
		||||
                "vite": "4.5.1",
 | 
			
		||||
                "vue": "3.3.10"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@alloc/quick-lru": {
 | 
			
		||||
@@ -32,10 +36,19 @@
 | 
			
		||||
                "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": {
 | 
			
		||||
            "version": "7.23.3",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz",
 | 
			
		||||
            "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==",
 | 
			
		||||
            "version": "7.23.5",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz",
 | 
			
		||||
            "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "bin": {
 | 
			
		||||
                "parser": "bin/babel-parser.js"
 | 
			
		||||
@@ -396,6 +409,11 @@
 | 
			
		||||
                "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": {
 | 
			
		||||
            "version": "0.3.3",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
 | 
			
		||||
@@ -503,9 +521,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vitejs/plugin-vue": {
 | 
			
		||||
            "version": "4.5.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.5.0.tgz",
 | 
			
		||||
            "integrity": "sha512-a2WSpP8X8HTEww/U00bU4mX1QpLINNuz/2KMNpLsdu3BzOpak3AGI1CJYBTXcc4SPhaD0eNRUp7IyQK405L5dQ==",
 | 
			
		||||
            "version": "4.5.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.5.1.tgz",
 | 
			
		||||
            "integrity": "sha512-DaUzYFr+2UGDG7VSSdShKa9sIWYBa1LL8KC0MNOf2H5LjcTPjob0x8LbkqXWmAtbANJCkpiQTj66UVcQkN2s3g==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "engines": {
 | 
			
		||||
                "node": "^14.18.0 || >=16.0.0"
 | 
			
		||||
@@ -516,77 +534,77 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-core": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-doe0hODR1+i1menPkRzJ5MNR6G+9uiZHIknK3Zn5OcIztu6GGw7u0XUzf3AgB8h/dfsZC9eouzoLo3c3+N/cVA==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@babel/parser": "^7.23.0",
 | 
			
		||||
                "@vue/shared": "3.3.8",
 | 
			
		||||
                "@babel/parser": "^7.23.5",
 | 
			
		||||
                "@vue/shared": "3.3.10",
 | 
			
		||||
                "estree-walker": "^2.0.2",
 | 
			
		||||
                "source-map-js": "^1.0.2"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-core/node_modules/@vue/shared": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
			
		||||
            "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-dom": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-NCrqF5fm10GXZIK0GrEAauBqdy+F2LZRt3yNHzrYjpYBuRssQbuPLtSnSNjyR9luHKkWSH8we5LMB3g+4z2HvA==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@vue/compiler-core": "3.3.8",
 | 
			
		||||
                "@vue/shared": "3.3.8"
 | 
			
		||||
                "@vue/compiler-core": "3.3.10",
 | 
			
		||||
                "@vue/shared": "3.3.10"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-dom/node_modules/@vue/shared": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
			
		||||
            "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-sfc": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-xpcTe7Rw7QefOTRFFTlcfzozccvjM40dT45JtrE3onGm/jBLZ0JhpKu3jkV7rbDFLeeagR/5RlJ2Y9SvyS0lAg==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@babel/parser": "^7.23.0",
 | 
			
		||||
                "@vue/compiler-core": "3.3.8",
 | 
			
		||||
                "@vue/compiler-dom": "3.3.8",
 | 
			
		||||
                "@vue/compiler-ssr": "3.3.8",
 | 
			
		||||
                "@vue/reactivity-transform": "3.3.8",
 | 
			
		||||
                "@vue/shared": "3.3.8",
 | 
			
		||||
                "@babel/parser": "^7.23.5",
 | 
			
		||||
                "@vue/compiler-core": "3.3.10",
 | 
			
		||||
                "@vue/compiler-dom": "3.3.10",
 | 
			
		||||
                "@vue/compiler-ssr": "3.3.10",
 | 
			
		||||
                "@vue/reactivity-transform": "3.3.10",
 | 
			
		||||
                "@vue/shared": "3.3.10",
 | 
			
		||||
                "estree-walker": "^2.0.2",
 | 
			
		||||
                "magic-string": "^0.30.5",
 | 
			
		||||
                "postcss": "^8.4.31",
 | 
			
		||||
                "postcss": "^8.4.32",
 | 
			
		||||
                "source-map-js": "^1.0.2"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
			
		||||
            "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-ssr": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-12iM4jA4GEbskwXMmPcskK5wImc2ohKm408+o9iox3tfN9qua8xL0THIZtoe9OJHnXP4eOWZpgCAAThEveNlqQ==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@vue/compiler-dom": "3.3.8",
 | 
			
		||||
                "@vue/shared": "3.3.8"
 | 
			
		||||
                "@vue/compiler-dom": "3.3.10",
 | 
			
		||||
                "@vue/shared": "3.3.10"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
			
		||||
            "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/reactivity": {
 | 
			
		||||
@@ -598,83 +616,83 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/reactivity-transform": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-0xBdk+CKHWT+Gev8oZ63Tc0qFfj935YZx+UAynlutnrDZ4diFCVFMWixn65HzjE3S1iJppWOo6Tt1OzASH7VEg==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@babel/parser": "^7.23.0",
 | 
			
		||||
                "@vue/compiler-core": "3.3.8",
 | 
			
		||||
                "@vue/shared": "3.3.8",
 | 
			
		||||
                "@babel/parser": "^7.23.5",
 | 
			
		||||
                "@vue/compiler-core": "3.3.10",
 | 
			
		||||
                "@vue/shared": "3.3.10",
 | 
			
		||||
                "estree-walker": "^2.0.2",
 | 
			
		||||
                "magic-string": "^0.30.5"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/reactivity-transform/node_modules/@vue/shared": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
			
		||||
            "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/runtime-core": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-DZ0v31oTN4YHX9JEU5VW1LoIVgFovWgIVb30bWn9DG9a7oA415idcwsRNNajqTx8HQJyOaWfRKoyuP2P2TYIag==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@vue/reactivity": "3.3.8",
 | 
			
		||||
                "@vue/shared": "3.3.8"
 | 
			
		||||
                "@vue/reactivity": "3.3.10",
 | 
			
		||||
                "@vue/shared": "3.3.10"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/runtime-core/node_modules/@vue/reactivity": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-H5Z7rOY/JLO+e5a6/FEXaQ1TMuOvY4LDVgT+/+HKubEAgs9qeeZ+NhADSeEtrNQeiKLDuzeKc8v0CUFpB6Pqgw==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@vue/shared": "3.3.8"
 | 
			
		||||
                "@vue/shared": "3.3.10"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/runtime-core/node_modules/@vue/shared": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
			
		||||
            "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/runtime-dom": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-c/jKb3ny05KJcYk0j1m7Wbhrxq7mZYr06GhKykDMNRRR9S+/dGT8KpHuNQjv3/8U4JshfkAk6TpecPD3B21Ijw==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@vue/runtime-core": "3.3.8",
 | 
			
		||||
                "@vue/shared": "3.3.8",
 | 
			
		||||
                "@vue/runtime-core": "3.3.10",
 | 
			
		||||
                "@vue/shared": "3.3.10",
 | 
			
		||||
                "csstype": "^3.1.2"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
			
		||||
            "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/server-renderer": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-0i6ww3sBV3SKlF3YTjSVqKQ74xialMbjVYGy7cOTi7Imd8ediE7t72SK3qnvhrTAhOvlQhq6Bk6nFPdXxe0sAg==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@vue/compiler-ssr": "3.3.8",
 | 
			
		||||
                "@vue/shared": "3.3.8"
 | 
			
		||||
                "@vue/compiler-ssr": "3.3.10",
 | 
			
		||||
                "@vue/shared": "3.3.10"
 | 
			
		||||
            },
 | 
			
		||||
            "peerDependencies": {
 | 
			
		||||
                "vue": "3.3.8"
 | 
			
		||||
                "vue": "3.3.10"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/server-renderer/node_modules/@vue/shared": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
			
		||||
            "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@vue/shared": {
 | 
			
		||||
@@ -683,9 +701,9 @@
 | 
			
		||||
            "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/alpinejs": {
 | 
			
		||||
            "version": "3.13.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.2.tgz",
 | 
			
		||||
            "integrity": "sha512-WzojeeN082kLZznGI1HAuP8yFJSWqJ1fGdz2mUjj45H4y0XwToE7fFqtI3mCPRR+BpcSbxT/NL+FyPnYAWSltw==",
 | 
			
		||||
            "version": "3.13.3",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.3.tgz",
 | 
			
		||||
            "integrity": "sha512-WZ6WQjkAOl+WdW/jukzNHq9zHFDNKmkk/x6WF7WdyNDD6woinrfXCVsZXm0galjbco+pEpYmJLtwlZwcOfIVdg==",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@vue/reactivity": "~3.1.1"
 | 
			
		||||
            }
 | 
			
		||||
@@ -896,6 +914,14 @@
 | 
			
		||||
                "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": {
 | 
			
		||||
            "version": "1.0.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
 | 
			
		||||
@@ -956,9 +982,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/daisyui": {
 | 
			
		||||
            "version": "4.3.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.3.1.tgz",
 | 
			
		||||
            "integrity": "sha512-dCi91VD+57lkoBd10CjdW4wPOeOPYvvzQbxti6xmyQbDMbCeCXwNq2KdoU798I4OsCcD5B+n7yVG7HAgYW+cvw==",
 | 
			
		||||
            "version": "4.4.19",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.4.19.tgz",
 | 
			
		||||
            "integrity": "sha512-IjOLWwnndD4N7Ut5CDxbUsaVtbqXPeVHM92IcgxGFxpuOd3CCKW/PAXZH6JoBTHFRaN57vB9XqEhdWm5yC+bPA==",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "css-selector-tokenizer": "^0.8",
 | 
			
		||||
                "culori": "^3",
 | 
			
		||||
@@ -973,6 +999,22 @@
 | 
			
		||||
                "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": {
 | 
			
		||||
            "version": "1.0.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
 | 
			
		||||
@@ -982,6 +1024,14 @@
 | 
			
		||||
                "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": {
 | 
			
		||||
            "version": "1.2.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
 | 
			
		||||
@@ -1100,6 +1150,14 @@
 | 
			
		||||
                "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": {
 | 
			
		||||
            "version": "1.15.2",
 | 
			
		||||
            "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",
 | 
			
		||||
            "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": {
 | 
			
		||||
            "version": "2.1.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
 | 
			
		||||
@@ -1290,6 +1371,15 @@
 | 
			
		||||
                "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": {
 | 
			
		||||
            "version": "0.8.1",
 | 
			
		||||
            "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",
 | 
			
		||||
            "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": {
 | 
			
		||||
            "version": "4.0.6",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
 | 
			
		||||
@@ -1398,6 +1498,11 @@
 | 
			
		||||
                "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": {
 | 
			
		||||
            "version": "2.7.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
 | 
			
		||||
@@ -1409,9 +1514,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/nanoid": {
 | 
			
		||||
            "version": "3.3.6",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
 | 
			
		||||
            "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
 | 
			
		||||
            "version": "3.3.7",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
 | 
			
		||||
            "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
                    "type": "github",
 | 
			
		||||
@@ -1518,9 +1623,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/postcss": {
 | 
			
		||||
            "version": "8.4.31",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
 | 
			
		||||
            "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
 | 
			
		||||
            "version": "8.4.32",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
 | 
			
		||||
            "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
 | 
			
		||||
            "funding": [
 | 
			
		||||
                {
 | 
			
		||||
                    "type": "opencollective",
 | 
			
		||||
@@ -1536,7 +1641,7 @@
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "nanoid": "^3.3.6",
 | 
			
		||||
                "nanoid": "^3.3.7",
 | 
			
		||||
                "picocolors": "^1.0.0",
 | 
			
		||||
                "source-map-js": "^1.0.2"
 | 
			
		||||
            },
 | 
			
		||||
@@ -1647,6 +1752,15 @@
 | 
			
		||||
            "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
 | 
			
		||||
            "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": {
 | 
			
		||||
            "version": "1.2.3",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
 | 
			
		||||
@@ -1685,6 +1799,25 @@
 | 
			
		||||
                "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": {
 | 
			
		||||
            "version": "1.22.2",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
 | 
			
		||||
@@ -1775,6 +1908,11 @@
 | 
			
		||||
                "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": {
 | 
			
		||||
            "version": "3.32.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
 | 
			
		||||
@@ -1807,10 +1945,15 @@
 | 
			
		||||
                "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": {
 | 
			
		||||
            "version": "3.3.5",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz",
 | 
			
		||||
            "integrity": "sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==",
 | 
			
		||||
            "version": "3.3.6",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.6.tgz",
 | 
			
		||||
            "integrity": "sha512-AKjF7qbbLvLaPieoKeTjG1+FyNZT6KaJMJPFeQyLfIp7l82ggH1fbHJSsYIvnbTFQOlkh+gBYpyby5GT1LIdLw==",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@alloc/quick-lru": "^5.2.0",
 | 
			
		||||
                "arg": "^5.0.2",
 | 
			
		||||
@@ -1886,6 +2029,12 @@
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
 | 
			
		||||
            "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": {
 | 
			
		||||
            "version": "1.0.13",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
 | 
			
		||||
@@ -1922,9 +2071,9 @@
 | 
			
		||||
            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/vite": {
 | 
			
		||||
            "version": "4.5.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",
 | 
			
		||||
            "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==",
 | 
			
		||||
            "version": "4.5.1",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.1.tgz",
 | 
			
		||||
            "integrity": "sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "esbuild": "^0.18.10",
 | 
			
		||||
@@ -1987,16 +2136,16 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/vue": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-zg6SIXZdTBwiqCw/1p+m04VyHjLfwtjwz8N57sPaBhEex31ND0RYECVOC1YrRwMRmxFf5T1dabl6SGUbMKKuVw==",
 | 
			
		||||
            "dev": true,
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@vue/compiler-dom": "3.3.8",
 | 
			
		||||
                "@vue/compiler-sfc": "3.3.8",
 | 
			
		||||
                "@vue/runtime-dom": "3.3.8",
 | 
			
		||||
                "@vue/server-renderer": "3.3.8",
 | 
			
		||||
                "@vue/shared": "3.3.8"
 | 
			
		||||
                "@vue/compiler-dom": "3.3.10",
 | 
			
		||||
                "@vue/compiler-sfc": "3.3.10",
 | 
			
		||||
                "@vue/runtime-dom": "3.3.10",
 | 
			
		||||
                "@vue/server-renderer": "3.3.10",
 | 
			
		||||
                "@vue/shared": "3.3.10"
 | 
			
		||||
            },
 | 
			
		||||
            "peerDependencies": {
 | 
			
		||||
                "typescript": "*"
 | 
			
		||||
@@ -2008,9 +2157,9 @@
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/vue/node_modules/@vue/shared": {
 | 
			
		||||
            "version": "3.3.8",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz",
 | 
			
		||||
            "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==",
 | 
			
		||||
            "version": "3.3.10",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.10.tgz",
 | 
			
		||||
            "integrity": "sha512-2y3Y2J1a3RhFa0WisHvACJR2ncvWiVHcP8t0Inxo+NKz+8RKO4ZV8eZgCxRgQoA6ITfV12L4E6POOL9HOU5nqw==",
 | 
			
		||||
            "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/wrappy": {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								package.json
									
									
									
									
									
								
							@@ -6,19 +6,23 @@
 | 
			
		||||
        "build": "vite build"
 | 
			
		||||
    },
 | 
			
		||||
    "devDependencies": {
 | 
			
		||||
        "@vitejs/plugin-vue": "4.5.0",
 | 
			
		||||
        "@vitejs/plugin-vue": "4.5.1",
 | 
			
		||||
        "autoprefixer": "10.4.16",
 | 
			
		||||
        "axios": "1.6.2",
 | 
			
		||||
        "laravel-echo": "1.15.3",
 | 
			
		||||
        "laravel-vite-plugin": "0.8.1",
 | 
			
		||||
        "postcss": "8.4.31",
 | 
			
		||||
        "tailwindcss": "3.3.5",
 | 
			
		||||
        "vite": "4.5.0",
 | 
			
		||||
        "vue": "3.3.8"
 | 
			
		||||
        "postcss": "8.4.32",
 | 
			
		||||
        "pusher-js": "8.4.0-rc2",
 | 
			
		||||
        "tailwindcss": "3.3.6",
 | 
			
		||||
        "vite": "4.5.1",
 | 
			
		||||
        "vue": "3.3.10"
 | 
			
		||||
    },
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@alpinejs/focus": "^3.13.3",
 | 
			
		||||
        "@tailwindcss/typography": "0.5.10",
 | 
			
		||||
        "alpinejs": "3.13.2",
 | 
			
		||||
        "daisyui": "4.3.1",
 | 
			
		||||
        "alpinejs": "3.13.3",
 | 
			
		||||
        "daisyui": "4.4.19",
 | 
			
		||||
        "ioredis": "5.3.2",
 | 
			
		||||
        "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.css": "/app.css?id=4d6a1a7fe095eedc2cb2a4ce822ea8a5",
 | 
			
		||||
    "/img/favicon.png": "/img/favicon.png?id=1542bfe8a0010dcbee710da13cce367f",
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,9 @@ button[isError] {
 | 
			
		||||
.main {
 | 
			
		||||
    @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 {
 | 
			
		||||
    @apply text-neutral-400;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
import Alpine from "alpinejs";
 | 
			
		||||
 | 
			
		||||
import focus from '@alpinejs/focus';
 | 
			
		||||
import { createApp } from "vue";
 | 
			
		||||
import MagicBar from "./components/MagicBar.vue";
 | 
			
		||||
import Toaster from "../../vendor/masmerise/livewire-toaster/resources/js";
 | 
			
		||||
import "../../vendor/wire-elements/modal/resources/js/modal";
 | 
			
		||||
 | 
			
		||||
Alpine.plugin(focus);
 | 
			
		||||
Alpine.plugin(Toaster);
 | 
			
		||||
 | 
			
		||||
window.Alpine = Alpine;
 | 
			
		||||
@@ -12,3 +14,6 @@ Alpine.start();
 | 
			
		||||
const app = createApp({});
 | 
			
		||||
app.component("magic-bar", MagicBar);
 | 
			
		||||
app.mount("#vue");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,14 +3,18 @@
 | 
			
		||||
        href="{{ route('project.application.configuration', $parameters) }}">
 | 
			
		||||
        <button>Configuration</button>
 | 
			
		||||
    </a>
 | 
			
		||||
    <a class="{{ request()->routeIs('project.application.deployments') ? 'text-white' : '' }}"
 | 
			
		||||
        href="{{ route('project.application.deployments', $parameters) }}">
 | 
			
		||||
        <button>Deployments</button>
 | 
			
		||||
    <a class="{{ request()->routeIs('project.application.command') ? 'text-white' : '' }}"
 | 
			
		||||
        href="{{ route('project.application.command', $parameters) }}">
 | 
			
		||||
        <button>Execute Command</button>
 | 
			
		||||
    </a>
 | 
			
		||||
    <a class="{{ request()->routeIs('project.application.logs') ? 'text-white' : '' }}"
 | 
			
		||||
        href="{{ route('project.application.logs', $parameters) }}">
 | 
			
		||||
        <button>Logs</button>
 | 
			
		||||
    </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" />
 | 
			
		||||
    <div class="flex-1"></div>
 | 
			
		||||
    @if ($application->build_pack === 'dockercompose' && is_null($application->docker_compose_raw))
 | 
			
		||||
@@ -45,6 +49,19 @@
 | 
			
		||||
                    </svg>
 | 
			
		||||
                    Restart
 | 
			
		||||
                </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
 | 
			
		||||
            <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"
 | 
			
		||||
@@ -66,6 +83,18 @@
 | 
			
		||||
                </svg>
 | 
			
		||||
                Deploy
 | 
			
		||||
            </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
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,10 @@
 | 
			
		||||
        href="{{ route('project.database.configuration', $parameters) }}">
 | 
			
		||||
        <button>Configuration</button>
 | 
			
		||||
    </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' : '' }}"
 | 
			
		||||
        href="{{ route('project.database.logs', $parameters) }}">
 | 
			
		||||
        <button>Logs</button>
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,8 @@
 | 
			
		||||
        </button>
 | 
			
		||||
    @endif
 | 
			
		||||
    @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">
 | 
			
		||||
                <path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
 | 
			
		||||
                <path fill="red"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
@props([
 | 
			
		||||
    '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="badge badge-warning badge-xs"></div>
 | 
			
		||||
    <div class="text-xs font-medium tracking-wide text-warning">{{ Str::headline($status) }}</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
@props([
 | 
			
		||||
    '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="badge badge-warning badge-xs"></div>
 | 
			
		||||
    <div class="text-xs font-medium tracking-wide text-warning">{{ Str::headline($status) }}</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
@section('body')
 | 
			
		||||
    @parent
 | 
			
		||||
    <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>
 | 
			
		||||
    </div>
 | 
			
		||||
    <livewire:sponsorship />
 | 
			
		||||
 
 | 
			
		||||
@@ -24,11 +24,20 @@
 | 
			
		||||
    @if (config('app.name') == 'Coolify Cloud')
 | 
			
		||||
        <script defer data-domain="app.coolify.io" src="https://analytics.coollabs.io/js/plausible.js"></script>
 | 
			
		||||
    @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>
 | 
			
		||||
@section('body')
 | 
			
		||||
 | 
			
		||||
    <body>
 | 
			
		||||
        @livewireScripts
 | 
			
		||||
        @livewire('livewire-ui-modal')
 | 
			
		||||
        <dialog id="help" class="modal">
 | 
			
		||||
            <livewire:help />
 | 
			
		||||
            <form method="dialog" class="modal-backdrop">
 | 
			
		||||
@@ -37,7 +46,47 @@
 | 
			
		||||
        </dialog>
 | 
			
		||||
        <x-toaster-hub />
 | 
			
		||||
        <x-version class="fixed left-2 bottom-1" />
 | 
			
		||||
 | 
			
		||||
        <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 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.compose-modal :raw="$service->docker_compose_raw" :actual="$service->docker_compose" />
 | 
			
		||||
    <div class="flex h-full pt-6">
 | 
			
		||||
        <div class="flex flex-col items-start gap-4 min-w-fit">
 | 
			
		||||
            <a target="_blank" href="{{ $service->documentation() }}">Documentation <x-external-link /></a>
 | 
			
		||||
@@ -8,6 +7,10 @@
 | 
			
		||||
                @click.prevent="activeTab = 'service-stack';
 | 
			
		||||
                window.location.hash = 'service-stack'"
 | 
			
		||||
                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'"
 | 
			
		||||
                @click.prevent="activeTab = 'storages';
 | 
			
		||||
                window.location.hash = 'storages'"
 | 
			
		||||
@@ -113,6 +116,9 @@
 | 
			
		||||
            <div x-cloak x-show="activeTab === 'webhooks'">
 | 
			
		||||
                <livewire:project.shared.webhooks :resource="$service" />
 | 
			
		||||
            </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'">
 | 
			
		||||
                    <livewire:project.shared.environment-variable.all :resource="$service" />
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,9 @@
 | 
			
		||||
            <div>Configuration</div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <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>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="flex gap-2">
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,8 @@
 | 
			
		||||
            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>
 | 
			
		||||
    </div>
 | 
			
		||||
    {{-- {{$resource->additional_destinations}} --}}
 | 
			
		||||
    {{-- Additonal Destinations:
 | 
			
		||||
    {{$resource->additional_destinations}} --}}
 | 
			
		||||
    {{-- @if (count($servers) > 0)
 | 
			
		||||
        <div>
 | 
			
		||||
            <h3>Additional Servers</h3>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
                    class="font-bold text-warning">({{ $env->key }})</span>?</p>
 | 
			
		||||
        </x-slot:modalBody>
 | 
			
		||||
    </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)
 | 
			
		||||
            <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">
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
//     return (int) $user->id === (int) $id;
 | 
			
		||||
// });
 | 
			
		||||
use App\Models\Application;
 | 
			
		||||
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\Dashboard;
 | 
			
		||||
use App\Http\Livewire\Project\CloneProject;
 | 
			
		||||
use App\Http\Livewire\Project\Shared\ExecuteContainerCommand;
 | 
			
		||||
use App\Http\Livewire\Project\Shared\Logs;
 | 
			
		||||
use App\Http\Livewire\Security\ApiTokens;
 | 
			
		||||
use App\Http\Livewire\Server\All;
 | 
			
		||||
@@ -42,6 +43,16 @@ use Laravel\Fortify\Fortify;
 | 
			
		||||
if (isDev()) {
 | 
			
		||||
    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) {
 | 
			
		||||
    if (is_transactional_emails_active()) {
 | 
			
		||||
        $arrayOfRequest = $request->only(Fortify::email());
 | 
			
		||||
@@ -111,18 +122,21 @@ Route::middleware(['auth', 'verified'])->group(function () {
 | 
			
		||||
    )->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}/command', ExecuteContainerCommand::class)->name('project.application.command');
 | 
			
		||||
 | 
			
		||||
    // 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}/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}/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
 | 
			
		||||
    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}/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 () {
 | 
			
		||||
 
 | 
			
		||||
@@ -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|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|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
 | 
			
		||||
 | 
			
		||||
# Merge .env and .env.production. New values will be added to .env
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,7 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
## Do not modify this file. You will lost the ability to autoupdate!
 | 
			
		||||
 | 
			
		||||
###########
 | 
			
		||||
## Always run "php artisan app:sync-to-bunny-cdn --env=secrets" if you update this file.
 | 
			
		||||
###########
 | 
			
		||||
 | 
			
		||||
VERSION="1.0.1"
 | 
			
		||||
VERSION="1.0.4"
 | 
			
		||||
CDN="https://cdn.coollabs.io/coolify"
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
docker network create --attachable coolify 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} */
 | 
			
		||||
module.exports = {
 | 
			
		||||
    content: [
 | 
			
		||||
        './vendor/wire-elements/modal/resources/views/*.blade.php',
 | 
			
		||||
        './storage/framework/views/*.php',
 | 
			
		||||
        "./resources/**/*.blade.php",
 | 
			
		||||
        "./app/**/*.php",
 | 
			
		||||
        "./resources/**/*.js",
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
            "version": "3.12.36"
 | 
			
		||||
        },
 | 
			
		||||
        "v4": {
 | 
			
		||||
            "version": "4.0.0-beta.153"
 | 
			
		||||
            "version": "4.0.0-beta.154"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user