@@ -33,12 +33,13 @@ Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
 | 
				
			|||||||
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
 | 
					<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Github Sponsors ($40+)
 | 
					## Github Sponsors ($40+)
 | 
				
			||||||
 | 
					<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
 | 
				
			||||||
 | 
					<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
 | 
				
			||||||
<a href="https://americancloud.com/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
 | 
					<a href="https://americancloud.com/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
 | 
				
			||||||
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
 | 
					<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
 | 
				
			||||||
<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
 | 
					 | 
				
			||||||
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
 | 
					 | 
				
			||||||
<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
 | 
					<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
 | 
				
			||||||
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></a>
 | 
					<a href="https://github.com/Flowko"><img src="https://barrad.me/_ipx/f_webp&s_300x300/younes.jpg" width="60px" alt="Younes Barrad" /></a>
 | 
				
			||||||
 | 
					<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Automaze" /></a>
 | 
				
			||||||
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
 | 
					<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
 | 
				
			||||||
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
 | 
					<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
 | 
				
			||||||
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
 | 
					<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										159
									
								
								app/Actions/Database/StartClickhouse.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								app/Actions/Database/StartClickhouse.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,159 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Actions\Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					use Symfony\Component\Yaml\Yaml;
 | 
				
			||||||
 | 
					use Lorisleiva\Actions\Concerns\AsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StartClickhouse
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use AsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public StandaloneClickhouse $database;
 | 
				
			||||||
 | 
					    public array $commands = [];
 | 
				
			||||||
 | 
					    public string $configuration_dir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function handle(StandaloneClickhouse $database)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->database = $database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $container_name = $this->database->uuid;
 | 
				
			||||||
 | 
					        $this->configuration_dir = database_configuration_dir() . '/' . $container_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->commands = [
 | 
				
			||||||
 | 
					            "echo 'Starting {$database->name}.'",
 | 
				
			||||||
 | 
					            "mkdir -p $this->configuration_dir",
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $persistent_storages = $this->generate_local_persistent_volumes();
 | 
				
			||||||
 | 
					        $volume_names = $this->generate_local_persistent_volumes_only_volume_names();
 | 
				
			||||||
 | 
					        $environment_variables = $this->generate_environment_variables();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $docker_compose = [
 | 
				
			||||||
 | 
					            'version' => '3.8',
 | 
				
			||||||
 | 
					            'services' => [
 | 
				
			||||||
 | 
					                $container_name => [
 | 
				
			||||||
 | 
					                    'image' => $this->database->image,
 | 
				
			||||||
 | 
					                    'container_name' => $container_name,
 | 
				
			||||||
 | 
					                    'environment' => $environment_variables,
 | 
				
			||||||
 | 
					                    'restart' => RESTART_MODE,
 | 
				
			||||||
 | 
					                    'networks' => [
 | 
				
			||||||
 | 
					                        $this->database->destination->network,
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'ulimits' => [
 | 
				
			||||||
 | 
					                        'nofile' => [
 | 
				
			||||||
 | 
					                            'soft' => 262144,
 | 
				
			||||||
 | 
					                            'hard' => 262144,
 | 
				
			||||||
 | 
					                        ],
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'labels' => [
 | 
				
			||||||
 | 
					                        'coolify.managed' => 'true',
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'healthcheck' => [
 | 
				
			||||||
 | 
					                        'test' => "clickhouse-client --password {$this->database->clickhouse_admin_password} --query 'SELECT 1'",
 | 
				
			||||||
 | 
					                        'interval' => '5s',
 | 
				
			||||||
 | 
					                        'timeout' => '5s',
 | 
				
			||||||
 | 
					                        'retries' => 10,
 | 
				
			||||||
 | 
					                        'start_period' => '5s'
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'mem_limit' => $this->database->limits_memory,
 | 
				
			||||||
 | 
					                    'memswap_limit' => $this->database->limits_memory_swap,
 | 
				
			||||||
 | 
					                    'mem_swappiness' => $this->database->limits_memory_swappiness,
 | 
				
			||||||
 | 
					                    'mem_reservation' => $this->database->limits_memory_reservation,
 | 
				
			||||||
 | 
					                    'cpus' => (float) $this->database->limits_cpus,
 | 
				
			||||||
 | 
					                    'cpu_shares' => $this->database->limits_cpu_shares,
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            'networks' => [
 | 
				
			||||||
 | 
					                $this->database->destination->network => [
 | 
				
			||||||
 | 
					                    'external' => true,
 | 
				
			||||||
 | 
					                    'name' => $this->database->destination->network,
 | 
				
			||||||
 | 
					                    'attachable' => true,
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					        if (!is_null($this->database->limits_cpuset)) {
 | 
				
			||||||
 | 
					            data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($this->database->destination->server->isLogDrainEnabled() && $this->database->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 (count($this->database->ports_mappings_array) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (count($persistent_storages) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (count($volume_names) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['volumes'] = $volume_names;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $docker_compose = Yaml::dump($docker_compose, 10);
 | 
				
			||||||
 | 
					        $docker_compose_base64 = base64_encode($docker_compose);
 | 
				
			||||||
 | 
					        $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
 | 
				
			||||||
 | 
					        $readme = generate_readme_file($this->database->name, now());
 | 
				
			||||||
 | 
					        $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
 | 
				
			||||||
 | 
					        $this->commands[] = "echo 'Pulling {$database->image} image.'";
 | 
				
			||||||
 | 
					        $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
 | 
				
			||||||
 | 
					        $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
 | 
				
			||||||
 | 
					        $this->commands[] = "echo 'Database started.'";
 | 
				
			||||||
 | 
					        return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function generate_local_persistent_volumes()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $local_persistent_volumes = [];
 | 
				
			||||||
 | 
					        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
				
			||||||
 | 
					            if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
 | 
				
			||||||
 | 
					                $local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $volume_name = $persistentStorage->name;
 | 
				
			||||||
 | 
					                $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return $local_persistent_volumes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function generate_local_persistent_volumes_only_volume_names()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $local_persistent_volumes_names = [];
 | 
				
			||||||
 | 
					        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
				
			||||||
 | 
					            if ($persistentStorage->host_path) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $name = $persistentStorage->name;
 | 
				
			||||||
 | 
					            $local_persistent_volumes_names[$name] = [
 | 
				
			||||||
 | 
					                'name' => $name,
 | 
				
			||||||
 | 
					                'external' => false,
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return $local_persistent_volumes_names;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function generate_environment_variables()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $environment_variables = collect();
 | 
				
			||||||
 | 
					        foreach ($this->database->runtime_environment_variables as $env) {
 | 
				
			||||||
 | 
					            $environment_variables->push("$env->key=$env->real_value");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('CLICKHOUSE_ADMIN_USER'))->isEmpty()) {
 | 
				
			||||||
 | 
					            $environment_variables->push("CLICKHOUSE_ADMIN_USER={$this->database->clickhouse_admin_user}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('CLICKHOUSE_ADMIN_PASSWORD'))->isEmpty()) {
 | 
				
			||||||
 | 
					            $environment_variables->push("CLICKHOUSE_ADMIN_PASSWORD={$this->database->clickhouse_admin_password}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $environment_variables->all();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,6 +3,9 @@
 | 
				
			|||||||
namespace App\Actions\Database;
 | 
					namespace App\Actions\Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Models\ServiceDatabase;
 | 
					use App\Models\ServiceDatabase;
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
use App\Models\StandaloneMongodb;
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
use App\Models\StandaloneMysql;
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
@@ -15,7 +18,7 @@ class StartDatabaseProxy
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    use AsAction;
 | 
					    use AsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
 | 
					    public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|ServiceDatabase $database)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $internalPort = null;
 | 
					        $internalPort = null;
 | 
				
			||||||
        $type = $database->getMorphClass();
 | 
					        $type = $database->getMorphClass();
 | 
				
			||||||
@@ -50,6 +53,18 @@ class StartDatabaseProxy
 | 
				
			|||||||
                    $type = 'App\Models\StandaloneRedis';
 | 
					                    $type = 'App\Models\StandaloneRedis';
 | 
				
			||||||
                    $containerName = "redis-{$database->service->uuid}";
 | 
					                    $containerName = "redis-{$database->service->uuid}";
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
					                case 'standalone-keydb':
 | 
				
			||||||
 | 
					                    $type = 'App\Models\StandaloneKeydb';
 | 
				
			||||||
 | 
					                    $containerName = "keydb-{$database->service->uuid}";
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case 'standalone-dragonfly':
 | 
				
			||||||
 | 
					                    $type = 'App\Models\StandaloneDragonfly';
 | 
				
			||||||
 | 
					                    $containerName = "dragonfly-{$database->service->uuid}";
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case 'standalone-clickhouse':
 | 
				
			||||||
 | 
					                    $type = 'App\Models\StandaloneClickhouse';
 | 
				
			||||||
 | 
					                    $containerName = "clickhouse-{$database->service->uuid}";
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if ($type === 'App\Models\StandaloneRedis') {
 | 
					        if ($type === 'App\Models\StandaloneRedis') {
 | 
				
			||||||
@@ -62,6 +77,12 @@ class StartDatabaseProxy
 | 
				
			|||||||
            $internalPort = 3306;
 | 
					            $internalPort = 3306;
 | 
				
			||||||
        } else if ($type === 'App\Models\StandaloneMariadb') {
 | 
					        } else if ($type === 'App\Models\StandaloneMariadb') {
 | 
				
			||||||
            $internalPort = 3306;
 | 
					            $internalPort = 3306;
 | 
				
			||||||
 | 
					        } else if ($type === 'App\Models\StandaloneKeydb') {
 | 
				
			||||||
 | 
					            $internalPort = 6379;
 | 
				
			||||||
 | 
					        } else if ($type === 'App\Models\StandaloneDragonfly') {
 | 
				
			||||||
 | 
					            $internalPort = 6379;
 | 
				
			||||||
 | 
					        } else if ($type === 'App\Models\StandaloneClickhouse') {
 | 
				
			||||||
 | 
					            $internalPort = 9000;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $configuration_dir = database_proxy_dir($database->uuid);
 | 
					        $configuration_dir = database_proxy_dir($database->uuid);
 | 
				
			||||||
        $nginxconf = <<<EOF
 | 
					        $nginxconf = <<<EOF
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										156
									
								
								app/Actions/Database/StartDragonfly.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								app/Actions/Database/StartDragonfly.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,156 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Actions\Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					use Symfony\Component\Yaml\Yaml;
 | 
				
			||||||
 | 
					use Lorisleiva\Actions\Concerns\AsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StartDragonfly
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use AsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public StandaloneDragonfly $database;
 | 
				
			||||||
 | 
					    public array $commands = [];
 | 
				
			||||||
 | 
					    public string $configuration_dir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function handle(StandaloneDragonfly $database)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->database = $database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $container_name = $this->database->uuid;
 | 
				
			||||||
 | 
					        $this->configuration_dir = database_configuration_dir() . '/' . $container_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->commands = [
 | 
				
			||||||
 | 
					            "echo 'Starting {$database->name}.'",
 | 
				
			||||||
 | 
					            "mkdir -p $this->configuration_dir",
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $persistent_storages = $this->generate_local_persistent_volumes();
 | 
				
			||||||
 | 
					        $volume_names = $this->generate_local_persistent_volumes_only_volume_names();
 | 
				
			||||||
 | 
					        $environment_variables = $this->generate_environment_variables();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $docker_compose = [
 | 
				
			||||||
 | 
					            'version' => '3.8',
 | 
				
			||||||
 | 
					            'services' => [
 | 
				
			||||||
 | 
					                $container_name => [
 | 
				
			||||||
 | 
					                    'image' => $this->database->image,
 | 
				
			||||||
 | 
					                    'command' => $startCommand,
 | 
				
			||||||
 | 
					                    'container_name' => $container_name,
 | 
				
			||||||
 | 
					                    'environment' => $environment_variables,
 | 
				
			||||||
 | 
					                    'restart' => RESTART_MODE,
 | 
				
			||||||
 | 
					                    'networks' => [
 | 
				
			||||||
 | 
					                        $this->database->destination->network,
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'ulimits' => [
 | 
				
			||||||
 | 
					                        'memlock'=> '-1'
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'labels' => [
 | 
				
			||||||
 | 
					                        'coolify.managed' => 'true',
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'healthcheck' => [
 | 
				
			||||||
 | 
					                        'test' => "redis-cli -a {$this->database->dragonfly_password} ping",
 | 
				
			||||||
 | 
					                        'interval' => '5s',
 | 
				
			||||||
 | 
					                        'timeout' => '5s',
 | 
				
			||||||
 | 
					                        'retries' => 10,
 | 
				
			||||||
 | 
					                        'start_period' => '5s'
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'mem_limit' => $this->database->limits_memory,
 | 
				
			||||||
 | 
					                    'memswap_limit' => $this->database->limits_memory_swap,
 | 
				
			||||||
 | 
					                    'mem_swappiness' => $this->database->limits_memory_swappiness,
 | 
				
			||||||
 | 
					                    'mem_reservation' => $this->database->limits_memory_reservation,
 | 
				
			||||||
 | 
					                    'cpus' => (float) $this->database->limits_cpus,
 | 
				
			||||||
 | 
					                    'cpu_shares' => $this->database->limits_cpu_shares,
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            'networks' => [
 | 
				
			||||||
 | 
					                $this->database->destination->network => [
 | 
				
			||||||
 | 
					                    'external' => true,
 | 
				
			||||||
 | 
					                    'name' => $this->database->destination->network,
 | 
				
			||||||
 | 
					                    'attachable' => true,
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					        if (!is_null($this->database->limits_cpuset)) {
 | 
				
			||||||
 | 
					            data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($this->database->destination->server->isLogDrainEnabled() && $this->database->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 (count($this->database->ports_mappings_array) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (count($persistent_storages) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (count($volume_names) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['volumes'] = $volume_names;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $docker_compose = Yaml::dump($docker_compose, 10);
 | 
				
			||||||
 | 
					        $docker_compose_base64 = base64_encode($docker_compose);
 | 
				
			||||||
 | 
					        $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
 | 
				
			||||||
 | 
					        $readme = generate_readme_file($this->database->name, now());
 | 
				
			||||||
 | 
					        $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
 | 
				
			||||||
 | 
					        $this->commands[] = "echo 'Pulling {$database->image} image.'";
 | 
				
			||||||
 | 
					        $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
 | 
				
			||||||
 | 
					        $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
 | 
				
			||||||
 | 
					        $this->commands[] = "echo 'Database started.'";
 | 
				
			||||||
 | 
					        return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function generate_local_persistent_volumes()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $local_persistent_volumes = [];
 | 
				
			||||||
 | 
					        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
				
			||||||
 | 
					            if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
 | 
				
			||||||
 | 
					                $local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $volume_name = $persistentStorage->name;
 | 
				
			||||||
 | 
					                $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return $local_persistent_volumes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function generate_local_persistent_volumes_only_volume_names()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $local_persistent_volumes_names = [];
 | 
				
			||||||
 | 
					        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
				
			||||||
 | 
					            if ($persistentStorage->host_path) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $name = $persistentStorage->name;
 | 
				
			||||||
 | 
					            $local_persistent_volumes_names[$name] = [
 | 
				
			||||||
 | 
					                'name' => $name,
 | 
				
			||||||
 | 
					                'external' => false,
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return $local_persistent_volumes_names;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function generate_environment_variables()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $environment_variables = collect();
 | 
				
			||||||
 | 
					        foreach ($this->database->runtime_environment_variables as $env) {
 | 
				
			||||||
 | 
					            $environment_variables->push("$env->key=$env->real_value");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
 | 
				
			||||||
 | 
					            $environment_variables->push("REDIS_PASSWORD={$this->database->dragonfly_password}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $environment_variables->all();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										174
									
								
								app/Actions/Database/StartKeydb.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								app/Actions/Database/StartKeydb.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,174 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Actions\Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					use Symfony\Component\Yaml\Yaml;
 | 
				
			||||||
 | 
					use Lorisleiva\Actions\Concerns\AsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StartKeydb
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use AsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public StandaloneKeydb $database;
 | 
				
			||||||
 | 
					    public array $commands = [];
 | 
				
			||||||
 | 
					    public string $configuration_dir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function handle(StandaloneKeydb $database)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->database = $database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $startCommand = "keydb-server --requirepass {$this->database->keydb_password} --appendonly yes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $container_name = $this->database->uuid;
 | 
				
			||||||
 | 
					        $this->configuration_dir = database_configuration_dir() . '/' . $container_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->commands = [
 | 
				
			||||||
 | 
					            "echo 'Starting {$database->name}.'",
 | 
				
			||||||
 | 
					            "mkdir -p $this->configuration_dir",
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $persistent_storages = $this->generate_local_persistent_volumes();
 | 
				
			||||||
 | 
					        $volume_names = $this->generate_local_persistent_volumes_only_volume_names();
 | 
				
			||||||
 | 
					        $environment_variables = $this->generate_environment_variables();
 | 
				
			||||||
 | 
					        $this->add_custom_keydb();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $docker_compose = [
 | 
				
			||||||
 | 
					            'version' => '3.8',
 | 
				
			||||||
 | 
					            'services' => [
 | 
				
			||||||
 | 
					                $container_name => [
 | 
				
			||||||
 | 
					                    'image' => $this->database->image,
 | 
				
			||||||
 | 
					                    'command' => $startCommand,
 | 
				
			||||||
 | 
					                    'container_name' => $container_name,
 | 
				
			||||||
 | 
					                    'environment' => $environment_variables,
 | 
				
			||||||
 | 
					                    'restart' => RESTART_MODE,
 | 
				
			||||||
 | 
					                    'networks' => [
 | 
				
			||||||
 | 
					                        $this->database->destination->network,
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'labels' => [
 | 
				
			||||||
 | 
					                        'coolify.managed' => 'true',
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'healthcheck' => [
 | 
				
			||||||
 | 
					                        'test' => "keydb-cli --pass {$this->database->keydb_password} ping",
 | 
				
			||||||
 | 
					                        'interval' => '5s',
 | 
				
			||||||
 | 
					                        'timeout' => '5s',
 | 
				
			||||||
 | 
					                        'retries' => 10,
 | 
				
			||||||
 | 
					                        'start_period' => '5s'
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'mem_limit' => $this->database->limits_memory,
 | 
				
			||||||
 | 
					                    'memswap_limit' => $this->database->limits_memory_swap,
 | 
				
			||||||
 | 
					                    'mem_swappiness' => $this->database->limits_memory_swappiness,
 | 
				
			||||||
 | 
					                    'mem_reservation' => $this->database->limits_memory_reservation,
 | 
				
			||||||
 | 
					                    'cpus' => (float) $this->database->limits_cpus,
 | 
				
			||||||
 | 
					                    'cpu_shares' => $this->database->limits_cpu_shares,
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            'networks' => [
 | 
				
			||||||
 | 
					                $this->database->destination->network => [
 | 
				
			||||||
 | 
					                    'external' => true,
 | 
				
			||||||
 | 
					                    'name' => $this->database->destination->network,
 | 
				
			||||||
 | 
					                    'attachable' => true,
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					        if (!is_null($this->database->limits_cpuset)) {
 | 
				
			||||||
 | 
					            data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($this->database->destination->server->isLogDrainEnabled() && $this->database->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 (count($this->database->ports_mappings_array) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (count($persistent_storages) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (count($volume_names) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['volumes'] = $volume_names;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!is_null($this->database->keydb_conf)) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
				
			||||||
 | 
					                'type' => 'bind',
 | 
				
			||||||
 | 
					                'source' => $this->configuration_dir . '/keydb.conf',
 | 
				
			||||||
 | 
					                'target' => '/etc/keydb/keydb.conf',
 | 
				
			||||||
 | 
					                'read_only' => true,
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['command'] = "keydb-server /etc/keydb/keydb.conf --requirepass {$this->database->keydb_password} --appendonly yes";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $docker_compose = Yaml::dump($docker_compose, 10);
 | 
				
			||||||
 | 
					        $docker_compose_base64 = base64_encode($docker_compose);
 | 
				
			||||||
 | 
					        $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
 | 
				
			||||||
 | 
					        $readme = generate_readme_file($this->database->name, now());
 | 
				
			||||||
 | 
					        $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
 | 
				
			||||||
 | 
					        $this->commands[] = "echo 'Pulling {$database->image} image.'";
 | 
				
			||||||
 | 
					        $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
 | 
				
			||||||
 | 
					        $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
 | 
				
			||||||
 | 
					        $this->commands[] = "echo 'Database started.'";
 | 
				
			||||||
 | 
					        return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function generate_local_persistent_volumes()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $local_persistent_volumes = [];
 | 
				
			||||||
 | 
					        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
				
			||||||
 | 
					            if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
 | 
				
			||||||
 | 
					                $local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $volume_name = $persistentStorage->name;
 | 
				
			||||||
 | 
					                $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return $local_persistent_volumes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function generate_local_persistent_volumes_only_volume_names()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $local_persistent_volumes_names = [];
 | 
				
			||||||
 | 
					        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
				
			||||||
 | 
					            if ($persistentStorage->host_path) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $name = $persistentStorage->name;
 | 
				
			||||||
 | 
					            $local_persistent_volumes_names[$name] = [
 | 
				
			||||||
 | 
					                'name' => $name,
 | 
				
			||||||
 | 
					                'external' => false,
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return $local_persistent_volumes_names;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function generate_environment_variables()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $environment_variables = collect();
 | 
				
			||||||
 | 
					        foreach ($this->database->runtime_environment_variables as $env) {
 | 
				
			||||||
 | 
					            $environment_variables->push("$env->key=$env->real_value");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
 | 
				
			||||||
 | 
					            $environment_variables->push("REDIS_PASSWORD={$this->database->keydb_password}");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $environment_variables->all();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    private function add_custom_keydb()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (is_null($this->database->keydb_conf)) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $filename = 'keydb.conf';
 | 
				
			||||||
 | 
					        Storage::disk('local')->put("tmp/keydb.conf_{$this->database->uuid}", $this->database->keydb_conf);
 | 
				
			||||||
 | 
					        $path = Storage::path("tmp/keydb.conf_{$this->database->uuid}");
 | 
				
			||||||
 | 
					        instant_scp($path, "{$this->configuration_dir}/{$filename}", $this->database->destination->server);
 | 
				
			||||||
 | 
					        Storage::disk('local')->delete("tmp/keydb.conf_{$this->database->uuid}");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -114,8 +114,12 @@ class StartMariadb
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $local_persistent_volumes = [];
 | 
					        $local_persistent_volumes = [];
 | 
				
			||||||
        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
					        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
				
			||||||
            $volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
 | 
					            if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
 | 
				
			||||||
            $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
					                $local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $volume_name = $persistentStorage->name;
 | 
				
			||||||
 | 
					                $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return $local_persistent_volumes;
 | 
					        return $local_persistent_volumes;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -130,8 +130,12 @@ class StartMongodb
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $local_persistent_volumes = [];
 | 
					        $local_persistent_volumes = [];
 | 
				
			||||||
        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
					        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
				
			||||||
            $volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
 | 
					            if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
 | 
				
			||||||
            $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
					                $local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $volume_name = $persistentStorage->name;
 | 
				
			||||||
 | 
					                $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return $local_persistent_volumes;
 | 
					        return $local_persistent_volumes;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -114,8 +114,12 @@ class StartMysql
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $local_persistent_volumes = [];
 | 
					        $local_persistent_volumes = [];
 | 
				
			||||||
        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
					        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
				
			||||||
            $volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
 | 
					            if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
 | 
				
			||||||
            $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
					                $local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $volume_name = $persistentStorage->name;
 | 
				
			||||||
 | 
					                $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return $local_persistent_volumes;
 | 
					        return $local_persistent_volumes;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -136,8 +136,12 @@ class StartPostgresql
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $local_persistent_volumes = [];
 | 
					        $local_persistent_volumes = [];
 | 
				
			||||||
        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
					        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
				
			||||||
            $volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
 | 
					            if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
 | 
				
			||||||
            $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
					                $local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $volume_name = $persistentStorage->name;
 | 
				
			||||||
 | 
					                $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return $local_persistent_volumes;
 | 
					        return $local_persistent_volumes;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -125,8 +125,12 @@ class StartRedis
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $local_persistent_volumes = [];
 | 
					        $local_persistent_volumes = [];
 | 
				
			||||||
        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
					        foreach ($this->database->persistentStorages as $persistentStorage) {
 | 
				
			||||||
            $volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
 | 
					            if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
 | 
				
			||||||
            $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
					                $local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $volume_name = $persistentStorage->name;
 | 
				
			||||||
 | 
					                $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return $local_persistent_volumes;
 | 
					        return $local_persistent_volumes;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Actions\Database;
 | 
					namespace App\Actions\Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Events\DatabaseStatusChanged;
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
use App\Models\StandaloneMongodb;
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
use App\Models\StandaloneMysql;
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
@@ -14,7 +16,7 @@ class StopDatabase
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    use AsAction;
 | 
					    use AsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
 | 
					    public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $server = $database->destination->server;
 | 
					        $server = $database->destination->server;
 | 
				
			||||||
        if (!$server->isFunctional()) {
 | 
					        if (!$server->isFunctional()) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,9 @@
 | 
				
			|||||||
namespace App\Actions\Database;
 | 
					namespace App\Actions\Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Models\ServiceDatabase;
 | 
					use App\Models\ServiceDatabase;
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
use App\Models\StandaloneMongodb;
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
use App\Models\StandaloneMysql;
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
@@ -14,7 +17,7 @@ class StopDatabaseProxy
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    use AsAction;
 | 
					    use AsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
 | 
					    public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|ServiceDatabase|StandaloneDragonfly|StandaloneClickhouse $database)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $server = data_get($database, 'destination.server');
 | 
					        $server = data_get($database, 'destination.server');
 | 
				
			||||||
        $uuid = $database->uuid;
 | 
					        $uuid = $database->uuid;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,9 @@ use App\Models\ScheduledTask;
 | 
				
			|||||||
use App\Models\Service;
 | 
					use App\Models\Service;
 | 
				
			||||||
use App\Models\ServiceApplication;
 | 
					use App\Models\ServiceApplication;
 | 
				
			||||||
use App\Models\ServiceDatabase;
 | 
					use App\Models\ServiceDatabase;
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
use App\Models\StandaloneMongodb;
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
use App\Models\StandaloneMysql;
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
@@ -55,6 +58,33 @@ class CleanupStuckedResources extends Command
 | 
				
			|||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
 | 
					            echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $keydbs = StandaloneKeydb::withTrashed()->whereNotNull('deleted_at')->get();
 | 
				
			||||||
 | 
					            foreach ($keydbs as $keydb) {
 | 
				
			||||||
 | 
					                echo "Deleting stuck keydb: {$keydb->name}\n";
 | 
				
			||||||
 | 
					                $redis->forceDelete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            echo "Error in cleaning stuck keydb: {$e->getMessage()}\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $dragonflies = StandaloneDragonfly::withTrashed()->whereNotNull('deleted_at')->get();
 | 
				
			||||||
 | 
					            foreach ($dragonflies as $dragonfly) {
 | 
				
			||||||
 | 
					                echo "Deleting stuck dragonfly: {$dragonfly->name}\n";
 | 
				
			||||||
 | 
					                $redis->forceDelete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            echo "Error in cleaning stuck dragonfly: {$e->getMessage()}\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $clickhouses = StandaloneClickhouse::withTrashed()->whereNotNull('deleted_at')->get();
 | 
				
			||||||
 | 
					            foreach ($clickhouses as $clickhouse) {
 | 
				
			||||||
 | 
					                echo "Deleting stuck clickhouse: {$clickhouse->name}\n";
 | 
				
			||||||
 | 
					                $redis->forceDelete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            echo "Error in cleaning stuck clickhouse: {$e->getMessage()}\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
 | 
					            $mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
 | 
				
			||||||
            foreach ($mongodbs as $mongodb) {
 | 
					            foreach ($mongodbs as $mongodb) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,7 @@ class Kernel extends ConsoleKernel
 | 
				
			|||||||
            $this->check_scheduled_backups($schedule);
 | 
					            $this->check_scheduled_backups($schedule);
 | 
				
			||||||
            $this->pull_helper_image($schedule);
 | 
					            $this->pull_helper_image($schedule);
 | 
				
			||||||
            $this->check_scheduled_tasks($schedule);
 | 
					            $this->check_scheduled_tasks($schedule);
 | 
				
			||||||
 | 
					            $schedule->command('uploads:clear')->everyTwoMinutes();
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Instance Jobs
 | 
					            // Instance Jobs
 | 
				
			||||||
            $schedule->command('horizon:snapshot')->everyFiveMinutes();
 | 
					            $schedule->command('horizon:snapshot')->everyFiveMinutes();
 | 
				
			||||||
@@ -49,6 +50,7 @@ class Kernel extends ConsoleKernel
 | 
				
			|||||||
            $this->check_scheduled_tasks($schedule);
 | 
					            $this->check_scheduled_tasks($schedule);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $schedule->command('cleanup:database --yes')->daily();
 | 
					            $schedule->command('cleanup:database --yes')->daily();
 | 
				
			||||||
 | 
					            $schedule->command('uploads:clear')->everyTwoMinutes();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private function pull_helper_image($schedule)
 | 
					    private function pull_helper_image($schedule)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Http\Controllers\Api;
 | 
					namespace App\Http\Controllers\Api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Actions\Database\StartClickhouse;
 | 
				
			||||||
 | 
					use App\Actions\Database\StartDragonfly;
 | 
				
			||||||
 | 
					use App\Actions\Database\StartKeydb;
 | 
				
			||||||
use App\Actions\Database\StartMariadb;
 | 
					use App\Actions\Database\StartMariadb;
 | 
				
			||||||
use App\Actions\Database\StartMongodb;
 | 
					use App\Actions\Database\StartMongodb;
 | 
				
			||||||
use App\Actions\Database\StartMysql;
 | 
					use App\Actions\Database\StartMysql;
 | 
				
			||||||
@@ -157,6 +160,24 @@ class Deploy extends Controller
 | 
				
			|||||||
                'started_at' => now(),
 | 
					                'started_at' => now(),
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
            $message = "Database {$resource->name} started.";
 | 
					            $message = "Database {$resource->name} started.";
 | 
				
			||||||
 | 
					        } else if ($type === 'App\Models\StandaloneKeydb') {
 | 
				
			||||||
 | 
					            StartKeydb::run($resource);
 | 
				
			||||||
 | 
					            $resource->update([
 | 
				
			||||||
 | 
					                'started_at' => now(),
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					            $message = "Database {$resource->name} started.";
 | 
				
			||||||
 | 
					        } else if ($type === 'App\Models\StandaloneDragonfly') {
 | 
				
			||||||
 | 
					            StartDragonfly::run($resource);
 | 
				
			||||||
 | 
					            $resource->update([
 | 
				
			||||||
 | 
					                'started_at' => now(),
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					            $message = "Database {$resource->name} started.";
 | 
				
			||||||
 | 
					        } else if ($type === 'App\Models\StandaloneClickhouse') {
 | 
				
			||||||
 | 
					            StartClickhouse::run($resource);
 | 
				
			||||||
 | 
					            $resource->update([
 | 
				
			||||||
 | 
					                'started_at' => now(),
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					            $message = "Database {$resource->name} started.";
 | 
				
			||||||
        } else if ($type === 'App\Models\StandaloneMongodb') {
 | 
					        } else if ($type === 'App\Models\StandaloneMongodb') {
 | 
				
			||||||
            StartMongodb::run($resource);
 | 
					            StartMongodb::run($resource);
 | 
				
			||||||
            $resource->update([
 | 
					            $resource->update([
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										83
									
								
								app/Http/Controllers/UploadController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								app/Http/Controllers/UploadController.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Http\Controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Routing\Controller as BaseController;
 | 
				
			||||||
 | 
					use Illuminate\Http\JsonResponse;
 | 
				
			||||||
 | 
					use Pion\Laravel\ChunkUpload\Exceptions\UploadFailedException;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					use Illuminate\Http\UploadedFile;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
 | 
					use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException;
 | 
				
			||||||
 | 
					use Pion\Laravel\ChunkUpload\Handler\AbstractHandler;
 | 
				
			||||||
 | 
					use Pion\Laravel\ChunkUpload\Handler\HandlerFactory;
 | 
				
			||||||
 | 
					use Pion\Laravel\ChunkUpload\Receiver\FileReceiver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UploadController extends BaseController
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function upload(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $resource = getResourceByUuid(request()->route('databaseUuid'), data_get(auth()->user()->currentTeam(), 'id'));
 | 
				
			||||||
 | 
					        if (is_null($resource)) {
 | 
				
			||||||
 | 
					            return response()->json(['error' => 'You do not have permission for this database'], 500);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $receiver = new FileReceiver("file", $request, HandlerFactory::classFromRequest($request));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($receiver->isUploaded() === false) {
 | 
				
			||||||
 | 
					            throw new UploadMissingFileException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $save = $receiver->receive();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($save->isFinished()) {
 | 
				
			||||||
 | 
					            return $this->saveFile($save->getFile(), $resource);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $handler = $save->handler();
 | 
				
			||||||
 | 
					        return response()->json([
 | 
				
			||||||
 | 
					            "done" => $handler->getPercentageDone(),
 | 
				
			||||||
 | 
					            'status' => true
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // protected function saveFileToS3($file)
 | 
				
			||||||
 | 
					    // {
 | 
				
			||||||
 | 
					    //     $fileName = $this->createFilename($file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     $disk = Storage::disk('s3');
 | 
				
			||||||
 | 
					    //     // It's better to use streaming Streaming (laravel 5.4+)
 | 
				
			||||||
 | 
					    //     $disk->putFileAs('photos', $file, $fileName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     // for older laravel
 | 
				
			||||||
 | 
					    //     // $disk->put($fileName, file_get_contents($file), 'public');
 | 
				
			||||||
 | 
					    //     $mime = str_replace('/', '-', $file->getMimeType());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     // We need to delete the file when uploaded to s3
 | 
				
			||||||
 | 
					    //     unlink($file->getPathname());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     return response()->json([
 | 
				
			||||||
 | 
					    //         'path' => $disk->url($fileName),
 | 
				
			||||||
 | 
					    //         'name' => $fileName,
 | 
				
			||||||
 | 
					    //         'mime_type' => $mime
 | 
				
			||||||
 | 
					    //     ]);
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					    protected function saveFile(UploadedFile $file, $resource)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $mime = str_replace('/', '-', $file->getMimeType());
 | 
				
			||||||
 | 
					        $filePath = "upload/{$resource->uuid}";
 | 
				
			||||||
 | 
					        $finalPath = storage_path("app/" . $filePath);
 | 
				
			||||||
 | 
					        $file->move($finalPath, 'restore');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response()->json([
 | 
				
			||||||
 | 
					            'mime_type' => $mime
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    protected function createFilename(UploadedFile $file)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $extension = $file->getClientOriginalExtension();
 | 
				
			||||||
 | 
					        $filename = str_replace("." . $extension, "", $file->getClientOriginalName()); // Filename without extension
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $filename .= "_" . md5(time()) . "." . $extension;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $filename;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -83,6 +83,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
    private $env_nixpacks_args;
 | 
					    private $env_nixpacks_args;
 | 
				
			||||||
    private $docker_compose;
 | 
					    private $docker_compose;
 | 
				
			||||||
    private $docker_compose_base64;
 | 
					    private $docker_compose_base64;
 | 
				
			||||||
 | 
					    private ?string $env_filename = null;
 | 
				
			||||||
    private ?string $nixpacks_plan = null;
 | 
					    private ?string $nixpacks_plan = null;
 | 
				
			||||||
    private ?string $nixpacks_type = null;
 | 
					    private ?string $nixpacks_type = null;
 | 
				
			||||||
    private string $dockerfile_location = '/Dockerfile';
 | 
					    private string $dockerfile_location = '/Dockerfile';
 | 
				
			||||||
@@ -239,50 +240,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                $this->build_server = $this->server;
 | 
					                $this->build_server = $this->server;
 | 
				
			||||||
                $this->original_server = $this->server;
 | 
					                $this->original_server = $this->server;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if ($this->restart_only && $this->application->build_pack !== 'dockerimage' && $this->application->build_pack !== 'dockerfile') {
 | 
					            $this->decide_what_to_do();
 | 
				
			||||||
                $this->just_restart();
 | 
					 | 
				
			||||||
                if ($this->server->isProxyShouldRun()) {
 | 
					 | 
				
			||||||
                    dispatch(new ContainerStatusJob($this->server));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $this->next(ApplicationDeploymentStatus::FINISHED->value);
 | 
					 | 
				
			||||||
                $this->application->isConfigurationChanged(false);
 | 
					 | 
				
			||||||
                $this->run_post_deployment_command();
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            } else if ($this->pull_request_id !== 0) {
 | 
					 | 
				
			||||||
                $this->deploy_pull_request();
 | 
					 | 
				
			||||||
            } else if ($this->application->dockerfile) {
 | 
					 | 
				
			||||||
                $this->deploy_simple_dockerfile();
 | 
					 | 
				
			||||||
            } else if ($this->application->build_pack === 'dockercompose') {
 | 
					 | 
				
			||||||
                $this->deploy_docker_compose_buildpack();
 | 
					 | 
				
			||||||
            } else if ($this->application->build_pack === 'dockerimage') {
 | 
					 | 
				
			||||||
                $this->deploy_dockerimage_buildpack();
 | 
					 | 
				
			||||||
            } else if ($this->application->build_pack === 'dockerfile') {
 | 
					 | 
				
			||||||
                $this->deploy_dockerfile_buildpack();
 | 
					 | 
				
			||||||
            } else if ($this->application->build_pack === 'static') {
 | 
					 | 
				
			||||||
                $this->deploy_static_buildpack();
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                $this->deploy_nixpacks_buildpack();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if ($this->server->isProxyShouldRun()) {
 | 
					 | 
				
			||||||
                dispatch(new ContainerStatusJob($this->server));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            // Otherwise built image needs to be pushed before from the build server.
 | 
					 | 
				
			||||||
            // ray($this->use_build_server);
 | 
					 | 
				
			||||||
            // if (!$this->use_build_server) {
 | 
					 | 
				
			||||||
            //     if ($this->application->additional_servers->count() > 0) {
 | 
					 | 
				
			||||||
            //         $this->push_to_docker_registry(forceFail: true);
 | 
					 | 
				
			||||||
            //     } else {
 | 
					 | 
				
			||||||
            //         $this->push_to_docker_registry();
 | 
					 | 
				
			||||||
            //     }
 | 
					 | 
				
			||||||
            // }
 | 
					 | 
				
			||||||
            $this->next(ApplicationDeploymentStatus::FINISHED->value);
 | 
					 | 
				
			||||||
            if ($this->pull_request_id !== 0) {
 | 
					 | 
				
			||||||
                if ($this->application->is_github_based()) {
 | 
					 | 
				
			||||||
                    ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            $this->run_post_deployment_command();
 | 
					 | 
				
			||||||
            $this->application->isConfigurationChanged(true);
 | 
					 | 
				
			||||||
        } catch (Exception $e) {
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
            if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
 | 
					            if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
 | 
				
			||||||
                ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR);
 | 
					                ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR);
 | 
				
			||||||
@@ -317,6 +275,43 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
            ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id'));
 | 
					            ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id'));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    private function decide_what_to_do()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->restart_only && $this->application->build_pack !== 'dockerimage' && $this->application->build_pack !== 'dockerfile') {
 | 
				
			||||||
 | 
					            $this->just_restart();
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        } else if ($this->pull_request_id !== 0) {
 | 
				
			||||||
 | 
					            $this->deploy_pull_request();
 | 
				
			||||||
 | 
					        } else if ($this->application->dockerfile) {
 | 
				
			||||||
 | 
					            $this->deploy_simple_dockerfile();
 | 
				
			||||||
 | 
					        } else if ($this->application->build_pack === 'dockercompose') {
 | 
				
			||||||
 | 
					            $this->deploy_docker_compose_buildpack();
 | 
				
			||||||
 | 
					        } else if ($this->application->build_pack === 'dockerimage') {
 | 
				
			||||||
 | 
					            $this->deploy_dockerimage_buildpack();
 | 
				
			||||||
 | 
					        } else if ($this->application->build_pack === 'dockerfile') {
 | 
				
			||||||
 | 
					            $this->deploy_dockerfile_buildpack();
 | 
				
			||||||
 | 
					        } else if ($this->application->build_pack === 'static') {
 | 
				
			||||||
 | 
					            $this->deploy_static_buildpack();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->deploy_nixpacks_buildpack();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $this->post_deployment();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    private function post_deployment()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($this->server->isProxyShouldRun()) {
 | 
				
			||||||
 | 
					            dispatch(new ContainerStatusJob($this->server));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $this->next(ApplicationDeploymentStatus::FINISHED->value);
 | 
				
			||||||
 | 
					        if ($this->pull_request_id !== 0) {
 | 
				
			||||||
 | 
					            if ($this->application->is_github_based()) {
 | 
				
			||||||
 | 
					                ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $this->run_post_deployment_command();
 | 
				
			||||||
 | 
					        $this->application->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    private function deploy_simple_dockerfile()
 | 
					    private function deploy_simple_dockerfile()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->use_build_server) {
 | 
					        if ($this->use_build_server) {
 | 
				
			||||||
@@ -336,7 +331,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        // if (!$this->force_rebuild) {
 | 
					        // if (!$this->force_rebuild) {
 | 
				
			||||||
        //     $this->check_image_locally_or_remotely();
 | 
					        //     $this->check_image_locally_or_remotely();
 | 
				
			||||||
        //     if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
 | 
					        //     if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
 | 
				
			||||||
        //         $this->create_workdir();
 | 
					 | 
				
			||||||
        //         $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
 | 
					        //         $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
 | 
				
			||||||
        //         $this->generate_compose_file();
 | 
					        //         $this->generate_compose_file();
 | 
				
			||||||
        //         $this->push_to_docker_registry();
 | 
					        //         $this->push_to_docker_registry();
 | 
				
			||||||
@@ -477,7 +471,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        if (!$this->force_rebuild) {
 | 
					        if (!$this->force_rebuild) {
 | 
				
			||||||
            $this->check_image_locally_or_remotely();
 | 
					            $this->check_image_locally_or_remotely();
 | 
				
			||||||
            if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
 | 
					            if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
 | 
				
			||||||
                $this->create_workdir();
 | 
					 | 
				
			||||||
                $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
 | 
					                $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
 | 
				
			||||||
                $this->generate_compose_file();
 | 
					                $this->generate_compose_file();
 | 
				
			||||||
                $this->push_to_docker_registry();
 | 
					                $this->push_to_docker_registry();
 | 
				
			||||||
@@ -506,7 +499,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        if (!$this->force_rebuild) {
 | 
					        if (!$this->force_rebuild) {
 | 
				
			||||||
            $this->check_image_locally_or_remotely();
 | 
					            $this->check_image_locally_or_remotely();
 | 
				
			||||||
            if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
 | 
					            if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
 | 
				
			||||||
                $this->create_workdir();
 | 
					 | 
				
			||||||
                $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
 | 
					                $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
 | 
				
			||||||
                $this->generate_compose_file();
 | 
					                $this->generate_compose_file();
 | 
				
			||||||
                ray('pushing to docker registry');
 | 
					                ray('pushing to docker registry');
 | 
				
			||||||
@@ -540,7 +532,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        if (!$this->force_rebuild) {
 | 
					        if (!$this->force_rebuild) {
 | 
				
			||||||
            $this->check_image_locally_or_remotely();
 | 
					            $this->check_image_locally_or_remotely();
 | 
				
			||||||
            if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
 | 
					            if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
 | 
				
			||||||
                $this->create_workdir();
 | 
					 | 
				
			||||||
                $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
 | 
					                $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
 | 
				
			||||||
                $this->generate_compose_file();
 | 
					                $this->generate_compose_file();
 | 
				
			||||||
                $this->push_to_docker_registry();
 | 
					                $this->push_to_docker_registry();
 | 
				
			||||||
@@ -683,7 +674,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    private function just_restart()
 | 
					    private function just_restart()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
 | 
					        $this->application_deployment_queue->addLogEntry("Restarting {$this->customRepository}:{$this->application->git_branch} on {$this->server->name}.");
 | 
				
			||||||
        $this->prepare_builder_image();
 | 
					        $this->prepare_builder_image();
 | 
				
			||||||
        $this->check_git_if_build_needed();
 | 
					        $this->check_git_if_build_needed();
 | 
				
			||||||
        $this->set_base_dir();
 | 
					        $this->set_base_dir();
 | 
				
			||||||
@@ -691,12 +682,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        $this->check_image_locally_or_remotely();
 | 
					        $this->check_image_locally_or_remotely();
 | 
				
			||||||
        if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
 | 
					        if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
 | 
				
			||||||
            $this->application_deployment_queue->addLogEntry("Image found ({$this->production_image_name}) with the same Git Commit SHA. Restarting container.");
 | 
					            $this->application_deployment_queue->addLogEntry("Image found ({$this->production_image_name}) with the same Git Commit SHA. Restarting container.");
 | 
				
			||||||
            $this->create_workdir();
 | 
					 | 
				
			||||||
            $this->generate_compose_file();
 | 
					            $this->generate_compose_file();
 | 
				
			||||||
            $this->rolling_update();
 | 
					            $this->rolling_update();
 | 
				
			||||||
            return;
 | 
					            $this->post_deployment();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->application_deployment_queue->addLogEntry("Image not found ({$this->production_image_name}). Redeploying the application.");
 | 
				
			||||||
 | 
					            $this->restart_only = false;
 | 
				
			||||||
 | 
					            $this->decide_what_to_do();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        throw new RuntimeException('Cannot find image anywhere. Please redeploy the application.');
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private function check_image_locally_or_remotely()
 | 
					    private function check_image_locally_or_remotely()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -716,19 +709,35 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $envs = collect([]);
 | 
					        $envs = collect([]);
 | 
				
			||||||
        if ($this->pull_request_id !== 0) {
 | 
					        if ($this->pull_request_id !== 0) {
 | 
				
			||||||
 | 
					            $this->env_filename = ".env-pr-$this->pull_request_id";
 | 
				
			||||||
            foreach ($this->application->environment_variables_preview as $env) {
 | 
					            foreach ($this->application->environment_variables_preview as $env) {
 | 
				
			||||||
                $envs->push($env->key . '=' . $env->real_value);
 | 
					                $envs->push($env->key . '=' . $env->real_value);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->env_filename = ".env";
 | 
				
			||||||
            foreach ($this->application->environment_variables as $env) {
 | 
					            foreach ($this->application->environment_variables as $env) {
 | 
				
			||||||
                $envs->push($env->key . '=' . $env->real_value);
 | 
					                $envs->push($env->key . '=' . $env->real_value);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if ($envs->isEmpty()) {
 | 
				
			||||||
 | 
					            $this->env_filename = null;
 | 
				
			||||||
 | 
					            $this->execute_remote_command(
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    "command" => "rm -f $this->configuration_dir/{$this->env_filename}",
 | 
				
			||||||
 | 
					                    "hidden" => true,
 | 
				
			||||||
 | 
					                    "ignore_errors" => true
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $envs_base64 = base64_encode($envs->implode("\n"));
 | 
					        $envs_base64 = base64_encode($envs->implode("\n"));
 | 
				
			||||||
        $this->execute_remote_command(
 | 
					        $this->execute_remote_command(
 | 
				
			||||||
            [
 | 
					            [
 | 
				
			||||||
                executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d > $this->workdir/.env")
 | 
					                executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d > $this->workdir/.env")
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                "echo '$envs_base64' | base64 -d > $this->configuration_dir/{$this->env_filename}"
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -868,6 +877,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
            [
 | 
					            [
 | 
				
			||||||
                "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
 | 
					                "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                "command" => "mkdir -p {$this->configuration_dir}"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private function prepare_builder_image()
 | 
					    private function prepare_builder_image()
 | 
				
			||||||
@@ -890,6 +902,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage.");
 | 
					        $this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage.");
 | 
				
			||||||
        $this->execute_remote_command(
 | 
					        $this->execute_remote_command(
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                "command" => "docker rm -f {$this->deployment_uuid}",
 | 
				
			||||||
 | 
					                "ignore_errors" => true,
 | 
				
			||||||
 | 
					                "hidden" => true
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
            [
 | 
					            [
 | 
				
			||||||
                $runCommand,
 | 
					                $runCommand,
 | 
				
			||||||
                "hidden" => true,
 | 
					                "hidden" => true,
 | 
				
			||||||
@@ -993,6 +1010,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                $importCommands, "hidden" => true
 | 
					                $importCommands, "hidden" => true
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					        $this->create_workdir();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function generate_git_import_commands()
 | 
					    private function generate_git_import_commands()
 | 
				
			||||||
@@ -1098,6 +1116,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private function generate_compose_file()
 | 
					    private function generate_compose_file()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        $this->create_workdir();
 | 
				
			||||||
        $ports = $this->application->settings->is_static ? [80] : $this->application->ports_exposes_array;
 | 
					        $ports = $this->application->settings->is_static ? [80] : $this->application->ports_exposes_array;
 | 
				
			||||||
        $onlyPort = null;
 | 
					        $onlyPort = null;
 | 
				
			||||||
        if (count($ports) > 0) {
 | 
					        if (count($ports) > 0) {
 | 
				
			||||||
@@ -1181,6 +1200,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                ]
 | 
					                ]
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					        if ($this->env_filename) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$this->container_name]['env_file'] = [
 | 
				
			||||||
 | 
					                $this->env_filename
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if (!$this->custom_healthcheck_found) {
 | 
					        if (!$this->custom_healthcheck_found) {
 | 
				
			||||||
            $docker_compose['services'][$this->container_name]['healthcheck'] = [
 | 
					            $docker_compose['services'][$this->container_name]['healthcheck'] = [
 | 
				
			||||||
                'test' => [
 | 
					                'test' => [
 | 
				
			||||||
@@ -1334,13 +1358,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        $this->docker_compose = Yaml::dump($docker_compose, 10);
 | 
					        $this->docker_compose = Yaml::dump($docker_compose, 10);
 | 
				
			||||||
        $this->docker_compose_base64 = base64_encode($this->docker_compose);
 | 
					        $this->docker_compose_base64 = base64_encode($this->docker_compose);
 | 
				
			||||||
        $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
 | 
					        $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
 | 
				
			||||||
 | 
					        $this->save_environment_variables();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function generate_local_persistent_volumes()
 | 
					    private function generate_local_persistent_volumes()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $local_persistent_volumes = [];
 | 
					        $local_persistent_volumes = [];
 | 
				
			||||||
        foreach ($this->application->persistentStorages as $persistentStorage) {
 | 
					        foreach ($this->application->persistentStorages as $persistentStorage) {
 | 
				
			||||||
            $volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
 | 
					            if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
 | 
				
			||||||
 | 
					                $volume_name = $persistentStorage->host_path;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $volume_name = $persistentStorage->name;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            if ($this->pull_request_id !== 0) {
 | 
					            if ($this->pull_request_id !== 0) {
 | 
				
			||||||
                $volume_name = $volume_name . '-pr-' . $this->pull_request_id;
 | 
					                $volume_name = $volume_name . '-pr-' . $this->pull_request_id;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,9 @@ use App\Actions\Service\DeleteService;
 | 
				
			|||||||
use App\Actions\Service\StopService;
 | 
					use App\Actions\Service\StopService;
 | 
				
			||||||
use App\Models\Application;
 | 
					use App\Models\Application;
 | 
				
			||||||
use App\Models\Service;
 | 
					use App\Models\Service;
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
use App\Models\StandaloneMongodb;
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
use App\Models\StandaloneMysql;
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
@@ -25,7 +28,7 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource)
 | 
					    public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource, public bool $deleteConfigurations = false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -42,6 +45,9 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                case 'standalone-mongodb':
 | 
					                case 'standalone-mongodb':
 | 
				
			||||||
                case 'standalone-mysql':
 | 
					                case 'standalone-mysql':
 | 
				
			||||||
                case 'standalone-mariadb':
 | 
					                case 'standalone-mariadb':
 | 
				
			||||||
 | 
					                case 'standalone-keydb':
 | 
				
			||||||
 | 
					                case 'standalone-dragonfly':
 | 
				
			||||||
 | 
					                case 'standalone-clickhouse':
 | 
				
			||||||
                    StopDatabase::run($this->resource);
 | 
					                    StopDatabase::run($this->resource);
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case 'service':
 | 
					                case 'service':
 | 
				
			||||||
@@ -49,6 +55,9 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                    DeleteService::run($this->resource);
 | 
					                    DeleteService::run($this->resource);
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if ($this->deleteConfigurations) {
 | 
				
			||||||
 | 
					                $this->resource?->delete_configurations();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            ray($e->getMessage());
 | 
					            ray($e->getMessage());
 | 
				
			||||||
            send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
 | 
					            send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@ class ForcePasswordReset extends Component
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    public function render()
 | 
					    public function render()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return view('livewire.force-password-reset')->layout('layouts.simple');
 | 
					        return view('livewire.force-password-reset');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function submit()
 | 
					    public function submit()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@ class General extends Component
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public $customLabels;
 | 
					    public $customLabels;
 | 
				
			||||||
    public bool $labelsChanged = false;
 | 
					    public bool $labelsChanged = false;
 | 
				
			||||||
    public bool $isConfigurationChanged = false;
 | 
					    public bool $initLoadingCompose = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public ?string $initialDockerComposeLocation = null;
 | 
					    public ?string $initialDockerComposeLocation = null;
 | 
				
			||||||
    public ?string $initialDockerComposePrLocation = null;
 | 
					    public ?string $initialDockerComposePrLocation = null;
 | 
				
			||||||
@@ -123,19 +123,22 @@ class General extends Component
 | 
				
			|||||||
            $this->application->settings->save();
 | 
					            $this->application->settings->save();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
 | 
					        $this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
 | 
				
			||||||
 | 
					 | 
				
			||||||
        $this->ports_exposes = $this->application->ports_exposes;
 | 
					        $this->ports_exposes = $this->application->ports_exposes;
 | 
				
			||||||
        if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
 | 
					               $this->customLabels = $this->application->parseContainerLabels();
 | 
				
			||||||
            $this->application->isConfigurationChanged(true);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        $this->isConfigurationChanged = $this->application->isConfigurationChanged();
 | 
					 | 
				
			||||||
        $this->customLabels = $this->application->parseContainerLabels();
 | 
					 | 
				
			||||||
        if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
 | 
					        if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
 | 
				
			||||||
            $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
 | 
					            $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
 | 
				
			||||||
            $this->application->custom_labels = base64_encode($this->customLabels);
 | 
					            $this->application->custom_labels = base64_encode($this->customLabels);
 | 
				
			||||||
            $this->application->save();
 | 
					            $this->application->save();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->initialDockerComposeLocation = $this->application->docker_compose_location;
 | 
					        $this->initialDockerComposeLocation = $this->application->docker_compose_location;
 | 
				
			||||||
 | 
					        if ($this->application->build_pack === 'dockercompose' && !$this->application->docker_compose_raw) {
 | 
				
			||||||
 | 
					            $this->initLoadingCompose = true;
 | 
				
			||||||
 | 
					            $this->dispatch('info', 'Loading docker compose file...');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
 | 
				
			||||||
 | 
					            $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function instantSave()
 | 
					    public function instantSave()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -154,11 +157,15 @@ class General extends Component
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            ['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
 | 
					            ['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
 | 
				
			||||||
            $this->dispatch('success', 'Docker compose file loaded.');
 | 
					            $this->dispatch('success', 'Docker compose file loaded.');
 | 
				
			||||||
 | 
					            $this->dispatch('compose_loaded');
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            $this->application->docker_compose_location = $this->initialDockerComposeLocation;
 | 
					            $this->application->docker_compose_location = $this->initialDockerComposeLocation;
 | 
				
			||||||
            $this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
 | 
					            $this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
 | 
				
			||||||
            $this->application->save();
 | 
					            $this->application->save();
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            $this->initLoadingCompose = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function generateDomain(string $serviceName)
 | 
					    public function generateDomain(string $serviceName)
 | 
				
			||||||
@@ -307,7 +314,7 @@ class General extends Component
 | 
				
			|||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
        } finally {
 | 
					        } finally {
 | 
				
			||||||
            $this->isConfigurationChanged = $this->application->isConfigurationChanged();
 | 
					            $this->dispatch('configurationChanged');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ class Heading extends Component
 | 
				
			|||||||
        $teamId = auth()->user()->currentTeam()->id;
 | 
					        $teamId = auth()->user()->currentTeam()->id;
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            "echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status',
 | 
					            "echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status',
 | 
				
			||||||
 | 
					            "compose_loaded" => '$refresh',
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
@@ -38,6 +39,7 @@ class Heading extends Component
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($showNotification) $this->dispatch('success', "Success", "Application status updated.");
 | 
					        if ($showNotification) $this->dispatch('success', "Success", "Application status updated.");
 | 
				
			||||||
 | 
					        $this->dispatch('configurationChanged');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function force_deploy_without_cache()
 | 
					    public function force_deploy_without_cache()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,8 @@ class Index extends Component
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public $database;
 | 
					    public $database;
 | 
				
			||||||
    public $s3s;
 | 
					    public $s3s;
 | 
				
			||||||
    public function mount() {
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
 | 
					        $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
 | 
				
			||||||
        if (!$project) {
 | 
					        if (!$project) {
 | 
				
			||||||
            return redirect()->route('dashboard');
 | 
					            return redirect()->route('dashboard');
 | 
				
			||||||
@@ -21,8 +22,13 @@ class Index extends Component
 | 
				
			|||||||
        if (!$database) {
 | 
					        if (!$database) {
 | 
				
			||||||
            return redirect()->route('dashboard');
 | 
					            return redirect()->route('dashboard');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // No backups for redis
 | 
					        // No backups
 | 
				
			||||||
        if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
 | 
					        if (
 | 
				
			||||||
 | 
					            $database->getMorphClass() === 'App\Models\StandaloneRedis' ||
 | 
				
			||||||
 | 
					            $database->getMorphClass() === 'App\Models\StandaloneKeydb' ||
 | 
				
			||||||
 | 
					            $database->getMorphClass() === 'App\Models\StandaloneDragonfly'||
 | 
				
			||||||
 | 
					            $database->getMorphClass() === 'App\Models\StandaloneClickhouse'
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
            return redirect()->route('project.database.configuration', [
 | 
					            return redirect()->route('project.database.configuration', [
 | 
				
			||||||
                'project_uuid' => $project->uuid,
 | 
					                'project_uuid' => $project->uuid,
 | 
				
			||||||
                'environment_name' => $environment->name,
 | 
					                'environment_name' => $environment->name,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use Illuminate\Support\Facades\Storage;
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					use Symfony\Component\HttpFoundation\StreamedResponse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BackupExecutions extends Component
 | 
					class BackupExecutions extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -36,32 +37,9 @@ class BackupExecutions extends Component
 | 
				
			|||||||
        $this->dispatch('success', 'Backup deleted.');
 | 
					        $this->dispatch('success', 'Backup deleted.');
 | 
				
			||||||
        $this->refreshBackupExecutions();
 | 
					        $this->refreshBackupExecutions();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function download($exeuctionId)
 | 
					    public function download_file($exeuctionId)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        return redirect()->route('download.backup', $exeuctionId);
 | 
				
			||||||
            $execution = $this->backup->executions()->where('id', $exeuctionId)->first();
 | 
					 | 
				
			||||||
            if (is_null($execution)) {
 | 
					 | 
				
			||||||
                $this->dispatch('error', 'Backup execution not found.');
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            $filename = data_get($execution, 'filename');
 | 
					 | 
				
			||||||
            if ($execution->scheduledDatabaseBackup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
 | 
					 | 
				
			||||||
                $server = $execution->scheduledDatabaseBackup->database->service->destination->server;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                $server = $execution->scheduledDatabaseBackup->database->destination->server;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            $privateKeyLocation = savePrivateKeyToFs($server);
 | 
					 | 
				
			||||||
            $disk = Storage::build([
 | 
					 | 
				
			||||||
                'driver' => 'sftp',
 | 
					 | 
				
			||||||
                'host' => $server->ip,
 | 
					 | 
				
			||||||
                'port' => $server->port,
 | 
					 | 
				
			||||||
                'username' => $server->user,
 | 
					 | 
				
			||||||
                'privateKey' => $privateKeyLocation,
 | 
					 | 
				
			||||||
            ]);
 | 
					 | 
				
			||||||
            return $disk->download($filename);
 | 
					 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					 | 
				
			||||||
            return handleError($e, $this);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function refreshBackupExecutions(): void
 | 
					    public function refreshBackupExecutions(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										115
									
								
								app/Livewire/Project/Database/Clickhouse/General.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								app/Livewire/Project/Database/Clickhouse/General.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Livewire\Project\Database\Clickhouse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Actions\Database\StartDatabaseProxy;
 | 
				
			||||||
 | 
					use App\Actions\Database\StopDatabaseProxy;
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
 | 
					use Exception;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class General extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public StandaloneClickhouse $database;
 | 
				
			||||||
 | 
					    public ?string $db_url = null;
 | 
				
			||||||
 | 
					    public ?string $db_url_public = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $listeners = ['refresh'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $rules = [
 | 
				
			||||||
 | 
					        'database.name' => 'required',
 | 
				
			||||||
 | 
					        'database.description' => 'nullable',
 | 
				
			||||||
 | 
					        'database.clickhouse_admin_user' => 'required',
 | 
				
			||||||
 | 
					        'database.clickhouse_admin_password' => 'required',
 | 
				
			||||||
 | 
					        'database.image' => 'required',
 | 
				
			||||||
 | 
					        'database.ports_mappings' => 'nullable',
 | 
				
			||||||
 | 
					        'database.is_public' => 'nullable|boolean',
 | 
				
			||||||
 | 
					        'database.public_port' => 'nullable|integer',
 | 
				
			||||||
 | 
					        'database.is_log_drain_enabled' => 'nullable|boolean',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    protected $validationAttributes = [
 | 
				
			||||||
 | 
					        'database.name' => 'Name',
 | 
				
			||||||
 | 
					        'database.description' => 'Description',
 | 
				
			||||||
 | 
					        'database.clickhouse_admin_user' => 'Postgres User',
 | 
				
			||||||
 | 
					        'database.clickhouse_admin_password' => 'Postgres Password',
 | 
				
			||||||
 | 
					        'database.image' => 'Image',
 | 
				
			||||||
 | 
					        'database.ports_mappings' => 'Port Mapping',
 | 
				
			||||||
 | 
					        'database.is_public' => 'Is Public',
 | 
				
			||||||
 | 
					        'database.public_port' => 'Public Port',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->db_url = $this->database->get_db_url(true);
 | 
				
			||||||
 | 
					        if ($this->database->is_public) {
 | 
				
			||||||
 | 
					            $this->db_url_public = $this->database->get_db_url();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function instantSaveAdvanced() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (!$this->database->destination->server->isLogDrainEnabled()) {
 | 
				
			||||||
 | 
					                $this->database->is_log_drain_enabled = false;
 | 
				
			||||||
 | 
					                $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->database->save();
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'Database updated.');
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'You need to restart the service for the changes to take effect.');
 | 
				
			||||||
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function instantSave()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if ($this->database->is_public && !$this->database->public_port) {
 | 
				
			||||||
 | 
					                $this->dispatch('error', 'Public port is required.');
 | 
				
			||||||
 | 
					                $this->database->is_public = false;
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ($this->database->is_public) {
 | 
				
			||||||
 | 
					                if (!str($this->database->status)->startsWith('running')) {
 | 
				
			||||||
 | 
					                    $this->dispatch('error', 'Database must be started to be publicly accessible.');
 | 
				
			||||||
 | 
					                    $this->database->is_public = false;
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                StartDatabaseProxy::run($this->database);
 | 
				
			||||||
 | 
					                $this->db_url_public = $this->database->get_db_url();
 | 
				
			||||||
 | 
					                $this->dispatch('success', 'Database is now publicly accessible.');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                StopDatabaseProxy::run($this->database);
 | 
				
			||||||
 | 
					                $this->db_url_public = null;
 | 
				
			||||||
 | 
					                $this->dispatch('success', 'Database is no longer publicly accessible.');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->database->save();
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            $this->database->is_public = !$this->database->is_public;
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function refresh(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->database->refresh();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function submit()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (str($this->database->public_port)->isEmpty()) {
 | 
				
			||||||
 | 
					                $this->database->public_port = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->validate();
 | 
				
			||||||
 | 
					            $this->database->save();
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'Database updated.');
 | 
				
			||||||
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            if (is_null($this->database->config_hash)) {
 | 
				
			||||||
 | 
					                $this->database->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,7 +7,8 @@ use Livewire\Component;
 | 
				
			|||||||
class Configuration extends Component
 | 
					class Configuration extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public $database;
 | 
					    public $database;
 | 
				
			||||||
    public function mount() {
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
 | 
					        $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
 | 
				
			||||||
        if (!$project) {
 | 
					        if (!$project) {
 | 
				
			||||||
            return redirect()->route('dashboard');
 | 
					            return redirect()->route('dashboard');
 | 
				
			||||||
@@ -21,6 +22,10 @@ class Configuration extends Component
 | 
				
			|||||||
            return redirect()->route('dashboard');
 | 
					            return redirect()->route('dashboard');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->database = $database;
 | 
					        $this->database = $database;
 | 
				
			||||||
 | 
					        if (str($this->database->status)->startsWith('running') && is_null($this->database->config_hash)) {
 | 
				
			||||||
 | 
					            $this->database->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function render()
 | 
					    public function render()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										113
									
								
								app/Livewire/Project/Database/Dragonfly/General.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								app/Livewire/Project/Database/Dragonfly/General.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Livewire\Project\Database\Dragonfly;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Actions\Database\StartDatabaseProxy;
 | 
				
			||||||
 | 
					use App\Actions\Database\StopDatabaseProxy;
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use Exception;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class General extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected $listeners = ['refresh'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public StandaloneDragonfly $database;
 | 
				
			||||||
 | 
					    public ?string $db_url = null;
 | 
				
			||||||
 | 
					    public ?string $db_url_public = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $rules = [
 | 
				
			||||||
 | 
					        'database.name' => 'required',
 | 
				
			||||||
 | 
					        'database.description' => 'nullable',
 | 
				
			||||||
 | 
					        'database.dragonfly_password' => 'required',
 | 
				
			||||||
 | 
					        'database.image' => 'required',
 | 
				
			||||||
 | 
					        'database.ports_mappings' => 'nullable',
 | 
				
			||||||
 | 
					        'database.is_public' => 'nullable|boolean',
 | 
				
			||||||
 | 
					        'database.public_port' => 'nullable|integer',
 | 
				
			||||||
 | 
					        'database.is_log_drain_enabled' => 'nullable|boolean',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    protected $validationAttributes = [
 | 
				
			||||||
 | 
					        'database.name' => 'Name',
 | 
				
			||||||
 | 
					        'database.description' => 'Description',
 | 
				
			||||||
 | 
					        'database.dragonfly_password' => 'Redis Password',
 | 
				
			||||||
 | 
					        'database.image' => 'Image',
 | 
				
			||||||
 | 
					        'database.ports_mappings' => 'Port Mapping',
 | 
				
			||||||
 | 
					        'database.is_public' => 'Is Public',
 | 
				
			||||||
 | 
					        'database.public_port' => 'Public Port',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->db_url = $this->database->get_db_url(true);
 | 
				
			||||||
 | 
					        if ($this->database->is_public) {
 | 
				
			||||||
 | 
					            $this->db_url_public = $this->database->get_db_url();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function instantSaveAdvanced() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (!$this->database->destination->server->isLogDrainEnabled()) {
 | 
				
			||||||
 | 
					                $this->database->is_log_drain_enabled = false;
 | 
				
			||||||
 | 
					                $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->database->save();
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'Database updated.');
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'You need to restart the service for the changes to take effect.');
 | 
				
			||||||
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function submit()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $this->validate();
 | 
				
			||||||
 | 
					            $this->database->save();
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'Database updated.');
 | 
				
			||||||
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            if (is_null($this->database->config_hash)) {
 | 
				
			||||||
 | 
					                $this->database->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function instantSave()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if ($this->database->is_public && !$this->database->public_port) {
 | 
				
			||||||
 | 
					                $this->dispatch('error', 'Public port is required.');
 | 
				
			||||||
 | 
					                $this->database->is_public = false;
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ($this->database->is_public) {
 | 
				
			||||||
 | 
					                if (!str($this->database->status)->startsWith('running')) {
 | 
				
			||||||
 | 
					                    $this->dispatch('error', 'Database must be started to be publicly accessible.');
 | 
				
			||||||
 | 
					                    $this->database->is_public = false;
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                StartDatabaseProxy::run($this->database);
 | 
				
			||||||
 | 
					                $this->db_url_public = $this->database->get_db_url();
 | 
				
			||||||
 | 
					                $this->dispatch('success', 'Database is now publicly accessible.');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                StopDatabaseProxy::run($this->database);
 | 
				
			||||||
 | 
					                $this->db_url_public = null;
 | 
				
			||||||
 | 
					                $this->dispatch('success', 'Database is no longer publicly accessible.');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->database->save();
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            $this->database->is_public = !$this->database->is_public;
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function refresh(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->database->refresh();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return view('livewire.project.database.dragonfly.general');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,13 +2,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Livewire\Project\Database;
 | 
					namespace App\Livewire\Project\Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Actions\Database\StartClickhouse;
 | 
				
			||||||
 | 
					use App\Actions\Database\StartDragonfly;
 | 
				
			||||||
 | 
					use App\Actions\Database\StartKeydb;
 | 
				
			||||||
use App\Actions\Database\StartMariadb;
 | 
					use App\Actions\Database\StartMariadb;
 | 
				
			||||||
use App\Actions\Database\StartMongodb;
 | 
					use App\Actions\Database\StartMongodb;
 | 
				
			||||||
use App\Actions\Database\StartMysql;
 | 
					use App\Actions\Database\StartMysql;
 | 
				
			||||||
use App\Actions\Database\StartPostgresql;
 | 
					use App\Actions\Database\StartPostgresql;
 | 
				
			||||||
use App\Actions\Database\StartRedis;
 | 
					use App\Actions\Database\StartRedis;
 | 
				
			||||||
use App\Actions\Database\StopDatabase;
 | 
					use App\Actions\Database\StopDatabase;
 | 
				
			||||||
use App\Events\DatabaseStatusChanged;
 | 
					 | 
				
			||||||
use App\Jobs\ContainerStatusJob;
 | 
					use App\Jobs\ContainerStatusJob;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,6 +34,12 @@ class Heading extends Component
 | 
				
			|||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
        $this->dispatch('refresh');
 | 
					        $this->dispatch('refresh');
 | 
				
			||||||
        $this->check_status();
 | 
					        $this->check_status();
 | 
				
			||||||
 | 
					        if (is_null($this->database->config_hash) || $this->database->isConfigurationChanged()) {
 | 
				
			||||||
 | 
					            $this->database->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function check_status($showNotification = false)
 | 
					    public function check_status($showNotification = false)
 | 
				
			||||||
@@ -71,6 +79,15 @@ class Heading extends Component
 | 
				
			|||||||
        } else if ($this->database->type() === 'standalone-mariadb') {
 | 
					        } else if ($this->database->type() === 'standalone-mariadb') {
 | 
				
			||||||
            $activity = StartMariadb::run($this->database);
 | 
					            $activity = StartMariadb::run($this->database);
 | 
				
			||||||
            $this->dispatch('activityMonitor', $activity->id);
 | 
					            $this->dispatch('activityMonitor', $activity->id);
 | 
				
			||||||
 | 
					        } else if ($this->database->type() === 'standalone-keydb') {
 | 
				
			||||||
 | 
					            $activity = StartKeydb::run($this->database);
 | 
				
			||||||
 | 
					            $this->dispatch('activityMonitor', $activity->id);
 | 
				
			||||||
 | 
					        } else if ($this->database->type() === 'standalone-dragonfly') {
 | 
				
			||||||
 | 
					            $activity = StartDragonfly::run($this->database);
 | 
				
			||||||
 | 
					            $this->dispatch('activityMonitor', $activity->id);
 | 
				
			||||||
 | 
					        } else if ($this->database->type() === 'standalone-clickhouse') {
 | 
				
			||||||
 | 
					            $activity = StartClickhouse::run($this->database);
 | 
				
			||||||
 | 
					            $this->dispatch('activityMonitor', $activity->id);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,29 +2,25 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Livewire\Project\Database;
 | 
					namespace App\Livewire\Project\Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Exception;
 | 
					 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
use Livewire\WithFileUploads;
 | 
					 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					 | 
				
			||||||
use App\Models\StandaloneMongodb;
 | 
					 | 
				
			||||||
use App\Models\StandaloneMysql;
 | 
					 | 
				
			||||||
use App\Models\StandalonePostgresql;
 | 
					 | 
				
			||||||
use App\Models\StandaloneRedis;
 | 
					 | 
				
			||||||
use Illuminate\Support\Facades\Storage;
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Import extends Component
 | 
					class Import extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    use WithFileUploads;
 | 
					    public bool $unsupported = false;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public $file;
 | 
					 | 
				
			||||||
    public $resource;
 | 
					    public $resource;
 | 
				
			||||||
    public $parameters;
 | 
					    public $parameters;
 | 
				
			||||||
    public $containers;
 | 
					    public $containers;
 | 
				
			||||||
    public bool $validated = true;
 | 
					 | 
				
			||||||
    public bool $scpInProgress = false;
 | 
					    public bool $scpInProgress = false;
 | 
				
			||||||
    public bool $importRunning = false;
 | 
					    public bool $importRunning = false;
 | 
				
			||||||
    public string $validationMsg = '';
 | 
					
 | 
				
			||||||
 | 
					    public ?string $filename = null;
 | 
				
			||||||
 | 
					    public ?string $filesize = null;
 | 
				
			||||||
 | 
					    public bool $isUploading = false;
 | 
				
			||||||
 | 
					    public int $progress = 0;
 | 
				
			||||||
 | 
					    public bool $error = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Server $server;
 | 
					    public Server $server;
 | 
				
			||||||
    public string $container;
 | 
					    public string $container;
 | 
				
			||||||
    public array $importCommands = [];
 | 
					    public array $importCommands = [];
 | 
				
			||||||
@@ -51,22 +47,9 @@ class Import extends Component
 | 
				
			|||||||
        if (!data_get($this->parameters, 'database_uuid')) {
 | 
					        if (!data_get($this->parameters, 'database_uuid')) {
 | 
				
			||||||
            abort(404);
 | 
					            abort(404);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        $resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id'));
 | 
				
			||||||
        $resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
 | 
					 | 
				
			||||||
        if (is_null($resource)) {
 | 
					        if (is_null($resource)) {
 | 
				
			||||||
            $resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
 | 
					            abort(404);
 | 
				
			||||||
            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->resource = $resource;
 | 
				
			||||||
        $this->server = $this->resource->destination->server;
 | 
					        $this->server = $this->resource->destination->server;
 | 
				
			||||||
@@ -75,38 +58,34 @@ class Import extends Component
 | 
				
			|||||||
            $this->containers->push($this->container);
 | 
					            $this->containers->push($this->container);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($this->containers->count() > 1) {
 | 
					 | 
				
			||||||
            $this->validated = false;
 | 
					 | 
				
			||||||
            $this->validationMsg = 'The database service has more than one container running. Cannot import.';
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (
 | 
					        if (
 | 
				
			||||||
            $this->resource->getMorphClass() == 'App\Models\StandaloneRedis'
 | 
					            $this->resource->getMorphClass() == 'App\Models\StandaloneRedis' ||
 | 
				
			||||||
            || $this->resource->getMorphClass() == 'App\Models\StandaloneMongodb'
 | 
					            $this->resource->getMorphClass() == 'App\Models\StandaloneKeydb' ||
 | 
				
			||||||
 | 
					            $this->resource->getMorphClass() == 'App\Models\StandaloneDragonfly' ||
 | 
				
			||||||
 | 
					            $this->resource->getMorphClass() == 'App\Models\StandaloneClickhouse' ||
 | 
				
			||||||
 | 
					            $this->resource->getMorphClass() == 'App\Models\StandaloneMongodb'
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            $this->validated = false;
 | 
					            $this->unsupported = true;
 | 
				
			||||||
            $this->validationMsg = 'This database type is not currently supported.';
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function runImport()
 | 
					    public function runImport()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->validate([
 | 
					 | 
				
			||||||
            'file' => 'required|file|max:102400'
 | 
					 | 
				
			||||||
        ]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $this->importRunning = true;
 | 
					 | 
				
			||||||
        $this->scpInProgress = true;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($this->filename == '') {
 | 
				
			||||||
 | 
					            $this->dispatch('error', 'Please select a file to import.');
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $uploadedFilename = $this->file->store('backup-import');
 | 
					            $uploadedFilename = "upload/{$this->resource->uuid}/restore";
 | 
				
			||||||
            $path = Storage::path($uploadedFilename);
 | 
					            $path = Storage::path($uploadedFilename);
 | 
				
			||||||
 | 
					            if (!Storage::exists($uploadedFilename)) {
 | 
				
			||||||
 | 
					                $this->dispatch('error', 'The file does not exist or has been deleted.');
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            $tmpPath = '/tmp/' . basename($uploadedFilename);
 | 
					            $tmpPath = '/tmp/' . basename($uploadedFilename);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            // SCP the backup file to the server.
 | 
					 | 
				
			||||||
            instant_scp($path, $tmpPath, $this->server);
 | 
					            instant_scp($path, $tmpPath, $this->server);
 | 
				
			||||||
            $this->scpInProgress = false;
 | 
					            Storage::delete($uploadedFilename);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            $this->importCommands[] = "docker cp {$tmpPath} {$this->container}:{$tmpPath}";
 | 
					            $this->importCommands[] = "docker cp {$tmpPath} {$this->container}:{$tmpPath}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            switch ($this->resource->getMorphClass()) {
 | 
					            switch ($this->resource->getMorphClass()) {
 | 
				
			||||||
@@ -132,8 +111,7 @@ class Import extends Component
 | 
				
			|||||||
                $this->dispatch('activityMonitor', $activity->id);
 | 
					                $this->dispatch('activityMonitor', $activity->id);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            $this->validated = false;
 | 
					            return handleError($e, $this);
 | 
				
			||||||
            $this->validationMsg = $e->getMessage();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										117
									
								
								app/Livewire/Project/Database/Keydb/General.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								app/Livewire/Project/Database/Keydb/General.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Livewire\Project\Database\Keydb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Actions\Database\StartDatabaseProxy;
 | 
				
			||||||
 | 
					use App\Actions\Database\StopDatabaseProxy;
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
 | 
					use Exception;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class General extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected $listeners = ['refresh'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public StandaloneKeydb $database;
 | 
				
			||||||
 | 
					    public ?string $db_url = null;
 | 
				
			||||||
 | 
					    public ?string $db_url_public = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $rules = [
 | 
				
			||||||
 | 
					        'database.name' => 'required',
 | 
				
			||||||
 | 
					        'database.description' => 'nullable',
 | 
				
			||||||
 | 
					        'database.keydb_conf' => 'nullable',
 | 
				
			||||||
 | 
					        'database.keydb_password' => 'required',
 | 
				
			||||||
 | 
					        'database.image' => 'required',
 | 
				
			||||||
 | 
					        'database.ports_mappings' => 'nullable',
 | 
				
			||||||
 | 
					        'database.is_public' => 'nullable|boolean',
 | 
				
			||||||
 | 
					        'database.public_port' => 'nullable|integer',
 | 
				
			||||||
 | 
					        'database.is_log_drain_enabled' => 'nullable|boolean',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    protected $validationAttributes = [
 | 
				
			||||||
 | 
					        'database.name' => 'Name',
 | 
				
			||||||
 | 
					        'database.description' => 'Description',
 | 
				
			||||||
 | 
					        'database.keydb_conf' => 'Redis Configuration',
 | 
				
			||||||
 | 
					        'database.keydb_password' => 'Redis Password',
 | 
				
			||||||
 | 
					        'database.image' => 'Image',
 | 
				
			||||||
 | 
					        'database.ports_mappings' => 'Port Mapping',
 | 
				
			||||||
 | 
					        'database.is_public' => 'Is Public',
 | 
				
			||||||
 | 
					        'database.public_port' => 'Public Port',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->db_url = $this->database->get_db_url(true);
 | 
				
			||||||
 | 
					        if ($this->database->is_public) {
 | 
				
			||||||
 | 
					            $this->db_url_public = $this->database->get_db_url();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function instantSaveAdvanced() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (!$this->database->destination->server->isLogDrainEnabled()) {
 | 
				
			||||||
 | 
					                $this->database->is_log_drain_enabled = false;
 | 
				
			||||||
 | 
					                $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->database->save();
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'Database updated.');
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'You need to restart the service for the changes to take effect.');
 | 
				
			||||||
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function submit()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $this->validate();
 | 
				
			||||||
 | 
					            if ($this->database->keydb_conf === "") {
 | 
				
			||||||
 | 
					                $this->database->keydb_conf = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->database->save();
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'Database updated.');
 | 
				
			||||||
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            if (is_null($this->database->config_hash)) {
 | 
				
			||||||
 | 
					                $this->database->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function instantSave()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if ($this->database->is_public && !$this->database->public_port) {
 | 
				
			||||||
 | 
					                $this->dispatch('error', 'Public port is required.');
 | 
				
			||||||
 | 
					                $this->database->is_public = false;
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ($this->database->is_public) {
 | 
				
			||||||
 | 
					                if (!str($this->database->status)->startsWith('running')) {
 | 
				
			||||||
 | 
					                    $this->dispatch('error', 'Database must be started to be publicly accessible.');
 | 
				
			||||||
 | 
					                    $this->database->is_public = false;
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                StartDatabaseProxy::run($this->database);
 | 
				
			||||||
 | 
					                $this->db_url_public = $this->database->get_db_url();
 | 
				
			||||||
 | 
					                $this->dispatch('success', 'Database is now publicly accessible.');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                StopDatabaseProxy::run($this->database);
 | 
				
			||||||
 | 
					                $this->db_url_public = null;
 | 
				
			||||||
 | 
					                $this->dispatch('success', 'Database is no longer publicly accessible.');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->database->save();
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            $this->database->is_public = !$this->database->is_public;
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function refresh(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->database->refresh();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return view('livewire.project.database.keydb.general');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -76,6 +76,12 @@ class General extends Component
 | 
				
			|||||||
            $this->dispatch('success', 'Database updated.');
 | 
					            $this->dispatch('success', 'Database updated.');
 | 
				
			||||||
        } catch (Exception $e) {
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            if (is_null($this->database->config_hash)) {
 | 
				
			||||||
 | 
					                $this->database->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function instantSave()
 | 
					    public function instantSave()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,6 +78,12 @@ class General extends Component
 | 
				
			|||||||
            $this->dispatch('success', 'Database updated.');
 | 
					            $this->dispatch('success', 'Database updated.');
 | 
				
			||||||
        } catch (Exception $e) {
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            if (is_null($this->database->config_hash)) {
 | 
				
			||||||
 | 
					                $this->database->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function instantSave()
 | 
					    public function instantSave()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,6 +77,12 @@ class General extends Component
 | 
				
			|||||||
            $this->dispatch('success', 'Database updated.');
 | 
					            $this->dispatch('success', 'Database updated.');
 | 
				
			||||||
        } catch (Exception $e) {
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            if (is_null($this->database->config_hash)) {
 | 
				
			||||||
 | 
					                $this->database->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function instantSave()
 | 
					    public function instantSave()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,7 +58,8 @@ class General extends Component
 | 
				
			|||||||
            $this->db_url_public = $this->database->get_db_url();
 | 
					            $this->db_url_public = $this->database->get_db_url();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function instantSaveAdvanced() {
 | 
					    public function instantSaveAdvanced()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            if (!$this->database->destination->server->isLogDrainEnabled()) {
 | 
					            if (!$this->database->destination->server->isLogDrainEnabled()) {
 | 
				
			||||||
                $this->database->is_log_drain_enabled = false;
 | 
					                $this->database->is_log_drain_enabled = false;
 | 
				
			||||||
@@ -164,6 +165,12 @@ class General extends Component
 | 
				
			|||||||
            $this->dispatch('success', 'Database updated.');
 | 
					            $this->dispatch('success', 'Database updated.');
 | 
				
			||||||
        } catch (Exception $e) {
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            if (is_null($this->database->config_hash)) {
 | 
				
			||||||
 | 
					                $this->database->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -120,6 +120,9 @@ class Select extends Component
 | 
				
			|||||||
            case 'mysql':
 | 
					            case 'mysql':
 | 
				
			||||||
            case 'mariadb':
 | 
					            case 'mariadb':
 | 
				
			||||||
            case 'redis':
 | 
					            case 'redis':
 | 
				
			||||||
 | 
					            case 'keydb':
 | 
				
			||||||
 | 
					            case 'dragonfly':
 | 
				
			||||||
 | 
					            case 'clickhouse':
 | 
				
			||||||
            case 'mongodb':
 | 
					            case 'mongodb':
 | 
				
			||||||
                $this->isDatabase = true;
 | 
					                $this->isDatabase = true;
 | 
				
			||||||
                $this->includeSwarm = false;
 | 
					                $this->includeSwarm = false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,12 @@ class Create extends Component
 | 
				
			|||||||
                $database = create_standalone_mysql($environment->id, $destination_uuid);
 | 
					                $database = create_standalone_mysql($environment->id, $destination_uuid);
 | 
				
			||||||
            } else if ($type->value() === 'mariadb') {
 | 
					            } else if ($type->value() === 'mariadb') {
 | 
				
			||||||
                $database = create_standalone_mariadb($environment->id, $destination_uuid);
 | 
					                $database = create_standalone_mariadb($environment->id, $destination_uuid);
 | 
				
			||||||
 | 
					            } else if ($type->value() === 'keydb') {
 | 
				
			||||||
 | 
					                $database = create_standalone_keydb($environment->id, $destination_uuid);
 | 
				
			||||||
 | 
					            } else if ($type->value() === 'dragonfly') {
 | 
				
			||||||
 | 
					                $database = create_standalone_dragonfly($environment->id, $destination_uuid);
 | 
				
			||||||
 | 
					            }else if ($type->value() === 'clickhouse') {
 | 
				
			||||||
 | 
					                $database = create_standalone_clickhouse($environment->id, $destination_uuid);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return redirect()->route('project.database.configuration', [
 | 
					            return redirect()->route('project.database.configuration', [
 | 
				
			||||||
                'project_uuid' => $project->uuid,
 | 
					                'project_uuid' => $project->uuid,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,9 @@ class Index extends Component
 | 
				
			|||||||
    public $mongodbs = [];
 | 
					    public $mongodbs = [];
 | 
				
			||||||
    public $mysqls = [];
 | 
					    public $mysqls = [];
 | 
				
			||||||
    public $mariadbs = [];
 | 
					    public $mariadbs = [];
 | 
				
			||||||
 | 
					    public $keydbs = [];
 | 
				
			||||||
 | 
					    public $dragonflies = [];
 | 
				
			||||||
 | 
					    public $clickhouses = [];
 | 
				
			||||||
    public $services = [];
 | 
					    public $services = [];
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -96,6 +99,39 @@ class Index extends Component
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            return $mariadb;
 | 
					            return $mariadb;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        $this->keydbs = $this->environment->keydbs->load(['tags'])->sortBy('name');
 | 
				
			||||||
 | 
					        $this->keydbs = $this->keydbs->map(function ($keydb) {
 | 
				
			||||||
 | 
					            if (data_get($keydb, 'environment.project.uuid')) {
 | 
				
			||||||
 | 
					                $keydb->hrefLink = route('project.database.configuration', [
 | 
				
			||||||
 | 
					                    'project_uuid' => data_get($keydb, 'environment.project.uuid'),
 | 
				
			||||||
 | 
					                    'environment_name' => data_get($keydb, 'environment.name'),
 | 
				
			||||||
 | 
					                    'database_uuid' => data_get($keydb, 'uuid')
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return $keydb;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        $this->dragonflies = $this->environment->dragonflies->load(['tags'])->sortBy('name');
 | 
				
			||||||
 | 
					        $this->dragonflies = $this->dragonflies->map(function ($dragonfly) {
 | 
				
			||||||
 | 
					            if (data_get($dragonfly, 'environment.project.uuid')) {
 | 
				
			||||||
 | 
					                $dragonfly->hrefLink = route('project.database.configuration', [
 | 
				
			||||||
 | 
					                    'project_uuid' => data_get($dragonfly, 'environment.project.uuid'),
 | 
				
			||||||
 | 
					                    'environment_name' => data_get($dragonfly, 'environment.name'),
 | 
				
			||||||
 | 
					                    'database_uuid' => data_get($dragonfly, 'uuid')
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return $dragonfly;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        $this->clickhouses = $this->environment->clickhouses->load(['tags'])->sortBy('name');
 | 
				
			||||||
 | 
					        $this->clickhouses = $this->clickhouses->map(function ($clickhouse) {
 | 
				
			||||||
 | 
					            if (data_get($clickhouse, 'environment.project.uuid')) {
 | 
				
			||||||
 | 
					                $clickhouse->hrefLink = route('project.database.configuration', [
 | 
				
			||||||
 | 
					                    'project_uuid' => data_get($clickhouse, 'environment.project.uuid'),
 | 
				
			||||||
 | 
					                    'environment_name' => data_get($clickhouse, 'environment.name'),
 | 
				
			||||||
 | 
					                    'database_uuid' => data_get($clickhouse, 'uuid')
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return $clickhouse;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        $this->services = $this->environment->services->load(['tags'])->sortBy('name');
 | 
					        $this->services = $this->environment->services->load(['tags'])->sortBy('name');
 | 
				
			||||||
        $this->services = $this->services->map(function ($service) {
 | 
					        $this->services = $this->services->map(function ($service) {
 | 
				
			||||||
            if (data_get($service, 'environment.project.uuid')) {
 | 
					            if (data_get($service, 'environment.project.uuid')) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,8 @@ class Configuration extends Component
 | 
				
			|||||||
        $userId = auth()->user()->id;
 | 
					        $userId = auth()->user()->id;
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            "echo-private:user.{$userId},ServiceStatusChanged" => 'check_status',
 | 
					            "echo-private:user.{$userId},ServiceStatusChanged" => 'check_status',
 | 
				
			||||||
            "check_status"
 | 
					            "check_status",
 | 
				
			||||||
 | 
					            "refresh" => '$refresh',
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function render()
 | 
					    public function render()
 | 
				
			||||||
@@ -65,7 +66,7 @@ class Configuration extends Component
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
            dispatch_sync(new ContainerStatusJob($this->service->server));
 | 
					            dispatch_sync(new ContainerStatusJob($this->service->server));
 | 
				
			||||||
            $this->dispatch('refresh')->self();
 | 
					            $this->dispatch('refresh')->self();
 | 
				
			||||||
            $this->dispatch('serviceStatusChanged');
 | 
					            $this->dispatch('updateStatus');
 | 
				
			||||||
        } catch (\Exception $e) {
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ class EditCompose extends Component
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function saveEditedCompose() {
 | 
					    public function saveEditedCompose() {
 | 
				
			||||||
        $this->dispatch('warning', "Saving new docker compose...");
 | 
					        $this->dispatch('info', "Saving new docker compose...");
 | 
				
			||||||
        $this->dispatch('saveCompose', $this->service->docker_compose_raw);
 | 
					        $this->dispatch('saveCompose', $this->service->docker_compose_raw);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function render()
 | 
					    public function render()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										53
									
								
								app/Livewire/Project/Service/EditDomain.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								app/Livewire/Project/Service/EditDomain.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Livewire\Project\Service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\ServiceApplication;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EditDomain extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public $applicationId;
 | 
				
			||||||
 | 
					    public ServiceApplication $application;
 | 
				
			||||||
 | 
					    protected $rules = [
 | 
				
			||||||
 | 
					        'application.fqdn' => 'nullable',
 | 
				
			||||||
 | 
					        'application.required_fqdn' => 'required|boolean',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    public function mount() {
 | 
				
			||||||
 | 
					        $this->application = ServiceApplication::find($this->applicationId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function updatedApplicationFqdn()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
 | 
				
			||||||
 | 
					        $this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
 | 
				
			||||||
 | 
					        $this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
 | 
				
			||||||
 | 
					            return str($domain)->trim()->lower();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        $this->application->fqdn = $this->application->fqdn->unique()->implode(',');
 | 
				
			||||||
 | 
					        $this->application->save();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function submit()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            check_domain_usage(resource: $this->application);
 | 
				
			||||||
 | 
					            $this->validate();
 | 
				
			||||||
 | 
					            $this->application->save();
 | 
				
			||||||
 | 
					            updateCompose($this->application);
 | 
				
			||||||
 | 
					            if (str($this->application->fqdn)->contains(',')) {
 | 
				
			||||||
 | 
					                $this->dispatch('warning', 'Some services do not support multiple domains, which can lead to problems and is NOT RECOMMENDED.<br><br>Only use multiple domains if you know what you are doing.');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->dispatch('success', 'Service saved.');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            $this->dispatch('generateDockerCompose');
 | 
				
			||||||
 | 
					            $this->dispatch('refresh');
 | 
				
			||||||
 | 
					            $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return view('livewire.project.service.edit-domain');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,13 +5,14 @@ namespace App\Livewire\Project\Service;
 | 
				
			|||||||
use App\Models\LocalFileVolume;
 | 
					use App\Models\LocalFileVolume;
 | 
				
			||||||
use App\Models\ServiceApplication;
 | 
					use App\Models\ServiceApplication;
 | 
				
			||||||
use App\Models\ServiceDatabase;
 | 
					use App\Models\ServiceDatabase;
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FileStorage extends Component
 | 
					class FileStorage extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public LocalFileVolume $fileStorage;
 | 
					    public LocalFileVolume $fileStorage;
 | 
				
			||||||
    public ServiceApplication|ServiceDatabase $service;
 | 
					    public ServiceApplication|ServiceDatabase|StandaloneClickhouse $resource;
 | 
				
			||||||
    public string $fs_path;
 | 
					    public string $fs_path;
 | 
				
			||||||
    public ?string $workdir = null;
 | 
					    public ?string $workdir = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,14 +24,14 @@ class FileStorage extends Component
 | 
				
			|||||||
    ];
 | 
					    ];
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->service = $this->fileStorage->service;
 | 
					        $this->resource = $this->fileStorage->service;
 | 
				
			||||||
         if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
 | 
					        if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
 | 
				
			||||||
            $this->workdir = $this->service->service->workdir();
 | 
					            $this->workdir = $this->resource->service->workdir();
 | 
				
			||||||
            $this->fs_path = Str::of($this->fileStorage->fs_path)->after('.');
 | 
					            $this->fs_path = Str::of($this->fileStorage->fs_path)->after('.');
 | 
				
			||||||
         } else {
 | 
					        } else {
 | 
				
			||||||
            $this->workdir = null;
 | 
					            $this->workdir = null;
 | 
				
			||||||
            $this->fs_path = $this->fileStorage->fs_path;
 | 
					            $this->fs_path = $this->fileStorage->fs_path;
 | 
				
			||||||
         }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function submit()
 | 
					    public function submit()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,21 +17,33 @@ class Navbar extends Component
 | 
				
			|||||||
    public array $query;
 | 
					    public array $query;
 | 
				
			||||||
    public $isDeploymentProgress = false;
 | 
					    public $isDeploymentProgress = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (str($this->service->status())->contains('running') && is_null($this->service->config_hash)) {
 | 
				
			||||||
 | 
					            ray('isConfigurationChanged init');
 | 
				
			||||||
 | 
					            $this->service->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function getListeners()
 | 
					    public function getListeners()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $userId = auth()->user()->id;
 | 
					        $userId = auth()->user()->id;
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            "echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted',
 | 
					            "echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted',
 | 
				
			||||||
            "serviceStatusChanged"
 | 
					            "updateStatus"=> '$refresh',
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function serviceStarted() {
 | 
					    public function serviceStarted()
 | 
				
			||||||
        $this->dispatch('success', 'Service status changed.');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    public function serviceStatusChanged()
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->dispatch('refresh')->self();
 | 
					        $this->dispatch('success', 'Service status changed.');
 | 
				
			||||||
 | 
					        if (is_null($this->service->config_hash) || $this->service->isConfigurationChanged()) {
 | 
				
			||||||
 | 
					            $this->service->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function check_status()
 | 
					    public function check_status()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->dispatch('check_status');
 | 
					        $this->dispatch('check_status');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,6 +69,13 @@ class StackForm extends Component
 | 
				
			|||||||
            $this->dispatch('success', 'Service saved.');
 | 
					            $this->dispatch('success', 'Service saved.');
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            if (is_null($this->service->config_hash)) {
 | 
				
			||||||
 | 
					                ray('asdf');
 | 
				
			||||||
 | 
					                $this->service->isConfigurationChanged(true);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->dispatch('configurationChanged');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function render()
 | 
					    public function render()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								app/Livewire/Project/Shared/ConfigurationChecker.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								app/Livewire/Project/Shared/ConfigurationChecker.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Livewire\Project\Shared;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Application;
 | 
				
			||||||
 | 
					use App\Models\Service;
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
 | 
					use App\Models\StandalonePostgresql;
 | 
				
			||||||
 | 
					use App\Models\StandaloneRedis;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConfigurationChecker extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public bool $isConfigurationChanged = false;
 | 
				
			||||||
 | 
					    public Application|Service|StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource;
 | 
				
			||||||
 | 
					    protected $listeners = ['configurationChanged'];
 | 
				
			||||||
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->configurationChanged();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return view('livewire.project.shared.configuration-checker');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function configurationChanged()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->isConfigurationChanged = $this->resource->isConfigurationChanged();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -11,6 +11,7 @@ class Danger extends Component
 | 
				
			|||||||
    public $resource;
 | 
					    public $resource;
 | 
				
			||||||
    public $projectUuid;
 | 
					    public $projectUuid;
 | 
				
			||||||
    public $environmentName;
 | 
					    public $environmentName;
 | 
				
			||||||
 | 
					    public bool $delete_configurations = true;
 | 
				
			||||||
    public ?string $modalId = null;
 | 
					    public ?string $modalId = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
@@ -20,12 +21,11 @@ class Danger extends Component
 | 
				
			|||||||
        $this->projectUuid = data_get($parameters, 'project_uuid');
 | 
					        $this->projectUuid = data_get($parameters, 'project_uuid');
 | 
				
			||||||
        $this->environmentName = data_get($parameters, 'environment_name');
 | 
					        $this->environmentName = data_get($parameters, 'environment_name');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function delete()
 | 
					    public function delete()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $this->resource->delete();
 | 
					            $this->resource->delete();
 | 
				
			||||||
            DeleteResourceJob::dispatch($this->resource);
 | 
					            DeleteResourceJob::dispatch($this->resource, $this->delete_configurations);
 | 
				
			||||||
            return redirect()->route('project.resource.index', [
 | 
					            return redirect()->route('project.resource.index', [
 | 
				
			||||||
                'project_uuid' => $this->projectUuid,
 | 
					                'project_uuid' => $this->projectUuid,
 | 
				
			||||||
                'environment_name' => $this->environmentName
 | 
					                'environment_name' => $this->environmentName
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -119,6 +119,15 @@ class All extends Component
 | 
				
			|||||||
                    case 'standalone-mariadb':
 | 
					                    case 'standalone-mariadb':
 | 
				
			||||||
                        $environment->standalone_mariadb_id = $this->resource->id;
 | 
					                        $environment->standalone_mariadb_id = $this->resource->id;
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
 | 
					                    case 'standalone-keydb':
 | 
				
			||||||
 | 
					                        $environment->standalone_keydb_id = $this->resource->id;
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    case 'standalone-dragonfly':
 | 
				
			||||||
 | 
					                        $environment->standalone_dragonfly_id = $this->resource->id;
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    case 'standalone-clickhouse':
 | 
				
			||||||
 | 
					                        $environment->standalone_clickhouse_id = $this->resource->id;
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
                    case 'service':
 | 
					                    case 'service':
 | 
				
			||||||
                        $environment->service_id = $this->resource->id;
 | 
					                        $environment->service_id = $this->resource->id;
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
@@ -173,6 +182,15 @@ class All extends Component
 | 
				
			|||||||
                case 'standalone-mariadb':
 | 
					                case 'standalone-mariadb':
 | 
				
			||||||
                    $environment->standalone_mariadb_id = $this->resource->id;
 | 
					                    $environment->standalone_mariadb_id = $this->resource->id;
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
					                case 'standalone-keydb':
 | 
				
			||||||
 | 
					                    $environment->standalone_keydb_id = $this->resource->id;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case 'standalone-dragonfly':
 | 
				
			||||||
 | 
					                    $environment->standalone_dragonfly_id = $this->resource->id;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case 'standalone-clickhouse':
 | 
				
			||||||
 | 
					                    $environment->standalone_clickhouse_id = $this->resource->id;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
                case 'service':
 | 
					                case 'service':
 | 
				
			||||||
                    $environment->service_id = $this->resource->id;
 | 
					                    $environment->service_id = $this->resource->id;
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,11 +5,6 @@ namespace App\Livewire\Project\Shared;
 | 
				
			|||||||
use App\Models\Application;
 | 
					use App\Models\Application;
 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
use App\Models\Service;
 | 
					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 Illuminate\Support\Collection;
 | 
					use Illuminate\Support\Collection;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -50,21 +45,9 @@ class ExecuteContainerCommand extends Component
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        } else if (data_get($this->parameters, 'database_uuid')) {
 | 
					        } else if (data_get($this->parameters, 'database_uuid')) {
 | 
				
			||||||
            $this->type = 'database';
 | 
					            $this->type = 'database';
 | 
				
			||||||
            $resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
 | 
					            $resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(),'id'));
 | 
				
			||||||
            if (is_null($resource)) {
 | 
					            if (is_null($resource)) {
 | 
				
			||||||
                $resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
 | 
					                abort(404);
 | 
				
			||||||
                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->resource = $resource;
 | 
				
			||||||
            if ($this->resource->destination->server->isFunctional()) {
 | 
					            if ($this->resource->destination->server->isFunctional()) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,9 @@ use App\Models\Server;
 | 
				
			|||||||
use App\Models\Service;
 | 
					use App\Models\Service;
 | 
				
			||||||
use App\Models\ServiceApplication;
 | 
					use App\Models\ServiceApplication;
 | 
				
			||||||
use App\Models\ServiceDatabase;
 | 
					use App\Models\ServiceDatabase;
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
use App\Models\StandaloneMongodb;
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
use App\Models\StandaloneMysql;
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
@@ -19,7 +22,7 @@ class GetLogs extends Component
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public string $outputs = '';
 | 
					    public string $outputs = '';
 | 
				
			||||||
    public string $errors = '';
 | 
					    public string $errors = '';
 | 
				
			||||||
    public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|null $resource = null;
 | 
					    public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|null $resource = null;
 | 
				
			||||||
    public ServiceApplication|ServiceDatabase|null $servicesubtype = null;
 | 
					    public ServiceApplication|ServiceDatabase|null $servicesubtype = null;
 | 
				
			||||||
    public Server $server;
 | 
					    public Server $server;
 | 
				
			||||||
    public ?string $container = null;
 | 
					    public ?string $container = null;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,9 @@ namespace App\Livewire\Project\Shared;
 | 
				
			|||||||
use App\Models\Application;
 | 
					use App\Models\Application;
 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
use App\Models\Service;
 | 
					use App\Models\Service;
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
use App\Models\StandaloneMongodb;
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
use App\Models\StandaloneMysql;
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
@@ -16,7 +19,7 @@ use Livewire\Component;
 | 
				
			|||||||
class Logs extends Component
 | 
					class Logs extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public ?string $type = null;
 | 
					    public ?string $type = null;
 | 
				
			||||||
    public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource;
 | 
					    public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource;
 | 
				
			||||||
    public Collection $servers;
 | 
					    public Collection $servers;
 | 
				
			||||||
    public Collection $containers;
 | 
					    public Collection $containers;
 | 
				
			||||||
    public $container = [];
 | 
					    public $container = [];
 | 
				
			||||||
@@ -67,21 +70,9 @@ class Logs extends Component
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            } else if (data_get($this->parameters, 'database_uuid')) {
 | 
					            } else if (data_get($this->parameters, 'database_uuid')) {
 | 
				
			||||||
                $this->type = 'database';
 | 
					                $this->type = 'database';
 | 
				
			||||||
                $resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
 | 
					                $resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id'));
 | 
				
			||||||
                if (is_null($resource)) {
 | 
					                if (is_null($resource)) {
 | 
				
			||||||
                    $resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
 | 
					                    abort(404);
 | 
				
			||||||
                    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->resource = $resource;
 | 
				
			||||||
                $this->status = $this->resource->status;
 | 
					                $this->status = $this->resource->status;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,7 +76,10 @@ class ResourceOperations extends Component
 | 
				
			|||||||
            $this->resource->getMorphClass() === 'App\Models\StandaloneMongodb' ||
 | 
					            $this->resource->getMorphClass() === 'App\Models\StandaloneMongodb' ||
 | 
				
			||||||
            $this->resource->getMorphClass() === 'App\Models\StandaloneMysql' ||
 | 
					            $this->resource->getMorphClass() === 'App\Models\StandaloneMysql' ||
 | 
				
			||||||
            $this->resource->getMorphClass() === 'App\Models\StandaloneMariadb' ||
 | 
					            $this->resource->getMorphClass() === 'App\Models\StandaloneMariadb' ||
 | 
				
			||||||
            $this->resource->getMorphClass() === 'App\Models\StandaloneRedis'
 | 
					            $this->resource->getMorphClass() === 'App\Models\StandaloneRedis' ||
 | 
				
			||||||
 | 
					            $this->resource->getMorphClass() === 'App\Models\StandaloneKeydb' ||
 | 
				
			||||||
 | 
					            $this->resource->getMorphClass() === 'App\Models\StandaloneDragonfly' ||
 | 
				
			||||||
 | 
					            $this->resource->getMorphClass() === 'App\Models\StandaloneClickhouse'
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            $uuid = (string)new Cuid2(7);
 | 
					            $uuid = (string)new Cuid2(7);
 | 
				
			||||||
            $new_resource = $this->resource->replicate()->fill([
 | 
					            $new_resource = $this->resource->replicate()->fill([
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Livewire\Project\Shared\Storages;
 | 
					namespace App\Livewire\Project\Shared\Storages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Models\LocalPersistentVolume;
 | 
					 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class All extends Component
 | 
					class All extends Component
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,6 +75,7 @@ class Form extends Component
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    public function checkLocalhostConnection()
 | 
					    public function checkLocalhostConnection()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        $this->submit();
 | 
				
			||||||
        $uptime = $this->server->validateConnection();
 | 
					        $uptime = $this->server->validateConnection();
 | 
				
			||||||
        if ($uptime) {
 | 
					        if ($uptime) {
 | 
				
			||||||
            $this->dispatch('success', 'Server is reachable.');
 | 
					            $this->dispatch('success', 'Server is reachable.');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,6 +57,15 @@ class Application extends BaseModel
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function delete_configurations()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $server = data_get($this, 'destination.server');
 | 
				
			||||||
 | 
					        $workdir = $this->workdir();
 | 
				
			||||||
 | 
					        if (str($workdir)->endsWith($this->uuid)) {
 | 
				
			||||||
 | 
					            ray('Deleting workdir');
 | 
				
			||||||
 | 
					            instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function additional_servers()
 | 
					    public function additional_servers()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->belongsToMany(Server::class, 'additional_destinations')
 | 
					        return $this->belongsToMany(Server::class, 'additional_destinations')
 | 
				
			||||||
@@ -500,7 +509,7 @@ class Application extends BaseModel
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    public function isConfigurationChanged(bool $save = false)
 | 
					    public function isConfigurationChanged(bool $save = false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command  . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
 | 
					        $newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command  . $this->build_command . $this->start_command . $this->ports_exposes . $this->ports_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels . $this->custom_docker_run_options . $this->dockerfile_target_build;
 | 
				
			||||||
        if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
 | 
					        if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
 | 
				
			||||||
            $newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
 | 
					            $newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,7 +46,18 @@ class Environment extends Model
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->hasMany(StandaloneMariadb::class);
 | 
					        return $this->hasMany(StandaloneMariadb::class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function keydbs()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(StandaloneKeydb::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function dragonflies()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(StandaloneDragonfly::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function clickhouses()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(StandaloneClickhouse::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function databases()
 | 
					    public function databases()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $postgresqls = $this->postgresqls;
 | 
					        $postgresqls = $this->postgresqls;
 | 
				
			||||||
@@ -54,7 +65,10 @@ class Environment extends Model
 | 
				
			|||||||
        $mongodbs = $this->mongodbs;
 | 
					        $mongodbs = $this->mongodbs;
 | 
				
			||||||
        $mysqls = $this->mysqls;
 | 
					        $mysqls = $this->mysqls;
 | 
				
			||||||
        $mariadbs = $this->mariadbs;
 | 
					        $mariadbs = $this->mariadbs;
 | 
				
			||||||
        return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
 | 
					        $keydbs = $this->keydbs;
 | 
				
			||||||
 | 
					        $dragonflies = $this->dragonflies;
 | 
				
			||||||
 | 
					        $clickhouses = $this->clickhouses;
 | 
				
			||||||
 | 
					        return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs)->concat($keydbs)->concat($dragonflies)->concat($clickhouses);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function project()
 | 
					    public function project()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,7 @@ class EnvironmentVariable extends Model
 | 
				
			|||||||
                            'key' => $environment_variable->key,
 | 
					                            'key' => $environment_variable->key,
 | 
				
			||||||
                            'value' => $environment_variable->value,
 | 
					                            'value' => $environment_variable->value,
 | 
				
			||||||
                            'is_build_time' => $environment_variable->is_build_time,
 | 
					                            'is_build_time' => $environment_variable->is_build_time,
 | 
				
			||||||
                            'is_multiline' => $environment_variable->is_multiline,
 | 
					                            'is_multiline' => $environment_variable->is_multiline ?? false,
 | 
				
			||||||
                            'application_id' => $environment_variable->application_id,
 | 
					                            'application_id' => $environment_variable->application_id,
 | 
				
			||||||
                            'is_preview' => true
 | 
					                            'is_preview' => true
 | 
				
			||||||
                        ]);
 | 
					                        ]);
 | 
				
			||||||
@@ -63,19 +63,7 @@ class EnvironmentVariable extends Model
 | 
				
			|||||||
        } else if ($this->service_id) {
 | 
					        } else if ($this->service_id) {
 | 
				
			||||||
            $resource = Service::find($this->service_id);
 | 
					            $resource = Service::find($this->service_id);
 | 
				
			||||||
        } else if ($this->database_id) {
 | 
					        } else if ($this->database_id) {
 | 
				
			||||||
            $resource = StandalonePostgresql::find($this->database_id);
 | 
					            $resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id'));
 | 
				
			||||||
            if (!$resource) {
 | 
					 | 
				
			||||||
                $resource = StandaloneMysql::find($this->database_id);
 | 
					 | 
				
			||||||
                if (!$resource) {
 | 
					 | 
				
			||||||
                    $resource = StandaloneRedis::find($this->database_id);
 | 
					 | 
				
			||||||
                    if (!$resource) {
 | 
					 | 
				
			||||||
                        $resource = StandaloneMongodb::find($this->database_id);
 | 
					 | 
				
			||||||
                        if (!$resource) {
 | 
					 | 
				
			||||||
                            $resource = StandaloneMariadb::find($this->database_id);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return $resource;
 | 
					        return $resource;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,8 +22,14 @@ class LocalFileVolume extends BaseModel
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    public function saveStorageOnServer()
 | 
					    public function saveStorageOnServer()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $workdir = $this->resource->service->workdir();
 | 
					        $isService = data_get($this->resource, 'service');
 | 
				
			||||||
        $server = $this->resource->service->server;
 | 
					        if ($isService) {
 | 
				
			||||||
 | 
					            $workdir = $this->resource->service->workdir();
 | 
				
			||||||
 | 
					            $server = $this->resource->service->server;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $workdir = $this->resource->workdir();
 | 
				
			||||||
 | 
					            $server = $this->resource->destination->server;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $commands = collect([
 | 
					        $commands = collect([
 | 
				
			||||||
            "mkdir -p $workdir > /dev/null 2>&1 || true",
 | 
					            "mkdir -p $workdir > /dev/null 2>&1 || true",
 | 
				
			||||||
            "cd $workdir"
 | 
					            "cd $workdir"
 | 
				
			||||||
@@ -55,8 +61,18 @@ class LocalFileVolume extends BaseModel
 | 
				
			|||||||
        if (!$fileVolume->is_directory && $isDir == 'NOK') {
 | 
					        if (!$fileVolume->is_directory && $isDir == 'NOK') {
 | 
				
			||||||
            if ($content) {
 | 
					            if ($content) {
 | 
				
			||||||
                $content = base64_encode($content);
 | 
					                $content = base64_encode($content);
 | 
				
			||||||
 | 
					                $chmod = $fileVolume->chmod;
 | 
				
			||||||
 | 
					                $chown = $fileVolume->chown;
 | 
				
			||||||
 | 
					                ray($content, $path, $chmod, $chown);
 | 
				
			||||||
                $commands->push("echo '$content' | base64 -d > $path");
 | 
					                $commands->push("echo '$content' | base64 -d > $path");
 | 
				
			||||||
                $commands->push("chmod +x $path");
 | 
					                $commands->push("chmod +x $path");
 | 
				
			||||||
 | 
					                if ($chown) {
 | 
				
			||||||
 | 
					                    $commands->push("chown $chown $path");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ($chmod) {
 | 
				
			||||||
 | 
					                    $commands->push("chmod $chmod $path");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else if ($isDir == 'NOK' && $fileVolume->is_directory) {
 | 
					        } else if ($isDir == 'NOK' && $fileVolume->is_directory) {
 | 
				
			||||||
            $commands->push("mkdir -p $path > /dev/null 2>&1 || true");
 | 
					            $commands->push("mkdir -p $path > /dev/null 2>&1 || true");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,6 +63,18 @@ class Project extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->hasManyThrough(StandaloneRedis::class, Environment::class);
 | 
					        return $this->hasManyThrough(StandaloneRedis::class, Environment::class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function keydbs()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasManyThrough(StandaloneKeydb::class, Environment::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function dragonflies()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasManyThrough(StandaloneDragonfly::class, Environment::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function clickhouses()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasManyThrough(StandaloneClickhouse::class, Environment::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function mongodbs()
 | 
					    public function mongodbs()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->hasManyThrough(StandaloneMongodb::class, Environment::class);
 | 
					        return $this->hasManyThrough(StandaloneMongodb::class, Environment::class);
 | 
				
			||||||
@@ -77,6 +89,6 @@ class Project extends BaseModel
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    public function resource_count()
 | 
					    public function resource_count()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count();
 | 
					        return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count() + $this->keydbs()->count() + $this->dragonflies()->count()  + $this->services()->count() + $this->clickhouses()->count();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -585,7 +585,10 @@ $schema://$host {
 | 
				
			|||||||
            $mongodbs = data_get($standaloneDocker, 'mongodbs', collect([]));
 | 
					            $mongodbs = data_get($standaloneDocker, 'mongodbs', collect([]));
 | 
				
			||||||
            $mysqls = data_get($standaloneDocker, 'mysqls', collect([]));
 | 
					            $mysqls = data_get($standaloneDocker, 'mysqls', collect([]));
 | 
				
			||||||
            $mariadbs = data_get($standaloneDocker, 'mariadbs', collect([]));
 | 
					            $mariadbs = data_get($standaloneDocker, 'mariadbs', collect([]));
 | 
				
			||||||
            return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
 | 
					            $keydbs = data_get($standaloneDocker, 'keydbs', collect([]));
 | 
				
			||||||
 | 
					            $dragonflies = data_get($standaloneDocker, 'dragonflies', collect([]));
 | 
				
			||||||
 | 
					            $clickhouses = data_get($standaloneDocker, 'clickhouses', collect([]));
 | 
				
			||||||
 | 
					            return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs)->concat($keydbs)->concat($dragonflies)->concat($clickhouses);
 | 
				
			||||||
        })->filter(function ($item) {
 | 
					        })->filter(function ($item) {
 | 
				
			||||||
            return data_get($item, 'name') !== 'coolify-db';
 | 
					            return data_get($item, 'name') !== 'coolify-db';
 | 
				
			||||||
        })->flatten();
 | 
					        })->flatten();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,46 @@ class Service extends BaseModel
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    use HasFactory, SoftDeletes;
 | 
					    use HasFactory, SoftDeletes;
 | 
				
			||||||
    protected $guarded = [];
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function isConfigurationChanged(bool $save = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $domains = $this->applications()->get()->pluck('fqdn')->toArray();
 | 
				
			||||||
 | 
					        $domains = implode(',', $domains);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $applicationImages = $this->applications()->get()->pluck('image');
 | 
				
			||||||
 | 
					        $databaseImages = $this->databases()->get()->pluck('image');
 | 
				
			||||||
 | 
					        $images = $applicationImages->merge($databaseImages);
 | 
				
			||||||
 | 
					        $images = implode(',', $images->toArray());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $applicationStorages = $this->applications()->get()->pluck('persistentStorages')->flatten();
 | 
				
			||||||
 | 
					        $databaseStorages = $this->databases()->get()->pluck('persistentStorages')->flatten();
 | 
				
			||||||
 | 
					        $storages = $applicationStorages->merge($databaseStorages)->implode('updated_at');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $newConfigHash =  $images . $domains . $images . $storages;
 | 
				
			||||||
 | 
					        $newConfigHash .= json_encode($this->environment_variables()->get('value'));
 | 
				
			||||||
 | 
					        $newConfigHash = md5($newConfigHash);
 | 
				
			||||||
 | 
					        $oldConfigHash = data_get($this, 'config_hash');
 | 
				
			||||||
 | 
					        if ($oldConfigHash === null) {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($oldConfigHash === $newConfigHash) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isExited()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (bool) str($this->status())->contains('exited');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function type()
 | 
					    public function type()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return 'service';
 | 
					        return 'service';
 | 
				
			||||||
@@ -27,6 +67,14 @@ class Service extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphToMany(Tag::class, 'taggable');
 | 
					        return $this->morphToMany(Tag::class, 'taggable');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function delete_configurations()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $server = data_get($this, 'server');
 | 
				
			||||||
 | 
					        $workdir = $this->workdir();
 | 
				
			||||||
 | 
					        if (str($workdir)->endsWith($this->uuid)) {
 | 
				
			||||||
 | 
					            instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function status()
 | 
					    public function status()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $applications = $this->applications;
 | 
					        $applications = $this->applications;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										210
									
								
								app/Models/StandaloneClickhouse.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								app/Models/StandaloneClickhouse.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,210 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Factories\HasFactory;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Relations\HasMany;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\SoftDeletes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StandaloneClickhouse extends BaseModel
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use HasFactory, SoftDeletes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					    protected $casts = [
 | 
				
			||||||
 | 
					        'clickhouse_password' => 'encrypted',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected static function booted()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static::created(function ($database) {
 | 
				
			||||||
 | 
					            LocalPersistentVolume::create([
 | 
				
			||||||
 | 
					                'name' => 'clickhouse-data-' . $database->uuid,
 | 
				
			||||||
 | 
					                'mount_path' => '/bitnami/clickhouse',
 | 
				
			||||||
 | 
					                'host_path' => null,
 | 
				
			||||||
 | 
					                'resource_id' => $database->id,
 | 
				
			||||||
 | 
					                'resource_type' => $database->getMorphClass(),
 | 
				
			||||||
 | 
					                'is_readonly' => true
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        static::deleting(function ($database) {
 | 
				
			||||||
 | 
					            $storages = $database->persistentStorages()->get();
 | 
				
			||||||
 | 
					            $server = data_get($database, 'destination.server');
 | 
				
			||||||
 | 
					            if ($server) {
 | 
				
			||||||
 | 
					                foreach ($storages as $storage) {
 | 
				
			||||||
 | 
					                    instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $database->scheduledBackups()->delete();
 | 
				
			||||||
 | 
					            $database->persistentStorages()->delete();
 | 
				
			||||||
 | 
					            $database->environment_variables()->delete();
 | 
				
			||||||
 | 
					            $database->tags()->detach();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isConfigurationChanged(bool $save = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $newConfigHash =  $this->image . $this->ports_mappings;
 | 
				
			||||||
 | 
					        $newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
 | 
				
			||||||
 | 
					        $newConfigHash = md5($newConfigHash);
 | 
				
			||||||
 | 
					        $oldConfigHash = data_get($this, 'config_hash');
 | 
				
			||||||
 | 
					        if ($oldConfigHash === null) {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($oldConfigHash === $newConfigHash) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isExited()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (bool) str($this->status)->startsWith('exited');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function workdir()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return database_configuration_dir() . "/{$this->uuid}";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function delete_configurations()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $server = data_get($this, 'destination.server');
 | 
				
			||||||
 | 
					        $workdir = $this->workdir();
 | 
				
			||||||
 | 
					        if (str($workdir)->endsWith($this->uuid)) {
 | 
				
			||||||
 | 
					            instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function realStatus()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->getRawOriginal('status');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function status(): Attribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Attribute::make(
 | 
				
			||||||
 | 
					            set: function ($value) {
 | 
				
			||||||
 | 
					                if (str($value)->contains('(')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before('(')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else if (str($value)->contains(':')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before(':')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after(':')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $status = $value;
 | 
				
			||||||
 | 
					                    $health = 'unhealthy';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return "$status:$health";
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            get: function ($value) {
 | 
				
			||||||
 | 
					                if (str($value)->contains('(')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before('(')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else if (str($value)->contains(':')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before(':')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after(':')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $status = $value;
 | 
				
			||||||
 | 
					                    $health = 'unhealthy';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return "$status:$health";
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function tags()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphToMany(Tag::class, 'taggable');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function project()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return data_get($this, 'environment.project');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function link()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (data_get($this, 'environment.project.uuid')) {
 | 
				
			||||||
 | 
					            return route('project.database.configuration', [
 | 
				
			||||||
 | 
					                'project_uuid' => data_get($this, 'environment.project.uuid'),
 | 
				
			||||||
 | 
					                'environment_name' => data_get($this, 'environment.name'),
 | 
				
			||||||
 | 
					                'database_uuid' => data_get($this, 'uuid')
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isLogDrainEnabled()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return data_get($this, 'is_log_drain_enabled', false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function portsMappings(): Attribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Attribute::make(
 | 
				
			||||||
 | 
					            set: fn ($value) => $value === "" ? null : $value,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function portsMappingsArray(): Attribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Attribute::make(
 | 
				
			||||||
 | 
					            get: fn () => is_null($this->ports_mappings)
 | 
				
			||||||
 | 
					                ? []
 | 
				
			||||||
 | 
					                : explode(',', $this->ports_mappings),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function team()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return data_get($this, 'environment.project.team');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function type(): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return 'standalone-clickhouse';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function get_db_url(bool $useInternal = false): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->is_public && !$useInternal) {
 | 
				
			||||||
 | 
					            return "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->clickhouse_db}";
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->uuid}:9000/{$this->clickhouse_db}";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function environment()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->belongsTo(Environment::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function fileStorages()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(LocalFileVolume::class, 'resource');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function destination()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphTo();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function environment_variables(): HasMany
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(EnvironmentVariable::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function runtime_environment_variables(): HasMany
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(EnvironmentVariable::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function persistentStorages()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(LocalPersistentVolume::class, 'resource');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function scheduledBackups()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -32,6 +32,18 @@ class StandaloneDocker extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(StandaloneMariadb::class, 'destination');
 | 
					        return $this->morphMany(StandaloneMariadb::class, 'destination');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function keydbs()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(StandaloneKeydb::class, 'destination');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function dragonflies()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(StandaloneDragonfly::class, 'destination');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function clickhouses()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(StandaloneClickhouse::class, 'destination');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function server()
 | 
					    public function server()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										210
									
								
								app/Models/StandaloneDragonfly.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								app/Models/StandaloneDragonfly.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,210 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Factories\HasFactory;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Relations\HasMany;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\SoftDeletes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StandaloneDragonfly extends BaseModel
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use HasFactory, SoftDeletes;
 | 
				
			||||||
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					    protected $casts = [
 | 
				
			||||||
 | 
					        'dragonfly_password' => 'encrypted',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected static function booted()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static::created(function ($database) {
 | 
				
			||||||
 | 
					            LocalPersistentVolume::create([
 | 
				
			||||||
 | 
					                'name' => 'dragonfly-data-' . $database->uuid,
 | 
				
			||||||
 | 
					                'mount_path' => '/data',
 | 
				
			||||||
 | 
					                'host_path' => null,
 | 
				
			||||||
 | 
					                'resource_id' => $database->id,
 | 
				
			||||||
 | 
					                'resource_type' => $database->getMorphClass(),
 | 
				
			||||||
 | 
					                'is_readonly' => true
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        static::deleting(function ($database) {
 | 
				
			||||||
 | 
					            $database->scheduledBackups()->delete();
 | 
				
			||||||
 | 
					            $storages = $database->persistentStorages()->get();
 | 
				
			||||||
 | 
					            $server = data_get($database, 'destination.server');
 | 
				
			||||||
 | 
					            if ($server) {
 | 
				
			||||||
 | 
					                foreach ($storages as $storage) {
 | 
				
			||||||
 | 
					                    instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $database->persistentStorages()->delete();
 | 
				
			||||||
 | 
					            $database->environment_variables()->delete();
 | 
				
			||||||
 | 
					            $database->tags()->detach();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isConfigurationChanged(bool $save = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $newConfigHash =  $this->image . $this->ports_mappings;
 | 
				
			||||||
 | 
					        $newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
 | 
				
			||||||
 | 
					        $newConfigHash = md5($newConfigHash);
 | 
				
			||||||
 | 
					        $oldConfigHash = data_get($this, 'config_hash');
 | 
				
			||||||
 | 
					        if ($oldConfigHash === null) {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($oldConfigHash === $newConfigHash) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isExited()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (bool) str($this->status)->startsWith('exited');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function workdir()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return database_configuration_dir() . "/{$this->uuid}";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function delete_configurations()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $server = data_get($this, 'destination.server');
 | 
				
			||||||
 | 
					        $workdir = $this->workdir();
 | 
				
			||||||
 | 
					        if (str($workdir)->endsWith($this->uuid)) {
 | 
				
			||||||
 | 
					            instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function realStatus()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->getRawOriginal('status');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function status(): Attribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Attribute::make(
 | 
				
			||||||
 | 
					            set: function ($value) {
 | 
				
			||||||
 | 
					                if (str($value)->contains('(')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before('(')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else if (str($value)->contains(':')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before(':')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after(':')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $status = $value;
 | 
				
			||||||
 | 
					                    $health = 'unhealthy';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return "$status:$health";
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            get: function ($value) {
 | 
				
			||||||
 | 
					                if (str($value)->contains('(')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before('(')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else if (str($value)->contains(':')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before(':')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after(':')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $status = $value;
 | 
				
			||||||
 | 
					                    $health = 'unhealthy';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return "$status:$health";
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function tags()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphToMany(Tag::class, 'taggable');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function project()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return data_get($this, 'environment.project');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function team()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return data_get($this, 'environment.project.team');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function link()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (data_get($this, 'environment.project.uuid')) {
 | 
				
			||||||
 | 
					            return route('project.database.configuration', [
 | 
				
			||||||
 | 
					                'project_uuid' => data_get($this, 'environment.project.uuid'),
 | 
				
			||||||
 | 
					                'environment_name' => data_get($this, 'environment.name'),
 | 
				
			||||||
 | 
					                'database_uuid' => data_get($this, 'uuid')
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isLogDrainEnabled()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return data_get($this, 'is_log_drain_enabled', false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function portsMappings(): Attribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Attribute::make(
 | 
				
			||||||
 | 
					            set: fn ($value) => $value === "" ? null : $value,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function portsMappingsArray(): Attribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Attribute::make(
 | 
				
			||||||
 | 
					            get: fn () => is_null($this->ports_mappings)
 | 
				
			||||||
 | 
					                ? []
 | 
				
			||||||
 | 
					                : explode(',', $this->ports_mappings),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function type(): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return 'standalone-dragonfly';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function get_db_url(bool $useInternal = false): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->is_public && !$useInternal) {
 | 
				
			||||||
 | 
					            return "redis://{$this->dragonfly_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return "redis://{$this->dragonfly_password}@{$this->uuid}:6379/0";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function environment()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->belongsTo(Environment::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function fileStorages()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(LocalFileVolume::class, 'resource');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function destination()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphTo();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function environment_variables(): HasMany
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(EnvironmentVariable::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function runtime_environment_variables(): HasMany
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(EnvironmentVariable::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function persistentStorages()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(LocalPersistentVolume::class, 'resource');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function scheduledBackups()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										212
									
								
								app/Models/StandaloneKeydb.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								app/Models/StandaloneKeydb.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,212 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Factories\HasFactory;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Relations\HasMany;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\SoftDeletes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StandaloneKeydb extends BaseModel
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use HasFactory, SoftDeletes;
 | 
				
			||||||
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					    protected $casts = [
 | 
				
			||||||
 | 
					        'keydb_password' => 'encrypted',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected static function booted()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static::created(function ($database) {
 | 
				
			||||||
 | 
					            LocalPersistentVolume::create([
 | 
				
			||||||
 | 
					                'name' => 'keydb-data-' . $database->uuid,
 | 
				
			||||||
 | 
					                'mount_path' => '/data',
 | 
				
			||||||
 | 
					                'host_path' => null,
 | 
				
			||||||
 | 
					                'resource_id' => $database->id,
 | 
				
			||||||
 | 
					                'resource_type' => $database->getMorphClass(),
 | 
				
			||||||
 | 
					                'is_readonly' => true
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        static::deleting(function ($database) {
 | 
				
			||||||
 | 
					            $database->scheduledBackups()->delete();
 | 
				
			||||||
 | 
					            $storages = $database->persistentStorages()->get();
 | 
				
			||||||
 | 
					            $server = data_get($database, 'destination.server');
 | 
				
			||||||
 | 
					            if ($server) {
 | 
				
			||||||
 | 
					                foreach ($storages as $storage) {
 | 
				
			||||||
 | 
					                    instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $database->persistentStorages()->delete();
 | 
				
			||||||
 | 
					            $database->environment_variables()->delete();
 | 
				
			||||||
 | 
					            $database->tags()->detach();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isConfigurationChanged(bool $save = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $newConfigHash =  $this->image . $this->ports_mappings . $this->keydb_conf;
 | 
				
			||||||
 | 
					        $newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
 | 
				
			||||||
 | 
					        $newConfigHash = md5($newConfigHash);
 | 
				
			||||||
 | 
					        $oldConfigHash = data_get($this, 'config_hash');
 | 
				
			||||||
 | 
					        if ($oldConfigHash === null) {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($oldConfigHash === $newConfigHash) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isExited()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (bool) str($this->status)->startsWith('exited');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function workdir()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return database_configuration_dir() . "/{$this->uuid}";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function delete_configurations()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $server = data_get($this, 'destination.server');
 | 
				
			||||||
 | 
					        $workdir = $this->workdir();
 | 
				
			||||||
 | 
					        if (str($workdir)->endsWith($this->uuid)) {
 | 
				
			||||||
 | 
					            instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function realStatus()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->getRawOriginal('status');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function status(): Attribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Attribute::make(
 | 
				
			||||||
 | 
					            set: function ($value) {
 | 
				
			||||||
 | 
					                if (str($value)->contains('(')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before('(')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else if (str($value)->contains(':')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before(':')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after(':')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $status = $value;
 | 
				
			||||||
 | 
					                    $health = 'unhealthy';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return "$status:$health";
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            get: function ($value) {
 | 
				
			||||||
 | 
					                if (str($value)->contains('(')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before('(')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else if (str($value)->contains(':')) {
 | 
				
			||||||
 | 
					                    $status = str($value)->before(':')->trim()->value();
 | 
				
			||||||
 | 
					                    $health = str($value)->after(':')->trim()->value() ?? 'unhealthy';
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $status = $value;
 | 
				
			||||||
 | 
					                    $health = 'unhealthy';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return "$status:$health";
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function tags()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphToMany(Tag::class, 'taggable');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function project()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return data_get($this, 'environment.project');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function team()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return data_get($this, 'environment.project.team');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function link()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (data_get($this, 'environment.project.uuid')) {
 | 
				
			||||||
 | 
					            return route('project.database.configuration', [
 | 
				
			||||||
 | 
					                'project_uuid' => data_get($this, 'environment.project.uuid'),
 | 
				
			||||||
 | 
					                'environment_name' => data_get($this, 'environment.name'),
 | 
				
			||||||
 | 
					                'database_uuid' => data_get($this, 'uuid')
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isLogDrainEnabled()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return data_get($this, 'is_log_drain_enabled', false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function portsMappings(): Attribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Attribute::make(
 | 
				
			||||||
 | 
					            set: fn ($value) => $value === "" ? null : $value,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function portsMappingsArray(): Attribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Attribute::make(
 | 
				
			||||||
 | 
					            get: fn () => is_null($this->ports_mappings)
 | 
				
			||||||
 | 
					                ? []
 | 
				
			||||||
 | 
					                : explode(',', $this->ports_mappings),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function type(): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return 'standalone-keydb';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function get_db_url(bool $useInternal = false): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->is_public && !$useInternal) {
 | 
				
			||||||
 | 
					            return "redis://{$this->keydb_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return "redis://{$this->keydb_password}@{$this->uuid}:6379/0";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function environment()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->belongsTo(Environment::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function fileStorages()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(LocalFileVolume::class, 'resource');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function destination()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphTo();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function environment_variables(): HasMany
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(EnvironmentVariable::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function runtime_environment_variables(): HasMany
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(EnvironmentVariable::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function persistentStorages()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(LocalPersistentVolume::class, 'resource');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function scheduledBackups()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -43,6 +43,45 @@ class StandaloneMariadb extends BaseModel
 | 
				
			|||||||
            $database->tags()->detach();
 | 
					            $database->tags()->detach();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function isConfigurationChanged(bool $save = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $newConfigHash =  $this->image . $this->ports_mappings . $this->mariadb_conf;
 | 
				
			||||||
 | 
					        $newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
 | 
				
			||||||
 | 
					        $newConfigHash = md5($newConfigHash);
 | 
				
			||||||
 | 
					        $oldConfigHash = data_get($this, 'config_hash');
 | 
				
			||||||
 | 
					        if ($oldConfigHash === null) {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($oldConfigHash === $newConfigHash) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isExited()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (bool) str($this->status)->startsWith('exited');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function workdir()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return database_configuration_dir() . "/{$this->uuid}";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function delete_configurations()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $server = data_get($this, 'destination.server');
 | 
				
			||||||
 | 
					        $workdir = $this->workdir();
 | 
				
			||||||
 | 
					        if (str($workdir)->endsWith($this->uuid)) {
 | 
				
			||||||
 | 
					            instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function realStatus()
 | 
					    public function realStatus()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
       return $this->getRawOriginal('status');
 | 
					       return $this->getRawOriginal('status');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,6 +46,45 @@ class StandaloneMongodb extends BaseModel
 | 
				
			|||||||
            $database->tags()->detach();
 | 
					            $database->tags()->detach();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function isConfigurationChanged(bool $save = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $newConfigHash =  $this->image . $this->ports_mappings . $this->mongo_conf;
 | 
				
			||||||
 | 
					        $newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
 | 
				
			||||||
 | 
					        $newConfigHash = md5($newConfigHash);
 | 
				
			||||||
 | 
					        $oldConfigHash = data_get($this, 'config_hash');
 | 
				
			||||||
 | 
					        if ($oldConfigHash === null) {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($oldConfigHash === $newConfigHash) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isExited()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (bool) str($this->status)->startsWith('exited');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function workdir()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return database_configuration_dir() . "/{$this->uuid}";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function delete_configurations()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $server = data_get($this, 'destination.server');
 | 
				
			||||||
 | 
					        $workdir = $this->workdir();
 | 
				
			||||||
 | 
					        if (str($workdir)->endsWith($this->uuid)) {
 | 
				
			||||||
 | 
					            instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function realStatus()
 | 
					    public function realStatus()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
       return $this->getRawOriginal('status');
 | 
					       return $this->getRawOriginal('status');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,45 @@ class StandaloneMysql extends BaseModel
 | 
				
			|||||||
            $database->tags()->detach();
 | 
					            $database->tags()->detach();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function isConfigurationChanged(bool $save = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $newConfigHash =  $this->image . $this->ports_mappings . $this->mysql_conf;
 | 
				
			||||||
 | 
					        $newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
 | 
				
			||||||
 | 
					        $newConfigHash = md5($newConfigHash);
 | 
				
			||||||
 | 
					        $oldConfigHash = data_get($this, 'config_hash');
 | 
				
			||||||
 | 
					        if ($oldConfigHash === null) {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($oldConfigHash === $newConfigHash) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isExited()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (bool) str($this->status)->startsWith('exited');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function workdir()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return database_configuration_dir() . "/{$this->uuid}";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function delete_configurations()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $server = data_get($this, 'destination.server');
 | 
				
			||||||
 | 
					        $workdir = $this->workdir();
 | 
				
			||||||
 | 
					        if (str($workdir)->endsWith($this->uuid)) {
 | 
				
			||||||
 | 
					            instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function realStatus()
 | 
					    public function realStatus()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
       return $this->getRawOriginal('status');
 | 
					       return $this->getRawOriginal('status');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,45 @@ class StandalonePostgresql extends BaseModel
 | 
				
			|||||||
            $database->tags()->detach();
 | 
					            $database->tags()->detach();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function workdir()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return database_configuration_dir() . "/{$this->uuid}";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function delete_configurations()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $server = data_get($this, 'destination.server');
 | 
				
			||||||
 | 
					        $workdir = $this->workdir();
 | 
				
			||||||
 | 
					        if (str($workdir)->endsWith($this->uuid)) {
 | 
				
			||||||
 | 
					            instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isConfigurationChanged(bool $save = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $newConfigHash =  $this->image . $this->ports_mappings . $this->postgres_initdb_args . $this->postgres_host_auth_method;
 | 
				
			||||||
 | 
					        $newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
 | 
				
			||||||
 | 
					        $newConfigHash = md5($newConfigHash);
 | 
				
			||||||
 | 
					        $oldConfigHash = data_get($this, 'config_hash');
 | 
				
			||||||
 | 
					        if ($oldConfigHash === null) {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($oldConfigHash === $newConfigHash) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isExited()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (bool) str($this->status)->startsWith('exited');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function realStatus()
 | 
					    public function realStatus()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->getRawOriginal('status');
 | 
					        return $this->getRawOriginal('status');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,6 +38,45 @@ class StandaloneRedis extends BaseModel
 | 
				
			|||||||
            $database->tags()->detach();
 | 
					            $database->tags()->detach();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function isConfigurationChanged(bool $save = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $newConfigHash =  $this->image . $this->ports_mappings . $this->redis_conf;
 | 
				
			||||||
 | 
					        $newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
 | 
				
			||||||
 | 
					        $newConfigHash = md5($newConfigHash);
 | 
				
			||||||
 | 
					        $oldConfigHash = data_get($this, 'config_hash');
 | 
				
			||||||
 | 
					        if ($oldConfigHash === null) {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($oldConfigHash === $newConfigHash) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($save) {
 | 
				
			||||||
 | 
					                $this->config_hash = $newConfigHash;
 | 
				
			||||||
 | 
					                $this->save();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function isExited()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (bool) str($this->status)->startsWith('exited');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function workdir()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return database_configuration_dir() . "/{$this->uuid}";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function delete_configurations()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $server = data_get($this, 'destination.server');
 | 
				
			||||||
 | 
					        $workdir = $this->workdir();
 | 
				
			||||||
 | 
					        if (str($workdir)->endsWith($this->uuid)) {
 | 
				
			||||||
 | 
					            instant_remote_process(["rm -rf " . $this->workdir()], $server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function realStatus()
 | 
					    public function realStatus()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->getRawOriginal('status');
 | 
					        return $this->getRawOriginal('status');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,18 @@ class SwarmDocker extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(StandaloneRedis::class, 'destination');
 | 
					        return $this->morphMany(StandaloneRedis::class, 'destination');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function keydbs()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(StandaloneKeydb::class, 'destination');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function dragonflies()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(StandaloneDragonfly::class, 'destination');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function clickhouses()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(StandaloneClickhouse::class, 'destination');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function mongodbs()
 | 
					    public function mongodbs()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(StandaloneMongodb::class, 'destination');
 | 
					        return $this->morphMany(StandaloneMongodb::class, 'destination');
 | 
				
			||||||
@@ -50,7 +62,10 @@ class SwarmDocker extends BaseModel
 | 
				
			|||||||
        $mongodbs = $this->mongodbs;
 | 
					        $mongodbs = $this->mongodbs;
 | 
				
			||||||
        $mysqls = $this->mysqls;
 | 
					        $mysqls = $this->mysqls;
 | 
				
			||||||
        $mariadbs = $this->mariadbs;
 | 
					        $mariadbs = $this->mariadbs;
 | 
				
			||||||
        return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
 | 
					        $keydbs = $this->keydbs;
 | 
				
			||||||
 | 
					        $dragonflies = $this->dragonflies;
 | 
				
			||||||
 | 
					        $clickhouses = $this->clickhouses;
 | 
				
			||||||
 | 
					        return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs)->concat($keydbs)->concat($dragonflies)->concat($clickhouses);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function attachedTo()
 | 
					    public function attachedTo()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ class Textarea extends Component
 | 
				
			|||||||
        public bool    $required = false,
 | 
					        public bool    $required = false,
 | 
				
			||||||
        public bool    $disabled = false,
 | 
					        public bool    $disabled = false,
 | 
				
			||||||
        public bool    $readonly = false,
 | 
					        public bool    $readonly = false,
 | 
				
			||||||
 | 
					        public bool    $allowTab = false,
 | 
				
			||||||
        public ?string $helper = null,
 | 
					        public ?string $helper = null,
 | 
				
			||||||
        public bool    $realtimeValidation = false,
 | 
					        public bool    $realtimeValidation = false,
 | 
				
			||||||
        public bool    $allowToPeak = true,
 | 
					        public bool    $allowToPeak = true,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const REDACTED = '<REDACTED>';
 | 
					const REDACTED = '<REDACTED>';
 | 
				
			||||||
const DATABASE_TYPES = ['postgresql', 'redis', 'mongodb', 'mysql', 'mariadb'];
 | 
					const DATABASE_TYPES = ['postgresql', 'redis', 'mongodb', 'mysql', 'mariadb', 'keydb', 'dragonfly', 'clickhouse'];
 | 
				
			||||||
const VALID_CRON_STRINGS = [
 | 
					const VALID_CRON_STRINGS = [
 | 
				
			||||||
    'every_minute' => '* * * * *',
 | 
					    'every_minute' => '* * * * *',
 | 
				
			||||||
    'hourly' => '0 * * * *',
 | 
					    'hourly' => '0 * * * *',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,10 @@
 | 
				
			|||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
use App\Models\StandaloneDocker;
 | 
					use App\Models\StandaloneDocker;
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
use App\Models\StandaloneMongodb;
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
use App\Models\StandaloneMysql;
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
@@ -90,6 +93,49 @@ function create_standalone_mariadb($environment_id, $destination_uuid): Standalo
 | 
				
			|||||||
        'destination_type' => $destination->getMorphClass(),
 | 
					        'destination_type' => $destination->getMorphClass(),
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					function create_standalone_keydb($environment_id, $destination_uuid): StandaloneKeydb
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    $destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
 | 
				
			||||||
 | 
					    if (!$destination) {
 | 
				
			||||||
 | 
					        throw new Exception('Destination not found');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return StandaloneKeydb::create([
 | 
				
			||||||
 | 
					        'name' => generate_database_name('keydb'),
 | 
				
			||||||
 | 
					        'keydb_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
 | 
				
			||||||
 | 
					        'environment_id' => $environment_id,
 | 
				
			||||||
 | 
					        'destination_id' => $destination->id,
 | 
				
			||||||
 | 
					        'destination_type' => $destination->getMorphClass(),
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function create_standalone_dragonfly($environment_id, $destination_uuid): StandaloneDragonfly
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    $destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
 | 
				
			||||||
 | 
					    if (!$destination) {
 | 
				
			||||||
 | 
					        throw new Exception('Destination not found');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return StandaloneDragonfly::create([
 | 
				
			||||||
 | 
					        'name' => generate_database_name('dragonfly'),
 | 
				
			||||||
 | 
					        'dragonfly_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
 | 
				
			||||||
 | 
					        'environment_id' => $environment_id,
 | 
				
			||||||
 | 
					        'destination_id' => $destination->id,
 | 
				
			||||||
 | 
					        'destination_type' => $destination->getMorphClass(),
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function create_standalone_clickhouse($environment_id, $destination_uuid): StandaloneClickhouse
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    $destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
 | 
				
			||||||
 | 
					    if (!$destination) {
 | 
				
			||||||
 | 
					        throw new Exception('Destination not found');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return StandaloneClickhouse::create([
 | 
				
			||||||
 | 
					        'name' => generate_database_name('clickhouse'),
 | 
				
			||||||
 | 
					        'clickhouse_admin_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
 | 
				
			||||||
 | 
					        'environment_id' => $environment_id,
 | 
				
			||||||
 | 
					        'destination_id' => $destination->id,
 | 
				
			||||||
 | 
					        'destination_type' => $destination->getMorphClass(),
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Delete file locally on the filesystem.
 | 
					 * Delete file locally on the filesystem.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -582,7 +582,7 @@ function validateComposeFile(string $compose, int $server_id): string|Throwable
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
function escapeEnvVariables($value)
 | 
					function escapeEnvVariables($value)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    $search = array("\\", "\r", "\t", "\x0", '"', "'", "$");
 | 
					    $search = array("\\", "\r", "\t", "\x0", '"', "'");
 | 
				
			||||||
    $replace = array("\\\\", "\\r", "\\t", "\\0", '\"', "\'", "$$");
 | 
					    $replace = array("\\\\", "\\r", "\\t", "\\0", '\"', "\'");
 | 
				
			||||||
    return str_replace($search, $replace, $value);
 | 
					    return str_replace($search, $replace, $value);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,9 @@ use App\Models\Server;
 | 
				
			|||||||
use App\Models\Service;
 | 
					use App\Models\Service;
 | 
				
			||||||
use App\Models\ServiceApplication;
 | 
					use App\Models\ServiceApplication;
 | 
				
			||||||
use App\Models\ServiceDatabase;
 | 
					use App\Models\ServiceDatabase;
 | 
				
			||||||
 | 
					use App\Models\StandaloneClickhouse;
 | 
				
			||||||
 | 
					use App\Models\StandaloneDragonfly;
 | 
				
			||||||
 | 
					use App\Models\StandaloneKeydb;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
use App\Models\StandaloneMongodb;
 | 
					use App\Models\StandaloneMongodb;
 | 
				
			||||||
use App\Models\StandaloneMysql;
 | 
					use App\Models\StandaloneMysql;
 | 
				
			||||||
@@ -463,15 +466,14 @@ function getServiceTemplates()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
function getResourceByUuid(string $uuid, ?int $teamId = null)
 | 
					function getResourceByUuid(string $uuid, ?int $teamId = null)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    $resource = queryResourcesByUuid($uuid);
 | 
					    if (is_null($teamId)) {
 | 
				
			||||||
    if (!is_null($teamId)) {
 | 
					 | 
				
			||||||
        if (!is_null($resource) && $resource->environment->project->team_id === $teamId) {
 | 
					 | 
				
			||||||
            return $resource;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    } else {
 | 
					    }
 | 
				
			||||||
 | 
					    $resource = queryResourcesByUuid($uuid);
 | 
				
			||||||
 | 
					    if (!is_null($resource) && $resource->environment->project->team_id === $teamId) {
 | 
				
			||||||
        return $resource;
 | 
					        return $resource;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function queryResourcesByUuid(string $uuid)
 | 
					function queryResourcesByUuid(string $uuid)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -490,6 +492,12 @@ function queryResourcesByUuid(string $uuid)
 | 
				
			|||||||
    if ($mysql) return $mysql;
 | 
					    if ($mysql) return $mysql;
 | 
				
			||||||
    $mariadb = StandaloneMariadb::whereUuid($uuid)->first();
 | 
					    $mariadb = StandaloneMariadb::whereUuid($uuid)->first();
 | 
				
			||||||
    if ($mariadb) return $mariadb;
 | 
					    if ($mariadb) return $mariadb;
 | 
				
			||||||
 | 
					    $keydb = StandaloneKeydb::whereUuid($uuid)->first();
 | 
				
			||||||
 | 
					    if ($keydb) return $keydb;
 | 
				
			||||||
 | 
					    $dragonfly = StandaloneDragonfly::whereUuid($uuid)->first();
 | 
				
			||||||
 | 
					    if ($dragonfly) return $dragonfly;
 | 
				
			||||||
 | 
					    $clickhouse = StandaloneClickhouse::whereUuid($uuid)->first();
 | 
				
			||||||
 | 
					    if ($clickhouse) return $clickhouse;
 | 
				
			||||||
    return $resource;
 | 
					    return $resource;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function generatTagDeployWebhook($tag_name)
 | 
					function generatTagDeployWebhook($tag_name)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@
 | 
				
			|||||||
        "lorisleiva/laravel-actions": "^2.7",
 | 
					        "lorisleiva/laravel-actions": "^2.7",
 | 
				
			||||||
        "nubs/random-name-generator": "^2.2",
 | 
					        "nubs/random-name-generator": "^2.2",
 | 
				
			||||||
        "phpseclib/phpseclib": "~3.0",
 | 
					        "phpseclib/phpseclib": "~3.0",
 | 
				
			||||||
 | 
					        "pion/laravel-chunk-upload": "^1.5",
 | 
				
			||||||
        "poliander/cron": "^3.0",
 | 
					        "poliander/cron": "^3.0",
 | 
				
			||||||
        "purplepixie/phpdns": "^2.1",
 | 
					        "purplepixie/phpdns": "^2.1",
 | 
				
			||||||
        "pusher/pusher-php-server": "^7.2",
 | 
					        "pusher/pusher-php-server": "^7.2",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										68
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										68
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							@@ -4,7 +4,7 @@
 | 
				
			|||||||
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
 | 
					        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
 | 
				
			||||||
        "This file is @generated automatically"
 | 
					        "This file is @generated automatically"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "content-hash": "e095b8a9eb22df2943cbc3e9649ff9e8",
 | 
					    "content-hash": "e6fd1d5c5183226a78df717b52343393",
 | 
				
			||||||
    "packages": [
 | 
					    "packages": [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "amphp/amp",
 | 
					            "name": "amphp/amp",
 | 
				
			||||||
@@ -6370,6 +6370,72 @@
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
            "time": "2021-10-28T11:13:42+00:00"
 | 
					            "time": "2021-10-28T11:13:42+00:00"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "pion/laravel-chunk-upload",
 | 
				
			||||||
 | 
					            "version": "v1.5.4",
 | 
				
			||||||
 | 
					            "source": {
 | 
				
			||||||
 | 
					                "type": "git",
 | 
				
			||||||
 | 
					                "url": "https://github.com/pionl/laravel-chunk-upload.git",
 | 
				
			||||||
 | 
					                "reference": "cfbc4292ddcace51308a4f2f446d310aa04e6133"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "dist": {
 | 
				
			||||||
 | 
					                "type": "zip",
 | 
				
			||||||
 | 
					                "url": "https://api.github.com/repos/pionl/laravel-chunk-upload/zipball/cfbc4292ddcace51308a4f2f446d310aa04e6133",
 | 
				
			||||||
 | 
					                "reference": "cfbc4292ddcace51308a4f2f446d310aa04e6133",
 | 
				
			||||||
 | 
					                "shasum": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require": {
 | 
				
			||||||
 | 
					                "illuminate/console": "5.2 - 5.8 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0",
 | 
				
			||||||
 | 
					                "illuminate/filesystem": "5.2 - 5.8 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0",
 | 
				
			||||||
 | 
					                "illuminate/http": "5.2 - 5.8 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0",
 | 
				
			||||||
 | 
					                "illuminate/support": "5.2 - 5.8 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "require-dev": {
 | 
				
			||||||
 | 
					                "friendsofphp/php-cs-fixer": "^2.16.0 | ^3.52.0",
 | 
				
			||||||
 | 
					                "mockery/mockery": "^1.1.0 | ^1.3.0 | ^1.6.0",
 | 
				
			||||||
 | 
					                "overtrue/phplint": "^1.1 | ^2.0 | ^9.1",
 | 
				
			||||||
 | 
					                "phpunit/phpunit": "5.7 | 6.0 | 7.0 | 7.5 | 8.4 | ^8.5 | ^9.3 | ^10.0 | ^11.0"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "type": "library",
 | 
				
			||||||
 | 
					            "extra": {
 | 
				
			||||||
 | 
					                "laravel": {
 | 
				
			||||||
 | 
					                    "providers": [
 | 
				
			||||||
 | 
					                        "Pion\\Laravel\\ChunkUpload\\Providers\\ChunkUploadServiceProvider"
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "autoload": {
 | 
				
			||||||
 | 
					                "psr-4": {
 | 
				
			||||||
 | 
					                    "Pion\\Laravel\\ChunkUpload\\": "src/"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "notification-url": "https://packagist.org/downloads/",
 | 
				
			||||||
 | 
					            "license": [
 | 
				
			||||||
 | 
					                "MIT"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "authors": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "name": "Martin Kluska",
 | 
				
			||||||
 | 
					                    "email": "martin@kluska.cz"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "description": "Service for chunked upload with several js providers",
 | 
				
			||||||
 | 
					            "support": {
 | 
				
			||||||
 | 
					                "issues": "https://github.com/pionl/laravel-chunk-upload/issues",
 | 
				
			||||||
 | 
					                "source": "https://github.com/pionl/laravel-chunk-upload/tree/v1.5.4"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "funding": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "url": "https://revolut.me/martinpv7n",
 | 
				
			||||||
 | 
					                    "type": "custom"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "url": "https://github.com/pionl",
 | 
				
			||||||
 | 
					                    "type": "github"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "time": "2024-03-25T15:50:07+00:00"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "poliander/cron",
 | 
					            "name": "poliander/cron",
 | 
				
			||||||
            "version": "3.1.0",
 | 
					            "version": "3.1.0",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										44
									
								
								config/chunk-upload.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								config/chunk-upload.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @see https://github.com/pionl/laravel-chunk-upload
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return [
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * The storage config
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    'storage' => [
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					         * Returns the folder name of the chunks. The location is in storage/app/{folder_name}
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        'chunks' => 'chunks',
 | 
				
			||||||
 | 
					        'disk' => 'local',
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    'clear' => [
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					         * How old chunks we should delete
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        'timestamp' => '-1 HOURS',
 | 
				
			||||||
 | 
					        'schedule' => [
 | 
				
			||||||
 | 
					            'enabled' => false,
 | 
				
			||||||
 | 
					            'cron' => '25 * * * *', // run every hour on the 25th minute
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    'chunk' => [
 | 
				
			||||||
 | 
					        // setup for the chunk naming setup to ensure same name upload at same time
 | 
				
			||||||
 | 
					        'name' => [
 | 
				
			||||||
 | 
					            'use' => [
 | 
				
			||||||
 | 
					                'session' => true, // should the chunk name use the session id? The uploader must send cookie!,
 | 
				
			||||||
 | 
					                'browser' => false, // instead of session we can use the ip and browser?
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    'handlers' => [
 | 
				
			||||||
 | 
					        // A list of handlers/providers that will be appended to existing list of handlers
 | 
				
			||||||
 | 
					        'custom' => [],
 | 
				
			||||||
 | 
					        // Overrides the list of handlers - use only what you really want
 | 
				
			||||||
 | 
					        'override' => [
 | 
				
			||||||
 | 
					            // \Pion\Laravel\ChunkUpload\Handler\DropZoneUploadHandler::class
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
@@ -7,7 +7,7 @@ return [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // The release version of your application
 | 
					    // The release version of your application
 | 
				
			||||||
    // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
 | 
					    // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
 | 
				
			||||||
    'release' => '4.0.0-beta.255',
 | 
					    'release' => '4.0.0-beta.256',
 | 
				
			||||||
    // When left empty or `null` the Laravel environment will be used
 | 
					    // When left empty or `null` the Laravel environment will be used
 | 
				
			||||||
    'environment' => config('app.env'),
 | 
					    'environment' => config('app.env'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,3 @@
 | 
				
			|||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return '4.0.0-beta.255';
 | 
					return '4.0.0-beta.256';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::create('standalone_keydbs', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->id();
 | 
				
			||||||
 | 
					            $table->string('uuid')->unique();
 | 
				
			||||||
 | 
					            $table->string('name');
 | 
				
			||||||
 | 
					            $table->string('description')->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->text('keydb_password');
 | 
				
			||||||
 | 
					            $table->longText('keydb_conf')->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->boolean('is_log_drain_enabled')->default(false);
 | 
				
			||||||
 | 
					            $table->boolean('is_include_timestamps')->default(false);
 | 
				
			||||||
 | 
					            $table->softDeletes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('status')->default('exited');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('image')->default('eqalpha/keydb:latest');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->boolean('is_public')->default(false);
 | 
				
			||||||
 | 
					            $table->integer('public_port')->nullable();
 | 
				
			||||||
 | 
					            $table->text('ports_mappings')->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('limits_memory')->default("0");
 | 
				
			||||||
 | 
					            $table->string('limits_memory_swap')->default("0");
 | 
				
			||||||
 | 
					            $table->integer('limits_memory_swappiness')->default(60);
 | 
				
			||||||
 | 
					            $table->string('limits_memory_reservation')->default("0");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('limits_cpus')->default("0");
 | 
				
			||||||
 | 
					            $table->string('limits_cpuset')->nullable()->default(null);
 | 
				
			||||||
 | 
					            $table->integer('limits_cpu_shares')->default(1024);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->timestamp('started_at')->nullable();
 | 
				
			||||||
 | 
					            $table->morphs('destination');
 | 
				
			||||||
 | 
					            $table->foreignId('environment_id')->nullable();
 | 
				
			||||||
 | 
					            $table->timestamps();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('environment_variables', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->foreignId('standalone_keydb_id')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::dropIfExists('standalone_keydbs');
 | 
				
			||||||
 | 
					        Schema::table('environment_variables', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('standalone_keydb_id');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::create('standalone_dragonflies', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->id();
 | 
				
			||||||
 | 
					            $table->string('uuid')->unique();
 | 
				
			||||||
 | 
					            $table->string('name');
 | 
				
			||||||
 | 
					            $table->string('description')->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->text('dragonfly_password');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->boolean('is_log_drain_enabled')->default(false);
 | 
				
			||||||
 | 
					            $table->boolean('is_include_timestamps')->default(false);
 | 
				
			||||||
 | 
					            $table->softDeletes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('status')->default('exited');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('image')->default('docker.dragonflydb.io/dragonflydb/dragonfly');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->boolean('is_public')->default(false);
 | 
				
			||||||
 | 
					            $table->integer('public_port')->nullable();
 | 
				
			||||||
 | 
					            $table->text('ports_mappings')->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('limits_memory')->default("0");
 | 
				
			||||||
 | 
					            $table->string('limits_memory_swap')->default("0");
 | 
				
			||||||
 | 
					            $table->integer('limits_memory_swappiness')->default(60);
 | 
				
			||||||
 | 
					            $table->string('limits_memory_reservation')->default("0");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('limits_cpus')->default("0");
 | 
				
			||||||
 | 
					            $table->string('limits_cpuset')->nullable()->default(null);
 | 
				
			||||||
 | 
					            $table->integer('limits_cpu_shares')->default(1024);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->timestamp('started_at')->nullable();
 | 
				
			||||||
 | 
					            $table->morphs('destination');
 | 
				
			||||||
 | 
					            $table->foreignId('environment_id')->nullable();
 | 
				
			||||||
 | 
					            $table->timestamps();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('environment_variables', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->foreignId('standalone_dragonfly_id')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::dropIfExists('standalone_dragonflies');
 | 
				
			||||||
 | 
					        Schema::table('environment_variables', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('standalone_dragonfly_id');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::create('standalone_clickhouses', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->id();
 | 
				
			||||||
 | 
					            $table->string('uuid')->unique();
 | 
				
			||||||
 | 
					            $table->string('name');
 | 
				
			||||||
 | 
					            $table->string('description')->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('clickhouse_admin_user')->default('default');
 | 
				
			||||||
 | 
					            $table->text('clickhouse_admin_password');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->boolean('is_log_drain_enabled')->default(false);
 | 
				
			||||||
 | 
					            $table->boolean('is_include_timestamps')->default(false);
 | 
				
			||||||
 | 
					            $table->softDeletes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('status')->default('exited');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('image')->default('bitnami/clickhouse');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->boolean('is_public')->default(false);
 | 
				
			||||||
 | 
					            $table->integer('public_port')->nullable();
 | 
				
			||||||
 | 
					            $table->text('ports_mappings')->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('limits_memory')->default("0");
 | 
				
			||||||
 | 
					            $table->string('limits_memory_swap')->default("0");
 | 
				
			||||||
 | 
					            $table->integer('limits_memory_swappiness')->default(60);
 | 
				
			||||||
 | 
					            $table->string('limits_memory_reservation')->default("0");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('limits_cpus')->default("0");
 | 
				
			||||||
 | 
					            $table->string('limits_cpuset')->nullable()->default(null);
 | 
				
			||||||
 | 
					            $table->integer('limits_cpu_shares')->default(1024);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->timestamp('started_at')->nullable();
 | 
				
			||||||
 | 
					            $table->morphs('destination');
 | 
				
			||||||
 | 
					            $table->foreignId('environment_id')->nullable();
 | 
				
			||||||
 | 
					            $table->timestamps();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('environment_variables', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->foreignId('standalone_clickhouse_id')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::dropIfExists('standalone_clickhouses');
 | 
				
			||||||
 | 
					        Schema::table('environment_variables', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('standalone_clickhouse_id');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('local_file_volumes', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->string('chown')->nullable();
 | 
				
			||||||
 | 
					            $table->string('chmod')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('local_file_volumes', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('chown');
 | 
				
			||||||
 | 
					            $table->dropColumn('chmod');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('standalone_postgresqls', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->string('config_hash')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_redis', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->string('config_hash')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_mysqls', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->string('config_hash')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_mariadbs', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->string('config_hash')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_mongodbs', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->string('config_hash')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_keydbs', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->string('config_hash')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_dragonflies', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->string('config_hash')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_clickhouses', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->string('config_hash')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('services', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->string('config_hash')->nullable();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('standalone_postgresqls', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('config_hash');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_redis', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('config_hash');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_mysqls', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('config_hash');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_mariadbs', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('config_hash');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_mongodbs', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('config_hash');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_keydbs', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('config_hash');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_dragonflies', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('config_hash');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('standalone_clickhouses', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('config_hash');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        Schema::table('services', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('config_hash');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,2 +1,5 @@
 | 
				
			|||||||
#!/command/execlineb -P
 | 
					#!/command/execlineb -P
 | 
				
			||||||
su - webuser -c "php /var/www/html/artisan horizon"
 | 
					foreground {
 | 
				
			||||||
 | 
					  s6-sleep 5
 | 
				
			||||||
 | 
					  su - webuser -c "php /var/www/html/artisan start:horizon"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,2 +1,5 @@
 | 
				
			|||||||
#!/command/execlineb -P
 | 
					#!/command/execlineb -P
 | 
				
			||||||
su - webuser -c "php /var/www/html/artisan schedule:work"
 | 
					foreground {
 | 
				
			||||||
 | 
					  s6-sleep 5
 | 
				
			||||||
 | 
					  su - webuser -c "php /var/www/html/artisan start:scheduler"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -289,3 +289,7 @@ tr td:first-child {
 | 
				
			|||||||
.toast {
 | 
					.toast {
 | 
				
			||||||
    z-index: 1;
 | 
					    z-index: 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.dz-button {
 | 
				
			||||||
 | 
					    @apply w-full p-4 py-10 my-4 font-bold bg-white border dark:border-coolgray-400 dark:text-white dark:bg-transparent hover:dark:bg-coolgray-400;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,41 +1,41 @@
 | 
				
			|||||||
<x-layout-simple>
 | 
					<x-layout-simple>
 | 
				
			||||||
    <div class="min-h-screen hero">
 | 
					    <section class="bg-gray-50 dark:bg-base">
 | 
				
			||||||
        <div>
 | 
					        <div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
 | 
				
			||||||
            <div class="flex flex-col items-center ">
 | 
					            <a class="flex items-center text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
 | 
				
			||||||
                <a href="{{ route('dashboard') }}">
 | 
					                Coolify
 | 
				
			||||||
                    <div class="text-5xl font-bold tracking-tight text-center dark:text-white">Coolify</div>
 | 
					            </a>
 | 
				
			||||||
                </a>
 | 
					            <div class="flex items-center justify-center pb-6 text-center">
 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
            <div class="flex items-center justify-center pb-4 text-center">
 | 
					 | 
				
			||||||
                {{ __('auth.reset_password') }}
 | 
					                {{ __('auth.reset_password') }}
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div>
 | 
					            <div class="w-full bg-white shadow md:mt-0 sm:max-w-md xl:p-0 dark:bg-base ">
 | 
				
			||||||
                <form action="/reset-password" method="POST" class="flex flex-col gap-2">
 | 
					                <div class="p-6 space-y-4 md:space-y-6 sm:p-8">
 | 
				
			||||||
                    @csrf
 | 
					                    <form action="/reset-password" method="POST" class="flex flex-col gap-2">
 | 
				
			||||||
                    <input hidden id="token" name="token" value="{{ request()->route('token') }}">
 | 
					                        @csrf
 | 
				
			||||||
                    <input hidden value="{{ request()->query('email') }}" type="email" name="email"
 | 
					                        <input hidden id="token" name="token" value="{{ request()->route('token') }}">
 | 
				
			||||||
                        label="{{ __('input.email') }}" />
 | 
					                        <input hidden value="{{ request()->query('email') }}" type="email" name="email"
 | 
				
			||||||
                    <div class="flex flex-col gap-2">
 | 
					                            label="{{ __('input.email') }}" />
 | 
				
			||||||
                        <x-forms.input required type="password" id="password" name="password"
 | 
					                        <div class="flex flex-col gap-2">
 | 
				
			||||||
                            label="{{ __('input.password') }}" autofocus />
 | 
					                            <x-forms.input required type="password" id="password" name="password"
 | 
				
			||||||
                        <x-forms.input required type="password" id="password_confirmation" name="password_confirmation"
 | 
					                                label="{{ __('input.password') }}" autofocus />
 | 
				
			||||||
                            label="{{ __('input.password.again') }}" />
 | 
					                            <x-forms.input required type="password" id="password_confirmation"
 | 
				
			||||||
                    </div>
 | 
					                                name="password_confirmation" label="{{ __('input.password.again') }}" />
 | 
				
			||||||
                    <x-forms.button type="submit">{{ __('auth.reset_password') }}</x-forms.button>
 | 
					                        </div>
 | 
				
			||||||
                </form>
 | 
					                        <x-forms.button type="submit">{{ __('auth.reset_password') }}</x-forms.button>
 | 
				
			||||||
                @if ($errors->any())
 | 
					                    </form>
 | 
				
			||||||
                    <div class="text-xs text-center text-error">
 | 
					                    @if ($errors->any())
 | 
				
			||||||
                        @foreach ($errors->all() as $error)
 | 
					                        <div class="text-xs text-center text-error">
 | 
				
			||||||
                            <p>{{ $error }}</p>
 | 
					                            @foreach ($errors->all() as $error)
 | 
				
			||||||
                        @endforeach
 | 
					                                <p>{{ $error }}</p>
 | 
				
			||||||
                    </div>
 | 
					                            @endforeach
 | 
				
			||||||
                @endif
 | 
					                        </div>
 | 
				
			||||||
                @if (session('status'))
 | 
					                    @endif
 | 
				
			||||||
                    <div class="mb-4 font-medium text-green-600">
 | 
					                    @if (session('status'))
 | 
				
			||||||
                        {{ session('status') }}
 | 
					                        <div class="mb-4 font-medium text-green-600">
 | 
				
			||||||
                    </div>
 | 
					                            {{ session('status') }}
 | 
				
			||||||
                @endif
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    @endif
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </section>
 | 
				
			||||||
</x-layout-simple>
 | 
					</x-layout-simple>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,11 +5,14 @@
 | 
				
			|||||||
        @endisset
 | 
					        @endisset
 | 
				
			||||||
    @isset($confirmAction)
 | 
					    @isset($confirmAction)
 | 
				
			||||||
            x-on:{{ explode('(', $confirmAction)[0] }}.window="$wire.{{ explode('(', $confirmAction)[0] }}"
 | 
					            x-on:{{ explode('(', $confirmAction)[0] }}.window="$wire.{{ explode('(', $confirmAction)[0] }}"
 | 
				
			||||||
        @endisset
 | 
					        @endisset>
 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {{ $slot }}
 | 
					    {{ $slot }}
 | 
				
			||||||
    @if ($attributes->whereStartsWith('wire:click')->first())
 | 
					    @if ($attributes->whereStartsWith('wire:click')->first())
 | 
				
			||||||
        <x-loading-on-button wire:target="{{ $attributes->whereStartsWith('wire:click')->first() }}" wire:loading.delay />
 | 
					        <x-loading-on-button wire:target="{{ $attributes->whereStartsWith('wire:click')->first() }}"
 | 
				
			||||||
 | 
					            wire:loading.delay />
 | 
				
			||||||
 | 
					    @elseif($attributes->whereStartsWith('wire:target')->first())
 | 
				
			||||||
 | 
					        <x-loading-on-button wire:target="{{ $attributes->whereStartsWith('wire:target')->first() }}"
 | 
				
			||||||
 | 
					            wire:loading.delay />
 | 
				
			||||||
    @endif
 | 
					    @endif
 | 
				
			||||||
</button>
 | 
					</button>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,18 @@
 | 
				
			|||||||
 | 
					<script>
 | 
				
			||||||
 | 
					    function handleKeydown(e) {
 | 
				
			||||||
 | 
					        if (e.keyCode === 9) {
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            e.target.setRangeText(
 | 
				
			||||||
 | 
					                '\t',
 | 
				
			||||||
 | 
					                e.target.selectionStart,
 | 
				
			||||||
 | 
					                e.target.selectionStart,
 | 
				
			||||||
 | 
					                'end'
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="flex-1 form-control">
 | 
					<div class="flex-1 form-control">
 | 
				
			||||||
    @if ($label)
 | 
					    @if ($label)
 | 
				
			||||||
        <label class="flex items-center gap-1 mb-1 text-sm font-medium">{{ $label }}
 | 
					        <label class="flex items-center gap-1 mb-1 text-sm font-medium">{{ $label }}
 | 
				
			||||||
@@ -41,7 +56,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    @else
 | 
					    @else
 | 
				
			||||||
        <textarea placeholder="{{ $placeholder }}" {{ $attributes->merge(['class' => $defaultClass]) }}
 | 
					        <textarea @keydown.tab="{{ $allowTab }} && handleKeydown" placeholder="{{ $placeholder }}" {{ $attributes->merge(['class' => $defaultClass]) }}
 | 
				
			||||||
            @if ($realtimeValidation) wire:model.debounce.200ms="{{ $id }}"
 | 
					            @if ($realtimeValidation) wire:model.debounce.200ms="{{ $id }}"
 | 
				
			||||||
        @else
 | 
					        @else
 | 
				
			||||||
    wire:model={{ $value ?? $id }}
 | 
					    wire:model={{ $value ?? $id }}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,14 @@
 | 
				
			|||||||
<div {{ $attributes->merge(['class' => "group"]) }}>
 | 
					<div {{ $attributes->merge(['class' => 'group']) }}>
 | 
				
			||||||
    <div class="info-helper">
 | 
					    <div class="info-helper">
 | 
				
			||||||
        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current">
 | 
					        @isset($icon)
 | 
				
			||||||
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
 | 
					            {{ $icon }}
 | 
				
			||||||
                d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
 | 
					        @else
 | 
				
			||||||
        </svg>
 | 
					            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current">
 | 
				
			||||||
 | 
					                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
 | 
				
			||||||
 | 
					                    d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
 | 
				
			||||||
 | 
					            </svg>
 | 
				
			||||||
 | 
					        @endisset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="info-helper-popup">
 | 
					    <div class="info-helper-popup">
 | 
				
			||||||
        <div class="p-4">
 | 
					        <div class="p-4">
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								resources/views/components/popup-small.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								resources/views/components/popup-small.blade.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					@props(['title' => 'Default title', 'description' => 'Default Description', 'buttonText' => 'Default Button Text'])
 | 
				
			||||||
 | 
					<div x-data="{
 | 
				
			||||||
 | 
					    bannerVisible: true,
 | 
				
			||||||
 | 
					    bannerVisibleAfter: 100
 | 
				
			||||||
 | 
					}" x-show="bannerVisible" x-transition:enter="transition ease-out duration-100"
 | 
				
			||||||
 | 
					    x-transition:enter-start="translate-y-full" x-transition:enter-end="translate-y-0"
 | 
				
			||||||
 | 
					    x-transition:leave="transition ease-in duration-300" x-transition:leave-start="translate-y-0"
 | 
				
			||||||
 | 
					    x-transition:leave-end="translate-y-full" x-init="setTimeout(() => { bannerVisible = true }, bannerVisibleAfter);"
 | 
				
			||||||
 | 
					    class="fixed bottom-0 right-0  h-auto duration-300 ease-out px-5 pb-5 max-w-[46rem] z-[999]"
 | 
				
			||||||
 | 
					    x-cloak>
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					        class="flex flex-col items-center justify-between w-full h-full max-w-4xl p-6 mx-auto bg-white border shadow-lg lg:border-t dark:border-coolgray-300 dark:bg-coolgray-100 lg:p-8 lg:flex-row sm:rounded">
 | 
				
			||||||
 | 
					        <div
 | 
				
			||||||
 | 
					            class="flex flex-col items-start h-full pb-0 text-xs lg:items-center lg:flex-row lg:pr-6 lg:space-x-5 dark:text-neutral-300 ">
 | 
				
			||||||
 | 
					            @if (isset($icon))
 | 
				
			||||||
 | 
					                {{ $icon }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <div class="pt-0">
 | 
				
			||||||
 | 
					                <h4 class="w-full mb-1 text-base font-bold leading-none -translate-y-1 lg:text-xl text-neutral-900 dark:text-white">
 | 
				
			||||||
 | 
					                    {{ $title }}
 | 
				
			||||||
 | 
					                </h4>
 | 
				
			||||||
 | 
					                <p class="">{{ $description }}</span></p>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -9,7 +9,17 @@
 | 
				
			|||||||
        {{ str($status)->before(':')->headline() }}
 | 
					        {{ str($status)->before(':')->headline() }}
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    @if (!str($status)->startsWith('Proxy') && !str($status)->contains('('))
 | 
					    @if (!str($status)->startsWith('Proxy') && !str($status)->contains('('))
 | 
				
			||||||
        <div class="text-xs {{ str($status)->contains('unhealthy') ? 'dark:text-warning' : 'text-success' }}">({{ str($status)->after(':') }})</div>
 | 
					        @if (str($status)->contains('unhealthy'))
 | 
				
			||||||
 | 
					            <x-helper helper="Unhealthy state. <span class='dark:text-warning text-coollabs'>This doesn't mean that the resource is malfunctioning.</span><br><br>- If the resource is accessible, it indicates that no health check is configured - it is not mandatory.<br>- If the resource is not accessible (returning 404 or 503), it may indicate that a health check is needed and has not passed. <span class='dark:text-warning text-coollabs'>Your action is required.</span>"  >
 | 
				
			||||||
 | 
					                <x-slot:icon>
 | 
				
			||||||
 | 
					                    <svg class="hidden w-4 h-4 dark:text-warning lg:block" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
 | 
				
			||||||
 | 
					                        <path fill="currentColor" d="M240.26 186.1L152.81 34.23a28.74 28.74 0 0 0-49.62 0L15.74 186.1a27.45 27.45 0 0 0 0 27.71A28.31 28.31 0 0 0 40.55 228h174.9a28.31 28.31 0 0 0 24.79-14.19a27.45 27.45 0 0 0 .02-27.71m-20.8 15.7a4.46 4.46 0 0 1-4 2.2H40.55a4.46 4.46 0 0 1-4-2.2a3.56 3.56 0 0 1 0-3.73L124 46.2a4.77 4.77 0 0 1 8 0l87.44 151.87a3.56 3.56 0 0 1 .02 3.73M116 136v-32a12 12 0 0 1 24 0v32a12 12 0 0 1-24 0m28 40a16 16 0 1 1-16-16a16 16 0 0 1 16 16"></path>
 | 
				
			||||||
 | 
					                    </svg>
 | 
				
			||||||
 | 
					                </x-slot:icon>
 | 
				
			||||||
 | 
					            </x-helper>
 | 
				
			||||||
 | 
					        {{-- @else
 | 
				
			||||||
 | 
					            <div class="text-xs dark:text-success">({{ str($status)->after(':') }})</div> --}}
 | 
				
			||||||
 | 
					        @endif
 | 
				
			||||||
    @endif
 | 
					    @endif
 | 
				
			||||||
    </span>
 | 
					    </span>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,19 @@
 | 
				
			|||||||
<div class="min-h-screen hero">
 | 
					<section class="bg-gray-50 dark:bg-base">
 | 
				
			||||||
    <div class="w-96 min-w-fit">
 | 
					    <div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
 | 
				
			||||||
        <div class="flex flex-col items-center">
 | 
					        <a class="flex items-center mb-6 text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
 | 
				
			||||||
            <a href="{{ route('dashboard') }}">
 | 
					            Coolify
 | 
				
			||||||
                <div class="text-5xl font-bold tracking-tight text-center dark:text-white">Coolify</div>
 | 
					        </a>
 | 
				
			||||||
            </a>
 | 
					        <div class="w-full bg-white shadow md:mt-0 sm:max-w-md xl:p-0 dark:bg-base ">
 | 
				
			||||||
 | 
					            <div class="p-6 space-y-4 md:space-y-6 sm:p-8">
 | 
				
			||||||
 | 
					                <form class="flex flex-col gap-2" wire:submit='submit'>
 | 
				
			||||||
 | 
					                    <x-forms.input id="email" type="email" placeholder="Email" readonly label="Email" />
 | 
				
			||||||
 | 
					                    <x-forms.input id="password" type="password" placeholder="New Password" label="New Password"
 | 
				
			||||||
 | 
					                        required />
 | 
				
			||||||
 | 
					                    <x-forms.input id="password_confirmation" type="password" placeholder="Confirm New Password"
 | 
				
			||||||
 | 
					                        label="Confirm New Password" required />
 | 
				
			||||||
 | 
					                    <x-forms.button type="submit">Reset Password</x-forms.button>
 | 
				
			||||||
 | 
					                </form>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
        <form class="flex flex-col gap-2" wire:submit='submit'>
 | 
					 | 
				
			||||||
            <x-forms.input id="email" type="email" placeholder="Email" readonly label="Email" />
 | 
					 | 
				
			||||||
            <x-forms.input id="password" type="password" placeholder="New Password" label="New Password" required />
 | 
					 | 
				
			||||||
            <x-forms.input id="password_confirmation" type="password" placeholder="Confirm New Password"
 | 
					 | 
				
			||||||
                label="Confirm New Password" required />
 | 
					 | 
				
			||||||
            <x-forms.button type="submit">Reset Password</x-forms.button>
 | 
					 | 
				
			||||||
        </form>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</div>
 | 
					</section>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
<div>
 | 
					<div>
 | 
				
			||||||
    <h1>Configuration</h1>
 | 
					    <h1>Configuration</h1>
 | 
				
			||||||
 | 
					    <livewire:project.shared.configuration-checker :resource="$application" />
 | 
				
			||||||
    <livewire:project.application.heading :application="$application" />
 | 
					    <livewire:project.application.heading :application="$application" />
 | 
				
			||||||
    <div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex h-full pt-6">
 | 
					    <div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex h-full pt-6">
 | 
				
			||||||
        <div class="flex flex-col gap-2 xl:w-48">
 | 
					        <div class="flex flex-col gap-2 xl:w-48">
 | 
				
			||||||
@@ -27,7 +28,7 @@
 | 
				
			|||||||
                <a class="menu-item" :class="activeTab === 'source' && 'menu-item-active'"
 | 
					                <a class="menu-item" :class="activeTab === 'source' && 'menu-item-active'"
 | 
				
			||||||
                    @click.prevent="activeTab = 'source'; window.location.hash = 'source'" href="#">Source</a>
 | 
					                    @click.prevent="activeTab = 'source'; window.location.hash = 'source'" href="#">Source</a>
 | 
				
			||||||
            @endif
 | 
					            @endif
 | 
				
			||||||
            <a  class="menu-item" :class="activeTab === 'servers' && 'menu-item-active'" class="flex items-center gap-2"
 | 
					            <a class="menu-item" :class="activeTab === 'servers' && 'menu-item-active'" class="flex items-center gap-2"
 | 
				
			||||||
                @click.prevent="activeTab = 'servers'; window.location.hash = 'servers'" href="#">Servers
 | 
					                @click.prevent="activeTab = 'servers'; window.location.hash = 'servers'" href="#">Servers
 | 
				
			||||||
                @if (str($application->status)->contains('degraded'))
 | 
					                @if (str($application->status)->contains('degraded'))
 | 
				
			||||||
                    <span title="Some servers are unavailable">
 | 
					                    <span title="Some servers are unavailable">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
<div>
 | 
					<div>
 | 
				
			||||||
    <h1>Deployments</h1>
 | 
					    <h1>Deployments</h1>
 | 
				
			||||||
 | 
					    <livewire:project.shared.configuration-checker :resource="$application" />
 | 
				
			||||||
    <livewire:project.application.heading :application="$application" />
 | 
					    <livewire:project.application.heading :application="$application" />
 | 
				
			||||||
    {{-- <livewire:project.application.deployment.show :application="$application" :deployments="$deployments" :deployments_count="$deployments_count" /> --}}
 | 
					    {{-- <livewire:project.application.deployment.show :application="$application" :deployments="$deployments" :deployments_count="$deployments_count" /> --}}
 | 
				
			||||||
    <div class="flex flex-col gap-2 pb-10"
 | 
					    <div class="flex flex-col gap-2 pb-10"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
<div>
 | 
					<div>
 | 
				
			||||||
    <h1 class="py-0">Deployment</h1>
 | 
					    <h1 class="py-0">Deployment</h1>
 | 
				
			||||||
 | 
					    <livewire:project.shared.configuration-checker :resource="$application" />
 | 
				
			||||||
    <livewire:project.application.heading :application="$application" />
 | 
					    <livewire:project.application.heading :application="$application" />
 | 
				
			||||||
    <div class="pt-4" x-data="{
 | 
					    <div class="pt-4" x-data="{
 | 
				
			||||||
        fullscreen: false,
 | 
					        fullscreen: false,
 | 
				
			||||||
@@ -85,10 +86,10 @@
 | 
				
			|||||||
                    @if (decode_remote_command_output($application_deployment_queue)->count() > 0)
 | 
					                    @if (decode_remote_command_output($application_deployment_queue)->count() > 0)
 | 
				
			||||||
                        @foreach (decode_remote_command_output($application_deployment_queue) as $line)
 | 
					                        @foreach (decode_remote_command_output($application_deployment_queue) as $line)
 | 
				
			||||||
                            <span @class([
 | 
					                            <span @class([
 | 
				
			||||||
                                'dark:text-warning' => $line['hidden'],
 | 
					                                'dark:text-warning whitespace-pre-line' => $line['hidden'],
 | 
				
			||||||
                                'text-red-500 font-bold' => $line['type'] == 'stderr',
 | 
					                                'text-red-500 font-bold whitespace-pre-line' => $line['type'] == 'stderr',
 | 
				
			||||||
                            ])>[{{ $line['timestamp'] }}] @if ($line['hidden'])
 | 
					                            ])>[{{ $line['timestamp'] }}] @if ($line['hidden'])
 | 
				
			||||||
                                    <br>COMMAND: {{ $line['command'] }}<br>OUTPUT :
 | 
					                                    <br><br>COMMAND: {{ $line['command'] }}<br><br>OUTPUT :
 | 
				
			||||||
                                    @endif @if (str($line['output'])->contains('http://') || str($line['output'])->contains('https://'))
 | 
					                                    @endif @if (str($line['output'])->contains('http://') || str($line['output'])->contains('https://'))
 | 
				
			||||||
                                        @php
 | 
					                                        @php
 | 
				
			||||||
                                            $line['output'] = preg_replace(
 | 
					                                            $line['output'] = preg_replace(
 | 
				
			||||||
@@ -100,6 +101,7 @@
 | 
				
			|||||||
                                    @else
 | 
					                                    @else
 | 
				
			||||||
                                        {{ $line['output'] }}
 | 
					                                        {{ $line['output'] }}
 | 
				
			||||||
                                    @endif
 | 
					                                    @endif
 | 
				
			||||||
 | 
					                                    <br>
 | 
				
			||||||
                            </span>
 | 
					                            </span>
 | 
				
			||||||
                        @endforeach
 | 
					                        @endforeach
 | 
				
			||||||
                    @else
 | 
					                    @else
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,30 +1,23 @@
 | 
				
			|||||||
<div>
 | 
					<div x-data="{ initLoadingCompose: $wire.entangle('initLoadingCompose') }">
 | 
				
			||||||
    <form wire:submit='submit' class="flex flex-col">
 | 
					    <form wire:submit='submit' class="flex flex-col pb-32">
 | 
				
			||||||
        <div class="flex items-center gap-2">
 | 
					        <div class="flex items-center gap-2">
 | 
				
			||||||
            <h2>General</h2>
 | 
					            <h2>General</h2>
 | 
				
			||||||
            <x-forms.button type="submit">
 | 
					            <x-forms.button type="submit">
 | 
				
			||||||
                Save
 | 
					                Save
 | 
				
			||||||
            </x-forms.button>
 | 
					        </x-forms.button>
 | 
				
			||||||
            @if ($isConfigurationChanged && !is_null($application->config_hash) && !$application->isExited())
 | 
					 | 
				
			||||||
                <div title="Configuration not applied to the running application. You need to redeploy.">
 | 
					 | 
				
			||||||
                    <svg class="w-6 h-6 dark:text-warning" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
 | 
					 | 
				
			||||||
                        <path fill="currentColor"
 | 
					 | 
				
			||||||
                            d="M240.26 186.1L152.81 34.23a28.74 28.74 0 0 0-49.62 0L15.74 186.1a27.45 27.45 0 0 0 0 27.71A28.31 28.31 0 0 0 40.55 228h174.9a28.31 28.31 0 0 0 24.79-14.19a27.45 27.45 0 0 0 .02-27.71m-20.8 15.7a4.46 4.46 0 0 1-4 2.2H40.55a4.46 4.46 0 0 1-4-2.2a3.56 3.56 0 0 1 0-3.73L124 46.2a4.77 4.77 0 0 1 8 0l87.44 151.87a3.56 3.56 0 0 1 .02 3.73M116 136v-32a12 12 0 0 1 24 0v32a12 12 0 0 1-24 0m28 40a16 16 0 1 1-16-16a16 16 0 0 1 16 16" />
 | 
					 | 
				
			||||||
                    </svg>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            @endif
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div>General configuration for your application.</div>
 | 
					        <div>General configuration for your application.</div>
 | 
				
			||||||
        <div class="flex flex-col gap-2 py-4">
 | 
					        <div class="flex flex-col gap-2 py-4">
 | 
				
			||||||
            <div class="flex flex-col items-end gap-2 xl:flex-row">
 | 
					            <div class="flex flex-col items-end gap-2 xl:flex-row">
 | 
				
			||||||
                <x-forms.input id="application.name" label="Name" required />
 | 
					                <x-forms.input x-bind:disabled="initLoadingCompose" id="application.name" label="Name" required />
 | 
				
			||||||
                <x-forms.input id="application.description" label="Description" />
 | 
					                <x-forms.input x-bind:disabled="initLoadingCompose" id="application.description" label="Description" />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            @if (!$application->dockerfile && $application->build_pack !== 'dockerimage')
 | 
					            @if (!$application->dockerfile && $application->build_pack !== 'dockerimage')
 | 
				
			||||||
                <div class="flex flex-col gap-2">
 | 
					                <div class="flex flex-col gap-2">
 | 
				
			||||||
                    <div class="flex gap-2">
 | 
					                    <div class="flex gap-2">
 | 
				
			||||||
                        <x-forms.select wire:model.live="application.build_pack" label="Build Pack" required>
 | 
					                        <x-forms.select x-bind:disabled="initLoadingCompose" wire:model.live="application.build_pack"
 | 
				
			||||||
 | 
					                            label="Build Pack" required>
 | 
				
			||||||
                            <option value="nixpacks">Nixpacks</option>
 | 
					                            <option value="nixpacks">Nixpacks</option>
 | 
				
			||||||
                            <option value="static">Static</option>
 | 
					                            <option value="static">Static</option>
 | 
				
			||||||
                            <option value="dockerfile">Dockerfile</option>
 | 
					                            <option value="dockerfile">Dockerfile</option>
 | 
				
			||||||
@@ -152,23 +145,24 @@
 | 
				
			|||||||
                    @endif
 | 
					                    @endif
 | 
				
			||||||
                @endif
 | 
					                @endif
 | 
				
			||||||
                @if ($application->build_pack === 'dockercompose')
 | 
					                @if ($application->build_pack === 'dockercompose')
 | 
				
			||||||
                    <div class="flex flex-col gap-2" wire:init='loadComposeFile(true)'>
 | 
					                    <div class="flex flex-col gap-2" x-init="$wire.dispatch('loadCompose', true)">
 | 
				
			||||||
                        <div class="flex gap-2">
 | 
					                        <div class="flex gap-2">
 | 
				
			||||||
                            <x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
 | 
					                            <x-forms.input x-bind:disabled="initLoadingCompose" placeholder="/"
 | 
				
			||||||
 | 
					                                id="application.base_directory" label="Base Directory"
 | 
				
			||||||
                                helper="Directory to use as root. Useful for monorepos." />
 | 
					                                helper="Directory to use as root. Useful for monorepos." />
 | 
				
			||||||
                            <x-forms.input placeholder="/docker-compose.yaml" id="application.docker_compose_location"
 | 
					                            <x-forms.input x-bind:disabled="initLoadingCompose" placeholder="/docker-compose.yaml"
 | 
				
			||||||
                                label="Docker Compose Location"
 | 
					                                id="application.docker_compose_location" label="Docker Compose Location"
 | 
				
			||||||
                                helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }}</span>" />
 | 
					                                helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }}</span>" />
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                        <div class="pt-4">The following commands are for advanced use cases. Only modify them if you
 | 
					                        <div class="pt-4">The following commands are for advanced use cases. Only modify them if you
 | 
				
			||||||
                            know what are
 | 
					                            know what are
 | 
				
			||||||
                            you doing.</div>
 | 
					                            you doing.</div>
 | 
				
			||||||
                        <div class="flex gap-2">
 | 
					                        <div class="flex gap-2">
 | 
				
			||||||
                            <x-forms.input placeholder="docker compose build"
 | 
					                            <x-forms.input placeholder="docker compose build" x-bind:disabled="initLoadingCompose"
 | 
				
			||||||
                                id="application.docker_compose_custom_build_command"
 | 
					                                id="application.docker_compose_custom_build_command"
 | 
				
			||||||
                                helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} build</span>"
 | 
					                                helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} build</span>"
 | 
				
			||||||
                                label="Custom Build Command" />
 | 
					                                label="Custom Build Command" />
 | 
				
			||||||
                            <x-forms.input placeholder="docker compose up -d"
 | 
					                            <x-forms.input placeholder="docker compose up -d" x-bind:disabled="initLoadingCompose"
 | 
				
			||||||
                                id="application.docker_compose_custom_start_command"
 | 
					                                id="application.docker_compose_custom_start_command"
 | 
				
			||||||
                                helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} up -d</span>"
 | 
					                                helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} up -d</span>"
 | 
				
			||||||
                                label="Custom Start Command" />
 | 
					                                label="Custom Start Command" />
 | 
				
			||||||
@@ -220,7 +214,8 @@
 | 
				
			|||||||
                    id="application.custom_docker_run_options" label="Custom Docker Options" />
 | 
					                    id="application.custom_docker_run_options" label="Custom Docker Options" />
 | 
				
			||||||
            @endif
 | 
					            @endif
 | 
				
			||||||
            @if ($application->build_pack === 'dockercompose')
 | 
					            @if ($application->build_pack === 'dockercompose')
 | 
				
			||||||
                <x-forms.button wire:click="loadComposeFile">Reload Compose File</x-forms.button>
 | 
					                <x-forms.button wire:target='initLoadingCompose'
 | 
				
			||||||
 | 
					                    x-on:click="$wire.dispatch('loadCompose', false)">Reload Compose File</x-forms.button>
 | 
				
			||||||
                @if ($application->settings->is_raw_compose_deployment_enabled)
 | 
					                @if ($application->settings->is_raw_compose_deployment_enabled)
 | 
				
			||||||
                    <x-forms.textarea rows="10" readonly id="application.docker_compose_raw"
 | 
					                    <x-forms.textarea rows="10" readonly id="application.docker_compose_raw"
 | 
				
			||||||
                        label="Docker Compose Content (applicationId: {{ $application->id }})"
 | 
					                        label="Docker Compose Content (applicationId: {{ $application->id }})"
 | 
				
			||||||
@@ -257,18 +252,29 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            <h3 class="pt-8">Pre/Post Deployment Commands</h3>
 | 
					            <h3 class="pt-8">Pre/Post Deployment Commands</h3>
 | 
				
			||||||
            <div class="flex flex-col gap-2 xl:flex-row">
 | 
					            <div class="flex flex-col gap-2 xl:flex-row">
 | 
				
			||||||
                <x-forms.input id="application.pre_deployment_command" label="Pre-deployment Command"
 | 
					                <x-forms.input x-bind:disabled="initLoadingCompose" id="application.pre_deployment_command"
 | 
				
			||||||
 | 
					                    label="Pre-deployment Command"
 | 
				
			||||||
                    helper="An optional script or command to execute in the existing container before the deployment begins." />
 | 
					                    helper="An optional script or command to execute in the existing container before the deployment begins." />
 | 
				
			||||||
                <x-forms.input id="application.pre_deployment_command_container" label="Container Name"
 | 
					                <x-forms.input x-bind:disabled="initLoadingCompose" id="application.pre_deployment_command_container"
 | 
				
			||||||
 | 
					                    label="Container Name"
 | 
				
			||||||
                    helper="The name of the container to execute within. You can leave it blank if your application only has one container." />
 | 
					                    helper="The name of the container to execute within. You can leave it blank if your application only has one container." />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div class="flex flex-col gap-2 xl:flex-row">
 | 
					            <div class="flex flex-col gap-2 xl:flex-row">
 | 
				
			||||||
                <x-forms.input placeholder="php artisan migrate" id="application.post_deployment_command"
 | 
					                <x-forms.input x-bind:disabled="initLoadingCompose" placeholder="php artisan migrate"
 | 
				
			||||||
                    label="Post-deployment Command"
 | 
					                    id="application.post_deployment_command" label="Post-deployment Command"
 | 
				
			||||||
                    helper="An optional script or command to execute in the newly built container after the deployment completes." />
 | 
					                    helper="An optional script or command to execute in the newly built container after the deployment completes." />
 | 
				
			||||||
                <x-forms.input id="application.post_deployment_command_container" label="Container Name"
 | 
					                <x-forms.input x-bind:disabled="initLoadingCompose" id="application.post_deployment_command_container"
 | 
				
			||||||
 | 
					                    label="Container Name"
 | 
				
			||||||
                    helper="The name of the container to execute within. You can leave it blank if your application only has one container." />
 | 
					                    helper="The name of the container to execute within. You can leave it blank if your application only has one container." />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
 | 
					    @script
 | 
				
			||||||
 | 
					        <script>
 | 
				
			||||||
 | 
					            $wire.$on('loadCompose', (isInit = true) => {
 | 
				
			||||||
 | 
					                $wire.initLoadingCompose = true;
 | 
				
			||||||
 | 
					                $wire.loadComposeFile(isInit);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        </script>
 | 
				
			||||||
 | 
					    @endscript
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@
 | 
				
			|||||||
                <div class="flex-1"></div>
 | 
					                <div class="flex-1"></div>
 | 
				
			||||||
                @if (data_get($execution, 'status') === 'success')
 | 
					                @if (data_get($execution, 'status') === 'success')
 | 
				
			||||||
                    <x-forms.button class=" dark:hover:bg-coolgray-400"
 | 
					                    <x-forms.button class=" dark:hover:bg-coolgray-400"
 | 
				
			||||||
                        wire:click="download({{ data_get($execution, 'id') }})">Download</x-forms.button>
 | 
					                        x-on:click="download_file('{{ data_get($execution, 'id') }}')">Download</x-forms.button>
 | 
				
			||||||
                @endif
 | 
					                @endif
 | 
				
			||||||
                <x-modal-confirmation isErrorButton action="deleteBackup({{ data_get($execution, 'id') }})">
 | 
					                <x-modal-confirmation isErrorButton action="deleteBackup({{ data_get($execution, 'id') }})">
 | 
				
			||||||
                    <x-slot:button-title>
 | 
					                    <x-slot:button-title>
 | 
				
			||||||
@@ -34,7 +34,13 @@
 | 
				
			|||||||
                </x-modal-confirmation>
 | 
					                </x-modal-confirmation>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @empty
 | 
					    @empty
 | 
				
			||||||
        <div>No executions found.</div>
 | 
					        <div>No executions found.</div>
 | 
				
			||||||
    @endforelse
 | 
					    @endforelse
 | 
				
			||||||
 | 
					    <script>
 | 
				
			||||||
 | 
					        function download_file(executionId) {
 | 
				
			||||||
 | 
					            window.open('/download/backup/' + executionId, '_blank');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    </script>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
<div>
 | 
					<div>
 | 
				
			||||||
    <h1>Backups</h1>
 | 
					    <h1>Backups</h1>
 | 
				
			||||||
 | 
					    <livewire:project.shared.configuration-checker :resource="$database" />
 | 
				
			||||||
    <livewire:project.database.heading :database="$database" />
 | 
					    <livewire:project.database.heading :database="$database" />
 | 
				
			||||||
    <div class="pt-6">
 | 
					    <div class="pt-6">
 | 
				
			||||||
        <livewire:project.database.backup-edit :backup="$backup" :s3s="$s3s" :status="data_get($database, 'status')" />
 | 
					        <livewire:project.database.backup-edit :backup="$backup" :s3s="$s3s" :status="data_get($database, 'status')" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
<div>
 | 
					<div>
 | 
				
			||||||
    <h1>Backups</h1>
 | 
					    <h1>Backups</h1>
 | 
				
			||||||
 | 
					    <livewire:project.shared.configuration-checker :resource="$database" />
 | 
				
			||||||
    <livewire:project.database.heading :database="$database" />
 | 
					    <livewire:project.database.heading :database="$database" />
 | 
				
			||||||
    <div class="pt-6">
 | 
					    <div class="pt-6">
 | 
				
			||||||
        <div class="flex gap-2 ">
 | 
					        <div class="flex gap-2 ">
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user