diff --git a/.github/workflows/fix-php-code-style-issues.yml b/.github/workflows/fix-php-code-style-issues.yml new file mode 100644 index 000000000..aebce91bc --- /dev/null +++ b/.github/workflows/fix-php-code-style-issues.yml @@ -0,0 +1,25 @@ +name: Fix PHP code style issues + +on: [push] + +permissions: + contents: write + +jobs: + php-code-styling: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Fix PHP code style issues + uses: aglipanci/laravel-pint-action@2.4 + + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: Fix styling diff --git a/README.md b/README.md index c4e575e16..56bee004e 100644 --- a/README.md +++ b/README.md @@ -31,15 +31,19 @@ # Donations Thank you so much! -Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)! +Special thanks to our biggest sponsors! cccareers logo +hetzner logo +logto logo +bc direct logo +quantcdn logo +arcjet logo + ## Github Sponsors ($40+) -BC Direct SerpAPI typebot -QuantCDN Lightspeed.run diff --git a/app/Actions/Application/StopApplication.php b/app/Actions/Application/StopApplication.php index 601b8e991..446659e5b 100644 --- a/app/Actions/Application/StopApplication.php +++ b/app/Actions/Application/StopApplication.php @@ -3,17 +3,17 @@ namespace App\Actions\Application; use App\Models\Application; -use App\Models\StandaloneDocker; -use App\Notifications\Application\StatusChanged; use Lorisleiva\Actions\Concerns\AsAction; class StopApplication { use AsAction; + public function handle(Application $application) { if ($application->destination->server->isSwarm()) { instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server); + return; } @@ -23,7 +23,7 @@ public function handle(Application $application) $servers->push($server); }); foreach ($servers as $server) { - if (!$server->isFunctional()) { + if (! $server->isFunctional()) { return 'Server is not functional'; } $containers = getCurrentApplicationContainerStatus($server, $application->id, 0); diff --git a/app/Actions/Application/StopApplicationOneServer.php b/app/Actions/Application/StopApplicationOneServer.php index 1945a94bd..da8c700fe 100644 --- a/app/Actions/Application/StopApplicationOneServer.php +++ b/app/Actions/Application/StopApplicationOneServer.php @@ -9,12 +9,13 @@ class StopApplicationOneServer { use AsAction; + public function handle(Application $application, Server $server) { if ($application->destination->server->isSwarm()) { return; } - if (!$server->isFunctional()) { + if (! $server->isFunctional()) { return 'Server is not functional'; } try { @@ -32,6 +33,7 @@ public function handle(Application $application, Server $server) } } catch (\Exception $e) { ray($e->getMessage()); + return $e->getMessage(); } } diff --git a/app/Actions/CoolifyTask/PrepareCoolifyTask.php b/app/Actions/CoolifyTask/PrepareCoolifyTask.php index e6a549756..d4cdf64e2 100644 --- a/app/Actions/CoolifyTask/PrepareCoolifyTask.php +++ b/app/Actions/CoolifyTask/PrepareCoolifyTask.php @@ -14,6 +14,7 @@ class PrepareCoolifyTask { protected Activity $activity; + protected CoolifyTaskArgs $remoteProcessArgs; public function __construct(CoolifyTaskArgs $remoteProcessArgs) @@ -28,12 +29,12 @@ public function __construct(CoolifyTaskArgs $remoteProcessArgs) ->withProperties($properties) ->performedOn($remoteProcessArgs->model) ->event($remoteProcessArgs->type) - ->log("[]"); + ->log('[]'); } else { $this->activity = activity() ->withProperties($remoteProcessArgs->toArray()) ->event($remoteProcessArgs->type) - ->log("[]"); + ->log('[]'); } } @@ -42,6 +43,7 @@ public function __invoke(): Activity $job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish, call_event_data: $this->remoteProcessArgs->call_event_data); dispatch($job); $this->activity->refresh(); + return $this->activity; } } diff --git a/app/Actions/CoolifyTask/RunRemoteProcess.php b/app/Actions/CoolifyTask/RunRemoteProcess.php index 16924476b..be986a76f 100644 --- a/app/Actions/CoolifyTask/RunRemoteProcess.php +++ b/app/Actions/CoolifyTask/RunRemoteProcess.php @@ -69,7 +69,7 @@ public static function decodeOutput(?Activity $activity = null): string return collect($decoded) ->sortBy(fn ($i) => $i['order']) ->map(fn ($i) => $i['output']) - ->implode(""); + ->implode(''); } public function __invoke(): ProcessResult @@ -91,7 +91,7 @@ public function __invoke(): ProcessResult if ($processResult->exitCode() == 0) { $status = ProcessStatus::FINISHED; } - if ($processResult->exitCode() != 0 && !$this->ignore_errors) { + if ($processResult->exitCode() != 0 && ! $this->ignore_errors) { $status = ProcessStatus::ERROR; } // if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) { @@ -109,14 +109,14 @@ public function __invoke(): ProcessResult 'status' => $status->value, ]); $this->activity->save(); - if ($processResult->exitCode() != 0 && !$this->ignore_errors) { + if ($processResult->exitCode() != 0 && ! $this->ignore_errors) { throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode()); } if ($this->call_event_on_finish) { try { if ($this->call_event_data) { event(resolve("App\\Events\\$this->call_event_on_finish", [ - "data" => $this->call_event_data, + 'data' => $this->call_event_data, ])); } else { event(resolve("App\\Events\\$this->call_event_on_finish", [ @@ -127,6 +127,7 @@ public function __invoke(): ProcessResult ray($e); } } + return $processResult; } @@ -182,6 +183,7 @@ protected function getLatestCounter(): int if ($description === null || count($description) === 0) { return 1; } + return end($description)['order'] + 1; } diff --git a/app/Actions/Database/StartClickhouse.php b/app/Actions/Database/StartClickhouse.php index 414d6b407..d9518cd80 100644 --- a/app/Actions/Database/StartClickhouse.php +++ b/app/Actions/Database/StartClickhouse.php @@ -4,24 +4,25 @@ use App\Models\StandaloneClickhouse; use Illuminate\Support\Str; -use Symfony\Component\Yaml\Yaml; use Lorisleiva\Actions\Concerns\AsAction; +use Symfony\Component\Yaml\Yaml; 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->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ "echo 'Starting {$database->name}.'", @@ -57,7 +58,7 @@ public function handle(StandaloneClickhouse $database) 'interval' => '5s', 'timeout' => '5s', 'retries' => 10, - 'start_period' => '5s' + 'start_period' => '5s', ], 'mem_limit' => $this->database->limits_memory, 'memswap_limit' => $this->database->limits_memory_swap, @@ -65,27 +66,27 @@ public function handle(StandaloneClickhouse $database) '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)) { + 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", - ] + 'fluentd-address' => 'tcp://127.0.0.1:24224', + 'fluentd-async' => 'true', + 'fluentd-sub-second-precision' => 'true', + ], ]; } if (count($this->database->ports_mappings_array) > 0) { @@ -111,6 +112,7 @@ public function handle(StandaloneClickhouse $database) $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'); } @@ -119,12 +121,13 @@ 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; + $local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path; } else { $volume_name = $persistentStorage->name; - $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; } } + return $local_persistent_volumes; } @@ -141,6 +144,7 @@ private function generate_local_persistent_volumes_only_volume_names() 'external' => false, ]; } + return $local_persistent_volumes_names; } diff --git a/app/Actions/Database/StartDatabaseProxy.php b/app/Actions/Database/StartDatabaseProxy.php index 547884b7a..a514c51b4 100644 --- a/app/Actions/Database/StartDatabaseProxy.php +++ b/app/Actions/Database/StartDatabaseProxy.php @@ -69,19 +69,19 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St } if ($type === 'App\Models\StandaloneRedis') { $internalPort = 6379; - } else if ($type === 'App\Models\StandalonePostgresql') { + } elseif ($type === 'App\Models\StandalonePostgresql') { $internalPort = 5432; - } else if ($type === 'App\Models\StandaloneMongodb') { + } elseif ($type === 'App\Models\StandaloneMongodb') { $internalPort = 27017; - } else if ($type === 'App\Models\StandaloneMysql') { + } elseif ($type === 'App\Models\StandaloneMysql') { $internalPort = 3306; - } else if ($type === 'App\Models\StandaloneMariadb') { + } elseif ($type === 'App\Models\StandaloneMariadb') { $internalPort = 3306; - } else if ($type === 'App\Models\StandaloneKeydb') { + } elseif ($type === 'App\Models\StandaloneKeydb') { $internalPort = 6379; - } else if ($type === 'App\Models\StandaloneDragonfly') { + } elseif ($type === 'App\Models\StandaloneDragonfly') { $internalPort = 6379; - } else if ($type === 'App\Models\StandaloneClickhouse') { + } elseif ($type === 'App\Models\StandaloneClickhouse') { $internalPort = 9000; } $configuration_dir = database_proxy_dir($database->uuid); @@ -101,7 +101,7 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St } } EOF; - $dockerfile = <<< EOF + $dockerfile = <<< 'EOF' FROM nginx:stable-alpine COPY nginx.conf /etc/nginx/nginx.conf @@ -113,7 +113,7 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St 'context' => $configuration_dir, 'dockerfile' => 'Dockerfile', ], - 'image' => "nginx:stable-alpine", + 'image' => 'nginx:stable-alpine', 'container_name' => $proxyContainerName, 'restart' => RESTART_MODE, 'ports' => [ @@ -130,17 +130,17 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St 'interval' => '5s', 'timeout' => '5s', 'retries' => 3, - 'start_period' => '1s' + 'start_period' => '1s', ], - ] + ], ], 'networks' => [ $network => [ 'external' => true, 'name' => $network, 'attachable' => true, - ] - ] + ], + ], ]; $dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2)); $nginxconf_base64 = base64_encode($nginxconf); diff --git a/app/Actions/Database/StartDragonfly.php b/app/Actions/Database/StartDragonfly.php index 04348c40a..19b1c5814 100644 --- a/app/Actions/Database/StartDragonfly.php +++ b/app/Actions/Database/StartDragonfly.php @@ -3,19 +3,19 @@ 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; +use Symfony\Component\Yaml\Yaml; class StartDragonfly { use AsAction; public StandaloneDragonfly $database; - public array $commands = []; - public string $configuration_dir; + public array $commands = []; + + public string $configuration_dir; public function handle(StandaloneDragonfly $database) { @@ -24,7 +24,7 @@ public function handle(StandaloneDragonfly $database) $startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}"; $container_name = $this->database->uuid; - $this->configuration_dir = database_configuration_dir() . '/' . $container_name; + $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ "echo 'Starting {$database->name}.'", @@ -48,7 +48,7 @@ public function handle(StandaloneDragonfly $database) $this->database->destination->network, ], 'ulimits' => [ - 'memlock'=> '-1' + 'memlock' => '-1', ], 'labels' => [ 'coolify.managed' => 'true', @@ -58,7 +58,7 @@ public function handle(StandaloneDragonfly $database) 'interval' => '5s', 'timeout' => '5s', 'retries' => 10, - 'start_period' => '5s' + 'start_period' => '5s', ], 'mem_limit' => $this->database->limits_memory, 'memswap_limit' => $this->database->limits_memory_swap, @@ -66,27 +66,27 @@ public function handle(StandaloneDragonfly $database) '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)) { + 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", - ] + 'fluentd-address' => 'tcp://127.0.0.1:24224', + 'fluentd-async' => 'true', + 'fluentd-sub-second-precision' => 'true', + ], ]; } if (count($this->database->ports_mappings_array) > 0) { @@ -112,6 +112,7 @@ public function handle(StandaloneDragonfly $database) $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'); } @@ -120,12 +121,13 @@ 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; + $local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path; } else { $volume_name = $persistentStorage->name; - $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; } } + return $local_persistent_volumes; } @@ -142,6 +144,7 @@ private function generate_local_persistent_volumes_only_volume_names() 'external' => false, ]; } + return $local_persistent_volumes_names; } diff --git a/app/Actions/Database/StartKeydb.php b/app/Actions/Database/StartKeydb.php index 672308d89..a632f6e8c 100644 --- a/app/Actions/Database/StartKeydb.php +++ b/app/Actions/Database/StartKeydb.php @@ -5,17 +5,18 @@ use App\Models\StandaloneKeydb; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; -use Symfony\Component\Yaml\Yaml; use Lorisleiva\Actions\Concerns\AsAction; +use Symfony\Component\Yaml\Yaml; class StartKeydb { use AsAction; public StandaloneKeydb $database; - public array $commands = []; - public string $configuration_dir; + public array $commands = []; + + public string $configuration_dir; public function handle(StandaloneKeydb $database) { @@ -24,7 +25,7 @@ public function handle(StandaloneKeydb $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->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ "echo 'Starting {$database->name}.'", @@ -56,7 +57,7 @@ public function handle(StandaloneKeydb $database) 'interval' => '5s', 'timeout' => '5s', 'retries' => 10, - 'start_period' => '5s' + 'start_period' => '5s', ], 'mem_limit' => $this->database->limits_memory, 'memswap_limit' => $this->database->limits_memory_swap, @@ -64,27 +65,27 @@ public function handle(StandaloneKeydb $database) '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)) { + 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", - ] + 'fluentd-address' => 'tcp://127.0.0.1:24224', + 'fluentd-async' => 'true', + 'fluentd-sub-second-precision' => 'true', + ], ]; } if (count($this->database->ports_mappings_array) > 0) { @@ -101,10 +102,10 @@ public function handle(StandaloneKeydb $database) if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } - if (!is_null($this->database->keydb_conf) || !empty($this->database->keydb_conf)) { + if (! is_null($this->database->keydb_conf) || ! empty($this->database->keydb_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', - 'source' => $this->configuration_dir . '/keydb.conf', + 'source' => $this->configuration_dir.'/keydb.conf', 'target' => '/etc/keydb/keydb.conf', 'read_only' => true, ]; @@ -119,6 +120,7 @@ public function handle(StandaloneKeydb $database) $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'); } @@ -127,12 +129,13 @@ 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; + $local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path; } else { $volume_name = $persistentStorage->name; - $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; } } + return $local_persistent_volumes; } @@ -149,6 +152,7 @@ private function generate_local_persistent_volumes_only_volume_names() 'external' => false, ]; } + return $local_persistent_volumes_names; } @@ -165,6 +169,7 @@ private function generate_environment_variables() return $environment_variables->all(); } + private function add_custom_keydb() { if (is_null($this->database->keydb_conf) || empty($this->database->keydb_conf)) { diff --git a/app/Actions/Database/StartMariadb.php b/app/Actions/Database/StartMariadb.php index 652d8fa29..31d3f0640 100644 --- a/app/Actions/Database/StartMariadb.php +++ b/app/Actions/Database/StartMariadb.php @@ -4,15 +4,17 @@ use App\Models\StandaloneMariadb; use Illuminate\Support\Str; -use Symfony\Component\Yaml\Yaml; use Lorisleiva\Actions\Concerns\AsAction; +use Symfony\Component\Yaml\Yaml; class StartMariadb { use AsAction; public StandaloneMariadb $database; + public array $commands = []; + public string $configuration_dir; public function handle(StandaloneMariadb $database) @@ -20,7 +22,7 @@ public function handle(StandaloneMariadb $database) $this->database = $database; $container_name = $this->database->uuid; - $this->configuration_dir = database_configuration_dir() . '/' . $container_name; + $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ "echo 'Starting {$database->name}.'", @@ -46,11 +48,11 @@ public function handle(StandaloneMariadb $database) 'coolify.managed' => 'true', ], 'healthcheck' => [ - 'test' => ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"], + 'test' => ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized'], 'interval' => '5s', 'timeout' => '5s', 'retries' => 10, - 'start_period' => '5s' + 'start_period' => '5s', ], 'mem_limit' => $this->database->limits_memory, 'memswap_limit' => $this->database->limits_memory_swap, @@ -58,27 +60,27 @@ public function handle(StandaloneMariadb $database) '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)) { + 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", - ] + 'fluentd-address' => 'tcp://127.0.0.1:24224', + 'fluentd-async' => 'true', + 'fluentd-sub-second-precision' => 'true', + ], ]; } if (count($this->database->ports_mappings_array) > 0) { @@ -95,10 +97,10 @@ public function handle(StandaloneMariadb $database) if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } - if (!is_null($this->database->mariadb_conf) || !empty($this->database->mariadb_conf)) { + if (! is_null($this->database->mariadb_conf) || ! empty($this->database->mariadb_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', - 'source' => $this->configuration_dir . '/custom-config.cnf', + 'source' => $this->configuration_dir.'/custom-config.cnf', 'target' => '/etc/mysql/conf.d/custom-config.cnf', 'read_only' => true, ]; @@ -112,6 +114,7 @@ public function handle(StandaloneMariadb $database) $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'); } @@ -120,12 +123,13 @@ 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; + $local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path; } else { $volume_name = $persistentStorage->name; - $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; } } + return $local_persistent_volumes; } @@ -142,6 +146,7 @@ private function generate_local_persistent_volumes_only_volume_names() 'external' => false, ]; } + return $local_persistent_volumes_names; } @@ -166,8 +171,10 @@ private function generate_environment_variables() if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_PASSWORD'))->isEmpty()) { $environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}"); } + return $environment_variables->all(); } + private function add_custom_mysql() { if (is_null($this->database->mariadb_conf) || empty($this->database->mariadb_conf)) { diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php index 38e2621bd..8db34b20f 100644 --- a/app/Actions/Database/StartMongodb.php +++ b/app/Actions/Database/StartMongodb.php @@ -4,25 +4,27 @@ use App\Models\StandaloneMongodb; use Illuminate\Support\Str; -use Symfony\Component\Yaml\Yaml; use Lorisleiva\Actions\Concerns\AsAction; +use Symfony\Component\Yaml\Yaml; class StartMongodb { use AsAction; public StandaloneMongodb $database; + public array $commands = []; + public string $configuration_dir; public function handle(StandaloneMongodb $database) { $this->database = $database; - $startCommand = "mongod"; + $startCommand = 'mongod'; $container_name = $this->database->uuid; - $this->configuration_dir = database_configuration_dir() . '/' . $container_name; + $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ "echo 'Starting {$database->name}.'", @@ -51,14 +53,14 @@ public function handle(StandaloneMongodb $database) ], 'healthcheck' => [ 'test' => [ - "CMD", - "echo", - "ok" + 'CMD', + 'echo', + 'ok', ], 'interval' => '5s', 'timeout' => '5s', 'retries' => 10, - 'start_period' => '5s' + 'start_period' => '5s', ], 'mem_limit' => $this->database->limits_memory, 'memswap_limit' => $this->database->limits_memory_swap, @@ -66,27 +68,27 @@ public function handle(StandaloneMongodb $database) '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)) { + 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", - ] + 'fluentd-address' => 'tcp://127.0.0.1:24224', + 'fluentd-async' => 'true', + 'fluentd-sub-second-precision' => 'true', + ], ]; } if (count($this->database->ports_mappings_array) > 0) { @@ -103,19 +105,19 @@ public function handle(StandaloneMongodb $database) if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } - if (!is_null($this->database->mongo_conf) || !empty($this->database->mongo_conf)) { + if (! is_null($this->database->mongo_conf) || ! empty($this->database->mongo_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', - 'source' => $this->configuration_dir . '/mongod.conf', + 'source' => $this->configuration_dir.'/mongod.conf', 'target' => '/etc/mongo/mongod.conf', 'read_only' => true, ]; - $docker_compose['services'][$container_name]['command'] = $startCommand . ' --config /etc/mongo/mongod.conf'; + $docker_compose['services'][$container_name]['command'] = $startCommand.' --config /etc/mongo/mongod.conf'; } $this->add_default_database(); $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', - 'source' => $this->configuration_dir . '/docker-entrypoint-initdb.d', + 'source' => $this->configuration_dir.'/docker-entrypoint-initdb.d', 'target' => '/docker-entrypoint-initdb.d', 'read_only' => true, ]; @@ -129,6 +131,7 @@ public function handle(StandaloneMongodb $database) $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'); } @@ -137,12 +140,13 @@ 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; + $local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path; } else { $volume_name = $persistentStorage->name; - $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; } } + return $local_persistent_volumes; } @@ -159,6 +163,7 @@ private function generate_local_persistent_volumes_only_volume_names() 'external' => false, ]; } + return $local_persistent_volumes_names; } @@ -180,8 +185,10 @@ private function generate_environment_variables() if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) { $environment_variables->push("MONGO_INITDB_DATABASE={$this->database->mongo_initdb_database}"); } + return $environment_variables->all(); } + private function add_custom_mongo_conf() { if (is_null($this->database->mongo_conf) || empty($this->database->mongo_conf)) { @@ -192,6 +199,7 @@ private function add_custom_mongo_conf() $content_base64 = base64_encode($content); $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null"; } + private function add_default_database() { $content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});"; diff --git a/app/Actions/Database/StartMysql.php b/app/Actions/Database/StartMysql.php index 604e72fde..8280faa56 100644 --- a/app/Actions/Database/StartMysql.php +++ b/app/Actions/Database/StartMysql.php @@ -4,15 +4,17 @@ use App\Models\StandaloneMysql; use Illuminate\Support\Str; -use Symfony\Component\Yaml\Yaml; use Lorisleiva\Actions\Concerns\AsAction; +use Symfony\Component\Yaml\Yaml; class StartMysql { use AsAction; public StandaloneMysql $database; + public array $commands = []; + public string $configuration_dir; public function handle(StandaloneMysql $database) @@ -20,7 +22,7 @@ public function handle(StandaloneMysql $database) $this->database = $database; $container_name = $this->database->uuid; - $this->configuration_dir = database_configuration_dir() . '/' . $container_name; + $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ "echo 'Starting {$database->name}.'", @@ -46,11 +48,11 @@ public function handle(StandaloneMysql $database) 'coolify.managed' => 'true', ], 'healthcheck' => [ - 'test' => ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p{$this->database->mysql_root_password}"], + 'test' => ['CMD', 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', "-p{$this->database->mysql_root_password}"], 'interval' => '5s', 'timeout' => '5s', 'retries' => 10, - 'start_period' => '5s' + 'start_period' => '5s', ], 'mem_limit' => $this->database->limits_memory, 'memswap_limit' => $this->database->limits_memory_swap, @@ -58,27 +60,27 @@ public function handle(StandaloneMysql $database) '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)) { + 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", - ] + 'fluentd-address' => 'tcp://127.0.0.1:24224', + 'fluentd-async' => 'true', + 'fluentd-sub-second-precision' => 'true', + ], ]; } if (count($this->database->ports_mappings_array) > 0) { @@ -95,10 +97,10 @@ public function handle(StandaloneMysql $database) if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } - if (!is_null($this->database->mysql_conf) || !empty($this->database->mysql_conf)) { + if (! is_null($this->database->mysql_conf) || ! empty($this->database->mysql_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', - 'source' => $this->configuration_dir . '/custom-config.cnf', + 'source' => $this->configuration_dir.'/custom-config.cnf', 'target' => '/etc/mysql/conf.d/custom-config.cnf', 'read_only' => true, ]; @@ -112,7 +114,8 @@ public function handle(StandaloneMysql $database) $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'); + + return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged'); } private function generate_local_persistent_volumes() @@ -120,12 +123,13 @@ 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; + $local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path; } else { $volume_name = $persistentStorage->name; - $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; } } + return $local_persistent_volumes; } @@ -142,6 +146,7 @@ private function generate_local_persistent_volumes_only_volume_names() 'external' => false, ]; } + return $local_persistent_volumes_names; } @@ -166,8 +171,10 @@ private function generate_environment_variables() if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_PASSWORD'))->isEmpty()) { $environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}"); } + return $environment_variables->all(); } + private function add_custom_mysql() { if (is_null($this->database->mysql_conf) || empty($this->database->mysql_conf)) { diff --git a/app/Actions/Database/StartPostgresql.php b/app/Actions/Database/StartPostgresql.php index 554e347d9..23b9742c7 100644 --- a/app/Actions/Database/StartPostgresql.php +++ b/app/Actions/Database/StartPostgresql.php @@ -4,28 +4,31 @@ use App\Models\StandalonePostgresql; use Illuminate\Support\Str; -use Symfony\Component\Yaml\Yaml; use Lorisleiva\Actions\Concerns\AsAction; +use Symfony\Component\Yaml\Yaml; class StartPostgresql { use AsAction; public StandalonePostgresql $database; + public array $commands = []; + public array $init_scripts = []; + public string $configuration_dir; public function handle(StandalonePostgresql $database) { $this->database = $database; $container_name = $this->database->uuid; - $this->configuration_dir = database_configuration_dir() . '/' . $container_name; + $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ "echo 'Starting {$database->name}.'", "mkdir -p $this->configuration_dir", - "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/" + "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/", ]; $persistent_storages = $this->generate_local_persistent_volumes(); @@ -50,13 +53,13 @@ public function handle(StandalonePostgresql $database) ], 'healthcheck' => [ 'test' => [ - "CMD-SHELL", - "psql -U {$this->database->postgres_user} -d {$this->database->postgres_db} -c 'SELECT 1' || exit 1" + 'CMD-SHELL', + "psql -U {$this->database->postgres_user} -d {$this->database->postgres_db} -c 'SELECT 1' || exit 1", ], 'interval' => '5s', 'timeout' => '5s', 'retries' => 10, - 'start_period' => '5s' + 'start_period' => '5s', ], 'mem_limit' => $this->database->limits_memory, 'memswap_limit' => $this->database->limits_memory_swap, @@ -64,27 +67,27 @@ public function handle(StandalonePostgresql $database) '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)) { + 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", - ] + 'fluentd-address' => 'tcp://127.0.0.1:24224', + 'fluentd-async' => 'true', + 'fluentd-sub-second-precision' => 'true', + ], ]; } if (count($this->database->ports_mappings_array) > 0) { @@ -106,15 +109,15 @@ public function handle(StandalonePostgresql $database) $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', 'source' => $init_script, - 'target' => '/docker-entrypoint-initdb.d/' . basename($init_script), + 'target' => '/docker-entrypoint-initdb.d/'.basename($init_script), 'read_only' => true, ]; } } - if (!is_null($this->database->postgres_conf) && !empty($this->database->postgres_conf)) { + if (! is_null($this->database->postgres_conf) && ! empty($this->database->postgres_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', - 'source' => $this->configuration_dir . '/custom-postgres.conf', + 'source' => $this->configuration_dir.'/custom-postgres.conf', 'target' => '/etc/postgresql/postgresql.conf', 'read_only' => true, ]; @@ -133,6 +136,7 @@ public function handle(StandalonePostgresql $database) $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'); } @@ -141,12 +145,13 @@ 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; + $local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path; } else { $volume_name = $persistentStorage->name; - $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; } } + return $local_persistent_volumes; } @@ -163,6 +168,7 @@ private function generate_local_persistent_volumes_only_volume_names() 'external' => false, ]; } + return $local_persistent_volumes_names; } @@ -187,6 +193,7 @@ private function generate_environment_variables() if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_DB'))->isEmpty()) { $environment_variables->push("POSTGRES_DB={$this->database->postgres_db}"); } + return $environment_variables->all(); } @@ -203,6 +210,7 @@ private function generate_init_scripts() $this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}"; } } + private function add_custom_conf() { if (is_null($this->database->postgres_conf) || empty($this->database->postgres_conf)) { @@ -210,7 +218,7 @@ private function add_custom_conf() } $filename = 'custom-postgres.conf'; $content = $this->database->postgres_conf; - if (!str($content)->contains('listen_addresses')) { + if (! str($content)->contains('listen_addresses')) { $content .= "\nlisten_addresses = '*'"; $this->database->postgres_conf = $content; $this->database->save(); diff --git a/app/Actions/Database/StartRedis.php b/app/Actions/Database/StartRedis.php index 055d82600..065df5e52 100644 --- a/app/Actions/Database/StartRedis.php +++ b/app/Actions/Database/StartRedis.php @@ -5,17 +5,18 @@ use App\Models\StandaloneRedis; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; -use Symfony\Component\Yaml\Yaml; use Lorisleiva\Actions\Concerns\AsAction; +use Symfony\Component\Yaml\Yaml; class StartRedis { use AsAction; public StandaloneRedis $database; - public array $commands = []; - public string $configuration_dir; + public array $commands = []; + + public string $configuration_dir; public function handle(StandaloneRedis $database) { @@ -24,7 +25,7 @@ public function handle(StandaloneRedis $database) $startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes"; $container_name = $this->database->uuid; - $this->configuration_dir = database_configuration_dir() . '/' . $container_name; + $this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->commands = [ "echo 'Starting {$database->name}.'", @@ -55,12 +56,12 @@ public function handle(StandaloneRedis $database) 'test' => [ 'CMD-SHELL', 'redis-cli', - 'ping' + 'ping', ], 'interval' => '5s', 'timeout' => '5s', 'retries' => 10, - 'start_period' => '5s' + 'start_period' => '5s', ], 'mem_limit' => $this->database->limits_memory, 'memswap_limit' => $this->database->limits_memory_swap, @@ -68,27 +69,27 @@ public function handle(StandaloneRedis $database) '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)) { + 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", - ] + 'fluentd-address' => 'tcp://127.0.0.1:24224', + 'fluentd-async' => 'true', + 'fluentd-sub-second-precision' => 'true', + ], ]; } if (count($this->database->ports_mappings_array) > 0) { @@ -105,10 +106,10 @@ public function handle(StandaloneRedis $database) if (count($volume_names) > 0) { $docker_compose['volumes'] = $volume_names; } - if (!is_null($this->database->redis_conf) || !empty($this->database->redis_conf)) { + if (! is_null($this->database->redis_conf) || ! empty($this->database->redis_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', - 'source' => $this->configuration_dir . '/redis.conf', + 'source' => $this->configuration_dir.'/redis.conf', 'target' => '/usr/local/etc/redis/redis.conf', 'read_only' => true, ]; @@ -123,6 +124,7 @@ public function handle(StandaloneRedis $database) $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'); } @@ -131,12 +133,13 @@ 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; + $local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path; } else { $volume_name = $persistentStorage->name; - $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; } } + return $local_persistent_volumes; } @@ -153,6 +156,7 @@ private function generate_local_persistent_volumes_only_volume_names() 'external' => false, ]; } + return $local_persistent_volumes_names; } @@ -169,6 +173,7 @@ private function generate_environment_variables() return $environment_variables->all(); } + private function add_custom_redis() { if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) { diff --git a/app/Actions/Database/StopDatabase.php b/app/Actions/Database/StopDatabase.php index 408c5a69e..66a32e811 100644 --- a/app/Actions/Database/StopDatabase.php +++ b/app/Actions/Database/StopDatabase.php @@ -19,7 +19,7 @@ class StopDatabase public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database) { $server = $database->destination->server; - if (!$server->isFunctional()) { + if (! $server->isFunctional()) { return 'Server is not functional'; } instant_remote_process( diff --git a/app/Actions/Docker/GetContainersStatus.php b/app/Actions/Docker/GetContainersStatus.php index a8a9185ba..9b32e89f3 100644 --- a/app/Actions/Docker/GetContainersStatus.php +++ b/app/Actions/Docker/GetContainersStatus.php @@ -17,7 +17,9 @@ class GetContainersStatus { use AsAction; + public $applications; + public $server; public function handle(Server $server) @@ -26,9 +28,9 @@ public function handle(Server $server) // $server = Server::find(0); // } $this->server = $server; - if (!$this->server->isFunctional()) { + if (! $this->server->isFunctional()) { return 'Server is not ready.'; - }; + } $this->applications = $this->server->applications(); $skip_these_applications = collect([]); foreach ($this->applications as $application) { @@ -41,7 +43,7 @@ public function handle(Server $server) } } $this->applications = $this->applications->filter(function ($value, $key) use ($skip_these_applications) { - return !$skip_these_applications->pluck('id')->contains($value->id); + return ! $skip_these_applications->pluck('id')->contains($value->id); }); $this->old_way(); // if ($this->server->isSwarm()) { @@ -133,7 +135,7 @@ private function sentinel() return data_get($value, 'name') === "$uuid-proxy"; } })->first(); - if (!$foundTcpProxy) { + if (! $foundTcpProxy) { StartDatabaseProxy::run($service_db); // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server)); } @@ -158,7 +160,7 @@ private function sentinel() return data_get($value, 'name') === "$uuid-proxy"; } })->first(); - if (!$foundTcpProxy) { + if (! $foundTcpProxy) { StartDatabaseProxy::run($database); $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); } @@ -177,13 +179,13 @@ private function sentinel() $subType = data_get($labels, 'coolify.service.subType'); $subId = data_get($labels, 'coolify.service.subId'); $service = $services->where('id', $serviceLabelId)->first(); - if (!$service) { + if (! $service) { continue; } if ($subType === 'application') { - $service = $service->applications()->where('id', $subId)->first(); + $service = $service->applications()->where('id', $subId)->first(); } else { - $service = $service->databases()->where('id', $subId)->first(); + $service = $service->databases()->where('id', $subId)->first(); } if ($service) { $foundServices[] = "$service->id-$service->name"; @@ -239,7 +241,7 @@ private function sentinel() $environmentName = data_get($service, 'environment.name'); if ($projectUuid && $serviceUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid; } else { $url = null; } @@ -265,7 +267,7 @@ private function sentinel() $environment = data_get($application, 'environment.name'); if ($projectUuid && $applicationUuid && $environment) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid; } else { $url = null; } @@ -290,7 +292,7 @@ private function sentinel() $applicationUuid = data_get($preview, 'application.uuid'); if ($projectUuid && $applicationUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid; } else { $url = null; } @@ -315,7 +317,7 @@ private function sentinel() $databaseUuid = data_get($database, 'uuid'); if ($projectUuid && $databaseUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid; } else { $url = null; } @@ -332,7 +334,7 @@ private function sentinel() return data_get($value, 'name') === 'coolify-proxy'; } })->first(); - if (!$foundProxyContainer) { + if (! $foundProxyContainer) { try { $shouldStart = CheckProxy::run($this->server); if ($shouldStart) { @@ -351,9 +353,11 @@ private function sentinel() } catch (\Exception $e) { // send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage()); ray($e->getMessage()); + return handleError($e); } } + private function old_way() { if ($this->server->isSwarm()) { @@ -361,8 +365,8 @@ private function old_way() $containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false); } else { // Precheck for containers - $containers = instant_remote_process(["docker container ls -q"], $this->server, false); - if (!$containers) { + $containers = instant_remote_process(['docker container ls -q'], $this->server, false); + if (! $containers) { return; } $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false); @@ -390,6 +394,7 @@ private function old_way() data_set($container, 'State.Health.Status', 'unhealthy'); } } + return $container; }); } @@ -463,7 +468,7 @@ private function old_way() return data_get($value, 'Name') === "/$uuid-proxy"; } })->first(); - if (!$foundTcpProxy) { + if (! $foundTcpProxy) { StartDatabaseProxy::run($service_db); // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server)); } @@ -488,7 +493,7 @@ private function old_way() return data_get($value, 'Name') === "/$uuid-proxy"; } })->first(); - if (!$foundTcpProxy) { + if (! $foundTcpProxy) { StartDatabaseProxy::run($database); $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); } @@ -507,13 +512,13 @@ private function old_way() $subType = data_get($labels, 'coolify.service.subType'); $subId = data_get($labels, 'coolify.service.subId'); $service = $services->where('id', $serviceLabelId)->first(); - if (!$service) { + if (! $service) { continue; } if ($subType === 'application') { - $service = $service->applications()->where('id', $subId)->first(); + $service = $service->applications()->where('id', $subId)->first(); } else { - $service = $service->databases()->where('id', $subId)->first(); + $service = $service->databases()->where('id', $subId)->first(); } if ($service) { $foundServices[] = "$service->id-$service->name"; @@ -569,7 +574,7 @@ private function old_way() $environmentName = data_get($service, 'environment.name'); if ($projectUuid && $serviceUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid; } else { $url = null; } @@ -595,7 +600,7 @@ private function old_way() $environment = data_get($application, 'environment.name'); if ($projectUuid && $applicationUuid && $environment) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid; } else { $url = null; } @@ -620,7 +625,7 @@ private function old_way() $applicationUuid = data_get($preview, 'application.uuid'); if ($projectUuid && $applicationUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid; } else { $url = null; } @@ -645,7 +650,7 @@ private function old_way() $databaseUuid = data_get($database, 'uuid'); if ($projectUuid && $databaseUuid && $environmentName) { - $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid; + $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid; } else { $url = null; } @@ -661,7 +666,7 @@ private function old_way() return data_get($value, 'Name') === '/coolify-proxy'; } })->first(); - if (!$foundProxyContainer) { + if (! $foundProxyContainer) { try { $shouldStart = CheckProxy::run($this->server); if ($shouldStart) { diff --git a/app/Actions/Fortify/CreateNewUser.php b/app/Actions/Fortify/CreateNewUser.php index 7950bd4f7..f8882d12a 100644 --- a/app/Actions/Fortify/CreateNewUser.php +++ b/app/Actions/Fortify/CreateNewUser.php @@ -16,12 +16,12 @@ class CreateNewUser implements CreatesNewUsers /** * Validate and create a newly registered user. * - * @param array $input + * @param array $input */ public function create(array $input): User { $settings = InstanceSettings::get(); - if (!$settings->is_registration_enabled) { + if (! $settings->is_registration_enabled) { abort(403); } Validator::make($input, [ @@ -66,6 +66,7 @@ public function create(array $input): User } // Set session variable session(['currentTeam' => $user->currentTeam = $team]); + return $user; } } diff --git a/app/Actions/Fortify/ResetUserPassword.php b/app/Actions/Fortify/ResetUserPassword.php index 58d99b1b2..7a57c5037 100644 --- a/app/Actions/Fortify/ResetUserPassword.php +++ b/app/Actions/Fortify/ResetUserPassword.php @@ -14,7 +14,7 @@ class ResetUserPassword implements ResetsUserPasswords /** * Validate and reset the user's forgotten password. * - * @param array $input + * @param array $input */ public function reset(User $user, array $input): void { diff --git a/app/Actions/Fortify/UpdateUserPassword.php b/app/Actions/Fortify/UpdateUserPassword.php index 5ebf31875..700563905 100644 --- a/app/Actions/Fortify/UpdateUserPassword.php +++ b/app/Actions/Fortify/UpdateUserPassword.php @@ -14,7 +14,7 @@ class UpdateUserPassword implements UpdatesUserPasswords /** * Validate and update the user's password. * - * @param array $input + * @param array $input */ public function update(User $user, array $input): void { diff --git a/app/Actions/Fortify/UpdateUserProfileInformation.php b/app/Actions/Fortify/UpdateUserProfileInformation.php index 85caf943b..c8bfd930a 100644 --- a/app/Actions/Fortify/UpdateUserProfileInformation.php +++ b/app/Actions/Fortify/UpdateUserProfileInformation.php @@ -13,7 +13,7 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation /** * Validate and update the given user's profile information. * - * @param array $input + * @param array $input */ public function update(User $user, array $input): void { @@ -45,7 +45,7 @@ public function update(User $user, array $input): void /** * Update the given verified user's profile information. * - * @param array $input + * @param array $input */ protected function updateVerifiedUser(User $user, array $input): void { diff --git a/app/Actions/License/CheckResaleLicense.php b/app/Actions/License/CheckResaleLicense.php index 12202b13e..dcb4058c0 100644 --- a/app/Actions/License/CheckResaleLicense.php +++ b/app/Actions/License/CheckResaleLicense.php @@ -6,10 +6,10 @@ use Illuminate\Support\Facades\Http; use Lorisleiva\Actions\Concerns\AsAction; - class CheckResaleLicense { use AsAction; + public function handle() { try { @@ -18,6 +18,7 @@ public function handle() $settings->update([ 'is_resale_license_active' => true, ]); + return; } // if (!$settings->resale_license) { @@ -38,6 +39,7 @@ public function handle() $settings->update([ 'is_resale_license_active' => true, ]); + return; } $data = Http::withHeaders([ @@ -51,6 +53,7 @@ public function handle() $settings->update([ 'is_resale_license_active' => true, ]); + return; } if (data_get($data, 'license_key.status') === 'active') { diff --git a/app/Actions/Proxy/CheckConfiguration.php b/app/Actions/Proxy/CheckConfiguration.php index 1058e8b5f..35374ba43 100644 --- a/app/Actions/Proxy/CheckConfiguration.php +++ b/app/Actions/Proxy/CheckConfiguration.php @@ -2,13 +2,14 @@ namespace App\Actions\Proxy; -use Lorisleiva\Actions\Concerns\AsAction; use App\Models\Server; use Illuminate\Support\Str; +use Lorisleiva\Actions\Concerns\AsAction; class CheckConfiguration { use AsAction; + public function handle(Server $server, bool $reset = false) { $proxyType = $server->proxyType(); @@ -22,12 +23,13 @@ public function handle(Server $server, bool $reset = false) ]; $proxy_configuration = instant_remote_process($payload, $server, false); - if ($reset || !$proxy_configuration || is_null($proxy_configuration)) { + if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) { $proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value; } - if (!$proxy_configuration || is_null($proxy_configuration)) { - throw new \Exception("Could not generate proxy configuration"); + if (! $proxy_configuration || is_null($proxy_configuration)) { + throw new \Exception('Could not generate proxy configuration'); } + return $proxy_configuration; } } diff --git a/app/Actions/Proxy/CheckProxy.php b/app/Actions/Proxy/CheckProxy.php index b1fb6a3cb..735b972af 100644 --- a/app/Actions/Proxy/CheckProxy.php +++ b/app/Actions/Proxy/CheckProxy.php @@ -8,9 +8,10 @@ class CheckProxy { use AsAction; + public function handle(Server $server, $fromUI = false) { - if (!$server->isFunctional()) { + if (! $server->isFunctional()) { return false; } if ($server->isBuildServer()) { @@ -18,6 +19,7 @@ public function handle(Server $server, $fromUI = false) $server->proxy = null; $server->save(); } + return false; } $proxyType = $server->proxyType(); @@ -25,12 +27,12 @@ public function handle(Server $server, $fromUI = false) return false; } ['uptime' => $uptime, 'error' => $error] = $server->validateConnection(); - if (!$uptime) { + if (! $uptime) { throw new \Exception($error); } - if (!$server->isProxyShouldRun()) { + if (! $server->isProxyShouldRun()) { if ($fromUI) { - throw new \Exception("Proxy should not run. You selected the Custom Proxy."); + throw new \Exception('Proxy should not run. You selected the Custom Proxy.'); } else { return false; } @@ -42,12 +44,14 @@ public function handle(Server $server, $fromUI = false) if ($status === 'running') { return false; } + return true; } else { $status = getContainerStatus($server, 'coolify-proxy'); if ($status === 'running') { $server->proxy->set('status', 'running'); $server->save(); + return false; } if ($server->settings->is_cloudflare_tunnel) { @@ -76,6 +80,7 @@ public function handle(Server $server, $fromUI = false) return false; } } + return true; } } diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index 92a5e5b56..710b5cdd8 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -11,6 +11,7 @@ class StartProxy { use AsAction; + public function handle(Server $server, bool $async = true): string|Activity { try { @@ -21,8 +22,8 @@ public function handle(Server $server, bool $async = true): string|Activity $commands = collect([]); $proxy_path = $server->proxyPath(); $configuration = CheckConfiguration::run($server); - if (!$configuration) { - throw new \Exception("Configuration is not synced"); + if (! $configuration) { + throw new \Exception('Configuration is not synced'); } SaveConfiguration::run($server, $configuration); $docker_compose_yml_base64 = base64_encode($configuration); @@ -34,11 +35,11 @@ public function handle(Server $server, bool $async = true): string|Activity "cd $proxy_path", "echo 'Creating required Docker Compose file.'", "echo 'Starting coolify-proxy.'", - "docker stack deploy -c docker-compose.yml coolify-proxy", - "echo 'Proxy started successfully.'" + 'docker stack deploy -c docker-compose.yml coolify-proxy', + "echo 'Proxy started successfully.'", ]); } else { - $caddfile = "import /dynamic/*.caddy"; + $caddfile = 'import /dynamic/*.caddy'; $commands = $commands->merge([ "mkdir -p $proxy_path/dynamic", "cd $proxy_path", @@ -47,16 +48,17 @@ public function handle(Server $server, bool $async = true): string|Activity "echo 'Pulling docker image.'", 'docker compose pull', "echo 'Stopping existing coolify-proxy.'", - "docker compose down -v --remove-orphans > /dev/null 2>&1", + 'docker compose down -v --remove-orphans > /dev/null 2>&1', "echo 'Starting coolify-proxy.'", 'docker compose up -d --remove-orphans', - "echo 'Proxy started successfully.'" + "echo 'Proxy started successfully.'", ]); $commands = $commands->merge(connectProxyToNetworks($server)); } if ($async) { $activity = remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server); + return $activity; } else { instant_remote_process($commands, $server); @@ -64,6 +66,7 @@ public function handle(Server $server, bool $async = true): string|Activity $server->proxy->set('type', $proxyType); $server->save(); ProxyStarted::dispatch($server); + return 'OK'; } } catch (\Throwable $e) { diff --git a/app/Actions/Server/CleanupDocker.php b/app/Actions/Server/CleanupDocker.php index 4faeccf1a..1261e6830 100644 --- a/app/Actions/Server/CleanupDocker.php +++ b/app/Actions/Server/CleanupDocker.php @@ -2,12 +2,13 @@ namespace App\Actions\Server; -use Lorisleiva\Actions\Concerns\AsAction; use App\Models\Server; +use Lorisleiva\Actions\Concerns\AsAction; class CleanupDocker { use AsAction; + public function handle(Server $server, bool $force = true) { if ($force) { diff --git a/app/Actions/Server/ConfigureCloudflared.php b/app/Actions/Server/ConfigureCloudflared.php index 3221557ae..3946afe95 100644 --- a/app/Actions/Server/ConfigureCloudflared.php +++ b/app/Actions/Server/ConfigureCloudflared.php @@ -9,18 +9,19 @@ class ConfigureCloudflared { use AsAction; + public function handle(Server $server, string $cloudflare_token) { try { $config = [ - "services" => [ - "coolify-cloudflared" => [ - "container_name" => "coolify-cloudflared", - "image" => "cloudflare/cloudflared:latest", - "restart" => RESTART_MODE, - "network_mode" => "host", - "command" => "tunnel run", - "environment" => [ + 'services' => [ + 'coolify-cloudflared' => [ + 'container_name' => 'coolify-cloudflared', + 'image' => 'cloudflare/cloudflared:latest', + 'restart' => RESTART_MODE, + 'network_mode' => 'host', + 'command' => 'tunnel run', + 'environment' => [ "TUNNEL_TOKEN={$cloudflare_token}", ], ], @@ -29,12 +30,12 @@ public function handle(Server $server, string $cloudflare_token) $config = Yaml::dump($config, 12, 2); $docker_compose_yml_base64 = base64_encode($config); $commands = collect([ - "mkdir -p /tmp/cloudflared", - "cd /tmp/cloudflared", + 'mkdir -p /tmp/cloudflared', + 'cd /tmp/cloudflared', "echo '$docker_compose_yml_base64' | base64 -d | tee docker-compose.yml > /dev/null", - "docker compose pull", - "docker compose down -v --remove-orphans > /dev/null 2>&1", - "docker compose up -d --remove-orphans", + 'docker compose pull', + 'docker compose down -v --remove-orphans > /dev/null 2>&1', + 'docker compose up -d --remove-orphans', ]); instant_remote_process($commands, $server); } catch (\Throwable $e) { @@ -42,7 +43,7 @@ public function handle(Server $server, string $cloudflare_token) throw $e; } finally { $commands = collect([ - "rm -fr /tmp/cloudflared", + 'rm -fr /tmp/cloudflared', ]); instant_remote_process($commands, $server); } diff --git a/app/Actions/Server/InstallDocker.php b/app/Actions/Server/InstallDocker.php index 721d174b8..f671f2d2a 100644 --- a/app/Actions/Server/InstallDocker.php +++ b/app/Actions/Server/InstallDocker.php @@ -2,20 +2,21 @@ namespace App\Actions\Server; -use Lorisleiva\Actions\Concerns\AsAction; use App\Models\Server; use App\Models\StandaloneDocker; +use Lorisleiva\Actions\Concerns\AsAction; class InstallDocker { use AsAction; + public function handle(Server $server) { $supported_os_type = $server->validateOS(); - if (!$supported_os_type) { + if (! $supported_os_type) { throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: documentation.'); } - ray('Installing Docker on server: ' . $server->name . ' (' . $server->ip . ')' . ' with OS type: ' . $supported_os_type); + ray('Installing Docker on server: '.$server->name.' ('.$server->ip.')'.' with OS type: '.$supported_os_type); $dockerVersion = '24.0'; $config = base64_encode('{ "log-driver": "json-file", @@ -36,40 +37,41 @@ public function handle(Server $server) if (isDev() && $server->id === 0) { $command = $command->merge([ "echo 'Installing Prerequisites...'", - "sleep 1", + 'sleep 1', "echo 'Installing Docker Engine...'", "echo 'Configuring Docker Engine (merging existing configuration with the required)...'", - "sleep 4", + 'sleep 4', "echo 'Restarting Docker Engine...'", - "ls -l /tmp" + 'ls -l /tmp', ]); + return remote_process($command, $server); } else { if ($supported_os_type->contains('debian')) { $command = $command->merge([ "echo 'Installing Prerequisites...'", - "apt-get update -y", - "command -v curl >/dev/null || apt install -y curl", - "command -v wget >/dev/null || apt install -y wget", - "command -v git >/dev/null || apt install -y git", - "command -v jq >/dev/null || apt install -y jq", + 'apt-get update -y', + 'command -v curl >/dev/null || apt install -y curl', + 'command -v wget >/dev/null || apt install -y wget', + 'command -v git >/dev/null || apt install -y git', + 'command -v jq >/dev/null || apt install -y jq', ]); - } else if ($supported_os_type->contains('rhel')) { + } elseif ($supported_os_type->contains('rhel')) { $command = $command->merge([ "echo 'Installing Prerequisites...'", - "command -v curl >/dev/null || dnf install -y curl", - "command -v wget >/dev/null || dnf install -y wget", - "command -v git >/dev/null || dnf install -y git", - "command -v jq >/dev/null || dnf install -y jq", + 'command -v curl >/dev/null || dnf install -y curl', + 'command -v wget >/dev/null || dnf install -y wget', + 'command -v git >/dev/null || dnf install -y git', + 'command -v jq >/dev/null || dnf install -y jq', ]); - } else if ($supported_os_type->contains('sles')) { + } elseif ($supported_os_type->contains('sles')) { $command = $command->merge([ "echo 'Installing Prerequisites...'", - "zypper update -y", - "command -v curl >/dev/null || zypper install -y curl", - "command -v wget >/dev/null || zypper install -y wget", - "command -v git >/dev/null || zypper install -y git", - "command -v jq >/dev/null || zypper install -y jq", + 'zypper update -y', + 'command -v curl >/dev/null || zypper install -y curl', + 'command -v wget >/dev/null || zypper install -y wget', + 'command -v git >/dev/null || zypper install -y git', + 'command -v jq >/dev/null || zypper install -y jq', ]); } else { throw new \Exception('Unsupported OS'); @@ -78,29 +80,30 @@ public function handle(Server $server) "echo 'Installing Docker Engine...'", "curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}", "echo 'Configuring Docker Engine (merging existing configuration with the required)...'", - "test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-$(date +\"%Y%m%d-%H%M%S\")\"", + 'test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json "/etc/docker/daemon.json.original-$(date +"%Y%m%d-%H%M%S")"', "test ! -s /etc/docker/daemon.json && echo '{$config}' | base64 -d | tee /etc/docker/daemon.json > /dev/null", "echo '{$config}' | base64 -d | tee /etc/docker/daemon.json.coolify > /dev/null", - "jq . /etc/docker/daemon.json.coolify | tee /etc/docker/daemon.json.coolify.pretty > /dev/null", - "mv /etc/docker/daemon.json.coolify.pretty /etc/docker/daemon.json.coolify", + 'jq . /etc/docker/daemon.json.coolify | tee /etc/docker/daemon.json.coolify.pretty > /dev/null', + 'mv /etc/docker/daemon.json.coolify.pretty /etc/docker/daemon.json.coolify', "jq -s '.[0] * .[1]' /etc/docker/daemon.json.coolify /etc/docker/daemon.json | tee /etc/docker/daemon.json.appended > /dev/null", - "mv /etc/docker/daemon.json.appended /etc/docker/daemon.json", + 'mv /etc/docker/daemon.json.appended /etc/docker/daemon.json', "echo 'Restarting Docker Engine...'", - "systemctl enable docker >/dev/null 2>&1 || true", - "systemctl restart docker", + 'systemctl enable docker >/dev/null 2>&1 || true', + 'systemctl restart docker', ]); if ($server->isSwarm()) { $command = $command->merge([ - "docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true", + 'docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true', ]); } else { $command = $command->merge([ - "docker network create --attachable coolify >/dev/null 2>&1 || true", + 'docker network create --attachable coolify >/dev/null 2>&1 || true', ]); $command = $command->merge([ "echo 'Done!'", ]); } + return remote_process($command, $server); } } diff --git a/app/Actions/Server/InstallLogDrain.php b/app/Actions/Server/InstallLogDrain.php index aa32d4c0b..6f74e020b 100644 --- a/app/Actions/Server/InstallLogDrain.php +++ b/app/Actions/Server/InstallLogDrain.php @@ -2,21 +2,22 @@ namespace App\Actions\Server; -use Lorisleiva\Actions\Concerns\AsAction; use App\Models\Server; +use Lorisleiva\Actions\Concerns\AsAction; class InstallLogDrain { use AsAction; + public function handle(Server $server) { if ($server->settings->is_logdrain_newrelic_enabled) { $type = 'newrelic'; - } else if ($server->settings->is_logdrain_highlight_enabled) { + } elseif ($server->settings->is_logdrain_highlight_enabled) { $type = 'highlight'; - } else if ($server->settings->is_logdrain_axiom_enabled) { + } elseif ($server->settings->is_logdrain_axiom_enabled) { $type = 'axiom'; - } else if ($server->settings->is_logdrain_custom_enabled) { + } elseif ($server->settings->is_logdrain_custom_enabled) { $type = 'custom'; } else { $type = 'none'; @@ -25,11 +26,12 @@ public function handle(Server $server) if ($type === 'none') { $command = [ "echo 'Stopping old Fluent Bit'", - "docker rm -f coolify-log-drain || true", + 'docker rm -f coolify-log-drain || true', ]; + return instant_remote_process($command, $server); - } else if ($type === 'newrelic') { - if (!$server->settings->is_logdrain_newrelic_enabled) { + } elseif ($type === 'newrelic') { + if (! $server->settings->is_logdrain_newrelic_enabled) { throw new \Exception('New Relic log drain is not enabled.'); } $config = base64_encode(" @@ -59,11 +61,11 @@ public function handle(Server $server) # https://log-api.newrelic.com/log/v1 - US base_uri \${BASE_URI} "); - } else if ($type === 'highlight') { - if (!$server->settings->is_logdrain_highlight_enabled) { + } elseif ($type === 'highlight') { + if (! $server->settings->is_logdrain_highlight_enabled) { throw new \Exception('Highlight log drain is not enabled.'); } - $config = base64_encode(" + $config = base64_encode(' [SERVICE] Flush 5 Daemon off @@ -71,7 +73,7 @@ public function handle(Server $server) Parsers_File parsers.conf [INPUT] Name forward - tag \${HIGHLIGHT_PROJECT_ID} + tag ${HIGHLIGHT_PROJECT_ID} Buffer_Chunk_Size 1M Buffer_Max_Size 6M [OUTPUT] @@ -79,9 +81,9 @@ public function handle(Server $server) Match * Host otel.highlight.io Port 24224 -"); - } else if ($type === 'axiom') { - if (!$server->settings->is_logdrain_axiom_enabled) { +'); + } elseif ($type === 'axiom') { + if (! $server->settings->is_logdrain_axiom_enabled) { throw new \Exception('Axiom log drain is not enabled.'); } $config = base64_encode(" @@ -116,8 +118,8 @@ public function handle(Server $server) json_date_format iso8601 tls On "); - } else if ($type === 'custom') { - if (!$server->settings->is_logdrain_custom_enabled) { + } elseif ($type === 'custom') { + if (! $server->settings->is_logdrain_custom_enabled) { throw new \Exception('Custom log drain is not enabled.'); } $config = base64_encode($server->settings->logdrain_custom_config); @@ -133,7 +135,7 @@ public function handle(Server $server) Regex /^(?!\s*$).+/ "); } - $compose = base64_encode(" + $compose = base64_encode(' services: coolify-log-drain: image: cr.fluentbit.io/fluent/fluent-bit:2.0 @@ -147,7 +149,7 @@ public function handle(Server $server) ports: - 127.0.0.1:24224:24224 restart: unless-stopped -"); +'); $readme = base64_encode('# New Relic Log Drain This log drain is based on [Fluent Bit](https://fluentbit.io/) and New Relic Log Forwarder. @@ -160,11 +162,11 @@ public function handle(Server $server) $base_uri = $server->settings->logdrain_newrelic_base_uri; $base_path = config('coolify.base_config_path'); - $config_path = $base_path . '/log-drains'; - $fluent_bit_config = $config_path . '/fluent-bit.conf'; - $parsers_config = $config_path . '/parsers.conf'; - $compose_path = $config_path . '/docker-compose.yml'; - $readme_path = $config_path . '/README.md'; + $config_path = $base_path.'/log-drains'; + $fluent_bit_config = $config_path.'/fluent-bit.conf'; + $parsers_config = $config_path.'/parsers.conf'; + $compose_path = $config_path.'/docker-compose.yml'; + $readme_path = $config_path.'/README.md'; $command = [ "echo 'Saving configuration'", "mkdir -p $config_path", @@ -180,18 +182,18 @@ public function handle(Server $server) "echo LICENSE_KEY=$license_key >> $config_path/.env", "echo BASE_URI=$base_uri >> $config_path/.env", ]; - } else if ($type === 'highlight') { + } elseif ($type === 'highlight') { $add_envs_command = [ "echo HIGHLIGHT_PROJECT_ID={$server->settings->logdrain_highlight_project_id} >> $config_path/.env", ]; - } else if ($type === 'axiom') { + } elseif ($type === 'axiom') { $add_envs_command = [ "echo AXIOM_DATASET_NAME={$server->settings->logdrain_axiom_dataset_name} >> $config_path/.env", "echo AXIOM_API_KEY={$server->settings->logdrain_axiom_api_key} >> $config_path/.env", ]; - } else if ($type === 'custom') { + } elseif ($type === 'custom') { $add_envs_command = [ - "touch $config_path/.env" + "touch $config_path/.env", ]; } else { throw new \Exception('Unknown log drain type.'); @@ -203,6 +205,7 @@ public function handle(Server $server) "cd $config_path && docker compose up -d --remove-orphans", ]; $command = array_merge($command, $add_envs_command, $restart_command); + return instant_remote_process($command, $server); } catch (\Throwable $e) { return handleError($e); diff --git a/app/Actions/Server/StartSentinel.php b/app/Actions/Server/StartSentinel.php index 6f3c81d77..eea429c79 100644 --- a/app/Actions/Server/StartSentinel.php +++ b/app/Actions/Server/StartSentinel.php @@ -2,12 +2,13 @@ namespace App\Actions\Server; -use Lorisleiva\Actions\Concerns\AsAction; use App\Models\Server; +use Lorisleiva\Actions\Concerns\AsAction; class StartSentinel { use AsAction; + public function handle(Server $server, $version = 'latest', bool $restart = false) { if ($restart) { @@ -15,8 +16,8 @@ public function handle(Server $server, $version = 'latest', bool $restart = fals } instant_remote_process([ "docker run --rm --pull always -d -e \"SCHEDULER=true\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version", - "chown -R 9999:root /data/coolify/metrics /data/coolify/logs", - "chmod -R 700 /data/coolify/metrics /data/coolify/logs" + 'chown -R 9999:root /data/coolify/metrics /data/coolify/logs', + 'chmod -R 700 /data/coolify/metrics /data/coolify/logs', ], $server, false); } } diff --git a/app/Actions/Server/UpdateCoolify.php b/app/Actions/Server/UpdateCoolify.php index 7fb788652..0e97d2aed 100644 --- a/app/Actions/Server/UpdateCoolify.php +++ b/app/Actions/Server/UpdateCoolify.php @@ -2,15 +2,18 @@ namespace App\Actions\Server; -use Lorisleiva\Actions\Concerns\AsAction; use App\Models\InstanceSettings; use App\Models\Server; +use Lorisleiva\Actions\Concerns\AsAction; class UpdateCoolify { use AsAction; + public ?Server $server = null; + public ?string $latestVersion = null; + public ?string $currentVersion = null; public function handle($manual_update = false) @@ -19,14 +22,14 @@ public function handle($manual_update = false) $settings = InstanceSettings::get(); ray('Running InstanceAutoUpdateJob'); $this->server = Server::find(0); - if (!$this->server) { + if (! $this->server) { return; } CleanupDocker::run($this->server, false); $this->latestVersion = get_latest_version_of_coolify(); $this->currentVersion = config('version'); - if (!$manual_update) { - if (!$settings->is_auto_update_enabled) { + if (! $manual_update) { + if (! $settings->is_auto_update_enabled) { return; } if ($this->latestVersion === $this->currentVersion) { @@ -46,14 +49,15 @@ private function update() { if (isDev()) { remote_process([ - "sleep 10" + 'sleep 10', ], $this->server); + return; } remote_process([ - "curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh", - "bash /data/coolify/source/upgrade.sh $this->latestVersion" + 'curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh', + "bash /data/coolify/source/upgrade.sh $this->latestVersion", ], $this->server); - return; + } } diff --git a/app/Actions/Service/DeleteService.php b/app/Actions/Service/DeleteService.php index 420f40f3b..194cf4db9 100644 --- a/app/Actions/Service/DeleteService.php +++ b/app/Actions/Service/DeleteService.php @@ -2,12 +2,13 @@ namespace App\Actions\Service; -use Lorisleiva\Actions\Concerns\AsAction; use App\Models\Service; +use Lorisleiva\Actions\Concerns\AsAction; class DeleteService { use AsAction; + public function handle(Service $service) { try { diff --git a/app/Actions/Service/StartService.php b/app/Actions/Service/StartService.php index 30b301095..4b6a25dcc 100644 --- a/app/Actions/Service/StartService.php +++ b/app/Actions/Service/StartService.php @@ -2,26 +2,27 @@ namespace App\Actions\Service; -use Lorisleiva\Actions\Concerns\AsAction; use App\Models\Service; +use Lorisleiva\Actions\Concerns\AsAction; use Symfony\Component\Yaml\Yaml; class StartService { use AsAction; + public function handle(Service $service) { - ray('Starting service: ' . $service->name); + ray('Starting service: '.$service->name); $service->saveComposeConfigs(); - $commands[] = "cd " . $service->workdir(); + $commands[] = 'cd '.$service->workdir(); $commands[] = "echo 'Saved configuration files to {$service->workdir()}.'"; $commands[] = "echo 'Creating Docker network.'"; $commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid"; - $commands[] = "echo Starting service."; + $commands[] = 'echo Starting service.'; $commands[] = "echo 'Pulling images.'"; - $commands[] = "docker compose pull"; + $commands[] = 'docker compose pull'; $commands[] = "echo 'Starting containers.'"; - $commands[] = "docker compose up -d --remove-orphans --force-recreate --build"; + $commands[] = 'docker compose up -d --remove-orphans --force-recreate --build'; $commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true"; if (data_get($service, 'connect_to_docker_network')) { $compose = data_get($service, 'docker_compose', []); @@ -32,6 +33,7 @@ public function handle(Service $service) } } $activity = remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged'); + return $activity; } } diff --git a/app/Actions/Service/StopService.php b/app/Actions/Service/StopService.php index 343b6d364..4c0042ebd 100644 --- a/app/Actions/Service/StopService.php +++ b/app/Actions/Service/StopService.php @@ -2,20 +2,21 @@ namespace App\Actions\Service; -use Lorisleiva\Actions\Concerns\AsAction; use App\Models\Service; +use Lorisleiva\Actions\Concerns\AsAction; class StopService { use AsAction; + public function handle(Service $service) { try { $server = $service->destination->server; - if (!$server->isFunctional()) { + if (! $server->isFunctional()) { return 'Server is not functional'; } - ray('Stopping service: ' . $service->name); + ray('Stopping service: '.$service->name); $applications = $service->applications()->get(); foreach ($applications as $application) { instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server); @@ -33,6 +34,7 @@ public function handle(Service $service) } catch (\Exception $e) { echo $e->getMessage(); ray($e->getMessage()); + return $e->getMessage(); } diff --git a/app/Actions/Shared/ComplexStatusCheck.php b/app/Actions/Shared/ComplexStatusCheck.php index 7987257f2..5a7ba6637 100644 --- a/app/Actions/Shared/ComplexStatusCheck.php +++ b/app/Actions/Shared/ComplexStatusCheck.php @@ -8,18 +8,21 @@ class ComplexStatusCheck { use AsAction; + public function handle(Application $application) { $servers = $application->additional_servers; $servers->push($application->destination->server); foreach ($servers as $server) { $is_main_server = $application->destination->server->id === $server->id; - if (!$server->isFunctional()) { + if (! $server->isFunctional()) { if ($is_main_server) { $application->update(['status' => 'exited:unhealthy']); + continue; } else { $application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']); + continue; } } @@ -44,9 +47,11 @@ public function handle(Application $application) } else { if ($is_main_server) { $application->update(['status' => 'exited:unhealthy']); + continue; } else { $application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']); + continue; } } diff --git a/app/Actions/Shared/PullImage.php b/app/Actions/Shared/PullImage.php index 42713b227..4bd1cf453 100644 --- a/app/Actions/Shared/PullImage.php +++ b/app/Actions/Shared/PullImage.php @@ -8,17 +8,20 @@ class PullImage { use AsAction; + public function handle(Service $resource) { $resource->saveComposeConfigs(); - $commands[] = "cd " . $resource->workdir(); + $commands[] = 'cd '.$resource->workdir(); $commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'"; - $commands[] = "docker compose pull"; + $commands[] = 'docker compose pull'; $server = data_get($resource, 'server'); - if (!$server) return; + if (! $server) { + return; + } instant_remote_process($commands, $resource->server); } diff --git a/app/Console/Commands/AdminRemoveUser.php b/app/Console/Commands/AdminRemoveUser.php index 76af0a97f..d4534399c 100644 --- a/app/Console/Commands/AdminRemoveUser.php +++ b/app/Console/Commands/AdminRemoveUser.php @@ -2,7 +2,6 @@ namespace App\Console\Commands; -use App\Models\Server; use App\Models\User; use Illuminate\Console\Command; @@ -29,9 +28,10 @@ public function handle() { try { $email = $this->argument('email'); - $confirm = $this->confirm('Are you sure you want to remove user with email: ' . $email . '?'); - if (!$confirm) { + $confirm = $this->confirm('Are you sure you want to remove user with email: '.$email.'?'); + if (! $confirm) { $this->info('User removal cancelled.'); + return; } $this->info("Removing user with email: $email"); @@ -40,6 +40,7 @@ public function handle() foreach ($teams as $team) { if ($team->members->count() > 1) { $this->error('User is a member of a team with more than one member. Please remove user from team first.'); + return; } $team->delete(); @@ -48,6 +49,7 @@ public function handle() } catch (\Exception $e) { $this->error('Failed to remove user.'); $this->error($e->getMessage()); + return; } } diff --git a/app/Console/Commands/CleanupApplicationDeploymentQueue.php b/app/Console/Commands/CleanupApplicationDeploymentQueue.php index 7c871d10b..f068e3eb2 100644 --- a/app/Console/Commands/CleanupApplicationDeploymentQueue.php +++ b/app/Console/Commands/CleanupApplicationDeploymentQueue.php @@ -8,6 +8,7 @@ class CleanupApplicationDeploymentQueue extends Command { protected $signature = 'cleanup:application-deployment-queue {--team-id=}'; + protected $description = 'CleanupApplicationDeploymentQueue'; public function handle() @@ -15,10 +16,10 @@ public function handle() $team_id = $this->option('team-id'); $servers = \App\Models\Server::where('team_id', $team_id)->get(); foreach ($servers as $server) { - $deployments = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->where("server_id", $server->id)->get(); + $deployments = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->where('server_id', $server->id)->get(); foreach ($deployments as $deployment) { $deployment->update(['status' => 'failed']); - instant_remote_process(['docker rm -f ' . $deployment->deployment_uuid], $server, false); + instant_remote_process(['docker rm -f '.$deployment->deployment_uuid], $server, false); } } } diff --git a/app/Console/Commands/CleanupDatabase.php b/app/Console/Commands/CleanupDatabase.php index 9e3a58a7e..1e177ca62 100644 --- a/app/Console/Commands/CleanupDatabase.php +++ b/app/Console/Commands/CleanupDatabase.php @@ -8,6 +8,7 @@ class CleanupDatabase extends Command { protected $signature = 'cleanup:database {--yes}'; + protected $description = 'Cleanup database'; public function handle() diff --git a/app/Console/Commands/CleanupQueue.php b/app/Console/Commands/CleanupQueue.php index 4d8fe8f6a..fd2b637ac 100644 --- a/app/Console/Commands/CleanupQueue.php +++ b/app/Console/Commands/CleanupQueue.php @@ -8,6 +8,7 @@ class CleanupQueue extends Command { protected $signature = 'cleanup:queue'; + protected $description = 'Cleanup Queue'; public function handle() diff --git a/app/Console/Commands/CleanupStuckedResources.php b/app/Console/Commands/CleanupStuckedResources.php index afc00140c..fbbf2c820 100644 --- a/app/Console/Commands/CleanupStuckedResources.php +++ b/app/Console/Commands/CleanupStuckedResources.php @@ -20,6 +20,7 @@ class CleanupStuckedResources extends Command { protected $signature = 'cleanup:stucked-resources'; + protected $description = 'Cleanup Stucked Resources'; public function handle() @@ -28,6 +29,7 @@ public function handle() echo "Running cleanup stucked resources.\n"; $this->cleanup_stucked_resources(); } + private function cleanup_stucked_resources() { @@ -142,7 +144,7 @@ private function cleanup_stucked_resources() try { $scheduled_tasks = ScheduledTask::all(); foreach ($scheduled_tasks as $scheduled_task) { - if (!$scheduled_task->service && !$scheduled_task->application) { + if (! $scheduled_task->service && ! $scheduled_task->application) { echo "Deleting stuck scheduledtask: {$scheduled_task->name}\n"; $scheduled_task->delete(); } @@ -155,19 +157,22 @@ private function cleanup_stucked_resources() try { $applications = Application::all(); foreach ($applications as $application) { - if (!data_get($application, 'environment')) { - echo 'Application without environment: ' . $application->name . '\n'; + if (! data_get($application, 'environment')) { + echo 'Application without environment: '.$application->name.'\n'; $application->forceDelete(); + continue; } - if (!$application->destination()) { - echo 'Application without destination: ' . $application->name . '\n'; + if (! $application->destination()) { + echo 'Application without destination: '.$application->name.'\n'; $application->forceDelete(); + continue; } - if (!data_get($application, 'destination.server')) { - echo 'Application without server: ' . $application->name . '\n'; + if (! data_get($application, 'destination.server')) { + echo 'Application without server: '.$application->name.'\n'; $application->forceDelete(); + continue; } } @@ -177,19 +182,22 @@ private function cleanup_stucked_resources() try { $postgresqls = StandalonePostgresql::all()->where('id', '!=', 0); foreach ($postgresqls as $postgresql) { - if (!data_get($postgresql, 'environment')) { - echo 'Postgresql without environment: ' . $postgresql->name . '\n'; + if (! data_get($postgresql, 'environment')) { + echo 'Postgresql without environment: '.$postgresql->name.'\n'; $postgresql->forceDelete(); + continue; } - if (!$postgresql->destination()) { - echo 'Postgresql without destination: ' . $postgresql->name . '\n'; + if (! $postgresql->destination()) { + echo 'Postgresql without destination: '.$postgresql->name.'\n'; $postgresql->forceDelete(); + continue; } - if (!data_get($postgresql, 'destination.server')) { - echo 'Postgresql without server: ' . $postgresql->name . '\n'; + if (! data_get($postgresql, 'destination.server')) { + echo 'Postgresql without server: '.$postgresql->name.'\n'; $postgresql->forceDelete(); + continue; } } @@ -199,19 +207,22 @@ private function cleanup_stucked_resources() try { $redis = StandaloneRedis::all(); foreach ($redis as $redis) { - if (!data_get($redis, 'environment')) { - echo 'Redis without environment: ' . $redis->name . '\n'; + if (! data_get($redis, 'environment')) { + echo 'Redis without environment: '.$redis->name.'\n'; $redis->forceDelete(); + continue; } - if (!$redis->destination()) { - echo 'Redis without destination: ' . $redis->name . '\n'; + if (! $redis->destination()) { + echo 'Redis without destination: '.$redis->name.'\n'; $redis->forceDelete(); + continue; } - if (!data_get($redis, 'destination.server')) { - echo 'Redis without server: ' . $redis->name . '\n'; + if (! data_get($redis, 'destination.server')) { + echo 'Redis without server: '.$redis->name.'\n'; $redis->forceDelete(); + continue; } } @@ -222,19 +233,22 @@ private function cleanup_stucked_resources() try { $mongodbs = StandaloneMongodb::all(); foreach ($mongodbs as $mongodb) { - if (!data_get($mongodb, 'environment')) { - echo 'Mongodb without environment: ' . $mongodb->name . '\n'; + if (! data_get($mongodb, 'environment')) { + echo 'Mongodb without environment: '.$mongodb->name.'\n'; $mongodb->forceDelete(); + continue; } - if (!$mongodb->destination()) { - echo 'Mongodb without destination: ' . $mongodb->name . '\n'; + if (! $mongodb->destination()) { + echo 'Mongodb without destination: '.$mongodb->name.'\n'; $mongodb->forceDelete(); + continue; } - if (!data_get($mongodb, 'destination.server')) { - echo 'Mongodb without server: ' . $mongodb->name . '\n'; + if (! data_get($mongodb, 'destination.server')) { + echo 'Mongodb without server: '.$mongodb->name.'\n'; $mongodb->forceDelete(); + continue; } } @@ -245,19 +259,22 @@ private function cleanup_stucked_resources() try { $mysqls = StandaloneMysql::all(); foreach ($mysqls as $mysql) { - if (!data_get($mysql, 'environment')) { - echo 'Mysql without environment: ' . $mysql->name . '\n'; + if (! data_get($mysql, 'environment')) { + echo 'Mysql without environment: '.$mysql->name.'\n'; $mysql->forceDelete(); + continue; } - if (!$mysql->destination()) { - echo 'Mysql without destination: ' . $mysql->name . '\n'; + if (! $mysql->destination()) { + echo 'Mysql without destination: '.$mysql->name.'\n'; $mysql->forceDelete(); + continue; } - if (!data_get($mysql, 'destination.server')) { - echo 'Mysql without server: ' . $mysql->name . '\n'; + if (! data_get($mysql, 'destination.server')) { + echo 'Mysql without server: '.$mysql->name.'\n'; $mysql->forceDelete(); + continue; } } @@ -268,19 +285,22 @@ private function cleanup_stucked_resources() try { $mariadbs = StandaloneMariadb::all(); foreach ($mariadbs as $mariadb) { - if (!data_get($mariadb, 'environment')) { - echo 'Mariadb without environment: ' . $mariadb->name . '\n'; + if (! data_get($mariadb, 'environment')) { + echo 'Mariadb without environment: '.$mariadb->name.'\n'; $mariadb->forceDelete(); + continue; } - if (!$mariadb->destination()) { - echo 'Mariadb without destination: ' . $mariadb->name . '\n'; + if (! $mariadb->destination()) { + echo 'Mariadb without destination: '.$mariadb->name.'\n'; $mariadb->forceDelete(); + continue; } - if (!data_get($mariadb, 'destination.server')) { - echo 'Mariadb without server: ' . $mariadb->name . '\n'; + if (! data_get($mariadb, 'destination.server')) { + echo 'Mariadb without server: '.$mariadb->name.'\n'; $mariadb->forceDelete(); + continue; } } @@ -291,19 +311,22 @@ private function cleanup_stucked_resources() try { $services = Service::all(); foreach ($services as $service) { - if (!data_get($service, 'environment')) { - echo 'Service without environment: ' . $service->name . '\n'; + if (! data_get($service, 'environment')) { + echo 'Service without environment: '.$service->name.'\n'; $service->forceDelete(); + continue; } - if (!$service->destination()) { - echo 'Service without destination: ' . $service->name . '\n'; + if (! $service->destination()) { + echo 'Service without destination: '.$service->name.'\n'; $service->forceDelete(); + continue; } - if (!data_get($service, 'server')) { - echo 'Service without server: ' . $service->name . '\n'; + if (! data_get($service, 'server')) { + echo 'Service without server: '.$service->name.'\n'; $service->forceDelete(); + continue; } } @@ -313,9 +336,10 @@ private function cleanup_stucked_resources() try { $serviceApplications = ServiceApplication::all(); foreach ($serviceApplications as $service) { - if (!data_get($service, 'service')) { - echo 'ServiceApplication without service: ' . $service->name . '\n'; + if (! data_get($service, 'service')) { + echo 'ServiceApplication without service: '.$service->name.'\n'; $service->forceDelete(); + continue; } } @@ -325,9 +349,10 @@ private function cleanup_stucked_resources() try { $serviceDatabases = ServiceDatabase::all(); foreach ($serviceDatabases as $service) { - if (!data_get($service, 'service')) { - echo 'ServiceDatabase without service: ' . $service->name . '\n'; + if (! data_get($service, 'service')) { + echo 'ServiceDatabase without service: '.$service->name.'\n'; $service->forceDelete(); + continue; } } diff --git a/app/Console/Commands/CleanupUnreachableServers.php b/app/Console/Commands/CleanupUnreachableServers.php index b63dc1d36..328628039 100644 --- a/app/Console/Commands/CleanupUnreachableServers.php +++ b/app/Console/Commands/CleanupUnreachableServers.php @@ -8,6 +8,7 @@ class CleanupUnreachableServers extends Command { protected $signature = 'cleanup:unreachable-servers'; + protected $description = 'Cleanup Unreachable Servers (7 days)'; public function handle() @@ -19,7 +20,7 @@ public function handle() echo "Cleanup unreachable server ($server->id) with name $server->name"; send_internal_notification("Server $server->name is unreachable for 7 days. Cleaning up..."); $server->update([ - 'ip' => '1.2.3.4' + 'ip' => '1.2.3.4', ]); } } diff --git a/app/Console/Commands/Dev.php b/app/Console/Commands/Dev.php index ba60826d1..80059bf00 100644 --- a/app/Console/Commands/Dev.php +++ b/app/Console/Commands/Dev.php @@ -10,6 +10,7 @@ class Dev extends Command { protected $signature = 'dev:init'; + protected $description = 'Init the app in dev mode'; public function handle() @@ -21,7 +22,7 @@ public function handle() } // Seed database if it's empty $settings = InstanceSettings::find(0); - if (!$settings) { + if (! $settings) { echo "Initializing instance, seeding database.\n"; Artisan::call('migrate --seed'); } else { diff --git a/app/Console/Commands/Emails.php b/app/Console/Commands/Emails.php index db4122b65..8ad0d458f 100644 --- a/app/Console/Commands/Emails.php +++ b/app/Console/Commands/Emails.php @@ -2,12 +2,10 @@ namespace App\Console\Commands; -use App\Jobs\DatabaseBackupStatusJob; use App\Jobs\SendConfirmationForWaitlistJob; use App\Models\Application; use App\Models\ApplicationPreview; use App\Models\ScheduledDatabaseBackup; -use App\Models\ScheduledDatabaseBackupExecution; use App\Models\Server; use App\Models\StandalonePostgresql; use App\Models\Team; @@ -49,7 +47,9 @@ class Emails extends Command * Execute the console command. */ private ?MailMessage $mail = null; + private ?string $email = null; + public function handle() { $type = select( @@ -73,21 +73,22 @@ public function handle() ); $emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection']; if (isDev()) { - $this->email = "test@example.com"; + $this->email = 'test@example.com'; } else { - if (!in_array($type, $emailsGathered)) { + if (! in_array($type, $emailsGathered)) { $this->email = text('Email Address to send to:'); } } set_transanctional_email_settings(); $this->mail = new MailMessage(); - $this->mail->subject("Test Email"); + $this->mail->subject('Test Email'); switch ($type) { case 'updates': $teams = Team::all(); - if (!$teams || $teams->isEmpty()) { - echo 'No teams found.' . PHP_EOL; + if (! $teams || $teams->isEmpty()) { + echo 'No teams found.'.PHP_EOL; + return; } $emails = []; @@ -99,7 +100,7 @@ public function handle() } } $emails = array_unique($emails); - $this->info("Sending to " . count($emails) . " emails."); + $this->info('Sending to '.count($emails).' emails.'); foreach ($emails as $email) { $this->info($email); } @@ -111,7 +112,7 @@ public function handle() $unsubscribeUrl = route('unsubscribe.marketing.emails', [ 'token' => encrypt($email), ]); - $this->mail->view('emails.updates', ["unsubscribeUrl" => $unsubscribeUrl]); + $this->mail->view('emails.updates', ['unsubscribeUrl' => $unsubscribeUrl]); $this->sendEmail($email); } } @@ -157,7 +158,7 @@ public function handle() case 'application-deployment-failed': $application = Application::all()->first(); $preview = ApplicationPreview::all()->first(); - if (!$preview) { + if (! $preview) { $preview = ApplicationPreview::create([ 'application_id' => $application->id, 'pull_request_id' => 1, @@ -178,7 +179,7 @@ public function handle() case 'backup-failed': $backup = ScheduledDatabaseBackup::all()->first(); $db = StandalonePostgresql::all()->first(); - if (!$backup) { + if (! $backup) { $backup = ScheduledDatabaseBackup::create([ 'enabled' => true, 'frequency' => 'daily', @@ -188,14 +189,14 @@ public function handle() 'team_id' => 0, ]); } - $output = 'Because of an error, the backup of the database ' . $db->name . ' failed.'; + $output = 'Because of an error, the backup of the database '.$db->name.' failed.'; $this->mail = (new BackupFailed($backup, $db, $output))->toMail(); $this->sendEmail(); break; case 'backup-success': $backup = ScheduledDatabaseBackup::all()->first(); $db = StandalonePostgresql::all()->first(); - if (!$backup) { + if (! $backup) { $backup = ScheduledDatabaseBackup::create([ 'enabled' => true, 'frequency' => 'daily', @@ -244,8 +245,9 @@ public function handle() $this->mail->view('emails.before-trial-conversion'); $this->mail->subject('Trial period has been added for all subscription plans.'); $teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get(); - if (!$teams || $teams->isEmpty()) { - echo 'No teams found.' . PHP_EOL; + if (! $teams || $teams->isEmpty()) { + echo 'No teams found.'.PHP_EOL; + return; } $emails = []; @@ -257,7 +259,7 @@ public function handle() } } $emails = array_unique($emails); - $this->info("Sending to " . count($emails) . " emails."); + $this->info('Sending to '.count($emails).' emails.'); foreach ($emails as $email) { $this->info($email); } @@ -271,7 +273,7 @@ public function handle() case 'realusers-server-lost-connection': $serverId = text('Server Id'); $server = Server::find($serverId); - if (!$server) { + if (! $server) { throw new Exception('Server not found'); } $admins = []; @@ -281,7 +283,7 @@ public function handle() $admins[] = $member->email; } } - $this->info('Sending to ' . count($admins) . ' admins.'); + $this->info('Sending to '.count($admins).' admins.'); foreach ($admins as $admin) { $this->info($admin); } @@ -289,14 +291,15 @@ public function handle() $this->mail->view('emails.server-lost-connection', [ 'name' => $server->name, ]); - $this->mail->subject('Action required: Server ' . $server->name . ' lost connection.'); + $this->mail->subject('Action required: Server '.$server->name.' lost connection.'); foreach ($admins as $email) { $this->sendEmail($email); } break; } } - private function sendEmail(string $email = null) + + private function sendEmail(?string $email = null) { if ($email) { $this->email = $email; @@ -307,7 +310,7 @@ private function sendEmail(string $email = null) fn (Message $message) => $message ->to($this->email) ->subject($this->mail->subject) - ->html((string)$this->mail->render()) + ->html((string) $this->mail->render()) ); $this->info("Email sent to $this->email successfully. 📧"); } diff --git a/app/Console/Commands/Horizon.php b/app/Console/Commands/Horizon.php index 8dd64a246..65a142d6e 100644 --- a/app/Console/Commands/Horizon.php +++ b/app/Console/Commands/Horizon.php @@ -7,7 +7,9 @@ class Horizon extends Command { protected $signature = 'start:horizon'; + protected $description = 'Start Horizon'; + public function handle() { if (config('coolify.is_horizon_enabled')) { diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php index 06414f715..50c9fe29b 100644 --- a/app/Console/Commands/Init.php +++ b/app/Console/Commands/Init.php @@ -15,6 +15,7 @@ class Init extends Command { protected $signature = 'app:init {--full-cleanup} {--cleanup-deployments}'; + protected $description = 'Cleanup instance related stuffs'; public function handle() @@ -26,6 +27,7 @@ public function handle() if ($cleanup_deployments) { echo "Running cleanup deployments.\n"; $this->cleanup_in_progress_application_deployments(); + return; } if ($full_cleanup) { @@ -35,7 +37,7 @@ public function handle() $this->cleanup_stucked_helper_containers(); $this->call('cleanup:queue'); $this->call('cleanup:stucked-resources'); - if (!isCloud()) { + if (! isCloud()) { try { $server = Server::find(0)->first(); $server->setupDynamicProxyConfiguration(); @@ -45,13 +47,14 @@ public function handle() } $settings = InstanceSettings::get(); - if (!is_null(env('AUTOUPDATE', null))) { + if (! is_null(env('AUTOUPDATE', null))) { if (env('AUTOUPDATE') == true) { $settings->update(['is_auto_update_enabled' => true]); } else { $settings->update(['is_auto_update_enabled' => false]); } } + return; } $this->cleanup_stucked_helper_containers(); @@ -66,7 +69,7 @@ private function restore_coolify_db_backup() echo "Restoring coolify db backup\n"; $database->restore(); $scheduledBackup = ScheduledDatabaseBackup::find(0); - if (!$scheduledBackup) { + if (! $scheduledBackup) { ScheduledDatabaseBackup::create([ 'id' => 0, 'enabled' => true, @@ -82,6 +85,7 @@ private function restore_coolify_db_backup() echo "Error in restoring coolify db backup: {$e->getMessage()}\n"; } } + private function cleanup_stucked_helper_containers() { $servers = Server::all(); @@ -91,6 +95,7 @@ private function cleanup_stucked_helper_containers() } } } + private function alive() { $id = config('app.id'); @@ -99,6 +104,7 @@ private function alive() $do_not_track = data_get($settings, 'do_not_track'); if ($do_not_track == true) { echo "Skipping alive as do_not_track is enabled\n"; + return; } try { diff --git a/app/Console/Commands/NotifyDemo.php b/app/Console/Commands/NotifyDemo.php index 72e4a37e6..81333b868 100644 --- a/app/Console/Commands/NotifyDemo.php +++ b/app/Console/Commands/NotifyDemo.php @@ -3,6 +3,7 @@ namespace App\Console\Commands; use Illuminate\Console\Command; + use function Termwind\ask; use function Termwind\render; use function Termwind\style; @@ -32,6 +33,7 @@ public function handle() if (blank($channel)) { $this->showHelp(); + return; } diff --git a/app/Console/Commands/RootChangeEmail.php b/app/Console/Commands/RootChangeEmail.php index 27344f8a5..c87a545c5 100644 --- a/app/Console/Commands/RootChangeEmail.php +++ b/app/Console/Commands/RootChangeEmail.php @@ -35,6 +35,7 @@ public function handle() $this->info('Root user\'s email updated successfully.'); } catch (\Exception $e) { $this->error('Failed to update root user\'s email.'); + return; } } diff --git a/app/Console/Commands/RootResetPassword.php b/app/Console/Commands/RootResetPassword.php index af2b1a45c..f36c11a4f 100644 --- a/app/Console/Commands/RootResetPassword.php +++ b/app/Console/Commands/RootResetPassword.php @@ -34,6 +34,7 @@ public function handle() $passwordAgain = password('Again'); if ($password != $passwordAgain) { $this->error('Passwords do not match.'); + return; } $this->info('Updating root password...'); @@ -42,6 +43,7 @@ public function handle() $this->info('Root password updated successfully.'); } catch (\Exception $e) { $this->error('Failed to update root password.'); + return; } } diff --git a/app/Console/Commands/Scheduler.php b/app/Console/Commands/Scheduler.php index eab623802..304cb357d 100644 --- a/app/Console/Commands/Scheduler.php +++ b/app/Console/Commands/Scheduler.php @@ -7,7 +7,9 @@ class Scheduler extends Command { protected $signature = 'start:scheduler'; + protected $description = 'Start Scheduler'; + public function handle() { if (config('coolify.is_scheduler_enabled')) { diff --git a/app/Console/Commands/ServicesDelete.php b/app/Console/Commands/ServicesDelete.php index bc30bd842..b5a74166a 100644 --- a/app/Console/Commands/ServicesDelete.php +++ b/app/Console/Commands/ServicesDelete.php @@ -48,11 +48,13 @@ public function handle() $this->deleteServer(); } } + private function deleteServer() { $servers = Server::all(); if ($servers->count() === 0) { $this->error('There are no applications to delete.'); + return; } $serversToDelete = multiselect( @@ -64,19 +66,21 @@ private function deleteServer() $toDelete = $servers->where('id', $server)->first(); if ($toDelete) { $this->info($toDelete); - $confirmed = confirm("Are you sure you want to delete all selected resources?"); - if (!$confirmed) { + $confirmed = confirm('Are you sure you want to delete all selected resources?'); + if (! $confirmed) { break; } $toDelete->delete(); } } } + private function deleteApplication() { $applications = Application::all(); if ($applications->count() === 0) { $this->error('There are no applications to delete.'); + return; } $applicationsToDelete = multiselect( @@ -88,19 +92,21 @@ private function deleteApplication() $toDelete = $applications->where('id', $application)->first(); if ($toDelete) { $this->info($toDelete); - $confirmed = confirm("Are you sure you want to delete all selected resources? "); - if (!$confirmed) { + $confirmed = confirm('Are you sure you want to delete all selected resources? '); + if (! $confirmed) { break; } DeleteResourceJob::dispatch($toDelete); } } } + private function deleteDatabase() { $databases = StandalonePostgresql::all(); if ($databases->count() === 0) { $this->error('There are no databases to delete.'); + return; } $databasesToDelete = multiselect( @@ -112,19 +118,21 @@ private function deleteDatabase() $toDelete = $databases->where('id', $database)->first(); if ($toDelete) { $this->info($toDelete); - $confirmed = confirm("Are you sure you want to delete all selected resources?"); - if (!$confirmed) { + $confirmed = confirm('Are you sure you want to delete all selected resources?'); + if (! $confirmed) { return; } DeleteResourceJob::dispatch($toDelete); } } } + private function deleteService() { $services = Service::all(); if ($services->count() === 0) { $this->error('There are no services to delete.'); + return; } $servicesToDelete = multiselect( @@ -136,8 +144,8 @@ private function deleteService() $toDelete = $services->where('id', $service)->first(); if ($toDelete) { $this->info($toDelete); - $confirmed = confirm("Are you sure you want to delete all selected resources?"); - if (!$confirmed) { + $confirmed = confirm('Are you sure you want to delete all selected resources?'); + if (! $confirmed) { return; } DeleteResourceJob::dispatch($toDelete); diff --git a/app/Console/Commands/ServicesGenerate.php b/app/Console/Commands/ServicesGenerate.php index 0471e2324..de64afefa 100644 --- a/app/Console/Commands/ServicesGenerate.php +++ b/app/Console/Commands/ServicesGenerate.php @@ -50,12 +50,13 @@ private function process_file($file) // $this->info($content); $ignore = collect(preg_grep('/^# ignore:/', explode("\n", $content)))->values(); if ($ignore->count() > 0) { - $ignore = (bool)str($ignore[0])->after('# ignore:')->trim()->value(); + $ignore = (bool) str($ignore[0])->after('# ignore:')->trim()->value(); } else { $ignore = false; } if ($ignore) { $this->info("Ignoring $file"); + return; } $this->info("Processing $file"); @@ -125,6 +126,7 @@ private function process_file($file) $env_file_base64 = base64_encode($env_file_content); $payload['envs'] = $env_file_base64; } + return $payload; } } diff --git a/app/Console/Commands/SyncBunny.php b/app/Console/Commands/SyncBunny.php index 939b3c927..7135cfc9c 100644 --- a/app/Console/Commands/SyncBunny.php +++ b/app/Console/Commands/SyncBunny.php @@ -33,30 +33,31 @@ public function handle() $that = $this; $only_template = $this->option('templates'); $only_version = $this->option('release'); - $bunny_cdn = "https://cdn.coollabs.io"; - $bunny_cdn_path = "coolify"; - $bunny_cdn_storage_name = "coolcdn"; + $bunny_cdn = 'https://cdn.coollabs.io'; + $bunny_cdn_path = 'coolify'; + $bunny_cdn_storage_name = 'coolcdn'; - $parent_dir = realpath(dirname(__FILE__) . '/../../..'); + $parent_dir = realpath(dirname(__FILE__).'/../../..'); - $compose_file = "docker-compose.yml"; - $compose_file_prod = "docker-compose.prod.yml"; - $install_script = "install.sh"; - $upgrade_script = "upgrade.sh"; - $production_env = ".env.production"; - $service_template = "service-templates.json"; + $compose_file = 'docker-compose.yml'; + $compose_file_prod = 'docker-compose.prod.yml'; + $install_script = 'install.sh'; + $upgrade_script = 'upgrade.sh'; + $production_env = '.env.production'; + $service_template = 'service-templates.json'; - $versions = "versions.json"; + $versions = 'versions.json'; PendingRequest::macro('storage', function ($fileName) use ($that) { $headers = [ 'AccessKey' => env('BUNNY_STORAGE_API_KEY'), 'Accept' => 'application/json', - 'Content-Type' => 'application/octet-stream' + 'Content-Type' => 'application/octet-stream', ]; - $fileStream = fopen($fileName, "r"); + $fileStream = fopen($fileName, 'r'); $file = fread($fileStream, filesize($fileName)); - $that->info('Uploading: ' . $fileName); + $that->info('Uploading: '.$fileName); + return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw(); }); PendingRequest::macro('purge', function ($url) use ($that) { @@ -64,20 +65,21 @@ public function handle() 'AccessKey' => env('BUNNY_API_KEY'), 'Accept' => 'application/json', ]; - $that->info('Purging: ' . $url); + $that->info('Purging: '.$url); + return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [ - "url" => $url, - "async" => false + 'url' => $url, + 'async' => false, ]); }); try { - if (!$only_template && !$only_version) { + if (! $only_template && ! $only_version) { $this->info('About to sync files (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.'); } if ($only_template) { $this->info('About to sync service-templates.json to BunnyCDN.'); - $confirmed = confirm("Are you sure you want to sync?"); - if (!$confirmed) { + $confirmed = confirm('Are you sure you want to sync?'); + if (! $confirmed) { return; } Http::pool(fn (Pool $pool) => [ @@ -85,15 +87,16 @@ public function handle() $pool->purge("$bunny_cdn/$bunny_cdn_path/$service_template"), ]); $this->info('Service template uploaded & purged...'); + return; - } else if ($only_version) { + } elseif ($only_version) { $this->info('About to sync versions.json to BunnyCDN.'); $file = file_get_contents("$parent_dir/$versions"); $json = json_decode($file, true); $actual_version = data_get($json, 'coolify.v4.version'); $confirmed = confirm("Are you sure you want to sync to {$actual_version}?"); - if (!$confirmed) { + if (! $confirmed) { return; } Http::pool(fn (Pool $pool) => [ @@ -101,10 +104,10 @@ public function handle() $pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"), ]); $this->info('versions.json uploaded & purged...'); + return; } - Http::pool(fn (Pool $pool) => [ $pool->storage(fileName: "$parent_dir/$compose_file")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file"), $pool->storage(fileName: "$parent_dir/$compose_file_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"), @@ -119,9 +122,9 @@ public function handle() $pool->purge("$bunny_cdn/$bunny_cdn_path/$upgrade_script"), $pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"), ]); - $this->info("All files uploaded & purged..."); + $this->info('All files uploaded & purged...'); } catch (\Throwable $e) { - $this->error("Error: " . $e->getMessage()); + $this->error('Error: '.$e->getMessage()); } } } diff --git a/app/Console/Commands/WaitlistInvite.php b/app/Console/Commands/WaitlistInvite.php index f3eefbcfa..88ff21d46 100644 --- a/app/Console/Commands/WaitlistInvite.php +++ b/app/Console/Commands/WaitlistInvite.php @@ -13,7 +13,9 @@ class WaitlistInvite extends Command { public Waitlist|User|null $next_patient = null; - public string|null $password = null; + + public ?string $password = null; + /** * The name and signature of the console command. * @@ -38,7 +40,9 @@ public function handle() $this->main(); } } - private function main() { + + private function main() + { if ($this->argument('email')) { if ($this->option('only-email')) { $this->next_patient = User::whereEmail($this->argument('email'))->first(); @@ -50,8 +54,9 @@ private function main() { } else { $this->next_patient = Waitlist::where('email', $this->argument('email'))->first(); } - if (!$this->next_patient) { + if (! $this->next_patient) { $this->error("{$this->argument('email')} not found in the waitlist."); + return; } } else { @@ -60,6 +65,7 @@ private function main() { if ($this->next_patient) { if ($this->option('only-email')) { $this->send_email(); + return; } $this->register_user(); @@ -69,10 +75,11 @@ private function main() { $this->info('No verified user found in the waitlist. 👀'); } } + private function register_user() { $already_registered = User::whereEmail($this->next_patient->email)->first(); - if (!$already_registered) { + if (! $already_registered) { $this->password = Str::password(); User::create([ 'name' => Str::of($this->next_patient->email)->before('@'), @@ -85,11 +92,13 @@ private function register_user() throw new \Exception('User already registered'); } } + private function remove_from_waitlist() { $this->next_patient->delete(); - $this->info("User removed from waitlist successfully."); + $this->info('User removed from waitlist successfully.'); } + private function send_email() { $token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password"); @@ -100,6 +109,6 @@ private function send_email() ]); $mail->subject('Congratulations! You are invited to join Coolify Cloud.'); send_user_an_email($mail, $this->next_patient->email); - $this->info("Email sent successfully. 📧"); + $this->info('Email sent successfully. 📧'); } } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 601e1517c..e2698d90e 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -4,13 +4,13 @@ use App\Jobs\CheckLogDrainContainerJob; use App\Jobs\CleanupInstanceStuffsJob; -use App\Jobs\DatabaseBackupJob; -use App\Jobs\ScheduledTaskJob; use App\Jobs\ContainerStatusJob; +use App\Jobs\DatabaseBackupJob; use App\Jobs\PullCoolifyImageJob; use App\Jobs\PullHelperImageJob; use App\Jobs\PullSentinelImageJob; use App\Jobs\PullTemplatesFromCDN; +use App\Jobs\ScheduledTaskJob; use App\Jobs\ServerStatusJob; use App\Models\ScheduledDatabaseBackup; use App\Models\ScheduledTask; @@ -22,6 +22,7 @@ class Kernel extends ConsoleKernel { private $all_servers; + protected function schedule(Schedule $schedule): void { $this->all_servers = Server::all(); @@ -55,6 +56,7 @@ protected function schedule(Schedule $schedule): void $schedule->command('uploads:clear')->everyTwoMinutes(); } } + private function pull_images($schedule) { $servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4'); @@ -65,6 +67,7 @@ private function pull_images($schedule) $schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer(); } } + private function check_resources($schedule) { if (isCloud()) { @@ -86,6 +89,7 @@ private function check_resources($schedule) $schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer(); } } + private function check_scheduled_backups($schedule) { $scheduled_backups = ScheduledDatabaseBackup::all(); @@ -93,12 +97,13 @@ private function check_scheduled_backups($schedule) return; } foreach ($scheduled_backups as $scheduled_backup) { - if (!$scheduled_backup->enabled) { + if (! $scheduled_backup->enabled) { continue; } if (is_null(data_get($scheduled_backup, 'database'))) { ray('database not found'); $scheduled_backup->delete(); + continue; } @@ -124,9 +129,10 @@ private function check_scheduled_tasks($schedule) $service = $scheduled_task->service; $application = $scheduled_task->application; - if (!$application && !$service) { + if (! $application && ! $service) { ray('application/service attached to scheduled task does not exist'); $scheduled_task->delete(); + continue; } if ($application) { @@ -150,7 +156,7 @@ private function check_scheduled_tasks($schedule) protected function commands(): void { - $this->load(__DIR__ . '/Commands'); + $this->load(__DIR__.'/Commands'); require base_path('routes/console.php'); } diff --git a/app/Data/CoolifyTaskArgs.php b/app/Data/CoolifyTaskArgs.php index e1e43f2f0..24132157a 100644 --- a/app/Data/CoolifyTaskArgs.php +++ b/app/Data/CoolifyTaskArgs.php @@ -12,18 +12,18 @@ class CoolifyTaskArgs extends Data { public function __construct( - public string $server_uuid, - public string $command, - public string $type, + public string $server_uuid, + public string $command, + public string $type, public ?string $type_uuid = null, public ?int $process_id = null, - public ?Model $model = null, - public ?string $status = null , - public bool $ignore_errors = false, + public ?Model $model = null, + public ?string $status = null, + public bool $ignore_errors = false, public $call_event_on_finish = null, public $call_event_data = null ) { - if(is_null($status)){ + if (is_null($status)) { $this->status = ProcessStatus::QUEUED->value; } } diff --git a/app/Data/ServerMetadata.php b/app/Data/ServerMetadata.php index b18ddab8e..b96efa622 100644 --- a/app/Data/ServerMetadata.php +++ b/app/Data/ServerMetadata.php @@ -9,7 +9,7 @@ class ServerMetadata extends Data { public function __construct( - public ?ProxyTypes $type, + public ?ProxyTypes $type, public ?ProxyStatus $status ) { } diff --git a/app/Events/ApplicationStatusChanged.php b/app/Events/ApplicationStatusChanged.php index 4224d4a29..4433248aa 100644 --- a/app/Events/ApplicationStatusChanged.php +++ b/app/Events/ApplicationStatusChanged.php @@ -2,9 +2,7 @@ namespace App\Events; -use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; @@ -13,14 +11,16 @@ class ApplicationStatusChanged implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; + public $teamId; + public function __construct($teamId = null) { if (is_null($teamId)) { $teamId = auth()->user()->currentTeam()->id ?? null; } if (is_null($teamId)) { - throw new \Exception("Team id is null"); + throw new \Exception('Team id is null'); } $this->teamId = $teamId; } diff --git a/app/Events/BackupCreated.php b/app/Events/BackupCreated.php index 41d0afcbc..45b2aacb7 100644 --- a/app/Events/BackupCreated.php +++ b/app/Events/BackupCreated.php @@ -2,9 +2,7 @@ namespace App\Events; -use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; @@ -13,14 +11,16 @@ class BackupCreated implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; + public $teamId; + public function __construct($teamId = null) { if (is_null($teamId)) { $teamId = auth()->user()->currentTeam()->id ?? null; } if (is_null($teamId)) { - throw new \Exception("Team id is null"); + throw new \Exception('Team id is null'); } $this->teamId = $teamId; } diff --git a/app/Events/DatabaseStatusChanged.php b/app/Events/DatabaseStatusChanged.php index 8f83406f4..190983c80 100644 --- a/app/Events/DatabaseStatusChanged.php +++ b/app/Events/DatabaseStatusChanged.php @@ -2,9 +2,7 @@ namespace App\Events; -use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; @@ -13,14 +11,16 @@ class DatabaseStatusChanged implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; + public $userId; + public function __construct($userId = null) { if (is_null($userId)) { $userId = auth()->user()->id ?? null; } if (is_null($userId)) { - throw new \Exception("User id is null"); + throw new \Exception('User id is null'); } $this->userId = $userId; } diff --git a/app/Events/ProxyStarted.php b/app/Events/ProxyStarted.php index a4e053171..ed62eccb1 100644 --- a/app/Events/ProxyStarted.php +++ b/app/Events/ProxyStarted.php @@ -9,6 +9,7 @@ class ProxyStarted { use Dispatchable, InteractsWithSockets, SerializesModels; + public function __construct(public $data) { diff --git a/app/Events/ProxyStatusChanged.php b/app/Events/ProxyStatusChanged.php index 42d276424..35eedef70 100644 --- a/app/Events/ProxyStatusChanged.php +++ b/app/Events/ProxyStatusChanged.php @@ -2,9 +2,7 @@ namespace App\Events; -use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; @@ -13,14 +11,16 @@ class ProxyStatusChanged implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; + public $teamId; + public function __construct($teamId = null) { if (is_null($teamId)) { $teamId = auth()->user()->currentTeam()->id ?? null; } if (is_null($teamId)) { - throw new \Exception("Team id is null"); + throw new \Exception('Team id is null'); } $this->teamId = $teamId; } diff --git a/app/Events/ServiceStatusChanged.php b/app/Events/ServiceStatusChanged.php index 3fe849190..e3e24a248 100644 --- a/app/Events/ServiceStatusChanged.php +++ b/app/Events/ServiceStatusChanged.php @@ -2,9 +2,7 @@ namespace App\Events; -use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; @@ -13,14 +11,16 @@ class ServiceStatusChanged implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; + public $userId; + public function __construct($userId = null) { if (is_null($userId)) { $userId = auth()->user()->id ?? null; } if (is_null($userId)) { - throw new \Exception("User id is null"); + throw new \Exception('User id is null'); } $this->userId = $userId; } diff --git a/app/Events/TestEvent.php b/app/Events/TestEvent.php index df677ba7a..2cc6683dc 100644 --- a/app/Events/TestEvent.php +++ b/app/Events/TestEvent.php @@ -2,9 +2,7 @@ namespace App\Events; -use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; @@ -13,7 +11,9 @@ class TestEvent implements ShouldBroadcast { use Dispatchable, InteractsWithSockets, SerializesModels; + public $teamId; + public function __construct() { $this->teamId = auth()->user()->currentTeam()->id; diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 5c8827085..254a8df7a 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -13,7 +13,6 @@ class Handler extends ExceptionHandler { - /** * A list of exception types with their corresponding custom log levels. * @@ -22,14 +21,16 @@ class Handler extends ExceptionHandler protected $levels = [ // ]; + /** * A list of the exception types that are not reported. * * @var array> */ protected $dontReport = [ - ProcessException::class + ProcessException::class, ]; + /** * A list of the inputs that are never flashed to the session on validation exceptions. * @@ -40,6 +41,7 @@ class Handler extends ExceptionHandler 'password', 'password_confirmation', ]; + private InstanceSettings $settings; protected function unauthenticated($request, AuthenticationException $exception) @@ -47,8 +49,10 @@ protected function unauthenticated($request, AuthenticationException $exception) if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) { return response()->json(['message' => $exception->getMessage()], 401); } + return redirect()->guest($exception->redirectTo() ?? route('login')); } + /** * Register the exception handling callbacks for the application. */ @@ -72,7 +76,7 @@ function (Scope $scope) { $scope->setUser( [ 'email' => $email, - 'instanceAdmin' => $instanceAdmin + 'instanceAdmin' => $instanceAdmin, ] ); } diff --git a/app/Exceptions/ProcessException.php b/app/Exceptions/ProcessException.php index 68dbb53b2..728a0d81b 100644 --- a/app/Exceptions/ProcessException.php +++ b/app/Exceptions/ProcessException.php @@ -6,5 +6,4 @@ class ProcessException extends Exception { - } diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/Deploy.php index f5798c52b..d2abe2e31 100644 --- a/app/Http/Controllers/Api/Deploy.php +++ b/app/Http/Controllers/Api/Deploy.php @@ -27,18 +27,20 @@ public function deployments(Request $request) return invalid_token(); } $servers = Server::whereTeamId($teamId)->get(); - $deployments_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn("server_id", $servers->pluck("id"))->get([ - "id", - "application_id", - "application_name", - "deployment_url", - "pull_request_id", - "server_name", - "server_id", - "status" + $deployments_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $servers->pluck('id'))->get([ + 'id', + 'application_id', + 'application_name', + 'deployment_url', + 'pull_request_id', + 'server_name', + 'server_id', + 'status', ])->sortBy('id')->toArray(); + return response()->json($deployments_per_server, 200); } + public function deploy(Request $request) { $teamId = get_team_id_from_token(); @@ -54,11 +56,13 @@ public function deploy(Request $request) } if ($tags) { return $this->by_tags($tags, $teamId, $force); - } else if ($uuids) { + } elseif ($uuids) { return $this->by_uuids($uuids, $teamId, $force); } + return response()->json(['error' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400); } + private function by_uuids(string $uuid, int $teamId, bool $force = false) { $uuids = explode(',', $uuid); @@ -82,10 +86,13 @@ private function by_uuids(string $uuid, int $teamId, bool $force = false) } if ($deployments->count() > 0) { $payload->put('deployments', $deployments->toArray()); + return response()->json($payload->toArray(), 200); } - return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); + + return response()->json(['error' => 'No resources found.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); } + public function by_tags(string $tags, int $team_id, bool $force = false) { $tags = explode(',', $tags); @@ -99,7 +106,7 @@ public function by_tags(string $tags, int $team_id, bool $force = false) $payload = collect(); foreach ($tags as $tag) { $found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first(); - if (!$found_tag) { + if (! $found_tag) { // $message->push("Tag {$tag} not found."); continue; } @@ -107,6 +114,7 @@ public function by_tags(string $tags, int $team_id, bool $force = false) $services = $found_tag->services()->get(); if ($applications->count() === 0 && $services->count() === 0) { $message->push("No resources found for tag {$tag}."); + continue; } foreach ($applications as $resource) { @@ -127,11 +135,13 @@ public function by_tags(string $tags, int $team_id, bool $force = false) if ($deployments->count() > 0) { $payload->put('details', $deployments->toArray()); } + return response()->json($payload->toArray(), 200); } - return response()->json(['error' => "No resources found with this tag.", 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); + return response()->json(['error' => 'No resources found with this tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404); } + public function deploy_resource($resource, bool $force = false): array { $message = null; @@ -148,58 +158,59 @@ public function deploy_resource($resource, bool $force = false): array force_rebuild: $force, ); $message = "Application {$resource->name} deployment queued."; - } else if ($type === 'App\Models\StandalonePostgresql') { + } elseif ($type === 'App\Models\StandalonePostgresql') { StartPostgresql::run($resource); $resource->update([ 'started_at' => now(), ]); $message = "Database {$resource->name} started."; - } else if ($type === 'App\Models\StandaloneRedis') { + } elseif ($type === 'App\Models\StandaloneRedis') { StartRedis::run($resource); $resource->update([ 'started_at' => now(), ]); $message = "Database {$resource->name} started."; - } else if ($type === 'App\Models\StandaloneKeydb') { + } elseif ($type === 'App\Models\StandaloneKeydb') { StartKeydb::run($resource); $resource->update([ 'started_at' => now(), ]); $message = "Database {$resource->name} started."; - } else if ($type === 'App\Models\StandaloneDragonfly') { + } elseif ($type === 'App\Models\StandaloneDragonfly') { StartDragonfly::run($resource); $resource->update([ 'started_at' => now(), ]); $message = "Database {$resource->name} started."; - } else if ($type === 'App\Models\StandaloneClickhouse') { + } elseif ($type === 'App\Models\StandaloneClickhouse') { StartClickhouse::run($resource); $resource->update([ 'started_at' => now(), ]); $message = "Database {$resource->name} started."; - } else if ($type === 'App\Models\StandaloneMongodb') { + } elseif ($type === 'App\Models\StandaloneMongodb') { StartMongodb::run($resource); $resource->update([ 'started_at' => now(), ]); $message = "Database {$resource->name} started."; - } else if ($type === 'App\Models\StandaloneMysql') { + } elseif ($type === 'App\Models\StandaloneMysql') { StartMysql::run($resource); $resource->update([ 'started_at' => now(), ]); $message = "Database {$resource->name} started."; - } else if ($type === 'App\Models\StandaloneMariadb') { + } elseif ($type === 'App\Models\StandaloneMariadb') { StartMariadb::run($resource); $resource->update([ 'started_at' => now(), ]); $message = "Database {$resource->name} started."; - } else if ($type === 'App\Models\Service') { + } elseif ($type === 'App\Models\Service') { StartService::run($resource); $message = "Service {$resource->name} started. It could take a while, be patient."; } + return ['message' => $message, 'deployment_uuid' => $deployment_uuid]; } } diff --git a/app/Http/Controllers/Api/Domains.php b/app/Http/Controllers/Api/Domains.php index f6468cdf0..c27ddf620 100644 --- a/app/Http/Controllers/Api/Domains.php +++ b/app/Http/Controllers/Api/Domains.php @@ -38,7 +38,7 @@ public function domains(Request $request) 'ip' => $settings->public_ipv6, ]); } - if (!$settings->public_ipv4 && !$settings->public_ipv6) { + if (! $settings->public_ipv4 && ! $settings->public_ipv6) { $domains->push([ 'domain' => $fqdn, 'ip' => $ip, @@ -74,7 +74,7 @@ public function domains(Request $request) 'ip' => $settings->public_ipv6, ]); } - if (!$settings->public_ipv4 && !$settings->public_ipv6) { + if (! $settings->public_ipv4 && ! $settings->public_ipv6) { $domains->push([ 'domain' => $fqdn, 'ip' => $ip, diff --git a/app/Http/Controllers/Api/Project.php b/app/Http/Controllers/Api/Project.php index 45d6b4059..baaf1eacb 100644 --- a/app/Http/Controllers/Api/Project.php +++ b/app/Http/Controllers/Api/Project.php @@ -15,8 +15,10 @@ public function projects(Request $request) return invalid_token(); } $projects = ModelsProject::whereTeamId($teamId)->select('id', 'name', 'uuid')->get(); + return response()->json($projects); } + public function project_by_uuid(Request $request) { $teamId = get_team_id_from_token(); @@ -24,8 +26,10 @@ public function project_by_uuid(Request $request) return invalid_token(); } $project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']); + return response()->json($project); } + public function environment_details(Request $request) { $teamId = get_team_id_from_token(); @@ -34,6 +38,7 @@ public function environment_details(Request $request) } $project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first(); $environment = $project->environments()->whereName(request()->environment_name)->first()->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']); + return response()->json($environment); } } diff --git a/app/Http/Controllers/Api/Resources.php b/app/Http/Controllers/Api/Resources.php index 4032d26e2..0d538b62e 100644 --- a/app/Http/Controllers/Api/Resources.php +++ b/app/Http/Controllers/Api/Resources.php @@ -30,9 +30,10 @@ public function resources(Request $request) $payload['status'] = $resource->status; } $payload['type'] = $resource->type(); + return $payload; }); + return response()->json($resources); } - } diff --git a/app/Http/Controllers/Api/Server.php b/app/Http/Controllers/Api/Server.php index bb5ef255b..9f88a3b28 100644 --- a/app/Http/Controllers/Api/Server.php +++ b/app/Http/Controllers/Api/Server.php @@ -17,10 +17,13 @@ public function servers(Request $request) $servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) { $server['is_reachable'] = $server->settings->is_reachable; $server['is_usable'] = $server->settings->is_usable; + return $server; }); + return response()->json($servers); } + public function server_by_uuid(Request $request) { $with_resources = $request->query('resources'); @@ -47,11 +50,13 @@ public function server_by_uuid(Request $request) } else { $payload['status'] = $resource->status; } + return $payload; }); } else { $server->load(['settings']); } + return response()->json($server); } } diff --git a/app/Http/Controllers/Api/Team.php b/app/Http/Controllers/Api/Team.php index d5b1f6209..c895f2c1b 100644 --- a/app/Http/Controllers/Api/Team.php +++ b/app/Http/Controllers/Api/Team.php @@ -14,8 +14,10 @@ public function teams(Request $request) return invalid_token(); } $teams = auth()->user()->teams; + return response()->json($teams); } + public function team_by_id(Request $request) { $id = $request->id; @@ -26,10 +28,12 @@ public function team_by_id(Request $request) $teams = auth()->user()->teams; $team = $teams->where('id', $id)->first(); if (is_null($team)) { - return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid"], 404); + return response()->json(['error' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid'], 404); } + return response()->json($team); } + public function members_by_id(Request $request) { $id = $request->id; @@ -40,10 +44,12 @@ public function members_by_id(Request $request) $teams = auth()->user()->teams; $team = $teams->where('id', $id)->first(); if (is_null($team)) { - return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid-members"], 404); + return response()->json(['error' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid-members'], 404); } + return response()->json($team->members); } + public function current_team(Request $request) { $teamId = get_team_id_from_token(); @@ -51,8 +57,10 @@ public function current_team(Request $request) return invalid_token(); } $team = auth()->user()->currentTeam(); + return response()->json($team); } + public function current_team_members(Request $request) { $teamId = get_team_id_from_token(); @@ -60,6 +68,7 @@ public function current_team_members(Request $request) return invalid_token(); } $team = auth()->user()->currentTeam(); + return response()->json($team->members); } } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index daba1cecb..3363d8164 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -14,40 +14,49 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Password; use Illuminate\Support\Str; -use Laravel\Fortify\Fortify; use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse; use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse; -use Illuminate\Support\Facades\Password; +use Laravel\Fortify\Fortify; class Controller extends BaseController { use AuthorizesRequests, ValidatesRequests; - public function realtime_test() { + public function realtime_test() + { if (auth()->user()?->currentTeam()->id !== 0) { return redirect(RouteServiceProvider::HOME); } TestEvent::dispatch(); + return 'Look at your other tab.'; } - public function verify() { + + public function verify() + { return view('auth.verify-email'); } - public function email_verify(EmailVerificationRequest $request) { + + public function email_verify(EmailVerificationRequest $request) + { $request->fulfill(); $name = request()->user()?->name; + // send_internal_notification("User {$name} verified their email address."); return redirect(RouteServiceProvider::HOME); } - public function forgot_password(Request $request) { + + public function forgot_password(Request $request) + { if (is_transactional_emails_active()) { $arrayOfRequest = $request->only(Fortify::email()); $request->merge([ 'email' => Str::lower($arrayOfRequest['email']), ]); $type = set_transanctional_email_settings(); - if (!$type) { + if (! $type) { return response()->json(['message' => 'Transactional emails are not active'], 400); } $request->validate([Fortify::email() => 'required|email']); @@ -60,10 +69,13 @@ public function forgot_password(Request $request) { if ($status == Password::RESET_THROTTLED) { return response('Already requested a password reset in the past minutes.', 400); } + return app(FailedPasswordResetLinkRequestResponse::class, ['status' => $status]); } + return response()->json(['message' => 'Transactional emails are not active'], 400); } + public function link() { $token = request()->get('token'); @@ -72,7 +84,7 @@ public function link() $email = Str::of($decrypted)->before('@@@'); $password = Str::of($decrypted)->after('@@@'); $user = User::whereEmail($email)->first(); - if (!$user) { + if (! $user) { return redirect()->route('login'); } if (Hash::check($password, $user->password)) { @@ -90,9 +102,11 @@ public function link() } Auth::login($user); session(['currentTeam' => $team]); + return redirect()->route('dashboard'); } } + return redirect()->route('login')->with('error', 'Invalid credentials.'); } @@ -108,11 +122,12 @@ public function accept_invitation() if ($resetPassword) { $user->update([ 'password' => Hash::make($invitationUuid), - 'force_password_reset' => true + 'force_password_reset' => true, ]); } if ($user->teams()->where('team_id', $invitation->team->id)->exists()) { $invitation->delete(); + return redirect()->route('team.index'); } $user->teams()->attach($invitation->team->id, ['role' => $invitation->role]); @@ -121,6 +136,7 @@ public function accept_invitation() return redirect()->route('login'); } refreshSession($invitation->team); + return redirect()->route('team.index'); } else { abort(401); @@ -143,6 +159,7 @@ public function revoke_invitation() abort(401); } $invitation->delete(); + return redirect()->route('team.index'); } catch (\Throwable $e) { throw $e; diff --git a/app/Http/Controllers/MagicController.php b/app/Http/Controllers/MagicController.php index d47acac0c..59c9b8b94 100644 --- a/app/Http/Controllers/MagicController.php +++ b/app/Http/Controllers/MagicController.php @@ -12,34 +12,35 @@ class MagicController extends Controller public function servers() { return response()->json([ - 'servers' => Server::isUsable()->get() + 'servers' => Server::isUsable()->get(), ]); } public function destinations() { return response()->json([ - 'destinations' => Server::destinationsByServer(request()->query('server_id'))->sortBy('name') + 'destinations' => Server::destinationsByServer(request()->query('server_id'))->sortBy('name'), ]); } public function projects() { return response()->json([ - 'projects' => Project::ownedByCurrentTeam()->get() + 'projects' => Project::ownedByCurrentTeam()->get(), ]); } public function environments() { $project = Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->first(); - if (!$project) { + if (! $project) { return response()->json([ - 'environments' => [] + 'environments' => [], ]); } + return response()->json([ - 'environments' => $project->environments + 'environments' => $project->environments, ]); } @@ -49,8 +50,9 @@ public function newProject() ['name' => request()->query('name') ?? generate_random_name()], ['team_id' => currentTeam()->id] ); + return response()->json([ - 'project_uuid' => $project->uuid + 'project_uuid' => $project->uuid, ]); } @@ -60,6 +62,7 @@ public function newEnvironment() ['name' => request()->query('name') ?? generate_random_name()], ['project_id' => Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->firstOrFail()->id] ); + return response()->json([ 'environment_name' => $environment->name, ]); @@ -75,6 +78,7 @@ public function newTeam() ); auth()->user()->teams()->attach($team, ['role' => 'admin']); refreshSession(); + return redirect(request()->header('Referer')); } } diff --git a/app/Http/Controllers/OauthController.php b/app/Http/Controllers/OauthController.php index 7d917e5a6..5b17fe926 100644 --- a/app/Http/Controllers/OauthController.php +++ b/app/Http/Controllers/OauthController.php @@ -2,15 +2,15 @@ namespace App\Http\Controllers; -use App\Http\Controllers\Controller; use App\Models\User; - use Illuminate\Support\Facades\Auth; -class OauthController extends Controller { +class OauthController extends Controller +{ public function redirect(string $provider) { $socialite_provider = get_socialite_provider($provider); + return $socialite_provider->redirect(); } @@ -19,16 +19,18 @@ public function callback(string $provider) try { $oauthUser = get_socialite_provider($provider)->user(); $user = User::whereEmail($oauthUser->email)->first(); - if (!$user) { + if (! $user) { $user = User::create([ 'name' => $oauthUser->name, 'email' => $oauthUser->email, ]); } Auth::login($user); + return redirect('/'); } catch (\Exception $e) { ray($e->getMessage()); + return redirect()->route('login')->withErrors([__('auth.failed.callback')]); } } diff --git a/app/Http/Controllers/UploadController.php b/app/Http/Controllers/UploadController.php index e0a7d1b23..8e52fda32 100644 --- a/app/Http/Controllers/UploadController.php +++ b/app/Http/Controllers/UploadController.php @@ -2,14 +2,11 @@ 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\Routing\Controller as BaseController; 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; @@ -21,7 +18,7 @@ public function upload(Request $request) 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)); + $receiver = new FileReceiver('file', $request, HandlerFactory::classFromRequest($request)); if ($receiver->isUploaded() === false) { throw new UploadMissingFileException(); @@ -34,9 +31,10 @@ public function upload(Request $request) } $handler = $save->handler(); + return response()->json([ - "done" => $handler->getPercentageDone(), - 'status' => true + 'done' => $handler->getPercentageDone(), + 'status' => true, ]); } // protected function saveFileToS3($file) @@ -64,19 +62,20 @@ protected function saveFile(UploadedFile $file, $resource) { $mime = str_replace('/', '-', $file->getMimeType()); $filePath = "upload/{$resource->uuid}"; - $finalPath = storage_path("app/" . $filePath); + $finalPath = storage_path('app/'.$filePath); $file->move($finalPath, 'restore'); return response()->json([ - 'mime_type' => $mime + 'mime_type' => $mime, ]); } + protected function createFilename(UploadedFile $file) { $extension = $file->getClientOriginalExtension(); - $filename = str_replace("." . $extension, "", $file->getClientOriginalName()); // Filename without extension + $filename = str_replace('.'.$extension, '', $file->getClientOriginalName()); // Filename without extension - $filename .= "_" . md5(time()) . "." . $extension; + $filename .= '_'.md5(time()).'.'.$extension; return $filename; } diff --git a/app/Http/Controllers/Webhook/Bitbucket.php b/app/Http/Controllers/Webhook/Bitbucket.php index 1fc7ea453..b9035b755 100644 --- a/app/Http/Controllers/Webhook/Bitbucket.php +++ b/app/Http/Controllers/Webhook/Bitbucket.php @@ -20,25 +20,26 @@ public function manual(Request $request) $epoch = now()->valueOf(); $data = [ 'attributes' => $request->attributes->all(), - 'request' => $request->request->all(), - 'query' => $request->query->all(), - 'server' => $request->server->all(), - 'files' => $request->files->all(), - 'cookies' => $request->cookies->all(), - 'headers' => $request->headers->all(), - 'content' => $request->getContent(), + 'request' => $request->request->all(), + 'query' => $request->query->all(), + 'server' => $request->server->all(), + 'files' => $request->files->all(), + 'cookies' => $request->cookies->all(), + 'headers' => $request->headers->all(), + 'content' => $request->getContent(), ]; $json = json_encode($data); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Bitbicket::manual_bitbucket", $json); + return; } $return_payloads = collect([]); $payload = $request->collect(); $headers = $request->headers->all(); - $x_bitbucket_token = data_get($headers, 'x-hub-signature.0', ""); - $x_bitbucket_event = data_get($headers, 'x-event-key.0', ""); + $x_bitbucket_token = data_get($headers, 'x-hub-signature.0', ''); + $x_bitbucket_event = data_get($headers, 'x-event-key.0', ''); $handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']); - if (!$handled_events->contains($x_bitbucket_event)) { + if (! $handled_events->contains($x_bitbucket_event)) { return response([ 'status' => 'failed', 'message' => 'Nothing to do. Event not handled.', @@ -48,13 +49,13 @@ public function manual(Request $request) $branch = data_get($payload, 'push.changes.0.new.name'); $full_name = data_get($payload, 'repository.full_name'); $commit = data_get($payload, 'push.changes.0.new.target.hash'); - if (!$branch) { + if (! $branch) { return response([ 'status' => 'failed', 'message' => 'Nothing to do. No branch found in the request.', ]); } - ray('Manual webhook bitbucket push event with branch: ' . $branch); + ray('Manual webhook bitbucket push event with branch: '.$branch); } if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') { $branch = data_get($payload, 'pullrequest.destination.branch.name'); @@ -76,30 +77,32 @@ public function manual(Request $request) $webhook_secret = data_get($application, 'manual_webhook_secret_bitbucket'); $payload = $request->getContent(); - list($algo, $hash) = explode('=', $x_bitbucket_token, 2); + [$algo, $hash] = explode('=', $x_bitbucket_token, 2); $payloadHash = hash_hmac($algo, $payload, $webhook_secret); - if (!hash_equals($hash, $payloadHash) && !isDev()) { + if (! hash_equals($hash, $payloadHash) && ! isDev()) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', 'message' => 'Invalid signature.', ]); ray('Invalid signature'); + continue; } $isFunctional = $application->destination->server->isFunctional(); - if (!$isFunctional) { + if (! $isFunctional) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', 'message' => 'Server is not functional.', ]); - ray('Server is not functional: ' . $application->destination->server->name); + ray('Server is not functional: '.$application->destination->server->name); + continue; } if ($x_bitbucket_event === 'repo:push') { if ($application->isDeployable()) { - ray('Deploying ' . $application->name . ' with branch ' . $branch); + ray('Deploying '.$application->name.' with branch '.$branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -123,10 +126,10 @@ public function manual(Request $request) } if ($x_bitbucket_event === 'pullrequest:created') { if ($application->isPRDeployable()) { - ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id); + ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id); $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found) { + if (! $found) { ApplicationPreview::create([ 'git_type' => 'bitbucket', 'application_id' => $application->id, @@ -178,9 +181,11 @@ public function manual(Request $request) } } ray($return_payloads); + return response($return_payloads); } catch (Exception $e) { ray($e); + return handleError($e); } } diff --git a/app/Http/Controllers/Webhook/Gitea.php b/app/Http/Controllers/Webhook/Gitea.php index 775e2c17e..388481949 100644 --- a/app/Http/Controllers/Webhook/Gitea.php +++ b/app/Http/Controllers/Webhook/Gitea.php @@ -27,20 +27,22 @@ public function manual(Request $request) })->first(); if ($gitea_delivery_found) { ray('Webhook already found'); + return; } $data = [ 'attributes' => $request->attributes->all(), - 'request' => $request->request->all(), - 'query' => $request->query->all(), - 'server' => $request->server->all(), - 'files' => $request->files->all(), - 'cookies' => $request->cookies->all(), - 'headers' => $request->headers->all(), - 'content' => $request->getContent(), + 'request' => $request->request->all(), + 'query' => $request->query->all(), + 'server' => $request->server->all(), + 'files' => $request->files->all(), + 'cookies' => $request->cookies->all(), + 'headers' => $request->headers->all(), + 'content' => $request->getContent(), ]; $json = json_encode($data); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitea::manual_{$x_gitea_delivery}", $json); + return; } $x_gitea_event = Str::lower($request->header('X-Gitea-Event')); @@ -66,7 +68,7 @@ public function manual(Request $request) $modified_files = data_get($payload, 'commits.*.modified'); $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); ray($changed_files); - ray('Manual Webhook Gitea Push Event with branch: ' . $branch); + ray('Manual Webhook Gitea Push Event with branch: '.$branch); } if ($x_gitea_event === 'pull_request') { $action = data_get($payload, 'action'); @@ -75,9 +77,9 @@ public function manual(Request $request) $pull_request_html_url = data_get($payload, 'pull_request.html_url'); $branch = data_get($payload, 'pull_request.head.ref'); $base_branch = data_get($payload, 'pull_request.base.ref'); - ray('Webhook Gitea Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id); + ray('Webhook Gitea Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id); } - if (!$branch) { + if (! $branch) { return response('Nothing to do. No branch found in the request.'); } $applications = Application::where('git_repository', 'like', "%$full_name%"); @@ -96,29 +98,31 @@ public function manual(Request $request) foreach ($applications as $application) { $webhook_secret = data_get($application, 'manual_webhook_secret_gitea'); $hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret); - if (!hash_equals($x_hub_signature_256, $hmac) && !isDev()) { + if (! hash_equals($x_hub_signature_256, $hmac) && ! isDev()) { ray('Invalid signature'); $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', 'message' => 'Invalid signature.', ]); + continue; } $isFunctional = $application->destination->server->isFunctional(); - if (!$isFunctional) { + if (! $isFunctional) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', 'message' => 'Server is not functional.', ]); + continue; } if ($x_gitea_event === 'push') { if ($application->isDeployable()) { $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); if ($is_watch_path_triggered || is_null($application->watch_paths)) { - ray('Deploying ' . $application->name . ' with branch ' . $branch); + ray('Deploying '.$application->name.' with branch '.$branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -160,7 +164,7 @@ public function manual(Request $request) if ($application->isPRDeployable()) { $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found) { + if (! $found) { ApplicationPreview::create([ 'git_type' => 'gitea', 'application_id' => $application->id, @@ -213,9 +217,11 @@ public function manual(Request $request) } } ray($return_payloads); + return response($return_payloads); } catch (Exception $e) { ray($e->getMessage()); + return handleError($e); } } diff --git a/app/Http/Controllers/Webhook/Github.php b/app/Http/Controllers/Webhook/Github.php index 07b573ab6..403438193 100644 --- a/app/Http/Controllers/Webhook/Github.php +++ b/app/Http/Controllers/Webhook/Github.php @@ -33,20 +33,22 @@ public function manual(Request $request) })->first(); if ($github_delivery_found) { ray('Webhook already found'); + return; } $data = [ 'attributes' => $request->attributes->all(), - 'request' => $request->request->all(), - 'query' => $request->query->all(), - 'server' => $request->server->all(), - 'files' => $request->files->all(), - 'cookies' => $request->cookies->all(), - 'headers' => $request->headers->all(), - 'content' => $request->getContent(), + 'request' => $request->request->all(), + 'query' => $request->query->all(), + 'server' => $request->server->all(), + 'files' => $request->files->all(), + 'cookies' => $request->cookies->all(), + 'headers' => $request->headers->all(), + 'content' => $request->getContent(), ]; $json = json_encode($data); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::manual_{$x_github_delivery}", $json); + return; } $x_github_event = Str::lower($request->header('X-GitHub-Event')); @@ -71,7 +73,7 @@ public function manual(Request $request) $removed_files = data_get($payload, 'commits.*.removed'); $modified_files = data_get($payload, 'commits.*.modified'); $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); - ray('Manual Webhook GitHub Push Event with branch: ' . $branch); + ray('Manual Webhook GitHub Push Event with branch: '.$branch); } if ($x_github_event === 'pull_request') { $action = data_get($payload, 'action'); @@ -80,9 +82,9 @@ public function manual(Request $request) $pull_request_html_url = data_get($payload, 'pull_request.html_url'); $branch = data_get($payload, 'pull_request.head.ref'); $base_branch = data_get($payload, 'pull_request.base.ref'); - ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id); + ray('Webhook GitHub Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id); } - if (!$branch) { + if (! $branch) { return response('Nothing to do. No branch found in the request.'); } $applications = Application::where('git_repository', 'like', "%$full_name%"); @@ -101,29 +103,31 @@ public function manual(Request $request) foreach ($applications as $application) { $webhook_secret = data_get($application, 'manual_webhook_secret_github'); $hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret); - if (!hash_equals($x_hub_signature_256, $hmac) && !isDev()) { + if (! hash_equals($x_hub_signature_256, $hmac) && ! isDev()) { ray('Invalid signature'); $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', 'message' => 'Invalid signature.', ]); + continue; } $isFunctional = $application->destination->server->isFunctional(); - if (!$isFunctional) { + if (! $isFunctional) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', 'message' => 'Server is not functional.', ]); + continue; } if ($x_github_event === 'push') { if ($application->isDeployable()) { $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); if ($is_watch_path_triggered || is_null($application->watch_paths)) { - ray('Deploying ' . $application->name . ' with branch ' . $branch); + ray('Deploying '.$application->name.' with branch '.$branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -165,7 +169,7 @@ public function manual(Request $request) if ($application->isPRDeployable()) { $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found) { + if (! $found) { ApplicationPreview::create([ 'git_type' => 'github', 'application_id' => $application->id, @@ -218,12 +222,15 @@ public function manual(Request $request) } } ray($return_payloads); + return response($return_payloads); } catch (Exception $e) { ray($e->getMessage()); + return handleError($e); } } + public function normal(Request $request) { try { @@ -239,20 +246,22 @@ public function normal(Request $request) })->first(); if ($github_delivery_found) { ray('Webhook already found'); + return; } $data = [ 'attributes' => $request->attributes->all(), - 'request' => $request->request->all(), - 'query' => $request->query->all(), - 'server' => $request->server->all(), - 'files' => $request->files->all(), - 'cookies' => $request->cookies->all(), - 'headers' => $request->headers->all(), - 'content' => $request->getContent(), + 'request' => $request->request->all(), + 'query' => $request->query->all(), + 'server' => $request->server->all(), + 'files' => $request->files->all(), + 'cookies' => $request->cookies->all(), + 'headers' => $request->headers->all(), + 'content' => $request->getContent(), ]; $json = json_encode($data); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::normal_{$x_github_delivery}", $json); + return; } $x_github_event = Str::lower($request->header('X-GitHub-Event')); @@ -270,7 +279,7 @@ public function normal(Request $request) $webhook_secret = data_get($github_app, 'webhook_secret'); $hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret); if (config('app.env') !== 'local') { - if (!hash_equals($x_hub_signature_256, $hmac)) { + if (! hash_equals($x_hub_signature_256, $hmac)) { return response('Invalid signature.'); } } @@ -280,6 +289,7 @@ public function normal(Request $request) if ($action === 'new_permissions_accepted') { GithubAppPermissionJob::dispatch($github_app); } + return response('cool'); } if ($x_github_event === 'push') { @@ -292,7 +302,7 @@ public function normal(Request $request) $removed_files = data_get($payload, 'commits.*.removed'); $modified_files = data_get($payload, 'commits.*.modified'); $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); - ray('Webhook GitHub Push Event: ' . $id . ' with branch: ' . $branch); + ray('Webhook GitHub Push Event: '.$id.' with branch: '.$branch); } if ($x_github_event === 'pull_request') { $action = data_get($payload, 'action'); @@ -301,9 +311,9 @@ public function normal(Request $request) $pull_request_html_url = data_get($payload, 'pull_request.html_url'); $branch = data_get($payload, 'pull_request.head.ref'); $base_branch = data_get($payload, 'pull_request.base.ref'); - ray('Webhook GitHub Pull Request Event: ' . $id . ' with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id); + ray('Webhook GitHub Pull Request Event: '.$id.' with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id); } - if (!$id || !$branch) { + if (! $id || ! $branch) { return response('Nothing to do. No id or branch found.'); } $applications = Application::where('repository_project_id', $id)->whereRelation('source', 'is_public', false); @@ -322,20 +332,21 @@ public function normal(Request $request) foreach ($applications as $application) { $isFunctional = $application->destination->server->isFunctional(); - if (!$isFunctional) { + if (! $isFunctional) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Server is not functional.', 'application_uuid' => $application->uuid, 'application_name' => $application->name, ]); + continue; } if ($x_github_event === 'push') { if ($application->isDeployable()) { $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); if ($is_watch_path_triggered || is_null($application->watch_paths)) { - ray('Deploying ' . $application->name . ' with branch ' . $branch); + ray('Deploying '.$application->name.' with branch '.$branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -377,7 +388,7 @@ public function normal(Request $request) if ($application->isPRDeployable()) { $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found) { + if (! $found) { ApplicationPreview::create([ 'git_type' => 'github', 'application_id' => $application->id, @@ -431,12 +442,15 @@ public function normal(Request $request) } } } + return response($return_payloads); } catch (Exception $e) { ray($e->getMessage()); + return handleError($e); } } + public function redirect(Request $request) { try { @@ -464,11 +478,13 @@ public function redirect(Request $request) $github_app->webhook_secret = $webhook_secret; $github_app->private_key_id = $private_key->id; $github_app->save(); + return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]); } catch (Exception $e) { return handleError($e); } } + public function install(Request $request) { try { @@ -478,16 +494,17 @@ public function install(Request $request) $epoch = now()->valueOf(); $data = [ 'attributes' => $request->attributes->all(), - 'request' => $request->request->all(), - 'query' => $request->query->all(), - 'server' => $request->server->all(), - 'files' => $request->files->all(), - 'cookies' => $request->cookies->all(), - 'headers' => $request->headers->all(), - 'content' => $request->getContent(), + 'request' => $request->request->all(), + 'query' => $request->query->all(), + 'server' => $request->server->all(), + 'files' => $request->files->all(), + 'cookies' => $request->cookies->all(), + 'headers' => $request->headers->all(), + 'content' => $request->getContent(), ]; $json = json_encode($data); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::install_{$installation_id}", $json); + return; } $source = $request->get('source'); @@ -497,6 +514,7 @@ public function install(Request $request) $github_app->installation_id = $installation_id; $github_app->save(); } + return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]); } catch (Exception $e) { return handleError($e); diff --git a/app/Http/Controllers/Webhook/Gitlab.php b/app/Http/Controllers/Webhook/Gitlab.php index a36929781..a3d7712eb 100644 --- a/app/Http/Controllers/Webhook/Gitlab.php +++ b/app/Http/Controllers/Webhook/Gitlab.php @@ -21,16 +21,17 @@ public function manual(Request $request) $epoch = now()->valueOf(); $data = [ 'attributes' => $request->attributes->all(), - 'request' => $request->request->all(), - 'query' => $request->query->all(), - 'server' => $request->server->all(), - 'files' => $request->files->all(), - 'cookies' => $request->cookies->all(), - 'headers' => $request->headers->all(), - 'content' => $request->getContent(), + 'request' => $request->request->all(), + 'query' => $request->query->all(), + 'server' => $request->server->all(), + 'files' => $request->files->all(), + 'cookies' => $request->cookies->all(), + 'headers' => $request->headers->all(), + 'content' => $request->getContent(), ]; $json = json_encode($data); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitlab::manual_gitlab", $json); + return; } $return_payloads = collect([]); @@ -39,11 +40,12 @@ public function manual(Request $request) $x_gitlab_token = data_get($headers, 'x-gitlab-token.0'); $x_gitlab_event = data_get($payload, 'object_kind'); $allowed_events = ['push', 'merge_request']; - if (!in_array($x_gitlab_event, $allowed_events)) { + if (! in_array($x_gitlab_event, $allowed_events)) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Event not allowed. Only push and merge_request events are allowed.', ]); + return response($return_payloads); } @@ -53,18 +55,19 @@ public function manual(Request $request) if (Str::isMatch('/refs\/heads\/*/', $branch)) { $branch = Str::after($branch, 'refs/heads/'); } - if (!$branch) { + if (! $branch) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Nothing to do. No branch found in the request.', ]); + return response($return_payloads); } $added_files = data_get($payload, 'commits.*.added'); $removed_files = data_get($payload, 'commits.*.removed'); $modified_files = data_get($payload, 'commits.*.modified'); $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); - ray('Manual Webhook GitLab Push Event with branch: ' . $branch); + ray('Manual Webhook GitLab Push Event with branch: '.$branch); } if ($x_gitlab_event === 'merge_request') { $action = data_get($payload, 'object_attributes.action'); @@ -73,14 +76,15 @@ public function manual(Request $request) $full_name = data_get($payload, 'project.path_with_namespace'); $pull_request_id = data_get($payload, 'object_attributes.iid'); $pull_request_html_url = data_get($payload, 'object_attributes.url'); - if (!$branch) { + if (! $branch) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Nothing to do. No branch found in the request.', ]); + return response($return_payloads); } - ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id); + ray('Webhook GitHub Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id); } $applications = Application::where('git_repository', 'like', "%$full_name%"); if ($x_gitlab_event === 'push') { @@ -90,6 +94,7 @@ public function manual(Request $request) 'status' => 'failed', 'message' => "Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.", ]); + return response($return_payloads); } } @@ -100,6 +105,7 @@ public function manual(Request $request) 'status' => 'failed', 'message' => "Nothing to do. No applications found with branch '$base_branch'.", ]); + return response($return_payloads); } } @@ -112,23 +118,25 @@ public function manual(Request $request) 'message' => 'Invalid signature.', ]); ray('Invalid signature'); + continue; } $isFunctional = $application->destination->server->isFunctional(); - if (!$isFunctional) { + if (! $isFunctional) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', 'message' => 'Server is not functional', ]); - ray('Server is not functional: ' . $application->destination->server->name); + ray('Server is not functional: '.$application->destination->server->name); + continue; } if ($x_gitlab_event === 'push') { if ($application->isDeployable()) { $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); if ($is_watch_path_triggered || is_null($application->watch_paths)) { - ray('Deploying ' . $application->name . ' with branch ' . $branch); + ray('Deploying '.$application->name.' with branch '.$branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -163,7 +171,7 @@ public function manual(Request $request) 'application_uuid' => $application->uuid, 'application_name' => $application->name, ]); - ray('Deployments disabled for ' . $application->name); + ray('Deployments disabled for '.$application->name); } } if ($x_gitlab_event === 'merge_request') { @@ -171,7 +179,7 @@ public function manual(Request $request) if ($application->isPRDeployable()) { $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found) { + if (! $found) { ApplicationPreview::create([ 'git_type' => 'gitlab', 'application_id' => $application->id, @@ -188,7 +196,7 @@ public function manual(Request $request) is_webhook: true, git_type: 'gitlab' ); - ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id); + ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id); $return_payloads->push([ 'application' => $application->name, 'status' => 'success', @@ -200,9 +208,9 @@ public function manual(Request $request) 'status' => 'failed', 'message' => 'Preview deployments disabled', ]); - ray('Preview deployments disabled for ' . $application->name); + ray('Preview deployments disabled for '.$application->name); } - } else if ($action === 'closed' || $action === 'close') { + } elseif ($action === 'closed' || $action === 'close' || $action === 'merge') { $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); if ($found) { $found->delete(); @@ -214,6 +222,7 @@ public function manual(Request $request) 'status' => 'success', 'message' => 'Preview Deployment closed', ]); + return response($return_payloads); } $return_payloads->push([ @@ -230,9 +239,11 @@ public function manual(Request $request) } } } + return response($return_payloads); } catch (Exception $e) { ray($e->getMessage()); + return handleError($e); } } diff --git a/app/Http/Controllers/Webhook/Stripe.php b/app/Http/Controllers/Webhook/Stripe.php index 200d3dd1c..e404a8ebc 100644 --- a/app/Http/Controllers/Webhook/Stripe.php +++ b/app/Http/Controllers/Webhook/Stripe.php @@ -26,16 +26,17 @@ public function events(Request $request) $epoch = now()->valueOf(); $data = [ 'attributes' => $request->attributes->all(), - 'request' => $request->request->all(), - 'query' => $request->query->all(), - 'server' => $request->server->all(), - 'files' => $request->files->all(), - 'cookies' => $request->cookies->all(), - 'headers' => $request->headers->all(), - 'content' => $request->getContent(), + 'request' => $request->request->all(), + 'query' => $request->query->all(), + 'server' => $request->server->all(), + 'files' => $request->files->all(), + 'cookies' => $request->cookies->all(), + 'headers' => $request->headers->all(), + 'content' => $request->getContent(), ]; $json = json_encode($data); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Stripe::events_stripe", $json); + return; } $webhookSecret = config('subscription.stripe_webhook_secret'); @@ -48,7 +49,7 @@ public function events(Request $request) ); $webhook = Webhook::create([ 'type' => 'stripe', - 'payload' => $request->getContent() + 'payload' => $request->getContent(), ]); $type = data_get($event, 'type'); $data = data_get($event, 'data.object'); @@ -65,20 +66,20 @@ public function events(Request $request) $customerId = data_get($data, 'customer'); $team = Team::find($teamId); $found = $team->members->where('id', $userId)->first(); - if (!$found->isAdmin()) { + if (! $found->isAdmin()) { send_internal_notification("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}."); throw new Exception("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}."); } $subscription = Subscription::where('team_id', $teamId)->first(); if ($subscription) { - send_internal_notification('Old subscription activated for team: ' . $teamId); + send_internal_notification('Old subscription activated for team: '.$teamId); $subscription->update([ 'stripe_subscription_id' => $subscriptionId, 'stripe_customer_id' => $customerId, 'stripe_invoice_paid' => true, ]); } else { - send_internal_notification('New subscription for team: ' . $teamId); + send_internal_notification('New subscription for team: '.$teamId); Subscription::create([ 'team_id' => $teamId, 'stripe_subscription_id' => $subscriptionId, @@ -95,7 +96,7 @@ public function events(Request $request) break; } $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); - if (!$subscription) { + if (! $subscription) { Sleep::for(5)->seconds(); $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); } @@ -106,34 +107,38 @@ public function events(Request $request) case 'invoice.payment_failed': $customerId = data_get($data, 'customer'); $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); - if (!$subscription) { - send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: ' . $customerId); + if (! $subscription) { + send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: '.$customerId); + return response('No subscription found in Coolify.'); } $team = data_get($subscription, 'team'); - if (!$team) { - send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: ' . $customerId); + if (! $team) { + send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: '.$customerId); + return response('No team found in Coolify.'); } - if (!$subscription->stripe_invoice_paid) { + if (! $subscription->stripe_invoice_paid) { SubscriptionInvoiceFailedJob::dispatch($team); - send_internal_notification('Invoice payment failed: ' . $customerId); + send_internal_notification('Invoice payment failed: '.$customerId); } else { - send_internal_notification('Invoice payment failed but already paid: ' . $customerId); + send_internal_notification('Invoice payment failed but already paid: '.$customerId); } break; case 'payment_intent.payment_failed': $customerId = data_get($data, 'customer'); $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); - if (!$subscription) { - send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: ' . $customerId); + if (! $subscription) { + send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: '.$customerId); + return response('No subscription found in Coolify.'); } if ($subscription->stripe_invoice_paid) { - send_internal_notification('payment_intent.payment_failed but invoice is active for customer: ' . $customerId); + send_internal_notification('payment_intent.payment_failed but invoice is active for customer: '.$customerId); + return; } - send_internal_notification('Subscription payment failed for customer: ' . $customerId); + send_internal_notification('Subscription payment failed for customer: '.$customerId); break; case 'customer.subscription.updated': $customerId = data_get($data, 'customer'); @@ -145,17 +150,19 @@ public function events(Request $request) break; } $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); - if (!$subscription) { + if (! $subscription) { Sleep::for(5)->seconds(); $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); } - if (!$subscription) { + if (! $subscription) { if ($status === 'incomplete_expired') { - send_internal_notification('Subscription incomplete expired for customer: ' . $customerId); - return response("Subscription incomplete expired", 200); + send_internal_notification('Subscription incomplete expired for customer: '.$customerId); + + return response('Subscription incomplete expired', 200); } - send_internal_notification('No subscription found for: ' . $customerId); - return response("No subscription found", 400); + send_internal_notification('No subscription found for: '.$customerId); + + return response('No subscription found', 400); } $trialEndedAlready = data_get($subscription, 'stripe_trial_already_ended'); $cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end'); @@ -187,7 +194,7 @@ public function events(Request $request) $subscription->update([ 'stripe_invoice_paid' => false, ]); - send_internal_notification('Subscription paused or incomplete for customer: ' . $customerId); + send_internal_notification('Subscription paused or incomplete for customer: '.$customerId); } // Trial ended but subscribed, reactive servers @@ -197,9 +204,9 @@ public function events(Request $request) } if ($feedback) { - $reason = "Cancellation feedback for {$customerId}: '" . $feedback . "'"; + $reason = "Cancellation feedback for {$customerId}: '".$feedback."'"; if ($comment) { - $reason .= ' with comment: \'' . $comment . "'"; + $reason .= ' with comment: \''.$comment."'"; } send_internal_notification($reason); } @@ -207,7 +214,7 @@ public function events(Request $request) if ($cancelAtPeriodEnd) { // send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id); } else { - send_internal_notification('customer.subscription.updated for customer: ' . $customerId); + send_internal_notification('customer.subscription.updated for customer: '.$customerId); } } break; @@ -226,15 +233,15 @@ public function events(Request $request) 'stripe_invoice_paid' => false, 'stripe_trial_already_ended' => true, ]); - send_internal_notification('customer.subscription.deleted for customer: ' . $customerId); + send_internal_notification('customer.subscription.deleted for customer: '.$customerId); break; case 'customer.subscription.trial_will_end': // Not used for now $customerId = data_get($data, 'customer'); $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); $team = data_get($subscription, 'team'); - if (!$team) { - throw new Exception('No team found for subscription: ' . $subscription->id); + if (! $team) { + throw new Exception('No team found for subscription: '.$subscription->id); } SubscriptionTrialEndsSoonJob::dispatch($team); break; @@ -242,8 +249,8 @@ public function events(Request $request) $customerId = data_get($data, 'customer'); $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); $team = data_get($subscription, 'team'); - if (!$team) { - throw new Exception('No team found for subscription: ' . $subscription->id); + if (! $team) { + throw new Exception('No team found for subscription: '.$subscription->id); } $team->trialEnded(); $subscription->update([ @@ -251,19 +258,20 @@ public function events(Request $request) 'stripe_invoice_paid' => false, ]); SubscriptionTrialEndedJob::dispatch($team); - send_internal_notification('Subscription paused for customer: ' . $customerId); + send_internal_notification('Subscription paused for customer: '.$customerId); break; default: // Unhandled event type } } catch (Exception $e) { if ($type !== 'payment_intent.payment_failed') { - send_internal_notification("Subscription webhook ($type) failed: " . $e->getMessage()); + send_internal_notification("Subscription webhook ($type) failed: ".$e->getMessage()); } $webhook->update([ 'status' => 'failed', 'failure_reason' => $e->getMessage(), ]); + return response($e->getMessage(), 400); } } diff --git a/app/Http/Controllers/Webhook/Waitlist.php b/app/Http/Controllers/Webhook/Waitlist.php index 620b0a595..ea635836c 100644 --- a/app/Http/Controllers/Webhook/Waitlist.php +++ b/app/Http/Controllers/Webhook/Waitlist.php @@ -17,41 +17,49 @@ public function confirm(Request $request) try { $found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first(); if ($found) { - if (!$found->verified) { + if (! $found->verified) { if ($found->created_at > now()->subMinutes(config('constants.waitlist.expiration'))) { $found->verified = true; $found->save(); - send_internal_notification('Waitlist confirmed: ' . $email); + send_internal_notification('Waitlist confirmed: '.$email); + return 'Thank you for confirming your email address. We will notify you when you are next in line.'; } else { $found->delete(); - send_internal_notification('Waitlist expired: ' . $email); + send_internal_notification('Waitlist expired: '.$email); + return 'Your confirmation code has expired. Please sign up again.'; } } } + return redirect()->route('dashboard'); } catch (Exception $e) { - send_internal_notification('Waitlist confirmation failed: ' . $e->getMessage()); + send_internal_notification('Waitlist confirmation failed: '.$e->getMessage()); ray($e->getMessage()); + return redirect()->route('dashboard'); } } + public function cancel(Request $request) { $email = request()->get('email'); $confirmation_code = request()->get('confirmation_code'); try { $found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first(); - if ($found && !$found->verified) { + if ($found && ! $found->verified) { $found->delete(); - send_internal_notification('Waitlist cancelled: ' . $email); + send_internal_notification('Waitlist cancelled: '.$email); + return 'Your email address has been removed from the waitlist.'; } + return redirect()->route('dashboard'); } catch (Exception $e) { - send_internal_notification('Waitlist cancellation failed: ' . $e->getMessage()); + send_internal_notification('Waitlist cancellation failed: '.$e->getMessage()); ray($e->getMessage()); + return redirect()->route('dashboard'); } } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index d8cba40b6..e29c4a307 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -44,7 +44,7 @@ class Kernel extends HttpKernel 'api' => [ // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, - \Illuminate\Routing\Middleware\ThrottleRequests::class . ':api', + \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ]; diff --git a/app/Http/Middleware/CheckForcePasswordReset.php b/app/Http/Middleware/CheckForcePasswordReset.php index 79b3819f7..78b1f896c 100644 --- a/app/Http/Middleware/CheckForcePasswordReset.php +++ b/app/Http/Middleware/CheckForcePasswordReset.php @@ -20,16 +20,19 @@ public function handle(Request $request, Closure $next): Response auth()->logout(); request()->session()->invalidate(); request()->session()->regenerateToken(); + return $next($request); } $force_password_reset = auth()->user()->force_password_reset; if ($force_password_reset) { - if ($request->routeIs('auth.force-password-reset') || $request->path() === 'force-password-reset' || $request->path() === 'livewire/update' || $request->path() === 'logout') { + if ($request->routeIs('auth.force-password-reset') || $request->path() === 'force-password-reset' || $request->path() === 'livewire/update' || $request->path() === 'logout') { return $next($request); } + return redirect()->route('auth.force-password-reset'); } } + return $next($request); } } diff --git a/app/Http/Middleware/DecideWhatToDoWithUser.php b/app/Http/Middleware/DecideWhatToDoWithUser.php index e5531a6e7..8b1c550df 100644 --- a/app/Http/Middleware/DecideWhatToDoWithUser.php +++ b/app/Http/Middleware/DecideWhatToDoWithUser.php @@ -5,8 +5,8 @@ use App\Providers\RouteServiceProvider; use Closure; use Illuminate\Http\Request; -use Symfony\Component\HttpFoundation\Response; use Illuminate\Support\Str; +use Symfony\Component\HttpFoundation\Response; class DecideWhatToDoWithUser { @@ -16,33 +16,37 @@ public function handle(Request $request, Closure $next): Response $currentTeam = auth()->user()?->recreate_personal_team(); refreshSession($currentTeam); } - if(auth()?->user()?->currentTeam()){ + if (auth()?->user()?->currentTeam()) { refreshSession(auth()->user()->currentTeam()); } - if (!auth()->user() || !isCloud() || isInstanceAdmin()) { - if (!isCloud() && showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) { + if (! auth()->user() || ! isCloud() || isInstanceAdmin()) { + if (! isCloud() && showBoarding() && ! in_array($request->path(), allowedPathsForBoardingAccounts())) { return redirect()->route('onboarding'); } + return $next($request); } - if (!auth()->user()->hasVerifiedEmail()) { + if (! auth()->user()->hasVerifiedEmail()) { if ($request->path() === 'verify' || in_array($request->path(), allowedPathsForInvalidAccounts()) || $request->routeIs('verify.verify')) { return $next($request); } + return redirect()->route('verify.email'); } - if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) { - if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) { + if (! isSubscriptionActive() && ! isSubscriptionOnGracePeriod()) { + if (! in_array($request->path(), allowedPathsForUnsubscribedAccounts())) { if (Str::startsWith($request->path(), 'invitations')) { return $next($request); } + return redirect()->route('subscription.index'); } } - if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) { + if (showBoarding() && ! in_array($request->path(), allowedPathsForBoardingAccounts())) { if (Str::startsWith($request->path(), 'invitations')) { return $next($request); } + return redirect()->route('onboarding'); } if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') { @@ -51,6 +55,7 @@ public function handle(Request $request, Closure $next): Response if (isSubscriptionActive() && $request->routeIs('subscription.index')) { return redirect(RouteServiceProvider::HOME); } + return $next($request); } } diff --git a/app/Http/Middleware/PreventRequestsDuringMaintenance.php b/app/Http/Middleware/PreventRequestsDuringMaintenance.php index e7d2b99fe..2a4feea1e 100644 --- a/app/Http/Middleware/PreventRequestsDuringMaintenance.php +++ b/app/Http/Middleware/PreventRequestsDuringMaintenance.php @@ -13,6 +13,6 @@ class PreventRequestsDuringMaintenance extends Middleware */ protected $except = [ 'webhooks/*', - '/api/health' + '/api/health', ]; } diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index 3f17e6def..afc78c4e5 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -13,7 +13,7 @@ class RedirectIfAuthenticated /** * Handle an incoming request. * - * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ public function handle(Request $request, Closure $next, string ...$guards): Response { @@ -24,6 +24,7 @@ public function handle(Request $request, Closure $next, string ...$guards): Resp return redirect(RouteServiceProvider::HOME); } } + return $next($request); } } diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index c80ad531b..559dd2fc3 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -20,7 +20,7 @@ class TrustProxies extends Middleware * @var int */ protected $headers = - Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 6517aae0a..149cde122 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -28,15 +28,14 @@ use Illuminate\Support\Sleep; use Illuminate\Support\Str; use RuntimeException; -use Spatie\Url\Url; use Symfony\Component\Yaml\Yaml; use Throwable; use Visus\Cuid2\Cuid2; use Yosymfony\Toml\Toml; -class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted +class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ExecuteRemoteCommand; + use Dispatchable, ExecuteRemoteCommand, InteractsWithQueue, Queueable, SerializesModels; public $timeout = 3600; @@ -45,72 +44,120 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted private int $application_deployment_queue_id; private bool $newVersionIsHealthy = false; + private ApplicationDeploymentQueue $application_deployment_queue; + private Application $application; + private string $deployment_uuid; + private int $pull_request_id; + private string $commit; + private bool $rollback; + private bool $force_rebuild; + private bool $restart_only; private ?string $dockerImage = null; + private ?string $dockerImageTag = null; private GithubApp|GitlabApp|string $source = 'other'; + private StandaloneDocker|SwarmDocker $destination; + // Deploy to Server private Server $server; + // Build Server private Server $build_server; + private bool $use_build_server = false; + // Save original server between phases private Server $original_server; + private Server $mainServer; + private bool $is_this_additional_server = false; + private ?ApplicationPreview $preview = null; + private ?string $git_type = null; + private bool $only_this_server = false; private string $container_name; + private ?string $currently_running_container_name = null; + private string $basedir; + private string $workdir; + private ?string $build_pack = null; + private string $configuration_dir; + private string $build_image_name; + private string $production_image_name; + private bool $is_debug_enabled; + private $build_args; + private $env_args; + private $env_nixpacks_args; + private $docker_compose; + private $docker_compose_base64; + private ?string $env_filename = null; + private ?string $nixpacks_plan = null; + private ?string $nixpacks_type = null; + private string $dockerfile_location = '/Dockerfile'; + private string $docker_compose_location = '/docker-compose.yml'; + private ?string $docker_compose_custom_start_command = null; + private ?string $docker_compose_custom_build_command = null; + private ?string $addHosts = null; + private ?string $buildTarget = null; + private Collection $saved_outputs; + private ?string $full_healthcheck_url = null; private string $serverUser = 'root'; + private string $serverUserHomeDir = '/root'; + private string $dockerConfigFileExists = 'NOK'; private int $customPort = 22; + private ?string $customRepository = null; private ?string $fullRepoUrl = null; + private ?string $branch = null; private ?string $coolify_variables = null; public $tries = 1; + public function __construct(int $application_deployment_queue_id) { $this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id); @@ -141,8 +188,8 @@ public function __construct(int $application_deployment_queue_id) $this->is_this_additional_server = $this->application->additional_servers()->wherePivot('server_id', $this->server->id)->count() > 0; $this->basedir = $this->application->generateBaseDir($this->deployment_uuid); - $this->workdir = "{$this->basedir}" . rtrim($this->application->base_directory, '/'); - $this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}"; + $this->workdir = "{$this->basedir}".rtrim($this->application->base_directory, '/'); + $this->configuration_dir = application_configuration_dir()."/{$this->application->uuid}"; $this->is_debug_enabled = $this->application->settings->is_debug_enabled; $this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id); @@ -170,16 +217,17 @@ public function handle(): void $this->application_deployment_queue->update([ 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value, ]); - if (!$this->server->isFunctional()) { - $this->application_deployment_queue->addLogEntry("Server is not functional."); - $this->fail("Server is not functional."); + if (! $this->server->isFunctional()) { + $this->application_deployment_queue->addLogEntry('Server is not functional.'); + $this->fail('Server is not functional.'); + return; } try { // Generate custom host<->ip mapping $allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server); - if (!is_null($allContainers)) { + if (! is_null($allContainers)) { $allContainers = format_docker_command_output_to_json($allContainers); $ips = collect([]); if (count($allContainers) > 0) { @@ -216,7 +264,7 @@ public function handle(): void $teamId = data_get($this->application, 'environment.project.team.id'); $buildServers = Server::buildServers($teamId)->get(); if ($buildServers->count() === 0) { - $this->application_deployment_queue->addLogEntry("No suitable build server found. Using the deployment server."); + $this->application_deployment_queue->addLogEntry('No suitable build server found. Using the deployment server.'); $this->build_server = $this->server; $this->original_server = $this->server; } else { @@ -247,12 +295,11 @@ public function handle(): void $this->execute_remote_command( [ "docker rm -f {$this->deployment_uuid} >/dev/null 2>&1", - "hidden" => true, - "ignore_errors" => true, + 'hidden' => true, + 'ignore_errors' => true, ] ); - // $this->execute_remote_command( // [ // "docker image prune -f >/dev/null 2>&1", @@ -261,32 +308,34 @@ public function handle(): void // ] // ); - ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id')); } } + private function decide_what_to_do() { if ($this->restart_only) { $this->just_restart(); + return; - } else if ($this->pull_request_id !== 0) { + } elseif ($this->pull_request_id !== 0) { $this->deploy_pull_request(); - } else if ($this->application->dockerfile) { + } elseif ($this->application->dockerfile) { $this->deploy_simple_dockerfile(); - } else if ($this->application->build_pack === 'dockercompose') { + } elseif ($this->application->build_pack === 'dockercompose') { $this->deploy_docker_compose_buildpack(); - } else if ($this->application->build_pack === 'dockerimage') { + } elseif ($this->application->build_pack === 'dockerimage') { $this->deploy_dockerimage_buildpack(); - } else if ($this->application->build_pack === 'dockerfile') { + } elseif ($this->application->build_pack === 'dockerfile') { $this->deploy_dockerfile_buildpack(); - } else if ($this->application->build_pack === 'static') { + } elseif ($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()) { @@ -302,6 +351,7 @@ private function post_deployment() $this->run_post_deployment_command(); $this->application->isConfigurationChanged(true); } + private function deploy_simple_dockerfile() { if ($this->use_build_server) { @@ -312,7 +362,7 @@ private function deploy_simple_dockerfile() $this->prepare_builder_image(); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null") + executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null"), ], ); $this->generate_image_names(); @@ -323,6 +373,7 @@ private function deploy_simple_dockerfile() $this->push_to_docker_registry(); $this->rolling_update(); } + private function deploy_dockerimage_buildpack() { $this->dockerImage = $this->application->docker_registry_image_name; @@ -338,6 +389,7 @@ private function deploy_dockerimage_buildpack() $this->generate_compose_file(); $this->rolling_update(); } + private function deploy_docker_compose_buildpack() { if (data_get($this->application, 'docker_compose_location')) { @@ -345,14 +397,14 @@ private function deploy_docker_compose_buildpack() } if (data_get($this->application, 'docker_compose_custom_start_command')) { $this->docker_compose_custom_start_command = $this->application->docker_compose_custom_start_command; - if (!str($this->docker_compose_custom_start_command)->contains('--project-directory')) { - $this->docker_compose_custom_start_command = str($this->docker_compose_custom_start_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value(); + if (! str($this->docker_compose_custom_start_command)->contains('--project-directory')) { + $this->docker_compose_custom_start_command = str($this->docker_compose_custom_start_command)->replaceFirst('compose', 'compose --project-directory '.$this->workdir)->value(); } } if (data_get($this->application, 'docker_compose_custom_build_command')) { $this->docker_compose_custom_build_command = $this->application->docker_compose_custom_build_command; - if (!str($this->docker_compose_custom_build_command)->contains('--project-directory')) { - $this->docker_compose_custom_build_command = str($this->docker_compose_custom_build_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value(); + if (! str($this->docker_compose_custom_build_command)->contains('--project-directory')) { + $this->docker_compose_custom_build_command = str($this->docker_compose_custom_build_command)->replaceFirst('compose', 'compose --project-directory '.$this->workdir)->value(); } } if ($this->pull_request_id === 0) { @@ -373,31 +425,33 @@ private function deploy_docker_compose_buildpack() } else { $composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id, preview_id: data_get($this, 'preview.id')); $this->save_environment_variables(); - if (!is_null($this->env_filename)) { + if (! is_null($this->env_filename)) { $services = collect($composeFile['services']); $services = $services->map(function ($service, $name) { $service['env_file'] = [$this->env_filename]; + return $service; }); $composeFile['services'] = $services->toArray(); } if (is_null($composeFile)) { - $this->application_deployment_queue->addLogEntry("Failed to parse docker-compose file."); - $this->fail("Failed to parse docker-compose file."); + $this->application_deployment_queue->addLogEntry('Failed to parse docker-compose file.'); + $this->fail('Failed to parse docker-compose file.'); + return; } $yaml = Yaml::dump($composeFile->toArray(), 10); } $this->docker_compose_base64 = base64_encode($yaml); $this->execute_remote_command([ - executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"), "hidden" => true + executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"), 'hidden' => true, ]); // Build new container to limit downtime. - $this->application_deployment_queue->addLogEntry("Pulling & building required images."); + $this->application_deployment_queue->addLogEntry('Pulling & building required images.'); if ($this->docker_compose_custom_build_command) { $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_build_command}"), "hidden" => true], + [executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_build_command}"), 'hidden' => true], ); } else { $command = "{$this->coolify_variables} docker compose"; @@ -406,12 +460,12 @@ private function deploy_docker_compose_buildpack() } $command .= " --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"; $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, $command), "hidden" => true], + [executeInDocker($this->deployment_uuid, $command), 'hidden' => true], ); } $this->stop_running_container(force: true); - $this->application_deployment_queue->addLogEntry("Starting new application."); + $this->application_deployment_queue->addLogEntry('Starting new application.'); $networkId = $this->application->uuid; if ($this->pull_request_id !== 0) { $networkId = "{$this->application->uuid}-{$this->pull_request_id}"; @@ -420,9 +474,9 @@ private function deploy_docker_compose_buildpack() // TODO } else { $this->execute_remote_command([ - "docker network inspect '{$networkId}' >/dev/null 2>&1 || docker network create --attachable '{$networkId}' >/dev/null || true", "hidden" => true, "ignore_errors" => true + "docker network inspect '{$networkId}' >/dev/null 2>&1 || docker network create --attachable '{$networkId}' >/dev/null || true", 'hidden' => true, 'ignore_errors' => true, ], [ - "docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true + "docker network connect {$networkId} coolify-proxy || true", 'hidden' => true, 'ignore_errors' => true, ]); } @@ -430,7 +484,7 @@ private function deploy_docker_compose_buildpack() if ($this->application->settings->is_raw_compose_deployment_enabled) { if ($this->docker_compose_custom_start_command) { $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "cd {$this->workdir} && {$this->docker_compose_custom_start_command}"), "hidden" => true], + [executeInDocker($this->deployment_uuid, "cd {$this->workdir} && {$this->docker_compose_custom_start_command}"), 'hidden' => true], ); $this->write_deployment_configurations(); } else { @@ -444,13 +498,13 @@ private function deploy_docker_compose_buildpack() $command .= " --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d"; $this->execute_remote_command( - ["command" => $command, "hidden" => true], + ['command' => $command, 'hidden' => true], ); } } else { if ($this->docker_compose_custom_start_command) { $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true], + [executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), 'hidden' => true], ); $this->write_deployment_configurations(); } else { @@ -460,14 +514,15 @@ private function deploy_docker_compose_buildpack() } $command .= " --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"; $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, $command), "hidden" => true], + [executeInDocker($this->deployment_uuid, $command), 'hidden' => true], ); $this->write_deployment_configurations(); } } - $this->application_deployment_queue->addLogEntry("New container started."); + $this->application_deployment_queue->addLogEntry('New container started.'); } + private function deploy_dockerfile_buildpack() { $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}."); @@ -481,7 +536,7 @@ private function deploy_dockerfile_buildpack() $this->check_git_if_build_needed(); $this->generate_image_names(); $this->clone_repository(); - if (!$this->force_rebuild) { + if (! $this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -495,6 +550,7 @@ private function deploy_dockerfile_buildpack() $this->push_to_docker_registry(); $this->rolling_update(); } + private function deploy_nixpacks_buildpack() { if ($this->use_build_server) { @@ -504,7 +560,7 @@ private function deploy_nixpacks_buildpack() $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->generate_image_names(); - if (!$this->force_rebuild) { + if (! $this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -519,6 +575,7 @@ private function deploy_nixpacks_buildpack() $this->push_to_docker_registry(); $this->rolling_update(); } + private function deploy_static_buildpack() { if ($this->use_build_server) { @@ -528,7 +585,7 @@ private function deploy_static_buildpack() $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->generate_image_names(); - if (!$this->force_rebuild) { + if (! $this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -557,7 +614,7 @@ private function write_deployment_configurations() } $this->execute_remote_command( [ - "mkdir -p $this->configuration_dir" + "mkdir -p $this->configuration_dir", ], [ "echo '{$this->docker_compose_base64}' | base64 -d | tee $composeFileName > /dev/null", @@ -571,19 +628,23 @@ private function write_deployment_configurations() } } } + private function push_to_docker_registry() { $forceFail = true; if (str($this->application->docker_registry_image_name)->isEmpty()) { ray('empty docker_registry_image_name'); + return; } if ($this->restart_only) { ray('restart_only'); + return; } if ($this->application->build_pack === 'dockerimage') { ray('dockerimage'); + return; } if ($this->use_build_server) { @@ -600,16 +661,17 @@ private function push_to_docker_registry() } if ($this->is_this_additional_server) { ray('this is an additional_servers, no pushy pushy'); + return; } - ray('push_to_docker_registry noww: ' . $this->production_image_name); + ray('push_to_docker_registry noww: '.$this->production_image_name); try { instant_remote_process(["docker images --format '{{json .}}' {$this->production_image_name}"], $this->server); - $this->application_deployment_queue->addLogEntry("----------------------------------------"); + $this->application_deployment_queue->addLogEntry('----------------------------------------'); $this->application_deployment_queue->addLogEntry("Pushing image to docker registry ({$this->production_image_name})."); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "docker push {$this->production_image_name}"), 'hidden' => true + executeInDocker($this->deployment_uuid, "docker push {$this->production_image_name}"), 'hidden' => true, ], ); if ($this->application->docker_registry_image_tag) { @@ -617,21 +679,22 @@ private function push_to_docker_registry() $this->application_deployment_queue->addLogEntry("Tagging and pushing image with {$this->application->docker_registry_image_tag} tag."); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "docker tag {$this->production_image_name} {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true + executeInDocker($this->deployment_uuid, "docker tag {$this->production_image_name} {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, "docker push {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true + executeInDocker($this->deployment_uuid, "docker push {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true, ], ); } } catch (Exception $e) { - $this->application_deployment_queue->addLogEntry("Failed to push image to docker registry. Please check debug logs for more information."); + $this->application_deployment_queue->addLogEntry('Failed to push image to docker registry. Please check debug logs for more information.'); if ($forceFail) { throw new RuntimeException($e->getMessage(), 69420); } ray($e); } } + private function generate_image_names() { if ($this->application->dockerfile) { @@ -642,9 +705,9 @@ private function generate_image_names() $this->build_image_name = "{$this->application->uuid}:build"; $this->production_image_name = "{$this->application->uuid}:latest"; } - } else if ($this->application->build_pack === 'dockerimage') { + } elseif ($this->application->build_pack === 'dockerimage') { $this->production_image_name = "{$this->dockerImage}:{$this->dockerImageTag}"; - } else if ($this->pull_request_id !== 0) { + } elseif ($this->pull_request_id !== 0) { if ($this->application->docker_registry_image_name) { $this->build_image_name = "{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}-build"; $this->production_image_name = "{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}"; @@ -666,6 +729,7 @@ private function generate_image_names() } } } + private function just_restart() { $this->application_deployment_queue->addLogEntry("Restarting {$this->customRepository}:{$this->application->git_branch} on {$this->server->name}."); @@ -677,6 +741,7 @@ private function just_restart() return; } } + private function should_skip_build() { if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) { @@ -688,16 +753,18 @@ private function should_skip_build() if ($this->restart_only) { $this->post_deployment(); } + return true; } - if (!$this->application->isConfigurationChanged()) { + if (! $this->application->isConfigurationChanged()) { $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->push_to_docker_registry(); $this->rolling_update(); + return true; } else { - $this->application_deployment_queue->addLogEntry("Configuration changed. Rebuilding image."); + $this->application_deployment_queue->addLogEntry('Configuration changed. Rebuilding image.'); } } else { $this->application_deployment_queue->addLogEntry("Image not found ({$this->production_image_name}). Building new image."); @@ -706,22 +773,25 @@ private function should_skip_build() $this->restart_only = false; $this->decide_what_to_do(); } + return false; } + private function check_image_locally_or_remotely() { $this->execute_remote_command([ - "docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found" + "docker images -q {$this->production_image_name} 2>/dev/null", 'hidden' => true, 'save' => 'local_image_found', ]); if (str($this->saved_outputs->get('local_image_found'))->isEmpty() && $this->application->docker_registry_image_name) { $this->execute_remote_command([ - "docker pull {$this->production_image_name} 2>/dev/null", "ignore_errors" => true, "hidden" => true + "docker pull {$this->production_image_name} 2>/dev/null", 'ignore_errors' => true, 'hidden' => true, ]); $this->execute_remote_command([ - "docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found" + "docker images -q {$this->production_image_name} 2>/dev/null", 'hidden' => true, 'save' => 'local_image_found', ]); } } + private function save_environment_variables() { $envs = collect([]); @@ -742,10 +812,10 @@ private function save_environment_variables() $this->env_filename = ".env-pr-$this->pull_request_id"; // Add SOURCE_COMMIT if not exists if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) { - if (!is_null($this->commit)) { + if (! is_null($this->commit)) { $envs->push("SOURCE_COMMIT={$this->commit}"); } else { - $envs->push("SOURCE_COMMIT=unknown"); + $envs->push('SOURCE_COMMIT=unknown'); } } if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) { @@ -763,13 +833,13 @@ private function save_environment_variables() if ($env->version === '4.0.0-beta.239') { $real_value = $env->real_value; } else { - if ($env->is_literal) { - $real_value = '\'' . $real_value . '\''; + if ($env->is_literal || $env->is_multiline) { + $real_value = '\''.$real_value.'\''; } else { $real_value = escapeEnvVariables($env->real_value); } } - $envs->push($env->key . '=' . $real_value); + $envs->push($env->key.'='.$real_value); } // Add PORT if not exists, use the first port as default if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) { @@ -777,16 +847,16 @@ private function save_environment_variables() } // Add HOST if not exists if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) { - $envs->push("HOST=0.0.0.0"); + $envs->push('HOST=0.0.0.0'); } } else { - $this->env_filename = ".env"; + $this->env_filename = '.env'; // Add SOURCE_COMMIT if not exists if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) { - if (!is_null($this->commit)) { + if (! is_null($this->commit)) { $envs->push("SOURCE_COMMIT={$this->commit}"); } else { - $envs->push("SOURCE_COMMIT=unknown"); + $envs->push('SOURCE_COMMIT=unknown'); } } if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) { @@ -804,13 +874,14 @@ private function save_environment_variables() if ($env->version === '4.0.0-beta.239') { $real_value = $env->real_value; } else { - if ($env->is_literal) { - $real_value = '\'' . $real_value . '\''; + if ($env->is_literal || $env->is_multiline) { + $real_value = '\''.$real_value.'\''; } else { $real_value = escapeEnvVariables($env->real_value); + ray($real_value); } } - $envs->push($env->key . '=' . $real_value); + $envs->push($env->key.'='.$real_value); } // Add PORT if not exists, use the first port as default if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) { @@ -818,7 +889,7 @@ private function save_environment_variables() } // Add HOST if not exists if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) { - $envs->push("HOST=0.0.0.0"); + $envs->push('HOST=0.0.0.0'); } } @@ -828,25 +899,25 @@ private function save_environment_variables() $this->server = $this->original_server; $this->execute_remote_command( [ - "command" => "rm -f $this->configuration_dir/{$this->env_filename}", - "hidden" => true, - "ignore_errors" => true + 'command' => "rm -f $this->configuration_dir/{$this->env_filename}", + 'hidden' => true, + 'ignore_errors' => true, ] ); $this->server = $this->build_server; $this->execute_remote_command( [ - "command" => "rm -f $this->configuration_dir/{$this->env_filename}", - "hidden" => true, - "ignore_errors" => true + 'command' => "rm -f $this->configuration_dir/{$this->env_filename}", + 'hidden' => true, + 'ignore_errors' => true, ] ); } else { $this->execute_remote_command( [ - "command" => "rm -f $this->configuration_dir/{$this->env_filename}", - "hidden" => true, - "ignore_errors" => true + 'command' => "rm -f $this->configuration_dir/{$this->env_filename}", + 'hidden' => true, + 'ignore_errors' => true, ] ); } @@ -854,7 +925,7 @@ private function save_environment_variables() $envs_base64 = base64_encode($envs->implode("\n")); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d | tee $this->workdir/{$this->env_filename} > /dev/null") + executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d | tee $this->workdir/{$this->env_filename} > /dev/null"), ], ); @@ -862,21 +933,20 @@ private function save_environment_variables() $this->server = $this->original_server; $this->execute_remote_command( [ - "echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null" + "echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null", ] ); $this->server = $this->build_server; } else { $this->execute_remote_command( [ - "echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null" + "echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null", ] ); } } } - private function framework_based_notification() { // Laravel old env variables @@ -888,55 +958,57 @@ private function framework_based_notification() $nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first(); } if ($nixpacks_php_fallback_path?->value === '/index.php' && $nixpacks_php_root_dir?->value === '/app/public' && $this->newVersionIsHealthy === false) { - $this->application_deployment_queue->addLogEntry("There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/resources/laravel", 'stderr'); + $this->application_deployment_queue->addLogEntry('There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/resources/laravel', 'stderr'); } } + private function rolling_update() { if ($this->server->isSwarm()) { - $this->application_deployment_queue->addLogEntry("Rolling update started."); + $this->application_deployment_queue->addLogEntry('Rolling update started.'); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "docker stack deploy --with-registry-auth -c {$this->workdir}{$this->docker_compose_location} {$this->application->uuid}") + executeInDocker($this->deployment_uuid, "docker stack deploy --with-registry-auth -c {$this->workdir}{$this->docker_compose_location} {$this->application->uuid}"), ], ); - $this->application_deployment_queue->addLogEntry("Rolling update completed."); + $this->application_deployment_queue->addLogEntry('Rolling update completed.'); } else { if ($this->use_build_server) { $this->write_deployment_configurations(); $this->server = $this->original_server; } if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name) || $this->pull_request_id !== 0 || str($this->application->custom_docker_run_options)->contains('--ip') || str($this->application->custom_docker_run_options)->contains('--ip6')) { - $this->application_deployment_queue->addLogEntry("----------------------------------------"); + $this->application_deployment_queue->addLogEntry('----------------------------------------'); if (count($this->application->ports_mappings_array) > 0) { - $this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported."); + $this->application_deployment_queue->addLogEntry('Application has ports mapped to the host system, rolling update is not supported.'); } if ((bool) $this->application->settings->is_consistent_container_name_enabled) { - $this->application_deployment_queue->addLogEntry("Consistent container name feature enabled, rolling update is not supported."); + $this->application_deployment_queue->addLogEntry('Consistent container name feature enabled, rolling update is not supported.'); } if (isset($this->application->settings->custom_internal_name)) { - $this->application_deployment_queue->addLogEntry("Custom internal name is set, rolling update is not supported."); + $this->application_deployment_queue->addLogEntry('Custom internal name is set, rolling update is not supported.'); } if ($this->pull_request_id !== 0) { $this->application->settings->is_consistent_container_name_enabled = true; - $this->application_deployment_queue->addLogEntry("Pull request deployment, rolling update is not supported."); + $this->application_deployment_queue->addLogEntry('Pull request deployment, rolling update is not supported.'); } if (str($this->application->custom_docker_run_options)->contains('--ip') || str($this->application->custom_docker_run_options)->contains('--ip6')) { - $this->application_deployment_queue->addLogEntry("Custom IP address is set, rolling update is not supported."); + $this->application_deployment_queue->addLogEntry('Custom IP address is set, rolling update is not supported.'); } $this->stop_running_container(force: true); $this->start_by_compose_file(); } else { - $this->application_deployment_queue->addLogEntry("----------------------------------------"); - $this->application_deployment_queue->addLogEntry("Rolling update started."); + $this->application_deployment_queue->addLogEntry('----------------------------------------'); + $this->application_deployment_queue->addLogEntry('Rolling update started.'); $this->start_by_compose_file(); $this->health_check(); $this->stop_running_container(); - $this->application_deployment_queue->addLogEntry("Rolling update completed."); + $this->application_deployment_queue->addLogEntry('Rolling update completed.'); } } $this->framework_based_notification(); } + private function health_check() { if ($this->server->isSwarm()) { @@ -944,15 +1016,16 @@ private function health_check() } else { if ($this->application->isHealthcheckDisabled() && $this->application->custom_healthcheck_found === false) { $this->newVersionIsHealthy = true; + return; } if ($this->application->custom_healthcheck_found) { - $this->application_deployment_queue->addLogEntry("Custom healthcheck found, skipping default healthcheck."); + $this->application_deployment_queue->addLogEntry('Custom healthcheck found, skipping default healthcheck.'); } // ray('New container name: ', $this->container_name); if ($this->container_name) { $counter = 1; - $this->application_deployment_queue->addLogEntry("Waiting for healthcheck to pass on the new container."); + $this->application_deployment_queue->addLogEntry('Waiting for healthcheck to pass on the new container.'); if ($this->full_healthcheck_url) { $this->application_deployment_queue->addLogEntry("Healthcheck URL (inside the container): {$this->full_healthcheck_url}"); } @@ -966,15 +1039,15 @@ private function health_check() $this->execute_remote_command( [ "docker inspect --format='{{json .State.Health.Status}}' {$this->container_name}", - "hidden" => true, - "save" => "health_check", - "append" => false + 'hidden' => true, + 'save' => 'health_check', + 'append' => false, ], [ "docker inspect --format='{{json .State.Health.Log}}' {$this->container_name}", - "hidden" => true, - "save" => "health_check_logs", - "append" => false + 'hidden' => true, + 'save' => 'health_check_logs', + 'append' => false, ], ); $this->application_deployment_queue->addLogEntry("Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}"); @@ -990,7 +1063,7 @@ private function health_check() if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'healthy') { $this->newVersionIsHealthy = true; $this->application->update(['status' => 'running']); - $this->application_deployment_queue->addLogEntry("New container is healthy."); + $this->application_deployment_queue->addLogEntry('New container is healthy.'); break; } if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'unhealthy') { @@ -1011,23 +1084,26 @@ private function health_check() } } } + private function query_logs() { - $this->application_deployment_queue->addLogEntry("----------------------------------------"); - $this->application_deployment_queue->addLogEntry("Container logs:"); + $this->application_deployment_queue->addLogEntry('----------------------------------------'); + $this->application_deployment_queue->addLogEntry('Container logs:'); $this->execute_remote_command( [ - "command" => "docker logs -n 100 {$this->container_name}", - "type" => "stderr", - "ignore_errors" => true, + 'command' => "docker logs -n 100 {$this->container_name}", + 'type' => 'stderr', + 'ignore_errors' => true, ], ); - $this->application_deployment_queue->addLogEntry("----------------------------------------"); + $this->application_deployment_queue->addLogEntry('----------------------------------------'); } + private function deploy_pull_request() { if ($this->application->build_pack === 'dockercompose') { $this->deploy_docker_compose_buildpack(); + return; } if ($this->use_build_server) { @@ -1053,40 +1129,42 @@ private function deploy_pull_request() // $this->stop_running_container(); $this->rolling_update(); } + private function create_workdir() { if ($this->use_build_server) { $this->server = $this->original_server; $this->execute_remote_command( [ - "command" => "mkdir -p {$this->configuration_dir}" + 'command' => "mkdir -p {$this->configuration_dir}", ], ); $this->server = $this->build_server; $this->execute_remote_command( [ - "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}") + 'command' => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}"), ], [ - "command" => "mkdir -p {$this->configuration_dir}" + 'command' => "mkdir -p {$this->configuration_dir}", ], ); } else { $this->execute_remote_command( [ - "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}") + 'command' => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}"), ], [ - "command" => "mkdir -p {$this->configuration_dir}" + 'command' => "mkdir -p {$this->configuration_dir}", ], ); } } + private function prepare_builder_image() { $helperImage = config('coolify.helper_image'); // Get user home directory - $this->serverUserHomeDir = instant_remote_process(["echo \$HOME"], $this->server); + $this->serverUserHomeDir = instant_remote_process(['echo $HOME'], $this->server); $this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server); if ($this->use_build_server) { if ($this->dockerConfigFileExists === 'NOK') { @@ -1103,22 +1181,23 @@ private function prepare_builder_image() $this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage."); $this->execute_remote_command( [ - "command" => "docker rm -f {$this->deployment_uuid}", - "ignore_errors" => true, - "hidden" => true + 'command' => "docker rm -f {$this->deployment_uuid}", + 'ignore_errors' => true, + 'hidden' => true, ] ); $this->execute_remote_command( [ $runCommand, - "hidden" => true, + 'hidden' => true, ], [ - "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->basedir}") + 'command' => executeInDocker($this->deployment_uuid, "mkdir -p {$this->basedir}"), ], ); $this->run_pre_deployment_command(); } + private function deploy_to_additional_destinations() { if ($this->application->additional_networks->count() === 0) { @@ -1129,11 +1208,13 @@ private function deploy_to_additional_destinations() } $destination_ids = $this->application->additional_networks->pluck('id'); if ($this->server->isSwarm()) { - $this->application_deployment_queue->addLogEntry("Additional destinations are not supported in swarm mode."); + $this->application_deployment_queue->addLogEntry('Additional destinations are not supported in swarm mode.'); + return; } if ($destination_ids->contains($this->destination->id)) { ray('Same destination found in additional destinations. Skipping.'); + return; } foreach ($destination_ids as $destination_id) { @@ -1141,6 +1222,7 @@ private function deploy_to_additional_destinations() $server = $destination->server; if ($server->team_id !== $this->mainServer->team_id) { $this->application_deployment_queue->addLogEntry("Skipping deployment to {$server->name}. Not in the same team?!"); + continue; } // ray('Deploying to additional destination: ', $server->name); @@ -1152,7 +1234,7 @@ private function deploy_to_additional_destinations() destination: $destination, no_questions_asked: true, ); - $this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: " . route('project.application.deployment.show', [ + $this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: ".route('project.application.deployment.show', [ 'project_uuid' => data_get($this->application, 'environment.project.uuid'), 'application_uuid' => data_get($this->application, 'uuid'), 'deployment_uuid' => $deployment_uuid, @@ -1160,6 +1242,7 @@ private function deploy_to_additional_destinations() ])); } } + private function set_coolify_variables() { $this->coolify_variables = "SOURCE_COMMIT={$this->commit} "; @@ -1177,6 +1260,7 @@ private function set_coolify_variables() $this->coolify_variables .= "COOLIFY_BRANCH={$this->application->git_branch} "; } } + private function check_git_if_build_needed() { $this->generate_git_import_commands(); @@ -1189,36 +1273,37 @@ private function check_git_if_build_needed() $private_key = base64_encode($private_key); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh") + executeInDocker($this->deployment_uuid, 'mkdir -p /root/.ssh'), ], [ - executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null") + executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null"), ], [ - executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa") + executeInDocker($this->deployment_uuid, 'chmod 600 /root/.ssh/id_rsa'), ], [ executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->fullRepoUrl} {$local_branch}"), - "hidden" => true, - "save" => "git_commit_sha" + 'hidden' => true, + 'save' => 'git_commit_sha', ], ); } else { $this->execute_remote_command( [ executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->fullRepoUrl} {$local_branch}"), - "hidden" => true, - "save" => "git_commit_sha" + 'hidden' => true, + 'save' => 'git_commit_sha', ], ); } - if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) { + if ($this->saved_outputs->get('git_commit_sha') && ! $this->rollback) { $this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t"); $this->application_deployment_queue->commit = $this->commit; $this->application_deployment_queue->save(); } $this->set_coolify_variables(); } + private function clone_repository() { $importCommands = $this->generate_git_import_commands(); @@ -1229,15 +1314,15 @@ private function clone_repository() } $this->execute_remote_command( [ - $importCommands, "hidden" => true + $importCommands, 'hidden' => true, ] ); $this->create_workdir(); $this->execute_remote_command( [ executeInDocker($this->deployment_uuid, "cd {$this->workdir} && git log -1 {$this->commit} --pretty=%B"), - "hidden" => true, - "save" => "commit_message" + 'hidden' => true, + 'save' => 'commit_message', ] ); if ($this->saved_outputs->get('commit_message')) { @@ -1257,6 +1342,7 @@ private function generate_git_import_commands() git_type: $this->git_type, commit: $this->commit ); + return $commands; } @@ -1272,8 +1358,8 @@ private function generate_nixpacks_confs() $nixpacks_command = $this->nixpacks_build_cmd(); $this->application_deployment_queue->addLogEntry("Generating nixpacks configuration with: $nixpacks_command"); $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, $nixpacks_command), "save" => "nixpacks_plan", "hidden" => true], - [executeInDocker($this->deployment_uuid, "nixpacks detect {$this->workdir}"), "save" => "nixpacks_type", "hidden" => true], + [executeInDocker($this->deployment_uuid, $nixpacks_command), 'save' => 'nixpacks_plan', 'hidden' => true], + [executeInDocker($this->deployment_uuid, "nixpacks detect {$this->workdir}"), 'save' => 'nixpacks_type', 'hidden' => true], ); if ($this->saved_outputs->get('nixpacks_type')) { $this->nixpacks_type = $this->saved_outputs->get('nixpacks_type'); @@ -1294,10 +1380,10 @@ private function generate_nixpacks_confs() if (count($aptPkgs) === 0) { data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']); } else { - if (!in_array('curl', $aptPkgs)) { + if (! in_array('curl', $aptPkgs)) { $aptPkgs[] = 'curl'; } - if (!in_array('wget', $aptPkgs)) { + if (! in_array('wget', $aptPkgs)) { $aptPkgs[] = 'wget'; } data_set($parsed, 'phases.setup.aptPkgs', $aptPkgs); @@ -1323,20 +1409,22 @@ private function nixpacks_build_cmd() $nixpacks_command .= " --install-cmd \"{$this->application->install_command}\""; } $nixpacks_command .= " {$this->workdir}"; + return $nixpacks_command; } + private function generate_nixpacks_env_variables() { $this->env_nixpacks_args = collect([]); if ($this->pull_request_id === 0) { foreach ($this->application->nixpacks_environment_variables as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}"); } } } else { foreach ($this->application->nixpacks_environment_variables_preview as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}"); } } @@ -1344,19 +1432,20 @@ private function generate_nixpacks_env_variables() $this->env_nixpacks_args = $this->env_nixpacks_args->implode(' '); } + private function generate_env_variables() { $this->env_args = collect([]); $this->env_args->put('SOURCE_COMMIT', $this->commit); if ($this->pull_request_id === 0) { foreach ($this->application->build_environment_variables as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_args->put($env->key, $env->real_value); } } } else { foreach ($this->application->build_environment_variables_preview as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_args->put($env->key, $env->real_value); } } @@ -1380,7 +1469,7 @@ private function generate_compose_file() $this->application->parseContainerLabels(); $labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels))); $labels = $labels->filter(function ($value, $key) { - return !Str::startsWith($value, 'coolify.'); + return ! Str::startsWith($value, 'coolify.'); }); $found_caddy_labels = $labels->filter(function ($value, $key) { return Str::startsWith($value, 'caddy_'); @@ -1419,7 +1508,7 @@ private function generate_compose_file() // Check for custom HEALTHCHECK if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) { $this->execute_remote_command([ - executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile_from_repo', "ignore_errors" => true + executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), 'hidden' => true, 'save' => 'dockerfile_from_repo', 'ignore_errors' => true, ]); $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n")); $this->application->parseHealthcheckFromDockerfile($dockerfile); @@ -1434,9 +1523,9 @@ private function generate_compose_file() 'networks' => [ $this->destination->network => [ 'aliases' => [ - $this->container_name - ] - ] + $this->container_name, + ], + ], ], 'mem_limit' => $this->application->limits_memory, 'memswap_limit' => $this->application->limits_memory_swap, @@ -1444,15 +1533,15 @@ private function generate_compose_file() 'mem_reservation' => $this->application->limits_memory_reservation, 'cpus' => (float) $this->application->limits_cpus, 'cpu_shares' => $this->application->limits_cpu_shares, - ] + ], ], 'networks' => [ $this->destination->network => [ 'external' => true, 'name' => $this->destination->network, - 'attachable' => true - ] - ] + 'attachable' => true, + ], + ], ]; if (isset($this->application->settings->custom_internal_name)) { $docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['aliases'][] = $this->application->settings->custom_internal_name; @@ -1471,44 +1560,44 @@ private function generate_compose_file() // $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; // } // } - if (!is_null($this->env_filename)) { + if (! is_null($this->env_filename)) { $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; } $docker_compose['services'][$this->container_name]['healthcheck'] = [ 'test' => [ 'CMD-SHELL', - $this->generate_healthcheck_commands() + $this->generate_healthcheck_commands(), ], - 'interval' => $this->application->health_check_interval . 's', - 'timeout' => $this->application->health_check_timeout . 's', + 'interval' => $this->application->health_check_interval.'s', + 'timeout' => $this->application->health_check_timeout.'s', 'retries' => $this->application->health_check_retries, - 'start_period' => $this->application->health_check_start_period . 's' + 'start_period' => $this->application->health_check_start_period.'s', ]; - if (!is_null($this->application->limits_cpuset)) { - data_set($docker_compose, 'services.' . $this->container_name . '.cpuset', $this->application->limits_cpuset); + if (! is_null($this->application->limits_cpuset)) { + data_set($docker_compose, 'services.'.$this->container_name.'.cpuset', $this->application->limits_cpuset); } if ($this->server->isSwarm()) { - data_forget($docker_compose, 'services.' . $this->container_name . '.container_name'); - data_forget($docker_compose, 'services.' . $this->container_name . '.expose'); - data_forget($docker_compose, 'services.' . $this->container_name . '.restart'); + data_forget($docker_compose, 'services.'.$this->container_name.'.container_name'); + data_forget($docker_compose, 'services.'.$this->container_name.'.expose'); + data_forget($docker_compose, 'services.'.$this->container_name.'.restart'); - data_forget($docker_compose, 'services.' . $this->container_name . '.mem_limit'); - data_forget($docker_compose, 'services.' . $this->container_name . '.memswap_limit'); - data_forget($docker_compose, 'services.' . $this->container_name . '.mem_swappiness'); - data_forget($docker_compose, 'services.' . $this->container_name . '.mem_reservation'); - data_forget($docker_compose, 'services.' . $this->container_name . '.cpus'); - data_forget($docker_compose, 'services.' . $this->container_name . '.cpuset'); - data_forget($docker_compose, 'services.' . $this->container_name . '.cpu_shares'); + data_forget($docker_compose, 'services.'.$this->container_name.'.mem_limit'); + data_forget($docker_compose, 'services.'.$this->container_name.'.memswap_limit'); + data_forget($docker_compose, 'services.'.$this->container_name.'.mem_swappiness'); + data_forget($docker_compose, 'services.'.$this->container_name.'.mem_reservation'); + data_forget($docker_compose, 'services.'.$this->container_name.'.cpus'); + data_forget($docker_compose, 'services.'.$this->container_name.'.cpuset'); + data_forget($docker_compose, 'services.'.$this->container_name.'.cpu_shares'); $docker_compose['services'][$this->container_name]['deploy'] = [ 'mode' => 'replicated', 'replicas' => data_get($this->application, 'swarm_replicas', 1), 'update_config' => [ - 'order' => 'start-first' + 'order' => 'start-first', ], 'rollback_config' => [ - 'order' => 'start-first' + 'order' => 'start-first', ], 'labels' => $labels, 'resources' => [ @@ -1519,14 +1608,14 @@ private function generate_compose_file() 'reservations' => [ 'cpus' => $this->application->limits_cpus, 'memory' => $this->application->limits_memory, - ] - ] + ], + ], ]; if (data_get($this->application, 'settings.is_swarm_only_worker_nodes')) { $docker_compose['services'][$this->container_name]['deploy']['placement'] = [ 'constraints' => [ - 'node.role == worker' - ] + 'node.role == worker', + ], ]; } if ($this->pull_request_id !== 0) { @@ -1539,10 +1628,10 @@ private function generate_compose_file() $docker_compose['services'][$this->container_name]['logging'] = [ 'driver' => 'fluentd', 'options' => [ - 'fluentd-address' => "tcp://127.0.0.1:24224", - 'fluentd-async' => "true", - 'fluentd-sub-second-precision' => "true", - ] + 'fluentd-address' => 'tcp://127.0.0.1:24224', + 'fluentd-async' => 'true', + 'fluentd-sub-second-precision' => 'true', + ], ]; } if ($this->application->settings->is_gpu_enabled) { @@ -1550,8 +1639,8 @@ private function generate_compose_file() [ 'driver' => data_get($this->application, 'settings.gpu_driver', 'nvidia'), 'capabilities' => ['gpu'], - 'options' => data_get($this->application, 'settings.gpu_options', []) - ] + 'options' => data_get($this->application, 'settings.gpu_options', []), + ], ]; if (data_get($this->application, 'settings.gpu_count')) { $count = data_get($this->application, 'settings.gpu_count'); @@ -1560,12 +1649,12 @@ private function generate_compose_file() } else { $docker_compose['services'][$this->container_name]['deploy']['resources']['reservations']['devices'][0]['count'] = (int) $count; } - } else if (data_get($this->application, 'settings.gpu_device_ids')) { + } elseif (data_get($this->application, 'settings.gpu_device_ids')) { $docker_compose['services'][$this->container_name]['deploy']['resources']['reservations']['devices'][0]['ids'] = data_get($this->application, 'settings.gpu_device_ids'); } } if ($this->application->isHealthcheckDisabled()) { - data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck'); + data_forget($docker_compose, 'services.'.$this->container_name.'.healthcheck'); } if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) { $docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array; @@ -1590,7 +1679,7 @@ private function generate_compose_file() if ($this->pull_request_id === 0) { $custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options); - if ((bool)$this->application->settings->is_consistent_container_name_enabled) { + if ((bool) $this->application->settings->is_consistent_container_name_enabled) { $docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name]; if (count($custom_compose) > 0) { $ipv4 = data_get($custom_compose, 'ip.0'); @@ -1630,7 +1719,7 @@ private function generate_compose_file() $this->docker_compose = Yaml::dump($docker_compose, 10); $this->docker_compose_base64 = base64_encode($this->docker_compose); - $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}/docker-compose.yml > /dev/null"), "hidden" => true]); + $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}/docker-compose.yml > /dev/null"), 'hidden' => true]); } private function generate_local_persistent_volumes() @@ -1643,10 +1732,11 @@ private function generate_local_persistent_volumes() $volume_name = $persistentStorage->name; } if ($this->pull_request_id !== 0) { - $volume_name = $volume_name . '-pr-' . $this->pull_request_id; + $volume_name = $volume_name.'-pr-'.$this->pull_request_id; } - $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; } + return $local_persistent_volumes; } @@ -1660,7 +1750,7 @@ private function generate_local_persistent_volumes_only_volume_names() $name = $persistentStorage->name; if ($this->pull_request_id !== 0) { - $name = $name . '-pr-' . $this->pull_request_id; + $name = $name.'-pr-'.$this->pull_request_id; } $local_persistent_volumes_names[$name] = [ @@ -1668,12 +1758,13 @@ private function generate_local_persistent_volumes_only_volume_names() 'external' => false, ]; } + return $local_persistent_volumes_names; } private function generate_healthcheck_commands() { - if (!$this->application->health_check_port) { + if (! $this->application->health_check_port) { $health_check_port = $this->application->ports_exposes_array[0]; } else { $health_check_port = $this->application->health_check_port; @@ -1684,39 +1775,42 @@ private function generate_healthcheck_commands() if ($this->application->health_check_path) { $this->full_healthcheck_url = "{$this->application->health_check_method}: {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path}"; $generated_healthchecks_commands = [ - "curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path} > /dev/null || wget -q -O- {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path} > /dev/null || exit 1" + "curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path} > /dev/null || wget -q -O- {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path} > /dev/null || exit 1", ]; } else { $this->full_healthcheck_url = "{$this->application->health_check_method}: {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/"; $generated_healthchecks_commands = [ - "curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/ > /dev/null || wget -q -O- {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/ > /dev/null || exit 1" + "curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/ > /dev/null || wget -q -O- {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/ > /dev/null || exit 1", ]; } + return implode(' ', $generated_healthchecks_commands); } + private function pull_latest_image($image) { $this->application_deployment_queue->addLogEntry("Pulling latest image ($image) from the registry."); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "docker pull {$image}"), "hidden" => true + executeInDocker($this->deployment_uuid, "docker pull {$image}"), 'hidden' => true, ] ); } + private function build_image() { - $this->application_deployment_queue->addLogEntry("----------------------------------------"); + $this->application_deployment_queue->addLogEntry('----------------------------------------'); if ($this->application->build_pack === 'static') { - $this->application_deployment_queue->addLogEntry("Static deployment. Copying static assets to the image."); + $this->application_deployment_queue->addLogEntry('Static deployment. Copying static assets to the image.'); } else { - $this->application_deployment_queue->addLogEntry("Building docker image started."); - $this->application_deployment_queue->addLogEntry("To check the current progress, click on Show Debug Logs."); + $this->application_deployment_queue->addLogEntry('Building docker image started.'); + $this->application_deployment_queue->addLogEntry('To check the current progress, click on Show Debug Logs.'); } if ($this->application->settings->is_static || $this->application->build_pack === 'static') { if ($this->application->static_image) { $this->pull_latest_image($this->application->static_image); - $this->application_deployment_queue->addLogEntry("Continuing with the building process."); + $this->application_deployment_queue->addLogEntry('Continuing with the building process.'); } if ($this->application->build_pack === 'static') { $dockerfile = base64_encode("FROM {$this->application->static_image} @@ -1726,7 +1820,7 @@ private function build_image() RUN rm -f /usr/share/nginx/html/nginx.conf RUN rm -f /usr/share/nginx/html/Dockerfile COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); - $nginx_config = base64_encode("server { + $nginx_config = base64_encode('server { listen 80; listen [::]:80; server_name localhost; @@ -1734,42 +1828,42 @@ private function build_image() location / { root /usr/share/nginx/html; index index.html; - try_files \$uri \$uri.html \$uri/index.html \$uri/ /index.html =404; + try_files $uri $uri.html $uri/index.html $uri/ /index.html =404; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } - }"); + }'); } else { if ($this->application->build_pack === 'nixpacks') { $this->nixpacks_plan = base64_encode($this->nixpacks_plan); - $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), "hidden" => true]); + $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]); if ($this->force_rebuild) { $this->execute_remote_command([ - executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), "hidden" => true + executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), 'hidden' => true, ]); } else { $this->execute_remote_command([ - executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), "hidden" => true + executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), 'hidden' => true, ]); } - $this->execute_remote_command([executeInDocker($this->deployment_uuid, "rm /artifacts/thegameplan.json"), "hidden" => true]); + $this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]); } else { if ($this->force_rebuild) { $build_command = "docker build --no-cache {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"; $base64_build_command = base64_encode($build_command); } else { - $build_command = "docker build {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"; + $build_command = "docker build {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"; $base64_build_command = base64_encode($build_command); } $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true + executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true + executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true, ] ); } @@ -1780,7 +1874,7 @@ private function build_image() COPY --from=$this->build_image_name /app/{$this->application->publish_directory} . COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); - $nginx_config = base64_encode("server { + $nginx_config = base64_encode('server { listen 80; listen [::]:80; server_name localhost; @@ -1788,29 +1882,29 @@ private function build_image() location / { root /usr/share/nginx/html; index index.html; - try_files \$uri \$uri.html \$uri/index.html \$uri/ /index.html =404; + try_files $uri $uri.html $uri/index.html $uri/ /index.html =404; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } - }"); + }'); } $build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"; $base64_build_command = base64_encode($build_command); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d | tee {$this->workdir}/Dockerfile > /dev/null") + executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d | tee {$this->workdir}/Dockerfile > /dev/null"), ], [ - executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null") + executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null"), ], [ - executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true + executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true + executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true, ] ); } else { @@ -1824,26 +1918,26 @@ private function build_image() $base64_build_command = base64_encode($build_command); $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true + executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true + executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true, ] ); } else { if ($this->application->build_pack === 'nixpacks') { $this->nixpacks_plan = base64_encode($this->nixpacks_plan); - $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), "hidden" => true]); + $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]); if ($this->force_rebuild) { $this->execute_remote_command([ - executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), "hidden" => true + executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), 'hidden' => true, ]); } else { $this->execute_remote_command([ - executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), "hidden" => true + executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), 'hidden' => true, ]); } - $this->execute_remote_command([executeInDocker($this->deployment_uuid, "rm /artifacts/thegameplan.json"), "hidden" => true]); + $this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]); } else { if ($this->force_rebuild) { $build_command = "docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"; @@ -1854,92 +1948,92 @@ private function build_image() } $this->execute_remote_command( [ - executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true + executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true, ], [ - executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true + executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true, ] ); } } } - $this->application_deployment_queue->addLogEntry("Building docker image completed."); + $this->application_deployment_queue->addLogEntry('Building docker image completed.'); } private function stop_running_container(bool $force = false) { - $this->application_deployment_queue->addLogEntry("Removing old containers."); + $this->application_deployment_queue->addLogEntry('Removing old containers.'); if ($this->newVersionIsHealthy || $force) { $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); if ($this->pull_request_id === 0) { $containers = $containers->filter(function ($container) { - return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name . '-pr-' . $this->pull_request_id; + return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name.'-pr-'.$this->pull_request_id; }); } $containers->each(function ($container) { $containerName = data_get($container, 'Names'); $this->execute_remote_command( - ["docker rm -f $containerName >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true], + ["docker rm -f $containerName >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true], ); }); if ($this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name)) { $this->execute_remote_command( - ["docker rm -f $this->container_name >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true], + ["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true], ); } } else { if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') { - $this->application_deployment_queue->addLogEntry("----------------------------------------"); + $this->application_deployment_queue->addLogEntry('----------------------------------------'); $this->application_deployment_queue->addLogEntry("WARNING: Dockerfile or Docker Image based deployment detected. The healthcheck needs a curl or wget command to check the health of the application. Please make sure that it is available in the image or turn off healthcheck on Coolify's UI."); - $this->application_deployment_queue->addLogEntry("----------------------------------------"); + $this->application_deployment_queue->addLogEntry('----------------------------------------'); } - $this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container."); + $this->application_deployment_queue->addLogEntry('New container is not healthy, rolling back to the old container.'); $this->application_deployment_queue->update([ 'status' => ApplicationDeploymentStatus::FAILED->value, ]); $this->execute_remote_command( - ["docker rm -f $this->container_name >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true], + ["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true], ); } } private function build_by_compose_file() { - $this->application_deployment_queue->addLogEntry("Pulling & building required images."); + $this->application_deployment_queue->addLogEntry('Pulling & building required images.'); if ($this->application->build_pack === 'dockerimage') { - $this->application_deployment_queue->addLogEntry("Pulling latest images from the registry."); + $this->application_deployment_queue->addLogEntry('Pulling latest images from the registry.'); $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true], - [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} build"), "hidden" => true], + [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), 'hidden' => true], + [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} build"), 'hidden' => true], ); } else { $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true], + [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), 'hidden' => true], ); } - $this->application_deployment_queue->addLogEntry("New images built."); + $this->application_deployment_queue->addLogEntry('New images built.'); } private function start_by_compose_file() { if ($this->application->build_pack === 'dockerimage') { - $this->application_deployment_queue->addLogEntry("Pulling latest images from the registry."); + $this->application_deployment_queue->addLogEntry('Pulling latest images from the registry.'); $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true], - [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true], + [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), 'hidden' => true], + [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} up --build -d"), 'hidden' => true], ); } else { if ($this->use_build_server) { $this->execute_remote_command( - ["{$this->coolify_variables} docker compose --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", "hidden" => true], + ["{$this->coolify_variables} docker compose --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", 'hidden' => true], ); } else { $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), "hidden" => true], + [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), 'hidden' => true], ); } } - $this->application_deployment_queue->addLogEntry("New container started."); + $this->application_deployment_queue->addLogEntry('New container started.'); } private function generate_build_env_variables() @@ -1958,27 +2052,37 @@ private function generate_build_env_variables() } $this->build_args = $this->build_args->implode(' '); + ray($this->build_args); } private function add_build_env_variables_to_dockerfile() { $this->execute_remote_command([ - executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile' + executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), 'hidden' => true, 'save' => 'dockerfile', ]); $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n")); if ($this->pull_request_id === 0) { foreach ($this->application->build_environment_variables as $env) { - $dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}"); + if (data_get($env, 'is_multiline') === true) { + $dockerfile->splice(1, 0, "ARG {$env->key}"); + } else { + $dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}"); + } } } else { foreach ($this->application->build_environment_variables_preview as $env) { + if (data_get($env, 'is_multiline') === true) { + $dockerfile->splice(1, 0, "ARG {$env->key}"); + } else { + $dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}"); + } $dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}"); } } $dockerfile_base64 = base64_encode($dockerfile->implode("\n")); $this->execute_remote_command([ executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null"), - "hidden" => true + 'hidden' => true, ]); } @@ -1991,18 +2095,19 @@ private function run_pre_deployment_command() if ($containers->count() == 0) { return; } - $this->application_deployment_queue->addLogEntry("Executing pre-deployment command (see debug log for output/errors)."); + $this->application_deployment_queue->addLogEntry('Executing pre-deployment command (see debug log for output/errors).'); foreach ($containers as $container) { $containerName = data_get($container, 'Names'); - if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container . '-' . $this->application->uuid)) { - $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->pre_deployment_command) . "'"; + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container.'-'.$this->application->uuid)) { + $cmd = "sh -c '".str_replace("'", "'\''", $this->application->pre_deployment_command)."'"; $exec = "docker exec {$containerName} {$cmd}"; $this->execute_remote_command( [ - 'command' => $exec, 'hidden' => true + 'command' => $exec, 'hidden' => true, ], ); + return; } } @@ -2014,25 +2119,25 @@ private function run_post_deployment_command() if (empty($this->application->post_deployment_command)) { return; } - $this->application_deployment_queue->addLogEntry("----------------------------------------"); - $this->application_deployment_queue->addLogEntry("Executing post-deployment command (see debug log for output)."); + $this->application_deployment_queue->addLogEntry('----------------------------------------'); + $this->application_deployment_queue->addLogEntry('Executing post-deployment command (see debug log for output).'); $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); foreach ($containers as $container) { $containerName = data_get($container, 'Names'); - if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container . '-' . $this->application->uuid)) { - $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->post_deployment_command) . "'"; + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container.'-'.$this->application->uuid)) { + $cmd = "sh -c '".str_replace("'", "'\''", $this->application->post_deployment_command)."'"; $exec = "docker exec {$containerName} {$cmd}"; try { $this->execute_remote_command( [ - 'command' => $exec, 'hidden' => true, 'save' => 'post-deployment-command-output' + 'command' => $exec, 'hidden' => true, 'save' => 'post-deployment-command-output', ], ); } catch (Exception $e) { $post_deployment_command_output = $this->saved_outputs->get('post-deployment-command-output'); if ($post_deployment_command_output) { - $this->application_deployment_queue->addLogEntry("Post-deployment command failed."); + $this->application_deployment_queue->addLogEntry('Post-deployment command failed.'); $this->application_deployment_queue->addLogEntry($post_deployment_command_output, 'stderr'); } } @@ -2056,10 +2161,11 @@ private function next(string $status) } if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::FAILED->value) { $this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview)); + return; } if ($status === ApplicationDeploymentStatus::FINISHED->value) { - if (!$this->only_this_server) { + if (! $this->only_this_server) { $this->deploy_to_additional_destinations(); } $this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview)); @@ -2069,7 +2175,7 @@ private function next(string $status) public function failed(Throwable $exception): void { $this->next(ApplicationDeploymentStatus::FAILED->value); - $this->application_deployment_queue->addLogEntry("Oops something is not okay, are you okay? 😢", 'stderr'); + $this->application_deployment_queue->addLogEntry('Oops something is not okay, are you okay? 😢', 'stderr'); if (str($exception->getMessage())->isNotEmpty()) { $this->application_deployment_queue->addLogEntry($exception->getMessage(), 'stderr'); } @@ -2079,9 +2185,9 @@ public function failed(Throwable $exception): void ray($code); if ($code !== 69420) { // 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one - $this->application_deployment_queue->addLogEntry("Deployment failed. Removing the new version of your application.", 'stderr'); + $this->application_deployment_queue->addLogEntry('Deployment failed. Removing the new version of your application.', 'stderr'); $this->execute_remote_command( - ["docker rm -f $this->container_name >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true] + ["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true] ); } } diff --git a/app/Jobs/ApplicationPullRequestUpdateJob.php b/app/Jobs/ApplicationPullRequestUpdateJob.php index 0aedd99a8..d400642dd 100755 --- a/app/Jobs/ApplicationPullRequestUpdateJob.php +++ b/app/Jobs/ApplicationPullRequestUpdateJob.php @@ -12,11 +12,12 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted +class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public string $build_logs_url; + public string $body; public function __construct( @@ -32,25 +33,27 @@ public function handle() try { if ($this->application->is_public_repository()) { ray('Public repository. Skipping comment update.'); + return; } if ($this->status === ProcessStatus::CLOSED) { $this->delete_comment(); + return; - } else if ($this->status === ProcessStatus::IN_PROGRESS) { + } elseif ($this->status === ProcessStatus::IN_PROGRESS) { $this->body = "The preview deployment is in progress. 🟡\n\n"; - } else if ($this->status === ProcessStatus::FINISHED) { + } elseif ($this->status === ProcessStatus::FINISHED) { $this->body = "The preview deployment is ready. 🟢\n\n"; if ($this->preview->fqdn) { $this->body .= "[Open Preview]({$this->preview->fqdn}) | "; } - } else if ($this->status === ProcessStatus::ERROR) { + } elseif ($this->status === ProcessStatus::ERROR) { $this->body = "The preview deployment failed. 🔴\n\n"; } - $this->build_logs_url = base_url() . "/project/{$this->application->environment->project->uuid}/{$this->application->environment->name}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; + $this->build_logs_url = base_url()."/project/{$this->application->environment->project->uuid}/{$this->application->environment->name}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; - $this->body .= "[Open Build Logs](" . $this->build_logs_url . ")\n\n\n"; - $this->body .= "Last updated at: " . now()->toDateTimeString() . " CET"; + $this->body .= '[Open Build Logs]('.$this->build_logs_url.")\n\n\n"; + $this->body .= 'Last updated at: '.now()->toDateTimeString().' CET'; ray('Updating comment', $this->body); if ($this->preview->pull_request_issue_comment_id) { @@ -60,6 +63,7 @@ public function handle() } } catch (\Throwable $e) { ray($e); + return $e; } } @@ -83,6 +87,7 @@ private function create_comment() $this->preview->pull_request_issue_comment_id = $data['id']; $this->preview->save(); } + private function delete_comment() { githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->preview->pull_request_issue_comment_id}", method: 'delete'); diff --git a/app/Jobs/ApplicationRestartJob.php b/app/Jobs/ApplicationRestartJob.php index 3216baa5a..54c062197 100644 --- a/app/Jobs/ApplicationRestartJob.php +++ b/app/Jobs/ApplicationRestartJob.php @@ -10,19 +10,23 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; - -class ApplicationRestartJob implements ShouldQueue, ShouldBeEncrypted +class ApplicationRestartJob implements ShouldBeEncrypted, ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ExecuteRemoteCommand; + use Dispatchable, ExecuteRemoteCommand, InteractsWithQueue, Queueable, SerializesModels; public $timeout = 3600; + public $tries = 1; + public string $applicationDeploymentQueueId; + public function __construct(string $applicationDeploymentQueueId) { $this->applicationDeploymentQueueId = $applicationDeploymentQueueId; } - public function handle() { + + public function handle() + { ray('Restarting application'); } } diff --git a/app/Jobs/CheckLogDrainContainerJob.php b/app/Jobs/CheckLogDrainContainerJob.php index 376a691cc..312200f66 100644 --- a/app/Jobs/CheckLogDrainContainerJob.php +++ b/app/Jobs/CheckLogDrainContainerJob.php @@ -15,13 +15,14 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Sleep; -class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted +class CheckLogDrainContainerJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function __construct(public Server $server) { } + public function middleware(): array { return [(new WithoutOverlapping($this->server->id))->dontRelease()]; @@ -31,6 +32,7 @@ public function uniqueId(): int { return $this->server->id; } + public function healthcheck() { $status = instant_remote_process(["docker inspect --format='{{json .State.Status}}' coolify-log-drain"], $this->server, false); @@ -40,15 +42,16 @@ public function healthcheck() return false; } } - public function handle(): void + + public function handle() { // ray("checking log drain statuses for {$this->server->id}"); try { - if (!$this->server->isFunctional()) { + if (! $this->server->isFunctional()) { return; - }; - $containers = instant_remote_process(["docker container ls -q"], $this->server, false); - if (!$containers) { + } + $containers = instant_remote_process(['docker container ls -q'], $this->server, false); + if (! $containers) { return; } $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server); @@ -57,7 +60,7 @@ public function handle(): void $foundLogDrainContainer = $containers->filter(function ($value, $key) { return data_get($value, 'Name') === '/coolify-log-drain'; })->first(); - if (!$foundLogDrainContainer || !$this->healthcheck()) { + if (! $foundLogDrainContainer || ! $this->healthcheck()) { ray('Log drain container not found or unhealthy. Restarting...'); InstallLogDrain::run($this->server); Sleep::for(10)->seconds(); @@ -66,9 +69,10 @@ public function handle(): void $this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server)); $this->server->update(['log_drain_notification_sent' => false]); } + return; } - if (!$this->server->log_drain_notification_sent) { + if (! $this->server->log_drain_notification_sent) { ray('Log drain container still unhealthy. Sending notification...'); // $this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null)); $this->server->update(['log_drain_notification_sent' => true]); @@ -80,8 +84,11 @@ public function handle(): void } } } catch (\Throwable $e) { - if (!isCloud()) send_internal_notification("CheckLogDrainContainerJob failed on ({$this->server->id}) with: " . $e->getMessage()); + if (! isCloud()) { + send_internal_notification("CheckLogDrainContainerJob failed on ({$this->server->id}) with: ".$e->getMessage()); + } ray($e->getMessage()); + return handleError($e); } } diff --git a/app/Jobs/CheckResaleLicenseJob.php b/app/Jobs/CheckResaleLicenseJob.php index fbc951579..8f2039ef2 100644 --- a/app/Jobs/CheckResaleLicenseJob.php +++ b/app/Jobs/CheckResaleLicenseJob.php @@ -10,7 +10,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class CheckResaleLicenseJob implements ShouldQueue, ShouldBeEncrypted +class CheckResaleLicenseJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -23,7 +23,7 @@ public function handle(): void try { CheckResaleLicense::run(); } catch (\Throwable $e) { - send_internal_notification('CheckResaleLicenseJob failed with: ' . $e->getMessage()); + send_internal_notification('CheckResaleLicenseJob failed with: '.$e->getMessage()); ray($e); throw $e; } diff --git a/app/Jobs/CleanupHelperContainersJob.php b/app/Jobs/CleanupHelperContainersJob.php index 5c26ca930..418c7a0f4 100644 --- a/app/Jobs/CleanupHelperContainersJob.php +++ b/app/Jobs/CleanupHelperContainersJob.php @@ -11,7 +11,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class CleanupHelperContainersJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted +class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -22,18 +22,18 @@ public function __construct(public Server $server) public function handle(): void { try { - ray('Cleaning up helper containers on ' . $this->server->name); + ray('Cleaning up helper containers on '.$this->server->name); $containers = instant_remote_process(['docker container ps --filter "ancestor=ghcr.io/coollabsio/coolify-helper:next" --filter "ancestor=ghcr.io/coollabsio/coolify-helper:latest" --format \'{{json .}}\''], $this->server, false); $containers = format_docker_command_output_to_json($containers); if ($containers->count() > 0) { foreach ($containers as $container) { - $containerId = data_get($container,'ID'); - ray('Removing container ' . $containerId); - instant_remote_process(['docker container rm -f ' . $containerId], $this->server, false); + $containerId = data_get($container, 'ID'); + ray('Removing container '.$containerId); + instant_remote_process(['docker container rm -f '.$containerId], $this->server, false); } } } catch (\Throwable $e) { - send_internal_notification('CleanupHelperContainersJob failed with error: ' . $e->getMessage()); + send_internal_notification('CleanupHelperContainersJob failed with error: '.$e->getMessage()); ray($e->getMessage()); } } diff --git a/app/Jobs/CleanupInstanceStuffsJob.php b/app/Jobs/CleanupInstanceStuffsJob.php index 81a6963ea..b846ad2bc 100644 --- a/app/Jobs/CleanupInstanceStuffsJob.php +++ b/app/Jobs/CleanupInstanceStuffsJob.php @@ -12,7 +12,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted +class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -31,13 +31,13 @@ public function handle(): void try { // $this->cleanup_waitlist(); } catch (\Throwable $e) { - send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage()); + send_internal_notification('CleanupInstanceStuffsJob failed with error: '.$e->getMessage()); ray($e->getMessage()); } try { $this->cleanup_invitation_link(); } catch (\Throwable $e) { - send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage()); + send_internal_notification('CleanupInstanceStuffsJob failed with error: '.$e->getMessage()); ray($e->getMessage()); } } @@ -49,6 +49,7 @@ private function cleanup_waitlist() $item->delete(); } } + private function cleanup_invitation_link() { $invitation = TeamInvitation::all(); diff --git a/app/Jobs/ContainerStatusJob.php b/app/Jobs/ContainerStatusJob.php index 11e7013ee..c50d17d4c 100644 --- a/app/Jobs/ContainerStatusJob.php +++ b/app/Jobs/ContainerStatusJob.php @@ -12,18 +12,21 @@ use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; -class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted +class ContainerStatusJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $tries = 4; + public function backoff(): int { return isDev() ? 1 : 3; } + public function __construct(public Server $server) { } + public function middleware(): array { return [(new WithoutOverlapping($this->server->uuid))]; diff --git a/app/Jobs/CoolifyTask.php b/app/Jobs/CoolifyTask.php index 56c4eee22..e5f4dfd5e 100755 --- a/app/Jobs/CoolifyTask.php +++ b/app/Jobs/CoolifyTask.php @@ -11,7 +11,7 @@ use Illuminate\Queue\SerializesModels; use Spatie\Activitylog\Models\Activity; -class CoolifyTask implements ShouldQueue, ShouldBeEncrypted +class CoolifyTask implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -20,7 +20,7 @@ class CoolifyTask implements ShouldQueue, ShouldBeEncrypted */ public function __construct( public Activity $activity, - public bool $ignore_errors = false, + public bool $ignore_errors = false, public $call_event_on_finish = null, public $call_event_data = null ) { @@ -35,7 +35,7 @@ public function handle(): void 'activity' => $this->activity, 'ignore_errors' => $this->ignore_errors, 'call_event_on_finish' => $this->call_event_on_finish, - 'call_event_data' => $this->call_event_data + 'call_event_data' => $this->call_event_data, ]); $remote_process(); diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index ed9694536..07386988c 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -25,26 +25,37 @@ use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Str; -use Throwable; -class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted +class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public ?Team $team = null; + public Server $server; + public ScheduledDatabaseBackup $backup; + public StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database; public ?string $container_name = null; + public ?string $directory_name = null; + public ?ScheduledDatabaseBackupExecution $backup_log = null; + public string $backup_status = 'failed'; + public ?string $backup_location = null; + public string $backup_dir; + public string $backup_file; + public int $size = 0; + public ?string $backup_output = null; + public ?S3Storage $s3 = null; public function __construct($backup) @@ -84,11 +95,13 @@ public function handle(): void $this->backup->update(['status' => 'failed']); StopDatabase::run($this->database); $this->database->delete(); + return; } $status = Str::of(data_get($this->database, 'status')); - if (!$status->startsWith('running') && $this->database->id !== 0) { + if (! $status->startsWith('running') && $this->database->id !== 0) { ray('database not running'); + return; } if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') { @@ -97,7 +110,7 @@ public function handle(): void $serviceName = str($this->database->service->name)->slug(); if (str($databaseType)->contains('postgres')) { $this->container_name = "{$this->database->name}-$serviceUuid"; - $this->directory_name = $serviceName . '-' . $this->container_name; + $this->directory_name = $serviceName.'-'.$this->container_name; $commands[] = "docker exec $this->container_name env | grep POSTGRES_"; $envs = instant_remote_process($commands, $this->server); $envs = str($envs)->explode("\n"); @@ -120,9 +133,9 @@ public function handle(): void } else { $databasesToBackup = $this->database->postgres_user; } - } else if (str($databaseType)->contains('mysql')) { + } elseif (str($databaseType)->contains('mysql')) { $this->container_name = "{$this->database->name}-$serviceUuid"; - $this->directory_name = $serviceName . '-' . $this->container_name; + $this->directory_name = $serviceName.'-'.$this->container_name; $commands[] = "docker exec $this->container_name env | grep MYSQL_"; $envs = instant_remote_process($commands, $this->server); $envs = str($envs)->explode("\n"); @@ -143,9 +156,9 @@ public function handle(): void } else { throw new \Exception('MYSQL_DATABASE not found'); } - } else if (str($databaseType)->contains('mariadb')) { + } elseif (str($databaseType)->contains('mariadb')) { $this->container_name = "{$this->database->name}-$serviceUuid"; - $this->directory_name = $serviceName . '-' . $this->container_name; + $this->directory_name = $serviceName.'-'.$this->container_name; $commands[] = "docker exec $this->container_name env"; $envs = instant_remote_process($commands, $this->server); $envs = str($envs)->explode("\n"); @@ -184,7 +197,7 @@ public function handle(): void } else { $databaseName = str($this->database->name)->slug()->value(); $this->container_name = $this->database->uuid; - $this->directory_name = $databaseName . '-' . $this->container_name; + $this->directory_name = $databaseName.'-'.$this->container_name; $databaseType = $this->database->type(); $databasesToBackup = data_get($this->backup, 'databases_to_backup'); } @@ -192,11 +205,11 @@ public function handle(): void if (is_null($databasesToBackup)) { if (str($databaseType)->contains('postgres')) { $databasesToBackup = [$this->database->postgres_db]; - } else if (str($databaseType)->contains('mongodb')) { + } elseif (str($databaseType)->contains('mongodb')) { $databasesToBackup = ['*']; - } else if (str($databaseType)->contains('mysql')) { + } elseif (str($databaseType)->contains('mysql')) { $databasesToBackup = [$this->database->mysql_database]; - } else if (str($databaseType)->contains('mariadb')) { + } elseif (str($databaseType)->contains('mariadb')) { $databasesToBackup = [$this->database->mariadb_database]; } else { return; @@ -206,16 +219,16 @@ public function handle(): void // Format: db1,db2,db3 $databasesToBackup = explode(',', $databasesToBackup); $databasesToBackup = array_map('trim', $databasesToBackup); - } else if (str($databaseType)->contains('mongodb')) { + } elseif (str($databaseType)->contains('mongodb')) { // Format: db1:collection1,collection2|db2:collection3,collection4 $databasesToBackup = explode('|', $databasesToBackup); $databasesToBackup = array_map('trim', $databasesToBackup); ray($databasesToBackup); - } else if (str($databaseType)->contains('mysql')) { + } elseif (str($databaseType)->contains('mysql')) { // Format: db1,db2,db3 $databasesToBackup = explode(',', $databasesToBackup); $databasesToBackup = array_map('trim', $databasesToBackup); - } else if (str($databaseType)->contains('mariadb')) { + } elseif (str($databaseType)->contains('mariadb')) { // Format: db1,db2,db3 $databasesToBackup = explode(',', $databasesToBackup); $databasesToBackup = array_map('trim', $databasesToBackup); @@ -223,28 +236,28 @@ public function handle(): void return; } } - $this->backup_dir = backup_dir() . "/databases/" . Str::of($this->team->name)->slug() . '-' . $this->team->id . '/' . $this->directory_name; + $this->backup_dir = backup_dir().'/databases/'.Str::of($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name; if ($this->database->name === 'coolify-db') { $databasesToBackup = ['coolify']; - $this->directory_name = $this->container_name = "coolify-db"; + $this->directory_name = $this->container_name = 'coolify-db'; $ip = Str::slug($this->server->ip); - $this->backup_dir = backup_dir() . "/coolify" . "/coolify-db-$ip"; + $this->backup_dir = backup_dir().'/coolify'."/coolify-db-$ip"; } foreach ($databasesToBackup as $database) { $size = 0; - ray('Backing up ' . $database); + ray('Backing up '.$database); try { if (str($databaseType)->contains('postgres')) { - $this->backup_file = "/pg-dump-$database-" . Carbon::now()->timestamp . ".dmp"; - $this->backup_location = $this->backup_dir . $this->backup_file; + $this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp'; + $this->backup_location = $this->backup_dir.$this->backup_file; $this->backup_log = ScheduledDatabaseBackupExecution::create([ 'database_name' => $database, 'filename' => $this->backup_location, 'scheduled_database_backup_id' => $this->backup->id, ]); $this->backup_standalone_postgresql($database); - } else if (str($databaseType)->contains('mongodb')) { + } elseif (str($databaseType)->contains('mongodb')) { if ($database === '*') { $database = 'all'; $databaseName = 'all'; @@ -255,26 +268,26 @@ public function handle(): void $databaseName = $database; } } - $this->backup_file = "/mongo-dump-$databaseName-" . Carbon::now()->timestamp . ".tar.gz"; - $this->backup_location = $this->backup_dir . $this->backup_file; + $this->backup_file = "/mongo-dump-$databaseName-".Carbon::now()->timestamp.'.tar.gz'; + $this->backup_location = $this->backup_dir.$this->backup_file; $this->backup_log = ScheduledDatabaseBackupExecution::create([ 'database_name' => $databaseName, 'filename' => $this->backup_location, 'scheduled_database_backup_id' => $this->backup->id, ]); $this->backup_standalone_mongodb($database); - } else if (str($databaseType)->contains('mysql')) { - $this->backup_file = "/mysql-dump-$database-" . Carbon::now()->timestamp . ".dmp"; - $this->backup_location = $this->backup_dir . $this->backup_file; + } elseif (str($databaseType)->contains('mysql')) { + $this->backup_file = "/mysql-dump-$database-".Carbon::now()->timestamp.'.dmp'; + $this->backup_location = $this->backup_dir.$this->backup_file; $this->backup_log = ScheduledDatabaseBackupExecution::create([ 'database_name' => $database, 'filename' => $this->backup_location, 'scheduled_database_backup_id' => $this->backup->id, ]); $this->backup_standalone_mysql($database); - } else if (str($databaseType)->contains('mariadb')) { - $this->backup_file = "/mariadb-dump-$database-" . Carbon::now()->timestamp . ".dmp"; - $this->backup_location = $this->backup_dir . $this->backup_file; + } elseif (str($databaseType)->contains('mariadb')) { + $this->backup_file = "/mariadb-dump-$database-".Carbon::now()->timestamp.'.dmp'; + $this->backup_location = $this->backup_dir.$this->backup_file; $this->backup_log = ScheduledDatabaseBackupExecution::create([ 'database_name' => $database, 'filename' => $this->backup_location, @@ -301,27 +314,28 @@ public function handle(): void 'status' => 'failed', 'message' => $this->backup_output, 'size' => $size, - 'filename' => null + 'filename' => null, ]); } - send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage()); + send_internal_notification('DatabaseBackupJob failed with: '.$e->getMessage()); $this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output, $database)); } } } catch (\Throwable $e) { - send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage()); + send_internal_notification('DatabaseBackupJob failed with: '.$e->getMessage()); throw $e; } finally { BackupCreated::dispatch($this->team->id); } } + private function backup_standalone_mongodb(string $databaseWithCollections): void { try { ray($this->database->toArray()); $url = $this->database->get_db_url(useInternal: true); if ($databaseWithCollections === 'all') { - $commands[] = "mkdir -p " . $this->backup_dir; + $commands[] = 'mkdir -p '.$this->backup_dir; if (str($this->database->image)->startsWith('mongo:4.0')) { $commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --archive > $this->backup_location"; } else { @@ -335,7 +349,7 @@ private function backup_standalone_mongodb(string $databaseWithCollections): voi $databaseName = $databaseWithCollections; $collectionsToExclude = collect(); } - $commands[] = "mkdir -p " . $this->backup_dir; + $commands[] = 'mkdir -p '.$this->backup_dir; if ($collectionsToExclude->count() === 0) { if (str($this->database->image)->startsWith('mongo:4.0')) { $commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --archive > $this->backup_location"; @@ -344,9 +358,9 @@ private function backup_standalone_mongodb(string $databaseWithCollections): voi } } else { if (str($this->database->image)->startsWith('mongo:4.0')) { - $commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location"; + $commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --excludeCollection ".$collectionsToExclude->implode(' --excludeCollection ')." --archive > $this->backup_location"; } else { - $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location"; + $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection ".$collectionsToExclude->implode(' --excludeCollection ')." --archive > $this->backup_location"; } } } @@ -355,34 +369,36 @@ private function backup_standalone_mongodb(string $databaseWithCollections): voi if ($this->backup_output === '') { $this->backup_output = null; } - ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location); + ray('Backup done for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location); } catch (\Throwable $e) { $this->add_to_backup_output($e->getMessage()); - ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage()); + ray('Backup failed for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location.'\n\nError:'.$e->getMessage()); throw $e; } } + private function backup_standalone_postgresql(string $database): void { try { - $commands[] = "mkdir -p " . $this->backup_dir; + $commands[] = 'mkdir -p '.$this->backup_dir; $commands[] = "docker exec $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location"; $this->backup_output = instant_remote_process($commands, $this->server); $this->backup_output = trim($this->backup_output); if ($this->backup_output === '') { $this->backup_output = null; } - ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location); + ray('Backup done for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location); } catch (\Throwable $e) { $this->add_to_backup_output($e->getMessage()); - ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage()); + ray('Backup failed for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location.'\n\nError:'.$e->getMessage()); throw $e; } } + private function backup_standalone_mysql(string $database): void { try { - $commands[] = "mkdir -p " . $this->backup_dir; + $commands[] = 'mkdir -p '.$this->backup_dir; $commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location"; ray($commands); $this->backup_output = instant_remote_process($commands, $this->server); @@ -390,17 +406,18 @@ private function backup_standalone_mysql(string $database): void if ($this->backup_output === '') { $this->backup_output = null; } - ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location); + ray('Backup done for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location); } catch (\Throwable $e) { $this->add_to_backup_output($e->getMessage()); - ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage()); + ray('Backup failed for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location.'\n\nError:'.$e->getMessage()); throw $e; } } + private function backup_standalone_mariadb(string $database): void { try { - $commands[] = "mkdir -p " . $this->backup_dir; + $commands[] = 'mkdir -p '.$this->backup_dir; $commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location"; ray($commands); $this->backup_output = instant_remote_process($commands, $this->server); @@ -408,17 +425,18 @@ private function backup_standalone_mariadb(string $database): void if ($this->backup_output === '') { $this->backup_output = null; } - ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location); + ray('Backup done for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location); } catch (\Throwable $e) { $this->add_to_backup_output($e->getMessage()); - ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage()); + ray('Backup failed for '.$this->container_name.' at '.$this->server->name.':'.$this->backup_location.'\n\nError:'.$e->getMessage()); throw $e; } } + private function add_to_backup_output($output): void { if ($this->backup_output) { - $this->backup_output = $this->backup_output . "\n" . $output; + $this->backup_output = $this->backup_output."\n".$output; } else { $this->backup_output = $output; } @@ -464,7 +482,7 @@ private function upload_to_s3(): void $commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/"; instant_remote_process($commands, $this->server); $this->add_to_backup_output('Uploaded to S3.'); - ray('Uploaded to S3. ' . $this->backup_location . ' to s3://' . $bucket . $this->backup_dir); + ray('Uploaded to S3. '.$this->backup_location.' to s3://'.$bucket.$this->backup_dir); } catch (\Throwable $e) { $this->add_to_backup_output($e->getMessage()); throw $e; diff --git a/app/Jobs/DatabaseBackupStatusJob.php b/app/Jobs/DatabaseBackupStatusJob.php index b92ed13e9..cf240e0d7 100644 --- a/app/Jobs/DatabaseBackupStatusJob.php +++ b/app/Jobs/DatabaseBackupStatusJob.php @@ -3,19 +3,16 @@ namespace App\Jobs; use App\Models\ScheduledDatabaseBackup; -use App\Models\Server; use App\Models\Team; use App\Notifications\Database\DailyBackup; -use App\Notifications\Server\HighDiskUsage; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeEncrypted; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; -class DatabaseBackupStatusJob implements ShouldQueue, ShouldBeEncrypted +class DatabaseBackupStatusJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -42,13 +39,6 @@ public function handle() // } // } - - - - - - - // $scheduled_backups = ScheduledDatabaseBackup::all(); // $databases = collect(); // $teams = collect(); diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php index f2a611863..6d4720f6b 100644 --- a/app/Jobs/DeleteResourceJob.php +++ b/app/Jobs/DeleteResourceJob.php @@ -24,7 +24,7 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Artisan; -class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted +class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -60,7 +60,7 @@ public function handle() } } catch (\Throwable $e) { ray($e->getMessage()); - send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage()); + send_internal_notification('ContainerStoppingJob failed with: '.$e->getMessage()); throw $e; } finally { Artisan::queue('cleanup:stucked-resources'); diff --git a/app/Jobs/DockerCleanupJob.php b/app/Jobs/DockerCleanupJob.php index 01f085d93..32c41e99c 100644 --- a/app/Jobs/DockerCleanupJob.php +++ b/app/Jobs/DockerCleanupJob.php @@ -10,21 +10,22 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; use RuntimeException; -class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted +class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $timeout = 300; + public ?int $usageBefore = null; public function __construct(public Server $server) { } + public function handle(): void { try { @@ -32,35 +33,36 @@ public function handle(): void $this->server->applications()->each(function ($application) use (&$isInprogress) { if ($application->isDeploymentInprogress()) { $isInprogress = true; + return; } }); if ($isInprogress) { throw new RuntimeException('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping...'); } - if (!$this->server->isFunctional()) { + if (! $this->server->isFunctional()) { return; } $this->usageBefore = $this->server->getDiskUsage(); - ray('Usage before: ' . $this->usageBefore); + ray('Usage before: '.$this->usageBefore); if ($this->usageBefore >= $this->server->settings->cleanup_after_percentage) { - ray('Cleaning up ' . $this->server->name); + ray('Cleaning up '.$this->server->name); CleanupDocker::run($this->server); $usageAfter = $this->server->getDiskUsage(); - if ($usageAfter < $this->usageBefore) { - $this->server->team?->notify(new DockerCleanup($this->server, 'Saved ' . ($this->usageBefore - $usageAfter) . '% disk space.')); + if ($usageAfter < $this->usageBefore) { + $this->server->team?->notify(new DockerCleanup($this->server, 'Saved '.($this->usageBefore - $usageAfter).'% disk space.')); // ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name); // send_internal_notification('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name); - Log::info('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name); + Log::info('DockerCleanupJob done: Saved '.($this->usageBefore - $usageAfter).'% disk space on '.$this->server->name); } else { - Log::info('DockerCleanupJob failed to save disk space on ' . $this->server->name); + Log::info('DockerCleanupJob failed to save disk space on '.$this->server->name); } } else { - ray('No need to clean up ' . $this->server->name); - Log::info('No need to clean up ' . $this->server->name); + ray('No need to clean up '.$this->server->name); + Log::info('No need to clean up '.$this->server->name); } } catch (\Throwable $e) { - send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage()); + send_internal_notification('DockerCleanupJob failed with: '.$e->getMessage()); ray($e->getMessage()); throw $e; } diff --git a/app/Jobs/GithubAppPermissionJob.php b/app/Jobs/GithubAppPermissionJob.php index deb414a13..bab8f3a25 100644 --- a/app/Jobs/GithubAppPermissionJob.php +++ b/app/Jobs/GithubAppPermissionJob.php @@ -12,18 +12,21 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Http; -class GithubAppPermissionJob implements ShouldQueue, ShouldBeEncrypted +class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $tries = 4; + public function backoff(): int { return isDev() ? 1 : 3; } + public function __construct(public GithubApp $github_app) { } + public function middleware(): array { return [(new WithoutOverlapping($this->github_app->uuid))]; @@ -40,7 +43,7 @@ public function handle() $github_access_token = generate_github_jwt_token($this->github_app); $response = Http::withHeaders([ 'Authorization' => "Bearer $github_access_token", - 'Accept' => 'application/vnd.github+json' + 'Accept' => 'application/vnd.github+json', ])->get("{$this->github_app->api_url}/app"); $response = $response->json(); $permissions = data_get($response, 'permissions'); @@ -51,7 +54,7 @@ public function handle() $this->github_app->save(); $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); } catch (\Throwable $e) { - send_internal_notification('GithubAppPermissionJob failed with: ' . $e->getMessage()); + send_internal_notification('GithubAppPermissionJob failed with: '.$e->getMessage()); ray($e->getMessage()); throw $e; } diff --git a/app/Jobs/InstanceAutoUpdateJob.php b/app/Jobs/InstanceAutoUpdateJob.php index ae629dab9..bce60bbc8 100644 --- a/app/Jobs/InstanceAutoUpdateJob.php +++ b/app/Jobs/InstanceAutoUpdateJob.php @@ -11,11 +11,12 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted +class InstanceAutoUpdateJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $timeout = 600; + public $tries = 1; public function __construct() diff --git a/app/Jobs/PullCoolifyImageJob.php b/app/Jobs/PullCoolifyImageJob.php index 9ab0ced0f..ccaa785dc 100644 --- a/app/Jobs/PullCoolifyImageJob.php +++ b/app/Jobs/PullCoolifyImageJob.php @@ -13,7 +13,7 @@ use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Http; -class PullCoolifyImageJob implements ShouldQueue, ShouldBeEncrypted +class PullCoolifyImageJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -22,6 +22,7 @@ class PullCoolifyImageJob implements ShouldQueue, ShouldBeEncrypted public function __construct() { } + public function handle(): void { try { @@ -39,7 +40,7 @@ public function handle(): void $settings = InstanceSettings::get(); $current_version = config('version'); - if (!$settings->is_auto_update_enabled) { + if (! $settings->is_auto_update_enabled) { return; } if ($latest_version === $current_version) { @@ -49,8 +50,8 @@ public function handle(): void return; } instant_remote_process([ - "curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh", - "bash /data/coolify/source/upgrade.sh $latest_version" + 'curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh', + "bash /data/coolify/source/upgrade.sh $latest_version", ], $server); } catch (\Throwable $e) { throw $e; diff --git a/app/Jobs/PullHelperImageJob.php b/app/Jobs/PullHelperImageJob.php index 848c316f2..d3bda2ea1 100644 --- a/app/Jobs/PullHelperImageJob.php +++ b/app/Jobs/PullHelperImageJob.php @@ -11,7 +11,7 @@ use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; -class PullHelperImageJob implements ShouldQueue, ShouldBeEncrypted +class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -26,9 +26,11 @@ public function uniqueId(): string { return $this->server->uuid; } + public function __construct(public Server $server) { } + public function handle(): void { try { @@ -37,7 +39,7 @@ public function handle(): void instant_remote_process(["docker pull -q {$helperImage}"], $this->server, false); ray('PullHelperImageJob done'); } catch (\Throwable $e) { - send_internal_notification('PullHelperImageJob failed with: ' . $e->getMessage()); + send_internal_notification('PullHelperImageJob failed with: '.$e->getMessage()); ray($e->getMessage()); throw $e; } diff --git a/app/Jobs/PullSentinelImageJob.php b/app/Jobs/PullSentinelImageJob.php index 1c51928f6..1dd4b1dd3 100644 --- a/app/Jobs/PullSentinelImageJob.php +++ b/app/Jobs/PullSentinelImageJob.php @@ -12,7 +12,7 @@ use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; -class PullSentinelImageJob implements ShouldQueue, ShouldBeEncrypted +class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -27,15 +27,18 @@ public function uniqueId(): string { return $this->server->uuid; } + public function __construct(public Server $server) { } + public function handle(): void { try { $version = get_latest_sentinel_version(); - if (!$version) { + if (! $version) { ray('Failed to get latest Sentinel version'); + return; } $local_version = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/version"'], $this->server, false); @@ -44,11 +47,12 @@ public function handle(): void } if (version_compare($local_version, $version, '<')) { StartSentinel::run($this->server, $version, true); + return; } ray('Sentinel image is up to date'); } catch (\Throwable $e) { - send_internal_notification('PullSentinelImageJob failed with: ' . $e->getMessage()); + send_internal_notification('PullSentinelImageJob failed with: '.$e->getMessage()); ray($e->getMessage()); throw $e; } diff --git a/app/Jobs/PullTemplatesFromCDN.php b/app/Jobs/PullTemplatesFromCDN.php index 66e7611a7..948060033 100644 --- a/app/Jobs/PullTemplatesFromCDN.php +++ b/app/Jobs/PullTemplatesFromCDN.php @@ -2,7 +2,6 @@ namespace App\Jobs; -use App\Models\Server; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeEncrypted; use Illuminate\Contracts\Queue\ShouldQueue; @@ -12,7 +11,7 @@ use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Http; -class PullTemplatesFromCDN implements ShouldQueue, ShouldBeEncrypted +class PullTemplatesFromCDN implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -21,21 +20,22 @@ class PullTemplatesFromCDN implements ShouldQueue, ShouldBeEncrypted public function __construct() { } + public function handle(): void { try { - if (!isDev()) { + if (! isDev()) { ray('PullTemplatesAndVersions service-templates'); $response = Http::retry(3, 1000)->get(config('constants.services.official')); if ($response->successful()) { $services = $response->json(); File::put(base_path('templates/service-templates.json'), json_encode($services)); } else { - send_internal_notification('PullTemplatesAndVersions failed with: ' . $response->status() . ' ' . $response->body()); + send_internal_notification('PullTemplatesAndVersions failed with: '.$response->status().' '.$response->body()); } } } catch (\Throwable $e) { - send_internal_notification('PullTemplatesAndVersions failed with: ' . $e->getMessage()); + send_internal_notification('PullTemplatesAndVersions failed with: '.$e->getMessage()); ray($e->getMessage()); } } diff --git a/app/Jobs/PullVersionsFromCDN.php b/app/Jobs/PullVersionsFromCDN.php index a5397ef08..1ad4989de 100644 --- a/app/Jobs/PullVersionsFromCDN.php +++ b/app/Jobs/PullVersionsFromCDN.php @@ -11,7 +11,7 @@ use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Http; -class PullVersionsFromCDN implements ShouldQueue, ShouldBeEncrypted +class PullVersionsFromCDN implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -20,17 +20,18 @@ class PullVersionsFromCDN implements ShouldQueue, ShouldBeEncrypted public function __construct() { } + public function handle(): void { try { - if (!isDev() && !isCloud()) { + if (! isDev() && ! isCloud()) { ray('PullTemplatesAndVersions versions.json'); $response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json'); if ($response->successful()) { $versions = $response->json(); File::put(base_path('versions.json'), json_encode($versions, JSON_PRETTY_PRINT)); } else { - send_internal_notification('PullTemplatesAndVersions failed with: ' . $response->status() . ' ' . $response->body()); + send_internal_notification('PullTemplatesAndVersions failed with: '.$response->status().' '.$response->body()); } } } catch (\Throwable $e) { diff --git a/app/Jobs/ScheduledTaskJob.php b/app/Jobs/ScheduledTaskJob.php index a28f85901..819e28f89 100644 --- a/app/Jobs/ScheduledTaskJob.php +++ b/app/Jobs/ScheduledTaskJob.php @@ -2,10 +2,10 @@ namespace App\Jobs; +use App\Models\Application; use App\Models\ScheduledTask; use App\Models\ScheduledTaskExecution; use App\Models\Server; -use App\Models\Application; use App\Models\Service; use App\Models\Team; use App\Notifications\ScheduledTask\TaskFailed; @@ -21,13 +21,19 @@ class ScheduledTaskJob implements ShouldQueue use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public ?Team $team = null; + public Server $server; + public ScheduledTask $task; + public Application|Service $resource; public ?ScheduledTaskExecution $task_log = null; + public string $task_status = 'failed'; + public ?string $task_output = null; + public array $containers = []; public function __construct($task) @@ -35,7 +41,7 @@ public function __construct($task) $this->task = $task; if ($service = $task->service()->first()) { $this->resource = $service; - } else if ($application = $task->application()->first()) { + } elseif ($application = $task->application()->first()) { $this->resource = $application; } else { throw new \RuntimeException('ScheduledTaskJob failed: No resource found.'); @@ -69,16 +75,15 @@ public function handle(): void $this->containers[] = str_replace('/', '', $container['Names']); }); } - } - elseif ($this->resource->type() == 'service') { + } elseif ($this->resource->type() == 'service') { $this->resource->applications()->get()->each(function ($application) { if (str(data_get($application, 'status'))->contains('running')) { - $this->containers[] = data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'); + $this->containers[] = data_get($application, 'name').'-'.data_get($this->resource, 'uuid'); } }); $this->resource->databases()->get()->each(function ($database) { if (str(data_get($database, 'status'))->contains('running')) { - $this->containers[] = data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'); + $this->containers[] = data_get($database, 'name').'-'.data_get($this->resource, 'uuid'); } }); } @@ -91,21 +96,21 @@ public function handle(): void } foreach ($this->containers as $containerName) { - if (count($this->containers) == 1 || str_starts_with($containerName, $this->task->container . '-' . $this->resource->uuid)) { - $cmd = "sh -c '" . str_replace("'", "'\''", $this->task->command) . "'"; + if (count($this->containers) == 1 || str_starts_with($containerName, $this->task->container.'-'.$this->resource->uuid)) { + $cmd = "sh -c '".str_replace("'", "'\''", $this->task->command)."'"; $exec = "docker exec {$containerName} {$cmd}"; $this->task_output = instant_remote_process([$exec], $this->server, true); $this->task_log->update([ 'status' => 'success', 'message' => $this->task_output, ]); + return; } } // No valid container was found. throw new \Exception('ScheduledTaskJob failed: No valid container was found. Is the container name correct?'); - } catch (\Throwable $e) { if ($this->task_log) { $this->task_log->update([ diff --git a/app/Jobs/SendConfirmationForWaitlistJob.php b/app/Jobs/SendConfirmationForWaitlistJob.php index bee15975c..4d5618df0 100755 --- a/app/Jobs/SendConfirmationForWaitlistJob.php +++ b/app/Jobs/SendConfirmationForWaitlistJob.php @@ -2,19 +2,15 @@ namespace App\Jobs; -use App\Models\InstanceSettings; -use App\Models\Waitlist; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeEncrypted; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Mail\Message; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Mail; -class SendConfirmationForWaitlistJob implements ShouldQueue, ShouldBeEncrypted +class SendConfirmationForWaitlistJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -26,8 +22,8 @@ public function handle() { try { $mail = new MailMessage(); - $confirmation_url = base_url() . '/webhooks/waitlist/confirm?email=' . $this->email . '&confirmation_code=' . $this->uuid; - $cancel_url = base_url() . '/webhooks/waitlist/cancel?email=' . $this->email . '&confirmation_code=' . $this->uuid; + $confirmation_url = base_url().'/webhooks/waitlist/confirm?email='.$this->email.'&confirmation_code='.$this->uuid; + $cancel_url = base_url().'/webhooks/waitlist/cancel?email='.$this->email.'&confirmation_code='.$this->uuid; $mail->view('emails.waitlist-confirmation', [ 'confirmation_url' => $confirmation_url, @@ -36,7 +32,7 @@ public function handle() $mail->subject('You are on the waitlist!'); send_user_an_email($mail, $this->email); } catch (\Throwable $e) { - send_internal_notification("SendConfirmationForWaitlistJob failed for {$this->email} with error: " . $e->getMessage()); + send_internal_notification("SendConfirmationForWaitlistJob failed for {$this->email} with error: ".$e->getMessage()); ray($e->getMessage()); throw $e; } diff --git a/app/Jobs/SendMessageToDiscordJob.php b/app/Jobs/SendMessageToDiscordJob.php index ddd6bd271..90f2e0b30 100644 --- a/app/Jobs/SendMessageToDiscordJob.php +++ b/app/Jobs/SendMessageToDiscordJob.php @@ -10,7 +10,7 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Http; -class SendMessageToDiscordJob implements ShouldQueue, ShouldBeEncrypted +class SendMessageToDiscordJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -20,6 +20,7 @@ class SendMessageToDiscordJob implements ShouldQueue, ShouldBeEncrypted * @var int */ public $tries = 5; + public $backoff = 10; /** diff --git a/app/Jobs/SendMessageToTelegramJob.php b/app/Jobs/SendMessageToTelegramJob.php index 4191b02fe..b81bbc50b 100644 --- a/app/Jobs/SendMessageToTelegramJob.php +++ b/app/Jobs/SendMessageToTelegramJob.php @@ -11,7 +11,7 @@ use Illuminate\Support\Facades\Http; use Illuminate\Support\Str; -class SendMessageToTelegramJob implements ShouldQueue, ShouldBeEncrypted +class SendMessageToTelegramJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -41,9 +41,9 @@ public function __construct( */ public function handle(): void { - $url = 'https://api.telegram.org/bot' . $this->token . '/sendMessage'; + $url = 'https://api.telegram.org/bot'.$this->token.'/sendMessage'; $inlineButtons = []; - if (!empty($this->buttons)) { + if (! empty($this->buttons)) { foreach ($this->buttons as $button) { $buttonUrl = data_get($button, 'url'); $text = data_get($button, 'text', 'Click here'); @@ -71,7 +71,7 @@ public function handle(): void } $response = Http::post($url, $payload); if ($response->failed()) { - throw new \Exception('Telegram notification failed with ' . $response->status() . ' status code.' . $response->body()); + throw new \Exception('Telegram notification failed with '.$response->status().' status code.'.$response->body()); } } } diff --git a/app/Jobs/ServerFilesFromServerJob.php b/app/Jobs/ServerFilesFromServerJob.php index 978a3dc19..2476c12dd 100644 --- a/app/Jobs/ServerFilesFromServerJob.php +++ b/app/Jobs/ServerFilesFromServerJob.php @@ -12,14 +12,14 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class ServerFilesFromServerJob implements ShouldQueue, ShouldBeEncrypted +class ServerFilesFromServerJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - public function __construct(public ServiceApplication|ServiceDatabase|Application $resource) { } + public function handle() { $this->resource->getFilesFromServer(isInit: true); diff --git a/app/Jobs/ServerLimitCheckJob.php b/app/Jobs/ServerLimitCheckJob.php index 9d0e5db94..3eaf88ba7 100644 --- a/app/Jobs/ServerLimitCheckJob.php +++ b/app/Jobs/ServerLimitCheckJob.php @@ -13,18 +13,21 @@ use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; -class ServerLimitCheckJob implements ShouldQueue, ShouldBeEncrypted +class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $tries = 4; + public function backoff(): int { return isDev() ? 1 : 3; } + public function __construct(public Team $team) { } + public function middleware(): array { return [(new WithoutOverlapping($this->team->uuid))]; @@ -51,7 +54,7 @@ public function handle() $server->forceDisableServer(); $this->team->notify(new ForceDisabled($server)); }); - } else if ($number_of_servers_to_disable === 0) { + } elseif ($number_of_servers_to_disable === 0) { $servers->each(function ($server) { if ($server->isForceDisabled()) { $server->forceEnableServer(); @@ -60,8 +63,9 @@ public function handle() }); } } catch (\Throwable $e) { - send_internal_notification('ServerLimitCheckJob failed with: ' . $e->getMessage()); + send_internal_notification('ServerLimitCheckJob failed with: '.$e->getMessage()); ray($e->getMessage()); + return handleError($e); } } diff --git a/app/Jobs/ServerStatusJob.php b/app/Jobs/ServerStatusJob.php index d104185c0..aaf8f5784 100644 --- a/app/Jobs/ServerStatusJob.php +++ b/app/Jobs/ServerStatusJob.php @@ -12,19 +12,23 @@ use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; -class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted +class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public int|string|null $disk_usage = null; + public $tries = 3; + public function backoff(): int { return isDev() ? 1 : 3; } + public function __construct(public Server $server) { } + public function middleware(): array { return [(new WithoutOverlapping($this->server->uuid))]; @@ -37,9 +41,9 @@ public function uniqueId(): int public function handle() { - if (!$this->server->isServerReady($this->tries)) { + if (! $this->server->isServerReady($this->tries)) { throw new \RuntimeException('Server is not ready.'); - }; + } try { if ($this->server->isFunctional()) { $this->cleanup(notify: false); @@ -49,8 +53,9 @@ public function handle() } } } catch (\Throwable $e) { - send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage()); + send_internal_notification('ServerStatusJob failed with: '.$e->getMessage()); ray($e->getMessage()); + return handleError($e); } try { @@ -59,47 +64,53 @@ public function handle() // Do nothing } } + private function check_docker_engine() { $version = instant_remote_process([ - "docker info", + 'docker info', ], $this->server, false); if (is_null($version)) { $os = instant_remote_process([ - "cat /etc/os-release | grep ^ID=", + 'cat /etc/os-release | grep ^ID=', ], $this->server, false); $os = str($os)->after('ID=')->trim(); if ($os === 'ubuntu') { try { instant_remote_process([ - "systemctl start docker", + 'systemctl start docker', ], $this->server); } catch (\Throwable $e) { ray($e->getMessage()); + return handleError($e); } } else { try { instant_remote_process([ - "service docker start", + 'service docker start', ], $this->server); } catch (\Throwable $e) { ray($e->getMessage()); + return handleError($e); } } } } + private function remove_unnecessary_coolify_yaml() { // This will remote the coolify.yaml file from the server as it is not needed on cloud servers if (isCloud() && $this->server->id !== 0) { - $file = $this->server->proxyPath() . "/dynamic/coolify.yaml"; + $file = $this->server->proxyPath().'/dynamic/coolify.yaml'; + return instant_remote_process([ "rm -f $file", ], $this->server, false); } } + public function cleanup(bool $notify = false): void { $this->disk_usage = $this->server->getDiskUsage(); @@ -107,6 +118,7 @@ public function cleanup(bool $notify = false): void if ($notify) { if ($this->server->high_disk_usage_notification_sent) { ray('high disk usage notification already sent'); + return; } else { $this->server->high_disk_usage_notification_sent = true; diff --git a/app/Jobs/ServerStorageSaveJob.php b/app/Jobs/ServerStorageSaveJob.php index 7ed55cf5a..c94a3edc5 100644 --- a/app/Jobs/ServerStorageSaveJob.php +++ b/app/Jobs/ServerStorageSaveJob.php @@ -10,17 +10,16 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class ServerStorageSaveJob implements ShouldQueue, ShouldBeEncrypted +class ServerStorageSaveJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - public function __construct(public LocalFileVolume $localFileVolume) { } + public function handle() { $this->localFileVolume->saveStorageOnServer(); } - } diff --git a/app/Jobs/SubscriptionInvoiceFailedJob.php b/app/Jobs/SubscriptionInvoiceFailedJob.php index 9b8534060..e4cd219c8 100755 --- a/app/Jobs/SubscriptionInvoiceFailedJob.php +++ b/app/Jobs/SubscriptionInvoiceFailedJob.php @@ -11,7 +11,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class SubscriptionInvoiceFailedJob implements ShouldQueue, ShouldBeEncrypted +class SubscriptionInvoiceFailedJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -35,7 +35,7 @@ public function handle() } }); } catch (\Throwable $e) { - send_internal_notification('SubscriptionInvoiceFailedJob failed with: ' . $e->getMessage()); + send_internal_notification('SubscriptionInvoiceFailedJob failed with: '.$e->getMessage()); ray($e->getMessage()); throw $e; } diff --git a/app/Jobs/SubscriptionTrialEndedJob.php b/app/Jobs/SubscriptionTrialEndedJob.php index 3f4ef187e..ee260d8d9 100755 --- a/app/Jobs/SubscriptionTrialEndedJob.php +++ b/app/Jobs/SubscriptionTrialEndedJob.php @@ -11,7 +11,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class SubscriptionTrialEndedJob implements ShouldQueue, ShouldBeEncrypted +class SubscriptionTrialEndedJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -31,13 +31,13 @@ public function handle(): void ]); $this->team->members()->each(function ($member) use ($mail) { if ($member->isAdmin()) { - ray('Sending trial ended email to ' . $member->email); + ray('Sending trial ended email to '.$member->email); send_user_an_email($mail, $member->email); - send_internal_notification('Trial reminder email sent to ' . $member->email); + send_internal_notification('Trial reminder email sent to '.$member->email); } }); } catch (\Throwable $e) { - send_internal_notification('SubscriptionTrialEndsSoonJob failed with: ' . $e->getMessage()); + send_internal_notification('SubscriptionTrialEndsSoonJob failed with: '.$e->getMessage()); ray($e->getMessage()); throw $e; } diff --git a/app/Jobs/SubscriptionTrialEndsSoonJob.php b/app/Jobs/SubscriptionTrialEndsSoonJob.php index 5e8b35aa8..fba668108 100755 --- a/app/Jobs/SubscriptionTrialEndsSoonJob.php +++ b/app/Jobs/SubscriptionTrialEndsSoonJob.php @@ -11,7 +11,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class SubscriptionTrialEndsSoonJob implements ShouldQueue, ShouldBeEncrypted +class SubscriptionTrialEndsSoonJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -31,13 +31,13 @@ public function handle(): void ]); $this->team->members()->each(function ($member) use ($mail) { if ($member->isAdmin()) { - ray('Sending trial ending email to ' . $member->email); + ray('Sending trial ending email to '.$member->email); send_user_an_email($mail, $member->email); - send_internal_notification('Trial reminder email sent to ' . $member->email); + send_internal_notification('Trial reminder email sent to '.$member->email); } }); } catch (\Throwable $e) { - send_internal_notification('SubscriptionTrialEndsSoonJob failed with: ' . $e->getMessage()); + send_internal_notification('SubscriptionTrialEndsSoonJob failed with: '.$e->getMessage()); ray($e->getMessage()); throw $e; } diff --git a/app/Listeners/MaintenanceModeDisabledNotification.php b/app/Listeners/MaintenanceModeDisabledNotification.php index e8a9c04c7..9f676ca99 100644 --- a/app/Listeners/MaintenanceModeDisabledNotification.php +++ b/app/Listeners/MaintenanceModeDisabledNotification.php @@ -37,7 +37,7 @@ public function handle(EventsMaintenanceModeDisabled $event): void } $request = Request::createFromBase($symfonyRequest); $endpoint = str($file)->after('_')->beforeLast('_')->value(); - $class = "App\Http\Controllers\Webhook\\" . ucfirst(str($endpoint)->before('::')->value()); + $class = "App\Http\Controllers\Webhook\\".ucfirst(str($endpoint)->before('::')->value()); $method = str($endpoint)->after('::')->value(); try { $instance = new $class(); diff --git a/app/Listeners/MaintenanceModeEnabledNotification.php b/app/Listeners/MaintenanceModeEnabledNotification.php index 8493a4d1f..b2cd8c738 100644 --- a/app/Listeners/MaintenanceModeEnabledNotification.php +++ b/app/Listeners/MaintenanceModeEnabledNotification.php @@ -2,10 +2,7 @@ namespace App\Listeners; -use App\Events\MaintenanceModeEnabled; -use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Events\MaintenanceModeEnabled as EventsMaintenanceModeEnabled; -use Illuminate\Queue\InteractsWithQueue; class MaintenanceModeEnabledNotification { diff --git a/app/Listeners/ProxyStartedNotification.php b/app/Listeners/ProxyStartedNotification.php index 1a4fe97bb..64271cc52 100644 --- a/app/Listeners/ProxyStartedNotification.php +++ b/app/Listeners/ProxyStartedNotification.php @@ -8,6 +8,7 @@ class ProxyStartedNotification { public Server $server; + public function __construct() { } diff --git a/app/Livewire/ActivityMonitor.php b/app/Livewire/ActivityMonitor.php index 37bfc77bb..bd1e30088 100644 --- a/app/Livewire/ActivityMonitor.php +++ b/app/Livewire/ActivityMonitor.php @@ -10,13 +10,19 @@ class ActivityMonitor extends Component { public ?string $header = null; + public $activityId; + public $eventToDispatch = 'activityFinished'; + public $isPollingActive = false; + public bool $fullHeight = false; + public bool $showWaiting = false; protected $activity; + protected $listeners = ['activityMonitor' => 'newMonitorActivity']; public function newMonitorActivity($activityId, $eventToDispatch = 'activityFinished') @@ -52,11 +58,12 @@ public function polling() $causer_id = data_get($this->activity, 'causer_id'); $user = User::find($causer_id); if ($user) { - foreach($user->teams as $team) { + foreach ($user->teams as $team) { $teamId = $team->id; $this->eventToDispatch::dispatch($teamId); } } + return; } $this->dispatch($this->eventToDispatch); diff --git a/app/Livewire/Admin/Index.php b/app/Livewire/Admin/Index.php index b72bc8e35..26b31e515 100644 --- a/app/Livewire/Admin/Index.php +++ b/app/Livewire/Admin/Index.php @@ -9,10 +9,14 @@ class Index extends Component { public $active_subscribers = []; + public $inactive_subscribers = []; + public $search = ''; - public function submitSearch() { - if ($this->search !== "") { + + public function submitSearch() + { + if ($this->search !== '') { $this->inactive_subscribers = User::whereDoesntHave('teams', function ($query) { $query->whereRelation('subscription', 'stripe_subscription_id', '!=', null); })->where(function ($query) { @@ -33,9 +37,10 @@ public function submitSearch() { $this->getSubscribers(); } } + public function mount() { - if (!isCloud()) { + if (! isCloud()) { return redirect()->route('dashboard'); } if (auth()->user()->id !== 0) { @@ -43,7 +48,9 @@ public function mount() } $this->getSubscribers(); } - public function getSubscribers() { + + public function getSubscribers() + { $this->inactive_subscribers = User::whereDoesntHave('teams', function ($query) { $query->whereRelation('subscription', 'stripe_subscription_id', '!=', null); })->get()->filter(function ($user) { @@ -55,6 +62,7 @@ public function getSubscribers() { return $user->id !== 0; }); } + public function switchUser(int $user_id) { if (auth()->user()->id !== 0) { @@ -65,8 +73,10 @@ public function switchUser(int $user_id) Cache::forget("team:{$user->id}"); auth()->login($user); refreshSession($team_to_switch_to); + return redirect(request()->header('Referer')); } + public function render() { return view('livewire.admin.index'); diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php index 8f4e87090..b787ed0cc 100644 --- a/app/Livewire/Boarding/Index.php +++ b/app/Livewire/Boarding/Index.php @@ -8,7 +8,6 @@ use App\Models\Server; use App\Models\Team; use Illuminate\Support\Collection; -use Livewire\Attributes\Url; use Livewire\Component; class Index extends Component @@ -18,36 +17,53 @@ class Index extends Component public string $currentState = 'welcome'; public ?string $selectedServerType = null; + public ?Collection $privateKeys = null; public ?int $selectedExistingPrivateKey = null; + public ?string $privateKeyType = null; + public ?string $privateKey = null; + public ?string $publicKey = null; + public ?string $privateKeyName = null; + public ?string $privateKeyDescription = null; + public ?PrivateKey $createdPrivateKey = null; public ?Collection $servers = null; public ?int $selectedExistingServer = null; + public ?string $remoteServerName = null; + public ?string $remoteServerDescription = null; + public ?string $remoteServerHost = null; - public ?int $remoteServerPort = 22; + + public ?int $remoteServerPort = 22; + public ?string $remoteServerUser = 'root'; + public bool $isSwarmManager = false; + public bool $isCloudflareTunnel = false; + public ?Server $createdServer = null; public Collection $projects; public ?int $selectedProject = null; + public ?Project $createdProject = null; public bool $dockerInstallationStarted = false; public string $serverPublicKey; + public bool $serverReachable = true; public function mount() @@ -90,6 +106,7 @@ public function mount() // } } + public function explanation() { if (isCloud()) { @@ -102,12 +119,14 @@ public function restartBoarding() { return redirect()->route('onboarding'); } + public function skipBoarding() { Team::find(currentTeam()->id)->update([ - 'show_boarding' => false + 'show_boarding' => false, ]); refreshSession(); + return redirect()->route('dashboard'); } @@ -117,10 +136,11 @@ public function setServerType(string $type) if ($this->selectedServerType === 'localhost') { $this->createdServer = Server::find(0); $this->selectedExistingServer = 0; - if (!$this->createdServer) { + if (! $this->createdServer) { return $this->dispatch('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.'); } $this->serverPublicKey = $this->createdServer->privateKey->publicKey(); + return $this->validateServer('localhost'); } elseif ($this->selectedServerType === 'remote') { if (isDev()) { @@ -135,23 +155,27 @@ public function setServerType(string $type) if ($this->servers->count() > 0) { $this->selectedExistingServer = $this->servers->first()->id; $this->currentState = 'select-existing-server'; + return; } $this->currentState = 'private-key'; } } + public function selectExistingServer() { $this->createdServer = Server::find($this->selectedExistingServer); - if (!$this->createdServer) { + if (! $this->createdServer) { $this->dispatch('error', 'Server is not found.'); $this->currentState = 'private-key'; + return; } $this->selectedExistingPrivateKey = $this->createdServer->privateKey->id; $this->serverPublicKey = $this->createdServer->privateKey->publicKey(); $this->currentState = 'validate-server'; } + public function getProxyType() { // Set Default Proxy Type @@ -163,21 +187,25 @@ public function getProxyType() // } $this->getProjects(); } + public function selectExistingPrivateKey() { if (is_null($this->selectedExistingPrivateKey)) { $this->restartBoarding(); + return; } $this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey); $this->privateKey = $this->createdPrivateKey->private_key; $this->currentState = 'create-server'; } + public function createNewServer() { $this->selectedExistingServer = null; $this->currentState = 'private-key'; } + public function setPrivateKey(string $type) { $this->selectedExistingPrivateKey = null; @@ -187,6 +215,7 @@ public function setPrivateKey(string $type) } $this->currentState = 'create-private-key'; } + public function savePrivateKey() { $this->validate([ @@ -197,11 +226,12 @@ public function savePrivateKey() 'name' => $this->privateKeyName, 'description' => $this->privateKeyDescription, 'private_key' => $this->privateKey, - 'team_id' => currentTeam()->id + 'team_id' => currentTeam()->id, ]); $this->createdPrivateKey->save(); $this->currentState = 'create-server'; } + public function saveServer() { $this->validate([ @@ -231,10 +261,12 @@ public function saveServer() $this->selectedExistingServer = $this->createdServer->id; $this->currentState = 'validate-server'; } + public function installServer() { $this->dispatch('init', true); } + public function validateServer() { try { @@ -249,6 +281,7 @@ public function validateServer() } catch (\Throwable $e) { $this->serverReachable = false; $this->createdServer->delete(); + return handleError(error: $e, livewire: $this); } @@ -267,9 +300,10 @@ public function validateServer() return handleError(error: $e, livewire: $this); } } + public function selectProxy(?string $proxyType = null) { - if (!$proxyType) { + if (! $proxyType) { return $this->getProjects(); } $this->createdServer->proxy->type = $proxyType; @@ -286,22 +320,26 @@ public function getProjects() } $this->currentState = 'create-project'; } + public function selectExistingProject() { $this->createdProject = Project::find($this->selectedProject); $this->currentState = 'create-resource'; } + public function createNewProject() { $this->createdProject = Project::create([ - 'name' => "My first project", - 'team_id' => currentTeam()->id + 'name' => 'My first project', + 'team_id' => currentTeam()->id, ]); $this->currentState = 'create-resource'; } + public function showNewResource() { $this->skipBoarding(); + return redirect()->route( 'project.resource.create', [ @@ -311,12 +349,14 @@ public function showNewResource() ] ); } + private function createNewPrivateKey() { $this->privateKeyName = generate_random_name(); $this->privateKeyDescription = 'Created by Coolify'; ['private' => $this->privateKey, 'public' => $this->publicKey] = generateSSHKey(); } + public function render() { return view('livewire.boarding.index')->layout('layouts.boarding'); diff --git a/app/Livewire/CommandCenter/Index.php b/app/Livewire/CommandCenter/Index.php index fd6bb7ed6..0a05e811f 100644 --- a/app/Livewire/CommandCenter/Index.php +++ b/app/Livewire/CommandCenter/Index.php @@ -8,9 +8,12 @@ class Index extends Component { public $servers = []; - public function mount() { + + public function mount() + { $this->servers = Server::isReachable()->get(); } + public function render() { return view('livewire.command-center.index'); diff --git a/app/Livewire/Dashboard.php b/app/Livewire/Dashboard.php index 8a5d491e4..1abd28c3c 100644 --- a/app/Livewire/Dashboard.php +++ b/app/Livewire/Dashboard.php @@ -13,9 +13,13 @@ class Dashboard extends Component { public $projects = []; + public Collection $servers; + public Collection $private_keys; + public $deployments_per_server; + public function mount() { $this->private_keys = PrivateKey::ownedByCurrentTeam()->get(); @@ -23,26 +27,29 @@ public function mount() $this->projects = Project::ownedByCurrentTeam()->get(); $this->get_deployments(); } + public function cleanup_queue() { $this->dispatch('success', 'Cleanup started.'); Artisan::queue('cleanup:application-deployment-queue', [ - '--team-id' => currentTeam()->id + '--team-id' => currentTeam()->id, ]); } + public function get_deployments() { - $this->deployments_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn("server_id", $this->servers->pluck("id"))->get([ - "id", - "application_id", - "application_name", - "deployment_url", - "pull_request_id", - "server_name", - "server_id", - "status" + $this->deployments_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $this->servers->pluck('id'))->get([ + 'id', + 'application_id', + 'application_name', + 'deployment_url', + 'pull_request_id', + 'server_name', + 'server_id', + 'status', ])->sortBy('id')->groupBy('server_name')->toArray(); } + // public function getIptables() // { // $servers = Server::ownedByCurrentTeam()->get(); diff --git a/app/Livewire/Destination/Form.php b/app/Livewire/Destination/Form.php index b59708303..7125f2120 100644 --- a/app/Livewire/Destination/Form.php +++ b/app/Livewire/Destination/Form.php @@ -13,6 +13,7 @@ class Form extends Component 'destination.network' => 'required', 'destination.server.ip' => 'required', ]; + protected $validationAttributes = [ 'destination.name' => 'name', 'destination.network' => 'network', @@ -33,9 +34,10 @@ public function delete() return $this->dispatch('error', 'You must delete all resources before deleting this destination.'); } instant_remote_process(["docker network disconnect {$this->destination->network} coolify-proxy"], $this->destination->server, throwError: false); - instant_remote_process(['docker network rm -f ' . $this->destination->network], $this->destination->server); + instant_remote_process(['docker network rm -f '.$this->destination->network], $this->destination->server); } $this->destination->delete(); + return redirect()->route('dashboard'); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Destination/New/Docker.php b/app/Livewire/Destination/New/Docker.php index d87f4bc0a..f822cfa5f 100644 --- a/app/Livewire/Destination/New/Docker.php +++ b/app/Livewire/Destination/New/Docker.php @@ -12,24 +12,29 @@ class Docker extends Component { public string $name; + public string $network; public ?Collection $servers = null; + public Server $server; + public ?int $server_id = null; + public bool $is_swarm = false; protected $rules = [ 'name' => 'required|string', 'network' => 'required|string', 'server_id' => 'required|integer', - 'is_swarm' => 'boolean' + 'is_swarm' => 'boolean', ]; + protected $validationAttributes = [ 'name' => 'name', 'network' => 'network', 'server_id' => 'server', - 'is_swarm' => 'swarm' + 'is_swarm' => 'swarm', ]; public function mount() @@ -69,6 +74,7 @@ public function submit() $found = $this->server->swarmDockers()->where('network', $this->network)->first(); if ($found) { $this->dispatch('error', 'Network already added to this server.'); + return; } else { $docker = SwarmDocker::create([ @@ -81,6 +87,7 @@ public function submit() $found = $this->server->standaloneDockers()->where('network', $this->network)->first(); if ($found) { $this->dispatch('error', 'Network already added to this server.'); + return; } else { $docker = ModelsStandaloneDocker::create([ @@ -91,6 +98,7 @@ public function submit() } } $this->createNetworkAndAttachToProxy(); + return redirect()->route('destination.show', $docker->uuid); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Destination/Show.php b/app/Livewire/Destination/Show.php index 4bdbf88b0..5650e82ba 100644 --- a/app/Livewire/Destination/Show.php +++ b/app/Livewire/Destination/Show.php @@ -11,6 +11,7 @@ class Show extends Component { public Server $server; + public Collection|array $networks = []; private function createNetworkAndAttachToProxy() @@ -18,16 +19,18 @@ private function createNetworkAndAttachToProxy() $connectProxyToDockerNetworks = connectProxyToNetworks($this->server); instant_remote_process($connectProxyToDockerNetworks, $this->server, false); } + public function add($name) { if ($this->server->isSwarm()) { $found = $this->server->swarmDockers()->where('network', $name)->first(); if ($found) { $this->dispatch('error', 'Network already added to this server.'); + return; } else { SwarmDocker::create([ - 'name' => $this->server->name . "-" . $name, + 'name' => $this->server->name.'-'.$name, 'network' => $this->name, 'server_id' => $this->server->id, ]); @@ -36,10 +39,11 @@ public function add($name) $found = $this->server->standaloneDockers()->where('network', $name)->first(); if ($found) { $this->dispatch('error', 'Network already added to this server.'); + return; } else { StandaloneDocker::create([ - 'name' => $this->server->name . "-" . $name, + 'name' => $this->server->name.'-'.$name, 'network' => $name, 'server_id' => $this->server->id, ]); @@ -47,6 +51,7 @@ public function add($name) $this->createNetworkAndAttachToProxy(); } } + public function scan() { if ($this->server->isSwarm()) { @@ -58,10 +63,11 @@ public function scan() $this->networks = format_docker_command_output_to_json($networks)->filter(function ($network) { return $network['Name'] !== 'bridge' && $network['Name'] !== 'host' && $network['Name'] !== 'none'; })->filter(function ($network) use ($alreadyAddedNetworks) { - return !$alreadyAddedNetworks->contains('network', $network['Name']); + return ! $alreadyAddedNetworks->contains('network', $network['Name']); }); if ($this->networks->count() === 0) { $this->dispatch('success', 'No new networks found.'); + return; } $this->dispatch('success', 'Scan done.'); diff --git a/app/Livewire/Dev/Compose.php b/app/Livewire/Dev/Compose.php index 8c361ba2a..a5cd53fc2 100644 --- a/app/Livewire/Dev/Compose.php +++ b/app/Livewire/Dev/Compose.php @@ -7,20 +7,29 @@ class Compose extends Component { public string $compose = ''; + public string $base64 = ''; + public $services; - public function mount() { + + public function mount() + { $this->services = get_service_templates(); } - public function setService(string $selected) { - $this->base64 = data_get($this->services, $selected . '.compose'); + + public function setService(string $selected) + { + $this->base64 = data_get($this->services, $selected.'.compose'); if ($this->base64) { $this->compose = base64_decode($this->base64); } } - public function updatedCompose($value) { + + public function updatedCompose($value) + { $this->base64 = base64_encode($value); } + public function render() { return view('livewire.dev.compose'); diff --git a/app/Livewire/ForcePasswordReset.php b/app/Livewire/ForcePasswordReset.php index 7bbec9d32..a732ef1c9 100644 --- a/app/Livewire/ForcePasswordReset.php +++ b/app/Livewire/ForcePasswordReset.php @@ -2,15 +2,18 @@ namespace App\Livewire; -use Illuminate\Support\Facades\Hash; use DanHarrin\LivewireRateLimiting\WithRateLimiting; +use Illuminate\Support\Facades\Hash; use Livewire\Component; class ForcePasswordReset extends Component { use WithRateLimiting; + public string $email; + public string $password; + public string $password_confirmation; protected $rules = [ @@ -18,14 +21,17 @@ class ForcePasswordReset extends Component 'password' => 'required|min:8', 'password_confirmation' => 'required|same:password', ]; + public function mount() { $this->email = auth()->user()->email; } + public function render() { return view('livewire.force-password-reset')->layout('layouts.simple'); } + public function submit() { try { @@ -37,8 +43,9 @@ public function submit() 'force_password_reset' => false, ])->save(); if ($firstLogin) { - send_internal_notification('First login for ' . auth()->user()->email); + send_internal_notification('First login for '.auth()->user()->email); } + return redirect()->route('dashboard'); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Help.php b/app/Livewire/Help.php index 657670526..2fbd2bc7e 100644 --- a/app/Livewire/Help.php +++ b/app/Livewire/Help.php @@ -12,13 +12,18 @@ class Help extends Component { use WithRateLimiting; + public string $description; + public string $subject; + public ?string $path = null; + protected $rules = [ 'description' => 'required|min:10', - 'subject' => 'required|min:3' + 'subject' => 'required|min:3', ]; + public function mount() { $this->path = Route::current()?->uri() ?? null; @@ -27,6 +32,7 @@ public function mount() $this->subject = "Help with {$this->path}"; } } + public function submit() { try { @@ -38,28 +44,29 @@ public function submit() 'emails.help', [ 'description' => $this->description, - 'debug' => $debug + 'debug' => $debug, ] ); $mail->subject("[HELP]: {$this->subject}"); $settings = InstanceSettings::get(); $type = set_transanctional_email_settings($settings); - if (!$type) { - $url = "https://app.coolify.io/api/feedback"; + if (! $type) { + $url = 'https://app.coolify.io/api/feedback'; if (isDev()) { - $url = "http://localhost:80/api/feedback"; + $url = 'http://localhost:80/api/feedback'; } Http::post($url, [ - 'content' => "User: `" . auth()->user()?->email . "` with subject: `" . $this->subject . "` has the following problem: `" . $this->description . "`" + 'content' => 'User: `'.auth()->user()?->email.'` with subject: `'.$this->subject.'` has the following problem: `'.$this->description.'`', ]); } else { - send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io'); + send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io'); } $this->dispatch('success', 'Feedback sent.', 'We will get in touch with you as soon as possible.'); } catch (\Throwable $e) { return handleError($e, $this); } } + public function render() { return view('livewire.help')->layout('layouts.app'); diff --git a/app/Livewire/LayoutPopups.php b/app/Livewire/LayoutPopups.php index 136c94ca2..f2ba78893 100644 --- a/app/Livewire/LayoutPopups.php +++ b/app/Livewire/LayoutPopups.php @@ -9,14 +9,17 @@ class LayoutPopups extends Component public function getListeners() { $teamId = auth()->user()->currentTeam()->id; + return [ "echo-private:team.{$teamId},TestEvent" => 'testEvent', ]; } + public function testEvent() { $this->dispatch('success', 'Realtime events configured!'); } + public function render() { return view('livewire.layout-popups'); diff --git a/app/Livewire/NewActivityMonitor.php b/app/Livewire/NewActivityMonitor.php index 853115888..10dbb9ce7 100644 --- a/app/Livewire/NewActivityMonitor.php +++ b/app/Livewire/NewActivityMonitor.php @@ -9,12 +9,17 @@ class NewActivityMonitor extends Component { public ?string $header = null; + public $activityId; + public $eventToDispatch = 'activityFinished'; + public $eventData = null; + public $isPollingActive = false; protected $activity; + protected $listeners = ['newActivityMonitor' => 'newMonitorActivity']; public function newMonitorActivity($activityId, $eventToDispatch = 'activityFinished', $eventData = null) @@ -55,14 +60,15 @@ public function polling() $this->eventToDispatch::dispatch($teamId); } } + return; } - if (!is_null($this->eventData)) { + if (! is_null($this->eventData)) { $this->dispatch($this->eventToDispatch, $this->eventData); } else { $this->dispatch($this->eventToDispatch); } - ray('Dispatched event: ' . $this->eventToDispatch . ' with data: ' . $this->eventData); + ray('Dispatched event: '.$this->eventToDispatch.' with data: '.$this->eventData); } } } diff --git a/app/Livewire/Notifications/Discord.php b/app/Livewire/Notifications/Discord.php index 88705437b..f2219bbc6 100644 --- a/app/Livewire/Notifications/Discord.php +++ b/app/Livewire/Notifications/Discord.php @@ -9,6 +9,7 @@ class Discord extends Component { public Team $team; + protected $rules = [ 'team.discord_enabled' => 'nullable|boolean', 'team.discord_webhook_url' => 'required|url', @@ -18,6 +19,7 @@ class Discord extends Component 'team.discord_notifications_database_backups' => 'nullable|boolean', 'team.discord_notifications_scheduled_tasks' => 'nullable|boolean', ]; + protected $validationAttributes = [ 'team.discord_webhook_url' => 'Discord Webhook', ]; @@ -26,6 +28,7 @@ public function mount() { $this->team = auth()->user()->currentTeam(); } + public function instantSave() { try { @@ -56,6 +59,7 @@ public function sendTestNotification() $this->team?->notify(new Test()); $this->dispatch('success', 'Test notification sent.'); } + public function render() { return view('livewire.notifications.discord'); diff --git a/app/Livewire/Notifications/Email.php b/app/Livewire/Notifications/Email.php index 6ef9b2255..91c108edc 100644 --- a/app/Livewire/Notifications/Email.php +++ b/app/Livewire/Notifications/Email.php @@ -2,15 +2,17 @@ namespace App\Livewire\Notifications; -use Livewire\Component; use App\Models\InstanceSettings; use App\Models\Team; use App\Notifications\Test; +use Livewire\Component; class Email extends Component { public Team $team; + public string $emails; + public bool $sharedEmailEnabled = false; protected $rules = [ @@ -33,6 +35,7 @@ class Email extends Component 'team.resend_enabled' => 'nullable|boolean', 'team.resend_api_key' => 'nullable', ]; + protected $validationAttributes = [ 'team.smtp_from_address' => 'From Address', 'team.smtp_from_name' => 'From Name', @@ -53,6 +56,7 @@ public function mount() ['sharedEmailEnabled' => $this->sharedEmailEnabled] = $this->team->limits; $this->emails = auth()->user()->email; } + public function submitFromFields() { try { @@ -68,15 +72,17 @@ public function submitFromFields() return handleError($e, $this); } } + public function sendTestNotification() { $this->team?->notify(new Test($this->emails)); $this->dispatch('success', 'Test Email sent.'); } + public function instantSaveInstance() { try { - if (!$this->sharedEmailEnabled) { + if (! $this->sharedEmailEnabled) { throw new \Exception('Not allowed to change settings. Please upgrade your subscription.'); } $this->team->smtp_enabled = false; @@ -96,9 +102,11 @@ public function instantSaveResend() $this->submitResend(); } catch (\Throwable $e) { $this->team->smtp_enabled = false; + return handleError($e, $this); } } + public function instantSave() { try { @@ -106,20 +114,23 @@ public function instantSave() $this->submit(); } catch (\Throwable $e) { $this->team->smtp_enabled = false; + return handleError($e, $this); } } + public function saveModel() { $this->team->save(); refreshSession(); $this->dispatch('success', 'Settings saved.'); } + public function submit() { try { $this->resetErrorBag(); - if (!$this->team->use_instance_email_settings) { + if (! $this->team->use_instance_email_settings) { $this->validate([ 'team.smtp_from_address' => 'required|email', 'team.smtp_from_name' => 'required', @@ -136,9 +147,11 @@ public function submit() $this->dispatch('success', 'Settings saved.'); } catch (\Throwable $e) { $this->team->smtp_enabled = false; + return handleError($e, $this); } } + public function submitResend() { try { @@ -146,16 +159,18 @@ public function submitResend() $this->validate([ 'team.smtp_from_address' => 'required|email', 'team.smtp_from_name' => 'required', - 'team.resend_api_key' => 'required' + 'team.resend_api_key' => 'required', ]); $this->team->save(); refreshSession(); $this->dispatch('success', 'Settings saved.'); } catch (\Throwable $e) { $this->team->resend_enabled = false; + return handleError($e, $this); } } + public function copyFromInstanceSettings() { $settings = InstanceSettings::get(); @@ -176,6 +191,7 @@ public function copyFromInstanceSettings() refreshSession(); $this->team = $team; $this->dispatch('success', 'Settings saved.'); + return; } if ($settings->resend_enabled) { @@ -187,10 +203,12 @@ public function copyFromInstanceSettings() refreshSession(); $this->team = $team; $this->dispatch('success', 'Settings saved.'); + return; } $this->dispatch('error', 'Instance SMTP/Resend settings are not enabled.'); } + public function render() { return view('livewire.notifications.email'); diff --git a/app/Livewire/Notifications/Telegram.php b/app/Livewire/Notifications/Telegram.php index 685c9e8eb..16123f123 100644 --- a/app/Livewire/Notifications/Telegram.php +++ b/app/Livewire/Notifications/Telegram.php @@ -8,8 +8,8 @@ class Telegram extends Component { - public Team $team; + protected $rules = [ 'team.telegram_enabled' => 'nullable|boolean', 'team.telegram_token' => 'required|string', @@ -25,6 +25,7 @@ class Telegram extends Component 'team.telegram_notifications_database_backups_message_thread_id' => 'nullable|string', 'team.telegram_notifications_scheduled_tasks_thread_id' => 'nullable|string', ]; + protected $validationAttributes = [ 'team.telegram_token' => 'Token', 'team.telegram_chat_id' => 'Chat ID', @@ -34,6 +35,7 @@ public function mount() { $this->team = auth()->user()->currentTeam(); } + public function instantSave() { try { @@ -64,6 +66,7 @@ public function sendTestNotification() $this->team?->notify(new Test()); $this->dispatch('success', 'Test notification sent.'); } + public function render() { return view('livewire.notifications.telegram'); diff --git a/app/Livewire/Profile/Index.php b/app/Livewire/Profile/Index.php index 631d4f956..3be1b05ce 100644 --- a/app/Livewire/Profile/Index.php +++ b/app/Livewire/Profile/Index.php @@ -9,20 +9,25 @@ class Index extends Component { public int $userId; + public string $email; public string $current_password; + public string $new_password; + public string $new_password_confirmation; #[Validate('required')] public string $name; + public function mount() { $this->userId = auth()->user()->id; $this->name = auth()->user()->name; $this->email = auth()->user()->email; } + public function submit() { try { @@ -38,6 +43,7 @@ public function submit() return handleError($e, $this); } } + public function resetPassword() { try { @@ -46,12 +52,14 @@ public function resetPassword() 'new_password' => 'required|min:8', 'new_password_confirmation' => 'required|min:8|same:new_password', ]); - if (!Hash::check($this->current_password, auth()->user()->password)) { + if (! Hash::check($this->current_password, auth()->user()->password)) { $this->dispatch('error', 'Current password is incorrect.'); + return; } if ($this->new_password !== $this->new_password_confirmation) { $this->dispatch('error', 'The two new passwords does not match.'); + return; } auth()->user()->update([ @@ -65,6 +73,7 @@ public function resetPassword() return handleError($e, $this); } } + public function render() { return view('livewire.profile.index'); diff --git a/app/Livewire/Project/AddEmpty.php b/app/Livewire/Project/AddEmpty.php index 5b358a61d..c3353be84 100644 --- a/app/Livewire/Project/AddEmpty.php +++ b/app/Livewire/Project/AddEmpty.php @@ -8,11 +8,14 @@ class AddEmpty extends Component { public string $name = ''; + public string $description = ''; + protected $rules = [ 'name' => 'required|string|min:3', 'description' => 'nullable|string', ]; + protected $validationAttributes = [ 'name' => 'Project Name', 'description' => 'Project Description', @@ -27,6 +30,7 @@ public function submit() 'description' => $this->description, 'team_id' => currentTeam()->id, ]); + return redirect()->route('project.show', $project->uuid); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Project/AddEnvironment.php b/app/Livewire/Project/AddEnvironment.php index c28cafd16..7b2767dc6 100644 --- a/app/Livewire/Project/AddEnvironment.php +++ b/app/Livewire/Project/AddEnvironment.php @@ -9,11 +9,15 @@ class AddEnvironment extends Component { public Project $project; + public string $name = ''; + public string $description = ''; + protected $rules = [ 'name' => 'required|string|min:3', ]; + protected $validationAttributes = [ 'name' => 'Environment Name', ]; diff --git a/app/Livewire/Project/Application/Advanced.php b/app/Livewire/Project/Application/Advanced.php index 45cb57ee3..3b402b3ec 100644 --- a/app/Livewire/Project/Application/Advanced.php +++ b/app/Livewire/Project/Application/Advanced.php @@ -8,9 +8,13 @@ class Advanced extends Component { public Application $application; + public bool $is_force_https_enabled; + public bool $is_gzip_enabled; + public bool $is_stripprefix_enabled; + protected $rules = [ 'application.settings.is_git_submodules_enabled' => 'boolean|required', 'application.settings.is_git_lfs_enabled' => 'boolean|required', @@ -31,18 +35,21 @@ class Advanced extends Component 'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required', 'application.settings.connect_to_docker_network' => 'boolean|required', ]; + public function mount() { $this->is_force_https_enabled = $this->application->isForceHttpsEnabled(); $this->is_gzip_enabled = $this->application->isGzipEnabled(); $this->is_stripprefix_enabled = $this->application->isStripprefixEnabled(); } + public function instantSave() { if ($this->application->isLogDrainEnabled()) { - if (!$this->application->destination->server->isLogDrainEnabled()) { + if (! $this->application->destination->server->isLogDrainEnabled()) { $this->application->settings->is_log_drain_enabled = false; $this->dispatch('error', 'Log drain is not enabled on this server.'); + return; } } @@ -67,6 +74,7 @@ public function instantSave() $this->dispatch('success', 'Settings saved.'); $this->dispatch('configurationChanged'); } + public function submit() { if ($this->application->settings->gpu_count && $this->application->settings->gpu_device_ids) { @@ -74,11 +82,13 @@ public function submit() $this->application->settings->gpu_count = null; $this->application->settings->gpu_device_ids = null; $this->application->settings->save(); + return; } $this->application->settings->save(); $this->dispatch('success', 'Settings saved.'); } + public function saveCustomName() { if (isset($this->application->settings->custom_internal_name)) { @@ -89,6 +99,7 @@ public function saveCustomName() $this->application->settings->save(); $this->dispatch('success', 'Custom name saved.'); } + public function render() { return view('livewire.project.application.advanced'); diff --git a/app/Livewire/Project/Application/Configuration.php b/app/Livewire/Project/Application/Configuration.php index 832f0fcc3..d4ec8f581 100644 --- a/app/Livewire/Project/Application/Configuration.php +++ b/app/Livewire/Project/Application/Configuration.php @@ -9,21 +9,23 @@ class Configuration extends Component { public Application $application; + public $servers; + protected $listeners = ['buildPackUpdated' => '$refresh']; public function mount() { $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); - if (!$project) { + if (! $project) { return redirect()->route('dashboard'); } $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); - if (!$environment) { + if (! $environment) { return redirect()->route('dashboard'); } $application = $environment->applications->where('uuid', request()->route('application_uuid'))->first(); - if (!$application) { + if (! $application) { return redirect()->route('dashboard'); } $this->application = $application; @@ -33,6 +35,7 @@ public function mount() return $server->id != $mainServer->id; }); } + public function render() { return view('livewire.project.application.configuration'); diff --git a/app/Livewire/Project/Application/Deployment/Index.php b/app/Livewire/Project/Application/Deployment/Index.php index d8e033b24..4f761c2cf 100644 --- a/app/Livewire/Project/Application/Deployment/Index.php +++ b/app/Livewire/Project/Application/Deployment/Index.php @@ -9,27 +9,37 @@ class Index extends Component { public Application $application; + public ?Collection $deployments; + public int $deployments_count = 0; + public string $current_url; + public int $skip = 0; + public int $default_take = 40; + public bool $show_next = false; + public bool $show_prev = false; + public ?string $pull_request_id = null; + protected $queryString = ['pull_request_id']; + public function mount() { $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); - if (!$project) { + if (! $project) { return redirect()->route('dashboard'); } $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); - if (!$environment) { + if (! $environment) { return redirect()->route('dashboard'); } $application = $environment->applications->where('uuid', request()->route('application_uuid'))->first(); - if (!$application) { + if (! $application) { return redirect()->route('dashboard'); } ['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40); @@ -40,12 +50,14 @@ public function mount() $this->show_pull_request_only(); $this->show_more(); } + private function show_pull_request_only() { if ($this->pull_request_id) { $this->deployments = $this->deployments->where('pull_request_id', $this->pull_request_id); } } + private function show_more() { if ($this->deployments->count() !== 0) { @@ -53,6 +65,7 @@ private function show_more() if ($this->deployments->count() < $this->default_take) { $this->show_next = false; } + return; } } @@ -61,6 +74,7 @@ public function reload_deployments() { $this->load_deployments(); } + public function previous_page(?int $take = null) { if ($take) { @@ -73,6 +87,7 @@ public function previous_page(?int $take = null) } $this->load_deployments(); } + public function next_page(?int $take = null) { if ($take) { @@ -81,6 +96,7 @@ public function next_page(?int $take = null) $this->show_prev = true; $this->load_deployments(); } + public function load_deployments() { ['deployments' => $deployments, 'count' => $count] = $this->application->deployments($this->skip, $this->default_take); @@ -89,6 +105,7 @@ public function load_deployments() $this->show_pull_request_only(); $this->show_more(); } + public function render() { return view('livewire.project.application.deployment.index'); diff --git a/app/Livewire/Project/Application/Deployment/Show.php b/app/Livewire/Project/Application/Deployment/Show.php index b83c3f3af..84a24255c 100644 --- a/app/Livewire/Project/Application/Deployment/Show.php +++ b/app/Livewire/Project/Application/Deployment/Show.php @@ -9,24 +9,29 @@ class Show extends Component { public Application $application; + public ApplicationDeploymentQueue $application_deployment_queue; + public string $deployment_uuid; + public $isKeepAliveOn = true; + protected $listeners = ['refreshQueue']; - public function mount() { + public function mount() + { $deploymentUuid = request()->route('deployment_uuid'); $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); - if (!$project) { + if (! $project) { return redirect()->route('dashboard'); } $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); - if (!$environment) { + if (! $environment) { return redirect()->route('dashboard'); } $application = $environment->applications->where('uuid', request()->route('application_uuid'))->first(); - if (!$application) { + if (! $application) { return redirect()->route('dashboard'); } // $activity = Activity::where('properties->type_uuid', '=', $deploymentUuid)->first(); @@ -38,7 +43,7 @@ public function mount() { // ]); // } $application_deployment_queue = ApplicationDeploymentQueue::where('deployment_uuid', $deploymentUuid)->first(); - if (!$application_deployment_queue) { + if (! $application_deployment_queue) { return redirect()->route('project.application.deployment.index', [ 'project_uuid' => $project->uuid, 'environment_name' => $environment->name, @@ -63,6 +68,7 @@ public function polling() $this->isKeepAliveOn = false; } } + public function render() { return view('livewire.project.application.deployment.show'); diff --git a/app/Livewire/Project/Application/DeploymentNavbar.php b/app/Livewire/Project/Application/DeploymentNavbar.php index 7a397f277..cbbe98d99 100644 --- a/app/Livewire/Project/Application/DeploymentNavbar.php +++ b/app/Livewire/Project/Application/DeploymentNavbar.php @@ -12,10 +12,15 @@ class DeploymentNavbar extends Component { public ApplicationDeploymentQueue $application_deployment_queue; + public Application $application; + public Server $server; + public bool $is_debug_enabled = false; + protected $listeners = ['deploymentFinished']; + public function mount() { $this->application = Application::find($this->application_deployment_queue->application_id); @@ -30,20 +35,23 @@ public function deploymentFinished() public function show_debug() { - $this->application->settings->is_debug_enabled = !$this->application->settings->is_debug_enabled; + $this->application->settings->is_debug_enabled = ! $this->application->settings->is_debug_enabled; $this->application->settings->save(); $this->is_debug_enabled = $this->application->settings->is_debug_enabled; $this->dispatch('refreshQueue'); } + public function force_start() { try { force_start_deployment($this->application_deployment_queue); } catch (\Throwable $e) { ray($e); + return handleError($e, $this); } } + public function cancel() { try { @@ -55,7 +63,7 @@ public function cancel() $new_log_entry = [ 'command' => $kill_command, - 'output' => "Deployment cancelled by user.", + 'output' => 'Deployment cancelled by user.', 'type' => 'stderr', 'order' => count($previous_logs) + 1, 'timestamp' => Carbon::now('UTC'), @@ -69,6 +77,7 @@ public function cancel() instant_remote_process([$kill_command], $server); } catch (\Throwable $e) { ray($e); + return handleError($e, $this); } finally { $this->application_deployment_queue->update([ diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 58a5ee267..60cdee48e 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -14,30 +14,44 @@ class General extends Component public string $applicationId; public Application $application; + public Collection $services; + public string $name; + public ?string $fqdn = null; + public string $git_repository; + public string $git_branch; + public ?string $git_commit_sha = null; + public string $build_pack; + public ?string $ports_exposes = null; + public bool $is_container_label_escape_enabled = true; public $customLabels; + public bool $labelsChanged = false; + public bool $initLoadingCompose = false; public ?string $initialDockerComposeLocation = null; + public ?string $initialDockerComposePrLocation = null; - public null|Collection $parsedServices; + public ?Collection $parsedServices; + public $parsedServiceDomains = []; protected $listeners = [ 'resetDefaultLabels', - 'configurationChanged' => '$refresh' + 'configurationChanged' => '$refresh', ]; + protected $rules = [ 'application.name' => 'required', 'application.description' => 'nullable', @@ -77,7 +91,9 @@ class General extends Component 'application.settings.is_build_server_enabled' => 'boolean|required', 'application.settings.is_container_label_escape_enabled' => 'boolean|required', 'application.watch_paths' => 'nullable', + 'application.redirect' => 'string|required', ]; + protected $validationAttributes = [ 'application.name' => 'name', 'application.description' => 'description', @@ -113,13 +129,16 @@ class General extends Component 'application.settings.is_build_server_enabled' => 'Is build server enabled', 'application.settings.is_container_label_escape_enabled' => 'Is container label escape enabled', 'application.watch_paths' => 'Watch paths', + 'application.redirect' => 'Redirect', ]; + public function mount() { try { $this->parsedServices = $this->application->parseCompose(); if (is_null($this->parsedServices) || empty($this->parsedServices)) { - $this->dispatch('error', "Failed to parse your docker-compose file. Please check the syntax and try again."); + $this->dispatch('error', 'Failed to parse your docker-compose file. Please check the syntax and try again.'); + return; } } catch (\Throwable $e) { @@ -133,13 +152,13 @@ public function mount() $this->ports_exposes = $this->application->ports_exposes; $this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled; $this->customLabels = $this->application->parseContainerLabels(); - if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') { - $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n"); + if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') { + $this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n"); $this->application->custom_labels = base64_encode($this->customLabels); $this->application->save(); } $this->initialDockerComposeLocation = $this->application->docker_compose_location; - if ($this->application->build_pack === 'dockercompose' && !$this->application->docker_compose_raw) { + if ($this->application->build_pack === 'dockercompose' && ! $this->application->docker_compose_raw) { $this->initLoadingCompose = true; $this->dispatch('info', 'Loading docker compose file.'); } @@ -148,6 +167,7 @@ public function mount() $this->dispatch('configurationChanged'); } } + public function instantSave() { $this->application->settings->save(); @@ -157,6 +177,7 @@ public function instantSave() $this->resetDefaultLabels(false); } } + public function loadComposeFile($isInit = false) { try { @@ -165,7 +186,8 @@ public function loadComposeFile($isInit = false) } ['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit); if (is_null($this->parsedServices)) { - $this->dispatch('error', "Failed to parse your docker-compose file. Please check the syntax and try again."); + $this->dispatch('error', 'Failed to parse your docker-compose file. Please check the syntax and try again.'); + return; } $compose = $this->application->parseCompose(); @@ -184,13 +206,13 @@ public function loadComposeFile($isInit = false) [ 'mount_path' => $target, 'resource_id' => $this->application->id, - 'resource_type' => get_class($this->application) + 'resource_type' => get_class($this->application), ], [ 'fs_path' => $source, 'mount_path' => $target, 'resource_id' => $this->application->id, - 'resource_type' => get_class($this->application) + 'resource_type' => get_class($this->application), ] ); } @@ -203,11 +225,13 @@ public function loadComposeFile($isInit = false) $this->application->docker_compose_location = $this->initialDockerComposeLocation; $this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation; $this->application->save(); + return handleError($e, $this); } finally { $this->initLoadingCompose = false; } } + public function generateDomain(string $serviceName) { $uuid = new Cuid2(7); @@ -219,14 +243,17 @@ public function generateDomain(string $serviceName) if ($this->application->build_pack === 'dockercompose') { $this->loadComposeFile(); } + return $domain; } + public function updatedApplicationBaseDirectory() { if ($this->application->build_pack === 'dockercompose') { $this->loadComposeFile(); } } + public function updatedApplicationFqdn() { $this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim(); @@ -237,6 +264,7 @@ public function updatedApplicationFqdn() $this->application->fqdn = $this->application->fqdn->unique()->implode(','); $this->resetDefaultLabels(); } + public function updatedApplicationBuildPack() { if ($this->application->build_pack !== 'nixpacks') { @@ -257,6 +285,7 @@ public function updatedApplicationBuildPack() $this->submit(); $this->dispatch('buildPackUpdated'); } + public function getWildcardDomain() { $server = data_get($this->application, 'destination.server'); @@ -268,9 +297,10 @@ public function getWildcardDomain() $this->dispatch('success', 'Wildcard domain generated.'); } } + public function resetDefaultLabels() { - $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n"); + $this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n"); $this->ports_exposes = $this->application->ports_exposes; $this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled; $this->application->custom_labels = base64_encode($this->customLabels); @@ -278,6 +308,7 @@ public function resetDefaultLabels() if ($this->application->build_pack === 'dockercompose') { $this->loadComposeFile(); } + $this->dispatch('configurationChanged'); } public function checkFqdns($showToaster = true) @@ -286,8 +317,8 @@ public function checkFqdns($showToaster = true) $domains = str($this->application->fqdn)->trim()->explode(','); if ($this->application->additional_servers->count() === 0) { foreach ($domains as $domain) { - if (!validate_dns_entry($domain, $this->application->destination->server)) { - $showToaster && $this->dispatch('error', "Validating DNS failed.", "Make sure you have added the DNS records correctly.

$domain->{$this->application->destination->server->ip}

Check this documentation for further help."); + if (! validate_dns_entry($domain, $this->application->destination->server)) { + $showToaster && $this->dispatch('error', 'Validating DNS failed.', "Make sure you have added the DNS records correctly.

$domain->{$this->application->destination->server->ip}

Check this documentation for further help."); } } } @@ -295,9 +326,28 @@ public function checkFqdns($showToaster = true) $this->application->fqdn = $domains->implode(','); } } + + public function set_redirect() + { + try { + $has_www = collect($this->application->fqdns)->filter(fn ($fqdn) => str($fqdn)->contains('www.'))->count(); + if ($has_www === 0 && $this->application->redirect === 'www') { + $this->dispatch('error', 'You want to redirect to www, but you do not have a www domain set.

Please add www to your domain list and as an A DNS record (if applicable).'); + + return; + } + $this->application->save(); + $this->resetDefaultLabels(); + $this->dispatch('success', 'Redirect updated.'); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + public function submit($showToaster = true) { try { + $this->set_redirect(); $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) { @@ -309,8 +359,8 @@ public function submit($showToaster = true) $this->application->save(); - if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') { - $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n"); + if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') { + $this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n"); $this->application->custom_labels = base64_encode($this->customLabels); $this->application->save(); } @@ -336,7 +386,7 @@ public function submit($showToaster = true) } if (data_get($this->application, 'dockerfile')) { $port = get_port_from_dockerfile($this->application->dockerfile); - if ($port && !$this->application->ports_exposes) { + if ($port && ! $this->application->ports_exposes) { $this->application->ports_exposes = $port; } } @@ -351,8 +401,8 @@ public function submit($showToaster = true) foreach ($this->parsedServiceDomains as $serviceName => $service) { $domain = data_get($service, 'domain'); if ($domain) { - if (!validate_dns_entry($domain, $this->application->destination->server)) { - $showToaster && $this->dispatch('error', "Validating DNS failed.", "Make sure you have added the DNS records correctly.

$domain->{$this->application->destination->server->ip}

Check this documentation for further help."); + if (! validate_dns_entry($domain, $this->application->destination->server)) { + $showToaster && $this->dispatch('error', 'Validating DNS failed.', "Make sure you have added the DNS records correctly.

$domain->{$this->application->destination->server->ip}

Check this documentation for further help."); } check_domain_usage(resource: $this->application); } diff --git a/app/Livewire/Project/Application/Heading.php b/app/Livewire/Project/Application/Heading.php index 473d00671..d224f4a9d 100644 --- a/app/Livewire/Project/Application/Heading.php +++ b/app/Livewire/Project/Application/Heading.php @@ -14,25 +14,31 @@ class Heading extends Component { public Application $application; + public ?string $lastDeploymentInfo = null; + public ?string $lastDeploymentLink = null; + public array $parameters; protected string $deploymentUuid; + public function getListeners() { $teamId = auth()->user()->currentTeam()->id; + return [ "echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status', - "compose_loaded" => '$refresh', - "update_links" => '$refresh', + 'compose_loaded' => '$refresh', + 'update_links' => '$refresh', ]; } + public function mount() { $this->parameters = get_route_parameters(); $lastDeployment = $this->application->get_last_successful_deployment(); - $this->lastDeploymentInfo = data_get_str($lastDeployment, 'commit')->limit(7) . ' ' . data_get($lastDeployment, 'commit_message'); + $this->lastDeploymentInfo = data_get_str($lastDeployment, 'commit')->limit(7).' '.data_get($lastDeployment, 'commit_message'); $this->lastDeploymentLink = $this->application->gitCommitLink(data_get($lastDeployment, 'commit')); } @@ -45,7 +51,9 @@ public function check_status($showNotification = false) dispatch(new ServerStatusJob($this->application->destination->server)); } - if ($showNotification) $this->dispatch('success', "Success", "Application status updated."); + if ($showNotification) { + $this->dispatch('success', 'Success', 'Application status updated.'); + } // Removed because it caused flickering // $this->dispatch('configurationChanged'); } @@ -59,18 +67,22 @@ public function deploy(bool $force_rebuild = false) { if ($this->application->build_pack === 'dockercompose' && is_null($this->application->docker_compose_raw)) { $this->dispatch('error', 'Failed to deploy', 'Please load a Compose file first.'); + return; } if ($this->application->destination->server->isSwarm() && str($this->application->docker_registry_image_name)->isEmpty()) { $this->dispatch('error', 'Failed to deploy.', 'To deploy to a Swarm cluster you must set a Docker image name first.'); + return; } if (data_get($this->application, 'settings.is_build_server_enabled') && str($this->application->docker_registry_image_name)->isEmpty()) { $this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.
More information here: documentation'); + return; } if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) { $this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.
More information here: documentation'); + return; } $this->setDeploymentUuid(); @@ -79,6 +91,7 @@ public function deploy(bool $force_rebuild = false) deployment_uuid: $this->deploymentUuid, force_rebuild: $force_rebuild, ); + return redirect()->route('project.application.deployment.show', [ 'project_uuid' => $this->parameters['project_uuid'], 'application_uuid' => $this->parameters['application_uuid'], @@ -100,16 +113,18 @@ public function stop() $this->application->save(); if ($this->application->additional_servers->count() > 0) { $this->application->additional_servers->each(function ($server) { - $server->pivot->status = "exited:unhealthy"; + $server->pivot->status = 'exited:unhealthy'; $server->pivot->save(); }); } ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id')); } + public function restart() { if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) { $this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.
More information here: documentation'); + return; } $this->setDeploymentUuid(); @@ -118,6 +133,7 @@ public function restart() deployment_uuid: $this->deploymentUuid, restart_only: true, ); + return redirect()->route('project.application.deployment.show', [ 'project_uuid' => $this->parameters['project_uuid'], 'application_uuid' => $this->parameters['application_uuid'], diff --git a/app/Livewire/Project/Application/Preview/Form.php b/app/Livewire/Project/Application/Preview/Form.php index 6826e154b..cf5ab9c82 100644 --- a/app/Livewire/Project/Application/Preview/Form.php +++ b/app/Livewire/Project/Application/Preview/Form.php @@ -10,10 +10,13 @@ class Form extends Component { public Application $application; + public string $preview_url_template; + protected $rules = [ 'application.preview_url_template' => 'required', ]; + protected $validationAttributes = [ 'application.preview_url_template' => 'preview url template', ]; diff --git a/app/Livewire/Project/Application/Previews.php b/app/Livewire/Project/Application/Previews.php index 90815a755..ca911339e 100644 --- a/app/Livewire/Project/Application/Previews.php +++ b/app/Livewire/Project/Application/Previews.php @@ -13,14 +13,19 @@ class Previews extends Component { public Application $application; + public string $deployment_uuid; + public array $parameters; + public Collection $pull_requests; + public int $rate_limit_remaining; protected $rules = [ 'application.previews.*.fqdn' => 'string|nullable', ]; + public function mount() { $this->pull_requests = collect(); @@ -35,9 +40,11 @@ public function load_prs() $this->pull_requests = $data->sortBy('number')->values(); } catch (\Throwable $e) { $this->rate_limit_remaining = 0; + return handleError($e, $this); } } + public function save_preview($preview_id) { try { @@ -47,14 +54,14 @@ public function save_preview($preview_id) $preview->fqdn = str($preview->fqdn)->replaceEnd(',', '')->trim(); $preview->fqdn = str($preview->fqdn)->replaceStart(',', '')->trim(); $preview->fqdn = str($preview->fqdn)->trim()->lower(); - if (!validate_dns_entry($preview->fqdn, $this->application->destination->server)) { - $this->dispatch('error', "Validating DNS failed.", "Make sure you have added the DNS records correctly.

$preview->fqdn->{$this->application->destination->server->ip}

Check this documentation for further help."); + if (! validate_dns_entry($preview->fqdn, $this->application->destination->server)) { + $this->dispatch('error', 'Validating DNS failed.', "Make sure you have added the DNS records correctly.

$preview->fqdn->{$this->application->destination->server->ip}

Check this documentation for further help."); $success = false; } check_domain_usage(resource: $this->application, domain: $preview->fqdn); } - if (!$preview) { + if (! $preview) { throw new \Exception('Preview not found'); } $success && $preview->save(); @@ -63,11 +70,13 @@ public function save_preview($preview_id) return handleError($e, $this); } } + public function generate_preview($preview_id) { $preview = $this->application->previews->find($preview_id); - if (!$preview) { + if (! $preview) { $this->dispatch('error', 'Preview not found.'); + return; } $fqdn = generateFqdn($this->application->destination->server, $this->application->uuid); @@ -85,13 +94,14 @@ public function generate_preview($preview_id) $preview->save(); $this->dispatch('success', 'Domain generated.'); } - public function add(int $pull_request_id, string|null $pull_request_html_url = null) + + public function add(int $pull_request_id, ?string $pull_request_html_url = null) { try { if ($this->application->build_pack === 'dockercompose') { $this->setDeploymentUuid(); $found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found && !is_null($pull_request_html_url)) { + if (! $found && ! is_null($pull_request_html_url)) { $found = ApplicationPreview::create([ 'application_id' => $this->application->id, 'pull_request_id' => $pull_request_id, @@ -104,7 +114,7 @@ public function add(int $pull_request_id, string|null $pull_request_html_url = n } else { $this->setDeploymentUuid(); $found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found && !is_null($pull_request_html_url)) { + if (! $found && ! is_null($pull_request_html_url)) { $found = ApplicationPreview::create([ 'application_id' => $this->application->id, 'pull_request_id' => $pull_request_id, @@ -120,16 +130,17 @@ public function add(int $pull_request_id, string|null $pull_request_html_url = n return handleError($e, $this); } } - public function deploy(int $pull_request_id, string|null $pull_request_html_url = null) + + public function deploy(int $pull_request_id, ?string $pull_request_html_url = null) { try { $this->setDeploymentUuid(); $found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found && !is_null($pull_request_html_url)) { + if (! $found && ! is_null($pull_request_html_url)) { ApplicationPreview::create([ 'application_id' => $this->application->id, 'pull_request_id' => $pull_request_id, - 'pull_request_html_url' => $pull_request_html_url + 'pull_request_html_url' => $pull_request_html_url, ]); } queue_application_deployment( @@ -139,6 +150,7 @@ public function deploy(int $pull_request_id, string|null $pull_request_html_url pull_request_id: $pull_request_id, git_type: $found->git_type ?? null, ); + return redirect()->route('project.application.deployment.show', [ 'project_uuid' => $this->parameters['project_uuid'], 'application_uuid' => $this->parameters['application_uuid'], diff --git a/app/Livewire/Project/Application/PreviewsCompose.php b/app/Livewire/Project/Application/PreviewsCompose.php index 85d8d81c9..bf4478e53 100644 --- a/app/Livewire/Project/Application/PreviewsCompose.php +++ b/app/Livewire/Project/Application/PreviewsCompose.php @@ -10,12 +10,16 @@ class PreviewsCompose extends Component { public $service; + public $serviceName; + public ApplicationPreview $preview; + public function render() { return view('livewire.project.application.previews-compose'); } + public function save() { $domain = data_get($this->service, 'domain'); @@ -27,6 +31,7 @@ public function save() $this->dispatch('update_links'); $this->dispatch('success', 'Domain saved.'); } + public function generate() { $domains = collect(json_decode($this->preview->application->docker_compose_domains)) ?? collect(); diff --git a/app/Livewire/Project/Application/Rollback.php b/app/Livewire/Project/Application/Rollback.php index f926b8e12..41fe598b1 100644 --- a/app/Livewire/Project/Application/Rollback.php +++ b/app/Livewire/Project/Application/Rollback.php @@ -10,14 +10,18 @@ class Rollback extends Component { public Application $application; + public $images = []; - public string|null $current; + + public ?string $current; + public array $parameters; public function mount() { $this->parameters = get_route_parameters(); } + public function rollbackImage($commit) { $deployment_uuid = new Cuid2(7); @@ -29,6 +33,7 @@ public function rollbackImage($commit) rollback: true, force_rebuild: false, ); + return redirect()->route('project.application.deployment.show', [ 'project_uuid' => $this->parameters['project_uuid'], 'application_uuid' => $this->parameters['application_uuid'], @@ -45,7 +50,7 @@ public function loadImages($showToast = false) $output = instant_remote_process([ "docker inspect --format='{{.Config.Image}}' {$this->application->uuid}", ], $this->application->destination->server, throwError: false); - $current_tag = Str::of($output)->trim()->explode(":"); + $current_tag = Str::of($output)->trim()->explode(':'); $this->current = data_get($current_tag, 1); $output = instant_remote_process([ @@ -58,6 +63,7 @@ public function loadImages($showToast = false) if ($item[1] === $this->current) { // $is_current = true; } + return [ 'tag' => $item[1], 'created_at' => $item[2], @@ -66,6 +72,7 @@ public function loadImages($showToast = false) })->toArray(); } $showToast && $this->dispatch('success', 'Images loaded.'); + return []; } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Project/Application/Source.php b/app/Livewire/Project/Application/Source.php index c9907c8c4..426626e55 100644 --- a/app/Livewire/Project/Application/Source.php +++ b/app/Livewire/Project/Application/Source.php @@ -9,13 +9,17 @@ class Source extends Component { public $applicationId; + public Application $application; + public $private_keys; + protected $rules = [ 'application.git_repository' => 'required', 'application.git_branch' => 'required', 'application.git_commit_sha' => 'nullable', ]; + protected $validationAttributes = [ 'application.git_repository' => 'repository', 'application.git_branch' => 'branch', @@ -45,7 +49,7 @@ public function setPrivateKey(int $private_key_id) public function submit() { $this->validate(); - if (!$this->application->git_commit_sha) { + if (! $this->application->git_commit_sha) { $this->application->git_commit_sha = 'HEAD'; } $this->application->save(); diff --git a/app/Livewire/Project/Application/Swarm.php b/app/Livewire/Project/Application/Swarm.php index 5f89f4934..0151b5222 100644 --- a/app/Livewire/Project/Application/Swarm.php +++ b/app/Livewire/Project/Application/Swarm.php @@ -8,6 +8,7 @@ class Swarm extends Component { public Application $application; + public string $swarm_placement_constraints = ''; protected $rules = [ @@ -15,12 +16,16 @@ class Swarm extends Component 'application.swarm_placement_constraints' => 'nullable', 'application.settings.is_swarm_only_worker_nodes' => 'required', ]; - public function mount() { + + public function mount() + { if ($this->application->swarm_placement_constraints) { $this->swarm_placement_constraints = base64_decode($this->application->swarm_placement_constraints); } } - public function instantSave() { + + public function instantSave() + { try { $this->validate(); $this->application->settings->save(); @@ -29,7 +34,9 @@ public function instantSave() { return handleError($e, $this); } } - public function submit() { + + public function submit() + { try { $this->validate(); if ($this->swarm_placement_constraints) { @@ -44,6 +51,7 @@ public function submit() { return handleError($e, $this); } } + public function render() { return view('livewire.project.application.swarm'); diff --git a/app/Livewire/Project/CloneMe.php b/app/Livewire/Project/CloneMe.php index 29cf0bea8..5373f1b3f 100644 --- a/app/Livewire/Project/CloneMe.php +++ b/app/Livewire/Project/CloneMe.php @@ -11,17 +11,27 @@ class CloneMe extends Component { public string $project_uuid; + public string $environment_name; + public int $project_id; public Project $project; + public $environments; + public $servers; + public ?Environment $environment = null; + public ?int $selectedServer = null; + public ?int $selectedDestination = null; + public ?Server $server = null; + public $resources = []; + public string $newName = ''; protected $messages = [ @@ -29,6 +39,7 @@ class CloneMe extends Component 'selectedDestination' => 'Please select a server & destination.', 'newName' => 'Please enter a name for the new project or environment.', ]; + public function mount($project_uuid) { $this->project_uuid = $project_uuid; @@ -36,7 +47,7 @@ public function mount($project_uuid) $this->environment = $this->project->environments->where('name', $this->environment_name)->first(); $this->project_id = $this->project->id; $this->servers = currentTeam()->servers; - $this->newName = str($this->project->name . '-clone-' . (string)new Cuid2(7))->slug(); + $this->newName = str($this->project->name.'-clone-'.(string) new Cuid2(7))->slug(); } public function render() @@ -50,6 +61,7 @@ public function selectServer($server_id, $destination_id) $this->selectedServer = null; $this->selectedDestination = null; $this->server = null; + return; } $this->selectedServer = $server_id; @@ -72,7 +84,7 @@ public function clone(string $type) $project = Project::create([ 'name' => $this->newName, 'team_id' => currentTeam()->id, - 'description' => $this->project->description . ' (clone)', + 'description' => $this->project->description.' (clone)', ]); if ($this->environment->name !== 'production') { $project->environments()->create([ @@ -94,7 +106,7 @@ public function clone(string $type) $databases = $this->environment->databases(); $services = $this->environment->services; foreach ($applications as $application) { - $uuid = (string)new Cuid2(7); + $uuid = (string) new Cuid2(7); $newApplication = $application->replicate()->fill([ 'uuid' => $uuid, 'fqdn' => generateFqdn($this->server, $uuid), @@ -114,14 +126,14 @@ public function clone(string $type) $persistentVolumes = $application->persistentStorages()->get(); foreach ($persistentVolumes as $volume) { $newPersistentVolume = $volume->replicate()->fill([ - 'name' => $newApplication->uuid . '-' . str($volume->name)->afterLast('-'), + 'name' => $newApplication->uuid.'-'.str($volume->name)->afterLast('-'), 'resource_id' => $newApplication->id, ]); $newPersistentVolume->save(); } } foreach ($databases as $database) { - $uuid = (string)new Cuid2(7); + $uuid = (string) new Cuid2(7); $newDatabase = $database->replicate()->fill([ 'uuid' => $uuid, 'status' => 'exited', @@ -135,21 +147,21 @@ public function clone(string $type) $payload = []; if ($database->type() === 'standalone-postgresql') { $payload['standalone_postgresql_id'] = $newDatabase->id; - } else if ($database->type() === 'standalone-redis') { + } elseif ($database->type() === 'standalone-redis') { $payload['standalone_redis_id'] = $newDatabase->id; - } else if ($database->type() === 'standalone-mongodb') { + } elseif ($database->type() === 'standalone-mongodb') { $payload['standalone_mongodb_id'] = $newDatabase->id; - } else if ($database->type() === 'standalone-mysql') { + } elseif ($database->type() === 'standalone-mysql') { $payload['standalone_mysql_id'] = $newDatabase->id; - } else if ($database->type() === 'standalone-mariadb') { + } elseif ($database->type() === 'standalone-mariadb') { $payload['standalone_mariadb_id'] = $newDatabase->id; } - $newEnvironmentVariable = $environmentVarible->replicate()->fill($payload); + $newEnvironmentVariable = $environmentVarible->replicate()->fill($payload); $newEnvironmentVariable->save(); } } foreach ($services as $service) { - $uuid = (string)new Cuid2(7); + $uuid = (string) new Cuid2(7); $newService = $service->replicate()->fill([ 'uuid' => $uuid, 'environment_id' => $environment->id, @@ -168,6 +180,7 @@ public function clone(string $type) } $newService->parse(); } + return redirect()->route('project.resource.index', [ 'project_uuid' => $project->uuid, 'environment_name' => $environment->name, diff --git a/app/Livewire/Project/Database/Backup/Execution.php b/app/Livewire/Project/Database/Backup/Execution.php index ed015dbbf..564091659 100644 --- a/app/Livewire/Project/Database/Backup/Execution.php +++ b/app/Livewire/Project/Database/Backup/Execution.php @@ -8,26 +8,30 @@ class Execution extends Component { public $database; + public ?ScheduledDatabaseBackup $backup; + public $executions; + public $s3s; + public function mount() { $backup_uuid = request()->route('backup_uuid'); $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); - if (!$project) { + if (! $project) { return redirect()->route('dashboard'); } $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); - if (!$environment) { + if (! $environment) { return redirect()->route('dashboard'); } $database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first(); - if (!$database) { + if (! $database) { return redirect()->route('dashboard'); } $backup = $database->scheduledBackups->where('uuid', $backup_uuid)->first(); - if (!$backup) { + if (! $backup) { return redirect()->route('dashboard'); } $executions = collect($backup->executions)->sortByDesc('created_at'); @@ -36,6 +40,7 @@ public function mount() $this->executions = $executions; $this->s3s = currentTeam()->s3s; } + public function render() { return view('livewire.project.database.backup.execution'); diff --git a/app/Livewire/Project/Database/Backup/Index.php b/app/Livewire/Project/Database/Backup/Index.php index 04ba642ec..d9a4b623d 100644 --- a/app/Livewire/Project/Database/Backup/Index.php +++ b/app/Livewire/Project/Database/Backup/Index.php @@ -7,25 +7,26 @@ class Index extends Component { public $database; + public function mount() { $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); - if (!$project) { + if (! $project) { return redirect()->route('dashboard'); } $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); - if (!$environment) { + if (! $environment) { return redirect()->route('dashboard'); } $database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first(); - if (!$database) { + if (! $database) { return redirect()->route('dashboard'); } // No backups if ( $database->getMorphClass() === 'App\Models\StandaloneRedis' || $database->getMorphClass() === 'App\Models\StandaloneKeydb' || - $database->getMorphClass() === 'App\Models\StandaloneDragonfly'|| + $database->getMorphClass() === 'App\Models\StandaloneDragonfly' || $database->getMorphClass() === 'App\Models\StandaloneClickhouse' ) { return redirect()->route('project.database.configuration', [ @@ -36,6 +37,7 @@ public function mount() } $this->database = $database; } + public function render() { return view('livewire.project.database.backup.index'); diff --git a/app/Livewire/Project/Database/BackupEdit.php b/app/Livewire/Project/Database/BackupEdit.php index 90eadfe43..59f2f9a39 100644 --- a/app/Livewire/Project/Database/BackupEdit.php +++ b/app/Livewire/Project/Database/BackupEdit.php @@ -9,8 +9,11 @@ class BackupEdit extends Component { public ?ScheduledDatabaseBackup $backup; + public $s3s; + public ?string $status = null; + public array $parameters; protected $rules = [ @@ -21,6 +24,7 @@ class BackupEdit extends Component 'backup.s3_storage_id' => 'nullable|integer', 'backup.databases_to_backup' => 'nullable', ]; + protected $validationAttributes = [ 'backup.enabled' => 'Enabled', 'backup.frequency' => 'Frequency', @@ -29,6 +33,7 @@ class BackupEdit extends Component 'backup.s3_storage_id' => 'S3 Storage', 'backup.databases_to_backup' => 'Databases to Backup', ]; + protected $messages = [ 'backup.s3_storage_id' => 'Select a S3 Storage', ]; @@ -50,7 +55,8 @@ public function delete() $url = Url::fromString($previousUrl); $url = $url->withoutQueryParameter('selectedBackupId'); $url = $url->withFragment('backups'); - $url = $url->getPath() . "#{$url->getFragment()}"; + $url = $url->getPath()."#{$url->getFragment()}"; + return redirect($url); } else { return redirect()->route('project.database.backup.index', $this->parameters); @@ -74,11 +80,11 @@ public function instantSave() private function custom_validate() { - if (!is_numeric($this->backup->s3_storage_id)) { + if (! is_numeric($this->backup->s3_storage_id)) { $this->backup->s3_storage_id = null; } $isValid = validate_cron_expression($this->backup->frequency); - if (!$isValid) { + if (! $isValid) { throw new \Exception('Invalid Cron / Human expression'); } $this->validate(); diff --git a/app/Livewire/Project/Database/BackupExecutions.php b/app/Livewire/Project/Database/BackupExecutions.php index 9666bbb43..de1bac36f 100644 --- a/app/Livewire/Project/Database/BackupExecutions.php +++ b/app/Livewire/Project/Database/BackupExecutions.php @@ -8,14 +8,18 @@ class BackupExecutions extends Component { public ?ScheduledDatabaseBackup $backup = null; + public $executions = []; + public $setDeletableBackup; + public function getListeners() { $userId = auth()->user()->id; + return [ "echo-private:team.{$userId},BackupCreated" => 'refreshBackupExecutions', - "deleteBackup" + 'deleteBackup', ]; } @@ -27,11 +31,13 @@ public function cleanupFailed() $this->dispatch('success', 'Failed backups cleaned up.'); } } + public function deleteBackup($exeuctionId) { $execution = $this->backup->executions()->where('id', $exeuctionId)->first(); if (is_null($execution)) { $this->dispatch('error', 'Backup execution not found.'); + return; } if ($execution->scheduledDatabaseBackup->database->getMorphClass() === 'App\Models\ServiceDatabase') { @@ -43,10 +49,12 @@ public function deleteBackup($exeuctionId) $this->dispatch('success', 'Backup deleted.'); $this->refreshBackupExecutions(); } + public function download_file($exeuctionId) { return redirect()->route('download.backup', $exeuctionId); } + public function refreshBackupExecutions(): void { if ($this->backup) { diff --git a/app/Livewire/Project/Database/BackupNow.php b/app/Livewire/Project/Database/BackupNow.php index 988f382a0..9c9c175e2 100644 --- a/app/Livewire/Project/Database/BackupNow.php +++ b/app/Livewire/Project/Database/BackupNow.php @@ -8,6 +8,7 @@ class BackupNow extends Component { public $backup; + public function backup_now() { dispatch(new DatabaseBackupJob( diff --git a/app/Livewire/Project/Database/Clickhouse/General.php b/app/Livewire/Project/Database/Clickhouse/General.php index 7fe9c1ce0..875a36141 100644 --- a/app/Livewire/Project/Database/Clickhouse/General.php +++ b/app/Livewire/Project/Database/Clickhouse/General.php @@ -4,14 +4,19 @@ use App\Actions\Database\StartDatabaseProxy; use App\Actions\Database\StopDatabaseProxy; +use App\Models\Server; use App\Models\StandaloneClickhouse; use Exception; use Livewire\Component; class General extends Component { + public Server $server; + public StandaloneClickhouse $database; + public ?string $db_url = null; + public ?string $db_url_public = null; protected $listeners = ['refresh']; @@ -27,6 +32,7 @@ class General extends Component 'database.public_port' => 'nullable|integer', 'database.is_log_drain_enabled' => 'nullable|boolean', ]; + protected $validationAttributes = [ 'database.name' => 'Name', 'database.description' => 'Description', @@ -37,18 +43,23 @@ class General extends Component '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(); } + $this->server = data_get($this->database, 'destination.server'); } - public function instantSaveAdvanced() { + + public function instantSaveAdvanced() + { try { - if (!$this->database->destination->server->isLogDrainEnabled()) { + if (! $this->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(); @@ -58,18 +69,21 @@ public function instantSaveAdvanced() { return handleError($e, $this); } } + public function instantSave() { try { - if ($this->database->is_public && !$this->database->public_port) { + 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')) { + 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); @@ -82,7 +96,8 @@ public function instantSave() } $this->database->save(); } catch (\Throwable $e) { - $this->database->is_public = !$this->database->is_public; + $this->database->is_public = ! $this->database->is_public; + return handleError($e, $this); } } @@ -92,7 +107,6 @@ public function refresh(): void $this->database->refresh(); } - public function submit() { try { diff --git a/app/Livewire/Project/Database/Configuration.php b/app/Livewire/Project/Database/Configuration.php index 4ab8aa530..e14b27cf6 100644 --- a/app/Livewire/Project/Database/Configuration.php +++ b/app/Livewire/Project/Database/Configuration.php @@ -7,18 +7,19 @@ class Configuration extends Component { public $database; + public function mount() { $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); - if (!$project) { + if (! $project) { return redirect()->route('dashboard'); } $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); - if (!$environment) { + if (! $environment) { return redirect()->route('dashboard'); } $database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first(); - if (!$database) { + if (! $database) { return redirect()->route('dashboard'); } $this->database = $database; @@ -27,6 +28,7 @@ public function mount() $this->dispatch('configurationChanged'); } } + public function render() { return view('livewire.project.database.configuration'); diff --git a/app/Livewire/Project/Database/CreateScheduledBackup.php b/app/Livewire/Project/Database/CreateScheduledBackup.php index 72ecca7b6..5ed74a6c3 100644 --- a/app/Livewire/Project/Database/CreateScheduledBackup.php +++ b/app/Livewire/Project/Database/CreateScheduledBackup.php @@ -9,20 +9,27 @@ class CreateScheduledBackup extends Component { public $database; + public $frequency; + public bool $enabled = true; + public bool $save_s3 = false; + public $s3_storage_id; + public Collection $s3s; protected $rules = [ 'frequency' => 'required|string', 'save_s3' => 'required|boolean', ]; + protected $validationAttributes = [ 'frequency' => 'Backup Frequency', 'save_s3' => 'Save to S3', ]; + public function mount() { $this->s3s = currentTeam()->s3s; @@ -36,8 +43,9 @@ public function submit(): void try { $this->validate(); $isValid = validate_cron_expression($this->frequency); - if (!$isValid) { + if (! $isValid) { $this->dispatch('error', 'Invalid Cron / Human expression.'); + return; } $payload = [ @@ -51,9 +59,9 @@ public function submit(): void ]; if ($this->database->type() === 'standalone-postgresql') { $payload['databases_to_backup'] = $this->database->postgres_db; - } else if ($this->database->type() === 'standalone-mysql') { + } elseif ($this->database->type() === 'standalone-mysql') { $payload['databases_to_backup'] = $this->database->mysql_database; - } else if ($this->database->type() === 'standalone-mariadb') { + } elseif ($this->database->type() === 'standalone-mariadb') { $payload['databases_to_backup'] = $this->database->mariadb_database; } diff --git a/app/Livewire/Project/Database/Dragonfly/General.php b/app/Livewire/Project/Database/Dragonfly/General.php index 0a4adf269..d6c4eb2ce 100644 --- a/app/Livewire/Project/Database/Dragonfly/General.php +++ b/app/Livewire/Project/Database/Dragonfly/General.php @@ -4,6 +4,7 @@ use App\Actions\Database\StartDatabaseProxy; use App\Actions\Database\StopDatabaseProxy; +use App\Models\Server; use App\Models\StandaloneDragonfly; use Exception; use Livewire\Component; @@ -12,8 +13,12 @@ class General extends Component { protected $listeners = ['refresh']; + public Server $server; + public StandaloneDragonfly $database; + public ?string $db_url = null; + public ?string $db_url_public = null; protected $rules = [ @@ -26,6 +31,7 @@ class General extends Component 'database.public_port' => 'nullable|integer', 'database.is_log_drain_enabled' => 'nullable|boolean', ]; + protected $validationAttributes = [ 'database.name' => 'Name', 'database.description' => 'Description', @@ -35,18 +41,23 @@ class General extends Component '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(); } + $this->server = data_get($this->database, 'destination.server'); } - public function instantSaveAdvanced() { + + public function instantSaveAdvanced() + { try { - if (!$this->database->destination->server->isLogDrainEnabled()) { + if (! $this->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(); @@ -56,6 +67,7 @@ public function instantSaveAdvanced() { return handleError($e, $this); } } + public function submit() { try { @@ -72,18 +84,21 @@ public function submit() } } } + public function instantSave() { try { - if ($this->database->is_public && !$this->database->public_port) { + 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')) { + 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); @@ -96,16 +111,17 @@ public function instantSave() } $this->database->save(); } catch (\Throwable $e) { - $this->database->is_public = !$this->database->is_public; + $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'); diff --git a/app/Livewire/Project/Database/Heading.php b/app/Livewire/Project/Database/Heading.php index d6a0fe087..61dafa76f 100644 --- a/app/Livewire/Project/Database/Heading.php +++ b/app/Livewire/Project/Database/Heading.php @@ -18,11 +18,13 @@ class Heading extends Component { public $database; + public array $parameters; public function getListeners() { $userId = auth()->user()->id; + return [ "echo-private:user.{$userId},DatabaseStatusChanged" => 'activityFinished', ]; @@ -48,7 +50,9 @@ public function check_status($showNotification = false) GetContainersStatus::run($this->database->destination->server); // dispatch_sync(new ContainerStatusJob($this->database->destination->server)); $this->database->refresh(); - if ($showNotification) $this->dispatch('success', 'Database status updated.'); + if ($showNotification) { + $this->dispatch('success', 'Database status updated.'); + } } public function mount() @@ -69,25 +73,25 @@ public function start() if ($this->database->type() === 'standalone-postgresql') { $activity = StartPostgresql::run($this->database); $this->dispatch('activityMonitor', $activity->id); - } else if ($this->database->type() === 'standalone-redis') { + } elseif ($this->database->type() === 'standalone-redis') { $activity = StartRedis::run($this->database); $this->dispatch('activityMonitor', $activity->id); - } else if ($this->database->type() === 'standalone-mongodb') { + } elseif ($this->database->type() === 'standalone-mongodb') { $activity = StartMongodb::run($this->database); $this->dispatch('activityMonitor', $activity->id); - } else if ($this->database->type() === 'standalone-mysql') { + } elseif ($this->database->type() === 'standalone-mysql') { $activity = StartMysql::run($this->database); $this->dispatch('activityMonitor', $activity->id); - } else if ($this->database->type() === 'standalone-mariadb') { + } elseif ($this->database->type() === 'standalone-mariadb') { $activity = StartMariadb::run($this->database); $this->dispatch('activityMonitor', $activity->id); - } else if ($this->database->type() === 'standalone-keydb') { + } elseif ($this->database->type() === 'standalone-keydb') { $activity = StartKeydb::run($this->database); $this->dispatch('activityMonitor', $activity->id); - } else if ($this->database->type() === 'standalone-dragonfly') { + } elseif ($this->database->type() === 'standalone-dragonfly') { $activity = StartDragonfly::run($this->database); $this->dispatch('activityMonitor', $activity->id); - } else if ($this->database->type() === 'standalone-clickhouse') { + } elseif ($this->database->type() === 'standalone-clickhouse') { $activity = StartClickhouse::run($this->database); $this->dispatch('activityMonitor', $activity->id); } diff --git a/app/Livewire/Project/Database/Import.php b/app/Livewire/Project/Database/Import.php index d435289fa..dfaa4461b 100644 --- a/app/Livewire/Project/Database/Import.php +++ b/app/Livewire/Project/Database/Import.php @@ -2,40 +2,57 @@ namespace App\Livewire\Project\Database; -use Livewire\Component; use App\Models\Server; use Illuminate\Support\Facades\Storage; +use Livewire\Component; class Import extends Component { public bool $unsupported = false; + public $resource; + public $parameters; + public $containers; + public bool $scpInProgress = false; + public bool $importRunning = false; public ?string $filename = null; + public ?string $filesize = null; + public bool $isUploading = false; + public int $progress = 0; + public bool $error = false; public Server $server; + public string $container; + public array $importCommands = []; + public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB'; + public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE'; + public string $mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE'; + public string $mongodbRestoreCommand = 'mongorestore --authenticationDatabase=admin --username $MONGO_INITDB_ROOT_USERNAME --password $MONGO_INITDB_ROOT_PASSWORD --uri mongodb://localhost:27017 --gzip --archive='; public function getListeners() { $userId = auth()->user()->id; + return [ "echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh', ]; } + public function mount() { $this->parameters = get_route_parameters(); @@ -45,7 +62,7 @@ public function mount() public function getContainers() { $this->containers = collect(); - if (!data_get($this->parameters, 'database_uuid')) { + if (! data_get($this->parameters, 'database_uuid')) { abort(404); } $resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id')); @@ -74,16 +91,18 @@ public function runImport() if ($this->filename == '') { $this->dispatch('error', 'Please select a file to import.'); + return; } try { $uploadedFilename = "upload/{$this->resource->uuid}/restore"; $path = Storage::path($uploadedFilename); - if (!Storage::exists($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); instant_scp($path, $tmpPath, $this->server); Storage::delete($uploadedFilename); $this->importCommands[] = "docker cp {$tmpPath} {$this->container}:{$tmpPath}"; @@ -110,7 +129,7 @@ public function runImport() $this->importCommands[] = "docker exec {$this->container} sh -c 'rm {$tmpPath}'"; $this->importCommands[] = "docker exec {$this->container} sh -c 'echo \"Import finished with exit code $?\"'"; - if (!empty($this->importCommands)) { + if (! empty($this->importCommands)) { $activity = remote_process($this->importCommands, $this->server, ignore_errors: true); $this->dispatch('activityMonitor', $activity->id); } diff --git a/app/Livewire/Project/Database/InitScript.php b/app/Livewire/Project/Database/InitScript.php index 2014fba3b..336762981 100644 --- a/app/Livewire/Project/Database/InitScript.php +++ b/app/Livewire/Project/Database/InitScript.php @@ -8,14 +8,18 @@ class InitScript extends Component { public array $script; + public int $index; - public string|null $filename; - public string|null $content; + + public ?string $filename; + + public ?string $content; protected $rules = [ 'filename' => 'required|string', 'content' => 'required|string', ]; + protected $validationAttributes = [ 'filename' => 'Filename', 'content' => 'Content', diff --git a/app/Livewire/Project/Database/Keydb/General.php b/app/Livewire/Project/Database/Keydb/General.php index 536f743f2..381711946 100644 --- a/app/Livewire/Project/Database/Keydb/General.php +++ b/app/Livewire/Project/Database/Keydb/General.php @@ -4,6 +4,7 @@ use App\Actions\Database\StartDatabaseProxy; use App\Actions\Database\StopDatabaseProxy; +use App\Models\Server; use App\Models\StandaloneKeydb; use Exception; use Livewire\Component; @@ -12,8 +13,12 @@ class General extends Component { protected $listeners = ['refresh']; + public Server $server; + public StandaloneKeydb $database; + public ?string $db_url = null; + public ?string $db_url_public = null; protected $rules = [ @@ -27,6 +32,7 @@ class General extends Component 'database.public_port' => 'nullable|integer', 'database.is_log_drain_enabled' => 'nullable|boolean', ]; + protected $validationAttributes = [ 'database.name' => 'Name', 'database.description' => 'Description', @@ -37,18 +43,24 @@ class General extends Component '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(); } + $this->server = data_get($this->database, 'destination.server'); + } - public function instantSaveAdvanced() { + + public function instantSaveAdvanced() + { try { - if (!$this->database->destination->server->isLogDrainEnabled()) { + if (! $this->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(); @@ -58,11 +70,12 @@ public function instantSaveAdvanced() { return handleError($e, $this); } } + public function submit() { try { $this->validate(); - if ($this->database->keydb_conf === "") { + if ($this->database->keydb_conf === '') { $this->database->keydb_conf = null; } $this->database->save(); @@ -77,18 +90,21 @@ public function submit() } } } + public function instantSave() { try { - if ($this->database->is_public && !$this->database->public_port) { + 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')) { + 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); @@ -101,10 +117,12 @@ public function instantSave() } $this->database->save(); } catch (\Throwable $e) { - $this->database->is_public = !$this->database->is_public; + $this->database->is_public = ! $this->database->is_public; + return handleError($e, $this); } } + public function refresh(): void { $this->database->refresh(); diff --git a/app/Livewire/Project/Database/Mariadb/General.php b/app/Livewire/Project/Database/Mariadb/General.php index c0c67898f..8b4b35d11 100644 --- a/app/Livewire/Project/Database/Mariadb/General.php +++ b/app/Livewire/Project/Database/Mariadb/General.php @@ -4,6 +4,7 @@ use App\Actions\Database\StartDatabaseProxy; use App\Actions\Database\StopDatabaseProxy; +use App\Models\Server; use App\Models\StandaloneMariadb; use Exception; use Livewire\Component; @@ -12,8 +13,12 @@ class General extends Component { protected $listeners = ['refresh']; + public Server $server; + public StandaloneMariadb $database; + public ?string $db_url = null; + public ?string $db_url_public = null; protected $rules = [ @@ -30,6 +35,7 @@ class General extends Component 'database.public_port' => 'nullable|integer', 'database.is_log_drain_enabled' => 'nullable|boolean', ]; + protected $validationAttributes = [ 'database.name' => 'Name', 'database.description' => 'Description', @@ -50,12 +56,17 @@ public function mount() if ($this->database->is_public) { $this->db_url_public = $this->database->get_db_url(); } + $this->server = data_get($this->database, 'destination.server'); + } - public function instantSaveAdvanced() { + + public function instantSaveAdvanced() + { try { - if (!$this->database->destination->server->isLogDrainEnabled()) { + if (! $this->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(); @@ -65,6 +76,7 @@ public function instantSaveAdvanced() { return handleError($e, $this); } } + public function submit() { try { @@ -84,18 +96,21 @@ public function submit() } } } + public function instantSave() { try { - if ($this->database->is_public && !$this->database->public_port) { + 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')) { + 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); @@ -108,10 +123,12 @@ public function instantSave() } $this->database->save(); } catch (\Throwable $e) { - $this->database->is_public = !$this->database->is_public; + $this->database->is_public = ! $this->database->is_public; + return handleError($e, $this); } } + public function refresh(): void { $this->database->refresh(); diff --git a/app/Livewire/Project/Database/Mongodb/General.php b/app/Livewire/Project/Database/Mongodb/General.php index 3c1271065..ee639ae41 100644 --- a/app/Livewire/Project/Database/Mongodb/General.php +++ b/app/Livewire/Project/Database/Mongodb/General.php @@ -4,6 +4,7 @@ use App\Actions\Database\StartDatabaseProxy; use App\Actions\Database\StopDatabaseProxy; +use App\Models\Server; use App\Models\StandaloneMongodb; use Exception; use Livewire\Component; @@ -12,8 +13,12 @@ class General extends Component { protected $listeners = ['refresh']; + public Server $server; + public StandaloneMongodb $database; + public ?string $db_url = null; + public ?string $db_url_public = null; protected $rules = [ @@ -29,6 +34,7 @@ class General extends Component 'database.public_port' => 'nullable|integer', 'database.is_log_drain_enabled' => 'nullable|boolean', ]; + protected $validationAttributes = [ 'database.name' => 'Name', 'database.description' => 'Description', @@ -48,13 +54,17 @@ public function mount() if ($this->database->is_public) { $this->db_url_public = $this->database->get_db_url(); } + $this->server = data_get($this->database, 'destination.server'); + } + public function instantSaveAdvanced() { try { - if (!$this->database->destination->server->isLogDrainEnabled()) { + if (! $this->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(); @@ -64,6 +74,7 @@ public function instantSaveAdvanced() return handleError($e, $this); } } + public function submit() { try { @@ -86,18 +97,21 @@ public function submit() } } } + public function instantSave() { try { - if ($this->database->is_public && !$this->database->public_port) { + 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')) { + 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); @@ -110,10 +124,12 @@ public function instantSave() } $this->database->save(); } catch (\Throwable $e) { - $this->database->is_public = !$this->database->is_public; + $this->database->is_public = ! $this->database->is_public; + return handleError($e, $this); } } + public function refresh(): void { $this->database->refresh(); diff --git a/app/Livewire/Project/Database/Mysql/General.php b/app/Livewire/Project/Database/Mysql/General.php index a1fb9201a..fc0767109 100644 --- a/app/Livewire/Project/Database/Mysql/General.php +++ b/app/Livewire/Project/Database/Mysql/General.php @@ -4,6 +4,7 @@ use App\Actions\Database\StartDatabaseProxy; use App\Actions\Database\StopDatabaseProxy; +use App\Models\Server; use App\Models\StandaloneMysql; use Exception; use Livewire\Component; @@ -13,7 +14,11 @@ class General extends Component protected $listeners = ['refresh']; public StandaloneMysql $database; + + public Server $server; + public ?string $db_url = null; + public ?string $db_url_public = null; protected $rules = [ @@ -30,6 +35,7 @@ class General extends Component 'database.public_port' => 'nullable|integer', 'database.is_log_drain_enabled' => 'nullable|boolean', ]; + protected $validationAttributes = [ 'database.name' => 'Name', 'database.description' => 'Description', @@ -50,13 +56,16 @@ public function mount() if ($this->database->is_public) { $this->db_url_public = $this->database->get_db_url(); } + $this->server = data_get($this->database, 'destination.server'); } + public function instantSaveAdvanced() { try { - if (!$this->database->destination->server->isLogDrainEnabled()) { + if (! $this->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(); @@ -66,6 +75,7 @@ public function instantSaveAdvanced() return handleError($e, $this); } } + public function submit() { try { @@ -85,18 +95,21 @@ public function submit() } } } + public function instantSave() { try { - if ($this->database->is_public && !$this->database->public_port) { + 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')) { + 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); @@ -109,10 +122,12 @@ public function instantSave() } $this->database->save(); } catch (\Throwable $e) { - $this->database->is_public = !$this->database->is_public; + $this->database->is_public = ! $this->database->is_public; + return handleError($e, $this); } } + public function refresh(): void { $this->database->refresh(); diff --git a/app/Livewire/Project/Database/Postgresql/General.php b/app/Livewire/Project/Database/Postgresql/General.php index 79d91e7aa..38cac2e5c 100644 --- a/app/Livewire/Project/Database/Postgresql/General.php +++ b/app/Livewire/Project/Database/Postgresql/General.php @@ -4,6 +4,7 @@ use App\Actions\Database\StartDatabaseProxy; use App\Actions\Database\StopDatabaseProxy; +use App\Models\Server; use App\Models\StandalonePostgresql; use Exception; use Livewire\Component; @@ -13,9 +14,15 @@ class General extends Component { public StandalonePostgresql $database; + + public Server $server; + public string $new_filename; + public string $new_content; + public ?string $db_url = null; + public ?string $db_url_public = null; protected $listeners = ['refresh', 'save_init_script', 'delete_init_script']; @@ -36,6 +43,7 @@ class General extends Component 'database.public_port' => 'nullable|integer', 'database.is_log_drain_enabled' => 'nullable|boolean', ]; + protected $validationAttributes = [ 'database.name' => 'Name', 'database.description' => 'Description', @@ -51,19 +59,23 @@ class General extends Component '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(); } + $this->server = data_get($this->database, 'destination.server'); } + public function instantSaveAdvanced() { try { - if (!$this->database->destination->server->isLogDrainEnabled()) { + if (! $this->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(); @@ -73,18 +85,21 @@ public function instantSaveAdvanced() return handleError($e, $this); } } + public function instantSave() { try { - if ($this->database->is_public && !$this->database->public_port) { + 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')) { + 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); @@ -97,10 +112,12 @@ public function instantSave() } $this->database->save(); } catch (\Throwable $e) { - $this->database->is_public = !$this->database->is_public; + $this->database->is_public = ! $this->database->is_public; + return handleError($e, $this); } } + public function save_init_script($script) { $this->database->init_scripts = filter($this->database->init_scripts, fn ($s) => $s['filename'] !== $script['filename']); @@ -118,6 +135,7 @@ public function delete_init_script($script) $this->database->save(); $this->refresh(); $this->dispatch('success', 'Init script deleted.'); + return; } } @@ -136,9 +154,10 @@ public function save_new_init_script() $found = collect($this->database->init_scripts)->firstWhere('filename', $this->new_filename); if ($found) { $this->dispatch('error', 'Filename already exists.'); + return; } - if (!isset($this->database->init_scripts)) { + if (! isset($this->database->init_scripts)) { $this->database->init_scripts = []; } $this->database->init_scripts = array_merge($this->database->init_scripts, [ @@ -146,7 +165,7 @@ public function save_new_init_script() 'index' => count($this->database->init_scripts), 'filename' => $this->new_filename, 'content' => $this->new_content, - ] + ], ]); $this->database->save(); $this->dispatch('success', 'Init script added.'); diff --git a/app/Livewire/Project/Database/Redis/General.php b/app/Livewire/Project/Database/Redis/General.php index a894626b0..b5c1dd881 100644 --- a/app/Livewire/Project/Database/Redis/General.php +++ b/app/Livewire/Project/Database/Redis/General.php @@ -4,6 +4,7 @@ use App\Actions\Database\StartDatabaseProxy; use App\Actions\Database\StopDatabaseProxy; +use App\Models\Server; use App\Models\StandaloneRedis; use Exception; use Livewire\Component; @@ -12,8 +13,12 @@ class General extends Component { protected $listeners = ['refresh']; + public Server $server; + public StandaloneRedis $database; + public ?string $db_url = null; + public ?string $db_url_public = null; protected $rules = [ @@ -27,6 +32,7 @@ class General extends Component 'database.public_port' => 'nullable|integer', 'database.is_log_drain_enabled' => 'nullable|boolean', ]; + protected $validationAttributes = [ 'database.name' => 'Name', 'database.description' => 'Description', @@ -37,18 +43,24 @@ class General extends Component '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(); } + $this->server = data_get($this->database, 'destination.server'); + } - public function instantSaveAdvanced() { + + public function instantSaveAdvanced() + { try { - if (!$this->database->destination->server->isLogDrainEnabled()) { + if (! $this->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(); @@ -58,11 +70,12 @@ public function instantSaveAdvanced() { return handleError($e, $this); } } + public function submit() { try { $this->validate(); - if ($this->database->redis_conf === "") { + if ($this->database->redis_conf === '') { $this->database->redis_conf = null; } $this->database->save(); @@ -71,18 +84,21 @@ public function submit() return handleError($e, $this); } } + public function instantSave() { try { - if ($this->database->is_public && !$this->database->public_port) { + 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')) { + 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); @@ -95,10 +111,12 @@ public function instantSave() } $this->database->save(); } catch (\Throwable $e) { - $this->database->is_public = !$this->database->is_public; + $this->database->is_public = ! $this->database->is_public; + return handleError($e, $this); } } + public function refresh(): void { $this->database->refresh(); diff --git a/app/Livewire/Project/Database/ScheduledBackups.php b/app/Livewire/Project/Database/ScheduledBackups.php index 61c2a3bb1..beb5a9c39 100644 --- a/app/Livewire/Project/Database/ScheduledBackups.php +++ b/app/Livewire/Project/Database/ScheduledBackups.php @@ -8,12 +8,19 @@ class ScheduledBackups extends Component { public $database; + public $parameters; + public $type; + public ?ScheduledDatabaseBackup $selectedBackup; + public $selectedBackupId; + public $s3s; + protected $listeners = ['refreshScheduledBackups']; + protected $queryString = ['selectedBackupId']; public function mount(): void @@ -29,13 +36,16 @@ public function mount(): void } $this->s3s = currentTeam()->s3s; } - public function setSelectedBackup($backupId) { + + public function setSelectedBackup($backupId) + { $this->selectedBackupId = $backupId; $this->selectedBackup = $this->database->scheduledBackups->find($this->selectedBackupId); if (is_null($this->selectedBackup)) { $this->selectedBackupId = null; } } + public function delete($scheduled_backup_id): void { $this->database->scheduledBackups->find($scheduled_backup_id)->delete(); diff --git a/app/Livewire/Project/DeleteEnvironment.php b/app/Livewire/Project/DeleteEnvironment.php index c64ebd4b2..22478916f 100644 --- a/app/Livewire/Project/DeleteEnvironment.php +++ b/app/Livewire/Project/DeleteEnvironment.php @@ -8,7 +8,9 @@ class DeleteEnvironment extends Component { public array $parameters; + public int $environment_id; + public bool $disabled = false; public function mount() @@ -24,8 +26,10 @@ public function delete() $environment = Environment::findOrFail($this->environment_id); if ($environment->isEmpty()) { $environment->delete(); + return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]); } + return $this->dispatch('error', 'Environment has defined resources, please delete them first.'); } } diff --git a/app/Livewire/Project/DeleteProject.php b/app/Livewire/Project/DeleteProject.php index 543b45784..499b86e3e 100644 --- a/app/Livewire/Project/DeleteProject.php +++ b/app/Livewire/Project/DeleteProject.php @@ -8,7 +8,9 @@ class DeleteProject extends Component { public array $parameters; + public int $project_id; + public bool $disabled = false; public function mount() @@ -26,6 +28,7 @@ public function delete() return $this->dispatch('error', 'Project has resources defined, please delete them first.'); } $project->delete(); + return redirect()->route('project.index'); } } diff --git a/app/Livewire/Project/Edit.php b/app/Livewire/Project/Edit.php index 8a35eff7f..bebec4752 100644 --- a/app/Livewire/Project/Edit.php +++ b/app/Livewire/Project/Edit.php @@ -8,16 +8,18 @@ class Edit extends Component { public Project $project; + protected $rules = [ 'project.name' => 'required|min:3|max:255', 'project.description' => 'nullable|string|max:255', ]; + public function mount() { $projectUuid = request()->route('project_uuid'); $teamId = currentTeam()->id; $project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first(); - if (!$project) { + if (! $project) { return redirect()->route('dashboard'); } $this->project = $project; diff --git a/app/Livewire/Project/EnvironmentEdit.php b/app/Livewire/Project/EnvironmentEdit.php index cd952a961..16fc7bc36 100644 --- a/app/Livewire/Project/EnvironmentEdit.php +++ b/app/Livewire/Project/EnvironmentEdit.php @@ -9,13 +9,18 @@ class EnvironmentEdit extends Component { public Project $project; + public Application $application; + public $environment; + public array $parameters; + protected $rules = [ 'environment.name' => 'required|min:3|max:255', 'environment.description' => 'nullable|min:3|max:255', ]; + public function mount() { $this->parameters = get_route_parameters(); @@ -28,11 +33,13 @@ public function submit() $this->validate(); try { $this->environment->save(); + return redirect()->route('project.environment.edit', ['project_uuid' => $this->project->uuid, 'environment_name' => $this->environment->name]); } catch (\Throwable $e) { return handleError($e, $this); } } + public function render() { return view('livewire.project.environment-edit'); diff --git a/app/Livewire/Project/Index.php b/app/Livewire/Project/Index.php index 0537ad192..0e4f15a5c 100644 --- a/app/Livewire/Project/Index.php +++ b/app/Livewire/Project/Index.php @@ -10,13 +10,18 @@ class Index extends Component { public $projects; + public $servers; + public $private_keys; - public function mount() { + + public function mount() + { $this->private_keys = PrivateKey::ownedByCurrentTeam()->get(); $this->projects = Project::ownedByCurrentTeam()->get(); $this->servers = Server::ownedByCurrentTeam()->count(); } + public function render() { return view('livewire.project.index'); diff --git a/app/Livewire/Project/New/DockerCompose.php b/app/Livewire/Project/New/DockerCompose.php index 79394d310..633ce5bda 100644 --- a/app/Livewire/Project/New/DockerCompose.php +++ b/app/Livewire/Project/New/DockerCompose.php @@ -5,16 +5,20 @@ use App\Models\EnvironmentVariable; use App\Models\Project; use App\Models\Service; -use Livewire\Component; use Illuminate\Support\Str; +use Livewire\Component; use Symfony\Component\Yaml\Yaml; class DockerCompose extends Component { public string $dockerComposeRaw = ''; + public string $envFile = ''; + public array $parameters; + public array $query; + public function mount() { $this->parameters = get_route_parameters(); @@ -37,12 +41,13 @@ public function mount() '; } } + public function submit() { $server_id = $this->query['server_id']; try { $this->validate([ - 'dockerComposeRaw' => 'required' + 'dockerComposeRaw' => 'required', ]); $this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK); @@ -54,7 +59,7 @@ public function submit() $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); $service = Service::create([ - 'name' => 'service' . Str::random(10), + 'name' => 'service'.Str::random(10), 'docker_compose_raw' => $this->dockerComposeRaw, 'environment_id' => $environment->id, 'server_id' => (int) $server_id, diff --git a/app/Livewire/Project/New/DockerImage.php b/app/Livewire/Project/New/DockerImage.php index cf3164e33..65a98b37f 100644 --- a/app/Livewire/Project/New/DockerImage.php +++ b/app/Livewire/Project/New/DockerImage.php @@ -6,24 +6,28 @@ use App\Models\Project; use App\Models\StandaloneDocker; use App\Models\SwarmDocker; +use Illuminate\Support\Str; use Livewire\Component; use Visus\Cuid2\Cuid2; -use Illuminate\Support\Str; class DockerImage extends Component { public string $dockerImage = ''; + public array $parameters; + public array $query; + public function mount() { $this->parameters = get_route_parameters(); $this->query = request()->query(); } + public function submit() { $this->validate([ - 'dockerImage' => 'required' + 'dockerImage' => 'required', ]); $image = Str::of($this->dockerImage)->before(':'); if (Str::of($this->dockerImage)->contains(':')) { @@ -33,21 +37,21 @@ public function submit() } $destination_uuid = $this->query['destination']; $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + if (! $destination) { $destination = SwarmDocker::where('uuid', $destination_uuid)->first(); } - if (!$destination) { + if (! $destination) { throw new \Exception('Destination not found. What?!'); } $destination_class = $destination->getMorphClass(); $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); - ray($image,$tag); + ray($image, $tag); $application = Application::create([ - 'name' => 'docker-image-' . new Cuid2(7), + 'name' => 'docker-image-'.new Cuid2(7), 'repository_project_id' => 0, - 'git_repository' => "coollabsio/coolify", + 'git_repository' => 'coollabsio/coolify', 'git_branch' => 'main', 'build_pack' => 'dockerimage', 'ports_exposes' => 80, @@ -61,15 +65,17 @@ public function submit() $fqdn = generateFqdn($destination->server, $application->uuid); $application->update([ - 'name' => 'docker-image-' . $application->uuid, - 'fqdn' => $fqdn + 'name' => 'docker-image-'.$application->uuid, + 'fqdn' => $fqdn, ]); + return redirect()->route('project.application.configuration', [ 'application_uuid' => $application->uuid, 'environment_name' => $environment->name, 'project_uuid' => $project->uuid, ]); } + public function render() { return view('livewire.project.new.docker-image'); diff --git a/app/Livewire/Project/New/EmptyProject.php b/app/Livewire/Project/New/EmptyProject.php index 52e9ce7dc..28249b442 100644 --- a/app/Livewire/Project/New/EmptyProject.php +++ b/app/Livewire/Project/New/EmptyProject.php @@ -13,6 +13,7 @@ public function createEmptyProject() 'name' => generate_random_name(), 'team_id' => currentTeam()->id, ]); + return redirect()->route('project.show', ['project_uuid' => $project->uuid, 'environment_name' => 'production']); } } diff --git a/app/Livewire/Project/New/GithubPrivateRepository.php b/app/Livewire/Project/New/GithubPrivateRepository.php index 58e3fe586..76b337c01 100644 --- a/app/Livewire/Project/New/GithubPrivateRepository.php +++ b/app/Livewire/Project/New/GithubPrivateRepository.php @@ -14,32 +14,50 @@ class GithubPrivateRepository extends Component { public $current_step = 'github_apps'; + public $github_apps; + public GithubApp $github_app; + public $parameters; + public $currentRoute; + public $query; + public $type; public int $selected_repository_id; + public int $selected_github_app_id; + public string $selected_repository_owner; + public string $selected_repository_repo; public string $selected_branch_name = 'main'; public string $token; - public $repositories; - public int $total_repositories_count = 0; - public $branches; - public int $total_branches_count = 0; - public int $port = 3000; - public bool $is_static = false; - public string|null $publish_directory = null; - protected int $page = 1; - public $build_pack = 'nixpacks'; - public bool $show_is_static = true; + public $repositories; + + public int $total_repositories_count = 0; + + public $branches; + + public int $total_branches_count = 0; + + public int $port = 3000; + + public bool $is_static = false; + + public ?string $publish_directory = null; + + protected int $page = 1; + + public $build_pack = 'nixpacks'; + + public bool $show_is_static = true; public function mount() { @@ -49,12 +67,13 @@ public function mount() $this->repositories = $this->branches = collect(); $this->github_apps = GithubApp::private(); } + public function updatedBuildPack() { if ($this->build_pack === 'nixpacks') { $this->show_is_static = true; $this->port = 3000; - } else if ($this->build_pack === 'static') { + } elseif ($this->build_pack === 'static') { $this->show_is_static = false; $this->is_static = false; $this->port = 80; @@ -63,6 +82,7 @@ public function updatedBuildPack() $this->is_static = false; } } + public function loadRepositories($github_app_id) { $this->repositories = collect(); @@ -117,7 +137,7 @@ public function loadBranches() protected function loadBranchByPage() { - ray('Loading page ' . $this->page); + ray('Loading page '.$this->page); $response = Http::withToken($this->token)->get("{$this->github_app->api_url}/repos/{$this->selected_repository_owner}/{$this->selected_repository_repo}/branches?per_page=100&page={$this->page}"); $json = $response->json(); if ($response->status() !== 200) { @@ -133,20 +153,19 @@ public function submit() try { $destination_uuid = $this->query['destination']; $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + if (! $destination) { $destination = SwarmDocker::where('uuid', $destination_uuid)->first(); } - if (!$destination) { + if (! $destination) { throw new \Exception('Destination not found. What?!'); } $destination_class = $destination->getMorphClass(); - $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); $application = Application::create([ - 'name' => generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name), + 'name' => generate_application_name($this->selected_repository_owner.'/'.$this->selected_repository_repo, $this->selected_branch_name), 'repository_project_id' => $this->selected_repository_id, 'git_repository' => "{$this->selected_repository_owner}/{$this->selected_repository_repo}", 'git_branch' => $this->selected_branch_name, @@ -157,7 +176,7 @@ public function submit() 'destination_id' => $destination->id, 'destination_type' => $destination_class, 'source_id' => $this->github_app->id, - 'source_type' => $this->github_app->getMorphClass() + 'source_type' => $this->github_app->getMorphClass(), ]); $application->settings->is_static = $this->is_static; $application->settings->save(); @@ -168,7 +187,7 @@ public function submit() $fqdn = generateFqdn($destination->server, $application->uuid); $application->fqdn = $fqdn; - $application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid); + $application->name = generate_application_name($this->selected_repository_owner.'/'.$this->selected_repository_repo, $this->selected_branch_name, $application->uuid); $application->save(); return redirect()->route('project.application.configuration', [ diff --git a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php index 691b246fd..690149cc4 100644 --- a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php +++ b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php @@ -9,34 +9,44 @@ use App\Models\Project; use App\Models\StandaloneDocker; use App\Models\SwarmDocker; -use Illuminate\Support\Collection; +use Illuminate\Support\Str; use Livewire\Component; use Spatie\Url\Url; -use Illuminate\Support\Str; class GithubPrivateRepositoryDeployKey extends Component { public $current_step = 'private_keys'; + public $parameters; + public $query; + public $private_keys = []; + public int $private_key_id; public int $port = 3000; + public string $type; public bool $is_static = false; - public null|string $publish_directory = null; + + public ?string $publish_directory = null; public string $repository_url; + public string $branch; public $build_pack = 'nixpacks'; + public bool $show_is_static = true; private object $repository_url_parsed; + private GithubApp|GitlabApp|string $git_source = 'other'; + private ?string $git_host = null; + private string $git_repository; protected $rules = [ @@ -47,6 +57,7 @@ class GithubPrivateRepositoryDeployKey extends Component 'publish_directory' => 'nullable|string', 'build_pack' => 'required|string', ]; + protected $validationAttributes = [ 'repository_url' => 'Repository', 'branch' => 'Branch', @@ -56,7 +67,6 @@ class GithubPrivateRepositoryDeployKey extends Component 'build_pack' => 'Build pack', ]; - public function mount() { if (isDev()) { @@ -76,7 +86,7 @@ public function updatedBuildPack() if ($this->build_pack === 'nixpacks') { $this->show_is_static = true; $this->port = 3000; - } else if ($this->build_pack === 'static') { + } elseif ($this->build_pack === 'static') { $this->show_is_static = false; $this->is_static = false; $this->port = 80; @@ -85,6 +95,7 @@ public function updatedBuildPack() $this->is_static = false; } } + public function instantSave() { if ($this->is_static) { @@ -108,10 +119,10 @@ public function submit() try { $destination_uuid = $this->query['destination']; $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + if (! $destination) { $destination = SwarmDocker::where('uuid', $destination_uuid)->first(); } - if (!$destination) { + if (! $destination) { throw new \Exception('Destination not found. What?!'); } $destination_class = $destination->getMorphClass(); @@ -146,7 +157,7 @@ public function submit() 'destination_type' => $destination_class, 'private_key_id' => $this->private_key_id, 'source_id' => $this->git_source->id, - 'source_type' => $this->git_source->getMorphClass() + 'source_type' => $this->git_source->getMorphClass(), ]; } if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') { @@ -175,15 +186,16 @@ private function get_git_source() { $this->repository_url_parsed = Url::fromString($this->repository_url); $this->git_host = $this->repository_url_parsed->getHost(); - $this->git_repository = $this->repository_url_parsed->getSegment(1) . '/' . $this->repository_url_parsed->getSegment(2); + $this->git_repository = $this->repository_url_parsed->getSegment(1).'/'.$this->repository_url_parsed->getSegment(2); if ($this->git_host == 'github.com') { $this->git_source = GithubApp::where('name', 'Public GitHub')->first(); + return; } if (Str::of($this->repository_url)->startsWith('http')) { $this->git_host = $this->repository_url_parsed->getHost(); - $this->git_repository = $this->repository_url_parsed->getSegment(1) . '/' . $this->repository_url_parsed->getSegment(2); + $this->git_repository = $this->repository_url_parsed->getSegment(1).'/'.$this->repository_url_parsed->getSegment(2); $this->git_repository = Str::finish("git@$this->git_host:$this->git_repository", '.git'); } else { $this->git_repository = $this->repository_url; diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index 802d6ccb2..739061f1f 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -16,22 +16,39 @@ class PublicGitRepository extends Component { public string $repository_url; + public int $port = 3000; + public string $type; + public $parameters; + public $query; + public bool $branch_found = false; + public string $selected_branch = 'main'; + public bool $is_static = false; - public string|null $publish_directory = null; + + public ?string $publish_directory = null; + public string $git_branch = 'main'; + public int $rate_limit_remaining = 0; + public $rate_limit_reset = 0; + private object $repository_url_parsed; + public GithubApp|GitlabApp|string $git_source = 'other'; + public string $git_host; + public string $git_repository; + public $build_pack = 'nixpacks'; + public bool $show_is_static = true; public bool $new_compose_services = false; @@ -43,6 +60,7 @@ class PublicGitRepository extends Component 'publish_directory' => 'nullable|string', 'build_pack' => 'required|string', ]; + protected $validationAttributes = [ 'repository_url' => 'repository', 'port' => 'port', @@ -60,12 +78,13 @@ public function mount() $this->parameters = get_route_parameters(); $this->query = request()->query(); } + public function updatedBuildPack() { if ($this->build_pack === 'nixpacks') { $this->show_is_static = true; $this->port = 3000; - } else if ($this->build_pack === 'static') { + } elseif ($this->build_pack === 'static') { $this->show_is_static = false; $this->is_static = false; $this->port = 80; @@ -74,6 +93,7 @@ public function updatedBuildPack() $this->is_static = false; } } + public function instantSave() { if ($this->is_static) { @@ -85,26 +105,28 @@ public function instantSave() } $this->dispatch('success', 'Application settings updated!'); } + public function load_any_git() { $this->branch_found = true; } + public function load_branch() { try { if (str($this->repository_url)->startsWith('git@')) { $github_instance = str($this->repository_url)->after('git@')->before(':'); $repository = str($this->repository_url)->after(':')->before('.git'); - $this->repository_url = 'https://' . str($github_instance) . '/' . $repository; + $this->repository_url = 'https://'.str($github_instance).'/'.$repository; } if ( (str($this->repository_url)->startsWith('https://') || str($this->repository_url)->startsWith('http://')) && - !str($this->repository_url)->endsWith('.git') && - (!str($this->repository_url)->contains('github.com') || - !str($this->repository_url)->contains('git.sr.ht')) + ! str($this->repository_url)->endsWith('.git') && + (! str($this->repository_url)->contains('github.com') || + ! str($this->repository_url)->contains('git.sr.ht')) ) { - $this->repository_url = $this->repository_url . '.git'; + $this->repository_url = $this->repository_url.'.git'; } if (str($this->repository_url)->contains('github.com')) { $this->repository_url = str($this->repository_url)->before('.git')->value(); @@ -119,7 +141,7 @@ public function load_branch() $this->selected_branch = $this->git_branch; } catch (\Throwable $e) { ray($e->getMessage()); - if (!$this->branch_found && $this->git_branch == 'main') { + if (! $this->branch_found && $this->git_branch == 'main') { try { $this->git_branch = 'master'; $this->get_branch(); @@ -136,11 +158,12 @@ private function get_git_source() { $this->repository_url_parsed = Url::fromString($this->repository_url); $this->git_host = $this->repository_url_parsed->getHost(); - $this->git_repository = $this->repository_url_parsed->getSegment(1) . '/' . $this->repository_url_parsed->getSegment(2); + $this->git_repository = $this->repository_url_parsed->getSegment(1).'/'.$this->repository_url_parsed->getSegment(2); $this->git_branch = $this->repository_url_parsed->getSegment(4) ?? 'main'; if ($this->git_host == 'github.com') { $this->git_source = GithubApp::where('name', 'Public GitHub')->first(); + return; } $this->git_repository = $this->repository_url; @@ -151,11 +174,12 @@ private function get_branch() { if ($this->git_source === 'other') { $this->branch_found = true; + return; } if ($this->git_source->getMorphClass() === 'App\Models\GithubApp') { ['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = githubApi(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}"); - $this->rate_limit_reset = Carbon::parse((int)$this->rate_limit_reset)->format('Y-M-d H:i:s'); + $this->rate_limit_reset = Carbon::parse((int) $this->rate_limit_reset)->format('Y-M-d H:i:s'); $this->branch_found = true; } } @@ -169,10 +193,10 @@ public function submit() $environment_name = $this->parameters['environment_name']; $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + if (! $destination) { $destination = SwarmDocker::where('uuid', $destination_uuid)->first(); } - if (!$destination) { + if (! $destination) { throw new \Exception('Destination not found. What?!'); } $destination_class = $destination->getMorphClass(); @@ -180,10 +204,10 @@ public function submit() $project = Project::where('uuid', $project_uuid)->first(); $environment = $project->load(['environments'])->environments->where('name', $environment_name)->first(); - if ($this->build_pack === 'dockercompose' && isDev() && $this->new_compose_services ) { + if ($this->build_pack === 'dockercompose' && isDev() && $this->new_compose_services) { $server = $destination->server; - $new_service = [ - 'name' => 'service' . str()->random(10), + $new_service = [ + 'name' => 'service'.str()->random(10), 'docker_compose_raw' => 'coolify', 'environment_id' => $environment->id, 'server_id' => $server->id, @@ -198,11 +222,13 @@ public function submit() $new_service['source_type'] = $this->git_source->getMorphClass(); } $service = Service::create($new_service); + return redirect()->route('project.service.configuration', [ 'service_uuid' => $service->uuid, 'environment_name' => $environment->name, 'project_uuid' => $project->uuid, ]); + return; } if ($this->git_source === 'other') { diff --git a/app/Livewire/Project/New/Select.php b/app/Livewire/Project/New/Select.php index 2b5ef9eca..b8d186dab 100644 --- a/app/Livewire/Project/New/Select.php +++ b/app/Livewire/Project/New/Select.php @@ -4,37 +4,54 @@ use App\Models\Project; use App\Models\Server; -use Countable; use Illuminate\Support\Collection; use Livewire\Component; class Select extends Component { public $current_step = 'type'; + public ?Server $server = null; + public string $type; + public string $server_id; + public string $destination_uuid; + public Collection|null|Server $allServers; + public Collection|null|Server $servers; + public ?Collection $standaloneDockers; + public ?Collection $swarmDockers; + public array $parameters; + public Collection|array $services = []; + public Collection|array $allServices = []; + public bool $isDatabase = false; + public bool $includeSwarm = true; public bool $loadingServices = true; + public bool $loading = false; + public $environments = []; + public ?string $selectedEnvironment = null; + public ?string $existingPostgresqlUrl = null; public ?string $search = null; + protected $queryString = [ 'server_id', - 'search' + 'search', ]; public function mount() @@ -47,6 +64,7 @@ public function mount() $this->environments = Project::whereUuid($projectUuid)->first()->environments; $this->selectedEnvironment = data_get($this->parameters, 'environment_name'); } + public function render() { return view('livewire.project.new.select'); @@ -74,17 +92,20 @@ public function updatedSearch() { $this->loadServices(); } + public function loadServices(bool $force = false) { try { $this->loadingServices = true; - if (count($this->allServices) > 0 && !$force) { - if (!$this->search) { + if (count($this->allServices) > 0 && ! $force) { + if (! $this->search) { $this->services = $this->allServices; + return; } $this->services = $this->allServices->filter(function ($service, $key) { $tags = collect(data_get($service, 'tags', [])); + return str_contains(strtolower($key), strtolower($this->search)) || $tags->contains(function ($tag) { return str_contains(strtolower($tag), strtolower($this->search)); }); @@ -102,6 +123,7 @@ public function loadServices(bool $force = false) $this->loadingServices = false; } } + public function instantSave() { if ($this->includeSwarm) { @@ -114,9 +136,12 @@ public function instantSave() } } } + public function setType(string $type) { - if ($this->loading) return; + if ($this->loading) { + return; + } $this->loading = true; $this->type = $type; switch ($type) { @@ -146,15 +171,16 @@ public function setType(string $type) $this->servers = $this->allServers; } } - if ($type === "existing-postgresql") { + if ($type === 'existing-postgresql') { $this->current_step = $type; + return; } // if (count($this->servers) === 1) { // $server = $this->servers->first(); // $this->setServer($server); // } - if (!is_null($this->server)) { + if (! is_null($this->server)) { $foundServer = $this->servers->where('id', $this->server->id)->first(); if ($foundServer) { return $this->setServer($foundServer); @@ -175,6 +201,7 @@ public function setServer(Server $server) public function setDestination(string $destination_uuid) { $this->destination_uuid = $destination_uuid; + return redirect()->route('project.resource.create', [ 'project_uuid' => $this->parameters['project_uuid'], 'environment_name' => $this->parameters['environment_name'], diff --git a/app/Livewire/Project/New/SimpleDockerfile.php b/app/Livewire/Project/New/SimpleDockerfile.php index 172403a1a..6f6bc9185 100644 --- a/app/Livewire/Project/New/SimpleDockerfile.php +++ b/app/Livewire/Project/New/SimpleDockerfile.php @@ -13,8 +13,11 @@ class SimpleDockerfile extends Component { public string $dockerfile = ''; + public array $parameters; + public array $query; + public function mount() { $this->parameters = get_route_parameters(); @@ -26,17 +29,18 @@ public function mount() '; } } + public function submit() { $this->validate([ - 'dockerfile' => 'required' + 'dockerfile' => 'required', ]); $destination_uuid = $this->query['destination']; $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + if (! $destination) { $destination = SwarmDocker::where('uuid', $destination_uuid)->first(); } - if (!$destination) { + if (! $destination) { throw new \Exception('Destination not found. What?!'); } $destination_class = $destination->getMorphClass(); @@ -45,13 +49,13 @@ public function submit() $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); $port = get_port_from_dockerfile($this->dockerfile); - if (!$port) { + if (! $port) { $port = 80; } $application = Application::create([ - 'name' => 'dockerfile-' . new Cuid2(7), + 'name' => 'dockerfile-'.new Cuid2(7), 'repository_project_id' => 0, - 'git_repository' => "coollabsio/coolify", + 'git_repository' => 'coollabsio/coolify', 'git_branch' => 'main', 'build_pack' => 'dockerfile', 'dockerfile' => $this->dockerfile, @@ -61,13 +65,13 @@ public function submit() 'destination_type' => $destination_class, 'health_check_enabled' => false, 'source_id' => 0, - 'source_type' => GithubApp::class + 'source_type' => GithubApp::class, ]); $fqdn = generateFqdn($destination->server, $application->uuid); $application->update([ - 'name' => 'dockerfile-' . $application->uuid, - 'fqdn' => $fqdn + 'name' => 'dockerfile-'.$application->uuid, + 'fqdn' => $fqdn, ]); $application->parseHealthcheckFromDockerfile(dockerfile: collect(str($this->dockerfile)->trim()->explode("\n")), isInit: true); diff --git a/app/Livewire/Project/Resource/Create.php b/app/Livewire/Project/Resource/Create.php index 6d94d833e..341dd93d8 100644 --- a/app/Livewire/Project/Resource/Create.php +++ b/app/Livewire/Project/Resource/Create.php @@ -10,7 +10,9 @@ class Create extends Component { public $type; + public $project; + public function mount() { $type = str(request()->query('type')); @@ -18,54 +20,55 @@ public function mount() $server_id = request()->query('server_id'); $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); - if (!$project) { + if (! $project) { return redirect()->route('dashboard'); } $this->project = $project; $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first(); - if (!$environment) { + if (! $environment) { return redirect()->route('dashboard'); } if (isset($type) && isset($destination_uuid) && isset($server_id)) { $services = get_service_templates(); if (in_array($type, DATABASE_TYPES)) { - if ($type->value() === "postgresql") { + if ($type->value() === 'postgresql') { $database = create_standalone_postgresql($environment->id, $destination_uuid); - } else if ($type->value() === 'redis') { + } elseif ($type->value() === 'redis') { $database = create_standalone_redis($environment->id, $destination_uuid); - } else if ($type->value() === 'mongodb') { + } elseif ($type->value() === 'mongodb') { $database = create_standalone_mongodb($environment->id, $destination_uuid); - } else if ($type->value() === 'mysql') { + } elseif ($type->value() === 'mysql') { $database = create_standalone_mysql($environment->id, $destination_uuid); - } else if ($type->value() === 'mariadb') { + } elseif ($type->value() === 'mariadb') { $database = create_standalone_mariadb($environment->id, $destination_uuid); - } else if ($type->value() === 'keydb') { + } elseif ($type->value() === 'keydb') { $database = create_standalone_keydb($environment->id, $destination_uuid); - } else if ($type->value() === 'dragonfly') { + } elseif ($type->value() === 'dragonfly') { $database = create_standalone_dragonfly($environment->id, $destination_uuid); - } else if ($type->value() === 'clickhouse') { + } elseif ($type->value() === 'clickhouse') { $database = create_standalone_clickhouse($environment->id, $destination_uuid); } + return redirect()->route('project.database.configuration', [ 'project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid, ]); } - if ($type->startsWith('one-click-service-') && !is_null((int)$server_id)) { + if ($type->startsWith('one-click-service-') && ! is_null((int) $server_id)) { $oneClickServiceName = $type->after('one-click-service-')->value(); $oneClickService = data_get($services, "$oneClickServiceName.compose"); $oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null); if ($oneClickDotEnvs) { $oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) { - return !empty($value); + return ! empty($value); }); } if ($oneClickService) { $destination = StandaloneDocker::whereUuid($destination_uuid)->first(); $service_payload = [ - 'name' => "$oneClickServiceName-" . str()->random(10), + 'name' => "$oneClickServiceName-".str()->random(10), 'docker_compose_raw' => base64_decode($oneClickService), 'environment_id' => $environment->id, 'service_type' => $oneClickServiceName, @@ -77,7 +80,7 @@ public function mount() data_set($service_payload, 'connect_to_docker_network', true); } $service = Service::create($service_payload); - $service->name = "$oneClickServiceName-" . $service->uuid; + $service->name = "$oneClickServiceName-".$service->uuid; $service->save(); if ($oneClickDotEnvs?->count() > 0) { $oneClickDotEnvs->each(function ($value) use ($service) { @@ -98,6 +101,7 @@ public function mount() }); } $service->parse(isNew: true); + return redirect()->route('project.service.configuration', [ 'service_uuid' => $service->uuid, 'environment_name' => $environment->name, @@ -108,6 +112,7 @@ public function mount() $this->type = $type->value(); } } + public function render() { return view('livewire.project.resource.create'); diff --git a/app/Livewire/Project/Resource/Index.php b/app/Livewire/Project/Resource/Index.php index e3f3864c3..71ce2c356 100644 --- a/app/Livewire/Project/Resource/Index.php +++ b/app/Livewire/Project/Resource/Index.php @@ -9,25 +9,37 @@ class Index extends Component { public Project $project; + public Environment $environment; + public $applications = []; + public $postgresqls = []; + public $redis = []; + public $mongodbs = []; + public $mysqls = []; + public $mariadbs = []; + public $keydbs = []; + public $dragonflies = []; + public $clickhouses = []; + public $services = []; + public function mount() { $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); - if (!$project) { + if (! $project) { return redirect()->route('dashboard'); } $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first(); - if (!$environment) { + if (! $environment) { return redirect()->route('dashboard'); } $this->project = $project; @@ -39,9 +51,10 @@ public function mount() $application->hrefLink = route('project.application.configuration', [ 'project_uuid' => data_get($application, 'environment.project.uuid'), 'environment_name' => data_get($application, 'environment.name'), - 'application_uuid' => data_get($application, 'uuid') + 'application_uuid' => data_get($application, 'uuid'), ]); } + return $application; }); $this->postgresqls = $this->environment->postgresqls->load(['tags'])->sortBy('name'); @@ -50,9 +63,10 @@ public function mount() $postgresql->hrefLink = route('project.database.configuration', [ 'project_uuid' => data_get($postgresql, 'environment.project.uuid'), 'environment_name' => data_get($postgresql, 'environment.name'), - 'database_uuid' => data_get($postgresql, 'uuid') + 'database_uuid' => data_get($postgresql, 'uuid'), ]); } + return $postgresql; }); $this->redis = $this->environment->redis->load(['tags'])->sortBy('name'); @@ -61,9 +75,10 @@ public function mount() $redis->hrefLink = route('project.database.configuration', [ 'project_uuid' => data_get($redis, 'environment.project.uuid'), 'environment_name' => data_get($redis, 'environment.name'), - 'database_uuid' => data_get($redis, 'uuid') + 'database_uuid' => data_get($redis, 'uuid'), ]); } + return $redis; }); $this->mongodbs = $this->environment->mongodbs->load(['tags'])->sortBy('name'); @@ -72,9 +87,10 @@ public function mount() $mongodb->hrefLink = route('project.database.configuration', [ 'project_uuid' => data_get($mongodb, 'environment.project.uuid'), 'environment_name' => data_get($mongodb, 'environment.name'), - 'database_uuid' => data_get($mongodb, 'uuid') + 'database_uuid' => data_get($mongodb, 'uuid'), ]); } + return $mongodb; }); $this->mysqls = $this->environment->mysqls->load(['tags'])->sortBy('name'); @@ -83,9 +99,10 @@ public function mount() $mysql->hrefLink = route('project.database.configuration', [ 'project_uuid' => data_get($mysql, 'environment.project.uuid'), 'environment_name' => data_get($mysql, 'environment.name'), - 'database_uuid' => data_get($mysql, 'uuid') + 'database_uuid' => data_get($mysql, 'uuid'), ]); } + return $mysql; }); $this->mariadbs = $this->environment->mariadbs->load(['tags'])->sortBy('name'); @@ -94,9 +111,10 @@ public function mount() $mariadb->hrefLink = route('project.database.configuration', [ 'project_uuid' => data_get($mariadb, 'environment.project.uuid'), 'environment_name' => data_get($mariadb, 'environment.name'), - 'database_uuid' => data_get($mariadb, 'uuid') + 'database_uuid' => data_get($mariadb, 'uuid'), ]); } + return $mariadb; }); $this->keydbs = $this->environment->keydbs->load(['tags'])->sortBy('name'); @@ -105,9 +123,10 @@ public function mount() $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') + 'database_uuid' => data_get($keydb, 'uuid'), ]); } + return $keydb; }); $this->dragonflies = $this->environment->dragonflies->load(['tags'])->sortBy('name'); @@ -116,9 +135,10 @@ public function mount() $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') + 'database_uuid' => data_get($dragonfly, 'uuid'), ]); } + return $dragonfly; }); $this->clickhouses = $this->environment->clickhouses->load(['tags'])->sortBy('name'); @@ -127,9 +147,10 @@ public function mount() $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') + 'database_uuid' => data_get($clickhouse, 'uuid'), ]); } + return $clickhouse; }); $this->services = $this->environment->services->load(['tags'])->sortBy('name'); @@ -138,13 +159,15 @@ public function mount() $service->hrefLink = route('project.service.configuration', [ 'project_uuid' => data_get($service, 'environment.project.uuid'), 'environment_name' => data_get($service, 'environment.name'), - 'service_uuid' => data_get($service, 'uuid') + 'service_uuid' => data_get($service, 'uuid'), ]); $service->status = $service->status(); } + return $service; }); } + public function render() { return view('livewire.project.resource.index'); diff --git a/app/Livewire/Project/Service/Configuration.php b/app/Livewire/Project/Service/Configuration.php index eaa794a93..47534ded1 100644 --- a/app/Livewire/Project/Service/Configuration.php +++ b/app/Livewire/Project/Service/Configuration.php @@ -9,34 +9,43 @@ class Configuration extends Component { public ?Service $service = null; + public $applications; + public $databases; + public array $parameters; + public array $query; + public function getListeners() { $userId = auth()->user()->id; + return [ "echo-private:user.{$userId},ServiceStatusChanged" => 'check_status', - "check_status", - "refresh" => '$refresh', + 'check_status', + 'refresh' => '$refresh', ]; } + public function render() { return view('livewire.project.service.configuration'); } + public function mount() { $this->parameters = get_route_parameters(); $this->query = request()->query(); $this->service = Service::whereUuid($this->parameters['service_uuid'])->first(); - if (!$this->service) { + if (! $this->service) { return redirect()->route('dashboard'); } $this->applications = $this->service->applications->sort(); $this->databases = $this->service->databases->sort(); } + public function restartApplication($id) { try { @@ -49,6 +58,7 @@ public function restartApplication($id) return handleError($e, $this); } } + public function restartDatabase($id) { try { @@ -61,6 +71,7 @@ public function restartDatabase($id) return handleError($e, $this); } } + public function check_status() { try { diff --git a/app/Livewire/Project/Service/Database.php b/app/Livewire/Project/Service/Database.php index d7c1c9f5c..9804fb5ba 100644 --- a/app/Livewire/Project/Service/Database.php +++ b/app/Livewire/Project/Service/Database.php @@ -10,10 +10,13 @@ class Database extends Component { public ServiceDatabase $database; + public ?string $db_url_public = null; + public $fileStorages; - protected $listeners = ["refreshFileStorages"]; + protected $listeners = ['refreshFileStorages']; + protected $rules = [ 'database.human_name' => 'nullable', 'database.description' => 'nullable', @@ -23,10 +26,12 @@ class Database extends Component 'database.is_public' => 'required|boolean', 'database.is_log_drain_enabled' => 'required|boolean', ]; + public function render() { return view('livewire.project.service.database'); } + public function mount() { if ($this->database->is_public) { @@ -34,31 +39,37 @@ public function mount() } $this->refreshFileStorages(); } + public function instantSaveExclude() { $this->submit(); } + public function instantSaveLogDrain() { - if (!$this->database->service->destination->server->isLogDrainEnabled()) { + if (! $this->database->service->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->submit(); $this->dispatch('success', 'You need to restart the service for the changes to take effect.'); } + public function instantSave() { - if ($this->database->is_public && !$this->database->public_port) { + 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')) { + 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); @@ -71,10 +82,12 @@ public function instantSave() } $this->submit(); } + public function refreshFileStorages() { $this->fileStorages = $this->database->fileStorages()->get(); } + public function submit() { try { diff --git a/app/Livewire/Project/Service/EditCompose.php b/app/Livewire/Project/Service/EditCompose.php index d6e867956..fd4d684b1 100644 --- a/app/Livewire/Project/Service/EditCompose.php +++ b/app/Livewire/Project/Service/EditCompose.php @@ -8,12 +8,15 @@ class EditCompose extends Component { public Service $service; + public $serviceId; + protected $rules = [ 'service.docker_compose_raw' => 'required', 'service.docker_compose' => 'required', 'service.is_container_label_escape_enabled' => 'required', ]; + public function mount() { $this->service = Service::find($this->serviceId); @@ -21,17 +24,19 @@ public function mount() public function saveEditedCompose() { - $this->dispatch('info', "Saving new docker compose..."); + $this->dispatch('info', 'Saving new docker compose...'); $this->dispatch('saveCompose', $this->service->docker_compose_raw); } + public function instantSave() { $this->validate([ 'service.is_container_label_escape_enabled' => 'required', ]); $this->service->save(['is_container_label_escape_enabled' => $this->service->is_container_label_escape_enabled]); - $this->dispatch('success', "Service updated successfully"); + $this->dispatch('success', 'Service updated successfully'); } + public function render() { return view('livewire.project.service.edit-compose'); diff --git a/app/Livewire/Project/Service/EditDomain.php b/app/Livewire/Project/Service/EditDomain.php index a09d6aa38..70e8006c7 100644 --- a/app/Livewire/Project/Service/EditDomain.php +++ b/app/Livewire/Project/Service/EditDomain.php @@ -8,14 +8,19 @@ class EditDomain extends Component { public $applicationId; + public ServiceApplication $application; + protected $rules = [ 'application.fqdn' => 'nullable', 'application.required_fqdn' => 'required|boolean', ]; - public function mount() { + + public function mount() + { $this->application = ServiceApplication::find($this->applicationId); } + public function updatedApplicationFqdn() { $this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim(); @@ -26,6 +31,7 @@ public function updatedApplicationFqdn() $this->application->fqdn = $this->application->fqdn->unique()->implode(','); $this->application->save(); } + public function submit() { try { @@ -46,6 +52,7 @@ public function submit() $this->dispatch('configurationChanged'); } } + public function render() { return view('livewire.project.service.edit-domain'); diff --git a/app/Livewire/Project/Service/FileStorage.php b/app/Livewire/Project/Service/FileStorage.php index f10c49794..201ebf58f 100644 --- a/app/Livewire/Project/Service/FileStorage.php +++ b/app/Livewire/Project/Service/FileStorage.php @@ -14,14 +14,17 @@ use App\Models\StandaloneMysql; use App\Models\StandalonePostgresql; use App\Models\StandaloneRedis; -use Livewire\Component; use Illuminate\Support\Str; +use Livewire\Component; class FileStorage extends Component { public LocalFileVolume $fileStorage; + public ServiceApplication|StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|ServiceDatabase|Application $resource; + public string $fs_path; + public ?string $workdir = null; protected $rules = [ @@ -30,6 +33,7 @@ class FileStorage extends Component 'fileStorage.mount_path' => 'required', 'fileStorage.content' => 'nullable', ]; + public function mount() { $this->resource = $this->fileStorage->service; @@ -41,7 +45,9 @@ public function mount() $this->fs_path = $this->fileStorage->fs_path; } } - public function convertToDirectory() { + + public function convertToDirectory() + { try { $this->fileStorage->deleteStorageOnServer(); $this->fileStorage->is_directory = true; @@ -54,7 +60,9 @@ public function convertToDirectory() { $this->dispatch('refresh_storages'); } } - public function convertToFile() { + + public function convertToFile() + { try { $this->fileStorage->deleteStorageOnServer(); $this->fileStorage->is_directory = false; @@ -67,7 +75,9 @@ public function convertToFile() { $this->dispatch('refresh_storages'); } } - public function delete() { + + public function delete() + { try { $this->fileStorage->deleteStorageOnServer(); $this->fileStorage->delete(); @@ -78,6 +88,7 @@ public function delete() { $this->dispatch('refresh_storages'); } } + public function submit() { $original = $this->fileStorage->getOriginal(); @@ -92,13 +103,16 @@ public function submit() } catch (\Throwable $e) { $this->fileStorage->setRawAttributes($original); $this->fileStorage->save(); + return handleError($e, $this); } } + public function instantSave() { $this->submit(); } + public function render() { return view('livewire.project.service.file-storage'); diff --git a/app/Livewire/Project/Service/Index.php b/app/Livewire/Project/Service/Index.php index fe335afb1..0a7b6ec90 100644 --- a/app/Livewire/Project/Service/Index.php +++ b/app/Livewire/Project/Service/Index.php @@ -11,11 +11,17 @@ class Index extends Component { public ?Service $service = null; + public ?ServiceApplication $serviceApplication = null; + public ?ServiceDatabase $serviceDatabase = null; + public array $parameters; + public array $query; + public Collection $services; + public $s3s; protected $listeners = ['generateDockerCompose']; @@ -27,7 +33,7 @@ public function mount() $this->parameters = get_route_parameters(); $this->query = request()->query(); $this->service = Service::whereUuid($this->parameters['service_uuid'])->first(); - if (!$this->service) { + if (! $this->service) { return redirect()->route('dashboard'); } $service = $this->service->applications()->whereUuid($this->parameters['stack_service_uuid'])->first(); @@ -39,15 +45,17 @@ public function mount() $this->serviceDatabase->getFilesFromServer(); } $this->s3s = currentTeam()->s3s; - } catch(\Throwable $e) { + } catch (\Throwable $e) { return handleError($e, $this); } } + public function generateDockerCompose() { $this->service->parse(); } + public function render() { return view('livewire.project.service.index'); diff --git a/app/Livewire/Project/Service/Navbar.php b/app/Livewire/Project/Service/Navbar.php index 392178633..7d3987b3d 100644 --- a/app/Livewire/Project/Service/Navbar.php +++ b/app/Livewire/Project/Service/Navbar.php @@ -2,9 +2,9 @@ namespace App\Livewire\Project\Service; -use App\Actions\Shared\PullImage; use App\Actions\Service\StartService; use App\Actions\Service\StopService; +use App\Actions\Shared\PullImage; use App\Events\ServiceStatusChanged; use App\Models\Service; use Livewire\Component; @@ -13,8 +13,11 @@ class Navbar extends Component { public Service $service; + public array $parameters; + public array $query; + public $isDeploymentProgress = false; public function mount() @@ -25,13 +28,16 @@ public function mount() $this->dispatch('configurationChanged'); } } + public function getListeners() { $userId = auth()->user()->id; + return [ "echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted', ]; } + public function serviceStarted() { $this->dispatch('success', 'Service status changed.'); @@ -48,10 +54,12 @@ public function check_status() $this->dispatch('check_status'); $this->dispatch('success', 'Service status updated.'); } + public function render() { return view('livewire.project.service.navbar'); } + public function checkDeployments() { $activity = Activity::where('properties->type_uuid', $this->service->uuid)->latest()->first(); @@ -62,17 +70,20 @@ public function checkDeployments() $this->isDeploymentProgress = false; } } + public function start() { $this->checkDeployments(); if ($this->isDeploymentProgress) { $this->dispatch('error', 'There is a deployment in progress.'); + return; } $this->service->parse(); $activity = StartService::run($this->service); $this->dispatch('activityMonitor', $activity->id); } + public function stop(bool $forceCleanup = false) { StopService::run($this->service); @@ -83,11 +94,13 @@ public function stop(bool $forceCleanup = false) } ServiceStatusChanged::dispatch(); } + public function restart() { $this->checkDeployments(); if ($this->isDeploymentProgress) { $this->dispatch('error', 'There is a deployment in progress.'); + return; } PullImage::run($this->service); diff --git a/app/Livewire/Project/Service/ServiceApplicationView.php b/app/Livewire/Project/Service/ServiceApplicationView.php index dfa2baced..e7d00c3dd 100644 --- a/app/Livewire/Project/Service/ServiceApplicationView.php +++ b/app/Livewire/Project/Service/ServiceApplicationView.php @@ -8,7 +8,9 @@ class ServiceApplicationView extends Component { public ServiceApplication $application; + public $parameters; + protected $rules = [ 'application.human_name' => 'nullable', 'application.description' => 'nullable', @@ -20,10 +22,12 @@ class ServiceApplicationView extends Component 'application.is_gzip_enabled' => 'nullable|boolean', 'application.is_stripprefix_enabled' => 'nullable|boolean', ]; + public function render() { return view('livewire.project.service.service-application-view'); } + public function updatedApplicationFqdn() { $this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim(); @@ -34,34 +38,41 @@ public function updatedApplicationFqdn() $this->application->fqdn = $this->application->fqdn->unique()->implode(','); $this->application->save(); } + public function instantSave() { $this->submit(); } + public function instantSaveAdvanced() { - if (!$this->application->service->destination->server->isLogDrainEnabled()) { + if (! $this->application->service->destination->server->isLogDrainEnabled()) { $this->application->is_log_drain_enabled = false; $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); + return; } $this->application->save(); $this->dispatch('success', 'You need to restart the service for the changes to take effect.'); } + public function delete() { try { $this->application->delete(); $this->dispatch('success', 'Application deleted.'); + return redirect()->route('project.service.configuration', $this->parameters); } catch (\Throwable $e) { return handleError($e, $this); } } + public function mount() { $this->parameters = get_route_parameters(); } + public function submit() { try { diff --git a/app/Livewire/Project/Service/StackForm.php b/app/Livewire/Project/Service/StackForm.php index 7eca5bf2d..05917f895 100644 --- a/app/Livewire/Project/Service/StackForm.php +++ b/app/Livewire/Project/Service/StackForm.php @@ -9,8 +9,11 @@ class StackForm extends Component { public Service $service; + public Collection $fields; - protected $listeners = ["saveCompose"]; + + protected $listeners = ['saveCompose']; + public $rules = [ 'service.docker_compose_raw' => 'required', 'service.docker_compose' => 'required', @@ -18,7 +21,9 @@ class StackForm extends Component 'service.description' => 'nullable', 'service.connect_to_docker_network' => 'nullable', ]; + public $validationAttributes = []; + public function mount() { $this->fields = collect([]); @@ -30,12 +35,12 @@ public function mount() $rules = data_get($field, 'rules', 'nullable'); $isPassword = data_get($field, 'isPassword'); $this->fields->put($key, [ - "serviceName" => $serviceName, - "key" => $key, - "name" => $fieldKey, - "value" => $value, - "isPassword" => $isPassword, - "rules" => $rules + 'serviceName' => $serviceName, + 'key' => $key, + 'name' => $fieldKey, + 'value' => $value, + 'isPassword' => $isPassword, + 'rules' => $rules, ]); $this->rules["fields.$key.value"] = $rules; @@ -44,11 +49,13 @@ public function mount() } $this->fields = $this->fields->sortBy('name'); } + public function saveCompose($raw) { $this->service->docker_compose_raw = $raw; $this->submit(); } + public function instantSave() { $this->service->save(); @@ -82,6 +89,7 @@ public function submit() } } } + public function render() { return view('livewire.project.service.stack-form'); diff --git a/app/Livewire/Project/Service/Storage.php b/app/Livewire/Project/Service/Storage.php index 1d40f1741..161c38097 100644 --- a/app/Livewire/Project/Service/Storage.php +++ b/app/Livewire/Project/Service/Storage.php @@ -8,6 +8,7 @@ class Storage extends Component { public $resource; + public function getListeners() { return [ @@ -15,6 +16,7 @@ public function getListeners() 'refresh_storages' => '$refresh', ]; } + public function addNewVolume($data) { try { @@ -33,6 +35,7 @@ public function addNewVolume($data) return handleError($e, $this); } } + public function render() { return view('livewire.project.service.storage'); diff --git a/app/Livewire/Project/Shared/ConfigurationChecker.php b/app/Livewire/Project/Shared/ConfigurationChecker.php index 930ac5fde..ab9f3785d 100644 --- a/app/Livewire/Project/Shared/ConfigurationChecker.php +++ b/app/Livewire/Project/Shared/ConfigurationChecker.php @@ -17,16 +17,21 @@ 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(); diff --git a/app/Livewire/Project/Shared/Danger.php b/app/Livewire/Project/Shared/Danger.php index 158549b06..e754749a4 100644 --- a/app/Livewire/Project/Shared/Danger.php +++ b/app/Livewire/Project/Shared/Danger.php @@ -9,9 +9,13 @@ class Danger extends Component { public $resource; + public $projectUuid; + public $environmentName; + public bool $delete_configurations = true; + public ?string $modalId = null; public function mount() @@ -21,15 +25,17 @@ public function mount() $this->projectUuid = data_get($parameters, 'project_uuid'); $this->environmentName = data_get($parameters, 'environment_name'); } + public function delete() { try { // $this->authorize('delete', $this->resource); $this->resource->delete(); DeleteResourceJob::dispatch($this->resource, $this->delete_configurations); + return redirect()->route('project.resource.index', [ 'project_uuid' => $this->projectUuid, - 'environment_name' => $this->environmentName + 'environment_name' => $this->environmentName, ]); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Project/Shared/Destination.php b/app/Livewire/Project/Shared/Destination.php index 2ccae47fd..22ada8ab8 100644 --- a/app/Livewire/Project/Shared/Destination.php +++ b/app/Livewire/Project/Shared/Destination.php @@ -14,19 +14,23 @@ class Destination extends Component { public $resource; + public $networks = []; public function getListeners() { $teamId = auth()->user()->currentTeam()->id; + return [ "echo-private:team.{$teamId},ApplicationStatusChanged" => 'loadData', ]; } + public function mount() { $this->loadData(); } + public function loadData() { $all_networks = collect([]); @@ -48,16 +52,19 @@ public function loadData() }); } } + public function stop(int $server_id) { $server = Server::find($server_id); StopApplicationOneServer::run($this->resource, $server); $this->refreshServers(); } + public function redeploy(int $network_id, int $server_id) { if ($this->resource->additional_servers->count() > 0 && str($this->resource->docker_registry_image_name)->isEmpty()) { $this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.
More information here: documentation'); + return; } $deployment_uuid = new Cuid2(7); @@ -71,6 +78,7 @@ public function redeploy(int $network_id, int $server_id) only_this_server: true, no_questions_asked: true, ); + return redirect()->route('project.application.deployment.show', [ 'project_uuid' => data_get($this->resource, 'environment.project.uuid'), 'application_uuid' => data_get($this->resource, 'uuid'), @@ -78,6 +86,7 @@ public function redeploy(int $network_id, int $server_id) 'environment_name' => data_get($this->resource, 'environment.name'), ]); } + public function promote(int $network_id, int $server_id) { $main_destination = $this->resource->destination; @@ -89,6 +98,7 @@ public function promote(int $network_id, int $server_id) $this->resource->additional_networks()->attach($main_destination->id, ['server_id' => $main_destination->server->id]); $this->refreshServers(); } + public function refreshServers() { GetContainersStatus::run($this->resource->destination->server); @@ -97,16 +107,19 @@ public function refreshServers() $this->dispatch('refresh'); ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id')); } + public function addServer(int $network_id, int $server_id) { $this->resource->additional_networks()->attach($network_id, ['server_id' => $server_id]); $this->loadData(); ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id')); } + public function removeServer(int $network_id, int $server_id) { if ($this->resource->destination->server->id == $server_id && $this->resource->destination->id == $network_id) { $this->dispatch('error', 'You cannot remove this destination server.', 'You are trying to remove the main server.'); + return; } $server = Server::find($server_id); diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php index df808ba52..b732b6b52 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php @@ -7,15 +7,23 @@ class Add extends Component { public $parameters; + public bool $shared = false; + public bool $is_preview = false; + public string $key; + public ?string $value = null; + public bool $is_build_time = false; + public bool $is_multiline = false; + public bool $is_literal = false; protected $listeners = ['clearAddEnv' => 'clear']; + protected $rules = [ 'key' => 'required|string', 'value' => 'nullable', @@ -23,6 +31,7 @@ class Add extends Component 'is_multiline' => 'required|boolean', 'is_literal' => 'required|boolean', ]; + protected $validationAttributes = [ 'key' => 'key', 'value' => 'value', @@ -40,9 +49,10 @@ public function submit() { $this->validate(); if (str($this->value)->startsWith('{{') && str($this->value)->endsWith('}}')) { - $type = str($this->value)->after("{{")->before(".")->value; - if (!collect(SHARED_VARIABLE_TYPES)->contains($type)) { - $this->dispatch('error', 'Invalid shared variable type.', "Valid types are: team, project, environment."); + $type = str($this->value)->after('{{')->before('.')->value; + if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) { + $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.'); + return; } } diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php index 561d20d19..4c06bfe23 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php @@ -9,16 +9,24 @@ class All extends Component { public $resource; + public string $resourceClass; + public bool $showPreview = false; + public ?string $modalId = null; + public ?string $variables = null; + public ?string $variablesPreview = null; + public string $view = 'normal'; + protected $listeners = [ 'refreshEnvs', 'saveKey' => 'submit', ]; + protected $rules = [ 'resource.settings.is_env_sorting_enabled' => 'required|boolean', ]; @@ -27,8 +35,8 @@ public function mount() { $this->resourceClass = get_class($this->resource); $resourceWithPreviews = ['App\Models\Application']; - $simpleDockerfile = !is_null(data_get($this->resource, 'dockerfile')); - if (str($this->resourceClass)->contains($resourceWithPreviews) && !$simpleDockerfile) { + $simpleDockerfile = ! is_null(data_get($this->resource, 'dockerfile')); + if (str($this->resourceClass)->contains($resourceWithPreviews) && ! $simpleDockerfile) { $this->showPreview = true; } $this->modalId = new Cuid2(7); @@ -49,6 +57,7 @@ public function sortMe() } $this->getDevView(); } + public function instantSave() { if ($this->resourceClass === 'App\Models\Application' && data_get($this->resource, 'build_pack') !== 'dockercompose') { @@ -57,6 +66,7 @@ public function instantSave() $this->sortMe(); } } + public function getDevView() { $this->variables = $this->resource->environment_variables->map(function ($item) { @@ -66,6 +76,7 @@ public function getDevView() if ($item->is_multiline) { return "$item->key=(multiline, edit in normal view)"; } + return "$item->key=$item->value"; })->join(' '); @@ -77,11 +88,13 @@ public function getDevView() if ($item->is_multiline) { return "$item->key=(multiline, edit in normal view)"; } + return "$item->key=$item->value"; })->join(' '); } } + public function switch() { if ($this->view === 'normal') { @@ -91,6 +104,7 @@ public function switch() } $this->sortMe(); } + public function saveVariables($isPreview) { if ($isPreview) { @@ -113,22 +127,25 @@ public function saveVariables($isPreview) } $found->value = $variable; if (str($found->value)->startsWith('{{') && str($found->value)->endsWith('}}')) { - $type = str($found->value)->after("{{")->before(".")->value; - if (!collect(SHARED_VARIABLE_TYPES)->contains($type)) { - $this->dispatch('error', 'Invalid shared variable type.', "Valid types are: team, project, environment."); + $type = str($found->value)->after('{{')->before('.')->value; + if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) { + $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.'); + return; } } $found->save(); + continue; } else { $environment = new EnvironmentVariable(); $environment->key = $key; $environment->value = $variable; if (str($environment->value)->startsWith('{{') && str($environment->value)->endsWith('}}')) { - $type = str($environment->value)->after("{{")->before(".")->value; - if (!collect(SHARED_VARIABLE_TYPES)->contains($type)) { - $this->dispatch('error', 'Invalid shared variable type.', "Valid types are: team, project, environment."); + $type = str($environment->value)->after('{{')->before('.')->value; + if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) { + $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.'); + return; } } @@ -177,6 +194,7 @@ public function saveVariables($isPreview) } $this->refreshEnvs(); } + public function refreshEnvs() { $this->resource->refresh(); @@ -189,6 +207,7 @@ public function submit($data) $found = $this->resource->environment_variables()->where('key', $data['key'])->first(); if ($found) { $this->dispatch('error', 'Environment variable already exists.'); + return; } $environment = new EnvironmentVariable(); diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php index 65e91e60a..e77c05d6b 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php @@ -10,14 +10,21 @@ class Show extends Component { public $parameters; + public ModelsEnvironmentVariable|SharedEnvironmentVariable $env; + public ?string $modalId = null; + public bool $isDisabled = false; + public bool $isLocked = false; + public bool $isSharedVariable = false; + public string $type; + protected $listeners = [ - "compose_loaded" => '$refresh', + 'compose_loaded' => '$refresh', ]; protected $rules = [ @@ -29,6 +36,7 @@ class Show extends Component 'env.is_shown_once' => 'required|boolean', 'env.real_value' => 'nullable', ]; + protected $validationAttributes = [ 'env.key' => 'Key', 'env.value' => 'Value', @@ -47,6 +55,7 @@ public function mount() $this->parameters = get_route_parameters(); $this->checkEnvs(); } + public function checkEnvs() { $this->isDisabled = false; @@ -57,6 +66,7 @@ public function checkEnvs() $this->isLocked = true; } } + public function serialize() { data_forget($this->env, 'real_value'); @@ -64,6 +74,7 @@ public function serialize() data_forget($this->env, 'is_build_time'); } } + public function lock() { $this->env->is_shown_once = true; @@ -72,10 +83,12 @@ public function lock() $this->checkEnvs(); $this->dispatch('refreshEnvs'); } + public function instantSave() { $this->submit(); } + public function submit() { try { @@ -89,9 +102,10 @@ public function submit() $this->validate(); } if (str($this->env->value)->startsWith('{{') && str($this->env->value)->endsWith('}}')) { - $type = str($this->env->value)->after("{{")->before(".")->value; - if (!collect(SHARED_VARIABLE_TYPES)->contains($type)) { - $this->dispatch('error', 'Invalid shared variable type.', "Valid types are: team, project, environment."); + $type = str($this->env->value)->after('{{')->before('.')->value; + if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) { + $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.'); + return; } } diff --git a/app/Livewire/Project/Shared/ExecuteContainerCommand.php b/app/Livewire/Project/Shared/ExecuteContainerCommand.php index 4fc8bb8c6..dc3a62c56 100644 --- a/app/Livewire/Project/Shared/ExecuteContainerCommand.php +++ b/app/Livewire/Project/Shared/ExecuteContainerCommand.php @@ -11,13 +11,21 @@ class ExecuteContainerCommand extends Component { public string $command; + public string $container; + public Collection $containers; + public $parameters; + public $resource; + public string $type; + public string $workDir = ''; + public Server $server; + public Collection $servers; protected $rules = [ @@ -43,9 +51,9 @@ public function mount() $this->servers = $this->servers->push($server); } } - } else if (data_get($this->parameters, 'database_uuid')) { + } elseif (data_get($this->parameters, 'database_uuid')) { $this->type = 'database'; - $resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(),'id')); + $resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id')); if (is_null($resource)) { abort(404); } @@ -55,14 +63,14 @@ public function mount() } $this->container = $this->resource->uuid; $this->containers->push($this->container); - } else if (data_get($this->parameters, 'service_uuid')) { + } elseif (data_get($this->parameters, 'service_uuid')) { $this->type = 'service'; $this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail(); $this->resource->applications()->get()->each(function ($application) { - $this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid')); + $this->containers->push(data_get($application, 'name').'-'.data_get($this->resource, 'uuid')); }); $this->resource->databases()->get()->each(function ($database) { - $this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid')); + $this->containers->push(data_get($database, 'name').'-'.data_get($this->resource, 'uuid')); }); if ($this->resource->server->isFunctional()) { $this->servers = $this->servers->push($this->resource->server); @@ -72,6 +80,7 @@ public function mount() $this->container = $this->containers->first(); } } + public function loadContainers() { foreach ($this->servers as $server) { @@ -79,8 +88,8 @@ public function loadContainers() if ($server->isSwarm()) { $containers = collect([ [ - 'Names' => $this->resource->uuid . '_' . $this->resource->uuid, - ] + 'Names' => $this->resource->uuid.'_'.$this->resource->uuid, + ], ]); } else { $containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true); @@ -122,8 +131,8 @@ public function runCommand() if ($server->isForceDisabled()) { throw new \RuntimeException('Server is disabled.'); } - $cmd = "sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; " . str_replace("'", "'\''", $this->command) . "'"; - if (!empty($this->workDir)) { + $cmd = "sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; ".str_replace("'", "'\''", $this->command)."'"; + if (! empty($this->workDir)) { $exec = "docker exec -w {$this->workDir} {$container_name} {$cmd}"; } else { $exec = "docker exec {$container_name} {$cmd}"; @@ -134,6 +143,7 @@ public function runCommand() return handleError($e, $this); } } + public function render() { return view('livewire.project.shared.execute-container-command'); diff --git a/app/Livewire/Project/Shared/GetLogs.php b/app/Livewire/Project/Shared/GetLogs.php index 0060fa16e..edcaf0f34 100644 --- a/app/Livewire/Project/Shared/GetLogs.php +++ b/app/Livewire/Project/Shared/GetLogs.php @@ -21,19 +21,28 @@ class GetLogs extends Component { public string $outputs = ''; + public string $errors = ''; + public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|null $resource = null; + public ServiceApplication|ServiceDatabase|null $servicesubtype = null; + public Server $server; + public ?string $container = null; + public ?string $pull_request = null; + public ?bool $streamLogs = false; + public ?bool $showTimeStamps = true; + public int $numberOfLines = 100; public function mount() { - if (!is_null($this->resource)) { + if (! is_null($this->resource)) { if ($this->resource->getMorphClass() === 'App\Models\Application') { $this->showTimeStamps = $this->resource->settings->is_include_timestamps; } else { @@ -45,18 +54,20 @@ public function mount() } if ($this->resource?->getMorphClass() === 'App\Models\Application') { if (str($this->container)->contains('-pr-')) { - $this->pull_request = "Pull Request: " . str($this->container)->afterLast('-pr-')->beforeLast('_')->value(); + $this->pull_request = 'Pull Request: '.str($this->container)->afterLast('-pr-')->beforeLast('_')->value(); } } } } + public function doSomethingWithThisChunkOfOutput($output) { $this->outputs .= removeAnsiColors($output); } + public function instantSave() { - if (!is_null($this->resource)) { + if (! is_null($this->resource)) { if ($this->resource->getMorphClass() === 'App\Models\Application') { $this->resource->settings->is_include_timestamps = $this->showTimeStamps; $this->resource->settings->save(); @@ -77,13 +88,16 @@ public function instantSave() } } } + public function getLogs($refresh = false) { - if (!$this->server->isFunctional()) { + if (! $this->server->isFunctional()) { return; } - if (!$refresh && ($this->resource?->getMorphClass() === 'App\Models\Service' || str($this->container)->contains('-pr-'))) return; - if (!$this->numberOfLines) { + if (! $refresh && ($this->resource?->getMorphClass() === 'App\Models\Service' || str($this->container)->contains('-pr-'))) { + return; + } + if (! $this->numberOfLines) { $this->numberOfLines = 1000; } if ($this->container) { @@ -130,11 +144,13 @@ public function getLogs($refresh = false) $this->outputs = str($this->outputs)->split('/\n/')->sort(function ($a, $b) { $a = explode(' ', $a); $b = explode(' ', $b); + return $a[0] <=> $b[0]; })->join("\n"); } } } + public function render() { return view('livewire.project.shared.get-logs'); diff --git a/app/Livewire/Project/Shared/HealthChecks.php b/app/Livewire/Project/Shared/HealthChecks.php index 56f5a2759..83162e36a 100644 --- a/app/Livewire/Project/Shared/HealthChecks.php +++ b/app/Livewire/Project/Shared/HealthChecks.php @@ -6,8 +6,8 @@ class HealthChecks extends Component { - public $resource; + protected $rules = [ 'resource.health_check_enabled' => 'boolean', 'resource.health_check_path' => 'string', @@ -24,11 +24,13 @@ class HealthChecks extends Component 'resource.custom_healthcheck_found' => 'boolean', ]; + public function instantSave() { $this->resource->save(); $this->dispatch('success', 'Health check updated.'); } + public function submit() { try { @@ -39,6 +41,7 @@ public function submit() return handleError($e, $this); } } + public function render() { return view('livewire.project.shared.health-checks'); diff --git a/app/Livewire/Project/Shared/Logs.php b/app/Livewire/Project/Shared/Logs.php index 52a7b568d..008d743ed 100644 --- a/app/Livewire/Project/Shared/Logs.php +++ b/app/Livewire/Project/Shared/Logs.php @@ -3,7 +3,6 @@ namespace App\Livewire\Project\Shared; use App\Models\Application; -use App\Models\Server; use App\Models\Service; use App\Models\StandaloneClickhouse; use App\Models\StandaloneDragonfly; @@ -19,27 +18,37 @@ class Logs extends Component { public ?string $type = null; + public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource; + public Collection $servers; + public Collection $containers; + public $container = []; + public $parameters; + public $query; + public $status; + public $serviceSubType; + public $cpu; + public function loadContainers($server_id) { try { $server = $this->servers->firstWhere('id', $server_id); - if (!$server->isFunctional()) { + if (! $server->isFunctional()) { return; } if ($server->isSwarm()) { $containers = collect([ [ - 'Names' => $this->resource->uuid . '_' . $this->resource->uuid, - ] + 'Names' => $this->resource->uuid.'_'.$this->resource->uuid, + ], ]); } else { $containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true); @@ -49,6 +58,7 @@ public function loadContainers($server_id) return handleError($e, $this); } } + public function loadMetrics() { return; @@ -57,6 +67,7 @@ public function loadMetrics() $this->cpu = $server->getMetrics(); } } + public function mount() { try { @@ -76,7 +87,7 @@ public function mount() $this->servers = $this->servers->push($server); } } - } else if (data_get($this->parameters, 'database_uuid')) { + } elseif (data_get($this->parameters, 'database_uuid')) { $this->type = 'database'; $resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id')); if (is_null($resource)) { @@ -89,21 +100,21 @@ public function mount() } $this->container = $this->resource->uuid; $this->containers->push($this->container); - } else if (data_get($this->parameters, 'service_uuid')) { + } elseif (data_get($this->parameters, 'service_uuid')) { $this->type = 'service'; $this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail(); $this->resource->applications()->get()->each(function ($application) { - $this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid')); + $this->containers->push(data_get($application, 'name').'-'.data_get($this->resource, 'uuid')); }); $this->resource->databases()->get()->each(function ($database) { - $this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid')); + $this->containers->push(data_get($database, 'name').'-'.data_get($this->resource, 'uuid')); }); if ($this->resource->server->isFunctional()) { $this->servers = $this->servers->push($this->resource->server); } } $this->containers = $this->containers->sort(); - if (data_get($this->query,'pull_request_id')) { + if (data_get($this->query, 'pull_request_id')) { $this->containers = $this->containers->filter(function ($container) { return str_contains($container, $this->query['pull_request_id']); }); diff --git a/app/Livewire/Project/Shared/ResourceLimits.php b/app/Livewire/Project/Shared/ResourceLimits.php index 767175313..608dfbf02 100644 --- a/app/Livewire/Project/Shared/ResourceLimits.php +++ b/app/Livewire/Project/Shared/ResourceLimits.php @@ -7,6 +7,7 @@ class ResourceLimits extends Component { public $resource; + protected $rules = [ 'resource.limits_memory' => 'required|string', 'resource.limits_memory_swap' => 'required|string', @@ -16,6 +17,7 @@ class ResourceLimits extends Component 'resource.limits_cpuset' => 'nullable', 'resource.limits_cpu_shares' => 'nullable', ]; + protected $validationAttributes = [ 'resource.limits_memory' => 'memory', 'resource.limits_memory_swap' => 'swap', @@ -29,22 +31,22 @@ class ResourceLimits extends Component public function submit() { try { - if (!$this->resource->limits_memory) { - $this->resource->limits_memory = "0"; + if (! $this->resource->limits_memory) { + $this->resource->limits_memory = '0'; } - if (!$this->resource->limits_memory_swap) { - $this->resource->limits_memory_swap = "0"; + if (! $this->resource->limits_memory_swap) { + $this->resource->limits_memory_swap = '0'; } if (is_null($this->resource->limits_memory_swappiness)) { - $this->resource->limits_memory_swappiness = "60"; + $this->resource->limits_memory_swappiness = '60'; } - if (!$this->resource->limits_memory_reservation) { - $this->resource->limits_memory_reservation = "0"; + if (! $this->resource->limits_memory_reservation) { + $this->resource->limits_memory_reservation = '0'; } - if (!$this->resource->limits_cpus) { - $this->resource->limits_cpus = "0"; + if (! $this->resource->limits_cpus) { + $this->resource->limits_cpus = '0'; } - if ($this->resource->limits_cpuset === "") { + if ($this->resource->limits_cpuset === '') { $this->resource->limits_cpuset = null; } if (is_null($this->resource->limits_cpu_shares)) { diff --git a/app/Livewire/Project/Shared/ResourceOperations.php b/app/Livewire/Project/Shared/ResourceOperations.php index 46f9021e5..586a125ae 100644 --- a/app/Livewire/Project/Shared/ResourceOperations.php +++ b/app/Livewire/Project/Shared/ResourceOperations.php @@ -12,9 +12,13 @@ class ResourceOperations extends Component { public $resource; + public $projectUuid; + public $environmentName; + public $projects; + public $servers; public function mount() @@ -25,28 +29,29 @@ public function mount() $this->projects = Project::ownedByCurrentTeam()->get(); $this->servers = currentTeam()->servers; } + public function cloneTo($destination_id) { $new_destination = StandaloneDocker::find($destination_id); - if (!$new_destination) { + if (! $new_destination) { $new_destination = SwarmDocker::find($destination_id); } - if (!$new_destination) { + if (! $new_destination) { return $this->addError('destination_id', 'Destination not found.'); } - $uuid = (string)new Cuid2(7); + $uuid = (string) new Cuid2(7); $server = $new_destination->server; if ($this->resource->getMorphClass() === 'App\Models\Application') { $new_resource = $this->resource->replicate()->fill([ 'uuid' => $uuid, - 'name' => $this->resource->name . '-clone-' . $uuid, + 'name' => $this->resource->name.'-clone-'.$uuid, 'fqdn' => generateFqdn($server, $uuid), 'status' => 'exited', 'destination_id' => $new_destination->id, ]); $new_resource->save(); if ($new_resource->destination->server->proxyType() !== 'NONE') { - $customLabels = str(implode("|", generateLabelsApplication($new_resource)))->replace("|", "\n"); + $customLabels = str(implode('|coolify|', generateLabelsApplication($new_resource)))->replace('|coolify|', "\n"); $new_resource->custom_labels = base64_encode($customLabels); $new_resource->save(); } @@ -60,7 +65,7 @@ public function cloneTo($destination_id) $persistentVolumes = $this->resource->persistentStorages()->get(); foreach ($persistentVolumes as $volume) { $newPersistentVolume = $volume->replicate()->fill([ - 'name' => $new_resource->uuid . '-' . str($volume->name)->afterLast('-'), + 'name' => $new_resource->uuid.'-'.str($volume->name)->afterLast('-'), 'resource_id' => $new_resource->id, ]); $newPersistentVolume->save(); @@ -69,9 +74,10 @@ public function cloneTo($destination_id) 'project_uuid' => $this->projectUuid, 'environment_name' => $this->environmentName, 'application_uuid' => $new_resource->uuid, - ]) . "#resource-operations"; + ]).'#resource-operations'; + return redirect()->to($route); - } else if ( + } elseif ( $this->resource->getMorphClass() === 'App\Models\StandalonePostgresql' || $this->resource->getMorphClass() === 'App\Models\StandaloneMongodb' || $this->resource->getMorphClass() === 'App\Models\StandaloneMysql' || @@ -81,10 +87,10 @@ public function cloneTo($destination_id) $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([ 'uuid' => $uuid, - 'name' => $this->resource->name . '-clone-' . $uuid, + 'name' => $this->resource->name.'-clone-'.$uuid, 'status' => 'exited', 'started_at' => null, 'destination_id' => $new_destination->id, @@ -95,29 +101,30 @@ public function cloneTo($destination_id) $payload = []; if ($this->resource->type() === 'standalone-postgresql') { $payload['standalone_postgresql_id'] = $new_resource->id; - } else if ($this->resource->type() === 'standalone-redis') { + } elseif ($this->resource->type() === 'standalone-redis') { $payload['standalone_redis_id'] = $new_resource->id; - } else if ($this->resource->type() === 'standalone-mongodb') { + } elseif ($this->resource->type() === 'standalone-mongodb') { $payload['standalone_mongodb_id'] = $new_resource->id; - } else if ($this->resource->type() === 'standalone-mysql') { + } elseif ($this->resource->type() === 'standalone-mysql') { $payload['standalone_mysql_id'] = $new_resource->id; - } else if ($this->resource->type() === 'standalone-mariadb') { + } elseif ($this->resource->type() === 'standalone-mariadb') { $payload['standalone_mariadb_id'] = $new_resource->id; } - $newEnvironmentVariable = $environmentVarible->replicate()->fill($payload); + $newEnvironmentVariable = $environmentVarible->replicate()->fill($payload); $newEnvironmentVariable->save(); } $route = route('project.database.configuration', [ 'project_uuid' => $this->projectUuid, 'environment_name' => $this->environmentName, 'database_uuid' => $new_resource->uuid, - ]) . "#resource-operations"; + ]).'#resource-operations'; + return redirect()->to($route); - } else if ($this->resource->type() === 'service') { - $uuid = (string)new Cuid2(7); + } elseif ($this->resource->type() === 'service') { + $uuid = (string) new Cuid2(7); $new_resource = $this->resource->replicate()->fill([ 'uuid' => $uuid, - 'name' => $this->resource->name . '-clone-' . $uuid, + 'name' => $this->resource->name.'-clone-'.$uuid, 'destination_id' => $new_destination->id, ]); $new_resource->save(); @@ -136,44 +143,50 @@ public function cloneTo($destination_id) 'project_uuid' => $this->projectUuid, 'environment_name' => $this->environmentName, 'service_uuid' => $new_resource->uuid, - ]) . "#resource-operations"; + ]).'#resource-operations'; + return redirect()->to($route); } - return; + } + public function moveTo($environment_id) { try { $new_environment = Environment::findOrFail($environment_id); $this->resource->update([ - 'environment_id' => $environment_id + 'environment_id' => $environment_id, ]); if ($this->resource->type() === 'application') { $route = route('project.application.configuration', [ 'project_uuid' => $new_environment->project->uuid, 'environment_name' => $new_environment->name, 'application_uuid' => $this->resource->uuid, - ]) . "#resource-operations"; + ]).'#resource-operations'; + return redirect()->to($route); - } else if (str($this->resource->type())->startsWith('standalone-')) { + } elseif (str($this->resource->type())->startsWith('standalone-')) { $route = route('project.database.configuration', [ 'project_uuid' => $new_environment->project->uuid, 'environment_name' => $new_environment->name, 'database_uuid' => $this->resource->uuid, - ]) . "#resource-operations"; + ]).'#resource-operations'; + return redirect()->to($route); - } else if ($this->resource->type() === 'service') { + } elseif ($this->resource->type() === 'service') { $route = route('project.service.configuration', [ 'project_uuid' => $new_environment->project->uuid, 'environment_name' => $new_environment->name, 'service_uuid' => $this->resource->uuid, - ]) . "#resource-operations"; + ]).'#resource-operations'; + return redirect()->to($route); } } catch (\Throwable $e) { return handleError($e, $this); } } + public function render() { return view('livewire.project.shared.resource-operations'); diff --git a/app/Livewire/Project/Shared/ScheduledTask/Add.php b/app/Livewire/Project/Shared/ScheduledTask/Add.php index c415ff3e4..f36b7b141 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/Add.php +++ b/app/Livewire/Project/Shared/ScheduledTask/Add.php @@ -8,20 +8,28 @@ class Add extends Component { public $parameters; + public string $type; + public Collection $containerNames; + public string $name; + public string $command; + public string $frequency; + public ?string $container = ''; protected $listeners = ['clearScheduledTask' => 'clear']; + protected $rules = [ 'name' => 'required|string', 'command' => 'required|string', 'frequency' => 'required|string', 'container' => 'nullable|string', ]; + protected $validationAttributes = [ 'name' => 'name', 'command' => 'command', @@ -42,8 +50,9 @@ public function submit() try { $this->validate(); $isValid = validate_cron_expression($this->frequency); - if (!$isValid) { + if (! $isValid) { $this->dispatch('error', 'Invalid Cron / Human expression.'); + return; } if (empty($this->container) || $this->container == 'null') { diff --git a/app/Livewire/Project/Shared/ScheduledTask/All.php b/app/Livewire/Project/Shared/ScheduledTask/All.php index e5ea66d13..1aa5a2b87 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/All.php +++ b/app/Livewire/Project/Shared/ScheduledTask/All.php @@ -9,9 +9,13 @@ class All extends Component { public $resource; + public Collection $containerNames; + public ?string $variables = null; + public array $parameters; + protected $listeners = ['refreshTasks', 'saveScheduledTask' => 'submit']; public function mount() @@ -23,13 +27,14 @@ public function mount() } elseif ($this->resource->type() == 'application') { if ($this->resource->build_pack === 'dockercompose') { $parsed = $this->resource->parseCompose(); - $containers = collect(data_get($parsed,'services'))->keys(); + $containers = collect(data_get($parsed, 'services'))->keys(); $this->containerNames = $containers; } else { $this->containerNames = collect([]); } } } + public function refreshTasks() { $this->resource->refresh(); diff --git a/app/Livewire/Project/Shared/ScheduledTask/Executions.php b/app/Livewire/Project/Shared/ScheduledTask/Executions.php index 9c1ec7cc5..7a2e14e89 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/Executions.php +++ b/app/Livewire/Project/Shared/ScheduledTask/Executions.php @@ -2,17 +2,18 @@ namespace App\Livewire\Project\Shared\ScheduledTask; -use Illuminate\Support\Facades\Storage; use Livewire\Component; class Executions extends Component { public $executions = []; + public $selectedKey; + public function getListeners() { return [ - "selectTask", + 'selectTask', ]; } @@ -20,6 +21,7 @@ public function selectTask($key): void { if ($key == $this->selectedKey) { $this->selectedKey = null; + return; } $this->selectedKey = $key; diff --git a/app/Livewire/Project/Shared/ScheduledTask/Show.php b/app/Livewire/Project/Shared/ScheduledTask/Show.php index 7490c7055..dbd420d94 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/Show.php +++ b/app/Livewire/Project/Shared/ScheduledTask/Show.php @@ -2,18 +2,22 @@ namespace App\Livewire\Project\Shared\ScheduledTask; -use App\Models\ScheduledTask as ModelsScheduledTask; -use Livewire\Component; use App\Models\Application; +use App\Models\ScheduledTask as ModelsScheduledTask; use App\Models\Service; +use Livewire\Component; use Visus\Cuid2\Cuid2; class Show extends Component { public $parameters; + public Application|Service $resource; + public ModelsScheduledTask $task; + public ?string $modalId = null; + public string $type; protected $rules = [ @@ -23,6 +27,7 @@ class Show extends Component 'task.frequency' => 'required|string', 'task.container' => 'nullable|string', ]; + protected $validationAttributes = [ 'name' => 'name', 'command' => 'command', @@ -37,7 +42,7 @@ public function mount() if (data_get($this->parameters, 'application_uuid')) { $this->type = 'application'; $this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail(); - } else if (data_get($this->parameters, 'service_uuid')) { + } elseif (data_get($this->parameters, 'service_uuid')) { $this->type = 'service'; $this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail(); } @@ -53,6 +58,7 @@ public function instantSave() $this->dispatch('success', 'Scheduled task updated.'); $this->dispatch('refreshTasks'); } + public function submit() { $this->validate(); diff --git a/app/Livewire/Project/Shared/Storages/Add.php b/app/Livewire/Project/Shared/Storages/Add.php index 156078805..d22f3b05f 100644 --- a/app/Livewire/Project/Shared/Storages/Add.php +++ b/app/Livewire/Project/Shared/Storages/Add.php @@ -9,15 +9,25 @@ class Add extends Component { public $resource; + public $uuid; + public $parameters; + public $isSwarm = false; + public string $name; + public string $mount_path; + public ?string $host_path = null; + public string $file_storage_path; + public ?string $file_storage_content = null; + public string $file_storage_directory_source; + public string $file_storage_directory_destination; public $rules = [ @@ -44,13 +54,13 @@ class Add extends Component public function mount() { - $this->file_storage_directory_source = application_configuration_dir() . "/{$this->resource->uuid}"; + $this->file_storage_directory_source = application_configuration_dir()."/{$this->resource->uuid}"; $this->uuid = $this->resource->uuid; $this->parameters = get_route_parameters(); if (data_get($this->parameters, 'application_uuid')) { $applicationUuid = $this->parameters['application_uuid']; $application = Application::where('uuid', $applicationUuid)->first(); - if (!$application) { + if (! $application) { abort(404); } if ($application->destination->server->isSwarm()) { @@ -59,6 +69,7 @@ public function mount() } } } + public function submitFileStorage() { try { @@ -69,7 +80,7 @@ public function submitFileStorage() $this->file_storage_path = trim($this->file_storage_path); $this->file_storage_path = str($this->file_storage_path)->start('/')->value(); if ($this->resource->getMorphClass() === 'App\Models\Application') { - $fs_path = application_configuration_dir() . '/' . $this->resource->uuid . $this->file_storage_path; + $fs_path = application_configuration_dir().'/'.$this->resource->uuid.$this->file_storage_path; } LocalFileVolume::create( [ @@ -78,7 +89,7 @@ public function submitFileStorage() 'content' => $this->file_storage_content, 'is_directory' => false, 'resource_id' => $this->resource->id, - 'resource_type' => get_class($this->resource) + 'resource_type' => get_class($this->resource), ], ); $this->dispatch('refresh_storages'); @@ -87,6 +98,7 @@ public function submitFileStorage() } } + public function submitFileStorageDirectory() { try { @@ -104,7 +116,7 @@ public function submitFileStorageDirectory() 'mount_path' => $this->file_storage_directory_destination, 'is_directory' => true, 'resource_id' => $this->resource->id, - 'resource_type' => get_class($this->resource) + 'resource_type' => get_class($this->resource), ], ); $this->dispatch('refresh_storages'); @@ -113,6 +125,7 @@ public function submitFileStorageDirectory() } } + public function submitPersistentVolume() { try { @@ -121,7 +134,7 @@ public function submitPersistentVolume() 'mount_path' => 'required|string', 'host_path' => 'string|nullable', ]); - $name = $this->uuid . '-' . $this->name; + $name = $this->uuid.'-'.$this->name; $this->dispatch('addNewVolume', [ 'name' => $name, 'mount_path' => $this->mount_path, diff --git a/app/Livewire/Project/Shared/Storages/All.php b/app/Livewire/Project/Shared/Storages/All.php index 14fa9b7b0..d2014694e 100644 --- a/app/Livewire/Project/Shared/Storages/All.php +++ b/app/Livewire/Project/Shared/Storages/All.php @@ -7,5 +7,6 @@ class All extends Component { public $resource; + protected $listeners = ['refresh_storages' => '$refresh']; } diff --git a/app/Livewire/Project/Shared/Storages/Show.php b/app/Livewire/Project/Shared/Storages/Show.php index 283930174..52b52ef6d 100644 --- a/app/Livewire/Project/Shared/Storages/Show.php +++ b/app/Livewire/Project/Shared/Storages/Show.php @@ -9,10 +9,15 @@ class Show extends Component { public LocalPersistentVolume $storage; + public bool $isReadOnly = false; + public ?string $modalId = null; + public bool $isFirst = true; + public bool $isService = false; + public ?string $startedAt = null; protected $rules = [ @@ -20,6 +25,7 @@ class Show extends Component 'storage.mount_path' => 'required|string', 'storage.host_path' => 'string|nullable', ]; + protected $validationAttributes = [ 'name' => 'name', 'mount_path' => 'mount', diff --git a/app/Livewire/Project/Shared/Tags.php b/app/Livewire/Project/Shared/Tags.php index 92a08f117..85d5c21dc 100644 --- a/app/Livewire/Project/Shared/Tags.php +++ b/app/Livewire/Project/Shared/Tags.php @@ -8,27 +8,35 @@ class Tags extends Component { public $resource = null; + public ?string $new_tag = null; + public $tags = []; + protected $listeners = [ 'refresh' => '$refresh', ]; + protected $rules = [ 'resource.tags.*.name' => 'required|string|min:2', - 'new_tag' => 'required|string|min:2' + 'new_tag' => 'required|string|min:2', ]; + protected $validationAttributes = [ - 'new_tag' => 'tag' + 'new_tag' => 'tag', ]; + public function mount() { $this->tags = Tag::ownedByCurrentTeam()->get(); } + public function addTag(string $id, string $name) { try { if ($this->resource->tags()->where('id', $id)->exists()) { $this->dispatch('error', 'Duplicate tags.', "Tag $name already added."); + return; } $this->resource->tags()->syncWithoutDetaching($id); @@ -37,13 +45,14 @@ public function addTag(string $id, string $name) return handleError($e, $this); } } + public function deleteTag(string $id) { try { $this->resource->tags()->detach($id); $found_more_tags = Tag::where(['id' => $id, 'team_id' => currentTeam()->id])->first(); - if ($found_more_tags->applications()->count() == 0 && $found_more_tags->services()->count() == 0){ + if ($found_more_tags->applications()->count() == 0 && $found_more_tags->services()->count() == 0) { $found_more_tags->delete(); } $this->refresh(); @@ -51,29 +60,32 @@ public function deleteTag(string $id) return handleError($e, $this); } } + public function refresh() { $this->resource->load(['tags']); $this->tags = Tag::ownedByCurrentTeam()->get(); $this->new_tag = null; } + public function submit() { try { $this->validate([ - 'new_tag' => 'required|string|min:2' + 'new_tag' => 'required|string|min:2', ]); $tags = str($this->new_tag)->trim()->explode(' '); foreach ($tags as $tag) { if ($this->resource->tags()->where('name', $tag)->exists()) { $this->dispatch('error', 'Duplicate tags.', "Tag $tag already added."); + continue; } $found = Tag::where(['name' => $tag, 'team_id' => currentTeam()->id])->first(); - if (!$found) { + if (! $found) { $found = Tag::create([ 'name' => $tag, - 'team_id' => currentTeam()->id + 'team_id' => currentTeam()->id, ]); } $this->resource->tags()->syncWithoutDetaching($found->id); @@ -83,6 +95,7 @@ public function submit() return handleError($e, $this); } } + public function render() { return view('livewire.project.shared.tags'); diff --git a/app/Livewire/Project/Shared/Webhooks.php b/app/Livewire/Project/Shared/Webhooks.php index 35a383ece..e96bd888e 100644 --- a/app/Livewire/Project/Shared/Webhooks.php +++ b/app/Livewire/Project/Shared/Webhooks.php @@ -7,27 +7,35 @@ class Webhooks extends Component { public $resource; + public ?string $deploywebhook = null; + public ?string $githubManualWebhook = null; + public ?string $gitlabManualWebhook = null; + public ?string $bitbucketManualWebhook = null; + public ?string $giteaManualWebhook = null; + protected $rules = [ 'resource.manual_webhook_secret_github' => 'nullable|string', 'resource.manual_webhook_secret_gitlab' => 'nullable|string', 'resource.manual_webhook_secret_bitbucket' => 'nullable|string', 'resource.manual_webhook_secret_gitea' => 'nullable|string', ]; + public function saveSecret() { try { $this->validate(); $this->resource->save(); - $this->dispatch('success','Secret Saved.'); + $this->dispatch('success', 'Secret Saved.'); } catch (\Exception $e) { return handleError($e, $this); } } + public function mount() { $this->deploywebhook = generateDeployWebhook($this->resource); @@ -36,6 +44,7 @@ public function mount() $this->bitbucketManualWebhook = generateGitManualWebhook($this->resource, 'bitbucket'); $this->giteaManualWebhook = generateGitManualWebhook($this->resource, 'gitea'); } + public function render() { return view('livewire.project.shared.webhooks'); diff --git a/app/Livewire/Project/Show.php b/app/Livewire/Project/Show.php index 0824ab32e..d5d660017 100644 --- a/app/Livewire/Project/Show.php +++ b/app/Livewire/Project/Show.php @@ -8,17 +8,20 @@ class Show extends Component { public Project $project; - public function mount() { + + public function mount() + { $projectUuid = request()->route('project_uuid'); $teamId = currentTeam()->id; $project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first(); - if (!$project) { + if (! $project) { return redirect()->route('dashboard'); } $project->load(['environments']); $this->project = $project; } + public function render() { return view('livewire.project.show'); diff --git a/app/Livewire/RunCommand.php b/app/Livewire/RunCommand.php index 42f914818..fc7f1eefc 100644 --- a/app/Livewire/RunCommand.php +++ b/app/Livewire/RunCommand.php @@ -8,13 +8,16 @@ class RunCommand extends Component { public string $command; + public $server; + public $servers = []; protected $rules = [ 'server' => 'required', 'command' => 'required', ]; + protected $validationAttributes = [ 'server' => 'server', 'command' => 'command', diff --git a/app/Livewire/Security/ApiTokens.php b/app/Livewire/Security/ApiTokens.php index f0ffff133..c485a6a3a 100644 --- a/app/Livewire/Security/ApiTokens.php +++ b/app/Livewire/Security/ApiTokens.php @@ -7,15 +7,19 @@ class ApiTokens extends Component { public ?string $description = null; + public $tokens = []; + public function render() { return view('livewire.security.api-tokens'); } + public function mount() { $this->tokens = auth()->user()->tokens; } + public function addNewToken() { try { @@ -29,6 +33,7 @@ public function addNewToken() return handleError($e, $this); } } + public function revoke(int $id) { $token = auth()->user()->tokens()->where('id', $id)->first(); diff --git a/app/Livewire/Security/PrivateKey/Create.php b/app/Livewire/Security/PrivateKey/Create.php index 30449b220..32a67bbea 100644 --- a/app/Livewire/Security/PrivateKey/Create.php +++ b/app/Livewire/Security/PrivateKey/Create.php @@ -10,17 +10,22 @@ class Create extends Component { use WithRateLimiting; + public string $name; + public string $value; public ?string $from = null; + public ?string $description = null; + public ?string $publicKey = null; protected $rules = [ 'name' => 'required|string', 'value' => 'required|string', ]; + protected $validationAttributes = [ 'name' => 'name', 'value' => 'private Key', @@ -33,10 +38,11 @@ public function generateNewRSAKey() $this->name = generate_random_name(); $this->description = 'Created by Coolify'; ['private' => $this->value, 'public' => $this->publicKey] = generateSSHKey(); - } catch(\Throwable $e) { + } catch (\Throwable $e) { return handleError($e, $this); } } + public function generateNewEDKey() { try { @@ -44,42 +50,45 @@ public function generateNewEDKey() $this->name = generate_random_name(); $this->description = 'Created by Coolify'; ['private' => $this->value, 'public' => $this->publicKey] = generateSSHKey('ed25519'); - } catch(\Throwable $e) { + } catch (\Throwable $e) { return handleError($e, $this); } } + public function updated($updateProperty) { if ($updateProperty === 'value') { try { - $this->publicKey = PublicKeyLoader::load($this->$updateProperty)->getPublicKey()->toString('OpenSSH',['comment' => '']); + $this->publicKey = PublicKeyLoader::load($this->$updateProperty)->getPublicKey()->toString('OpenSSH', ['comment' => '']); } catch (\Throwable $e) { - if ($this->$updateProperty === "") { - $this->publicKey = ""; + if ($this->$updateProperty === '') { + $this->publicKey = ''; } else { - $this->publicKey = "Invalid private key"; + $this->publicKey = 'Invalid private key'; } } } $this->validateOnly($updateProperty); } + public function createPrivateKey() { $this->validate(); try { $this->value = trim($this->value); - if (!str_ends_with($this->value, "\n")) { + if (! str_ends_with($this->value, "\n")) { $this->value .= "\n"; } $private_key = PrivateKey::create([ 'name' => $this->name, 'description' => $this->description, 'private_key' => $this->value, - 'team_id' => currentTeam()->id + 'team_id' => currentTeam()->id, ]); if ($this->from === 'server') { return redirect()->route('dashboard'); } + return redirect()->route('security.private-key.show', ['private_key_uuid' => $private_key->uuid]); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Security/PrivateKey/Show.php b/app/Livewire/Security/PrivateKey/Show.php index 0a292731b..d86bd5d1e 100644 --- a/app/Livewire/Security/PrivateKey/Show.php +++ b/app/Livewire/Security/PrivateKey/Show.php @@ -8,36 +8,43 @@ class Show extends Component { public PrivateKey $private_key; - public $public_key = "Loading..."; + + public $public_key = 'Loading...'; + protected $rules = [ 'private_key.name' => 'required|string', 'private_key.description' => 'nullable|string', 'private_key.private_key' => 'required|string', - 'private_key.is_git_related' => 'nullable|boolean' + 'private_key.is_git_related' => 'nullable|boolean', ]; + protected $validationAttributes = [ 'private_key.name' => 'name', 'private_key.description' => 'description', - 'private_key.private_key' => 'private key' + 'private_key.private_key' => 'private key', ]; public function mount() { try { $this->private_key = PrivateKey::ownedByCurrentTeam(['name', 'description', 'private_key', 'is_git_related'])->whereUuid(request()->private_key_uuid)->firstOrFail(); - }catch(\Throwable $e) { + } catch (\Throwable $e) { return handleError($e, $this); } } - public function loadPublicKey() { + + public function loadPublicKey() + { $this->public_key = $this->private_key->publicKey(); } + public function delete() { try { if ($this->private_key->isEmpty()) { $this->private_key->delete(); currentTeam()->privateKeys = PrivateKey::where('team_id', currentTeam()->id)->get(); + return redirect()->route('security.private-key.index'); } $this->dispatch('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.'); diff --git a/app/Livewire/Server/ConfigureCloudflareTunnels.php b/app/Livewire/Server/ConfigureCloudflareTunnels.php index 03a48c3e1..7d2103e37 100644 --- a/app/Livewire/Server/ConfigureCloudflareTunnels.php +++ b/app/Livewire/Server/ConfigureCloudflareTunnels.php @@ -9,8 +9,11 @@ class ConfigureCloudflareTunnels extends Component { public $server_id; + public string $cloudflare_token; + public string $ssh_domain; + public function alreadyConfigured() { try { @@ -23,6 +26,7 @@ public function alreadyConfigured() return handleError($e, $this); } } + public function submit() { try { @@ -34,10 +38,11 @@ public function submit() $server->settings->save(); $this->dispatch('success', 'Cloudflare Tunnels configured successfully.'); $this->dispatch('serverInstalled'); - } catch(\Throwable $e) { + } catch (\Throwable $e) { return handleError($e, $this); } } + public function render() { return view('livewire.server.configure-cloudflare-tunnels'); diff --git a/app/Livewire/Server/Create.php b/app/Livewire/Server/Create.php index 2f30caf0e..2d4ba4430 100644 --- a/app/Livewire/Server/Create.php +++ b/app/Livewire/Server/Create.php @@ -9,16 +9,20 @@ class Create extends Component { public $private_keys = []; + public bool $limit_reached = false; + public function mount() { $this->private_keys = PrivateKey::ownedByCurrentTeam()->get(); - if (!isCloud()) { + if (! isCloud()) { $this->limit_reached = false; + return; } $this->limit_reached = Team::serverLimitReached(); } + public function render() { return view('livewire.server.create'); diff --git a/app/Livewire/Server/Delete.php b/app/Livewire/Server/Delete.php index 3333283eb..3beec0c91 100644 --- a/app/Livewire/Server/Delete.php +++ b/app/Livewire/Server/Delete.php @@ -10,20 +10,24 @@ class Delete extends Component use AuthorizesRequests; public $server; + public function delete() { try { $this->authorize('delete', $this->server); if ($this->server->hasDefinedResources()) { $this->dispatch('error', 'Server has defined resources. Please delete them first.'); + return; } $this->server->delete(); + return redirect()->route('server.index'); } catch (\Throwable $e) { return handleError($e, $this); } } + public function render() { return view('livewire.server.delete'); diff --git a/app/Livewire/Server/Destination/Show.php b/app/Livewire/Server/Destination/Show.php index 4e0f54296..986e16cbf 100644 --- a/app/Livewire/Server/Destination/Show.php +++ b/app/Livewire/Server/Destination/Show.php @@ -8,7 +8,9 @@ class Show extends Component { public ?Server $server = null; + public $parameters = []; + public function mount() { $this->parameters = get_route_parameters(); @@ -21,6 +23,7 @@ public function mount() return handleError($e, $this); } } + public function render() { return view('livewire.server.destination.show'); diff --git a/app/Livewire/Server/Form.php b/app/Livewire/Server/Form.php index 44f016aca..263ff6367 100644 --- a/app/Livewire/Server/Form.php +++ b/app/Livewire/Server/Form.php @@ -8,11 +8,17 @@ class Form extends Component { public Server $server; + public bool $isValidConnection = false; + public bool $isValidDocker = false; + public ?string $wildcard_domain = null; + public int $cleanup_after_percentage; + public bool $dockerInstallationStarted = false; + public bool $revalidate = false; protected $listeners = ['serverInstalled', 'revalidate' => '$refresh']; @@ -32,6 +38,7 @@ class Form extends Component 'server.settings.dynamic_timeout' => 'required|integer|min:1', 'wildcard_domain' => 'nullable|url', ]; + protected $validationAttributes = [ 'server.name' => 'Name', 'server.description' => 'Description', @@ -53,17 +60,20 @@ public function mount() $this->wildcard_domain = $this->server->settings->wildcard_domain; $this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage; } + public function serverInstalled() { $this->server->refresh(); $this->server->settings->refresh(); } + public function updatedServerSettingsIsBuildServer() { $this->dispatch('serverInstalled'); $this->dispatch('serverRefresh'); $this->dispatch('proxyStatusUpdated'); } + public function instantSave() { try { @@ -75,10 +85,12 @@ public function instantSave() return handleError($e, $this); } } + public function revalidate() { $this->revalidate = true; } + public function checkLocalhostConnection() { $this->submit(); @@ -90,10 +102,12 @@ public function checkLocalhostConnection() $this->server->settings->save(); $this->dispatch('proxyStatusUpdated'); } else { - $this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.

Check this documentation for further help.

Error: ' . $error); + $this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.

Check this documentation for further help.

Error: '.$error); + return; } } + public function validateServer($install = true) { $this->dispatch('init', $install); @@ -101,7 +115,7 @@ public function validateServer($install = true) public function submit() { - if (isCloud() && !isDev()) { + if (isCloud() && ! isDev()) { $this->validate(); $this->validate([ 'server.ip' => 'required', @@ -114,6 +128,7 @@ public function submit() })->pluck('ip')->toArray(); if (in_array($this->server->ip, $uniqueIPs)) { $this->dispatch('error', 'IP address is already in use by another team.'); + return; } refresh_server_connection($this->server->privateKey); diff --git a/app/Livewire/Server/Index.php b/app/Livewire/Server/Index.php index 45bb1c3e1..74764960a 100644 --- a/app/Livewire/Server/Index.php +++ b/app/Livewire/Server/Index.php @@ -10,9 +10,11 @@ class Index extends Component { public ?Collection $servers = null; - public function mount () { + public function mount() + { $this->servers = Server::ownedByCurrentTeam()->get(); } + public function render() { return view('livewire.server.index'); diff --git a/app/Livewire/Server/LogDrains.php b/app/Livewire/Server/LogDrains.php index 4eca682d4..3d7b34de1 100644 --- a/app/Livewire/Server/LogDrains.php +++ b/app/Livewire/Server/LogDrains.php @@ -9,7 +9,9 @@ class LogDrains extends Component { public Server $server; + public $parameters = []; + protected $rules = [ 'server.settings.is_logdrain_newrelic_enabled' => 'required|boolean', 'server.settings.logdrain_newrelic_license_key' => 'required|string', @@ -23,6 +25,7 @@ class LogDrains extends Component 'server.settings.logdrain_custom_config' => 'required|string', 'server.settings.logdrain_custom_config_parser' => 'nullable', ]; + protected $validationAttributes = [ 'server.settings.is_logdrain_newrelic_enabled' => 'New Relic log drain', 'server.settings.logdrain_newrelic_license_key' => 'New Relic license key', @@ -50,13 +53,15 @@ public function mount() return handleError($e, $this); } } + public function configureLogDrain() { try { InstallLogDrain::run($this->server); - if (!$this->server->isLogDrainEnabled()) { + if (! $this->server->isLogDrainEnabled()) { $this->dispatch('serverRefresh'); $this->dispatch('success', 'Log drain service stopped.'); + return; } $this->dispatch('serverRefresh'); @@ -65,11 +70,12 @@ public function configureLogDrain() return handleError($e, $this); } } + public function instantSave(string $type) { try { $ok = $this->submit($type); - if (!$ok) { + if (! $ok) { return; } $this->configureLogDrain(); @@ -77,6 +83,7 @@ public function instantSave(string $type) return handleError($e, $this); } } + public function submit(string $type) { try { @@ -92,7 +99,7 @@ public function submit(string $type) 'is_logdrain_axiom_enabled' => false, 'is_logdrain_custom_enabled' => false, ]); - } else if ($type === 'highlight') { + } elseif ($type === 'highlight') { $this->validate([ 'server.settings.is_logdrain_highlight_enabled' => 'required|boolean', 'server.settings.logdrain_highlight_project_id' => 'required|string', @@ -102,7 +109,7 @@ public function submit(string $type) 'is_logdrain_axiom_enabled' => false, 'is_logdrain_custom_enabled' => false, ]); - } else if ($type === 'axiom') { + } elseif ($type === 'axiom') { $this->validate([ 'server.settings.is_logdrain_axiom_enabled' => 'required|boolean', 'server.settings.logdrain_axiom_dataset_name' => 'required|string', @@ -113,7 +120,7 @@ public function submit(string $type) 'is_logdrain_highlight_enabled' => false, 'is_logdrain_custom_enabled' => false, ]); - } else if ($type === 'custom') { + } elseif ($type === 'custom') { $this->validate([ 'server.settings.is_logdrain_custom_enabled' => 'required|boolean', 'server.settings.logdrain_custom_config' => 'required|string', @@ -127,29 +134,32 @@ public function submit(string $type) } $this->server->settings->save(); $this->dispatch('success', 'Settings saved.'); + return true; } catch (\Throwable $e) { if ($type === 'newrelic') { $this->server->settings->update([ 'is_logdrain_newrelic_enabled' => false, ]); - } else if ($type === 'highlight') { + } elseif ($type === 'highlight') { $this->server->settings->update([ 'is_logdrain_highlight_enabled' => false, ]); - } else if ($type === 'axiom') { + } elseif ($type === 'axiom') { $this->server->settings->update([ 'is_logdrain_axiom_enabled' => false, ]); - } else if ($type === 'custom') { + } elseif ($type === 'custom') { $this->server->settings->update([ 'is_logdrain_custom_enabled' => false, ]); } handleError($e, $this); + return false; } } + public function render() { return view('livewire.server.log-drains'); diff --git a/app/Livewire/Server/New/ByIp.php b/app/Livewire/Server/New/ByIp.php index c56e9bec6..0aad33b1c 100644 --- a/app/Livewire/Server/New/ByIp.php +++ b/app/Livewire/Server/New/ByIp.php @@ -11,24 +11,37 @@ class ByIp extends Component { public $private_keys; + public $limit_reached; + public ?int $private_key_id = null; + public $new_private_key_name; + public $new_private_key_description; + public $new_private_key_value; public string $name; + public ?string $description = null; + public string $ip; + public string $user = 'root'; + public int $port = 22; + public bool $is_swarm_manager = false; + public bool $is_swarm_worker = false; + public $selected_swarm_cluster = null; public bool $is_build_server = false; public $swarm_managers = []; + protected $rules = [ 'name' => 'required|string', 'description' => 'nullable|string', @@ -39,6 +52,7 @@ class ByIp extends Component 'is_swarm_worker' => 'required|boolean', 'is_build_server' => 'required|boolean', ]; + protected $validationAttributes = [ 'name' => 'Name', 'description' => 'Description', @@ -90,8 +104,8 @@ public function submit() 'private_key_id' => $this->private_key_id, 'proxy' => [ // set default proxy type to traefik v2 - "type" => ProxyTypes::TRAEFIK_V2->value, - "status" => ProxyStatus::EXITED->value, + 'type' => ProxyTypes::TRAEFIK_V2->value, + 'status' => ProxyStatus::EXITED->value, ], ]; if ($this->is_swarm_worker) { @@ -111,6 +125,7 @@ public function submit() $server->settings->is_build_server = $this->is_build_server; $server->settings->save(); $server->addInitialNetwork(); + return redirect()->route('server.show', $server->uuid); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Server/PrivateKey/Show.php b/app/Livewire/Server/PrivateKey/Show.php index 71dea7c9d..0ad820428 100644 --- a/app/Livewire/Server/PrivateKey/Show.php +++ b/app/Livewire/Server/PrivateKey/Show.php @@ -9,8 +9,11 @@ class Show extends Component { public ?Server $server = null; + public $privateKeys = []; + public $parameters = []; + public function mount() { $this->parameters = get_route_parameters(); @@ -24,6 +27,7 @@ public function mount() return handleError($e, $this); } } + public function render() { return view('livewire.server.private-key.show'); diff --git a/app/Livewire/Server/Proxy.php b/app/Livewire/Server/Proxy.php index dab7f54be..8d1ece1c6 100644 --- a/app/Livewire/Server/Proxy.php +++ b/app/Livewire/Server/Proxy.php @@ -6,15 +6,17 @@ use App\Actions\Proxy\SaveConfiguration; use App\Actions\Proxy\StartProxy; use App\Models\Server; -use Livewire\Component; use Illuminate\Support\Str; +use Livewire\Component; class Proxy extends Component { public Server $server; public ?string $selectedProxy = null; + public $proxy_settings = null; + public ?string $redirect_url = null; protected $listeners = ['proxyStatusUpdated', 'saveConfiguration' => 'submit']; diff --git a/app/Livewire/Server/Proxy/Deploy.php b/app/Livewire/Server/Proxy/Deploy.php index 5587451a4..6d3f00dc8 100644 --- a/app/Livewire/Server/Proxy/Deploy.php +++ b/app/Livewire/Server/Proxy/Deploy.php @@ -11,20 +11,24 @@ class Deploy extends Component { public Server $server; + public bool $traefikDashboardAvailable = false; + public ?string $currentRoute = null; + public ?string $serverIp = null; public function getListeners() { $teamId = auth()->user()->currentTeam()->id; + return [ "echo-private:team.{$teamId},ProxyStatusChanged" => 'proxyStarted', 'proxyStatusUpdated', 'traefikDashboardAvailable', 'serverRefresh' => 'proxyStatusUpdated', - "checkProxy", - "startProxy" + 'checkProxy', + 'startProxy', ]; } @@ -37,19 +41,23 @@ public function mount() } $this->currentRoute = request()->route()->getName(); } + public function traefikDashboardAvailable(bool $data) { $this->traefikDashboardAvailable = $data; } + public function proxyStarted() { CheckProxy::run($this->server, true); $this->dispatch('success', 'Proxy started.'); } + public function proxyStatusUpdated() { $this->server->refresh(); } + public function restart() { try { @@ -59,6 +67,7 @@ public function restart() return handleError($e, $this); } } + public function checkProxy() { try { @@ -69,6 +78,7 @@ public function checkProxy() return handleError($e, $this); } } + public function startProxy() { try { @@ -86,11 +96,11 @@ public function stop() try { if ($this->server->isSwarm()) { instant_remote_process([ - "docker service rm coolify-proxy_traefik", + 'docker service rm coolify-proxy_traefik', ], $this->server); } else { instant_remote_process([ - "docker rm -f coolify-proxy", + 'docker rm -f coolify-proxy', ], $this->server); } $this->server->proxy->status = 'exited'; diff --git a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php index a9c01daed..392ad38fa 100644 --- a/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php +++ b/app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php @@ -8,17 +8,22 @@ class DynamicConfigurationNavbar extends Component { public $server_id; + public $fileName = ''; + public $value = ''; + public $newFile = false; + public function delete(string $fileName) { $server = Server::ownedByCurrentTeam()->whereId($this->server_id)->first(); $proxy_path = $server->proxyPath(); $proxy_type = $server->proxyType(); $file = str_replace('|', '.', $fileName); - if ($proxy_type === 'CADDY' && $file === "Caddyfile") { + if ($proxy_type === 'CADDY' && $file === 'Caddyfile') { $this->dispatch('error', 'Cannot delete Caddyfile.'); + return; } instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $server); @@ -29,6 +34,7 @@ public function delete(string $fileName) $this->dispatch('loadDynamicConfigurations'); $this->dispatch('refresh'); } + public function render() { return view('livewire.server.proxy.dynamic-configuration-navbar'); diff --git a/app/Livewire/Server/Proxy/DynamicConfigurations.php b/app/Livewire/Server/Proxy/DynamicConfigurations.php index ae84ce949..c858481db 100644 --- a/app/Livewire/Server/Proxy/DynamicConfigurations.php +++ b/app/Livewire/Server/Proxy/DynamicConfigurations.php @@ -9,25 +9,31 @@ class DynamicConfigurations extends Component { public ?Server $server = null; + public $parameters = []; + public Collection $contents; + public function getListeners() { $teamId = auth()->user()->currentTeam()->id; + return [ "echo-private:team.{$teamId},ProxyStatusChanged" => 'loadDynamicConfigurations', 'loadDynamicConfigurations', - 'refresh' => '$refresh' + 'refresh' => '$refresh', ]; } + protected $rules = [ 'contents.*' => 'nullable|string', ]; + public function loadDynamicConfigurations() { $proxy_path = $this->server->proxyPath(); $files = instant_remote_process(["mkdir -p $proxy_path/dynamic && ls -1 {$proxy_path}/dynamic"], $this->server); - $files = collect(explode("\n", $files))->filter(fn ($file) => !empty($file)); + $files = collect(explode("\n", $files))->filter(fn ($file) => ! empty($file)); $files = $files->map(fn ($file) => trim($file)); $files = $files->sort(); $contents = collect([]); @@ -38,6 +44,7 @@ public function loadDynamicConfigurations() $this->contents = $contents; $this->dispatch('refresh'); } + public function mount() { $this->parameters = get_route_parameters(); @@ -50,6 +57,7 @@ public function mount() return handleError($e, $this); } } + public function render() { return view('livewire.server.proxy.dynamic-configurations'); diff --git a/app/Livewire/Server/Proxy/Logs.php b/app/Livewire/Server/Proxy/Logs.php index 7949b0086..8e0f40c54 100644 --- a/app/Livewire/Server/Proxy/Logs.php +++ b/app/Livewire/Server/Proxy/Logs.php @@ -8,7 +8,9 @@ class Logs extends Component { public ?Server $server = null; + public $parameters = []; + public function mount() { $this->parameters = get_route_parameters(); @@ -21,6 +23,7 @@ public function mount() return handleError($e, $this); } } + public function render() { return view('livewire.server.proxy.logs'); diff --git a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php index 8110986a9..e5de6eda0 100644 --- a/app/Livewire/Server/Proxy/NewDynamicConfiguration.php +++ b/app/Livewire/Server/Proxy/NewDynamicConfiguration.php @@ -3,18 +3,23 @@ namespace App\Livewire\Server\Proxy; use App\Models\Server; -use Illuminate\Routing\Route; use Livewire\Component; use Symfony\Component\Yaml\Yaml; class NewDynamicConfiguration extends Component { public string $fileName = ''; + public string $value = ''; + public bool $newFile = false; + public Server $server; + public $server_id; + public $parameters = []; + public function mount() { $this->parameters = get_route_parameters(); @@ -22,6 +27,7 @@ public function mount() $this->fileName = str_replace('|', '.', $this->fileName); } } + public function addDynamicConfiguration() { try { @@ -32,7 +38,7 @@ public function addDynamicConfiguration() if (data_get($this->parameters, 'server_uuid')) { $this->server = Server::ownedByCurrentTeam()->whereUuid(data_get($this->parameters, 'server_uuid'))->first(); } - if (!is_null($this->server_id)) { + if (! is_null($this->server_id)) { $this->server = Server::ownedByCurrentTeam()->whereId($this->server_id)->first(); } if (is_null($this->server)) { @@ -40,15 +46,16 @@ public function addDynamicConfiguration() } $proxy_type = $this->server->proxyType(); if ($proxy_type === 'TRAEFIK_V2') { - if (!str($this->fileName)->endsWith('.yaml') && !str($this->fileName)->endsWith('.yml')) { + if (! str($this->fileName)->endsWith('.yaml') && ! str($this->fileName)->endsWith('.yml')) { $this->fileName = "{$this->fileName}.yaml"; } if ($this->fileName === 'coolify.yaml') { $this->dispatch('error', 'File name is reserved.'); + return; } - } else if ($proxy_type === 'CADDY') { - if (!str($this->fileName)->endsWith('.caddy')) { + } elseif ($proxy_type === 'CADDY') { + if (! str($this->fileName)->endsWith('.caddy')) { $this->fileName = "{$this->fileName}.caddy"; } } @@ -58,6 +65,7 @@ public function addDynamicConfiguration() $exists = instant_remote_process(["test -f $file && echo 1 || echo 0"], $this->server); if ($exists == 1) { $this->dispatch('error', 'File already exists'); + return; } } @@ -80,6 +88,7 @@ public function addDynamicConfiguration() return handleError($e, $this); } } + public function render() { return view('livewire.server.proxy.new-dynamic-configuration'); diff --git a/app/Livewire/Server/Proxy/Show.php b/app/Livewire/Server/Proxy/Show.php index 7e21e3344..cef909a45 100644 --- a/app/Livewire/Server/Proxy/Show.php +++ b/app/Livewire/Server/Proxy/Show.php @@ -8,12 +8,16 @@ class Show extends Component { public ?Server $server = null; + public $parameters = []; + protected $listeners = ['proxyStatusUpdated']; + public function proxyStatusUpdated() { $this->server->refresh(); } + public function mount() { $this->parameters = get_route_parameters(); @@ -26,6 +30,7 @@ public function mount() return handleError($e, $this); } } + public function render() { return view('livewire.server.proxy.show'); diff --git a/app/Livewire/Server/Proxy/Status.php b/app/Livewire/Server/Proxy/Status.php index fbc16fde4..8dd4dd8e6 100644 --- a/app/Livewire/Server/Proxy/Status.php +++ b/app/Livewire/Server/Proxy/Status.php @@ -11,18 +11,23 @@ class Status extends Component { public Server $server; + public bool $polling = false; + public int $numberOfPolls = 0; + protected $listeners = ['proxyStatusUpdated' => '$refresh', 'startProxyPolling']; public function startProxyPolling() { $this->checkProxy(); } + public function proxyStatusUpdated() { $this->server->refresh(); } + public function checkProxy(bool $notification = false) { try { @@ -31,6 +36,7 @@ public function checkProxy(bool $notification = false) $this->polling = false; $this->numberOfPolls = 0; $notification && $this->dispatch('error', 'Proxy is not running.'); + return; } $this->numberOfPolls++; @@ -47,6 +53,7 @@ public function checkProxy(bool $notification = false) return handleError($e, $this); } } + public function getProxyStatus() { try { diff --git a/app/Livewire/Server/Resources.php b/app/Livewire/Server/Resources.php index 1c8a8267e..800344ac3 100644 --- a/app/Livewire/Server/Resources.php +++ b/app/Livewire/Server/Resources.php @@ -10,45 +10,61 @@ class Resources extends Component { use AuthorizesRequests; + public ?Server $server = null; + public $parameters = []; + public Collection $unmanagedContainers; + public function getListeners() { $teamId = auth()->user()->currentTeam()->id; + return [ "echo-private:team.{$teamId},ApplicationStatusChanged" => 'refreshStatus', ]; } - public function startUnmanaged($id) { + public function startUnmanaged($id) + { $this->server->startUnmanaged($id); $this->dispatch('success', 'Container started.'); $this->loadUnmanagedContainers(); } - public function restartUnmanaged($id) { + + public function restartUnmanaged($id) + { $this->server->restartUnmanaged($id); $this->dispatch('success', 'Container restarted.'); $this->loadUnmanagedContainers(); } - public function stopUnmanaged($id) { + + public function stopUnmanaged($id) + { $this->server->stopUnmanaged($id); $this->dispatch('success', 'Container stopped.'); $this->loadUnmanagedContainers(); } - public function refreshStatus() { + + public function refreshStatus() + { $this->server->refresh(); $this->loadUnmanagedContainers(); $this->dispatch('success', 'Resource statuses refreshed.'); } - public function loadUnmanagedContainers() { + + public function loadUnmanagedContainers() + { try { $this->unmanagedContainers = $this->server->loadUnmanagedContainers(); } catch (\Throwable $e) { return handleError($e, $this); } } - public function mount() { + + public function mount() + { $this->unmanagedContainers = collect(); $this->parameters = get_route_parameters(); try { @@ -60,6 +76,7 @@ public function mount() { return handleError($e, $this); } } + public function render() { return view('livewire.server.resources'); diff --git a/app/Livewire/Server/Show.php b/app/Livewire/Server/Show.php index 92449820c..7ebf90115 100644 --- a/app/Livewire/Server/Show.php +++ b/app/Livewire/Server/Show.php @@ -9,9 +9,13 @@ class Show extends Component { use AuthorizesRequests; + public ?Server $server = null; + public $parameters = []; + protected $listeners = ['serverInstalled' => '$refresh']; + public function mount() { $this->parameters = get_route_parameters(); @@ -24,10 +28,12 @@ public function mount() return handleError($e, $this); } } + public function submit() { $this->dispatch('serverRefresh', false); } + public function render() { return view('livewire.server.show'); diff --git a/app/Livewire/Server/ShowPrivateKey.php b/app/Livewire/Server/ShowPrivateKey.php index e0474f2c4..578a08967 100644 --- a/app/Livewire/Server/ShowPrivateKey.php +++ b/app/Livewire/Server/ShowPrivateKey.php @@ -8,7 +8,9 @@ class ShowPrivateKey extends Component { public Server $server; + public $privateKeys; + public $parameters; public function setPrivateKey($newPrivateKeyId) @@ -17,17 +19,18 @@ public function setPrivateKey($newPrivateKeyId) $oldPrivateKeyId = $this->server->private_key_id; refresh_server_connection($this->server->privateKey); $this->server->update([ - 'private_key_id' => $newPrivateKeyId + 'private_key_id' => $newPrivateKeyId, ]); $this->server->refresh(); refresh_server_connection($this->server->privateKey); $this->checkConnection(); } catch (\Throwable $e) { $this->server->update([ - 'private_key_id' => $oldPrivateKeyId + 'private_key_id' => $oldPrivateKeyId, ]); $this->server->refresh(); refresh_server_connection($this->server->privateKey); + return handleError($e, $this); } } @@ -41,6 +44,7 @@ public function checkConnection() } else { ray($error); $this->dispatch('error', 'Server is not reachable.
Please validate your configuration and connection.

Check this documentation for further help.'); + return; } } catch (\Throwable $e) { diff --git a/app/Livewire/Server/ValidateAndInstall.php b/app/Livewire/Server/ValidateAndInstall.php index aef7b800c..bd33937e0 100644 --- a/app/Livewire/Server/ValidateAndInstall.php +++ b/app/Livewire/Server/ValidateAndInstall.php @@ -10,16 +10,27 @@ class ValidateAndInstall extends Component { public Server $server; + public int $number_of_tries = 0; + public int $max_tries = 3; + public bool $install = true; + public $uptime = null; + public $supported_os_type = null; + public $docker_installed = null; + public $docker_compose_installed = null; + public $docker_version = null; + public $proxy_started = false; + public $error = null; + public bool $ask = false; protected $listeners = [ @@ -42,15 +53,17 @@ public function init(int $data = 0) $this->proxy_started = null; $this->error = null; $this->number_of_tries = $data; - if (!$this->ask) { + if (! $this->ask) { $this->dispatch('validateConnection'); } } + public function startValidatingAfterAsking() { $this->ask = false; $this->init(); } + public function startProxy() { try { @@ -60,7 +73,7 @@ public function startProxy() if ($proxy === 'OK') { $this->proxy_started = true; } else { - throw new \Exception("Proxy could not be started."); + throw new \Exception('Proxy could not be started.'); } } else { $this->proxy_started = true; @@ -69,32 +82,38 @@ public function startProxy() return handleError($e, $this); } } + public function validateConnection() { ['uptime' => $this->uptime, 'error' => $error] = $this->server->validateConnection(); - if (!$this->uptime) { - $this->error = 'Server is not reachable. Please validate your configuration and connection.

Check this documentation for further help.

Error: ' . $error; + if (! $this->uptime) { + $this->error = 'Server is not reachable. Please validate your configuration and connection.

Check this documentation for further help.

Error: '.$error; + return; } $this->dispatch('validateOS'); } + public function validateOS() { $this->supported_os_type = $this->server->validateOS(); - if (!$this->supported_os_type) { + if (! $this->supported_os_type) { $this->error = 'Server OS type is not supported. Please install Docker manually before continuing: documentation.'; + return; } $this->dispatch('validateDockerEngine'); } + public function validateDockerEngine() { $this->docker_installed = $this->server->validateDockerEngine(); $this->docker_compose_installed = $this->server->validateDockerCompose(); - if (!$this->docker_installed || !$this->docker_compose_installed) { + if (! $this->docker_installed || ! $this->docker_compose_installed) { if ($this->install) { if ($this->number_of_tries == $this->max_tries) { $this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: documentation.'; + return; } else { if ($this->number_of_tries <= $this->max_tries) { @@ -102,15 +121,18 @@ public function validateDockerEngine() $this->number_of_tries++; $this->dispatch('newActivityMonitor', $activity->id, 'init', $this->number_of_tries); } + return; } } else { $this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: documentation.'; + return; } } $this->dispatch('validateDockerVersion'); } + public function validateDockerVersion() { if ($this->server->isSwarm()) { @@ -125,6 +147,7 @@ public function validateDockerVersion() $this->dispatch('success', 'Server validated.'); } else { $this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: documentation.'; + return; } } @@ -134,6 +157,7 @@ public function validateDockerVersion() } $this->dispatch('startProxy'); } + public function render() { return view('livewire.server.validate-and-install'); diff --git a/app/Livewire/Settings/Auth.php b/app/Livewire/Settings/Auth.php index 100f99a73..783b163e0 100644 --- a/app/Livewire/Settings/Auth.php +++ b/app/Livewire/Settings/Auth.php @@ -2,41 +2,49 @@ namespace App\Livewire\Settings; -use Livewire\Component; use App\Models\OauthSetting; +use Livewire\Component; -class Auth extends Component { +class Auth extends Component +{ public $oauth_settings_map; - protected function rules() { - return OauthSetting::all()->reduce(function($carry, $setting) { + protected function rules() + { + return OauthSetting::all()->reduce(function ($carry, $setting) { $carry["oauth_settings_map.$setting->provider.enabled"] = 'required'; $carry["oauth_settings_map.$setting->provider.client_id"] = 'nullable'; $carry["oauth_settings_map.$setting->provider.client_secret"] = 'nullable'; $carry["oauth_settings_map.$setting->provider.redirect_uri"] = 'nullable'; $carry["oauth_settings_map.$setting->provider.tenant"] = 'nullable'; + return $carry; }, []); } - public function mount() { - $this->oauth_settings_map = OauthSetting::all()->sortBy('provider')->reduce(function($carry, $setting) { + public function mount() + { + $this->oauth_settings_map = OauthSetting::all()->sortBy('provider')->reduce(function ($carry, $setting) { $carry[$setting->provider] = $setting; + return $carry; }, []); } - private function updateOauthSettings() { + private function updateOauthSettings() + { foreach (array_values($this->oauth_settings_map) as &$setting) { $setting->save(); } } - public function instantSave() { + public function instantSave() + { $this->updateOauthSettings(); } - public function submit() { + public function submit() + { $this->updateOauthSettings(); $this->dispatch('success', 'Instance settings updated successfully!'); } diff --git a/app/Livewire/Settings/Backup.php b/app/Livewire/Settings/Backup.php index 82b3075c0..08ad04b2d 100644 --- a/app/Livewire/Settings/Backup.php +++ b/app/Livewire/Settings/Backup.php @@ -13,9 +13,13 @@ class Backup extends Component { public InstanceSettings $settings; + public $s3s; + public StandalonePostgresql|null|array $database = []; + public ScheduledDatabaseBackup|null|array $backup = []; + public $executions = []; protected $rules = [ @@ -26,6 +30,7 @@ class Backup extends Component 'database.postgres_password' => 'required', ]; + protected $validationAttributes = [ 'database.uuid' => 'uuid', 'database.name' => 'name', @@ -39,6 +44,7 @@ public function mount() $this->backup = $this->database?->scheduledBackups->first() ?? null; $this->executions = $this->backup?->executions ?? []; } + public function add_coolify_database() { try { @@ -83,6 +89,7 @@ public function backup_now() )); $this->dispatch('success', 'Backup queued. It will be available in a few minutes.'); } + public function submit() { $this->dispatch('success', 'Backup updated.'); diff --git a/app/Livewire/Settings/Configuration.php b/app/Livewire/Settings/Configuration.php index 68dc59a7f..4dfa16e30 100644 --- a/app/Livewire/Settings/Configuration.php +++ b/app/Livewire/Settings/Configuration.php @@ -9,12 +9,18 @@ class Configuration extends Component { public ModelsInstanceSettings $settings; + public bool $do_not_track; + public bool $is_auto_update_enabled; + public bool $is_registration_enabled; + public bool $is_dns_validation_enabled; + // public bool $next_channel; protected string $dynamic_config_path = '/data/coolify/proxy/dynamic'; + protected Server $server; protected $rules = [ @@ -24,6 +30,7 @@ class Configuration extends Component 'settings.public_port_max' => 'required', 'settings.custom_dns_servers' => 'nullable', ]; + protected $validationAttributes = [ 'settings.fqdn' => 'FQDN', 'settings.resale_license' => 'Resale License', @@ -65,17 +72,20 @@ public function submit() $this->resetErrorBag(); if ($this->settings->public_port_min > $this->settings->public_port_max) { $this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.'); + return; } $this->validate(); if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) { - if (!validate_dns_entry($this->settings->fqdn, $this->server)) { - $this->dispatch('error', "Validating DNS failed.

Make sure you have added the DNS records correctly.

{$this->settings->fqdn}->{$this->server->ip}

Check this documentation for further help."); + if (! validate_dns_entry($this->settings->fqdn, $this->server)) { + $this->dispatch('error', "Validating DNS failed.

Make sure you have added the DNS records correctly.

{$this->settings->fqdn}->{$this->server->ip}

Check this documentation for further help."); $error_show = true; } } - if ($this->settings->fqdn) check_domain_usage(domain: $this->settings->fqdn); + if ($this->settings->fqdn) { + check_domain_usage(domain: $this->settings->fqdn); + } $this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->replaceEnd(',', '')->trim(); $this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->trim()->explode(',')->map(function ($dns) { return str($dns)->trim()->lower(); @@ -85,7 +95,7 @@ public function submit() $this->settings->save(); $this->server->setupDynamicProxyConfiguration(); - if (!$error_show) { + if (! $error_show) { $this->dispatch('success', 'Instance settings updated successfully!'); } } catch (\Exception $e) { diff --git a/app/Livewire/Settings/Email.php b/app/Livewire/Settings/Email.php index 77b82df43..bd7f8201e 100644 --- a/app/Livewire/Settings/Email.php +++ b/app/Livewire/Settings/Email.php @@ -9,7 +9,9 @@ class Email extends Component { public InstanceSettings $settings; + public string $emails; + protected $rules = [ 'settings.smtp_enabled' => 'nullable|boolean', 'settings.smtp_host' => 'required', @@ -21,9 +23,10 @@ class Email extends Component 'settings.smtp_from_address' => 'required|email', 'settings.smtp_from_name' => 'required', 'settings.resend_enabled' => 'nullable|boolean', - 'settings.resend_api_key' => 'nullable' + 'settings.resend_api_key' => 'nullable', ]; + protected $validationAttributes = [ 'settings.smtp_from_address' => 'From Address', 'settings.smtp_from_name' => 'From Name', @@ -34,14 +37,16 @@ class Email extends Component 'settings.smtp_username' => 'Username', 'settings.smtp_password' => 'Password', 'settings.smtp_timeout' => 'Timeout', - 'settings.resend_api_key' => 'Resend API Key' + 'settings.resend_api_key' => 'Resend API Key', ]; + public function mount() { $this->emails = auth()->user()->email; } - public function submitFromFields() { + public function submitFromFields() + { try { $this->resetErrorBag(); $this->validate([ @@ -54,22 +59,27 @@ public function submitFromFields() { return handleError($e, $this); } } - public function submitResend() { + + public function submitResend() + { try { $this->resetErrorBag(); $this->validate([ 'settings.smtp_from_address' => 'required|email', 'settings.smtp_from_name' => 'required', - 'settings.resend_api_key' => 'required' + 'settings.resend_api_key' => 'required', ]); $this->settings->save(); $this->dispatch('success', 'Settings saved.'); } catch (\Throwable $e) { $this->settings->resend_enabled = false; + return handleError($e, $this); } } - public function instantSaveResend() { + + public function instantSaveResend() + { try { $this->settings->smtp_enabled = false; $this->submitResend(); @@ -77,6 +87,7 @@ public function instantSaveResend() { return handleError($e, $this); } } + public function instantSave() { try { diff --git a/app/Livewire/Settings/Index.php b/app/Livewire/Settings/Index.php index 0c1dd50e9..f6f918933 100644 --- a/app/Livewire/Settings/Index.php +++ b/app/Livewire/Settings/Index.php @@ -10,8 +10,11 @@ class Index extends Component { public InstanceSettings $settings; + public StandalonePostgresql $database; + public $s3s; + public function mount() { if (isInstanceAdmin()) { @@ -31,6 +34,7 @@ public function mount() return redirect()->route('dashboard'); } } + public function render() { return view('livewire.settings.index'); diff --git a/app/Livewire/Settings/License.php b/app/Livewire/Settings/License.php index e2ae5fcf7..212bc95be 100644 --- a/app/Livewire/Settings/License.php +++ b/app/Livewire/Settings/License.php @@ -9,29 +9,34 @@ class License extends Component { public InstanceSettings $settings; - public string|null $instance_id = null; + + public ?string $instance_id = null; protected $rules = [ 'settings.resale_license' => 'nullable', 'settings.is_resale_license_active' => 'nullable', ]; + protected $validationAttributes = [ 'settings.resale_license' => 'License', 'instance_id' => 'Instance Id (Do not change this)', 'settings.is_resale_license_active' => 'Is License Active', ]; - public function mount () { - if (!isCloud()) { + public function mount() + { + if (! isCloud()) { abort(404); } $this->instance_id = config('app.id'); $this->settings = InstanceSettings::get(); } + public function render() { return view('livewire.settings.license'); } + public function submit() { $this->validate(); @@ -41,8 +46,9 @@ public function submit() CheckResaleLicense::run(); $this->dispatch('reloadWindow'); } catch (\Throwable $e) { - session()->flash('error', 'Something went wrong. Please contact support.
Error: ' . $e->getMessage()); + session()->flash('error', 'Something went wrong. Please contact support.
Error: '.$e->getMessage()); ray($e->getMessage()); + return redirect()->route('settings.license'); } } diff --git a/app/Livewire/SharedVariables/Environment/Index.php b/app/Livewire/SharedVariables/Environment/Index.php index 34f33ef5d..3673a3882 100644 --- a/app/Livewire/SharedVariables/Environment/Index.php +++ b/app/Livewire/SharedVariables/Environment/Index.php @@ -9,9 +9,12 @@ class Index extends Component { public Collection $projects; - public function mount() { + + public function mount() + { $this->projects = Project::ownedByCurrentTeam()->get(); } + public function render() { return view('livewire.shared-variables.environment.index'); diff --git a/app/Livewire/SharedVariables/Environment/Show.php b/app/Livewire/SharedVariables/Environment/Show.php index 29fd91153..e025d8f7c 100644 --- a/app/Livewire/SharedVariables/Environment/Show.php +++ b/app/Livewire/SharedVariables/Environment/Show.php @@ -9,9 +9,13 @@ class Show extends Component { public Project $project; + public Application $application; + public $environment; + public array $parameters; + protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey']; public function saveKey($data) @@ -34,12 +38,14 @@ public function saveKey($data) return handleError($e, $this); } } + public function mount() { $this->parameters = get_route_parameters(); $this->project = Project::ownedByCurrentTeam()->where('uuid', request()->route('project_uuid'))->first(); $this->environment = $this->project->environments()->where('name', request()->route('environment_name'))->first(); } + public function render() { return view('livewire.shared-variables.environment.show'); diff --git a/app/Livewire/SharedVariables/Project/Index.php b/app/Livewire/SharedVariables/Project/Index.php index 39de974e8..570da74d3 100644 --- a/app/Livewire/SharedVariables/Project/Index.php +++ b/app/Livewire/SharedVariables/Project/Index.php @@ -9,9 +9,12 @@ class Index extends Component { public Collection $projects; - public function mount() { + + public function mount() + { $this->projects = Project::ownedByCurrentTeam()->get(); } + public function render() { return view('livewire.shared-variables.project.index'); diff --git a/app/Livewire/SharedVariables/Project/Show.php b/app/Livewire/SharedVariables/Project/Show.php index a172c52f0..8d4844442 100644 --- a/app/Livewire/SharedVariables/Project/Show.php +++ b/app/Livewire/SharedVariables/Project/Show.php @@ -8,6 +8,7 @@ class Show extends Component { public Project $project; + protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey']; public function saveKey($data) @@ -30,16 +31,18 @@ public function saveKey($data) return handleError($e, $this); } } + public function mount() { $projectUuid = request()->route('project_uuid'); $teamId = currentTeam()->id; $project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first(); - if (!$project) { + if (! $project) { return redirect()->route('dashboard'); } $this->project = $project; } + public function render() { return view('livewire.shared-variables.project.show'); diff --git a/app/Livewire/SharedVariables/Team/Index.php b/app/Livewire/SharedVariables/Team/Index.php index ef5c7472c..a3085304a 100644 --- a/app/Livewire/SharedVariables/Team/Index.php +++ b/app/Livewire/SharedVariables/Team/Index.php @@ -8,6 +8,7 @@ class Index extends Component { public Team $team; + protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey']; public function saveKey($data) @@ -35,6 +36,7 @@ public function mount() { $this->team = currentTeam(); } + public function render() { return view('livewire.shared-variables.team.index'); diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 8fd36adc5..ee28f8847 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -11,17 +11,25 @@ class Change extends Component { public string $webhook_endpoint; + public ?string $ipv4 = null; + public ?string $ipv6 = null; + public ?string $fqdn = null; public ?bool $default_permissions = true; + public ?bool $preview_deployment_permissions = true; + public ?bool $administration = false; public $parameters; + public ?GithubApp $github_app = null; + public string $name; + public bool $is_system_wide; public $applications; @@ -57,7 +65,6 @@ public function checkPermissions() // Need administration:read:write permission // https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-self-hosted-runners-for-a-repository - // $github_access_token = generate_github_installation_token($this->github_app); // $repositories = Http::withToken($github_access_token)->get("{$this->github_app->api_url}/installation/repositories?per_page=100"); // $runners_by_repository = collect([]); @@ -89,7 +96,7 @@ public function mount() { $github_app_uuid = request()->github_app_uuid; $this->github_app = GithubApp::where('uuid', $github_app_uuid)->first(); - if (!$this->github_app) { + if (! $this->github_app) { return redirect()->route('source.all'); } $this->applications = $this->github_app->applications; @@ -100,14 +107,14 @@ public function mount() $this->fqdn = $settings->fqdn; if ($settings->public_ipv4) { - $this->ipv4 = 'http://' . $settings->public_ipv4 . ':' . config('app.port'); + $this->ipv4 = 'http://'.$settings->public_ipv4.':'.config('app.port'); } if ($settings->public_ipv6) { - $this->ipv6 = 'http://' . $settings->public_ipv6 . ':' . config('app.port'); + $this->ipv6 = 'http://'.$settings->public_ipv6.':'.config('app.port'); } if ($this->github_app->installation_id && session('from')) { $source_id = data_get(session('from'), 'source_id'); - if (!$source_id || $this->github_app->id !== $source_id) { + if (! $source_id || $this->github_app->id !== $source_id) { session()->forget('from'); } else { $parameters = data_get(session('from'), 'parameters'); @@ -117,6 +124,7 @@ public function mount() $type = data_get($parameters, 'type'); $destination = data_get($parameters, 'destination'); session()->forget('from'); + return redirect()->route($back, [ 'environment_name' => $environment_name, 'project_uuid' => $project_uuid, @@ -126,7 +134,7 @@ public function mount() } } $this->parameters = get_route_parameters(); - if (isCloud() && !isDev()) { + if (isCloud() && ! isDev()) { $this->webhook_endpoint = config('app.url'); } else { $this->webhook_endpoint = $this->ipv4; @@ -176,9 +184,11 @@ public function delete() if ($this->github_app->applications->isNotEmpty()) { $this->dispatch('error', 'This source is being used by an application. Please delete all applications first.'); $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); + return; } $this->github_app->delete(); + return redirect()->route('source.all'); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Source/Github/Create.php b/app/Livewire/Source/Github/Create.php index 032fc9318..f85e8646e 100644 --- a/app/Livewire/Source/Github/Create.php +++ b/app/Livewire/Source/Github/Create.php @@ -8,11 +8,17 @@ class Create extends Component { public string $name; - public string|null $organization = null; + + public ?string $organization = null; + public string $api_url = 'https://api.github.com'; + public string $html_url = 'https://github.com'; + public string $custom_user = 'git'; + public int $custom_port = 22; + public bool $is_system_wide = false; public function mount() @@ -24,13 +30,13 @@ public function createGitHubApp() { try { $this->validate([ - "name" => 'required|string', - "organization" => 'nullable|string', - "api_url" => 'required|string', - "html_url" => 'required|string', - "custom_user" => 'required|string', - "custom_port" => 'required|int', - "is_system_wide" => 'required|bool', + 'name' => 'required|string', + 'organization' => 'nullable|string', + 'api_url' => 'required|string', + 'html_url' => 'required|string', + 'custom_user' => 'required|string', + 'custom_port' => 'required|int', + 'is_system_wide' => 'required|bool', ]); $payload = [ 'name' => $this->name, @@ -48,6 +54,7 @@ public function createGitHubApp() if (session('from')) { session(['from' => session('from') + ['source_id' => $github_app->id]]); } + return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Storage/Create.php b/app/Livewire/Storage/Create.php index 1b2510f5d..1ccc3997c 100644 --- a/app/Livewire/Storage/Create.php +++ b/app/Livewire/Storage/Create.php @@ -8,13 +8,21 @@ class Create extends Component { public string $name; + public string $description; + public string $region = 'us-east-1'; + public string $key; + public string $secret; + public string $bucket; + public string $endpoint; + public S3Storage $storage; + protected $rules = [ 'name' => 'required|min:3|max:255', 'description' => 'nullable|min:3|max:255', @@ -24,12 +32,13 @@ class Create extends Component 'bucket' => 'required|max:255', 'endpoint' => 'required|url|max:255', ]; + protected $validationAttributes = [ 'name' => 'Name', 'description' => 'Description', 'region' => 'Region', 'key' => 'Key', - 'secret' => "Secret", + 'secret' => 'Secret', 'bucket' => 'Bucket', 'endpoint' => 'Endpoint', ]; @@ -65,6 +74,7 @@ public function submit() $this->storage->team_id = currentTeam()->id; $this->storage->testConnection(); $this->storage->save(); + return redirect()->route('storage.show', $this->storage->uuid); } catch (\Throwable $e) { $this->dispatch('error', 'Failed to create storage.', $e->getMessage()); diff --git a/app/Livewire/Storage/Form.php b/app/Livewire/Storage/Form.php index 79c1f0c30..8ca0020c7 100644 --- a/app/Livewire/Storage/Form.php +++ b/app/Livewire/Storage/Form.php @@ -8,6 +8,7 @@ class Form extends Component { public S3Storage $storage; + protected $rules = [ 'storage.is_usable' => 'nullable|boolean', 'storage.name' => 'nullable|min:3|max:255', @@ -18,13 +19,14 @@ class Form extends Component 'storage.bucket' => 'required|max:255', 'storage.endpoint' => 'required|url|max:255', ]; + protected $validationAttributes = [ 'storage.is_usable' => 'Is Usable', 'storage.name' => 'Name', 'storage.description' => 'Description', 'storage.region' => 'Region', 'storage.key' => 'Key', - 'storage.secret' => "Secret", + 'storage.secret' => 'Secret', 'storage.bucket' => 'Bucket', 'storage.endpoint' => 'Endpoint', ]; @@ -33,6 +35,7 @@ public function test_s3_connection() { try { $this->storage->testConnection(shouldSave: true); + return $this->dispatch('success', 'Connection is working.', 'Tested with "ListObjectsV2" action.'); } catch (\Throwable $e) { $this->dispatch('error', 'Failed to create storage.', $e->getMessage()); @@ -43,6 +46,7 @@ public function delete() { try { $this->storage->delete(); + return redirect()->route('storage.index'); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Storage/Index.php b/app/Livewire/Storage/Index.php index f071a0af0..71ad89f70 100644 --- a/app/Livewire/Storage/Index.php +++ b/app/Livewire/Storage/Index.php @@ -8,9 +8,12 @@ class Index extends Component { public $s3; - public function mount() { + + public function mount() + { $this->s3 = S3Storage::ownedByCurrentTeam()->get(); } + public function render() { return view('livewire.storage.index'); diff --git a/app/Livewire/Storage/Show.php b/app/Livewire/Storage/Show.php index 988fb30cb..bdea9a3b0 100644 --- a/app/Livewire/Storage/Show.php +++ b/app/Livewire/Storage/Show.php @@ -8,13 +8,15 @@ class Show extends Component { public $storage = null; + public function mount() { $this->storage = S3Storage::ownedByCurrentTeam()->whereUuid(request()->storage_uuid)->first(); - if (!$this->storage) { + if (! $this->storage) { abort(404); } } + public function render() { return view('livewire.storage.show'); diff --git a/app/Livewire/Subscription/Actions.php b/app/Livewire/Subscription/Actions.php index a6a201f3b..db1f565a6 100644 --- a/app/Livewire/Subscription/Actions.php +++ b/app/Livewire/Subscription/Actions.php @@ -9,23 +9,24 @@ class Actions extends Component { public $server_limits = 0; - + public function mount() { $this->server_limits = Team::serverLimit(); } + public function cancel() { try { $subscription_id = currentTeam()->subscription->lemon_subscription_id; - if (!$subscription_id) { + if (! $subscription_id) { throw new \Exception('No subscription found'); } $response = Http::withHeaders([ 'Accept' => 'application/vnd.api+json', 'Content-Type' => 'application/vnd.api+json', - 'Authorization' => 'Bearer ' . config('subscription.lemon_squeezy_api_key'), - ])->delete('https://api.lemonsqueezy.com/v1/subscriptions/' . $subscription_id); + 'Authorization' => 'Bearer '.config('subscription.lemon_squeezy_api_key'), + ])->delete('https://api.lemonsqueezy.com/v1/subscriptions/'.$subscription_id); $json = $response->json(); if ($response->failed()) { $error = data_get($json, 'errors.0.status'); @@ -41,18 +42,19 @@ public function cancel() return handleError($e, $this); } } + public function resume() { try { $subscription_id = currentTeam()->subscription->lemon_subscription_id; - if (!$subscription_id) { + if (! $subscription_id) { throw new \Exception('No subscription found'); } $response = Http::withHeaders([ 'Accept' => 'application/vnd.api+json', 'Content-Type' => 'application/vnd.api+json', - 'Authorization' => 'Bearer ' . config('subscription.lemon_squeezy_api_key'), - ])->patch('https://api.lemonsqueezy.com/v1/subscriptions/' . $subscription_id, [ + 'Authorization' => 'Bearer '.config('subscription.lemon_squeezy_api_key'), + ])->patch('https://api.lemonsqueezy.com/v1/subscriptions/'.$subscription_id, [ 'data' => [ 'type' => 'subscriptions', 'id' => $subscription_id, @@ -76,6 +78,7 @@ public function resume() return handleError($e, $this); } } + public function stripeCustomerPortal() { $session = getStripeCustomerPortalSession(currentTeam()); diff --git a/app/Livewire/Subscription/Index.php b/app/Livewire/Subscription/Index.php index b367e6dcc..c072352fe 100644 --- a/app/Livewire/Subscription/Index.php +++ b/app/Livewire/Subscription/Index.php @@ -9,10 +9,12 @@ class Index extends Component { public InstanceSettings $settings; + public bool $alreadySubscribed = false; + public function mount() { - if (!isCloud()) { + if (! isCloud()) { return redirect(RouteServiceProvider::HOME); } if (auth()->user()?->isMember()) { @@ -24,14 +26,17 @@ public function mount() $this->settings = InstanceSettings::get(); $this->alreadySubscribed = currentTeam()->subscription()->exists(); } + public function stripeCustomerPortal() { $session = getStripeCustomerPortalSession(currentTeam()); if (is_null($session)) { return; } + return redirect($session->url); } + public function render() { return view('livewire.subscription.index'); diff --git a/app/Livewire/Subscription/PricingPlans.php b/app/Livewire/Subscription/PricingPlans.php index dddfa3a80..9bc11d862 100644 --- a/app/Livewire/Subscription/PricingPlans.php +++ b/app/Livewire/Subscription/PricingPlans.php @@ -3,19 +3,21 @@ namespace App\Livewire\Subscription; use Livewire\Component; -use Stripe\Stripe; use Stripe\Checkout\Session; +use Stripe\Stripe; class PricingPlans extends Component { public bool $isTrial = false; + public function mount() { - $this->isTrial = !data_get(currentTeam(), 'subscription.stripe_trial_already_ended'); + $this->isTrial = ! data_get(currentTeam(), 'subscription.stripe_trial_already_ended'); if (config('constants.limits.trial_period') == 0) { $this->isTrial = false; } } + public function subscribeStripe($type) { $team = currentTeam(); @@ -49,14 +51,15 @@ public function subscribeStripe($type) $priceId = config('subscription.stripe_price_id_basic_monthly'); break; } - if (!$priceId) { + if (! $priceId) { $this->dispatch('error', 'Price ID not found! Please contact the administrator.'); + return; } $payload = [ 'allow_promotion_codes' => true, 'billing_address_collection' => 'required', - 'client_reference_id' => auth()->user()->id . ':' . currentTeam()->id, + 'client_reference_id' => auth()->user()->id.':'.currentTeam()->id, 'line_items' => [[ 'price' => $priceId, 'quantity' => 1, @@ -87,14 +90,14 @@ public function subscribeStripe($type) $payload['line_items'][0]['quantity'] = 2; } - if (!data_get($team, 'subscription.stripe_trial_already_ended')) { + if (! data_get($team, 'subscription.stripe_trial_already_ended')) { if (config('constants.limits.trial_period') > 0) { $payload['subscription_data'] = [ 'trial_period_days' => config('constants.limits.trial_period'), 'trial_settings' => [ 'end_behavior' => [ 'missing_payment_method' => 'cancel', - ] + ], ], ]; } @@ -104,12 +107,13 @@ public function subscribeStripe($type) if ($customer) { $payload['customer'] = $customer; $payload['customer_update'] = [ - 'name' => 'auto' + 'name' => 'auto', ]; } else { $payload['customer_email'] = auth()->user()->email; } $session = Session::create($payload); + return redirect($session->url, 303); } } diff --git a/app/Livewire/Subscription/Show.php b/app/Livewire/Subscription/Show.php index 2ae89806d..96258c64e 100644 --- a/app/Livewire/Subscription/Show.php +++ b/app/Livewire/Subscription/Show.php @@ -8,16 +8,17 @@ class Show extends Component { public function mount() { - if (!isCloud()) { + if (! isCloud()) { return redirect()->route('dashboard'); } if (auth()->user()?->isMember()) { return redirect()->route('dashboard'); } - if (!data_get(currentTeam(), 'subscription')) { + if (! data_get(currentTeam(), 'subscription')) { return redirect()->route('subscription.index'); } } + public function render() { return view('livewire.subscription.show'); diff --git a/app/Livewire/SwitchTeam.php b/app/Livewire/SwitchTeam.php index 49b73cdc6..7629c9596 100644 --- a/app/Livewire/SwitchTeam.php +++ b/app/Livewire/SwitchTeam.php @@ -8,9 +8,12 @@ class SwitchTeam extends Component { public string $selectedTeamId = 'default'; - public function mount() { + + public function mount() + { $this->selectedTeamId = auth()->user()->currentTeam()->id; } + public function updatedSelectedTeamId() { $this->switch_to($this->selectedTeamId); @@ -18,14 +21,15 @@ public function updatedSelectedTeamId() public function switch_to($team_id) { - if (!auth()->user()->teams->contains($team_id)) { + if (! auth()->user()->teams->contains($team_id)) { return; } $team_to_switch_to = Team::find($team_id); - if (!$team_to_switch_to) { + if (! $team_to_switch_to) { return; } refreshSession($team_to_switch_to); + return redirect(request()->header('Referer')); } } diff --git a/app/Livewire/Tags/Deployments.php b/app/Livewire/Tags/Deployments.php index 07034ed5d..270aa176a 100644 --- a/app/Livewire/Tags/Deployments.php +++ b/app/Livewire/Tags/Deployments.php @@ -8,23 +8,26 @@ class Deployments extends Component { public $deployments_per_tag_per_server = []; + public $resource_ids = []; + public function render() { return view('livewire.tags.deployments'); } + public function get_deployments() { try { - $this->deployments_per_tag_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn('application_id', $this->resource_ids)->get([ - "id", - "application_id", - "application_name", - "deployment_url", - "pull_request_id", - "server_name", - "server_id", - "status" + $this->deployments_per_tag_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('application_id', $this->resource_ids)->get([ + 'id', + 'application_id', + 'application_name', + 'deployment_url', + 'pull_request_id', + 'server_name', + 'server_id', + 'status', ])->sortBy('id')->groupBy('server_name')->toArray(); $this->dispatch('deployments', $this->deployments_per_tag_per_server); } catch (\Exception $e) { diff --git a/app/Livewire/Tags/Index.php b/app/Livewire/Tags/Index.php index c2b2a5928..91e15835f 100644 --- a/app/Livewire/Tags/Index.php +++ b/app/Livewire/Tags/Index.php @@ -3,7 +3,6 @@ namespace App\Livewire\Tags; use App\Http\Controllers\Api\Deploy; -use App\Models\ApplicationDeploymentQueue; use App\Models\Tag; use Illuminate\Support\Collection; use Livewire\Attributes\Url; @@ -15,9 +14,13 @@ class Index extends Component public ?string $tag = null; public Collection $tags; + public Collection $applications; + public Collection $services; + public $webhook = null; + public $deployments_per_tag_per_server = []; protected $listeners = ['deployments' => 'update_deployments']; @@ -26,15 +29,17 @@ public function update_deployments($deployments) { $this->deployments_per_tag_per_server = $deployments; } + public function tag_updated() { - if ($this->tag == "") { + if ($this->tag == '') { return; } $tag = $this->tags->where('name', $this->tag)->first(); - if (!$tag) { + if (! $tag) { $this->dispatch('error', "Tag ({$this->tag}) not found."); - $this->tag = ""; + $this->tag = ''; + return; } $this->webhook = generatTagDeployWebhook($tag->name); @@ -45,7 +50,7 @@ public function tag_updated() public function redeploy_all() { try { - $this->applications->each(function ($resource){ + $this->applications->each(function ($resource) { $deploy = new Deploy(); $deploy->deploy_resource($resource); }); @@ -58,6 +63,7 @@ public function redeploy_all() return handleError($e, $this); } } + public function mount() { $this->tags = Tag::ownedByCurrentTeam()->get()->unique('name')->sortBy('name'); @@ -65,6 +71,7 @@ public function mount() $this->tag_updated(); } } + public function render() { return view('livewire.tags.index'); diff --git a/app/Livewire/Tags/Show.php b/app/Livewire/Tags/Show.php index 05b25955a..f4ecc67a0 100644 --- a/app/Livewire/Tags/Show.php +++ b/app/Livewire/Tags/Show.php @@ -10,17 +10,22 @@ class Show extends Component { public $tags; + public Tag $tag; + public $applications; + public $services; + public $webhook = null; + public $deployments_per_tag_per_server = []; public function mount() { $this->tags = Tag::ownedByCurrentTeam()->get()->unique('name')->sortBy('name'); $tag = $this->tags->where('name', request()->tag_name)->first(); - if (!$tag) { + if (! $tag) { return redirect()->route('tags.index'); } $this->webhook = generatTagDeployWebhook($tag->name); @@ -29,24 +34,26 @@ public function mount() $this->tag = $tag; $this->get_deployments(); } + public function get_deployments() { try { $resource_ids = $this->applications->pluck('id'); - $this->deployments_per_tag_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn('application_id', $resource_ids)->get([ - "id", - "application_id", - "application_name", - "deployment_url", - "pull_request_id", - "server_name", - "server_id", - "status" + $this->deployments_per_tag_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('application_id', $resource_ids)->get([ + 'id', + 'application_id', + 'application_name', + 'deployment_url', + 'pull_request_id', + 'server_name', + 'server_id', + 'status', ])->sortBy('id')->groupBy('server_name')->toArray(); } catch (\Exception $e) { return handleError($e, $this); } } + public function redeploy_all() { try { diff --git a/app/Livewire/Team/AdminView.php b/app/Livewire/Team/AdminView.php index 97bc6c04f..97d4fcdbf 100644 --- a/app/Livewire/Team/AdminView.php +++ b/app/Livewire/Team/AdminView.php @@ -9,19 +9,24 @@ class AdminView extends Component { public $users; - public ?string $search = ""; + + public ?string $search = ''; + public bool $lots_of_users = false; + private $number_of_users_to_show = 20; + public function mount() { - if (!isInstanceAdmin()) { + if (! isInstanceAdmin()) { return redirect()->route('dashboard'); } $this->getUsers(); } + public function submitSearch() { - if ($this->search !== "") { + if ($this->search !== '') { $this->users = User::where(function ($query) { $query->where('name', 'like', "%{$this->search}%") ->orWhere('email', 'like', "%{$this->search}%"); @@ -32,6 +37,7 @@ public function submitSearch() $this->getUsers(); } } + public function getUsers() { $users = User::where('id', '!=', auth()->id())->get(); @@ -43,31 +49,33 @@ public function getUsers() $this->users = $users; } } + private function finalizeDeletion(User $user, Team $team) { $servers = $team->servers; foreach ($servers as $server) { $resources = $server->definedResources(); foreach ($resources as $resource) { - ray("Deleting resource: " . $resource->name); + ray('Deleting resource: '.$resource->name); $resource->forceDelete(); } - ray("Deleting server: " . $server->name); + ray('Deleting server: '.$server->name); $server->forceDelete(); } $projects = $team->projects; foreach ($projects as $project) { - ray("Deleting project: " . $project->name); + ray('Deleting project: '.$project->name); $project->forceDelete(); } $team->members()->detach($user->id); - ray('Deleting team: ' . $team->name); + ray('Deleting team: '.$team->name); $team->delete(); } + public function delete($id) { - if (!auth()->user()->isInstanceAdmin()) { + if (! auth()->user()->isInstanceAdmin()) { return $this->dispatch('error', 'You are not authorized to delete users'); } $user = User::find($id); @@ -78,12 +86,14 @@ public function delete($id) if ($team->id === 0) { if ($user_alone_in_team) { ray('user is alone in the root team, do nothing'); + return $this->dispatch('error', 'User is alone in the root team, cannot delete'); } } if ($user_alone_in_team) { ray('user is alone in the team'); $this->finalizeDeletion($user, $team); + continue; } ray('user is not alone in the team'); @@ -95,6 +105,7 @@ public function delete($id) if ($found_other_owner_or_admin) { ray('found other owner or admin'); $team->members()->detach($user->id); + continue; } else { $found_other_member_who_is_not_owner = $team->members->filter(function ($member) { @@ -110,6 +121,7 @@ public function delete($id) ray('found no other member who is not owner'); $this->finalizeDeletion($user, $team); } + continue; } } else { @@ -117,10 +129,11 @@ public function delete($id) $team->members()->detach($user->id); } } - ray("Deleting user: " . $user->name); + ray('Deleting user: '.$user->name); $user->delete(); $this->getUsers(); } + public function render() { return view('livewire.team.admin-view'); diff --git a/app/Livewire/Team/Create.php b/app/Livewire/Team/Create.php index 2ca647092..992833da5 100644 --- a/app/Livewire/Team/Create.php +++ b/app/Livewire/Team/Create.php @@ -8,12 +8,14 @@ class Create extends Component { public string $name = ''; - public string|null $description = null; + + public ?string $description = null; protected $rules = [ 'name' => 'required|min:3|max:255', 'description' => 'nullable|min:3|max:255', ]; + protected $validationAttributes = [ 'name' => 'name', 'description' => 'description', @@ -30,6 +32,7 @@ public function submit() ]); auth()->user()->teams()->attach($team, ['role' => 'admin']); refreshSession(); + return redirect()->route('team.index'); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Team/Index.php b/app/Livewire/Team/Index.php index 1822620f8..45600dbfe 100644 --- a/app/Livewire/Team/Index.php +++ b/app/Livewire/Team/Index.php @@ -10,22 +10,28 @@ class Index extends Component { public $invitations = []; + public Team $team; + protected $rules = [ 'team.name' => 'required|min:3|max:255', 'team.description' => 'nullable|min:3|max:255', ]; + protected $validationAttributes = [ 'team.name' => 'name', 'team.description' => 'description', ]; - public function mount() { + + public function mount() + { $this->team = currentTeam(); if (auth()->user()->isAdminFromSession()) { $this->invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get(); } } + public function render() { return view('livewire.team.index'); @@ -60,6 +66,7 @@ public function delete() }); refreshSession(); + return redirect()->route('team.index'); } } diff --git a/app/Livewire/Team/Invitations.php b/app/Livewire/Team/Invitations.php index 436c0778d..6a32a1d16 100644 --- a/app/Livewire/Team/Invitations.php +++ b/app/Livewire/Team/Invitations.php @@ -8,12 +8,13 @@ class Invitations extends Component { public $invitations; + protected $listeners = ['refreshInvitations']; public function deleteInvitation(int $invitation_id) { $initiation_found = TeamInvitation::find($invitation_id); - if (!$initiation_found) { + if (! $initiation_found) { return $this->dispatch('error', 'Invitation not found.'); } $initiation_found->delete(); diff --git a/app/Livewire/Team/InviteLink.php b/app/Livewire/Team/InviteLink.php index c03bb0c45..cc69e6650 100644 --- a/app/Livewire/Team/InviteLink.php +++ b/app/Livewire/Team/InviteLink.php @@ -5,22 +5,23 @@ use App\Models\TeamInvitation; use App\Models\User; use Illuminate\Notifications\Messages\MailMessage; -use Illuminate\Support\Facades\Artisan; -use Livewire\Component; -use Visus\Cuid2\Cuid2; use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; +use Livewire\Component; +use Visus\Cuid2\Cuid2; class InviteLink extends Component { public string $email; + public string $role = 'member'; protected $rules = [ 'email' => 'required|email', 'role' => 'required|string', ]; + public function mount() { $this->email = isDev() ? 'test3@example.com' : ''; @@ -35,16 +36,17 @@ public function viaLink() { $this->generate_invite_link(sendEmail: false); } + private function generate_invite_link(bool $sendEmail = false) { try { $this->validate(); $member_emails = currentTeam()->members()->get()->pluck('email'); if ($member_emails->contains($this->email)) { - return handleError(livewire: $this, customErrorMessage: "$this->email is already a member of " . currentTeam()->name . "."); + return handleError(livewire: $this, customErrorMessage: "$this->email is already a member of ".currentTeam()->name.'.'); } $uuid = new Cuid2(32); - $link = url('/') . config('constants.invitation.link.base_url') . $uuid; + $link = url('/').config('constants.invitation.link.base_url').$uuid; $user = User::whereEmail($this->email)->first(); if (is_null($user)) { @@ -59,7 +61,7 @@ private function generate_invite_link(bool $sendEmail = false) $link = route('auth.link', ['token' => $token]); } $invitation = TeamInvitation::whereEmail($this->email)->first(); - if (!is_null($invitation)) { + if (! is_null($invitation)) { $invitationValid = $invitation->isValid(); if ($invitationValid) { return handleError(livewire: $this, customErrorMessage: "Pending invitation already exists for $this->email."); @@ -82,10 +84,11 @@ private function generate_invite_link(bool $sendEmail = false) 'team' => currentTeam()->name, 'invitation_link' => $link, ]); - $mail->subject('You have been invited to ' . currentTeam()->name . ' on ' . config('app.name') . '.'); + $mail->subject('You have been invited to '.currentTeam()->name.' on '.config('app.name').'.'); send_user_an_email($mail, $this->email); $this->dispatch('success', 'Invitation sent via email.'); $this->dispatch('refreshInvitations'); + return; } else { $this->dispatch('success', 'Invitation link generated.'); @@ -96,6 +99,7 @@ private function generate_invite_link(bool $sendEmail = false) if ($e->getCode() === '23505') { $error_message = 'Invitation already sent.'; } + return handleError(error: $e, livewire: $this, customErrorMessage: $error_message); } } diff --git a/app/Livewire/Team/Member.php b/app/Livewire/Team/Member.php index 0f0774898..680cb901b 100644 --- a/app/Livewire/Team/Member.php +++ b/app/Livewire/Team/Member.php @@ -21,6 +21,7 @@ public function makeOwner() $this->member->teams()->updateExistingPivot(currentTeam()->id, ['role' => 'owner']); $this->dispatch('reloadWindow'); } + public function makeReadonly() { $this->member->teams()->updateExistingPivot(currentTeam()->id, ['role' => 'member']); @@ -31,7 +32,7 @@ public function remove() { $this->member->teams()->detach(currentTeam()); Cache::forget("team:{$this->member->id}"); - Cache::remember('team:' . $this->member->id, 3600, function () { + Cache::remember('team:'.$this->member->id, 3600, function () { return $this->member->teams()->first(); }); $this->dispatch('reloadWindow'); diff --git a/app/Livewire/Team/Member/Index.php b/app/Livewire/Team/Member/Index.php index bca24c26c..00b745fe4 100644 --- a/app/Livewire/Team/Member/Index.php +++ b/app/Livewire/Team/Member/Index.php @@ -8,11 +8,14 @@ class Index extends Component { public $invitations = []; - public function mount() { + + public function mount() + { if (auth()->user()->isAdminFromSession()) { $this->invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get(); } } + public function render() { return view('livewire.team.member.index'); diff --git a/app/Livewire/Team/Storage/Show.php b/app/Livewire/Team/Storage/Show.php index ab2acbbb9..d3051afd4 100644 --- a/app/Livewire/Team/Storage/Show.php +++ b/app/Livewire/Team/Storage/Show.php @@ -8,13 +8,15 @@ class Show extends Component { public $storage = null; + public function mount() { $this->storage = S3Storage::ownedByCurrentTeam()->whereUuid(request()->storage_uuid)->first(); - if (!$this->storage) { + if (! $this->storage) { abort(404); } } + public function render() { return view('livewire.storage.show'); diff --git a/app/Livewire/Upgrade.php b/app/Livewire/Upgrade.php index e81ee93e6..7ad7e9523 100644 --- a/app/Livewire/Upgrade.php +++ b/app/Livewire/Upgrade.php @@ -3,14 +3,16 @@ namespace App\Livewire; use App\Actions\Server\UpdateCoolify; - use Livewire\Component; class Upgrade extends Component { public bool $showProgress = false; + public bool $updateInProgress = false; + public bool $isUpgradeAvailable = false; + public string $latestVersion = ''; public function checkUpdate() diff --git a/app/Livewire/VerifyEmail.php b/app/Livewire/VerifyEmail.php index b1aec4353..d1f79c835 100644 --- a/app/Livewire/VerifyEmail.php +++ b/app/Livewire/VerifyEmail.php @@ -2,23 +2,27 @@ namespace App\Livewire; -use Livewire\Component; use DanHarrin\LivewireRateLimiting\WithRateLimiting; +use Livewire\Component; class VerifyEmail extends Component { use WithRateLimiting; - public function again() { + + public function again() + { try { $this->rateLimit(1, 300); auth()->user()->sendVerificationEmail(); $this->dispatch('success', 'Email verification link sent!'); - } catch(\Exception $e) { + } catch (\Exception $e) { ray($e); - return handleError($e,$this); + + return handleError($e, $this); } } + public function render() { return view('livewire.verify-email'); diff --git a/app/Livewire/Waitlist/Index.php b/app/Livewire/Waitlist/Index.php index a3829dec7..422415449 100644 --- a/app/Livewire/Waitlist/Index.php +++ b/app/Livewire/Waitlist/Index.php @@ -5,22 +5,26 @@ use App\Jobs\SendConfirmationForWaitlistJob; use App\Models\User; use App\Models\Waitlist; -use Livewire\Component; use Illuminate\Support\Str; +use Livewire\Component; class Index extends Component { public string $email; + public int $users = 0; + public int $waitingInLine = 0; protected $rules = [ 'email' => 'required|email', ]; + public function render() { return view('livewire.waitlist.index')->layout('layouts.simple'); } + public function mount() { if (config('coolify.waitlist') == false) { @@ -32,6 +36,7 @@ public function mount() $this->email = 'waitlist@example.com'; } } + public function submit() { $this->validate(); @@ -42,11 +47,13 @@ public function submit() } $found = Waitlist::where('email', $this->email)->first(); if ($found) { - if (!$found->verified) { + if (! $found->verified) { $this->dispatch('error', 'You are already on the waitlist.
Please check your email to verify your email address.'); + return; } $this->dispatch('error', 'You are already on the waitlist.
You will be notified when your turn comes.
Thank you.'); + return; } $waitlist = Waitlist::create([ diff --git a/app/Models/Application.php b/app/Models/Application.php index e606735df..6e55f6626 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -7,9 +7,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Collection; -use Spatie\Activitylog\Models\Activity; use Illuminate\Support\Str; use RuntimeException; +use Spatie\Activitylog\Models\Activity; use Spatie\Url\Url; use Symfony\Component\Yaml\Yaml; use Visus\Cuid2\Cuid2; @@ -17,7 +17,9 @@ class Application extends BaseModel { use SoftDeletes; + protected $guarded = []; + protected static function booted() { static::saving(function ($application) { @@ -64,56 +66,68 @@ public function delete_configurations() $workdir = $this->workdir(); if (str($workdir)->endsWith($this->uuid)) { ray('Deleting workdir'); - instant_remote_process(["rm -rf " . $this->workdir()], $server, false); + instant_remote_process(['rm -rf '.$this->workdir()], $server, false); } } + public function additional_servers() { return $this->belongsToMany(Server::class, 'additional_destinations') ->withPivot('standalone_docker_id', 'status'); } + public function additional_networks() { return $this->belongsToMany(StandaloneDocker::class, 'additional_destinations') ->withPivot('server_id', 'status'); } + public function is_public_repository(): bool { if (data_get($this, 'source.is_public')) { return true; } + return false; } + public function is_github_based(): bool { if (data_get($this, 'source')) { return true; } + return false; } + public function isForceHttpsEnabled() { return data_get($this, 'settings.is_force_https_enabled', false); } + public function isStripprefixEnabled() { return data_get($this, 'settings.is_stripprefix_enabled', true); } + public function isGzipEnabled() { return data_get($this, 'settings.is_gzip_enabled', true); } + public function link() { if (data_get($this, 'environment.project.uuid')) { return route('project.application.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), 'environment_name' => data_get($this, 'environment.name'), - 'application_uuid' => data_get($this, 'uuid') + 'application_uuid' => data_get($this, 'uuid'), ]); } + return null; } + public function failedTaskLink($task_uuid) { if (data_get($this, 'environment.project.uuid')) { @@ -121,11 +135,13 @@ public function failedTaskLink($task_uuid) 'project_uuid' => data_get($this, 'environment.project.uuid'), 'environment_name' => data_get($this, 'environment.name'), 'application_uuid' => data_get($this, 'uuid'), - 'task_uuid' => $task_uuid + 'task_uuid' => $task_uuid, ]); } + return null; } + public function settings() { return $this->hasOne(ApplicationSetting::class); @@ -135,6 +151,7 @@ public function persistentStorages() { return $this->morphMany(LocalPersistentVolume::class, 'resource'); } + public function fileStorages() { return $this->morphMany(LocalFileVolume::class, 'resource'); @@ -148,7 +165,7 @@ public function type() public function publishDirectory(): Attribute { return Attribute::make( - set: fn ($value) => $value ? '/' . ltrim($value, '/') : null, + set: fn ($value) => $value ? '/'.ltrim($value, '/') : null, ); } @@ -156,14 +173,16 @@ public function gitBranchLocation(): Attribute { return Attribute::make( get: function () { - if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) { + if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) { return "{$this->source->html_url}/{$this->git_repository}/tree/{$this->git_branch}"; } // Convert the SSH URL to HTTPS URL if (strpos($this->git_repository, 'git@') === 0) { $git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository); + return "https://{$git_repository}/tree/{$this->git_branch}"; } + return $this->git_repository; } ); @@ -173,14 +192,16 @@ public function gitWebhook(): Attribute { return Attribute::make( get: function () { - if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) { + if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) { return "{$this->source->html_url}/{$this->git_repository}/settings/hooks"; } // Convert the SSH URL to HTTPS URL if (strpos($this->git_repository, 'git@') === 0) { $git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository); + return "https://{$git_repository}/settings/hooks"; } + return $this->git_repository; } ); @@ -190,39 +211,47 @@ public function gitCommits(): Attribute { return Attribute::make( get: function () { - if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) { + if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) { return "{$this->source->html_url}/{$this->git_repository}/commits/{$this->git_branch}"; } // Convert the SSH URL to HTTPS URL if (strpos($this->git_repository, 'git@') === 0) { $git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository); + return "https://{$git_repository}/commits/{$this->git_branch}"; } + return $this->git_repository; } ); } + public function gitCommitLink($link): string { - if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) { + if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) { if (str($this->source->html_url)->contains('bitbucket')) { return "{$this->source->html_url}/{$this->git_repository}/commits/{$link}"; } + return "{$this->source->html_url}/{$this->git_repository}/commit/{$link}"; } if (strpos($this->git_repository, 'git@') === 0) { $git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository); + return "https://{$git_repository}/commit/{$link}"; } if (str($this->git_repository)->contains('bitbucket')) { $git_repository = str_replace('.git', '', $this->git_repository); $url = Url::fromString($git_repository); $url = $url->withUserInfo(''); - $url = $url->withPath($url->getPath() . '/commits/' . $link); + $url = $url->withPath($url->getPath().'/commits/'.$link); + return $url->__toString(); } + return $this->git_repository; } + public function dockerfileLocation(): Attribute { return Attribute::make( @@ -233,11 +262,13 @@ public function dockerfileLocation(): Attribute if ($value !== '/') { return Str::start(Str::replaceEnd('/', '', $value), '/'); } + return Str::start($value, '/'); } } ); } + public function dockerComposeLocation(): Attribute { return Attribute::make( @@ -248,11 +279,13 @@ public function dockerComposeLocation(): Attribute if ($value !== '/') { return Str::start(Str::replaceEnd('/', '', $value), '/'); } + return Str::start($value, '/'); } } ); } + public function dockerComposePrLocation(): Attribute { return Attribute::make( @@ -263,24 +296,27 @@ public function dockerComposePrLocation(): Attribute if ($value !== '/') { return Str::start(Str::replaceEnd('/', '', $value), '/'); } + return Str::start($value, '/'); } } ); } + public function baseDirectory(): Attribute { return Attribute::make( - set: fn ($value) => '/' . ltrim($value, '/'), + set: fn ($value) => '/'.ltrim($value, '/'), ); } public function portsMappings(): Attribute { return Attribute::make( - set: fn ($value) => $value === "" ? null : $value, + set: fn ($value) => $value === '' ? null : $value, ); } + public function portsMappingsArray(): Attribute { return Attribute::make( @@ -290,14 +326,17 @@ public function portsMappingsArray(): Attribute ); } + public function isExited() { return (bool) str($this->status)->startsWith('exited'); } + public function realStatus() { return $this->getRawOriginal('status'); } + public function status(): Attribute { return Attribute::make( @@ -306,25 +345,27 @@ public function status(): Attribute if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; - } else if (str($value)->contains(':')) { + } elseif (str($value)->contains(':')) { $status = str($value)->before(':')->trim()->value(); $health = str($value)->after(':')->trim()->value() ?? 'unhealthy'; } else { $status = $value; $health = 'unhealthy'; } + return "$status:$health"; } else { if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; - } else if (str($value)->contains(':')) { + } elseif (str($value)->contains(':')) { $status = str($value)->before(':')->trim()->value(); $health = str($value)->after(':')->trim()->value() ?? 'unhealthy'; } else { $status = $value; $health = 'unhealthy'; } + return "$status:$health"; } }, @@ -334,13 +375,14 @@ public function status(): Attribute if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; - } else if (str($value)->contains(':')) { + } elseif (str($value)->contains(':')) { $status = str($value)->before(':')->trim()->value(); $health = str($value)->after(':')->trim()->value() ?? 'unhealthy'; } else { $status = $value; $health = 'unhealthy'; } + return "$status:$health"; } else { $complex_status = null; @@ -358,6 +400,7 @@ public function status(): Attribute $complex_health = 'unhealthy'; } } + return "$complex_status:$complex_health"; } }, @@ -372,18 +415,22 @@ public function portsExposesArray(): Attribute : explode(',', $this->ports_exposes) ); } + 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 serviceType() { $found = str(collect(SPECIFIC_SERVICES)->filter(function ($service) { @@ -392,12 +439,15 @@ public function serviceType() if ($found->isNotEmpty()) { return $found; } + return null; } + public function main_port() { return $this->settings->is_static ? [80] : $this->ports_exposes_array; } + public function environment_variables(): HasMany { return $this->hasMany(EnvironmentVariable::class)->where('is_preview', false)->orderBy('key', 'asc'); @@ -469,30 +519,36 @@ public function source() { return $this->morphTo(); } + public function isDeploymentInprogress() { - $deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', ApplicationDeploymentStatus::IN_PROGRESS)->where('status', ApplicationDeploymentStatus::QUEUED)->count(); + $deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS, ApplicationDeploymentStatus::QUEUED])->count(); if ($deployments > 0) { return true; } + return false; } + public function get_last_successful_deployment() { return ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', 'finished')->where('pull_request_id', 0)->orderBy('created_at', 'desc')->first(); } + public function get_last_days_deployments() { return ApplicationDeploymentQueue::where('application_id', $this->id)->where('created_at', '>=', now()->subDays(7))->orderBy('created_at', 'desc')->get(); } + public function deployments(int $skip = 0, int $take = 10) { $deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->orderBy('created_at', 'desc'); $count = $deployments->count(); $deployments = $deployments->skip($skip)->take($take)->get(); + return [ 'count' => $count, - 'deployments' => $deployments + 'deployments' => $deployments, ]; } @@ -506,6 +562,7 @@ public function isDeployable(): bool if ($this->settings->is_auto_deploy_enabled) { return true; } + return false; } @@ -514,6 +571,7 @@ public function isPRDeployable(): bool if ($this->settings->is_preview_deployments_enabled) { return true; } + return false; } @@ -524,20 +582,23 @@ public function deploymentType() } if (data_get($this, 'private_key_id')) { return 'deploy_key'; - } else if (data_get($this, 'source')) { + } elseif (data_get($this, 'source')) { return 'source'; } else { return 'other'; } throw new \Exception('No deployment type found'); } + public function could_set_build_commands(): bool { if ($this->build_pack === 'nixpacks') { return true; } + return false; } + public function git_based(): bool { if ($this->dockerfile) { @@ -546,26 +607,32 @@ public function git_based(): bool if ($this->build_pack === 'dockerimage') { return false; } + return true; } + public function isHealthcheckDisabled(): bool { if (data_get($this, 'health_check_enabled') === false) { return true; } + return false; } + public function workdir() { - return application_configuration_dir() . "/{$this->uuid}"; + return application_configuration_dir()."/{$this->uuid}"; } + public function isLogDrainEnabled() { return data_get($this, 'settings.is_log_drain_enabled', 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->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; + $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.$this->redirect; if ($this->pull_request_id === 0 || $this->pull_request_id === null) { $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); } else { @@ -578,6 +645,7 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } if ($oldConfigHash === $newConfigHash) { @@ -587,10 +655,12 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } } - function customRepository() + + public function customRepository() { preg_match('/(?<=:)\d+(?=\/)/', $this->git_repository, $matches); $port = 22; @@ -602,16 +672,19 @@ function customRepository() } else { $repository = $this->git_repository; } + return [ 'repository' => $repository, - 'port' => $port + 'port' => $port, ]; } - function generateBaseDir(string $uuid) + + public function generateBaseDir(string $uuid) { return "/artifacts/{$uuid}"; } - function setGitImportSettings(string $deployment_uuid, string $git_clone_command, bool $public = false) + + public function setGitImportSettings(string $deployment_uuid, string $git_clone_command, bool $public = false) { $baseDir = $this->generateBaseDir($deployment_uuid); @@ -627,9 +700,11 @@ function setGitImportSettings(string $deployment_uuid, string $git_clone_command if ($this->settings->is_git_lfs_enabled) { $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git lfs pull"; } + return $git_clone_command; } - function generateGitImportCommands(string $deployment_uuid, int $pull_request_id = 0, ?string $git_type = null, bool $exec_in_docker = true, bool $only_checkout = false, ?string $custom_base_dir = null, ?string $commit = null) + + public function generateGitImportCommands(string $deployment_uuid, int $pull_request_id = 0, ?string $git_type = null, bool $exec_in_docker = true, bool $only_checkout = false, ?string $custom_base_dir = null, ?string $commit = null) { $branch = $this->git_branch; ['repository' => $customRepository, 'port' => $customPort] = $this->customRepository(); @@ -652,7 +727,7 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id if ($this->source->is_public) { $fullRepoUrl = "{$this->source->html_url}/{$customRepository}"; $git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$customRepository} {$baseDir}"; - if (!$only_checkout) { + if (! $only_checkout) { $git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command, public: true); } if ($exec_in_docker) { @@ -669,7 +744,7 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id $git_clone_command = "{$git_clone_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository} {$baseDir}"; $fullRepoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}"; } - if (!$only_checkout) { + if (! $only_checkout) { $git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command, public: false); } if ($exec_in_docker) { @@ -688,10 +763,11 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id $commands->push("cd {$baseDir} && git fetch origin {$branch} && $git_checkout_command"); } } + return [ 'commands' => $commands->implode(' && '), 'branch' => $branch, - 'fullRepoUrl' => $fullRepoUrl + 'fullRepoUrl' => $fullRepoUrl, ]; } } @@ -710,15 +786,15 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id } if ($exec_in_docker) { $commands = collect([ - executeInDocker($deployment_uuid, "mkdir -p /root/.ssh"), + executeInDocker($deployment_uuid, 'mkdir -p /root/.ssh'), executeInDocker($deployment_uuid, "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null"), - executeInDocker($deployment_uuid, "chmod 600 /root/.ssh/id_rsa"), + executeInDocker($deployment_uuid, 'chmod 600 /root/.ssh/id_rsa'), ]); } else { $commands = collect([ - "mkdir -p /root/.ssh", + 'mkdir -p /root/.ssh', "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null", - "chmod 600 /root/.ssh/id_rsa", + 'chmod 600 /root/.ssh/id_rsa', ]); } if ($pull_request_id !== 0) { @@ -729,22 +805,22 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name); - } else if ($git_type === 'github') { + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name); + } elseif ($git_type === 'github') { $branch = "pull/{$pull_request_id}/head:$pr_branch_name"; if ($exec_in_docker) { $commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'")); } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name); - } else if ($git_type === 'bitbucket') { + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name); + } elseif ($git_type === 'bitbucket') { if ($exec_in_docker) { $commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'")); } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" " . $this->buildGitCheckoutCommand($commit); + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" ".$this->buildGitCheckoutCommand($commit); } } @@ -753,10 +829,11 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id } else { $commands->push($git_clone_command); } + return [ 'commands' => $commands->implode(' && '), 'branch' => $branch, - 'fullRepoUrl' => $fullRepoUrl + 'fullRepoUrl' => $fullRepoUrl, ]; } if ($this->deploymentType() === 'other') { @@ -772,22 +849,22 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name); - } else if ($git_type === 'github') { + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name); + } elseif ($git_type === 'github') { $branch = "pull/{$pull_request_id}/head:$pr_branch_name"; if ($exec_in_docker) { $commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'")); } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name); - } else if ($git_type === 'bitbucket') { + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name); + } elseif ($git_type === 'bitbucket') { if ($exec_in_docker) { $commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'")); } else { $commands->push("echo 'Checking out $branch'"); } - $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" " . $this->buildGitCheckoutCommand($commit); + $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" ".$this->buildGitCheckoutCommand($commit); } } @@ -796,14 +873,16 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id } else { $commands->push($git_clone_command); } + return [ 'commands' => $commands->implode(' && '), 'branch' => $branch, - 'fullRepoUrl' => $fullRepoUrl + 'fullRepoUrl' => $fullRepoUrl, ]; } } - function parseRawCompose() + + public function parseRawCompose() { try { $yaml = Yaml::parse($this->docker_compose_raw); @@ -824,12 +903,12 @@ function parseRawCompose() if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) { $type = Str::of('bind'); } - } else if (is_array($volume)) { + } elseif (is_array($volume)) { $type = data_get_str($volume, 'type'); $source = data_get_str($volume, 'source'); } if ($type?->value() === 'bind') { - if ($source->value() === "/var/run/docker.sock") { + if ($source->value() === '/var/run/docker.sock') { continue; } if ($source->value() === '/tmp' || $source->value() === '/tmp/') { @@ -837,23 +916,24 @@ function parseRawCompose() } if ($source->startsWith('.')) { $source = $source->after('.'); - $source = $workdir . $source; + $source = $workdir.$source; } $commands->push("mkdir -p $source > /dev/null 2>&1 || true"); } } } $labels = collect(data_get($service, 'labels', [])); - if (!$labels->contains('coolify.managed')) { + if (! $labels->contains('coolify.managed')) { $labels->push('coolify.managed=true'); } - if (!$labels->contains('coolify.applicationId')) { - $labels->push('coolify.applicationId=' . $this->id); + if (! $labels->contains('coolify.applicationId')) { + $labels->push('coolify.applicationId='.$this->id); } - if (!$labels->contains('coolify.type')) { + if (! $labels->contains('coolify.type')) { $labels->push('coolify.type=application'); } data_set($service, 'labels', $labels->toArray()); + return $service; }); data_set($yaml, 'services', $services->toArray()); @@ -861,7 +941,8 @@ function parseRawCompose() instant_remote_process($commands, $this->destination->server, false); } - function parseCompose(int $pull_request_id = 0, ?int $preview_id = null) + + public function parseCompose(int $pull_request_id = 0, ?int $preview_id = null) { if ($this->docker_compose_raw) { return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id); @@ -869,7 +950,8 @@ function parseCompose(int $pull_request_id = 0, ?int $preview_id = null) return collect([]); } } - function loadComposeFile($isInit = false) + + public function loadComposeFile($isInit = false) { $initialDockerComposeLocation = $this->docker_compose_location; if ($isInit && $this->docker_compose_raw) { @@ -889,13 +971,13 @@ function loadComposeFile($isInit = false) "mkdir -p /tmp/{$uuid}", "cd /tmp/{$uuid}", $cloneCommand, - "git sparse-checkout init --cone", + 'git sparse-checkout init --cone', "git sparse-checkout set {$fileList->implode(' ')}", - "git read-tree -mu HEAD", + 'git read-tree -mu HEAD', "cat .$workdir$composeFile", ]); $composeFileContent = instant_remote_process($commands, $this->destination->server, false); - if (!$composeFileContent) { + if (! $composeFileContent) { $this->docker_compose_location = $initialDockerComposeLocation; $this->save(); $commands = collect([ @@ -919,7 +1001,7 @@ function loadComposeFile($isInit = false) $jsonNames = $json->keys()->toArray(); $diff = array_diff($jsonNames, $names); $json = $json->filter(function ($value, $key) use ($diff) { - return !in_array($key, $diff); + return ! in_array($key, $diff); }); if ($json) { $this->docker_compose_domains = json_encode($json); @@ -928,16 +1010,18 @@ function loadComposeFile($isInit = false) } $this->save(); } + return [ 'parsedServices' => $parsedServices, 'initialDockerComposeLocation' => $this->docker_compose_location, 'initialDockerComposePrLocation' => $this->docker_compose_pr_location, ]; } - function parseContainerLabels(?ApplicationPreview $preview = null) + + public function parseContainerLabels(?ApplicationPreview $preview = null) { $customLabels = data_get($this, 'custom_labels'); - if (!$customLabels) { + if (! $customLabels) { return; } if (base64_encode(base64_decode($customLabels, true)) !== $customLabels) { @@ -948,12 +1032,14 @@ function parseContainerLabels(?ApplicationPreview $preview = null) $customLabels = base64_decode($this->custom_labels); if (mb_detect_encoding($customLabels, 'ASCII', true) === false) { ray('custom_labels contains non-ascii characters'); - $customLabels = str(implode("|", generateLabelsApplication($this, $preview)))->replace("|", "\n"); + $customLabels = str(implode('|coolify|', generateLabelsApplication($this, $preview)))->replace('|coolify|', "\n"); } $this->custom_labels = base64_encode($customLabels); $this->save(); + return $customLabels; } + public function fqdns(): Attribute { return Attribute::make( @@ -962,16 +1048,18 @@ public function fqdns(): Attribute : explode(',', $this->fqdn), ); } + protected function buildGitCheckoutCommand($target): string { $command = "git checkout $target"; if ($this->settings->is_git_submodules_enabled) { - $command .= " && git submodule update --init --recursive"; + $command .= ' && git submodule update --init --recursive'; } return $command; } + public function watchPaths(): Attribute { return Attribute::make( @@ -982,6 +1070,7 @@ public function watchPaths(): Attribute } ); } + public function isWatchPathsTriggered(Collection $modified_files): bool { if (is_null($this->watch_paths)) { @@ -993,6 +1082,7 @@ public function isWatchPathsTriggered(Collection $modified_files): bool return fnmatch($glob, $file); }); }); + return $matches->count() > 0; } @@ -1010,13 +1100,14 @@ public function parseHealthcheckFromDockerfile($dockerfile, bool $isInit = false $trimmedLine = trim($line); if (str_starts_with($trimmedLine, 'HEALTHCHECK')) { $healthcheckCommand .= trim($trimmedLine, '\\ '); + continue; } if (isset($healthcheckCommand) && str_contains($trimmedLine, '\\')) { - $healthcheckCommand .= ' ' . trim($trimmedLine, '\\ '); + $healthcheckCommand .= ' '.trim($trimmedLine, '\\ '); } - if (isset($healthcheckCommand) && !str_contains($trimmedLine, '\\') && !empty($healthcheckCommand)) { - $healthcheckCommand .= ' ' . $trimmedLine; + if (isset($healthcheckCommand) && ! str_contains($trimmedLine, '\\') && ! empty($healthcheckCommand)) { + $healthcheckCommand .= ' '.$trimmedLine; break; } } @@ -1048,7 +1139,8 @@ public function parseHealthcheckFromDockerfile($dockerfile, bool $isInit = false } } } - function generate_preview_fqdn(int $pull_request_id) + + public function generate_preview_fqdn(int $pull_request_id) { $preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->id, $pull_request_id); if (is_null(data_get($preview, 'fqdn')) && $this->fqdn) { @@ -1072,6 +1164,7 @@ function generate_preview_fqdn(int $pull_request_id) $preview->fqdn = $preview_fqdn; $preview->save(); } + return $preview; } } diff --git a/app/Models/ApplicationDeploymentQueue.php b/app/Models/ApplicationDeploymentQueue.php index c55f89e21..b1c595046 100644 --- a/app/Models/ApplicationDeploymentQueue.php +++ b/app/Models/ApplicationDeploymentQueue.php @@ -15,20 +15,25 @@ public function setStatus(string $status) 'status' => $status, ]); } + public function getOutput($name) { - if (!$this->logs) { + if (! $this->logs) { return null; } + return collect(json_decode($this->logs))->where('name', $name)->first()?->output ?? null; } + public function commitMessage() { if (empty($this->commit_message) || is_null($this->commit_message)) { return null; } + return str($this->commit_message)->trim()->limit(50)->value(); } + public function addLogEntry(string $message, string $type = 'stdout', bool $hidden = false) { if ($type === 'error') { @@ -36,7 +41,7 @@ public function addLogEntry(string $message, string $type = 'stdout', bool $hidd } $message = str($message)->trim(); if ($message->startsWith('â•”')) { - $message = "\n" . $message; + $message = "\n".$message; } $newLogEntry = [ 'command' => null, diff --git a/app/Models/ApplicationPreview.php b/app/Models/ApplicationPreview.php index 7c7297af2..3bdd24014 100644 --- a/app/Models/ApplicationPreview.php +++ b/app/Models/ApplicationPreview.php @@ -8,6 +8,7 @@ class ApplicationPreview extends BaseModel { protected $guarded = []; + protected static function booted() { static::deleting(function ($preview) { @@ -28,7 +29,8 @@ protected static function booted() } }); } - static function findPreviewByApplicationAndPullId(int $application_id, int $pull_request_id) + + public static function findPreviewByApplicationAndPullId(int $application_id, int $pull_request_id) { return self::where('application_id', $application_id)->where('pull_request_id', $pull_request_id)->firstOrFail(); } @@ -37,7 +39,8 @@ public function application() { return $this->belongsTo(Application::class); } - function generate_preview_fqdn_compose() + + public function generate_preview_fqdn_compose() { $domains = collect(json_decode($this->application->docker_compose_domains)) ?? collect(); foreach ($domains as $service_name => $domain) { diff --git a/app/Models/ApplicationSetting.php b/app/Models/ApplicationSetting.php index 216553b30..c7624fdaa 100644 --- a/app/Models/ApplicationSetting.php +++ b/app/Models/ApplicationSetting.php @@ -16,6 +16,7 @@ class ApplicationSetting extends Model 'is_git_submodules_enabled' => 'boolean', 'is_git_lfs_enabled' => 'boolean', ]; + protected $guarded = []; public function isStatic(): Attribute @@ -26,6 +27,7 @@ public function isStatic(): Attribute $this->application->ports_exposes = 80; } $this->application->save(); + return $value; } ); diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index be487a497..7e028a6b5 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -13,8 +13,8 @@ protected static function boot() static::creating(function (Model $model) { // Generate a UUID if one isn't set - if (!$model->uuid) { - $model->uuid = (string)new Cuid2(7); + if (! $model->uuid) { + $model->uuid = (string) new Cuid2(7); } }); } diff --git a/app/Models/Environment.php b/app/Models/Environment.php index a1f3e4190..e84b6989b 100644 --- a/app/Models/Environment.php +++ b/app/Models/Environment.php @@ -14,12 +14,13 @@ protected static function booted() static::deleting(function ($environment) { $shared_variables = $environment->environment_variables(); foreach ($shared_variables as $shared_variable) { - ray('Deleting environment shared variable: ' . $shared_variable->name); + ray('Deleting environment shared variable: '.$shared_variable->name); $shared_variable->delete(); } }); } + public function isEmpty() { return $this->applications()->count() == 0 && @@ -31,45 +32,56 @@ public function isEmpty() $this->services()->count() == 0; } - public function environment_variables() { + public function environment_variables() + { return $this->hasMany(SharedEnvironmentVariable::class); } + public function applications() { return $this->hasMany(Application::class); } + public function postgresqls() { return $this->hasMany(StandalonePostgresql::class); } + public function redis() { return $this->hasMany(StandaloneRedis::class); } + public function mongodbs() { return $this->hasMany(StandaloneMongodb::class); } + public function mysqls() { return $this->hasMany(StandaloneMysql::class); } + public function mariadbs() { 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() { $postgresqls = $this->postgresqls; @@ -80,6 +92,7 @@ public function databases() $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); } diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php index c30560954..ff63bca5a 100644 --- a/app/Models/EnvironmentVariable.php +++ b/app/Models/EnvironmentVariable.php @@ -11,22 +11,24 @@ class EnvironmentVariable extends Model { protected $guarded = []; + protected $casts = [ 'key' => 'string', 'value' => 'encrypted', 'is_build_time' => 'boolean', 'is_multiline' => 'boolean', 'is_preview' => 'boolean', - 'version' => 'string' + 'version' => 'string', ]; + protected $appends = ['real_value', 'is_shared']; protected static function booted() { static::created(function (EnvironmentVariable $environment_variable) { - if ($environment_variable->application_id && !$environment_variable->is_preview) { + if ($environment_variable->application_id && ! $environment_variable->is_preview) { $found = ModelsEnvironmentVariable::where('key', $environment_variable->key)->where('application_id', $environment_variable->application_id)->where('is_preview', true)->first(); - if (!$found) { + if (! $found) { $application = Application::find($environment_variable->application_id); if ($application->build_pack !== 'dockerfile') { ModelsEnvironmentVariable::create([ @@ -35,20 +37,22 @@ protected static function booted() 'is_build_time' => $environment_variable->is_build_time, 'is_multiline' => $environment_variable->is_multiline ?? false, 'application_id' => $environment_variable->application_id, - 'is_preview' => true + 'is_preview' => true, ]); } } } $environment_variable->update([ - 'version' => config('version') + 'version' => config('version'), ]); }); } + public function service() { return $this->belongsTo(Service::class); } + protected function value(): Attribute { return Attribute::make( @@ -56,44 +60,51 @@ protected function value(): Attribute set: fn (?string $value = null) => $this->set_environment_variables($value), ); } + public function resource() { $resource = null; if ($this->application_id) { $resource = Application::find($this->application_id); - } else if ($this->service_id) { + } elseif ($this->service_id) { $resource = Service::find($this->service_id); - } else if ($this->database_id) { + } elseif ($this->database_id) { $resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id')); } + return $resource; } + public function realValue(): Attribute { $resource = $this->resource(); + return Attribute::make( get: function () use ($resource) { $env = $this->get_real_environment_variables($this->value, $resource); + return data_get($env, 'value', $env); if (is_string($env)) { return $env; } + return $env->value; } ); } + protected function isFoundInCompose(): Attribute { return Attribute::make( get: function () { - if (!$this->application_id) { + if (! $this->application_id) { return true; } $found_in_compose = false; $found_in_args = false; $resource = $this->resource(); $compose = data_get($resource, 'docker_compose_raw'); - if (!$compose) { + if (! $compose) { return true; } $yaml = Yaml::parse($compose); @@ -113,6 +124,7 @@ protected function isFoundInCompose(): Attribute if (str($item)->contains('=')) { $item = str($item)->before('='); } + return strpos($item, $this->key) !== false; }); @@ -124,6 +136,7 @@ protected function isFoundInCompose(): Attribute if (str($item)->contains('=')) { $item = str($item)->before('='); } + return strpos($item, $this->key) !== false; }); @@ -131,68 +144,76 @@ protected function isFoundInCompose(): Attribute break; } } + return $found_in_compose || $found_in_args; } ); } + protected function isShared(): Attribute { return Attribute::make( get: function () { - $type = str($this->value)->after("{{")->before(".")->value; - if (str($this->value)->startsWith('{{' . $type) && str($this->value)->endsWith('}}')) { + $type = str($this->value)->after('{{')->before('.')->value; + if (str($this->value)->startsWith('{{'.$type) && str($this->value)->endsWith('}}')) { return true; } + return false; } ); } + private function get_real_environment_variables(?string $environment_variable = null, $resource = null) { if ((is_null($environment_variable) && $environment_variable == '') || is_null($resource)) { return null; } $environment_variable = trim($environment_variable); - $type = str($environment_variable)->after("{{")->before(".")->value; - if (str($environment_variable)->startsWith("{{" . $type) && str($environment_variable)->endsWith('}}')) { + $type = str($environment_variable)->after('{{')->before('.')->value; + if (str($environment_variable)->startsWith('{{'.$type) && str($environment_variable)->endsWith('}}')) { $variable = Str::after($environment_variable, "{$type}."); $variable = Str::before($variable, '}}'); $variable = Str::of($variable)->trim()->value; - if (!collect(SHARED_VARIABLE_TYPES)->contains($type)) { + if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) { return $variable; } if ($type === 'environment') { $id = $resource->environment->id; - } else if ($type === 'project') { + } elseif ($type === 'project') { $id = $resource->environment->project->id; } else { $id = $resource->team()->id; } - $environment_variable_found = SharedEnvironmentVariable::where("type", $type)->where('key', $variable)->where('team_id', $resource->team()->id)->where("{$type}_id", $id)->first(); + $environment_variable_found = SharedEnvironmentVariable::where('type', $type)->where('key', $variable)->where('team_id', $resource->team()->id)->where("{$type}_id", $id)->first(); if ($environment_variable_found) { return $environment_variable_found; } } + return $environment_variable; } - private function get_environment_variables(?string $environment_variable = null): string|null + + private function get_environment_variables(?string $environment_variable = null): ?string { - if (!$environment_variable) { + if (! $environment_variable) { return null; } + return trim(decrypt($environment_variable)); } - private function set_environment_variables(?string $environment_variable = null): string|null + private function set_environment_variables(?string $environment_variable = null): ?string { if (is_null($environment_variable) && $environment_variable == '') { return null; } $environment_variable = trim($environment_variable); - $type = str($environment_variable)->after("{{")->before(".")->value; - if (str($environment_variable)->startsWith("{{" . $type) && str($environment_variable)->endsWith('}}')) { + $type = str($environment_variable)->after('{{')->before('.')->value; + if (str($environment_variable)->startsWith('{{'.$type) && str($environment_variable)->endsWith('}}')) { return encrypt((string) str($environment_variable)->replace(' ', '')); } + return encrypt($environment_variable); } diff --git a/app/Models/GithubApp.php b/app/Models/GithubApp.php index 758bf35c5..daf902daf 100644 --- a/app/Models/GithubApp.php +++ b/app/Models/GithubApp.php @@ -6,25 +6,26 @@ class GithubApp extends BaseModel { - protected $guarded = []; + protected $appends = ['type']; + protected $casts = [ 'is_public' => 'boolean', - 'type' => 'string' + 'type' => 'string', ]; + protected $hidden = [ 'client_secret', 'webhook_secret', ]; - - static public function public() + public static function public() { return GithubApp::whereTeamId(currentTeam()->id)->whereisPublic(true)->whereNotNull('app_id')->get(); } - static public function private() + public static function private() { return GithubApp::whereTeamId(currentTeam()->id)->whereisPublic(false)->whereNotNull('app_id')->get(); } @@ -34,7 +35,7 @@ protected static function booted(): void static::deleting(function (GithubApp $github_app) { $applications_count = Application::where('source_id', $github_app->id)->count(); if ($applications_count > 0) { - throw new \Exception('You cannot delete this GitHub App because it is in use by ' . $applications_count . ' application(s). Delete them first.'); + throw new \Exception('You cannot delete this GitHub App because it is in use by '.$applications_count.' application(s). Delete them first.'); } $github_app->privateKey()->delete(); }); diff --git a/app/Models/InstanceSettings.php b/app/Models/InstanceSettings.php index 0705ef1a1..452c5ca22 100644 --- a/app/Models/InstanceSettings.php +++ b/app/Models/InstanceSettings.php @@ -6,8 +6,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\Notifiable; -use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Request; use Spatie\Url\Url; class InstanceSettings extends Model implements SendsEmail @@ -15,6 +13,7 @@ class InstanceSettings extends Model implements SendsEmail use Notifiable; protected $guarded = []; + protected $casts = [ 'resale_license' => 'encrypted', 'smtp_password' => 'encrypted', @@ -27,11 +26,13 @@ public function fqdn(): Attribute if ($value) { $url = Url::fromString($value); $host = $url->getHost(); - return $url->getScheme() . '://' . $host; + + return $url->getScheme().'://'.$host; } } ); } + public static function get() { return InstanceSettings::findOrFail(0); @@ -43,6 +44,7 @@ public function getRecepients($notification) if (is_null($recipients) || $recipients === '') { return []; } + return explode(',', $recipients); } } diff --git a/app/Models/LocalFileVolume.php b/app/Models/LocalFileVolume.php index 5595bbb13..62ee4c45c 100644 --- a/app/Models/LocalFileVolume.php +++ b/app/Models/LocalFileVolume.php @@ -7,6 +7,7 @@ class LocalFileVolume extends BaseModel { use HasFactory; + protected $guarded = []; protected static function booted() @@ -16,10 +17,12 @@ protected static function booted() dispatch(new \App\Jobs\ServerStorageSaveJob($fileVolume)); }); } + public function service() { return $this->morphTo('resource'); } + public function deleteStorageOnServer() { $isService = data_get($this->resource, 'service'); @@ -31,15 +34,17 @@ public function deleteStorageOnServer() $server = $this->resource->destination->server; } $commands = collect([ - "cd $workdir" + "cd $workdir", ]); $fs_path = data_get($this, 'fs_path'); if ($fs_path && $fs_path != '/' && $fs_path != '.' && $fs_path != '..') { $commands->push("rm -rf $fs_path"); } ray($commands); + return instant_remote_process($commands, $server); } + public function saveStorageOnServer() { $isService = data_get($this->resource, 'service'); @@ -52,7 +57,7 @@ public function saveStorageOnServer() } $commands = collect([ "mkdir -p $workdir > /dev/null 2>&1 || true", - "cd $workdir" + "cd $workdir", ]); $is_directory = $this->is_directory; if ($is_directory) { @@ -69,16 +74,16 @@ public function saveStorageOnServer() $content = data_get($fileVolume, 'content'); if ($path->startsWith('.')) { $path = $path->after('.'); - $path = $workdir . $path; + $path = $workdir.$path; } $isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server); $isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server); if ($isFile == 'OK' && $fileVolume->is_directory) { - throw new \Exception("The following file is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory."); - } else if ($isDir == 'OK' && !$fileVolume->is_directory) { - throw new \Exception("The following file is a directory on the server, but you are trying to mark it as a file.

Please delete the directory on the server or mark it as directory."); + throw new \Exception('The following file is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.'); + } elseif ($isDir == 'OK' && ! $fileVolume->is_directory) { + throw new \Exception('The following file is a directory on the server, but you are trying to mark it as a file.

Please delete the directory on the server or mark it as directory.'); } - if (!$fileVolume->is_directory && $isDir == 'NOK') { + if (! $fileVolume->is_directory && $isDir == 'NOK') { if ($content) { $content = base64_encode($content); $chmod = $fileVolume->chmod; @@ -92,9 +97,10 @@ public function saveStorageOnServer() $commands->push("chmod $chmod $path"); } } - } else if ($isDir == 'NOK' && $fileVolume->is_directory) { + } elseif ($isDir == 'NOK' && $fileVolume->is_directory) { $commands->push("mkdir -p $path > /dev/null 2>&1 || true"); } + return instant_remote_process($commands, $server); } } diff --git a/app/Models/LocalPersistentVolume.php b/app/Models/LocalPersistentVolume.php index 2a29f8abb..e48b8b405 100644 --- a/app/Models/LocalPersistentVolume.php +++ b/app/Models/LocalPersistentVolume.php @@ -14,14 +14,17 @@ public function application() { return $this->morphTo('resource'); } + public function service() { return $this->morphTo('resource'); } + public function database() { return $this->morphTo('resource'); } + public function standalone_postgresql() { return $this->morphTo('resource'); @@ -44,7 +47,7 @@ protected function mountPath(): Attribute protected function hostPath(): Attribute { return Attribute::make( - set: function (string|null $value) { + set: function (?string $value) { if ($value) { return Str::of($value)->trim()->start('/')->value; } else { diff --git a/app/Models/OauthSetting.php b/app/Models/OauthSetting.php index 4ab21aeec..c17c318f1 100644 --- a/app/Models/OauthSetting.php +++ b/app/Models/OauthSetting.php @@ -14,8 +14,8 @@ class OauthSetting extends Model protected function clientSecret(): Attribute { return Attribute::make( - get: fn (string | null $value) => empty($value) ? null : Crypt::decryptString($value), - set: fn (string | null $value) => empty($value) ? null : Crypt::encryptString($value), + get: fn (?string $value) => empty($value) ? null : Crypt::decryptString($value), + set: fn (?string $value) => empty($value) ? null : Crypt::encryptString($value), ); } } diff --git a/app/Models/PersonalAccessToken.php b/app/Models/PersonalAccessToken.php index f5b11883a..398046a7c 100644 --- a/app/Models/PersonalAccessToken.php +++ b/app/Models/PersonalAccessToken.php @@ -1,4 +1,5 @@ concat(['id']); + return PrivateKey::whereTeamId(currentTeam()->id)->select($selectArray->all()); } public function publicKey() { try { - return PublicKeyLoader::load($this->private_key)->getPublicKey()->toString('OpenSSH',['comment' => '']); + return PublicKeyLoader::load($this->private_key)->getPublicKey()->toString('OpenSSH', ['comment' => '']); } catch (\Throwable $e) { return 'Error loading private key'; } @@ -34,6 +35,7 @@ public function isEmpty() if ($this->servers()->count() === 0 && $this->applications()->count() === 0 && $this->githubApps()->count() === 0 && $this->gitlabApps()->count() === 0) { return true; } + return false; } diff --git a/app/Models/Project.php b/app/Models/Project.php index c2be8cc32..acc98e341 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -6,7 +6,7 @@ class Project extends BaseModel { protected $guarded = []; - static public function ownedByCurrentTeam() + public static function ownedByCurrentTeam() { return Project::whereTeamId(currentTeam()->id)->orderBy('name'); } @@ -27,15 +27,17 @@ protected static function booted() $project->settings()->delete(); $shared_variables = $project->environment_variables(); foreach ($shared_variables as $shared_variable) { - ray('Deleting project shared variable: ' . $shared_variable->name); + ray('Deleting project shared variable: '.$shared_variable->name); $shared_variable->delete(); } }); } + public function environment_variables() { return $this->hasMany(SharedEnvironmentVariable::class); } + public function environments() { return $this->hasMany(Environment::class); @@ -55,49 +57,59 @@ public function services() { return $this->hasManyThrough(Service::class, Environment::class); } + public function applications() { return $this->hasManyThrough(Application::class, Environment::class); } - public function postgresqls() { return $this->hasManyThrough(StandalonePostgresql::class, Environment::class); } + public function redis() { 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() { return $this->hasManyThrough(StandaloneMongodb::class, Environment::class); } + public function mysqls() { return $this->hasManyThrough(StandaloneMysql::class, Environment::class); } + public function mariadbs() { return $this->hasManyThrough(StandaloneMariadb::class, Environment::class); } + public function resource_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(); + 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(); } - public function databases() { + + public function databases() + { return $this->postgresqls()->get()->merge($this->redis()->get())->merge($this->mongodbs()->get())->merge($this->mysqls()->get())->merge($this->mariadbs()->get())->merge($this->keydbs()->get())->merge($this->dragonflies()->get())->merge($this->clickhouses()->get()); } } diff --git a/app/Models/S3Storage.php b/app/Models/S3Storage.php index dc0b93466..278ee5995 100644 --- a/app/Models/S3Storage.php +++ b/app/Models/S3Storage.php @@ -11,17 +11,20 @@ class S3Storage extends BaseModel use HasFactory; protected $guarded = []; + protected $casts = [ 'is_usable' => 'boolean', 'key' => 'encrypted', 'secret' => 'encrypted', ]; - static public function ownedByCurrentTeam(array $select = ['*']) + public static function ownedByCurrentTeam(array $select = ['*']) { $selectArray = collect($select)->concat(['id']); + return S3Storage::whereTeamId(currentTeam()->id)->select($selectArray->all())->orderBy('name'); } + public function isUsable() { return $this->is_usable; @@ -31,6 +34,7 @@ public function team() { return $this->belongsTo(Team::class); } + public function awsUrl() { return "{$this->endpoint}/{$this->bucket}"; diff --git a/app/Models/ScheduledDatabaseBackup.php b/app/Models/ScheduledDatabaseBackup.php index 2cf62cac2..edd840e7d 100644 --- a/app/Models/ScheduledDatabaseBackup.php +++ b/app/Models/ScheduledDatabaseBackup.php @@ -2,7 +2,6 @@ namespace App\Models; - use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\MorphTo; @@ -30,6 +29,7 @@ public function s3() { return $this->belongsTo(S3Storage::class, 's3_storage_id'); } + public function get_last_days_backup_status($days = 7) { return $this->hasMany(ScheduledDatabaseBackupExecution::class)->where('created_at', '>=', now()->subDays($days))->get(); diff --git a/app/Models/ScheduledTask.php b/app/Models/ScheduledTask.php index 2ff391c59..1cb805e8e 100644 --- a/app/Models/ScheduledTask.php +++ b/app/Models/ScheduledTask.php @@ -13,14 +13,17 @@ public function service() { return $this->belongsTo(Service::class); } + public function application() { return $this->belongsTo(Application::class); } + public function latest_log(): HasOne { return $this->hasOne(ScheduledTaskExecution::class)->latest(); } + public function executions(): HasMany { return $this->hasMany(ScheduledTaskExecution::class); diff --git a/app/Models/Server.php b/app/Models/Server.php index 38c427dc4..b1419dc0e 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -3,7 +3,6 @@ namespace App\Models; use App\Actions\Server\InstallDocker; -use App\Actions\Server\StartSentinel; use App\Enums\ProxyTypes; use App\Jobs\PullSentinelImageJob; use App\Notifications\Server\Revived; @@ -13,16 +12,17 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Storage; -use Spatie\SchemalessAttributes\Casts\SchemalessAttributes; -use Spatie\SchemalessAttributes\SchemalessAttributesTrait; use Illuminate\Support\Str; use Illuminate\Support\Stringable; +use Spatie\SchemalessAttributes\Casts\SchemalessAttributes; +use Spatie\SchemalessAttributes\SchemalessAttributesTrait; use Spatie\Url\Url; use Symfony\Component\Yaml\Yaml; class Server extends BaseModel { use SchemalessAttributesTrait; + public static $batch_counter = 0; protected static function booted() @@ -55,39 +55,45 @@ protected static function booted() 'logdrain_axiom_api_key' => 'encrypted', 'logdrain_newrelic_license_key' => 'encrypted', ]; + protected $schemalessAttributes = [ 'proxy', ]; + protected $guarded = []; - static public function isReachable() + public static function isReachable() { return Server::ownedByCurrentTeam()->whereRelation('settings', 'is_reachable', true); } - static public function ownedByCurrentTeam(array $select = ['*']) + public static function ownedByCurrentTeam(array $select = ['*']) { $teamId = currentTeam()->id; $selectArray = collect($select)->concat(['id']); + return Server::whereTeamId($teamId)->with('settings', 'swarmDockers', 'standaloneDockers')->select($selectArray->all())->orderBy('name'); } - static public function isUsable() + public static function isUsable() { return Server::ownedByCurrentTeam()->whereRelation('settings', 'is_reachable', true)->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_swarm_worker', false)->whereRelation('settings', 'is_build_server', false)->whereRelation('settings', 'force_disabled', false); } - static public function destinationsByServer(string $server_id) + public static function destinationsByServer(string $server_id) { $server = Server::ownedByCurrentTeam()->get()->where('id', $server_id)->firstOrFail(); $standaloneDocker = collect($server->standaloneDockers->all()); $swarmDocker = collect($server->swarmDockers->all()); + return $standaloneDocker->concat($swarmDocker); } + public function settings() { return $this->hasOne(ServerSetting::class); } + public function addInitialNetwork() { if ($this->id === 0) { @@ -122,24 +128,25 @@ public function addInitialNetwork() } } } + public function setupDefault404Redirect() { - $dynamic_conf_path = $this->proxyPath() . "/dynamic"; + $dynamic_conf_path = $this->proxyPath().'/dynamic'; $proxy_type = $this->proxyType(); $redirect_url = $this->proxy->redirect_url; if ($proxy_type === 'TRAEFIK_V2') { $default_redirect_file = "$dynamic_conf_path/default_redirect_404.yaml"; - } else if ($proxy_type === 'CADDY') { + } elseif ($proxy_type === 'CADDY') { $default_redirect_file = "$dynamic_conf_path/default_redirect_404.caddy"; } if (empty($redirect_url)) { if ($proxy_type === 'CADDY') { - $conf = ":80, :443 { + $conf = ':80, :443 { respond 404 -}"; +}'; $conf = - "# This file is automatically generated by Coolify.\n" . - "# Do not edit it manually (only if you know what are you doing).\n\n" . + "# This file is automatically generated by Coolify.\n". + "# Do not edit it manually (only if you know what are you doing).\n\n". $conf; $base64 = base64_encode($conf); instant_remote_process([ @@ -147,56 +154,47 @@ public function setupDefault404Redirect() "echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null", ], $this); $this->reloadCaddy(); + return; } instant_remote_process([ "mkdir -p $dynamic_conf_path", "rm -f $default_redirect_file", ], $this); + return; } if ($proxy_type === 'TRAEFIK_V2') { $dynamic_conf = [ - 'http' => - [ - 'routers' => - [ - 'catchall' => - [ + 'http' => [ + 'routers' => [ + 'catchall' => [ 'entryPoints' => [ 0 => 'http', 1 => 'https', ], 'service' => 'noop', - 'rule' => "HostRegexp(`{catchall:.*}`)", + 'rule' => 'HostRegexp(`{catchall:.*}`)', 'priority' => 1, 'middlewares' => [ 0 => 'redirect-regexp@file', ], ], ], - 'services' => - [ - 'noop' => - [ - 'loadBalancer' => - [ - 'servers' => - [ - 0 => - [ + 'services' => [ + 'noop' => [ + 'loadBalancer' => [ + 'servers' => [ + 0 => [ 'url' => '', ], ], ], ], ], - 'middlewares' => - [ - 'redirect-regexp' => - [ - 'redirectRegex' => - [ + 'middlewares' => [ + 'redirect-regexp' => [ + 'redirectRegex' => [ 'regex' => '(.*)', 'replacement' => $redirect_url, 'permanent' => false, @@ -207,23 +205,22 @@ public function setupDefault404Redirect() ]; $conf = Yaml::dump($dynamic_conf, 12, 2); $conf = - "# This file is automatically generated by Coolify.\n" . - "# Do not edit it manually (only if you know what are you doing).\n\n" . + "# This file is automatically generated by Coolify.\n". + "# Do not edit it manually (only if you know what are you doing).\n\n". $conf; $base64 = base64_encode($conf); - } else if ($proxy_type === 'CADDY') { - $conf = ":80, :443 { + } elseif ($proxy_type === 'CADDY') { + $conf = ":80, :443 { redir $redirect_url }"; $conf = - "# This file is automatically generated by Coolify.\n" . - "# Do not edit it manually (only if you know what are you doing).\n\n" . + "# This file is automatically generated by Coolify.\n". + "# Do not edit it manually (only if you know what are you doing).\n\n". $conf; $base64 = base64_encode($conf); } - instant_remote_process([ "mkdir -p $dynamic_conf_path", "echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null", @@ -236,10 +233,11 @@ public function setupDefault404Redirect() $this->reloadCaddy(); } } + public function setupDynamicProxyConfiguration() { $settings = InstanceSettings::get(); - $dynamic_config_path = $this->proxyPath() . "/dynamic"; + $dynamic_config_path = $this->proxyPath().'/dynamic'; if ($this->proxyType() === 'TRAEFIK_V2') { $file = "$dynamic_config_path/coolify.yaml"; if (empty($settings->fqdn) || (isCloud() && $this->id !== 0)) { @@ -251,8 +249,7 @@ public function setupDynamicProxyConfiguration() $host = $url->getHost(); $schema = $url->getScheme(); $traefik_dynamic_conf = [ - 'http' => - [ + 'http' => [ 'middlewares' => [ 'redirect-to-https' => [ 'redirectscheme' => [ @@ -263,10 +260,8 @@ public function setupDynamicProxyConfiguration() 'compress' => true, ], ], - 'routers' => - [ - 'coolify-http' => - [ + 'routers' => [ + 'coolify-http' => [ 'middlewares' => [ 0 => 'gzip', ], @@ -276,8 +271,7 @@ public function setupDynamicProxyConfiguration() 'service' => 'coolify', 'rule' => "Host(`{$host}`)", ], - 'coolify-realtime-ws' => - [ + 'coolify-realtime-ws' => [ 'entryPoints' => [ 0 => 'http', ], @@ -285,29 +279,20 @@ public function setupDynamicProxyConfiguration() 'rule' => "Host(`{$host}`) && PathPrefix(`/app`)", ], ], - 'services' => - [ - 'coolify' => - [ - 'loadBalancer' => - [ - 'servers' => - [ - 0 => - [ + 'services' => [ + 'coolify' => [ + 'loadBalancer' => [ + 'servers' => [ + 0 => [ 'url' => 'http://coolify:80', ], ], ], ], - 'coolify-realtime' => - [ - 'loadBalancer' => - [ - 'servers' => - [ - 0 => - [ + 'coolify-realtime' => [ + 'loadBalancer' => [ + 'servers' => [ + 0 => [ 'url' => 'http://coolify-realtime:6001', ], ], @@ -345,8 +330,8 @@ public function setupDynamicProxyConfiguration() } $yaml = Yaml::dump($traefik_dynamic_conf, 12, 2); $yaml = - "# This file is automatically generated by Coolify.\n" . - "# Do not edit it manually (only if you know what are you doing).\n\n" . + "# This file is automatically generated by Coolify.\n". + "# Do not edit it manually (only if you know what are you doing).\n\n". $yaml; $base64 = base64_encode($yaml); @@ -359,7 +344,7 @@ public function setupDynamicProxyConfiguration() // ray($yaml); } } - } else if ($this->proxyType() === 'CADDY') { + } elseif ($this->proxyType() === 'CADDY') { $file = "$dynamic_config_path/coolify.caddy"; if (empty($settings->fqdn) || (isCloud() && $this->id !== 0)) { instant_remote_process([ @@ -385,12 +370,14 @@ public function setupDynamicProxyConfiguration() } } } + public function reloadCaddy() { return instant_remote_process([ - "docker exec coolify-proxy caddy reload --config /config/caddy/Caddyfile.autosave", + 'docker exec coolify-proxy caddy reload --config /config/caddy/Caddyfile.autosave', ], $this); } + public function proxyPath() { $base_path = config('coolify.base_config_path'); @@ -401,13 +388,15 @@ public function proxyPath() // The code needs to be modified as well, so maybe it does not worth it if ($proxyType === ProxyTypes::TRAEFIK_V2->value) { $proxy_path = $proxy_path; - } else if ($proxyType === ProxyTypes::CADDY->value) { - $proxy_path = $proxy_path . '/caddy'; - } else if ($proxyType === ProxyTypes::NGINX->value) { - $proxy_path = $proxy_path . '/nginx'; + } elseif ($proxyType === ProxyTypes::CADDY->value) { + $proxy_path = $proxy_path.'/caddy'; + } elseif ($proxyType === ProxyTypes::NGINX->value) { + $proxy_path = $proxy_path.'/nginx'; } + return $proxy_path; } + public function proxyType() { // $proxyType = $this->proxy->get('type'); @@ -421,6 +410,7 @@ public function proxyType() // } return data_get($this->proxy, 'type'); } + public function scopeWithProxy(): Builder { return $this->proxy->modelScope(); @@ -430,10 +420,12 @@ public function isLocalhost() { return $this->ip === 'host.docker.internal' || $this->id === 0; } - static public function buildServers($teamId) + + public static function buildServers($teamId) { return Server::whereTeamId($teamId)->whereRelation('settings', 'is_reachable', true)->whereRelation('settings', 'is_build_server', true); } + public function skipServer() { if ($this->ip === '1.2.3.4') { @@ -444,18 +436,22 @@ public function skipServer() // ray('force_disabled'); return true; } + return false; } + public function isForceDisabled() { return $this->settings->force_disabled; } + public function forceEnableServer() { $this->settings->update([ 'force_disabled' => false, ]); } + public function forceDisableServer() { $this->settings->update([ @@ -465,11 +461,12 @@ public function forceDisableServer() Storage::disk('ssh-keys')->delete($sshKeyFileLocation); Storage::disk('ssh-mux')->delete($this->muxFilename()); } + public function checkSentinel() { ray("Checking sentinel on server: {$this->name}"); if ($this->is_metrics_enabled) { - $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this, false); + $sentinel_found = instant_remote_process(['docker inspect coolify-sentinel'], $this, false); $sentinel_found = json_decode($sentinel_found, true); $status = data_get($sentinel_found, '0.State.Status', 'exited'); if ($status !== 'running') { @@ -480,6 +477,7 @@ public function checkSentinel() } } } + public function getMetrics() { if ($this->is_metrics_enabled) { @@ -488,13 +486,16 @@ public function getMetrics() $cpu = str($cpu)->explode("\n")->skip(1)->all(); $parsedCollection = collect($cpu)->flatMap(function ($item) { return collect(explode("\n", trim($item)))->map(function ($line) { - list($time, $value) = explode(',', trim($line)); + [$time, $value] = explode(',', trim($line)); + return [(int) $time, (float) $value]; }); })->toArray(); + return $parsedCollection; } } + public function isServerReady(int $tries = 3) { if ($this->skipServer()) { @@ -519,6 +520,7 @@ public function isServerReady(int $tries = 3) if ($this->unreachable_notification_sent === true) { $this->update(['unreachable_notification_sent' => false]); } + return true; } else { if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) { @@ -555,35 +557,43 @@ public function isServerReady(int $tries = 3) 'unreachable_count' => $this->unreachable_count + 1, ]); } + return false; } } + public function getDiskUsage() { return instant_remote_process(["df /| tail -1 | awk '{ print $5}' | sed 's/%//g'"], $this, false); } + public function definedResources() { $applications = $this->applications(); $databases = $this->databases(); $services = $this->services(); + return $applications->concat($databases)->concat($services->get()); } + public function stopUnmanaged($id) { return instant_remote_process(["docker stop -t 0 $id"], $this); } + public function restartUnmanaged($id) { return instant_remote_process(["docker restart $id"], $this); } + public function startUnmanaged($id) { return instant_remote_process(["docker start $id"], $this); } + public function getContainers(): Collection { - $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this, false); + $sentinel_found = instant_remote_process(['docker inspect coolify-sentinel'], $this, false); $sentinel_found = json_decode($sentinel_found, true); $status = data_get($sentinel_found, '0.State.Status', 'exited'); if ($status === 'running') { @@ -592,13 +602,14 @@ public function getContainers(): Collection return collect([]); } $containers = data_get(json_decode($containers, true), 'containers', []); + return collect($containers); } else { if ($this->isSwarm()) { $containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this, false); } else { - $containers = instant_remote_process(["docker container ls -q"], $this, false); - if (!$containers) { + $containers = instant_remote_process(['docker container ls -q'], $this, false); + if (! $containers) { return collect([]); } $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this, false); @@ -610,6 +621,7 @@ public function getContainers(): Collection return format_docker_command_output_to_json($containers); } } + public function loadUnmanagedContainers(): Collection { if ($this->isFunctional()) { @@ -617,17 +629,20 @@ public function loadUnmanagedContainers(): Collection $containers = format_docker_command_output_to_json($containers); $containers = $containers->map(function ($container) { $labels = data_get($container, 'Labels'); - if (!str($labels)->contains("coolify.managed")) { + if (! str($labels)->contains('coolify.managed')) { return $container; } + return null; }); $containers = $containers->filter(); + return collect($containers); } else { return collect([]); } } + public function hasDefinedResources() { $applications = $this->applications()->count() > 0; @@ -636,6 +651,7 @@ public function hasDefinedResources() if ($applications || $databases || $services) { return true; } + return false; } @@ -650,11 +666,13 @@ public function databases() $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) { return data_get($item, 'name') !== 'coolify-db'; })->flatten(); } + public function applications() { $applications = $this->destinations()->map(function ($standaloneDocker) { @@ -667,29 +685,35 @@ public function applications() Application::whereIn('id', $additionalApplicationIds)->get()->each(function ($application) use ($applications) { $applications->push($application); }); + return $applications; } + public function dockerComposeBasedApplications() { return $this->applications()->filter(function ($application) { return data_get($application, 'build_pack') === 'dockercompose'; }); } + public function dockerComposeBasedPreviewDeployments() { return $this->previews()->filter(function ($preview) { $applicationId = data_get($preview, 'application_id'); $application = Application::find($applicationId); - if (!$application) { + if (! $application) { return false; } + return data_get($application, 'build_pack') === 'dockercompose'; }); } + public function services() { return $this->hasMany(Service::class); } + public function getIp(): Attribute { return Attribute::make( @@ -700,10 +724,12 @@ public function getIp(): Attribute if ($this->isLocalhost()) { return base_ip(); } + return $this->ip; } ); } + public function previews() { return $this->destinations()->map(function ($standaloneDocker) { @@ -717,6 +743,7 @@ public function destinations() { $standalone_docker = $this->hasMany(StandaloneDocker::class)->get(); $swarm_docker = $this->hasMany(SwarmDocker::class)->get(); + // $additional_dockers = $this->belongsToMany(StandaloneDocker::class, 'additional_destinations')->withPivot('server_id')->get(); // return $standalone_docker->concat($swarm_docker)->concat($additional_dockers); return $standalone_docker->concat($swarm_docker); @@ -746,28 +773,34 @@ public function team() { return $this->belongsTo(Team::class); } + public function isProxyShouldRun() { if ($this->proxyType() === ProxyTypes::NONE->value || $this->settings->is_build_server) { return false; } + return true; } + public function isFunctional() { - $isFunctional = $this->settings->is_reachable && $this->settings->is_usable && !$this->settings->force_disabled; - ['private_key_filename' => $private_key_filename, 'mux_filename' => $mux_filename] = server_ssh_configuration($this); - if (!$isFunctional) { + $isFunctional = $this->settings->is_reachable && $this->settings->is_usable && ! $this->settings->force_disabled; + ['private_key_filename' => $private_key_filename, 'mux_filename' => $mux_filename] = server_ssh_configuration($this); + if (! $isFunctional) { Storage::disk('ssh-keys')->delete($private_key_filename); Storage::disk('ssh-mux')->delete($mux_filename); } + return $isFunctional; } + public function isLogDrainEnabled() { return $this->settings->is_logdrain_newrelic_enabled || $this->settings->is_logdrain_highlight_enabled || $this->settings->is_logdrain_axiom_enabled || $this->settings->is_logdrain_custom_enabled; } - public function validateOS(): bool | Stringable + + public function validateOS(): bool|Stringable { $os_release = instant_remote_process(['cat /etc/os-release'], $this); $releaseLines = collect(explode("\n", $os_release)); @@ -792,24 +825,28 @@ public function validateOS(): bool | Stringable return false; } } + public function isSwarm() { return data_get($this, 'settings.is_swarm_manager') || data_get($this, 'settings.is_swarm_worker'); } + public function isSwarmManager() { return data_get($this, 'settings.is_swarm_manager'); } + public function isSwarmWorker() { return data_get($this, 'settings.is_swarm_worker'); } + public function validateConnection() { config()->set('coolify.mux_enabled', false); $server = Server::find($this->id); - if (!$server) { + if (! $server) { return ['uptime' => false, 'error' => 'Server not found.']; } if ($server->skipServer()) { @@ -828,108 +865,130 @@ public function validateConnection() // $server->team?->notify(new Revived($server)); $server->update(['unreachable_notification_sent' => false]); } + return ['uptime' => true, 'error' => null]; } catch (\Throwable $e) { $server->settings()->update([ 'is_reachable' => false, ]); + return ['uptime' => false, 'error' => $e->getMessage()]; } } + public function installDocker() { $activity = InstallDocker::run($this); + return $activity; } + public function validateDockerEngine($throwError = false) { - $dockerBinary = instant_remote_process(["command -v docker"], $this, false, no_sudo: true); + $dockerBinary = instant_remote_process(['command -v docker'], $this, false, no_sudo: true); if (is_null($dockerBinary)) { $this->settings->is_usable = false; $this->settings->save(); if ($throwError) { throw new \Exception('Server is not usable. Docker Engine is not installed.'); } + return false; } try { - instant_remote_process(["docker version"], $this); + instant_remote_process(['docker version'], $this); } catch (\Throwable $e) { $this->settings->is_usable = false; $this->settings->save(); if ($throwError) { throw new \Exception('Server is not usable. Docker Engine is not running.'); } + return false; } $this->settings->is_usable = true; $this->settings->save(); $this->validateCoolifyNetwork(isSwarm: false, isBuildServer: $this->settings->is_build_server); + return true; } + public function validateDockerCompose($throwError = false) { - $dockerCompose = instant_remote_process(["docker compose version"], $this, false); + $dockerCompose = instant_remote_process(['docker compose version'], $this, false); if (is_null($dockerCompose)) { $this->settings->is_usable = false; $this->settings->save(); if ($throwError) { throw new \Exception('Server is not usable. Docker Compose is not installed.'); } + return false; } $this->settings->is_usable = true; $this->settings->save(); + return true; } + public function validateDockerSwarm() { - $swarmStatus = instant_remote_process(["docker info|grep -i swarm"], $this, false); + $swarmStatus = instant_remote_process(['docker info|grep -i swarm'], $this, false); $swarmStatus = str($swarmStatus)->trim()->after(':')->trim(); if ($swarmStatus === 'inactive') { throw new \Exception('Docker Swarm is not initiated. Please join the server to a swarm before continuing.'); + return false; } $this->settings->is_usable = true; $this->settings->save(); $this->validateCoolifyNetwork(isSwarm: true); + return true; } + public function validateDockerEngineVersion() { - $dockerVersionRaw = instant_remote_process(["docker version --format json"], $this, false); + $dockerVersionRaw = instant_remote_process(['docker version --format json'], $this, false); $dockerVersionJson = json_decode($dockerVersionRaw, true); $dockerVersion = data_get($dockerVersionJson, 'Server.Version', '0.0.0'); $dockerVersion = checkMinimumDockerEngineVersion($dockerVersion); if (is_null($dockerVersion)) { $this->settings->is_usable = false; $this->settings->save(); + return false; } $this->settings->is_reachable = true; $this->settings->is_usable = true; $this->settings->save(); + return true; } + public function validateCoolifyNetwork($isSwarm = false, $isBuildServer = false) { if ($isBuildServer) { return; } if ($isSwarm) { - return instant_remote_process(["docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true"], $this, false); + return instant_remote_process(['docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true'], $this, false); } else { - return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false); + return instant_remote_process(['docker network create coolify --attachable >/dev/null 2>&1 || true'], $this, false); } } + public function isNonRoot() { if ($this->user instanceof Stringable) { return $this->user->value() !== 'root'; } + return $this->user !== 'root'; } - public function isBuildServer() { + + public function isBuildServer() + { return $this->settings->is_build_server; } } diff --git a/app/Models/Service.php b/app/Models/Service.php index ac7c15dcf..7851eb58a 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -10,6 +10,7 @@ class Service extends BaseModel { use HasFactory, SoftDeletes; + protected $guarded = []; public function isConfigurationChanged(bool $save = false) @@ -26,7 +27,7 @@ public function isConfigurationChanged(bool $save = false) $databaseStorages = $this->databases()->get()->pluck('persistentStorages')->flatten()->sortBy('id'); $storages = $applicationStorages->merge($databaseStorages)->implode('updated_at'); - $newConfigHash = $images . $domains . $images . $storages; + $newConfigHash = $images.$domains.$images.$storages; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -35,6 +36,7 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } if ($oldConfigHash === $newConfigHash) { @@ -44,37 +46,45 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } } + public function isExited() { return (bool) str($this->status())->contains('exited'); } + public function type() { return 'service'; } + public function project() { return data_get($this, 'environment.project'); } + public function team() { return data_get($this, 'environment.project.team'); } + public function tags() { 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); + instant_remote_process(['rm -rf '.$this->workdir()], $server, false); } } + public function status() { $applications = $this->applications; @@ -98,9 +108,9 @@ public function status() } else { $complexStatus = 'running'; } - } else if ($status->startsWith('restarting')) { + } elseif ($status->startsWith('restarting')) { $complexStatus = 'degraded'; - } else if ($status->startsWith('exited')) { + } elseif ($status->startsWith('exited')) { $complexStatus = 'exited'; } if ($health->value() === 'healthy') { @@ -127,9 +137,9 @@ public function status() } else { $complexStatus = 'running'; } - } else if ($status->startsWith('restarting')) { + } elseif ($status->startsWith('restarting')) { $complexStatus = 'degraded'; - } else if ($status->startsWith('exited')) { + } elseif ($status->startsWith('exited')) { $complexStatus = 'exited'; } if ($health->value() === 'healthy') { @@ -141,8 +151,10 @@ public function status() $complexHealth = 'unhealthy'; } } + return "{$complexStatus}:{$complexHealth}"; } + public function extraFields() { $fields = collect([]); @@ -686,8 +698,10 @@ public function extraFields() break; } } + return $fields; } + public function saveExtraFields($fields) { foreach ($fields as $field) { @@ -708,17 +722,20 @@ public function saveExtraFields($fields) } } } + public function link() { if (data_get($this, 'environment.project.uuid')) { return route('project.service.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), 'environment_name' => data_get($this, 'environment.name'), - 'service_uuid' => data_get($this, 'uuid') + 'service_uuid' => data_get($this, 'uuid'), ]); } + return null; } + public function failedTaskLink($task_uuid) { if (data_get($this, 'environment.project.uuid')) { @@ -726,38 +743,48 @@ public function failedTaskLink($task_uuid) 'project_uuid' => data_get($this, 'environment.project.uuid'), 'environment_name' => data_get($this, 'environment.name'), 'service_uuid' => data_get($this, 'uuid'), - 'task_uuid' => $task_uuid + 'task_uuid' => $task_uuid, ]); } + return null; } + public function documentation() { $services = get_service_templates(); $service = data_get($services, str($this->name)->beforeLast('-')->value, []); + return data_get($service, 'documentation', config('constants.docs.base_url')); } + public function applications() { return $this->hasMany(ServiceApplication::class); } + public function databases() { return $this->hasMany(ServiceDatabase::class); } + public function destination() { return $this->morphTo(); } + public function environment() { return $this->belongsTo(Environment::class); } + public function server() { return $this->belongsTo(Server::class); } - public function byUuid(string $uuid) { + + public function byUuid(string $uuid) + { $app = $this->applications()->whereUuid($uuid)->first(); if ($app) { return $app; @@ -766,8 +793,10 @@ public function byUuid(string $uuid) { if ($db) { return $db; } + return null; } + public function byName(string $name) { $app = $this->applications()->whereName($name)->first(); @@ -778,24 +807,30 @@ public function byName(string $name) if ($db) { return $db; } + return null; } + public function scheduled_tasks(): HasMany { return $this->hasMany(ScheduledTask::class)->orderBy('name', 'asc'); } + public function environment_variables(): HasMany { return $this->hasMany(EnvironmentVariable::class)->orderBy('key', 'asc'); } + public function environment_variables_preview(): HasMany { return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->orderBy('key', 'asc'); } + public function workdir() { - return service_configuration_dir() . "/{$this->uuid}"; + return service_configuration_dir()."/{$this->uuid}"; } + public function saveComposeConfigs() { $workdir = $this->workdir(); @@ -805,12 +840,12 @@ public function saveComposeConfigs() $docker_compose_base64 = base64_encode($this->docker_compose); $commands[] = "echo $docker_compose_base64 | base64 -d | tee docker-compose.yml > /dev/null"; $envs = $this->environment_variables()->get(); - $commands[] = "rm -f .env || true"; + $commands[] = 'rm -f .env || true'; foreach ($envs as $env) { $commands[] = "echo '{$env->key}={$env->real_value}' >> .env"; } if ($envs->count() === 0) { - $commands[] = "touch .env"; + $commands[] = 'touch .env'; } instant_remote_process($commands, $this->server); } @@ -819,9 +854,11 @@ public function parse(bool $isNew = false): Collection { return parseDockerComposeFile($this, $isNew); } + public function networks() { $networks = getTopLevelNetworks($this); + // ray($networks); return $networks; } diff --git a/app/Models/ServiceApplication.php b/app/Models/ServiceApplication.php index f8fcda004..98c1cf4e7 100644 --- a/app/Models/ServiceApplication.php +++ b/app/Models/ServiceApplication.php @@ -9,6 +9,7 @@ class ServiceApplication extends BaseModel { use HasFactory, SoftDeletes; + protected $guarded = []; protected static function booted() @@ -19,34 +20,43 @@ protected static function booted() $service->fileStorages()->delete(); }); } + public function restart() { - $container_id = $this->name . '-' . $this->service->uuid; + $container_id = $this->name.'-'.$this->service->uuid; instant_remote_process(["docker restart {$container_id}"], $this->service->server); } + public function isLogDrainEnabled() { return data_get($this, 'is_log_drain_enabled', false); } + public function isStripprefixEnabled() { return data_get($this, 'is_stripprefix_enabled', true); } + public function isGzipEnabled() { return data_get($this, 'is_gzip_enabled', true); } + public function type() { return 'service'; } + public function team() { return data_get($this, 'environment.project.team'); } - public function workdir() { - return service_configuration_dir() . "/{$this->service->uuid}"; + + public function workdir() + { + return service_configuration_dir()."/{$this->service->uuid}"; } + public function serviceType() { $found = str(collect(SPECIFIC_SERVICES)->filter(function ($service) { @@ -55,20 +65,25 @@ public function serviceType() if ($found->isNotEmpty()) { return $found; } + return null; } + public function service() { return $this->belongsTo(Service::class); } + public function persistentStorages() { return $this->morphMany(LocalPersistentVolume::class, 'resource'); } + public function fileStorages() { return $this->morphMany(LocalFileVolume::class, 'resource'); } + public function fqdns(): Attribute { return Attribute::make( @@ -77,6 +92,7 @@ public function fqdns(): Attribute : explode(',', $this->fqdn), ); } + public function getFilesFromServer(bool $isInit = false) { getFilesystemVolumesFromServer($this, $isInit); diff --git a/app/Models/ServiceDatabase.php b/app/Models/ServiceDatabase.php index 9d90641e1..4a749913e 100644 --- a/app/Models/ServiceDatabase.php +++ b/app/Models/ServiceDatabase.php @@ -8,6 +8,7 @@ class ServiceDatabase extends BaseModel { use HasFactory, SoftDeletes; + protected $guarded = []; protected static function booted() @@ -17,39 +18,48 @@ protected static function booted() $service->fileStorages()->delete(); }); } + public function restart() { - $container_id = $this->name . '-' . $this->service->uuid; + $container_id = $this->name.'-'.$this->service->uuid; remote_process(["docker restart {$container_id}"], $this->service->server); } + public function isLogDrainEnabled() { return data_get($this, 'is_log_drain_enabled', false); } + public function isStripprefixEnabled() { return data_get($this, 'is_stripprefix_enabled', true); } + public function isGzipEnabled() { return data_get($this, 'is_gzip_enabled', true); } + public function type() { return 'service'; } + public function serviceType() { return null; } + public function databaseType() { $image = str($this->image)->before(':'); if ($image->value() === 'postgres') { $image = 'postgresql'; } + return "standalone-$image"; } + public function getServiceDatabaseUrl() { $port = $this->public_port; @@ -57,31 +67,40 @@ public function getServiceDatabaseUrl() if ($this->service->server->isLocalhost() || isDev()) { $realIp = base_ip(); } + return "{$realIp}:{$port}"; } + public function team() { return data_get($this, 'environment.project.team'); } - public function workdir() { - return service_configuration_dir() . "/{$this->service->uuid}"; + + public function workdir() + { + return service_configuration_dir()."/{$this->service->uuid}"; } + public function service() { return $this->belongsTo(Service::class); } + public function persistentStorages() { return $this->morphMany(LocalPersistentVolume::class, 'resource'); } + public function fileStorages() { return $this->morphMany(LocalFileVolume::class, 'resource'); } + public function getFilesFromServer(bool $isInit = false) { getFilesystemVolumesFromServer($this, $isInit); } + public function scheduledBackups() { return $this->morphMany(ScheduledDatabaseBackup::class, 'database'); diff --git a/app/Models/SharedEnvironmentVariable.php b/app/Models/SharedEnvironmentVariable.php index 5fad8fd96..aab8b8735 100644 --- a/app/Models/SharedEnvironmentVariable.php +++ b/app/Models/SharedEnvironmentVariable.php @@ -2,12 +2,12 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; class SharedEnvironmentVariable extends Model { protected $guarded = []; + protected $casts = [ 'key' => 'string', 'value' => 'encrypted', diff --git a/app/Models/StandaloneClickhouse.php b/app/Models/StandaloneClickhouse.php index 2197d51df..c5e252c34 100644 --- a/app/Models/StandaloneClickhouse.php +++ b/app/Models/StandaloneClickhouse.php @@ -12,6 +12,7 @@ class StandaloneClickhouse extends BaseModel use HasFactory, SoftDeletes; protected $guarded = []; + protected $casts = [ 'clickhouse_password' => 'encrypted', ]; @@ -20,12 +21,12 @@ protected static function booted() { static::created(function ($database) { LocalPersistentVolume::create([ - 'name' => 'clickhouse-data-' . $database->uuid, + 'name' => 'clickhouse-data-'.$database->uuid, 'mount_path' => '/bitnami/clickhouse', 'host_path' => null, 'resource_id' => $database->id, 'resource_type' => $database->getMorphClass(), - 'is_readonly' => true + 'is_readonly' => true, ]); }); static::deleting(function ($database) { @@ -42,9 +43,10 @@ protected static function booted() $database->tags()->detach(); }); } + public function isConfigurationChanged(bool $save = false) { - $newConfigHash = $this->image . $this->ports_mappings; + $newConfigHash = $this->image.$this->ports_mappings; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -53,6 +55,7 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } if ($oldConfigHash === $newConfigHash) { @@ -62,29 +65,35 @@ public function isConfigurationChanged(bool $save = false) $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}"; + 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); + instant_remote_process(['rm -rf '.$this->workdir()], $server, false); } } + public function realStatus() { return $this->getRawOriginal('status'); } + public function status(): Attribute { return Attribute::make( @@ -92,49 +101,56 @@ public function status(): Attribute if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; - } else if (str($value)->contains(':')) { + } elseif (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(':')) { + } elseif (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') + 'database_uuid' => data_get($this, 'uuid'), ]); } + return null; } + public function isLogDrainEnabled() { return data_get($this, 'is_log_drain_enabled', false); @@ -143,7 +159,7 @@ public function isLogDrainEnabled() public function portsMappings(): Attribute { return Attribute::make( - set: fn ($value) => $value === "" ? null : $value, + set: fn ($value) => $value === '' ? null : $value, ); } @@ -156,17 +172,20 @@ public function portsMappingsArray(): Attribute ); } + 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) { + 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}"; diff --git a/app/Models/StandaloneDocker.php b/app/Models/StandaloneDocker.php index 228a82086..1ef6ff587 100644 --- a/app/Models/StandaloneDocker.php +++ b/app/Models/StandaloneDocker.php @@ -20,26 +20,32 @@ public function redis() { return $this->morphMany(StandaloneRedis::class, 'destination'); } + public function mongodbs() { return $this->morphMany(StandaloneMongodb::class, 'destination'); } + public function mysqls() { return $this->morphMany(StandaloneMysql::class, 'destination'); } + public function mariadbs() { 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'); @@ -62,6 +68,7 @@ public function databases() $mongodbs = $this->mongodbs; $mysqls = $this->mysqls; $mariadbs = $this->mariadbs; + return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs); } diff --git a/app/Models/StandaloneDragonfly.php b/app/Models/StandaloneDragonfly.php index 7b18666b8..8c739d984 100644 --- a/app/Models/StandaloneDragonfly.php +++ b/app/Models/StandaloneDragonfly.php @@ -10,7 +10,9 @@ class StandaloneDragonfly extends BaseModel { use HasFactory, SoftDeletes; + protected $guarded = []; + protected $casts = [ 'dragonfly_password' => 'encrypted', ]; @@ -19,12 +21,12 @@ protected static function booted() { static::created(function ($database) { LocalPersistentVolume::create([ - 'name' => 'dragonfly-data-' . $database->uuid, + 'name' => 'dragonfly-data-'.$database->uuid, 'mount_path' => '/data', 'host_path' => null, 'resource_id' => $database->id, 'resource_type' => $database->getMorphClass(), - 'is_readonly' => true + 'is_readonly' => true, ]); }); static::deleting(function ($database) { @@ -41,9 +43,10 @@ protected static function booted() $database->tags()->detach(); }); } + public function isConfigurationChanged(bool $save = false) { - $newConfigHash = $this->image . $this->ports_mappings; + $newConfigHash = $this->image.$this->ports_mappings; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -52,6 +55,7 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } if ($oldConfigHash === $newConfigHash) { @@ -61,29 +65,35 @@ public function isConfigurationChanged(bool $save = false) $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}"; + 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); + instant_remote_process(['rm -rf '.$this->workdir()], $server, false); } } + public function realStatus() { return $this->getRawOriginal('status'); } + public function status(): Attribute { return Attribute::make( @@ -91,53 +101,61 @@ public function status(): Attribute if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; - } else if (str($value)->contains(':')) { + } elseif (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(':')) { + } elseif (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') + 'database_uuid' => data_get($this, 'uuid'), ]); } + return null; } + public function isLogDrainEnabled() { return data_get($this, 'is_log_drain_enabled', false); @@ -146,7 +164,7 @@ public function isLogDrainEnabled() public function portsMappings(): Attribute { return Attribute::make( - set: fn ($value) => $value === "" ? null : $value, + set: fn ($value) => $value === '' ? null : $value, ); } @@ -164,9 +182,10 @@ public function type(): string { return 'standalone-dragonfly'; } + public function get_db_url(bool $useInternal = false): string { - if ($this->is_public && !$useInternal) { + 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"; diff --git a/app/Models/StandaloneKeydb.php b/app/Models/StandaloneKeydb.php index c2c1b98da..5216681c9 100644 --- a/app/Models/StandaloneKeydb.php +++ b/app/Models/StandaloneKeydb.php @@ -10,7 +10,9 @@ class StandaloneKeydb extends BaseModel { use HasFactory, SoftDeletes; + protected $guarded = []; + protected $casts = [ 'keydb_password' => 'encrypted', ]; @@ -19,12 +21,12 @@ protected static function booted() { static::created(function ($database) { LocalPersistentVolume::create([ - 'name' => 'keydb-data-' . $database->uuid, + 'name' => 'keydb-data-'.$database->uuid, 'mount_path' => '/data', 'host_path' => null, 'resource_id' => $database->id, 'resource_type' => $database->getMorphClass(), - 'is_readonly' => true + 'is_readonly' => true, ]); }); static::deleting(function ($database) { @@ -41,9 +43,10 @@ protected static function booted() $database->tags()->detach(); }); } + public function isConfigurationChanged(bool $save = false) { - $newConfigHash = $this->image . $this->ports_mappings . $this->keydb_conf; + $newConfigHash = $this->image.$this->ports_mappings.$this->keydb_conf; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -52,6 +55,7 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } if ($oldConfigHash === $newConfigHash) { @@ -61,23 +65,27 @@ public function isConfigurationChanged(bool $save = false) $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}"; + 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); + instant_remote_process(['rm -rf '.$this->workdir()], $server, false); } } @@ -85,6 +93,7 @@ public function realStatus() { return $this->getRawOriginal('status'); } + public function status(): Attribute { return Attribute::make( @@ -92,53 +101,61 @@ public function status(): Attribute if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; - } else if (str($value)->contains(':')) { + } elseif (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(':')) { + } elseif (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') + 'database_uuid' => data_get($this, 'uuid'), ]); } + return null; } + public function isLogDrainEnabled() { return data_get($this, 'is_log_drain_enabled', false); @@ -147,7 +164,7 @@ public function isLogDrainEnabled() public function portsMappings(): Attribute { return Attribute::make( - set: fn ($value) => $value === "" ? null : $value, + set: fn ($value) => $value === '' ? null : $value, ); } @@ -165,9 +182,10 @@ public function type(): string { return 'standalone-keydb'; } + public function get_db_url(bool $useInternal = false): string { - if ($this->is_public && !$useInternal) { + 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"; diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php index 5e18bbfde..33fd2cbc2 100644 --- a/app/Models/StandaloneMariadb.php +++ b/app/Models/StandaloneMariadb.php @@ -4,7 +4,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; @@ -13,6 +12,7 @@ class StandaloneMariadb extends BaseModel use HasFactory, SoftDeletes; protected $guarded = []; + protected $casts = [ 'mariadb_password' => 'encrypted', ]; @@ -21,12 +21,12 @@ protected static function booted() { static::created(function ($database) { LocalPersistentVolume::create([ - 'name' => 'mariadb-data-' . $database->uuid, + 'name' => 'mariadb-data-'.$database->uuid, 'mount_path' => '/var/lib/mysql', 'host_path' => null, 'resource_id' => $database->id, 'resource_type' => $database->getMorphClass(), - 'is_readonly' => true + 'is_readonly' => true, ]); }); static::deleting(function ($database) { @@ -43,9 +43,10 @@ protected static function booted() $database->tags()->detach(); }); } + public function isConfigurationChanged(bool $save = false) { - $newConfigHash = $this->image . $this->ports_mappings . $this->mariadb_conf; + $newConfigHash = $this->image.$this->ports_mappings.$this->mariadb_conf; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -54,6 +55,7 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } if ($oldConfigHash === $newConfigHash) { @@ -63,29 +65,35 @@ public function isConfigurationChanged(bool $save = false) $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}"; + 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); + instant_remote_process(['rm -rf '.$this->workdir()], $server, false); } } + public function realStatus() { - return $this->getRawOriginal('status'); + return $this->getRawOriginal('status'); } + public function status(): Attribute { return Attribute::make( @@ -93,56 +101,66 @@ public function status(): Attribute if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; - } else if (str($value)->contains(':')) { + } elseif (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(':')) { + } elseif (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() { + + 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') + 'database_uuid' => data_get($this, 'uuid'), ]); } + return null; } + public function isLogDrainEnabled() { return data_get($this, 'is_log_drain_enabled', false); } + public function type(): string { return 'standalone-mariadb'; @@ -151,7 +169,7 @@ public function type(): string public function portsMappings(): Attribute { return Attribute::make( - set: fn ($value) => $value === "" ? null : $value, + set: fn ($value) => $value === '' ? null : $value, ); } @@ -167,7 +185,7 @@ public function portsMappingsArray(): Attribute public function get_db_url(bool $useInternal = false): string { - if ($this->is_public && !$useInternal) { + if ($this->is_public && ! $useInternal) { return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}"; } else { return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->uuid}:3306/{$this->mariadb_database}"; diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php index 8e4d327a3..0cc52b3f7 100644 --- a/app/Models/StandaloneMongodb.php +++ b/app/Models/StandaloneMongodb.php @@ -10,26 +10,27 @@ class StandaloneMongodb extends BaseModel { use HasFactory, SoftDeletes; + protected $guarded = []; protected static function booted() { static::created(function ($database) { LocalPersistentVolume::create([ - 'name' => 'mongodb-configdb-' . $database->uuid, + 'name' => 'mongodb-configdb-'.$database->uuid, 'mount_path' => '/data/configdb', 'host_path' => null, 'resource_id' => $database->id, 'resource_type' => $database->getMorphClass(), - 'is_readonly' => true + 'is_readonly' => true, ]); LocalPersistentVolume::create([ - 'name' => 'mongodb-db-' . $database->uuid, + 'name' => 'mongodb-db-'.$database->uuid, 'mount_path' => '/data/db', 'host_path' => null, 'resource_id' => $database->id, 'resource_type' => $database->getMorphClass(), - 'is_readonly' => true + 'is_readonly' => true, ]); }); static::deleting(function ($database) { @@ -46,9 +47,10 @@ protected static function booted() $database->tags()->detach(); }); } + public function isConfigurationChanged(bool $save = false) { - $newConfigHash = $this->image . $this->ports_mappings . $this->mongo_conf; + $newConfigHash = $this->image.$this->ports_mappings.$this->mongo_conf; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -57,6 +59,7 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } if ($oldConfigHash === $newConfigHash) { @@ -66,29 +69,35 @@ public function isConfigurationChanged(bool $save = false) $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}"; + 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); + instant_remote_process(['rm -rf '.$this->workdir()], $server, false); } } + public function realStatus() { - return $this->getRawOriginal('status'); + return $this->getRawOriginal('status'); } + public function status(): Attribute { return Attribute::make( @@ -96,56 +105,66 @@ public function status(): Attribute if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; - } else if (str($value)->contains(':')) { + } elseif (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(':')) { + } elseif (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() { + + public function project() + { return data_get($this, 'environment.project'); } + public function team() { return data_get($this, 'environment.project.team'); } + public function isLogDrainEnabled() { return data_get($this, 'is_log_drain_enabled', false); } + 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') + 'database_uuid' => data_get($this, 'uuid'), ]); } + return null; } + public function mongoInitdbRootPassword(): Attribute { return Attribute::make( @@ -155,15 +174,17 @@ public function mongoInitdbRootPassword(): Attribute } catch (\Throwable $th) { $this->mongo_initdb_root_password = encrypt($value); $this->save(); + return $value; } } ); } + public function portsMappings(): Attribute { return Attribute::make( - set: fn ($value) => $value === "" ? null : $value, + set: fn ($value) => $value === '' ? null : $value, ); } @@ -181,14 +202,16 @@ public function type(): string { return 'standalone-mongodb'; } + public function get_db_url(bool $useInternal = false) { - if ($this->is_public && !$useInternal) { + if ($this->is_public && ! $useInternal) { return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true"; } else { return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->uuid}:27017/?directConnection=true"; } } + public function environment() { return $this->belongsTo(Environment::class); diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php index eede451d7..174736f77 100644 --- a/app/Models/StandaloneMysql.php +++ b/app/Models/StandaloneMysql.php @@ -12,6 +12,7 @@ class StandaloneMysql extends BaseModel use HasFactory, SoftDeletes; protected $guarded = []; + protected $casts = [ 'mysql_password' => 'encrypted', 'mysql_root_password' => 'encrypted', @@ -21,12 +22,12 @@ protected static function booted() { static::created(function ($database) { LocalPersistentVolume::create([ - 'name' => 'mysql-data-' . $database->uuid, + 'name' => 'mysql-data-'.$database->uuid, 'mount_path' => '/var/lib/mysql', 'host_path' => null, 'resource_id' => $database->id, 'resource_type' => $database->getMorphClass(), - 'is_readonly' => true + 'is_readonly' => true, ]); }); static::deleting(function ($database) { @@ -43,9 +44,10 @@ protected static function booted() $database->tags()->detach(); }); } + public function isConfigurationChanged(bool $save = false) { - $newConfigHash = $this->image . $this->ports_mappings . $this->mysql_conf; + $newConfigHash = $this->image.$this->ports_mappings.$this->mysql_conf; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -54,6 +56,7 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } if ($oldConfigHash === $newConfigHash) { @@ -63,29 +66,35 @@ public function isConfigurationChanged(bool $save = false) $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}"; + 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); + instant_remote_process(['rm -rf '.$this->workdir()], $server, false); } } + public function realStatus() { - return $this->getRawOriginal('status'); + return $this->getRawOriginal('status'); } + public function status(): Attribute { return Attribute::make( @@ -93,52 +102,61 @@ public function status(): Attribute if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; - } else if (str($value)->contains(':')) { + } elseif (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(':')) { + } elseif (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() { + + 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') + 'database_uuid' => data_get($this, 'uuid'), ]); } + return null; } + public function type(): string { return 'standalone-mysql'; @@ -152,7 +170,7 @@ public function isLogDrainEnabled() public function portsMappings(): Attribute { return Attribute::make( - set: fn ($value) => $value === "" ? null : $value, + set: fn ($value) => $value === '' ? null : $value, ); } @@ -168,7 +186,7 @@ public function portsMappingsArray(): Attribute public function get_db_url(bool $useInternal = false): string { - if ($this->is_public && !$useInternal) { + if ($this->is_public && ! $useInternal) { return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mysql_database}"; } else { return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->uuid}:3306/{$this->mysql_database}"; diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index cf449a815..a5bf4dc2a 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -12,6 +12,7 @@ class StandalonePostgresql extends BaseModel use HasFactory, SoftDeletes; protected $guarded = []; + protected $casts = [ 'init_scripts' => 'array', 'postgres_password' => 'encrypted', @@ -21,12 +22,12 @@ protected static function booted() { static::created(function ($database) { LocalPersistentVolume::create([ - 'name' => 'postgres-data-' . $database->uuid, + 'name' => 'postgres-data-'.$database->uuid, 'mount_path' => '/var/lib/postgresql/data', 'host_path' => null, 'resource_id' => $database->id, 'resource_type' => $database->getMorphClass(), - 'is_readonly' => true + 'is_readonly' => true, ]); }); static::deleting(function ($database) { @@ -43,21 +44,24 @@ protected static function booted() $database->tags()->detach(); }); } + public function workdir() { - return database_configuration_dir() . "/{$this->uuid}"; + 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); + 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 = $this->image.$this->ports_mappings.$this->postgres_initdb_args.$this->postgres_host_auth_method; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -66,6 +70,7 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } if ($oldConfigHash === $newConfigHash) { @@ -75,17 +80,21 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } } + public function isExited() { return (bool) str($this->status)->startsWith('exited'); } + public function realStatus() { return $this->getRawOriginal('status'); } + public function status(): Attribute { return Attribute::make( @@ -93,49 +102,56 @@ public function status(): Attribute if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; - } else if (str($value)->contains(':')) { + } elseif (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(':')) { + } elseif (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') + 'database_uuid' => data_get($this, 'uuid'), ]); } + return null; } + public function isLogDrainEnabled() { return data_get($this, 'is_log_drain_enabled', false); @@ -144,7 +160,7 @@ public function isLogDrainEnabled() public function portsMappings(): Attribute { return Attribute::make( - set: fn ($value) => $value === "" ? null : $value, + set: fn ($value) => $value === '' ? null : $value, ); } @@ -157,17 +173,20 @@ public function portsMappingsArray(): Attribute ); } + public function team() { return data_get($this, 'environment.project.team'); } + public function type(): string { return 'standalone-postgresql'; } + public function get_db_url(bool $useInternal = false): string { - if ($this->is_public && !$useInternal) { + if ($this->is_public && ! $useInternal) { return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}"; } else { return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}"; diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php index da4701df9..ed379750e 100644 --- a/app/Models/StandaloneRedis.php +++ b/app/Models/StandaloneRedis.php @@ -10,18 +10,19 @@ class StandaloneRedis extends BaseModel { use HasFactory, SoftDeletes; + protected $guarded = []; protected static function booted() { static::created(function ($database) { LocalPersistentVolume::create([ - 'name' => 'redis-data-' . $database->uuid, + 'name' => 'redis-data-'.$database->uuid, 'mount_path' => '/data', 'host_path' => null, 'resource_id' => $database->id, 'resource_type' => $database->getMorphClass(), - 'is_readonly' => true + 'is_readonly' => true, ]); }); static::deleting(function ($database) { @@ -38,9 +39,10 @@ protected static function booted() $database->tags()->detach(); }); } + public function isConfigurationChanged(bool $save = false) { - $newConfigHash = $this->image . $this->ports_mappings . $this->redis_conf; + $newConfigHash = $this->image.$this->ports_mappings.$this->redis_conf; $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash = md5($newConfigHash); $oldConfigHash = data_get($this, 'config_hash'); @@ -49,6 +51,7 @@ public function isConfigurationChanged(bool $save = false) $this->config_hash = $newConfigHash; $this->save(); } + return true; } if ($oldConfigHash === $newConfigHash) { @@ -58,29 +61,35 @@ public function isConfigurationChanged(bool $save = false) $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}"; + 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); + instant_remote_process(['rm -rf '.$this->workdir()], $server, false); } } + public function realStatus() { return $this->getRawOriginal('status'); } + public function status(): Attribute { return Attribute::make( @@ -88,53 +97,61 @@ public function status(): Attribute if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; - } else if (str($value)->contains(':')) { + } elseif (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(':')) { + } elseif (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') + 'database_uuid' => data_get($this, 'uuid'), ]); } + return null; } + public function isLogDrainEnabled() { return data_get($this, 'is_log_drain_enabled', false); @@ -143,7 +160,7 @@ public function isLogDrainEnabled() public function portsMappings(): Attribute { return Attribute::make( - set: fn ($value) => $value === "" ? null : $value, + set: fn ($value) => $value === '' ? null : $value, ); } @@ -161,9 +178,10 @@ public function type(): string { return 'standalone-redis'; } + public function get_db_url(bool $useInternal = false): string { - if ($this->is_public && !$useInternal) { + if ($this->is_public && ! $useInternal) { return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0"; } else { return "redis://:{$this->redis_password}@{$this->uuid}:6379/0"; diff --git a/app/Models/Subscription.php b/app/Models/Subscription.php index 4b8c6d70e..35dc43c0c 100644 --- a/app/Models/Subscription.php +++ b/app/Models/Subscription.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Str; class Subscription extends Model { @@ -13,6 +12,7 @@ public function team() { return $this->belongsTo(Team::class); } + public function type() { if (isLemon()) { @@ -30,20 +30,20 @@ public function type() if (in_array($subscription, $ultimate)) { return 'ultimate'; } - } else if (isStripe()) { - if (!$this->stripe_plan_id) { + } elseif (isStripe()) { + if (! $this->stripe_plan_id) { return 'zero'; } $subscription = Subscription::where('id', $this->id)->first(); - if (!$subscription) { + if (! $subscription) { return null; } $subscriptionPlanId = data_get($subscription, 'stripe_plan_id'); - if (!$subscriptionPlanId) { + if (! $subscriptionPlanId) { return null; } $subscriptionInvoicePaid = data_get($subscription, 'stripe_invoice_paid'); - if (!$subscriptionInvoicePaid) { + if (! $subscriptionInvoicePaid) { return null; } $subscriptionConfigs = collect(config('subscription')); @@ -51,12 +51,13 @@ public function type() $subscriptionConfigs->map(function ($value, $key) use ($subscriptionPlanId, &$stripePlanId) { if ($value === $subscriptionPlanId) { $stripePlanId = $key; - }; + } })->first(); if ($stripePlanId) { return str($stripePlanId)->after('stripe_price_id_')->before('_')->lower(); } } + return 'zero'; } } diff --git a/app/Models/SwarmDocker.php b/app/Models/SwarmDocker.php index a14131f43..e0fe349c7 100644 --- a/app/Models/SwarmDocker.php +++ b/app/Models/SwarmDocker.php @@ -20,26 +20,32 @@ public function redis() { 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() { return $this->morphMany(StandaloneMongodb::class, 'destination'); } + public function mysqls() { return $this->morphMany(StandaloneMysql::class, 'destination'); } + public function mariadbs() { return $this->morphMany(StandaloneMariadb::class, 'destination'); @@ -65,6 +71,7 @@ public function databases() $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); } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index b7d50b84f..a64c994a3 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -8,7 +8,6 @@ class Tag extends BaseModel { protected $guarded = []; - public function name(): Attribute { return Attribute::make( @@ -16,14 +15,17 @@ public function name(): Attribute set: fn ($value) => strtolower($value) ); } - static public function ownedByCurrentTeam() + + public static function ownedByCurrentTeam() { return Tag::whereTeamId(currentTeam()->id)->orderBy('name'); } + public function applications() { return $this->morphedByMany(Application::class, 'taggable'); } + public function services() { return $this->morphedByMany(Service::class, 'taggable'); diff --git a/app/Models/Team.php b/app/Models/Team.php index 81206019f..fe5995a1b 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -13,6 +13,7 @@ class Team extends Model implements SendsDiscord, SendsEmail use Notifiable; protected $guarded = []; + protected $casts = [ 'personal_team' => 'boolean', 'smtp_password' => 'encrypted', @@ -30,27 +31,27 @@ protected static function booted() static::deleting(function ($team) { $keys = $team->privateKeys; foreach ($keys as $key) { - ray('Deleting key: ' . $key->name); + ray('Deleting key: '.$key->name); $key->delete(); } $sources = $team->sources(); foreach ($sources as $source) { - ray('Deleting source: ' . $source->name); + ray('Deleting source: '.$source->name); $source->delete(); } $tags = Tag::whereTeamId($team->id)->get(); foreach ($tags as $tag) { - ray('Deleting tag: ' . $tag->name); + ray('Deleting tag: '.$tag->name); $tag->delete(); } $shared_variables = $team->environment_variables(); foreach ($shared_variables as $shared_variable) { - ray('Deleting team shared variable: ' . $shared_variable->name); + ray('Deleting team shared variable: '.$shared_variable->name); $shared_variable->delete(); } $s3s = $team->s3s; foreach ($s3s as $s3) { - ray('Deleting s3: ' . $s3->name); + ray('Deleting s3: '.$s3->name); $s3->delete(); } }); @@ -64,8 +65,8 @@ public function routeNotificationForDiscord() public function routeNotificationForTelegram() { return [ - "token" => data_get($this, 'telegram_token', null), - "chat_id" => data_get($this, 'telegram_chat_id', null), + 'token' => data_get($this, 'telegram_token', null), + 'chat_id' => data_get($this, 'telegram_chat_id', null), ]; } @@ -74,31 +75,40 @@ public function getRecepients($notification) $recipients = data_get($notification, 'emails', null); if (is_null($recipients)) { $recipients = $this->members()->pluck('email')->toArray(); + return $recipients; } + return explode(',', $recipients); } - static public function serverLimitReached() + + public static function serverLimitReached() { $serverLimit = Team::serverLimit(); $team = currentTeam(); $servers = $team->servers->count(); + return $servers >= $serverLimit; } + public function serverOverflow() { if ($this->serverLimit() < $this->servers->count()) { return true; } + return false; } - static public function serverLimit() + + public static function serverLimit() { if (currentTeam()->id === 0 && isDev()) { return 9999999; } + return Team::find(currentTeam()->id)->limits['serverLimit']; } + public function limits(): Attribute { return Attribute::make( @@ -119,15 +129,18 @@ public function limits(): Attribute $serverLimit = config('constants.limits.server')[strtolower($subscription)]; } $sharedEmailEnabled = config('constants.limits.email')[strtolower($subscription)]; + return ['serverLimit' => $serverLimit, 'sharedEmailEnabled' => $sharedEmailEnabled]; } ); } + public function environment_variables() { return $this->hasMany(SharedEnvironmentVariable::class)->whereNull('project_id')->whereNull('environment_id'); } + public function members() { return $this->belongsToMany(User::class, 'team_user', 'team_id', 'user_id')->withPivot('role'); @@ -153,6 +166,7 @@ public function isEmpty() if ($this->projects()->count() === 0 && $this->servers()->count() === 0 && $this->privateKeys()->count() === 0 && $this->sources()->count() === 0) { return true; } + return false; } @@ -177,6 +191,7 @@ public function sources() $github_apps = $this->hasMany(GithubApp::class)->whereisPublic(false)->get(); $gitlab_apps = $this->hasMany(GitlabApp::class)->whereisPublic(false)->get(); $sources = $sources->merge($github_apps)->merge($gitlab_apps); + return $sources; } @@ -184,6 +199,7 @@ public function s3s() { return $this->hasMany(S3Storage::class)->where('is_usable', true); } + public function trialEnded() { foreach ($this->servers as $server) { @@ -193,6 +209,7 @@ public function trialEnded() ]); } } + public function trialEndedButSubscribed() { foreach ($this->servers as $server) { @@ -202,6 +219,7 @@ public function trialEndedButSubscribed() ]); } } + public function isAnyNotificationEnabled() { if (isCloud()) { @@ -210,6 +228,7 @@ public function isAnyNotificationEnabled() if ($this->smtp_enabled || $this->resend_enabled || $this->discord_enabled || $this->telegram_enabled || $this->use_instance_email_settings) { return true; } + return false; } } diff --git a/app/Models/TeamInvitation.php b/app/Models/TeamInvitation.php index 8564a867f..c202710e2 100644 --- a/app/Models/TeamInvitation.php +++ b/app/Models/TeamInvitation.php @@ -19,13 +19,16 @@ public function team() { return $this->belongsTo(Team::class); } - public function isValid() { + + public function isValid() + { $createdAt = $this->created_at; $diff = $createdAt->diffInMinutes(now()); if ($diff <= config('constants.invitation.link.expiration')) { return true; } else { $this->delete(); + return false; } } diff --git a/app/Models/User.php b/app/Models/User.php index 0e66fdaea..1e120e951 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -13,22 +13,24 @@ use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\URL; +use Illuminate\Support\Str; use Laravel\Fortify\TwoFactorAuthenticatable; use Laravel\Sanctum\HasApiTokens; use Laravel\Sanctum\NewAccessToken; -use Illuminate\Support\Str; class User extends Authenticatable implements SendsEmail { use HasApiTokens, HasFactory, Notifiable, TwoFactorAuthenticatable; protected $guarded = []; + protected $hidden = [ 'password', 'remember_token', 'two_factor_recovery_codes', 'two_factor_secret', ]; + protected $casts = [ 'email_verified_at' => 'datetime', 'force_password_reset' => 'boolean', @@ -40,9 +42,9 @@ protected static function boot() parent::boot(); static::created(function (User $user) { $team = [ - 'name' => $user->name . "'s Team", + 'name' => $user->name."'s Team", 'personal_team' => true, - 'show_boarding' => true + 'show_boarding' => true, ]; if ($user->id === 0) { $team['id'] = 0; @@ -52,12 +54,13 @@ protected static function boot() $user->teams()->attach($new_team, ['role' => 'owner']); }); } + public function recreate_personal_team() { $team = [ - 'name' => $this->name . "'s Team", + 'name' => $this->name."'s Team", 'personal_team' => true, - 'show_boarding' => true + 'show_boarding' => true, ]; if ($this->id === 0) { $team['id'] = 0; @@ -65,9 +68,11 @@ public function recreate_personal_team() } $new_team = Team::create($team); $this->teams()->attach($new_team, ['role' => 'owner']); + return $new_team; } - public function createToken(string $name, array $abilities = ['*'], DateTimeInterface $expiresAt = null) + + public function createToken(string $name, array $abilities = ['*'], ?DateTimeInterface $expiresAt = null) { $plainTextToken = sprintf( '%s%s%s', @@ -81,11 +86,12 @@ public function createToken(string $name, array $abilities = ['*'], DateTimeInte 'token' => hash('sha256', $plainTextToken), 'abilities' => $abilities, 'expires_at' => $expiresAt, - 'team_id' => session('currentTeam')->id + 'team_id' => session('currentTeam')->id, ]); - return new NewAccessToken($token, $token->getKey() . '|' . $plainTextToken); + return new NewAccessToken($token, $token->getKey().'|'.$plainTextToken); } + public function teams() { return $this->belongsToMany(Team::class)->withPivot('role'); @@ -113,6 +119,7 @@ public function sendVerificationEmail() $mail->subject('Coolify: Verify your email.'); send_user_an_email($mail, $this->email); } + public function sendPasswordResetNotification($token): void { $this?->notify(new TransactionalEmailsResetPassword($token)); @@ -127,10 +134,12 @@ public function isOwner() { return $this->role() === 'owner'; } + public function isMember() { return $this->role() === 'member'; } + public function isAdminFromSession() { if (auth()->user()->id === 0) { @@ -147,6 +156,7 @@ public function isAdminFromSession() } $team = $teams->where('id', session('currentTeam')->id)->first(); $role = data_get($team, 'pivot.role'); + return $role === 'admin' || $role === 'owner'; } @@ -156,17 +166,20 @@ public function isInstanceAdmin() if ($team->id == 0) { return true; } + return false; }); + return $found_root_team->count() > 0; } public function currentTeam() { - return Cache::remember('team:' . auth()->user()->id, 3600, function () { - if (is_null(data_get(session('currentTeam'), 'id')) && auth()->user()->teams->count() > 0){ + return Cache::remember('team:'.auth()->user()->id, 3600, function () { + if (is_null(data_get(session('currentTeam'), 'id')) && auth()->user()->teams->count() > 0) { return auth()->user()->teams[0]; } + return Team::find(session('currentTeam')->id); }); } @@ -184,6 +197,7 @@ public function role() return $this->pivot->role; } $user = auth()->user()->teams->where('id', currentTeam()->id)->first(); + return data_get($user, 'pivot.role'); } } diff --git a/app/Models/Waitlist.php b/app/Models/Waitlist.php index 552c25eb3..28e5f01fd 100644 --- a/app/Models/Waitlist.php +++ b/app/Models/Waitlist.php @@ -7,5 +7,6 @@ class Waitlist extends BaseModel { use HasFactory; + protected $guarded = []; } diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index e259d16c1..8e2b62955 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -7,6 +7,7 @@ class Webhook extends Model { protected $guarded = []; + protected $casts = [ 'type' => 'string', 'payload' => 'encrypted', diff --git a/app/Notifications/Application/DeploymentFailed.php b/app/Notifications/Application/DeploymentFailed.php index 05fe544d0..1858f31e0 100644 --- a/app/Notifications/Application/DeploymentFailed.php +++ b/app/Notifications/Application/DeploymentFailed.php @@ -15,15 +15,21 @@ class DeploymentFailed extends Notification implements ShouldQueue use Queueable; public $tries = 1; + public Application $application; + public ?ApplicationPreview $preview = null; public string $deployment_uuid; + public string $application_name; + public string $project_uuid; + public string $environment_name; public ?string $deployment_url = null; + public ?string $fqdn = null; public function __construct(Application $application, string $deployment_uuid, ?ApplicationPreview $preview = null) @@ -38,7 +44,7 @@ public function __construct(Application $application, string $deployment_uuid, ? if (Str::of($this->fqdn)->explode(',')->count() > 1) { $this->fqdn = Str::of($this->fqdn)->explode(',')->first(); } - $this->deployment_url = base_url() . "/project/{$this->project_uuid}/" . urlencode($this->environment_name) . "/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; + $this->deployment_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; } public function via(object $notifiable): array @@ -52,10 +58,10 @@ public function toMail(): MailMessage $pull_request_id = data_get($this->preview, 'pull_request_id', 0); $fqdn = $this->fqdn; if ($pull_request_id === 0) { - $mail->subject('Coolify: Deployment failed of ' . $this->application_name . '.'); + $mail->subject('Coolify: Deployment failed of '.$this->application_name.'.'); } else { $fqdn = $this->preview->fqdn; - $mail->subject('Coolify: Deployment failed of pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . '.'); + $mail->subject('Coolify: Deployment failed of pull request #'.$this->preview->pull_request_id.' of '.$this->application_name.'.'); } $mail->view('emails.application-deployment-failed', [ 'name' => $this->application_name, @@ -63,35 +69,39 @@ public function toMail(): MailMessage 'deployment_url' => $this->deployment_url, 'pull_request_id' => data_get($this->preview, 'pull_request_id', 0), ]); + return $mail; } public function toDiscord(): string { if ($this->preview) { - $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . ' (' . $this->preview->fqdn . ') deployment failed: '; - $message .= '[View Deployment Logs](' . $this->deployment_url . ')'; + $message = 'Coolify: Pull request #'.$this->preview->pull_request_id.' of '.$this->application_name.' ('.$this->preview->fqdn.') deployment failed: '; + $message .= '[View Deployment Logs]('.$this->deployment_url.')'; } else { - $message = 'Coolify: Deployment failed of ' . $this->application_name . ' (' . $this->fqdn . '): '; - $message .= '[View Deployment Logs](' . $this->deployment_url . ')'; + $message = 'Coolify: Deployment failed of '.$this->application_name.' ('.$this->fqdn.'): '; + $message .= '[View Deployment Logs]('.$this->deployment_url.')'; } + return $message; } + public function toTelegram(): array { if ($this->preview) { - $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . ' (' . $this->preview->fqdn . ') deployment failed: '; + $message = 'Coolify: Pull request #'.$this->preview->pull_request_id.' of '.$this->application_name.' ('.$this->preview->fqdn.') deployment failed: '; } else { - $message = 'Coolify: Deployment failed of ' . $this->application_name . ' (' . $this->fqdn . '): '; + $message = 'Coolify: Deployment failed of '.$this->application_name.' ('.$this->fqdn.'): '; } $buttons[] = [ - "text" => "Deployment logs", - "url" => $this->deployment_url + 'text' => 'Deployment logs', + 'url' => $this->deployment_url, ]; + return [ - "message" => $message, - "buttons" => [ - ...$buttons + 'message' => $message, + 'buttons' => [ + ...$buttons, ], ]; } diff --git a/app/Notifications/Application/DeploymentSuccess.php b/app/Notifications/Application/DeploymentSuccess.php index e138ac91e..0cac6cbab 100644 --- a/app/Notifications/Application/DeploymentSuccess.php +++ b/app/Notifications/Application/DeploymentSuccess.php @@ -15,18 +15,24 @@ class DeploymentSuccess extends Notification implements ShouldQueue use Queueable; public $tries = 1; + public Application $application; - public ApplicationPreview|null $preview = null; + + public ?ApplicationPreview $preview = null; public string $deployment_uuid; + public string $application_name; + public string $project_uuid; + public string $environment_name; public ?string $deployment_url = null; + public ?string $fqdn; - public function __construct(Application $application, string $deployment_uuid, ApplicationPreview|null $preview = null) + public function __construct(Application $application, string $deployment_uuid, ?ApplicationPreview $preview = null) { $this->application = $application; $this->deployment_uuid = $deployment_uuid; @@ -38,7 +44,7 @@ public function __construct(Application $application, string $deployment_uuid, A if (Str::of($this->fqdn)->explode(',')->count() > 1) { $this->fqdn = Str::of($this->fqdn)->explode(',')->first(); } - $this->deployment_url = base_url() . "/project/{$this->project_uuid}/" . urlencode($this->environment_name) . "/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; + $this->deployment_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; } public function via(object $notifiable): array @@ -48,8 +54,10 @@ public function via(object $notifiable): array // TODO: Make batch notifications work with email $channels = array_diff($channels, ['App\Notifications\Channels\EmailChannel']); } + return $channels; } + public function toMail(): MailMessage { $mail = new MailMessage(); @@ -67,57 +75,61 @@ public function toMail(): MailMessage 'deployment_url' => $this->deployment_url, 'pull_request_id' => $pull_request_id, ]); + return $mail; } public function toDiscord(): string { if ($this->preview) { - $message = 'Coolify: New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . ' + $message = 'Coolify: New PR'.$this->preview->pull_request_id.' version successfully deployed of '.$this->application_name.' '; if ($this->preview->fqdn) { - $message .= '[Open Application](' . $this->preview->fqdn . ') | '; + $message .= '[Open Application]('.$this->preview->fqdn.') | '; } - $message .= '[Deployment logs](' . $this->deployment_url . ')'; + $message .= '[Deployment logs]('.$this->deployment_url.')'; } else { - $message = 'Coolify: New version successfully deployed of ' . $this->application_name . ' + $message = 'Coolify: New version successfully deployed of '.$this->application_name.' '; if ($this->fqdn) { - $message .= '[Open Application](' . $this->fqdn . ') | '; + $message .= '[Open Application]('.$this->fqdn.') | '; } - $message .= '[Deployment logs](' . $this->deployment_url . ')'; + $message .= '[Deployment logs]('.$this->deployment_url.')'; } + return $message; } + public function toTelegram(): array { if ($this->preview) { - $message = 'Coolify: New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . ''; + $message = 'Coolify: New PR'.$this->preview->pull_request_id.' version successfully deployed of '.$this->application_name.''; if ($this->preview->fqdn) { $buttons[] = [ - "text" => "Open Application", - "url" => $this->preview->fqdn + 'text' => 'Open Application', + 'url' => $this->preview->fqdn, ]; } } else { - $message = '✅ New version successfully deployed of ' . $this->application_name . ''; + $message = '✅ New version successfully deployed of '.$this->application_name.''; if ($this->fqdn) { $buttons[] = [ - "text" => "Open Application", - "url" => $this->fqdn + 'text' => 'Open Application', + 'url' => $this->fqdn, ]; } } $buttons[] = [ - "text" => "Deployment logs", - "url" => $this->deployment_url + 'text' => 'Deployment logs', + 'url' => $this->deployment_url, ]; + return [ - "message" => $message, - "buttons" => [ - ...$buttons + 'message' => $message, + 'buttons' => [ + ...$buttons, ], ]; } diff --git a/app/Notifications/Application/StatusChanged.php b/app/Notifications/Application/StatusChanged.php index 3d3b042dd..baf508895 100644 --- a/app/Notifications/Application/StatusChanged.php +++ b/app/Notifications/Application/StatusChanged.php @@ -16,10 +16,13 @@ class StatusChanged extends Notification implements ShouldQueue public $tries = 1; public string $resource_name; + public string $project_uuid; + public string $environment_name; public ?string $resource_url = null; + public ?string $fqdn; public function __construct(public Application $resource) @@ -31,7 +34,7 @@ public function __construct(public Application $resource) if (Str::of($this->fqdn)->explode(',')->count() > 1) { $this->fqdn = Str::of($this->fqdn)->explode(',')->first(); } - $this->resource_url = base_url() . "/project/{$this->project_uuid}/" . urlencode($this->environment_name) . "/application/{$this->resource->uuid}"; + $this->resource_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->resource->uuid}"; } public function via(object $notifiable): array @@ -49,27 +52,31 @@ public function toMail(): MailMessage 'fqdn' => $fqdn, 'resource_url' => $this->resource_url, ]); + return $mail; } public function toDiscord(): string { - $message = 'Coolify: ' . $this->resource_name . ' has been stopped. + $message = 'Coolify: '.$this->resource_name.' has been stopped. '; - $message .= '[Open Application in Coolify](' . $this->resource_url . ')'; + $message .= '[Open Application in Coolify]('.$this->resource_url.')'; + return $message; } + public function toTelegram(): array { - $message = 'Coolify: ' . $this->resource_name . ' has been stopped.'; + $message = 'Coolify: '.$this->resource_name.' has been stopped.'; + return [ - "message" => $message, - "buttons" => [ + 'message' => $message, + 'buttons' => [ [ - "text" => "Open Application in Coolify", - "url" => $this->resource_url - ] + 'text' => 'Open Application in Coolify', + 'url' => $this->resource_url, + ], ], ]; } diff --git a/app/Notifications/Channels/DiscordChannel.php b/app/Notifications/Channels/DiscordChannel.php index 6c361f89e..f1706f138 100644 --- a/app/Notifications/Channels/DiscordChannel.php +++ b/app/Notifications/Channels/DiscordChannel.php @@ -14,7 +14,7 @@ public function send(SendsDiscord $notifiable, Notification $notification): void { $message = $notification->toDiscord($notifiable); $webhookUrl = $notifiable->routeNotificationForDiscord(); - if (!$webhookUrl) { + if (! $webhookUrl) { return; } dispatch(new SendMessageToDiscordJob($message, $webhookUrl)); diff --git a/app/Notifications/Channels/EmailChannel.php b/app/Notifications/Channels/EmailChannel.php index da8ef812e..413d3de53 100644 --- a/app/Notifications/Channels/EmailChannel.php +++ b/app/Notifications/Channels/EmailChannel.php @@ -6,7 +6,6 @@ use Illuminate\Mail\Message; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Mail; -use Log; class EmailChannel { @@ -26,7 +25,7 @@ public function send(SendsEmail $notifiable, Notification $notification): void fn (Message $message) => $message ->to($recipients) ->subject($mailMessage->subject) - ->html((string)$mailMessage->render()) + ->html((string) $mailMessage->render()) ); } catch (Exception $e) { $error = $e->getMessage(); @@ -50,9 +49,10 @@ private function bootConfigs($notifiable): void { if (data_get($notifiable, 'use_instance_email_settings')) { $type = set_transanctional_email_settings(); - if (!$type) { + if (! $type) { throw new Exception('No email settings found.'); } + return; } config()->set('mail.from.address', data_get($notifiable, 'smtp_from_address', 'test@example.com')); @@ -64,14 +64,14 @@ private function bootConfigs($notifiable): void if (data_get($notifiable, 'smtp_enabled')) { config()->set('mail.default', 'smtp'); config()->set('mail.mailers.smtp', [ - "transport" => "smtp", - "host" => data_get($notifiable, 'smtp_host'), - "port" => data_get($notifiable, 'smtp_port'), - "encryption" => data_get($notifiable, 'smtp_encryption'), - "username" => data_get($notifiable, 'smtp_username'), - "password" => data_get($notifiable, 'smtp_password'), - "timeout" => data_get($notifiable, 'smtp_timeout'), - "local_domain" => null, + 'transport' => 'smtp', + 'host' => data_get($notifiable, 'smtp_host'), + 'port' => data_get($notifiable, 'smtp_port'), + 'encryption' => data_get($notifiable, 'smtp_encryption'), + 'username' => data_get($notifiable, 'smtp_username'), + 'password' => data_get($notifiable, 'smtp_password'), + 'timeout' => data_get($notifiable, 'smtp_timeout'), + 'local_domain' => null, ]); } } diff --git a/app/Notifications/Channels/SendsTelegram.php b/app/Notifications/Channels/SendsTelegram.php index ee8bd0656..fc2160a95 100644 --- a/app/Notifications/Channels/SendsTelegram.php +++ b/app/Notifications/Channels/SendsTelegram.php @@ -5,5 +5,4 @@ interface SendsTelegram { public function routeNotificationForTelegram(); - } diff --git a/app/Notifications/Channels/TelegramChannel.php b/app/Notifications/Channels/TelegramChannel.php index 6101ef208..b1a607651 100644 --- a/app/Notifications/Channels/TelegramChannel.php +++ b/app/Notifications/Channels/TelegramChannel.php @@ -22,6 +22,8 @@ public function send($notifiable, $notification): void $topicId = data_get($notifiable, 'telegram_notifications_test_message_thread_id'); break; case 'App\Notifications\Application\StatusChanged': + case 'App\Notifications\Container\ContainerRestarted': + case 'App\Notifications\Container\ContainerStopped': $topicId = data_get($notifiable, 'telegram_notifications_status_changes_message_thread_id'); break; case 'App\Notifications\Application\DeploymentSuccess': @@ -36,7 +38,7 @@ public function send($notifiable, $notification): void $topicId = data_get($notifiable, 'telegram_notifications_scheduled_tasks_thread_id'); break; } - if (!$telegramToken || !$chatId || !$message) { + if (! $telegramToken || ! $chatId || ! $message) { return; } dispatch(new SendMessageToTelegramJob($message, $buttons, $telegramToken, $chatId, $topicId)); diff --git a/app/Notifications/Channels/TransactionalEmailChannel.php b/app/Notifications/Channels/TransactionalEmailChannel.php index 2985d5183..3d7b7c8d0 100644 --- a/app/Notifications/Channels/TransactionalEmailChannel.php +++ b/app/Notifications/Channels/TransactionalEmailChannel.php @@ -15,12 +15,13 @@ class TransactionalEmailChannel public function send(User $notifiable, Notification $notification): void { $settings = InstanceSettings::get(); - if (!data_get($settings, 'smtp_enabled') && !data_get($settings, 'resend_enabled')) { + if (! data_get($settings, 'smtp_enabled') && ! data_get($settings, 'resend_enabled')) { Log::info('SMTP/Resend not enabled'); + return; } $email = $notifiable->email; - if (!$email) { + if (! $email) { return; } $this->bootConfigs(); @@ -31,14 +32,14 @@ public function send(User $notifiable, Notification $notification): void fn (Message $message) => $message ->to($email) ->subject($mailMessage->subject) - ->html((string)$mailMessage->render()) + ->html((string) $mailMessage->render()) ); } private function bootConfigs(): void { $type = set_transanctional_email_settings(); - if (!$type) { + if (! $type) { throw new Exception('No email settings found.'); } } diff --git a/app/Notifications/Container/ContainerRestarted.php b/app/Notifications/Container/ContainerRestarted.php index d9c524da4..a55f16a83 100644 --- a/app/Notifications/Container/ContainerRestarted.php +++ b/app/Notifications/Container/ContainerRestarted.php @@ -14,7 +14,6 @@ class ContainerRestarted extends Notification implements ShouldQueue public $tries = 1; - public function __construct(public string $name, public Server $server, public ?string $url = null) { } @@ -33,30 +32,34 @@ public function toMail(): MailMessage 'serverName' => $this->server->name, 'url' => $this->url, ]); + return $mail; } public function toDiscord(): string { $message = "Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}"; + return $message; } + public function toTelegram(): array { $message = "Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}"; $payload = [ - "message" => $message, + 'message' => $message, ]; if ($this->url) { $payload['buttons'] = [ [ [ - "text" => "Check Proxy in Coolify", - "url" => $this->url - ] - ] + 'text' => 'Check Proxy in Coolify', + 'url' => $this->url, + ], + ], ]; - }; + } + return $payload; } } diff --git a/app/Notifications/Container/ContainerStopped.php b/app/Notifications/Container/ContainerStopped.php index 7bab74934..d9dc57b98 100644 --- a/app/Notifications/Container/ContainerStopped.php +++ b/app/Notifications/Container/ContainerStopped.php @@ -32,30 +32,34 @@ public function toMail(): MailMessage 'serverName' => $this->server->name, 'url' => $this->url, ]); + return $mail; } public function toDiscord(): string { $message = "Coolify: A resource ($this->name) has been stopped unexpectedly on {$this->server->name}"; + return $message; } + public function toTelegram(): array { $message = "Coolify: A resource ($this->name) has been stopped unexpectedly on {$this->server->name}"; $payload = [ - "message" => $message, + 'message' => $message, ]; if ($this->url) { $payload['buttons'] = [ [ [ - "text" => "Open Application in Coolify", - "url" => $this->url - ] - ] + 'text' => 'Open Application in Coolify', + 'url' => $this->url, + ], + ], ]; } + return $payload; } } diff --git a/app/Notifications/Database/BackupFailed.php b/app/Notifications/Database/BackupFailed.php index 7cad486b3..c6403ab71 100644 --- a/app/Notifications/Database/BackupFailed.php +++ b/app/Notifications/Database/BackupFailed.php @@ -3,11 +3,8 @@ namespace App\Notifications\Database; use App\Models\ScheduledDatabaseBackup; -use App\Notifications\Channels\DiscordChannel; -use App\Notifications\Channels\TelegramChannel; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Notifications\Channels\MailChannel; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; @@ -16,8 +13,11 @@ class BackupFailed extends Notification implements ShouldQueue use Queueable; public $backoff = 10; + public $tries = 2; + public string $name; + public string $frequency; public function __construct(ScheduledDatabaseBackup $backup, public $database, public $output, public $database_name) @@ -41,6 +41,7 @@ public function toMail(): MailMessage 'frequency' => $this->frequency, 'output' => $this->output, ]); + return $mail; } @@ -48,11 +49,13 @@ public function toDiscord(): string { return "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason:\n{$this->output}"; } + public function toTelegram(): array { $message = "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason:\n{$this->output}"; + return [ - "message" => $message, + 'message' => $message, ]; } } diff --git a/app/Notifications/Database/BackupSuccess.php b/app/Notifications/Database/BackupSuccess.php index c43a12276..f3a3d5943 100644 --- a/app/Notifications/Database/BackupSuccess.php +++ b/app/Notifications/Database/BackupSuccess.php @@ -13,8 +13,11 @@ class BackupSuccess extends Notification implements ShouldQueue use Queueable; public $backoff = 10; + public $tries = 3; + public string $name; + public string $frequency; public function __construct(ScheduledDatabaseBackup $backup, public $database, public $database_name) @@ -37,6 +40,7 @@ public function toMail(): MailMessage 'database_name' => $this->database_name, 'frequency' => $this->frequency, ]); + return $mail; } @@ -44,12 +48,14 @@ public function toDiscord(): string { return "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was successful."; } + public function toTelegram(): array { $message = "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was successful."; ray($message); + return [ - "message" => $message, + 'message' => $message, ]; } } diff --git a/app/Notifications/Database/DailyBackup.php b/app/Notifications/Database/DailyBackup.php index dfa508fbd..c74676eb7 100644 --- a/app/Notifications/Database/DailyBackup.php +++ b/app/Notifications/Database/DailyBackup.php @@ -2,7 +2,6 @@ namespace App\Notifications\Database; -use App\Models\ScheduledDatabaseBackup; use App\Notifications\Channels\DiscordChannel; use App\Notifications\Channels\TelegramChannel; use Illuminate\Bus\Queueable; @@ -29,22 +28,25 @@ public function via(object $notifiable): array public function toMail(): MailMessage { $mail = new MailMessage(); - $mail->subject("Coolify: Daily backup statuses"); + $mail->subject('Coolify: Daily backup statuses'); $mail->view('emails.daily-backup', [ 'databases' => $this->databases, ]); + return $mail; } public function toDiscord(): string { - return "Coolify: Daily backup statuses"; + return 'Coolify: Daily backup statuses'; } + public function toTelegram(): array { - $message = "Coolify: Daily backup statuses"; + $message = 'Coolify: Daily backup statuses'; + return [ - "message" => $message, + 'message' => $message, ]; } } diff --git a/app/Notifications/Internal/GeneralNotification.php b/app/Notifications/Internal/GeneralNotification.php index ddb5a553d..6acd770f6 100644 --- a/app/Notifications/Internal/GeneralNotification.php +++ b/app/Notifications/Internal/GeneralNotification.php @@ -13,6 +13,7 @@ class GeneralNotification extends Notification implements ShouldQueue use Queueable; public $tries = 1; + public function __construct(public string $message) { } @@ -29,6 +30,7 @@ public function via(object $notifiable): array if ($isTelegramEnabled) { $channels[] = TelegramChannel::class; } + return $channels; } @@ -36,10 +38,11 @@ public function toDiscord(): string { return $this->message; } + public function toTelegram(): array { return [ - "message" => $this->message, + 'message' => $this->message, ]; } } diff --git a/app/Notifications/ScheduledTask/TaskFailed.php b/app/Notifications/ScheduledTask/TaskFailed.php index f61b1f573..3a41fb687 100644 --- a/app/Notifications/ScheduledTask/TaskFailed.php +++ b/app/Notifications/ScheduledTask/TaskFailed.php @@ -13,6 +13,7 @@ class TaskFailed extends Notification implements ShouldQueue use Queueable; public $backoff = 10; + public $tries = 2; public ?string $url = null; @@ -21,7 +22,7 @@ public function __construct(public ScheduledTask $task, public string $output) { if ($task->application) { $this->url = $task->application->failedTaskLink($task->uuid); - } else if ($task->service) { + } elseif ($task->service) { $this->url = $task->service->failedTaskLink($task->uuid); } } @@ -41,6 +42,7 @@ public function toMail(): MailMessage 'url' => $this->url, 'output' => $this->output, ]); + return $mail; } @@ -48,17 +50,19 @@ public function toDiscord(): string { return "Coolify: Scheduled task ({$this->task->name}, [link]({$this->url})) failed with output: {$this->output}"; } + public function toTelegram(): array { $message = "Coolify: Scheduled task ({$this->task->name}) failed with output: {$this->output}"; if ($this->url) { $buttons[] = [ - "text" => "Open task in Coolify", - "url" => (string) $this->url + 'text' => 'Open task in Coolify', + 'url' => (string) $this->url, ]; } + return [ - "message" => $message, + 'message' => $message, ]; } } diff --git a/app/Notifications/Server/DockerCleanup.php b/app/Notifications/Server/DockerCleanup.php index 754287fa1..0e445f035 100644 --- a/app/Notifications/Server/DockerCleanup.php +++ b/app/Notifications/Server/DockerCleanup.php @@ -3,9 +3,9 @@ namespace App\Notifications\Server; use App\Models\Server; -use Illuminate\Bus\Queueable; use App\Notifications\Channels\DiscordChannel; use App\Notifications\Channels\TelegramChannel; +use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Notification; @@ -14,6 +14,7 @@ class DockerCleanup extends Notification implements ShouldQueue use Queueable; public $tries = 1; + public function __construct(public Server $server, public string $message) { } @@ -34,6 +35,7 @@ public function via(object $notifiable): array if ($isTelegramEnabled) { $channels[] = TelegramChannel::class; } + return $channels; } @@ -52,12 +54,14 @@ public function via(object $notifiable): array public function toDiscord(): string { $message = "Coolify: Server '{$this->server->name}' cleanup job done!\n\n{$this->message}"; + return $message; } + public function toTelegram(): array { return [ - "message" => "Coolify: Server '{$this->server->name}' cleanup job done!\n\n{$this->message}" + 'message' => "Coolify: Server '{$this->server->name}' cleanup job done!\n\n{$this->message}", ]; } } diff --git a/app/Notifications/Server/ForceDisabled.php b/app/Notifications/Server/ForceDisabled.php index 4bce44e46..960a7c79f 100644 --- a/app/Notifications/Server/ForceDisabled.php +++ b/app/Notifications/Server/ForceDisabled.php @@ -3,10 +3,10 @@ namespace App\Notifications\Server; use App\Models\Server; -use Illuminate\Bus\Queueable; use App\Notifications\Channels\DiscordChannel; use App\Notifications\Channels\EmailChannel; use App\Notifications\Channels\TelegramChannel; +use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; @@ -16,6 +16,7 @@ class ForceDisabled extends Notification implements ShouldQueue use Queueable; public $tries = 1; + public function __construct(public Server $server) { } @@ -36,6 +37,7 @@ public function via(object $notifiable): array if ($isTelegramEnabled) { $channels[] = TelegramChannel::class; } + return $channels; } @@ -46,18 +48,21 @@ public function toMail(): MailMessage $mail->view('emails.server-force-disabled', [ 'name' => $this->server->name, ]); + return $mail; } public function toDiscord(): string { $message = "Coolify: Server ({$this->server->name}) disabled because it is not paid!\n All automations and integrations are stopped.\nPlease update your subscription to enable the server again [here](https://app.coolify.io/subsciprtions)."; + return $message; } + public function toTelegram(): array { return [ - "message" => "Coolify: Server ({$this->server->name}) disabled because it is not paid!\n All automations and integrations are stopped.\nPlease update your subscription to enable the server again [here](https://app.coolify.io/subsciprtions)." + 'message' => "Coolify: Server ({$this->server->name}) disabled because it is not paid!\n All automations and integrations are stopped.\nPlease update your subscription to enable the server again [here](https://app.coolify.io/subsciprtions).", ]; } } diff --git a/app/Notifications/Server/ForceEnabled.php b/app/Notifications/Server/ForceEnabled.php index c29a08644..6a4b5d74b 100644 --- a/app/Notifications/Server/ForceEnabled.php +++ b/app/Notifications/Server/ForceEnabled.php @@ -3,10 +3,10 @@ namespace App\Notifications\Server; use App\Models\Server; -use Illuminate\Bus\Queueable; use App\Notifications\Channels\DiscordChannel; use App\Notifications\Channels\EmailChannel; use App\Notifications\Channels\TelegramChannel; +use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; @@ -16,6 +16,7 @@ class ForceEnabled extends Notification implements ShouldQueue use Queueable; public $tries = 1; + public function __construct(public Server $server) { } @@ -36,6 +37,7 @@ public function via(object $notifiable): array if ($isTelegramEnabled) { $channels[] = TelegramChannel::class; } + return $channels; } @@ -46,18 +48,21 @@ public function toMail(): MailMessage $mail->view('emails.server-force-enabled', [ 'name' => $this->server->name, ]); + return $mail; } public function toDiscord(): string { $message = "Coolify: Server ({$this->server->name}) enabled again!"; + return $message; } + public function toTelegram(): array { return [ - "message" => "Coolify: Server ({$this->server->name}) enabled again!" + 'message' => "Coolify: Server ({$this->server->name}) enabled again!", ]; } } diff --git a/app/Notifications/Server/HighDiskUsage.php b/app/Notifications/Server/HighDiskUsage.php index 33e49387e..5f63ef8f1 100644 --- a/app/Notifications/Server/HighDiskUsage.php +++ b/app/Notifications/Server/HighDiskUsage.php @@ -3,10 +3,10 @@ namespace App\Notifications\Server; use App\Models\Server; -use Illuminate\Bus\Queueable; use App\Notifications\Channels\DiscordChannel; use App\Notifications\Channels\EmailChannel; use App\Notifications\Channels\TelegramChannel; +use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; @@ -16,6 +16,7 @@ class HighDiskUsage extends Notification implements ShouldQueue use Queueable; public $tries = 1; + public function __construct(public Server $server, public int $disk_usage, public int $cleanup_after_percentage) { } @@ -36,6 +37,7 @@ public function via(object $notifiable): array if ($isTelegramEnabled) { $channels[] = TelegramChannel::class; } + return $channels; } @@ -48,18 +50,21 @@ public function toMail(): MailMessage 'disk_usage' => $this->disk_usage, 'threshold' => $this->cleanup_after_percentage, ]); + return $mail; } public function toDiscord(): string { $message = "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup."; + return $message; } + public function toTelegram(): array { return [ - "message" => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup." + 'message' => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.", ]; } } diff --git a/app/Notifications/Server/Revived.php b/app/Notifications/Server/Revived.php index 36775976b..e7d3baf3e 100644 --- a/app/Notifications/Server/Revived.php +++ b/app/Notifications/Server/Revived.php @@ -5,10 +5,10 @@ use App\Actions\Docker\GetContainersStatus; use App\Jobs\ContainerStatusJob; use App\Models\Server; -use Illuminate\Bus\Queueable; use App\Notifications\Channels\DiscordChannel; use App\Notifications\Channels\EmailChannel; use App\Notifications\Channels\TelegramChannel; +use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; @@ -18,6 +18,7 @@ class Revived extends Notification implements ShouldQueue use Queueable; public $tries = 1; + public function __construct(public Server $server) { if ($this->server->unreachable_notification_sent === false) { @@ -37,12 +38,13 @@ public function via(object $notifiable): array if ($isDiscordEnabled) { $channels[] = DiscordChannel::class; } - if ($isEmailEnabled ) { + if ($isEmailEnabled) { $channels[] = EmailChannel::class; } if ($isTelegramEnabled) { $channels[] = TelegramChannel::class; } + return $channels; } @@ -53,18 +55,21 @@ public function toMail(): MailMessage $mail->view('emails.server-revived', [ 'name' => $this->server->name, ]); + return $mail; } public function toDiscord(): string { $message = "Coolify: Server '{$this->server->name}' revived. All automations & integrations are turned on again!"; + return $message; } + public function toTelegram(): array { return [ - "message" => "Coolify: Server '{$this->server->name}' revived. All automations & integrations are turned on again!" + 'message' => "Coolify: Server '{$this->server->name}' revived. All automations & integrations are turned on again!", ]; } } diff --git a/app/Notifications/Server/Unreachable.php b/app/Notifications/Server/Unreachable.php index bfd862993..2dcfe28b8 100644 --- a/app/Notifications/Server/Unreachable.php +++ b/app/Notifications/Server/Unreachable.php @@ -16,6 +16,7 @@ class Unreachable extends Notification implements ShouldQueue use Queueable; public $tries = 1; + public function __construct(public Server $server) { @@ -31,12 +32,13 @@ public function via(object $notifiable): array if ($isDiscordEnabled) { $channels[] = DiscordChannel::class; } - if ($isEmailEnabled ) { + if ($isEmailEnabled) { $channels[] = EmailChannel::class; } if ($isTelegramEnabled) { $channels[] = TelegramChannel::class; } + return $channels; } @@ -47,18 +49,21 @@ public function toMail(): MailMessage $mail->view('emails.server-lost-connection', [ 'name' => $this->server->name, ]); + return $mail; } public function toDiscord(): string { $message = "Coolify: Your server '{$this->server->name}' is unreachable. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server and turn on all automations & integrations."; + return $message; } + public function toTelegram(): array { return [ - "message" => "Coolify: Your server '{$this->server->name}' is unreachable. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server and turn on all automations & integrations." + 'message' => "Coolify: Your server '{$this->server->name}' is unreachable. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server and turn on all automations & integrations.", ]; } } diff --git a/app/Notifications/Test.php b/app/Notifications/Test.php index 06e3adbaa..925859aba 100644 --- a/app/Notifications/Test.php +++ b/app/Notifications/Test.php @@ -12,7 +12,8 @@ class Test extends Notification implements ShouldQueue use Queueable; public $tries = 5; - public function __construct(public string|null $emails = null) + + public function __construct(public ?string $emails = null) { } @@ -24,8 +25,9 @@ public function via(object $notifiable): array public function toMail(): MailMessage { $mail = new MailMessage(); - $mail->subject("Coolify: Test Email"); + $mail->subject('Coolify: Test Email'); $mail->view('emails.test'); + return $mail; } @@ -33,18 +35,20 @@ public function toDiscord(): string { $message = 'Coolify: This is a test Discord notification from Coolify.'; $message .= "\n\n"; - $message .= '[Go to your dashboard](' . base_url() . ')'; + $message .= '[Go to your dashboard]('.base_url().')'; + return $message; } + public function toTelegram(): array { return [ - "message" => 'Coolify: This is a test Telegram notification from Coolify.', - "buttons" => [ + 'message' => 'Coolify: This is a test Telegram notification from Coolify.', + 'buttons' => [ [ - "text" => "Go to your dashboard", - "url" => base_url() - ] + 'text' => 'Go to your dashboard', + 'url' => base_url(), + ], ], ]; } diff --git a/app/Notifications/TransactionalEmails/InvitationLink.php b/app/Notifications/TransactionalEmails/InvitationLink.php index dd1275c2d..a251b47ea 100644 --- a/app/Notifications/TransactionalEmails/InvitationLink.php +++ b/app/Notifications/TransactionalEmails/InvitationLink.php @@ -16,6 +16,7 @@ class InvitationLink extends Notification implements ShouldQueue use Queueable; public $tries = 5; + public function via(): array { return [TransactionalEmailChannel::class]; @@ -24,18 +25,20 @@ public function via(): array public function __construct(public User $user) { } + public function toMail(): MailMessage { $invitation = TeamInvitation::whereEmail($this->user->email)->first(); $invitation_team = Team::find($invitation->team->id); $mail = new MailMessage(); - $mail->subject('Coolify: Invitation for ' . $invitation_team->name); + $mail->subject('Coolify: Invitation for '.$invitation_team->name); $mail->view('emails.invitation-link', [ 'team' => $invitation_team->name, 'email' => $this->user->email, 'invitation_link' => $invitation->link, ]); + return $mail; } } diff --git a/app/Notifications/TransactionalEmails/ResetPassword.php b/app/Notifications/TransactionalEmails/ResetPassword.php index cde6190e2..45243c4d5 100644 --- a/app/Notifications/TransactionalEmails/ResetPassword.php +++ b/app/Notifications/TransactionalEmails/ResetPassword.php @@ -9,8 +9,11 @@ class ResetPassword extends Notification { public static $createUrlCallback; + public static $toMailCallback; + public $token; + public InstanceSettings $settings; public function __construct($token) @@ -32,9 +35,10 @@ public static function toMailUsing($callback) public function via($notifiable) { $type = set_transanctional_email_settings(); - if (!$type) { + if (! $type) { throw new \Exception('No email settings found.'); } + return ['mail']; } @@ -51,7 +55,8 @@ protected function buildMailMessage($url) { $mail = new MailMessage(); $mail->subject('Coolify: Reset Password'); - $mail->view('emails.reset-password', ['url' => $url, 'count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')]); + $mail->view('emails.reset-password', ['url' => $url, 'count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]); + return $mail; } diff --git a/app/Notifications/TransactionalEmails/Test.php b/app/Notifications/TransactionalEmails/Test.php index 6a4e5533f..ed30c1883 100644 --- a/app/Notifications/TransactionalEmails/Test.php +++ b/app/Notifications/TransactionalEmails/Test.php @@ -13,6 +13,7 @@ class Test extends Notification implements ShouldQueue use Queueable; public $tries = 5; + public function __construct(public string $emails) { } @@ -27,6 +28,7 @@ public function toMail(): MailMessage $mail = new MailMessage(); $mail->subject('Coolify: Test Email'); $mail->view('emails.test'); + return $mail; } } diff --git a/app/Policies/ApplicationPolicy.php b/app/Policies/ApplicationPolicy.php index 860479a94..05fc289b8 100644 --- a/app/Policies/ApplicationPolicy.php +++ b/app/Policies/ApplicationPolicy.php @@ -4,7 +4,6 @@ use App\Models\Application; use App\Models\User; -use Illuminate\Auth\Access\Response; class ApplicationPolicy { @@ -48,6 +47,7 @@ public function delete(User $user, Application $application): bool if ($user->isAdmin()) { return true; } + return false; } diff --git a/app/Policies/ServerPolicy.php b/app/Policies/ServerPolicy.php index 08ee5e64d..ad59b7140 100644 --- a/app/Policies/ServerPolicy.php +++ b/app/Policies/ServerPolicy.php @@ -4,7 +4,6 @@ use App\Models\Server; use App\Models\User; -use Illuminate\Auth\Access\Response; class ServerPolicy { diff --git a/app/Policies/ServicePolicy.php b/app/Policies/ServicePolicy.php index 93882be9a..51a6d8116 100644 --- a/app/Policies/ServicePolicy.php +++ b/app/Policies/ServicePolicy.php @@ -4,7 +4,6 @@ use App\Models\Service; use App\Models\User; -use Illuminate\Auth\Access\Response; class ServicePolicy { @@ -48,6 +47,7 @@ public function delete(User $user, Service $service): bool if ($user->isAdmin()) { return true; } + return false; } @@ -67,13 +67,16 @@ public function forceDelete(User $user, Service $service): bool if ($user->isAdmin()) { return true; } + return false; } + public function stop(User $user, Service $service): bool { if ($user->isAdmin()) { return true; } + return false; } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index d0618f406..1bce22c12 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,10 +2,10 @@ namespace App\Providers; +use App\Models\PersonalAccessToken; use Illuminate\Support\Facades\Http; use Illuminate\Support\ServiceProvider; use Laravel\Sanctum\Sanctum; -use App\Models\PersonalAccessToken; class AppServiceProvider extends ServiceProvider { @@ -16,7 +16,7 @@ public function register(): void public function boot(): void { Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); - Http::macro('github', function (string $api_url, string|null $github_access_token = null) { + Http::macro('github', function (string $api_url, ?string $github_access_token = null) { if ($github_access_token) { return Http::withHeaders([ 'X-GitHub-Api-Version' => '2022-11-28', diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index a1b6beb6e..7ba72e10d 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -20,16 +20,18 @@ class EventServiceProvider extends ServiceProvider MaintenanceModeDisabledNotification::class, ], \SocialiteProviders\Manager\SocialiteWasCalled::class => [ - \SocialiteProviders\Azure\AzureExtendSocialite::class . '@handle', + \SocialiteProviders\Azure\AzureExtendSocialite::class.'@handle', ], ProxyStarted::class => [ ProxyStartedNotification::class, ], ]; + public function boot(): void { // } + public function shouldDiscoverEvents(): bool { return false; diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php index 6bb284eef..cd6ec7705 100644 --- a/app/Providers/FortifyServiceProvider.php +++ b/app/Providers/FortifyServiceProvider.php @@ -32,6 +32,7 @@ public function toResponse($request) if ($request->user()->currentTeam->id === 0) { return redirect()->route('settings.index'); } + return redirect(RouteServiceProvider::HOME); } }); @@ -45,7 +46,7 @@ public function boot(): void Fortify::createUsersUsing(CreateNewUser::class); Fortify::registerView(function () { $settings = InstanceSettings::get(); - if (!$settings->is_registration_enabled) { + if (! $settings->is_registration_enabled) { return redirect()->route('login'); } if (config('coolify.waitlist')) { @@ -63,6 +64,7 @@ public function boot(): void // If there are no users, redirect to registration return redirect()->route('register'); } + return view('auth.login', [ 'is_registration_enabled' => $settings->is_registration_enabled, 'enabled_oauth_providers' => $enabled_oauth_providers, @@ -78,10 +80,11 @@ public function boot(): void $user->updated_at = now(); $user->save(); $user->currentTeam = $user->teams->firstWhere('personal_team', true); - if (!$user->currentTeam) { + if (! $user->currentTeam) { $user->currentTeam = $user->recreate_personal_team(); } session(['currentTeam' => $user->currentTeam]); + return $user; } }); @@ -113,9 +116,9 @@ public function boot(): void }); RateLimiter::for('login', function (Request $request) { - $email = (string)$request->email; + $email = (string) $request->email; - return Limit::perMinute(5)->by($email . $request->ip()); + return Limit::perMinute(5)->by($email.$request->ip()); }); RateLimiter::for('two-factor', function (Request $request) { diff --git a/app/Providers/HorizonServiceProvider.php b/app/Providers/HorizonServiceProvider.php index b25167602..2e2b79a59 100644 --- a/app/Providers/HorizonServiceProvider.php +++ b/app/Providers/HorizonServiceProvider.php @@ -16,7 +16,6 @@ public function boot(): void { parent::boot(); - // Horizon::routeSmsNotificationsTo('15556667777'); // Horizon::routeMailNotificationsTo('example@example.com'); // Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel'); @@ -33,8 +32,9 @@ protected function gate(): void { Gate::define('viewHorizon', function ($user) { $root_user = User::find(0); + return in_array($user->email, [ - $root_user->email + $root_user->email, ]); }); } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 79b214502..c85960746 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -48,6 +48,7 @@ protected function configureRateLimiting(): void if ($request->path() === 'api/health') { return Limit::perMinute(1000)->by($request->user()?->id ?: $request->ip()); } + return Limit::perMinute(200)->by($request->user()?->id ?: $request->ip()); }); RateLimiter::for('5', function (Request $request) { diff --git a/app/Traits/ExecuteRemoteCommand.php b/app/Traits/ExecuteRemoteCommand.php index 028dbaadc..0c6422f0c 100644 --- a/app/Traits/ExecuteRemoteCommand.php +++ b/app/Traits/ExecuteRemoteCommand.php @@ -12,7 +12,9 @@ trait ExecuteRemoteCommand { public ?string $save = null; + public static int $batch_counter = 0; + public function execute_remote_command(...$commands) { static::$batch_counter++; @@ -45,7 +47,7 @@ public function execute_remote_command(...$commands) $process = Process::timeout(3600)->idleTimeout(3600)->start($remote_command, function (string $type, string $output) use ($command, $hidden, $customType, $append) { $output = Str::of($output)->trim(); if ($output->startsWith('╔')) { - $output = "\n" . $output; + $output = "\n".$output; } $new_log_entry = [ 'command' => remove_iip($command), @@ -55,7 +57,7 @@ public function execute_remote_command(...$commands) 'hidden' => $hidden, 'batch' => static::$batch_counter, ]; - if (!$this->application_deployment_queue->logs) { + if (! $this->application_deployment_queue->logs) { $new_log_entry['order'] = 1; } else { $previous_logs = json_decode($this->application_deployment_queue->logs, associative: true, flags: JSON_THROW_ON_ERROR); @@ -83,7 +85,7 @@ public function execute_remote_command(...$commands) $process_result = $process->wait(); if ($process_result->exitCode() !== 0) { - if (!$ignore_errors) { + if (! $ignore_errors) { $this->application_deployment_queue->status = ApplicationDeploymentStatus::FAILED->value; $this->application_deployment_queue->save(); throw new \RuntimeException($process_result->errorOutput()); diff --git a/app/Traits/SaveFromRedirect.php b/app/Traits/SaveFromRedirect.php index 83013a857..166c16a4b 100644 --- a/app/Traits/SaveFromRedirect.php +++ b/app/Traits/SaveFromRedirect.php @@ -9,7 +9,7 @@ trait SaveFromRedirect public function saveFromRedirect(string $route, ?Collection $parameters = null) { session()->forget('from'); - if (!$parameters || $parameters->count() === 0) { + if (! $parameters || $parameters->count() === 0) { $parameters = $this->parameters; } $parameters = collect($parameters) ?? collect([]); @@ -18,8 +18,9 @@ public function saveFromRedirect(string $route, ?Collection $parameters = null) session(['from' => [ 'back' => $this->currentRoute, 'route' => $route, - 'parameters' => $parameters + 'parameters' => $parameters, ]]); + return redirect()->route($route); } } diff --git a/app/View/Components/Forms/Button.php b/app/View/Components/Forms/Button.php index 06681910e..da8b46dec 100644 --- a/app/View/Components/Forms/Button.php +++ b/app/View/Components/Forms/Button.php @@ -12,13 +12,13 @@ class Button extends Component * Create a new component instance. */ public function __construct( - public bool $disabled = false, - public bool $noStyle = false, - public ?string $modalId = null, - public string $defaultClass = "button" + public bool $disabled = false, + public bool $noStyle = false, + public ?string $modalId = null, + public string $defaultClass = 'button' ) { if ($this->noStyle) { - $this->defaultClass = ""; + $this->defaultClass = ''; } } diff --git a/app/View/Components/Forms/Checkbox.php b/app/View/Components/Forms/Checkbox.php index 95fe2d4f4..414dbf2ae 100644 --- a/app/View/Components/Forms/Checkbox.php +++ b/app/View/Components/Forms/Checkbox.php @@ -12,14 +12,14 @@ class Checkbox extends Component * Create a new component instance. */ public function __construct( - public ?string $id = null, - public ?string $name = null, - public ?string $value = null, - public ?string $label = null, - public ?string $helper = null, + public ?string $id = null, + public ?string $name = null, + public ?string $value = null, + public ?string $label = null, + public ?string $helper = null, public string|bool $instantSave = false, - public bool $disabled = false, - public string $defaultClass = "dark:border-neutral-700 text-coolgray-400 focus:ring-warning dark:bg-coolgray-100 rounded cursor-pointer dark:disabled:bg-base dark:disabled:cursor-not-allowed", + public bool $disabled = false, + public string $defaultClass = 'dark:border-neutral-700 text-coolgray-400 focus:ring-warning dark:bg-coolgray-100 rounded cursor-pointer dark:disabled:bg-base dark:disabled:cursor-not-allowed', ) { // } diff --git a/app/View/Components/Forms/Datalist.php b/app/View/Components/Forms/Datalist.php index d4ed44266..df0c1cb11 100644 --- a/app/View/Components/Forms/Datalist.php +++ b/app/View/Components/Forms/Datalist.php @@ -18,8 +18,8 @@ public function __construct( public ?string $name = null, public ?string $label = null, public ?string $helper = null, - public bool $required = false, - public string $defaultClass = "input" + public bool $required = false, + public string $defaultClass = 'input' ) { // } @@ -29,10 +29,15 @@ public function __construct( */ public function render(): View|Closure|string { - if (is_null($this->id)) $this->id = new Cuid2(7); - if (is_null($this->name)) $this->name = $this->id; + if (is_null($this->id)) { + $this->id = new Cuid2(7); + } + if (is_null($this->name)) { + $this->name = $this->id; + } $this->label = Str::title($this->label); + return view('components.forms.datalist'); } } diff --git a/app/View/Components/Forms/Input.php b/app/View/Components/Forms/Input.php index 45f8e9678..36c07dae1 100644 --- a/app/View/Components/Forms/Input.php +++ b/app/View/Components/Forms/Input.php @@ -15,23 +15,28 @@ public function __construct( public ?string $type = 'text', public ?string $value = null, public ?string $label = null, - public bool $required = false, - public bool $disabled = false, - public bool $readonly = false, + public bool $required = false, + public bool $disabled = false, + public bool $readonly = false, public ?string $helper = null, - public bool $allowToPeak = true, - public bool $isMultiline = false, - public string $defaultClass = "input", + public bool $allowToPeak = true, + public bool $isMultiline = false, + public string $defaultClass = 'input', ) { } public function render(): View|Closure|string { - if (is_null($this->id)) $this->id = new Cuid2(7); - if (is_null($this->name)) $this->name = $this->id; - if ($this->type === 'password') { - $this->defaultClass = $this->defaultClass . " pr-[2.8rem]"; + if (is_null($this->id)) { + $this->id = new Cuid2(7); } + if (is_null($this->name)) { + $this->name = $this->id; + } + if ($this->type === 'password') { + $this->defaultClass = $this->defaultClass.' pr-[2.8rem]'; + } + // $this->label = Str::title($this->label); return view('components.forms.input'); } diff --git a/app/View/Components/Forms/Select.php b/app/View/Components/Forms/Select.php index 40279bea6..21c147c2b 100644 --- a/app/View/Components/Forms/Select.php +++ b/app/View/Components/Forms/Select.php @@ -18,8 +18,8 @@ public function __construct( public ?string $name = null, public ?string $label = null, public ?string $helper = null, - public bool $required = false, - public string $defaultClass = "select" + public bool $required = false, + public string $defaultClass = 'select' ) { // } @@ -29,10 +29,15 @@ public function __construct( */ public function render(): View|Closure|string { - if (is_null($this->id)) $this->id = new Cuid2(7); - if (is_null($this->name)) $this->name = $this->id; + if (is_null($this->id)) { + $this->id = new Cuid2(7); + } + if (is_null($this->name)) { + $this->name = $this->id; + } $this->label = Str::title($this->label); + return view('components.forms.select'); } } diff --git a/app/View/Components/Forms/Textarea.php b/app/View/Components/Forms/Textarea.php index 28f4a45ba..bfdf03a31 100644 --- a/app/View/Components/Forms/Textarea.php +++ b/app/View/Components/Forms/Textarea.php @@ -19,16 +19,16 @@ public function __construct( public ?string $value = null, public ?string $label = null, public ?string $placeholder = null, - public bool $required = false, - public bool $disabled = false, - public bool $readonly = false, - public bool $allowTab = false, - public bool $spellcheck = false, + public bool $required = false, + public bool $disabled = false, + public bool $readonly = false, + public bool $allowTab = false, + public bool $spellcheck = false, public ?string $helper = null, - public bool $realtimeValidation = false, - public bool $allowToPeak = true, - public string $defaultClass = "input scrollbar font-mono", - public string $defaultClassInput = "input" + public bool $realtimeValidation = false, + public bool $allowToPeak = true, + public string $defaultClass = 'input scrollbar font-mono', + public string $defaultClassInput = 'input' ) { // } @@ -38,8 +38,12 @@ public function __construct( */ public function render(): View|Closure|string { - if (is_null($this->id)) $this->id = new Cuid2(7); - if (is_null($this->name)) $this->name = $this->id; + if (is_null($this->id)) { + $this->id = new Cuid2(7); + } + if (is_null($this->name)) { + $this->name = $this->id; + } // $this->label = Str::title($this->label); return view('components.forms.textarea'); diff --git a/app/View/Components/Modal.php b/app/View/Components/Modal.php index e38d2dfb1..7e254ebdc 100644 --- a/app/View/Components/Modal.php +++ b/app/View/Components/Modal.php @@ -12,14 +12,14 @@ class Modal extends Component * Create a new component instance. */ public function __construct( - public string $modalId, - public ?string $submitWireAction = null, - public ?string $modalTitle = null, - public ?string $modalBody = null, - public ?string $modalSubmit = null, - public bool $noSubmit = false, - public bool $yesOrNo = false, - public string $action = 'delete' + public string $modalId, + public ?string $submitWireAction = null, + public ?string $modalTitle = null, + public ?string $modalBody = null, + public ?string $modalSubmit = null, + public bool $noSubmit = false, + public bool $yesOrNo = false, + public string $action = 'delete' ) { // } diff --git a/app/View/Components/ResourceView.php b/app/View/Components/ResourceView.php index 98efddc00..5a11b159d 100644 --- a/app/View/Components/ResourceView.php +++ b/app/View/Components/ResourceView.php @@ -16,8 +16,7 @@ public function __construct( public ?string $logo = null, public ?string $documentation = null, public bool $upgrade = false, - ) - { + ) { } diff --git a/app/View/Components/Services/Links.php b/app/View/Components/Services/Links.php index 4cc19b518..9baf0578d 100644 --- a/app/View/Components/Services/Links.php +++ b/app/View/Components/Services/Links.php @@ -6,12 +6,13 @@ use Closure; use Illuminate\Contracts\View\View; use Illuminate\Support\Collection; -use Illuminate\View\Component; use Illuminate\Support\Str; +use Illuminate\View\Component; class Links extends Component { public Collection $links; + public function __construct(public Service $service) { $this->links = collect([]); @@ -38,7 +39,7 @@ public function __construct(public Service $service) } else { $hostPort = $port; } - $this->links->push(base_url(withPort: false) . ":{$hostPort}"); + $this->links->push(base_url(withPort: false).":{$hostPort}"); }); } } diff --git a/app/View/Components/Status/Index.php b/app/View/Components/Status/Index.php index 56f4c598b..f8436a102 100644 --- a/app/View/Components/Status/Index.php +++ b/app/View/Components/Status/Index.php @@ -11,7 +11,6 @@ class Index extends Component /** * Create a new component instance. */ - public function __construct( public $resource = null, public bool $showRefreshButton = true, diff --git a/bootstrap/getVersion.php b/bootstrap/getVersion.php index 2653f6575..a8329a319 100644 --- a/bootstrap/getVersion.php +++ b/bootstrap/getVersion.php @@ -1,3 +1,4 @@ user()->currentAccessToken(); + return data_get($token, 'team_id'); } function invalid_token() diff --git a/bootstrap/helpers/applications.php b/bootstrap/helpers/applications.php index 39d21bcca..376b0f2aa 100644 --- a/bootstrap/helpers/applications.php +++ b/bootstrap/helpers/applications.php @@ -8,10 +8,10 @@ use App\Models\StandaloneDocker; use Spatie\Url\Url; -function queue_application_deployment(Application $application, string $deployment_uuid, int | null $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null, bool $no_questions_asked = false, Server $server = null, StandaloneDocker $destination = null, bool $only_this_server = false, bool $rollback = false) +function queue_application_deployment(Application $application, string $deployment_uuid, ?int $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null, bool $no_questions_asked = false, ?Server $server = null, ?StandaloneDocker $destination = null, bool $only_this_server = false, bool $rollback = false) { $application_id = $application->id; - $deployment_link = Url::fromString($application->link() . "/deployment/{$deployment_uuid}"); + $deployment_link = Url::fromString($application->link()."/deployment/{$deployment_uuid}"); $deployment_url = $deployment_link->getPath(); $server_id = $application->destination->server->id; $server_name = $application->destination->server->name; @@ -39,14 +39,14 @@ function queue_application_deployment(Application $application, string $deployme 'commit' => $commit, 'rollback' => $rollback, 'git_type' => $git_type, - 'only_this_server' => $only_this_server + 'only_this_server' => $only_this_server, ]); if ($no_questions_asked) { dispatch(new ApplicationDeploymentJob( application_deployment_queue_id: $deployment->id, )); - } else if (next_queuable($server_id, $application_id)) { + } elseif (next_queuable($server_id, $application_id)) { dispatch(new ApplicationDeploymentJob( application_deployment_queue_id: $deployment->id, )); @@ -95,5 +95,6 @@ function next_queuable(string $server_id, string $application_id): bool if ($deployments->count() > $concurrent_builds) { return false; } + return true; } diff --git a/bootstrap/helpers/constants.php b/bootstrap/helpers/constants.php index 8976f8d48..e0272fa4c 100644 --- a/bootstrap/helpers/constants.php +++ b/bootstrap/helpers/constants.php @@ -28,18 +28,18 @@ 'neo4j', 'influxdb', 'clickhouse/clickhouse-server', - 'supabase/postgres' + 'supabase/postgres', ]; const SPECIFIC_SERVICES = [ 'quay.io/minio/minio', - 'svhd/logto' + 'svhd/logto', ]; // Based on /etc/os-release const SUPPORTED_OS = [ 'ubuntu debian raspbian', 'centos fedora rhel ol rocky amzn almalinux', - 'sles opensuse-leap opensuse-tumbleweed' + 'sles opensuse-leap opensuse-tumbleweed', ]; const SHARED_VARIABLE_TYPES = ['team', 'project', 'environment']; diff --git a/bootstrap/helpers/databases.php b/bootstrap/helpers/databases.php index 7e12350fb..dba8aa543 100644 --- a/bootstrap/helpers/databases.php +++ b/bootstrap/helpers/databases.php @@ -15,16 +15,18 @@ function generate_database_name(string $type): string { $cuid = new Cuid2(7); - return $type . '-database-' . $cuid; + + return $type.'-database-'.$cuid; } function create_standalone_postgresql($environment_id, $destination_uuid): StandalonePostgresql { // TODO: If another type of destination is added, this will need to be updated. $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + if (! $destination) { throw new Exception('Destination not found'); } + return StandalonePostgresql::create([ 'name' => generate_database_name('postgresql'), 'postgres_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), @@ -37,9 +39,10 @@ function create_standalone_postgresql($environment_id, $destination_uuid): Stand function create_standalone_redis($environment_id, $destination_uuid): StandaloneRedis { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + if (! $destination) { throw new Exception('Destination not found'); } + return StandaloneRedis::create([ 'name' => generate_database_name('redis'), 'redis_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), @@ -52,9 +55,10 @@ function create_standalone_redis($environment_id, $destination_uuid): Standalone function create_standalone_mongodb($environment_id, $destination_uuid): StandaloneMongodb { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + if (! $destination) { throw new Exception('Destination not found'); } + return StandaloneMongodb::create([ 'name' => generate_database_name('mongodb'), 'mongo_initdb_root_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), @@ -66,9 +70,10 @@ function create_standalone_mongodb($environment_id, $destination_uuid): Standalo function create_standalone_mysql($environment_id, $destination_uuid): StandaloneMysql { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + if (! $destination) { throw new Exception('Destination not found'); } + return StandaloneMysql::create([ 'name' => generate_database_name('mysql'), 'mysql_root_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), @@ -81,9 +86,10 @@ function create_standalone_mysql($environment_id, $destination_uuid): Standalone function create_standalone_mariadb($environment_id, $destination_uuid): StandaloneMariadb { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + if (! $destination) { throw new Exception('Destination not found'); } + return StandaloneMariadb::create([ 'name' => generate_database_name('mariadb'), 'mariadb_root_password' => \Illuminate\Support\Str::password(length: 64, symbols: false), @@ -96,9 +102,10 @@ function create_standalone_mariadb($environment_id, $destination_uuid): Standalo function create_standalone_keydb($environment_id, $destination_uuid): StandaloneKeydb { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + 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), @@ -111,9 +118,10 @@ function create_standalone_keydb($environment_id, $destination_uuid): Standalone function create_standalone_dragonfly($environment_id, $destination_uuid): StandaloneDragonfly { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + 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), @@ -125,9 +133,10 @@ function create_standalone_dragonfly($environment_id, $destination_uuid): Standa function create_standalone_clickhouse($environment_id, $destination_uuid): StandaloneClickhouse { $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + 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), @@ -139,11 +148,8 @@ function create_standalone_clickhouse($environment_id, $destination_uuid): Stand /** * Delete file locally on the filesystem. - * @param string $filename - * @param Server $server - * @return void */ -function delete_backup_locally(string | null $filename, Server $server): void +function delete_backup_locally(?string $filename, Server $server): void { if (empty($filename)) { return; diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index fb980c296..91e553cf6 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -1,6 +1,5 @@ isSwarm()) { + if (! $server->isSwarm()) { $containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server); $containers = format_docker_command_output_to_json($containers); $containers = $containers->map(function ($container) use ($pullRequestId, $includePullrequests) { $labels = data_get($container, 'Labels'); - if (!str($labels)->contains("coolify.pullRequestId=")) { - data_set($container, 'Labels', $labels . ",coolify.pullRequestId={$pullRequestId}"); + if (! str($labels)->contains('coolify.pullRequestId=')) { + data_set($container, 'Labels', $labels.",coolify.pullRequestId={$pullRequestId}"); + return $container; } if ($includePullrequests) { @@ -28,11 +28,14 @@ function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pul if (str($labels)->contains("coolify.pullRequestId=$pullRequestId")) { return $container; } + return null; }); $containers = $containers->filter(); + return $containers; } + return $containers; } @@ -44,6 +47,7 @@ function format_docker_command_output_to_json($rawOutput): Collection } else { $outputLines = collect($outputLines); } + return $outputLines ->reject(fn ($line) => empty($line)) ->map(fn ($outputLine) => json_decode($outputLine, true, flags: JSON_THROW_ON_ERROR)); @@ -60,6 +64,7 @@ function format_docker_labels_to_json(string|array $rawOutput): Collection ->reject(fn ($line) => empty($line)) ->map(function ($outputLine) { $outputArray = explode(',', $outputLine); + return collect($outputArray) ->map(function ($outputLine) { return explode('=', $outputLine); @@ -74,8 +79,10 @@ function format_docker_envs_to_json($rawOutput) { try { $outputLines = json_decode($rawOutput, true, flags: JSON_THROW_ON_ERROR); + return collect(data_get($outputLines[0], 'Config.Env', []))->mapWithKeys(function ($env) { $env = explode('=', $env); + return [$env[0] => $env[1]]; }); } catch (\Throwable $e) { @@ -88,6 +95,7 @@ function checkMinimumDockerEngineVersion($dockerVersion) if ($majorDockerVersion <= 22) { $dockerVersion = null; } + return $dockerVersion; } function executeInDocker(string $containerId, string $command) @@ -103,7 +111,7 @@ function getContainerStatus(Server $server, string $container_id, bool $all_data } else { $container = instant_remote_process(["docker inspect --format '{{json .}}' {$container_id}"], $server, $throwError); } - if (!$container) { + if (! $container) { return 'exited'; } $container = format_docker_command_output_to_json($container); @@ -113,8 +121,8 @@ function getContainerStatus(Server $server, string $container_id, bool $all_data if ($server->isSwarm()) { $replicas = data_get($container[0], 'Replicas'); $replicas = explode('/', $replicas); - $active = (int)$replicas[0]; - $total = (int)$replicas[1]; + $active = (int) $replicas[0]; + $total = (int) $replicas[1]; if ($active === $total) { return 'running'; } else { @@ -130,15 +138,16 @@ function generateApplicationContainerName(Application $application, $pull_reques $consistent_container_name = $application->settings->is_consistent_container_name_enabled; $now = now()->format('Hisu'); if ($pull_request_id !== 0 && $pull_request_id !== null) { - return $application->uuid . '-pr-' . $pull_request_id; + return $application->uuid.'-pr-'.$pull_request_id; } else { if ($consistent_container_name) { return $application->uuid; } - return $application->uuid . '-' . $now; + + return $application->uuid.'-'.$now; } } -function get_port_from_dockerfile($dockerfile): int|null +function get_port_from_dockerfile($dockerfile): ?int { $dockerfile_array = explode("\n", $dockerfile); $found_exposed_port = null; @@ -150,8 +159,9 @@ function get_port_from_dockerfile($dockerfile): int|null } } if ($found_exposed_port) { - return (int)$found_exposed_port->value(); + return (int) $found_exposed_port->value(); } + return null; } @@ -159,15 +169,16 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica { $labels = collect([]); $labels->push('coolify.managed=true'); - $labels->push('coolify.version=' . config('version')); - $labels->push("coolify." . $type . "Id=" . $id); + $labels->push('coolify.version='.config('version')); + $labels->push('coolify.'.$type.'Id='.$id); $labels->push("coolify.type=$type"); - $labels->push('coolify.name=' . $name); - $labels->push('coolify.pullRequestId=' . $pull_request_id); + $labels->push('coolify.name='.$name); + $labels->push('coolify.pullRequestId='.$pull_request_id); if ($type === 'service') { - $subId && $labels->push('coolify.service.subId=' . $subId); - $subType && $labels->push('coolify.service.subType=' . $subType); + $subId && $labels->push('coolify.service.subId='.$subId); + $subType && $labels->push('coolify.service.subType='.$subType); } + return $labels; } function generateServiceSpecificFqdns(ServiceApplication|Application $resource) @@ -177,7 +188,7 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource) $server = data_get($resource, 'service.server'); $environment_variables = data_get($resource, 'service.environment_variables'); $type = $resource->serviceType(); - } else if ($resource->getMorphClass() === 'App\Models\Application') { + } elseif ($resource->getMorphClass() === 'App\Models\Application') { $uuid = data_get($resource, 'uuid'); $server = data_get($resource, 'destination.server'); $environment_variables = data_get($resource, 'environment_variables'); @@ -197,17 +208,17 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource) } if (is_null($MINIO_BROWSER_REDIRECT_URL?->value)) { $MINIO_BROWSER_REDIRECT_URL?->update([ - "value" => generateFqdn($server, 'console-' . $uuid) + 'value' => generateFqdn($server, 'console-'.$uuid), ]); } if (is_null($MINIO_SERVER_URL?->value)) { $MINIO_SERVER_URL?->update([ - "value" => generateFqdn($server, 'minio-' . $uuid) + 'value' => generateFqdn($server, 'minio-'.$uuid), ]); } $payload = collect([ - $MINIO_BROWSER_REDIRECT_URL->value . ':9001', - $MINIO_SERVER_URL->value . ':9000', + $MINIO_BROWSER_REDIRECT_URL->value.':9001', + $MINIO_SERVER_URL->value.':9000', ]); break; case $type?->contains('logto'): @@ -218,23 +229,24 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource) } if (is_null($LOGTO_ENDPOINT?->value)) { $LOGTO_ENDPOINT?->update([ - "value" => generateFqdn($server, 'logto-' . $uuid) + 'value' => generateFqdn($server, 'logto-'.$uuid), ]); } if (is_null($LOGTO_ADMIN_ENDPOINT?->value)) { $LOGTO_ADMIN_ENDPOINT?->update([ - "value" => generateFqdn($server, 'logto-admin-' . $uuid) + 'value' => generateFqdn($server, 'logto-admin-'.$uuid), ]); } $payload = collect([ - $LOGTO_ENDPOINT->value . ':3001', - $LOGTO_ADMIN_ENDPOINT->value . ':3002', + $LOGTO_ENDPOINT->value.':3001', + $LOGTO_ADMIN_ENDPOINT->value.':3002', ]); break; } + return $payload; } -function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null) +function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both') { $labels = collect([]); if ($serviceLabels) { @@ -247,10 +259,10 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, $url = Url::fromString($domain); $host = $url->getHost(); $path = $url->getPath(); - + $host_without_www = str($host)->replace('www.', ''); $schema = $url->getScheme(); $port = $url->getPort(); - if (is_null($port) && !is_null($onlyPort)) { + if (is_null($port) && ! is_null($onlyPort)) { $port = $onlyPort; } $labels->push("caddy_{$loop}={$schema}://{$host}"); @@ -266,23 +278,31 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, if ($is_gzip_enabled) { $labels->push("caddy_{$loop}.encode=zstd gzip"); } + if ($redirect_direction === 'www' && ! str($host)->startsWith('www.')) { + $labels->push("caddy_{$loop}.redir={$schema}://www.{$host}{uri}"); + } + if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) { + $labels->push("caddy_{$loop}.redir={$schema}://{$host_without_www}{uri}"); + } if (isDev()) { // $labels->push("caddy_{$loop}.tls=internal"); } } + return $labels->sort(); } -function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false, ?string $image = null) +function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false, ?string $image = null, string $redirect_direction = 'both') { $labels = collect([]); $labels->push('traefik.enable=true'); - $labels->push("traefik.http.middlewares.gzip.compress=true"); - $labels->push("traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"); + $labels->push('traefik.http.middlewares.gzip.compress=true'); + $labels->push('traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https'); $basic_auth = false; $basic_auth_middleware = null; $redirect = false; $redirect_middleware = null; + if ($serviceLabels) { $basic_auth = $serviceLabels->contains(function ($value) { return str_contains($value, 'basicauth'); @@ -316,12 +336,13 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ if ($generate_unique_uuid) { $uuid = new Cuid2(7); } + $url = Url::fromString($domain); $host = $url->getHost(); $path = $url->getPath(); $schema = $url->getScheme(); $port = $url->getPort(); - if (is_null($port) && !is_null($onlyPort)) { + if (is_null($port) && ! is_null($onlyPort)) { $port = $onlyPort; } $http_label = "http-{$loop}-{$uuid}"; @@ -332,8 +353,21 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ } if (str($image)->contains('ghost')) { $labels->push("traefik.http.middlewares.redir-ghost.redirectregex.regex=^{$path}/(.*)"); - $labels->push("traefik.http.middlewares.redir-ghost.redirectregex.replacement=/$1"); + $labels->push('traefik.http.middlewares.redir-ghost.redirectregex.replacement=/$1'); } + + $to_www_name = "{$loop}-{$uuid}-to-www"; + $to_non_www_name = "{$loop}-{$uuid}-to-non-www"; + $redirect_to_non_www = [ + "traefik.http.middlewares.{$to_non_www_name}.redirectregex.regex=^(http|https)://www\.(.+)", + "traefik.http.middlewares.{$to_non_www_name}.redirectregex.replacement=\${1}://\${2}", + "traefik.http.middlewares.{$to_non_www_name}.redirectregex.permanent=false", + ]; + $redirect_to_www = [ + "traefik.http.middlewares.{$to_www_name}.redirectregex.regex=^(http|https)://(?:www\.)?(.+)", + "traefik.http.middlewares.{$to_www_name}.redirectregex.replacement=\${1}://www.\${2}", + "traefik.http.middlewares.{$to_www_name}.redirectregex.permanent=false", + ]; if ($schema === 'https') { // Set labels for https $labels->push("traefik.http.routers.{$https_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)"); @@ -344,7 +378,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ } if ($path !== '/') { $middlewares = collect([]); - if ($is_stripprefix_enabled && !str($image)->contains('ghost')) { + if ($is_stripprefix_enabled && ! str($image)->contains('ghost')) { $labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}"); $middlewares->push("{$https_label}-stripprefix"); } @@ -360,6 +394,14 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ if (str($image)->contains('ghost')) { $middlewares->push('redir-ghost'); } + if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) { + $labels = $labels->merge($redirect_to_non_www); + $middlewares->push($to_non_www_name); + } + if ($redirect_direction === 'www' && ! str($host)->startsWith('www.')) { + $labels = $labels->merge($redirect_to_www); + $middlewares->push($to_www_name); + } if ($middlewares->isNotEmpty()) { $middlewares = $middlewares->join(','); $labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}"); @@ -378,6 +420,14 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ if (str($image)->contains('ghost')) { $middlewares->push('redir-ghost'); } + if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) { + $labels = $labels->merge($redirect_to_non_www); + $middlewares->push($to_non_www_name); + } + if ($redirect_direction === 'www' && ! str($host)->startsWith('www.')) { + $labels = $labels->merge($redirect_to_www); + $middlewares->push($to_www_name); + } if ($middlewares->isNotEmpty()) { $middlewares = $middlewares->join(','); $labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}"); @@ -406,7 +456,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ } if ($path !== '/') { $middlewares = collect([]); - if ($is_stripprefix_enabled && !str($image)->contains('ghost')) { + if ($is_stripprefix_enabled && ! str($image)->contains('ghost')) { $labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}"); $middlewares->push("{$https_label}-stripprefix"); } @@ -422,6 +472,14 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ if (str($image)->contains('ghost')) { $middlewares->push('redir-ghost'); } + if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) { + $labels = $labels->merge($redirect_to_non_www); + $middlewares->push($to_non_www_name); + } + if ($redirect_direction === 'www' && ! str($host)->startsWith('www.')) { + $labels = $labels->merge($redirect_to_www); + $middlewares->push($to_www_name); + } if ($middlewares->isNotEmpty()) { $middlewares = $middlewares->join(','); $labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}"); @@ -440,6 +498,14 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ if (str($image)->contains('ghost')) { $middlewares->push('redir-ghost'); } + if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) { + $labels = $labels->merge($redirect_to_non_www); + $middlewares->push($to_non_www_name); + } + if ($redirect_direction === 'www' && ! str($host)->startsWith('www.')) { + $labels = $labels->merge($redirect_to_www); + $middlewares->push($to_www_name); + } if ($middlewares->isNotEmpty()) { $middlewares = $middlewares->join(','); $labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}"); @@ -450,6 +516,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ continue; } } + return $labels->sort(); } function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array @@ -462,7 +529,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview $pull_request_id = data_get($preview, 'pull_request_id', 0); $appUuid = $application->uuid; if ($pull_request_id !== 0) { - $appUuid = $appUuid . '-pr-' . $pull_request_id; + $appUuid = $appUuid.'-pr-'.$pull_request_id; } $labels = collect([]); if ($pull_request_id === 0) { @@ -474,7 +541,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview onlyPort: $onlyPort, is_force_https_enabled: $application->isForceHttpsEnabled(), is_gzip_enabled: $application->isGzipEnabled(), - is_stripprefix_enabled: $application->isStripprefixEnabled() + is_stripprefix_enabled: $application->isStripprefixEnabled(), + redirect_direction: $application->redirect )); // Add Caddy labels $labels = $labels->merge(fqdnLabelsForCaddy( @@ -484,11 +552,12 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview onlyPort: $onlyPort, is_force_https_enabled: $application->isForceHttpsEnabled(), is_gzip_enabled: $application->isGzipEnabled(), - is_stripprefix_enabled: $application->isStripprefixEnabled() + is_stripprefix_enabled: $application->isStripprefixEnabled(), + redirect_direction: $application->redirect )); } } else { - if (data_get($preview,'fqdn')) { + if (data_get($preview, 'fqdn')) { $domains = Str::of(data_get($preview, 'fqdn'))->explode(','); } else { $domains = collect([]); @@ -513,6 +582,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview )); } + return $labels->all(); } @@ -531,6 +601,7 @@ function isDatabaseImage(?string $image = null) if (collect(DATABASE_DOCKER_IMAGES)->contains($imageName)) { return true; } + return false; } @@ -573,7 +644,7 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null $options = collect($options); // Easily get mappings from https://github.com/composerize/composerize/blob/master/packages/composerize/src/mappings.js foreach ($options as $option => $value) { - if (!data_get($mapping, $option)) { + if (! data_get($mapping, $option)) { continue; } if ($option === '--ulimit') { @@ -587,7 +658,7 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null $hard_limit = $limits[1]; $ulimits->put($type, [ 'soft' => $soft_limit, - 'hard' => $hard_limit + 'hard' => $hard_limit, ]); } else { $soft_limit = $ulimit[1]; @@ -600,18 +671,21 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null } else { if ($list_options->contains($option)) { if ($compose_options->has($mapping[$option])) { - $compose_options->put($mapping[$option], $options->get($mapping[$option]) . ',' . $value); + $compose_options->put($mapping[$option], $options->get($mapping[$option]).','.$value); } else { $compose_options->put($mapping[$option], $value); } + continue; } else { $compose_options->put($mapping[$option], $value); + continue; } $compose_options->forget($option); } } + return $compose_options->toArray(); } @@ -627,9 +701,11 @@ function validateComposeFile(string $compose, int $server_id): string|Throwable "docker compose -f /tmp/{$uuid}.yml config", ], $server); ray($output); + return 'OK'; } catch (\Throwable $e) { ray($e); + return $e->getMessage(); } finally { instant_remote_process([ @@ -640,13 +716,15 @@ function validateComposeFile(string $compose, int $server_id): string|Throwable function escapeEnvVariables($value) { - $search = array("\\", "\r", "\t", "\x0", '"', "'"); - $replace = array("\\\\", "\\r", "\\t", "\\0", '\"', "\'"); + $search = ['\\', "\r", "\t", "\x0", '"', "'"]; + $replace = ['\\\\', '\\r', '\\t', '\\0', '\"', "\'"]; + return str_replace($search, $replace, $value); } function escapeDollarSign($value) { - $search = array('$'); - $replace = array('$$'); + $search = ['$']; + $replace = ['$$']; + return str_replace($search, $replace, $value); } diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index 0ae94363b..d916dc9c8 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -26,11 +26,12 @@ function generate_github_installation_token(GithubApp $source) ->toString(); $token = Http::withHeaders([ 'Authorization' => "Bearer $issuedToken", - 'Accept' => 'application/vnd.github.machine-man-preview+json' + 'Accept' => 'application/vnd.github.machine-man-preview+json', ])->post("{$source->api_url}/app/installations/{$source->installation_id}/access_tokens"); if ($token->failed()) { - throw new RuntimeException("Failed to get access token for " . $source->name . " with error: " . data_get($token->json(),'message','no error message found')); + throw new RuntimeException('Failed to get access token for '.$source->name.' with error: '.data_get($token->json(), 'message', 'no error message found')); } + return $token->json()['token']; } @@ -47,10 +48,11 @@ function generate_github_jwt_token(GithubApp $source) ->expiresAt($now->modify('+10 minutes')) ->getToken($algorithm, $signingKey) ->toString(); + return $issuedToken; } -function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $method = 'get', array|null $data = null, bool $throwError = true) +function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $method = 'get', ?array $data = null, bool $throwError = true) { if (is_null($source)) { throw new \Exception('Not implemented yet.'); @@ -70,12 +72,13 @@ function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $m $json = $response->json(); if ($response->failed() && $throwError) { ray($json); - throw new \Exception("Failed to get data from {$source->name} with error:

" . $json['message'] . "

Rate Limit resets at: " . Carbon::parse((int)$response->header('X-RateLimit-Reset'))->format('Y-m-d H:i:s') . 'UTC'); + throw new \Exception("Failed to get data from {$source->name} with error:

".$json['message'].'

Rate Limit resets at: '.Carbon::parse((int) $response->header('X-RateLimit-Reset'))->format('Y-m-d H:i:s').'UTC'); } + return [ 'rate_limit_remaining' => $response->header('X-RateLimit-Remaining'), 'rate_limit_reset' => $response->header('X-RateLimit-Reset'), - 'data' => collect($json) + 'data' => collect($json), ]; } @@ -84,10 +87,13 @@ function get_installation_path(GithubApp $source) $github = GithubApp::where('uuid', $source->uuid)->first(); $name = Str::of(Str::kebab($github->name)); $installation_path = $github->html_url === 'https://github.com' ? 'apps' : 'github-apps'; + return "$github->html_url/$installation_path/$name/installations/new"; } -function get_permissions_path(GithubApp $source) { +function get_permissions_path(GithubApp $source) +{ $github = GithubApp::where('uuid', $source->uuid)->first(); $name = Str::of(Str::kebab($github->name)); + return "$github->html_url/settings/apps/$name/permissions"; } diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php index 1eea1893e..2bf230c20 100644 --- a/bootstrap/helpers/proxy.php +++ b/bootstrap/helpers/proxy.php @@ -2,12 +2,9 @@ use App\Actions\Proxy\SaveConfiguration; use App\Models\Application; -use App\Models\InstanceSettings; use App\Models\Server; -use Spatie\Url\Url; use Symfony\Component\Yaml\Yaml; - function connectProxyToNetworks(Server $server) { if ($server->isSwarm()) { @@ -35,7 +32,7 @@ function connectProxyToNetworks(Server $server) $pullRequestId = $preview->pull_request_id; $applicationId = $preview->application_id; $application = Application::find($applicationId); - if (!$application) { + if (! $application) { continue; } $network = "{$application->uuid}-{$pullRequestId}"; @@ -92,108 +89,108 @@ function generate_default_proxy_configuration(Server $server) $array_of_networks = collect([]); $networks->map(function ($network) use ($array_of_networks) { $array_of_networks[$network] = [ - "external" => true, + 'external' => true, ]; }); if ($proxy_type === 'TRAEFIK_V2') { $labels = [ - "traefik.enable=true", - "traefik.http.routers.traefik.entrypoints=http", - "traefik.http.routers.traefik.service=api@internal", - "traefik.http.services.traefik.loadbalancer.server.port=8080", - "coolify.managed=true", + 'traefik.enable=true', + 'traefik.http.routers.traefik.entrypoints=http', + 'traefik.http.routers.traefik.service=api@internal', + 'traefik.http.services.traefik.loadbalancer.server.port=8080', + 'coolify.managed=true', ]; $config = [ - "version" => "3.8", - "networks" => $array_of_networks->toArray(), - "services" => [ - "traefik" => [ - "container_name" => "coolify-proxy", - "image" => "traefik:v2.10", - "restart" => RESTART_MODE, - "extra_hosts" => [ - "host.docker.internal:host-gateway", + 'version' => '3.8', + 'networks' => $array_of_networks->toArray(), + 'services' => [ + 'traefik' => [ + 'container_name' => 'coolify-proxy', + 'image' => 'traefik:v2.10', + 'restart' => RESTART_MODE, + 'extra_hosts' => [ + 'host.docker.internal:host-gateway', ], - "networks" => $networks->toArray(), - "ports" => [ - "80:80", - "443:443", - "8080:8080", + 'networks' => $networks->toArray(), + 'ports' => [ + '80:80', + '443:443', + '8080:8080', ], - "healthcheck" => [ - "test" => "wget -qO- http://localhost:80/ping || exit 1", - "interval" => "4s", - "timeout" => "2s", - "retries" => 5, + 'healthcheck' => [ + 'test' => 'wget -qO- http://localhost:80/ping || exit 1', + 'interval' => '4s', + 'timeout' => '2s', + 'retries' => 5, ], - "volumes" => [ - "/var/run/docker.sock:/var/run/docker.sock:ro", + 'volumes' => [ + '/var/run/docker.sock:/var/run/docker.sock:ro', "{$proxy_path}:/traefik", ], - "command" => [ - "--ping=true", - "--ping.entrypoint=http", - "--api.dashboard=true", - "--api.insecure=false", - "--entrypoints.http.address=:80", - "--entrypoints.https.address=:443", - "--entrypoints.http.http.encodequerysemicolons=true", - "--entryPoints.http.http2.maxConcurrentStreams=50", - "--entrypoints.https.http.encodequerysemicolons=true", - "--entryPoints.https.http2.maxConcurrentStreams=50", - "--providers.docker.exposedbydefault=false", - "--providers.file.directory=/traefik/dynamic/", - "--providers.file.watch=true", - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true", - "--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json", - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http", + 'command' => [ + '--ping=true', + '--ping.entrypoint=http', + '--api.dashboard=true', + '--api.insecure=false', + '--entrypoints.http.address=:80', + '--entrypoints.https.address=:443', + '--entrypoints.http.http.encodequerysemicolons=true', + '--entryPoints.http.http2.maxConcurrentStreams=50', + '--entrypoints.https.http.encodequerysemicolons=true', + '--entryPoints.https.http2.maxConcurrentStreams=50', + '--providers.docker.exposedbydefault=false', + '--providers.file.directory=/traefik/dynamic/', + '--providers.file.watch=true', + '--certificatesresolvers.letsencrypt.acme.httpchallenge=true', + '--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json', + '--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http', ], - "labels" => $labels, + 'labels' => $labels, ], ], ]; if (isDev()) { // $config['services']['traefik']['command'][] = "--log.level=debug"; - $config['services']['traefik']['command'][] = "--accesslog.filepath=/traefik/access.log"; - $config['services']['traefik']['command'][] = "--accesslog.bufferingsize=100"; + $config['services']['traefik']['command'][] = '--accesslog.filepath=/traefik/access.log'; + $config['services']['traefik']['command'][] = '--accesslog.bufferingsize=100'; } if ($server->isSwarm()) { data_forget($config, 'services.traefik.container_name'); data_forget($config, 'services.traefik.restart'); data_forget($config, 'services.traefik.labels'); - $config['services']['traefik']['command'][] = "--providers.docker.swarmMode=true"; + $config['services']['traefik']['command'][] = '--providers.docker.swarmMode=true'; $config['services']['traefik']['deploy'] = [ - "labels" => $labels, - "placement" => [ - "constraints" => [ - "node.role==manager", + 'labels' => $labels, + 'placement' => [ + 'constraints' => [ + 'node.role==manager', ], ], ]; } else { - $config['services']['traefik']['command'][] = "--providers.docker=true"; + $config['services']['traefik']['command'][] = '--providers.docker=true'; } - } else if ($proxy_type === 'CADDY') { + } elseif ($proxy_type === 'CADDY') { $config = [ - "version" => "3.8", - "networks" => $array_of_networks->toArray(), - "services" => [ - "caddy" => [ - "container_name" => "coolify-proxy", - "image" => "lucaslorentz/caddy-docker-proxy:2.8-alpine", - "restart" => RESTART_MODE, - "extra_hosts" => [ - "host.docker.internal:host-gateway", + 'version' => '3.8', + 'networks' => $array_of_networks->toArray(), + 'services' => [ + 'caddy' => [ + 'container_name' => 'coolify-proxy', + 'image' => 'lucaslorentz/caddy-docker-proxy:2.8-alpine', + 'restart' => RESTART_MODE, + 'extra_hosts' => [ + 'host.docker.internal:host-gateway', ], - "environment" => [ - "CADDY_DOCKER_POLLING_INTERVAL=5s", - "CADDY_DOCKER_CADDYFILE_PATH=/dynamic/Caddyfile", + 'environment' => [ + 'CADDY_DOCKER_POLLING_INTERVAL=5s', + 'CADDY_DOCKER_CADDYFILE_PATH=/dynamic/Caddyfile', ], - "networks" => $networks->toArray(), - "ports" => [ - "80:80", - "443:443", + 'networks' => $networks->toArray(), + 'ports' => [ + '80:80', + '443:443', ], // "healthcheck" => [ // "test" => "wget -qO- http://localhost:80|| exit 1", @@ -201,8 +198,8 @@ function generate_default_proxy_configuration(Server $server) // "timeout" => "2s", // "retries" => 5, // ], - "volumes" => [ - "/var/run/docker.sock:/var/run/docker.sock:ro", + 'volumes' => [ + '/var/run/docker.sock:/var/run/docker.sock:ro', "{$proxy_path}/dynamic:/dynamic", "{$proxy_path}/config:/config", "{$proxy_path}/data:/data", @@ -216,5 +213,6 @@ function generate_default_proxy_configuration(Server $server) $config = Yaml::dump($config, 12, 2); SaveConfiguration::run($server, $config); + return $config; } diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php index 85533550b..918aa74cc 100644 --- a/bootstrap/helpers/remoteProcess.php +++ b/bootstrap/helpers/remoteProcess.php @@ -17,12 +17,12 @@ use Spatie\Activitylog\Contracts\Activity; function remote_process( - Collection|array $command, - Server $server, - ?string $type = null, + Collection|array $command, + Server $server, + ?string $type = null, ?string $type_uuid = null, - ?Model $model = null, - bool $ignore_errors = false, + ?Model $model = null, + bool $ignore_errors = false, $callEventOnFinish = null, $callEventData = null ): Activity { @@ -38,10 +38,11 @@ function remote_process( $command_string = implode("\n", $command); if (auth()->user()) { $teams = auth()->user()->teams->pluck('id'); - if (!$teams->contains($server->team_id) && !$teams->contains(0)) { - throw new \Exception("User is not part of the team that owns this server"); + if (! $teams->contains($server->team_id) && ! $teams->contains(0)) { + throw new \Exception('User is not part of the team that owns this server'); } } + return resolve(PrepareCoolifyTask::class, [ 'remoteProcessArgs' => new CoolifyTaskArgs( server_uuid: $server->uuid, @@ -61,15 +62,16 @@ function server_ssh_configuration(Server $server) { $uuid = data_get($server, 'uuid'); if (is_null($uuid)) { - throw new \Exception("Server does not have a uuid"); + throw new \Exception('Server does not have a uuid'); } $private_key_filename = "id.root@{$server->uuid}"; - $location = '/var/www/html/storage/app/ssh/keys/' . $private_key_filename; - $mux_filename = '/var/www/html/storage/app/ssh/mux/' . $server->muxFilename(); + $location = '/var/www/html/storage/app/ssh/keys/'.$private_key_filename; + $mux_filename = '/var/www/html/storage/app/ssh/mux/'.$server->muxFilename(); + return [ 'location' => $location, 'mux_filename' => $mux_filename, - 'private_key_filename' => $private_key_filename + 'private_key_filename' => $private_key_filename, ]; } function savePrivateKeyToFs(Server $server) @@ -77,10 +79,11 @@ function savePrivateKeyToFs(Server $server) if (data_get($server, 'privateKey.private_key') === null) { throw new \Exception("Server {$server->name} does not have a private key"); } - ['location' => $location, 'private_key_filename' => $private_key_filename] = server_ssh_configuration($server); + ['location' => $location, 'private_key_filename' => $private_key_filename] = server_ssh_configuration($server); Storage::disk('ssh-keys')->makeDirectory('.'); Storage::disk('ssh-mux')->makeDirectory('.'); Storage::disk('ssh-keys')->put($private_key_filename, $server->privateKey->private_key); + return $location; } @@ -95,15 +98,15 @@ function generateScpCommand(Server $server, string $source, string $dest) $scp_command = "timeout $timeout scp "; $scp_command .= "-i {$privateKeyLocation} " - . '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ' - . '-o PasswordAuthentication=no ' - . "-o ConnectTimeout=$connectionTimeout " - . "-o ServerAliveInterval=$serverInterval " - . '-o RequestTTY=no ' - . '-o LogLevel=ERROR ' - . "-P {$port} " - . "{$source} " - . "{$user}@{$server->ip}:{$dest}"; + .'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ' + .'-o PasswordAuthentication=no ' + ."-o ConnectTimeout=$connectionTimeout " + ."-o ServerAliveInterval=$serverInterval " + .'-o RequestTTY=no ' + .'-o LogLevel=ERROR ' + ."-P {$port} " + ."{$source} " + ."{$user}@{$server->ip}:{$dest}"; return $scp_command; } @@ -115,14 +118,16 @@ function instant_scp(string $source, string $dest, Server $server, $throwError = $output = trim($process->output()); $exitCode = $process->exitCode(); if ($exitCode !== 0) { - if (!$throwError) { + if (! $throwError) { return null; } + return excludeCertainErrors($process->errorOutput(), $exitCode); } if ($output === 'null') { $output = null; } + return $output; } function generateSshCommand(Server $server, string $command) @@ -150,17 +155,18 @@ function generateSshCommand(Server $server, string $command) $delimiter = Hash::make($command); $command = str_replace($delimiter, '', $command); $ssh_command .= "-i {$privateKeyLocation} " - . '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ' - . '-o PasswordAuthentication=no ' - . "-o ConnectTimeout=$connectionTimeout " - . "-o ServerAliveInterval=$serverInterval " - . '-o RequestTTY=no ' - . '-o LogLevel=ERROR ' - . "-p {$port} " - . "{$user}@{$server->ip} " - . " 'bash -se' << \\$delimiter" . PHP_EOL - . $command . PHP_EOL - . $delimiter; + .'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ' + .'-o PasswordAuthentication=no ' + ."-o ConnectTimeout=$connectionTimeout " + ."-o ServerAliveInterval=$serverInterval " + .'-o RequestTTY=no ' + .'-o LogLevel=ERROR ' + ."-p {$port} " + ."{$user}@{$server->ip} " + ." 'bash -se' << \\$delimiter".PHP_EOL + .$command.PHP_EOL + .$delimiter; + // ray($ssh_command); return $ssh_command; } @@ -170,7 +176,7 @@ function instant_remote_process(Collection|array $command, Server $server, bool if ($command instanceof Collection) { $command = $command->toArray(); } - if ($server->isNonRoot() && !$no_sudo) { + if ($server->isNonRoot() && ! $no_sudo) { $command = parseCommandsByLineForSudo(collect($command), $server); } $command_string = implode("\n", $command); @@ -179,14 +185,16 @@ function instant_remote_process(Collection|array $command, Server $server, bool $output = trim($process->output()); $exitCode = $process->exitCode(); if ($exitCode !== 0) { - if (!$throwError) { + if (! $throwError) { return null; } + return excludeCertainErrors($process->errorOutput(), $exitCode); } if ($output === 'null') { $output = null; } + return $output; } function excludeCertainErrors(string $errorOutput, ?int $exitCode = null) @@ -227,20 +235,23 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d } // ray($decoded ); $formatted = collect($decoded); - if (!$is_debug_enabled) { + if (! $is_debug_enabled) { $formatted = $formatted->filter(fn ($i) => $i['hidden'] === false ?? false); } $formatted = $formatted ->sortBy(fn ($i) => data_get($i, 'order')) ->map(function ($i) { data_set($i, 'timestamp', Carbon::parse(data_get($i, 'timestamp'))->format('Y-M-d H:i:s.u')); + return $i; }); + return $formatted; } function remove_iip($text) { - $text = preg_replace('/x-access-token:.*?(?=@)/', "x-access-token:" . REDACTED, $text); + $text = preg_replace('/x-access-token:.*?(?=@)/', 'x-access-token:'.REDACTED, $text); + return preg_replace('/\x1b\[[0-9;]*m/', '', $text); } function remove_mux_and_private_key(Server $server) @@ -262,26 +273,28 @@ function refresh_server_connection(?PrivateKey $private_key = null) function checkRequiredCommands(Server $server) { - $commands = collect(["jq", "jc"]); + $commands = collect(['jq', 'jc']); foreach ($commands as $command) { $commandFound = instant_remote_process(["docker run --rm --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c 'command -v {$command}'"], $server, false); if ($commandFound) { - ray($command . ' found'); + ray($command.' found'); + continue; } try { instant_remote_process(["docker run --rm --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c 'apt update && apt install -y {$command}'"], $server); } catch (\Throwable $e) { - ray('could not install ' . $command); + ray('could not install '.$command); ray($e); break; } $commandFound = instant_remote_process(["docker run --rm --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c 'command -v {$command}'"], $server, false); if ($commandFound) { - ray($command . ' found'); + ray($command.' found'); + continue; } - ray('could not install ' . $command); + ray('could not install '.$command); break; } } diff --git a/bootstrap/helpers/services.php b/bootstrap/helpers/services.php index 5021071d8..0cc4c51e7 100644 --- a/bootstrap/helpers/services.php +++ b/bootstrap/helpers/services.php @@ -34,7 +34,7 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli $fileVolumes = $oneService->fileStorages()->get(); $commands = collect([ "mkdir -p $workdir > /dev/null 2>&1 || true", - "cd $workdir" + "cd $workdir", ]); instant_remote_process($commands, $server); foreach ($fileVolumes as $fileVolume) { @@ -42,7 +42,7 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli $content = data_get($fileVolume, 'content'); if ($path->startsWith('.')) { $path = $path->after('.'); - $fileLocation = $workdir . $path; + $fileLocation = $workdir.$path; } else { $fileLocation = $path; } @@ -57,12 +57,12 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli $fileVolume->content = $filesystemContent; $fileVolume->is_directory = false; $fileVolume->save(); - } else if ($isDir == 'OK') { + } elseif ($isDir == 'OK') { // If its a directory & exists $fileVolume->content = null; $fileVolume->is_directory = true; $fileVolume->save(); - } else if ($isFile == 'NOK' && $isDir == 'NOK' && !$fileVolume->is_directory && $isInit && $content) { + } elseif ($isFile == 'NOK' && $isDir == 'NOK' && ! $fileVolume->is_directory && $isInit && $content) { // Does not exists (no dir or file), not flagged as directory, is init, has content $fileVolume->content = $content; $fileVolume->is_directory = false; @@ -71,9 +71,9 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli $dir = Str::of($fileLocation)->dirname(); instant_remote_process([ "mkdir -p $dir", - "echo '$content' | base64 -d | tee $fileLocation" + "echo '$content' | base64 -d | tee $fileLocation", ], $server); - } else if ($isFile == 'NOK' && $isDir == 'NOK' && $fileVolume->is_directory && $isInit) { + } elseif ($isFile == 'NOK' && $isDir == 'NOK' && $fileVolume->is_directory && $isInit) { $fileVolume->content = null; $fileVolume->is_directory = true; $fileVolume->save(); @@ -106,26 +106,26 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource) $resourceFqdns = str($resource->fqdn)->explode(','); if ($resourceFqdns->count() === 1) { $resourceFqdns = $resourceFqdns->first(); - $variableName = "SERVICE_FQDN_" . Str::of($resource->name)->upper()->replace('-', ''); + $variableName = 'SERVICE_FQDN_'.Str::of($resource->name)->upper()->replace('-', ''); $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); $fqdn = Url::fromString($resourceFqdns); $port = $fqdn->getPort(); $path = $fqdn->getPath(); - $fqdn = $fqdn->getScheme() . '://' . $fqdn->getHost(); + $fqdn = $fqdn->getScheme().'://'.$fqdn->getHost(); if ($generatedEnv) { - $generatedEnv->value = $fqdn . $path; + $generatedEnv->value = $fqdn.$path; $generatedEnv->save(); } if ($port) { - $variableName = $variableName . "_$port"; + $variableName = $variableName."_$port"; $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); // ray($generatedEnv); if ($generatedEnv) { - $generatedEnv->value = $fqdn . $path; + $generatedEnv->value = $fqdn.$path; $generatedEnv->save(); } } - $variableName = "SERVICE_URL_" . Str::of($resource->name)->upper()->replace('-', ''); + $variableName = 'SERVICE_URL_'.Str::of($resource->name)->upper()->replace('-', ''); $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); $url = Url::fromString($fqdn); $port = $url->getPort(); @@ -133,60 +133,60 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource) $url = $url->getHost(); if ($generatedEnv) { $url = Str::of($fqdn)->after('://'); - $generatedEnv->value = $url . $path; + $generatedEnv->value = $url.$path; $generatedEnv->save(); } if ($port) { - $variableName = $variableName . "_$port"; + $variableName = $variableName."_$port"; $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); if ($generatedEnv) { - $generatedEnv->value = $url . $path; + $generatedEnv->value = $url.$path; $generatedEnv->save(); } } - } else if ($resourceFqdns->count() > 1) { + } elseif ($resourceFqdns->count() > 1) { foreach ($resourceFqdns as $fqdn) { $host = Url::fromString($fqdn); $port = $host->getPort(); $url = $host->getHost(); $path = $host->getPath(); - $host = $host->getScheme() . '://' . $host->getHost(); + $host = $host->getScheme().'://'.$host->getHost(); if ($port) { $port_envs = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'like', "SERVICE_FQDN_%_$port")->get(); foreach ($port_envs as $port_env) { $service_fqdn = str($port_env->key)->beforeLast('_')->after('SERVICE_FQDN_'); - $env = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'SERVICE_FQDN_' . $service_fqdn)->first(); + $env = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'SERVICE_FQDN_'.$service_fqdn)->first(); if ($env) { - $env->value = $host . $path; + $env->value = $host.$path; $env->save(); } - $port_env->value = $host . $path; + $port_env->value = $host.$path; $port_env->save(); } $port_envs_url = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'like', "SERVICE_URL_%_$port")->get(); foreach ($port_envs_url as $port_env_url) { $service_url = str($port_env_url->key)->beforeLast('_')->after('SERVICE_URL_'); - $env = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'SERVICE_URL_' . $service_url)->first(); + $env = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'SERVICE_URL_'.$service_url)->first(); if ($env) { - $env->value = $url . $path; + $env->value = $url.$path; $env->save(); } - $port_env_url->value = $url . $path; + $port_env_url->value = $url.$path; $port_env_url->save(); } } else { - $variableName = "SERVICE_FQDN_" . Str::of($resource->name)->upper()->replace('-', ''); + $variableName = 'SERVICE_FQDN_'.Str::of($resource->name)->upper()->replace('-', ''); $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); $fqdn = Url::fromString($fqdn); - $fqdn = $fqdn->getScheme() . '://' . $fqdn->getHost() . $fqdn->getPath(); + $fqdn = $fqdn->getScheme().'://'.$fqdn->getHost().$fqdn->getPath(); if ($generatedEnv) { $generatedEnv->value = $fqdn; $generatedEnv->save(); } - $variableName = "SERVICE_URL_" . Str::of($resource->name)->upper()->replace('-', ''); + $variableName = 'SERVICE_URL_'.Str::of($resource->name)->upper()->replace('-', ''); $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); $url = Url::fromString($fqdn); - $url = $url->getHost() . $url->getPath(); + $url = $url->getHost().$url->getPath(); if ($generatedEnv) { $url = Str::of($fqdn)->after('://'); $generatedEnv->value = $url; diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 3fcc335a0..9a09adee1 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1,5 +1,7 @@ user()?->isMember()) { return false; } + return currentTeam()->show_boarding ?? false; } function refreshSession(?Team $team = null): void { - if (!$team) { + if (! $team) { if (auth()->user()?->currentTeam()) { $team = Team::find(auth()->user()->currentTeam()->id); } else { $team = User::find(auth()->user()->id)->teams->first(); } } - Cache::forget('team:' . auth()->user()->id); - Cache::remember('team:' . auth()->user()->id, 3600, function () use ($team) { + Cache::forget('team:'.auth()->user()->id); + Cache::remember('team:'.auth()->user()->id, 3600, function () use ($team) { return $team; }); session(['currentTeam' => $team]); @@ -122,13 +125,15 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n if (isset($livewire)) { return $livewire->dispatch('error', "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds."); } + return "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds."; } if ($error instanceof UniqueConstraintViolationException) { if (isset($livewire)) { return $livewire->dispatch('error', 'Duplicate entry found. Please use a different name.'); } - return "Duplicate entry found. Please use a different name."; + + return 'Duplicate entry found. Please use a different name.'; } if ($error instanceof Throwable) { @@ -137,7 +142,7 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n $message = null; } if ($customErrorMessage) { - $message = $customErrorMessage . ' ' . $message; + $message = $customErrorMessage.' '.$message; } if (isset($livewire)) { @@ -155,10 +160,12 @@ function get_latest_sentinel_version(): string try { $response = Http::get('https://cdn.coollabs.io/coolify/versions.json'); $versions = $response->json(); + return data_get($versions, 'coolify.sentinel.version'); } catch (\Throwable $e) { //throw $e; ray($e->getMessage()); + return '0.0.0'; } } @@ -167,6 +174,7 @@ function get_latest_version_of_coolify(): string try { $versions = File::get(base_path('versions.json')); $versions = json_decode($versions, true); + return data_get($versions, 'coolify.v4.version'); // $response = Http::get('https://cdn.coollabs.io/coolify/versions.json'); // $versions = $response->json(); @@ -174,6 +182,7 @@ function get_latest_version_of_coolify(): string } catch (\Throwable $e) { //throw $e; ray($e->getMessage()); + return '0.0.0'; } } @@ -188,21 +197,24 @@ function generate_random_name(?string $cuid = null): string if (is_null($cuid)) { $cuid = new Cuid2(7); } + return Str::kebab("{$generator->getName()}-$cuid"); } function generateSSHKey(string $type = 'rsa') { if ($type === 'rsa') { $key = RSA::createKey(); + return [ 'private' => $key->toString('PKCS1'), - 'public' => $key->getPublicKey()->toString('OpenSSH', ['comment' => 'coolify-generated-ssh-key']) + 'public' => $key->getPublicKey()->toString('OpenSSH', ['comment' => 'coolify-generated-ssh-key']), ]; - } else if ($type === 'ed25519') { + } elseif ($type === 'ed25519') { $key = EC::createKey('Ed25519'); + return [ 'private' => $key->toString('OpenSSH'), - 'public' => $key->getPublicKey()->toString('OpenSSH', ['comment' => 'coolify-generated-ssh-key']) + 'public' => $key->getPublicKey()->toString('OpenSSH', ['comment' => 'coolify-generated-ssh-key']), ]; } throw new Exception('Invalid key type'); @@ -210,9 +222,10 @@ function generateSSHKey(string $type = 'rsa') function formatPrivateKey(string $privateKey) { $privateKey = trim($privateKey); - if (!str_ends_with($privateKey, "\n")) { + if (! str_ends_with($privateKey, "\n")) { $privateKey .= "\n"; } + return $privateKey; } function generate_application_name(string $git_repository, string $git_branch, ?string $cuid = null): string @@ -220,6 +233,7 @@ function generate_application_name(string $git_repository, string $git_branch, ? if (is_null($cuid)) { $cuid = new Cuid2(7); } + return Str::kebab("$git_repository:$git_branch-$cuid"); } @@ -228,9 +242,9 @@ function is_transactional_emails_active(): bool return isEmailEnabled(InstanceSettings::get()); } -function set_transanctional_email_settings(InstanceSettings | null $settings = null): string|null +function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string { - if (!$settings) { + if (! $settings) { $settings = InstanceSettings::get(); } config()->set('mail.from.address', data_get($settings, 'smtp_from_address')); @@ -238,29 +252,32 @@ function set_transanctional_email_settings(InstanceSettings | null $settings = n if (data_get($settings, 'resend_enabled')) { config()->set('mail.default', 'resend'); config()->set('resend.api_key', data_get($settings, 'resend_api_key')); + return 'resend'; } if (data_get($settings, 'smtp_enabled')) { config()->set('mail.default', 'smtp'); config()->set('mail.mailers.smtp', [ - "transport" => "smtp", - "host" => data_get($settings, 'smtp_host'), - "port" => data_get($settings, 'smtp_port'), - "encryption" => data_get($settings, 'smtp_encryption'), - "username" => data_get($settings, 'smtp_username'), - "password" => data_get($settings, 'smtp_password'), - "timeout" => data_get($settings, 'smtp_timeout'), - "local_domain" => null, + 'transport' => 'smtp', + 'host' => data_get($settings, 'smtp_host'), + 'port' => data_get($settings, 'smtp_port'), + 'encryption' => data_get($settings, 'smtp_encryption'), + 'username' => data_get($settings, 'smtp_username'), + 'password' => data_get($settings, 'smtp_password'), + 'timeout' => data_get($settings, 'smtp_timeout'), + 'local_domain' => null, ]); + return 'smtp'; } + return null; } function base_ip(): string { if (isDev()) { - return "localhost"; + return 'localhost'; } $settings = InstanceSettings::get(); if ($settings->public_ipv4) { @@ -269,15 +286,17 @@ function base_ip(): string if ($settings->public_ipv6) { return "$settings->public_ipv6"; } - return "localhost"; + + return 'localhost'; } -function getFqdnWithoutPort(String $fqdn) +function getFqdnWithoutPort(string $fqdn) { try { $url = Url::fromString($fqdn); $host = $url->getHost(); $scheme = $url->getScheme(); $path = $url->getPath(); + return "$scheme://$host$path"; } catch (\Throwable $e) { return $fqdn; @@ -298,19 +317,23 @@ function base_url(bool $withPort = true): string if (isDev()) { return "http://localhost:$port"; } + return "http://$settings->public_ipv4:$port"; } if (isDev()) { - return "http://localhost"; + return 'http://localhost'; } + return "http://$settings->public_ipv4"; } if ($settings->public_ipv6) { if ($withPort) { return "http://$settings->public_ipv6:$port"; } + return "http://$settings->public_ipv6"; } + return url('/'); } @@ -325,7 +348,7 @@ function isDev(): bool function isCloud(): bool { - return !config('coolify.self_hosted'); + return ! config('coolify.self_hosted'); } function validate_cron_expression($expression_to_validate): bool @@ -337,6 +360,7 @@ function validate_cron_expression($expression_to_validate): bool if (isset(VALID_CRON_STRINGS[$expression_to_validate])) { $isValid = true; } + return $isValid; } function send_internal_notification(string $message): void @@ -352,7 +376,7 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null { $settings = InstanceSettings::get(); $type = set_transanctional_email_settings($settings); - if (!$type) { + if (! $type) { throw new Exception('No email settings found.'); } if ($cc) { @@ -381,9 +405,10 @@ function isTestEmailEnabled($notifiable) { if (data_get($notifiable, 'use_instance_email_settings') && isInstanceAdmin()) { return true; - } else if (data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') && auth()->user()->isAdminFromSession()) { + } elseif (data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') && auth()->user()->isAdminFromSession()) { return true; } + return false; } function isEmailEnabled($notifiable) @@ -409,11 +434,12 @@ function setNotificationChannels($notifiable, $event) if ($isTelegramEnabled && $isSubscribedToTelegramEvent) { $channels[] = TelegramChannel::class; } + return $channels; } function parseEnvFormatToArray($env_file_contents) { - $env_array = array(); + $env_array = []; $lines = explode("\n", $env_file_contents); foreach ($lines as $line) { if ($line === '' || substr($line, 0, 1) === '#') { @@ -431,12 +457,14 @@ function parseEnvFormatToArray($env_file_contents) $env_array[$key] = $value; } } + return $env_array; } function data_get_str($data, $key, $default = null): Stringable { $str = data_get($data, $key, $default) ?? $default; + return Str::of($str); } @@ -451,17 +479,20 @@ function generateFqdn(Server $server, string $random) $path = $url->getPath() === '/' ? '' : $url->getPath(); $scheme = $url->getScheme(); $finalFqdn = "$scheme://{$random}.$host$path"; + return $finalFqdn; } function sslip(Server $server) { if (isDev() && $server->id === 0) { - return "http://127.0.0.1.sslip.io"; + return 'http://127.0.0.1.sslip.io'; } if ($server->ip === 'host.docker.internal') { $baseIp = base_ip(); + return "http://$baseIp.sslip.io"; } + return "http://{$server->ip}.sslip.io"; } @@ -474,13 +505,16 @@ function get_service_templates(bool $force = false): Collection return collect([]); } $services = $response->json(); + return collect($services); } catch (\Throwable $e) { $services = File::get(base_path('templates/service-templates.json')); + return collect(json_decode($services))->sortKeys(); } } else { $services = File::get(base_path('templates/service-templates.json')); + return collect(json_decode($services))->sortKeys(); } } @@ -491,63 +525,89 @@ function getResourceByUuid(string $uuid, ?int $teamId = null) return null; } $resource = queryResourcesByUuid($uuid); - if (!is_null($resource) && $resource->environment->project->team_id === $teamId) { + if (! is_null($resource) && $resource->environment->project->team_id === $teamId) { return $resource; } + return null; } function queryResourcesByUuid(string $uuid) { $resource = null; $application = Application::whereUuid($uuid)->first(); - if ($application) return $application; + if ($application) { + return $application; + } $service = Service::whereUuid($uuid)->first(); - if ($service) return $service; + if ($service) { + return $service; + } $postgresql = StandalonePostgresql::whereUuid($uuid)->first(); - if ($postgresql) return $postgresql; + if ($postgresql) { + return $postgresql; + } $redis = StandaloneRedis::whereUuid($uuid)->first(); - if ($redis) return $redis; + if ($redis) { + return $redis; + } $mongodb = StandaloneMongodb::whereUuid($uuid)->first(); - if ($mongodb) return $mongodb; + if ($mongodb) { + return $mongodb; + } $mysql = StandaloneMysql::whereUuid($uuid)->first(); - if ($mysql) return $mysql; + if ($mysql) { + return $mysql; + } $mariadb = StandaloneMariadb::whereUuid($uuid)->first(); - if ($mariadb) return $mariadb; + if ($mariadb) { + return $mariadb; + } $keydb = StandaloneKeydb::whereUuid($uuid)->first(); - if ($keydb) return $keydb; + if ($keydb) { + return $keydb; + } $dragonfly = StandaloneDragonfly::whereUuid($uuid)->first(); - if ($dragonfly) return $dragonfly; + if ($dragonfly) { + return $dragonfly; + } $clickhouse = StandaloneClickhouse::whereUuid($uuid)->first(); - if ($clickhouse) return $clickhouse; + if ($clickhouse) { + return $clickhouse; + } + return $resource; } function generatTagDeployWebhook($tag_name) { $baseUrl = base_url(); - $api = Url::fromString($baseUrl) . '/api/v1'; + $api = Url::fromString($baseUrl).'/api/v1'; $endpoint = "/deploy?tag=$tag_name"; - $url = $api . $endpoint; + $url = $api.$endpoint; + return $url; } function generateDeployWebhook($resource) { $baseUrl = base_url(); - $api = Url::fromString($baseUrl) . '/api/v1'; + $api = Url::fromString($baseUrl).'/api/v1'; $endpoint = '/deploy'; $uuid = data_get($resource, 'uuid'); - $url = $api . $endpoint . "?uuid=$uuid&force=false"; + $url = $api.$endpoint."?uuid=$uuid&force=false"; + return $url; } function generateGitManualWebhook($resource, $type) { - if ($resource->source_id !== 0 && !is_null($resource->source_id)) { + if ($resource->source_id !== 0 && ! is_null($resource->source_id)) { return null; } if ($resource->getMorphClass() === 'App\Models\Application') { $baseUrl = base_url(); - $api = Url::fromString($baseUrl) . "/webhooks/source/$type/events/manual"; + $api = Url::fromString($baseUrl)."/webhooks/source/$type/events/manual"; + return $api; } + return null; } function removeAnsiColors($text) @@ -572,7 +632,7 @@ function getTopLevelNetworks(Service|Application $resource) $hasHostNetworkMode = data_get($service, 'network_mode') === 'host' ? true : false; // Only add 'networks' key if 'network_mode' is not 'host' - if (!$hasHostNetworkMode) { + if (! $hasHostNetworkMode) { // Collect/create/update networks if ($serviceNetworks->count() > 0) { foreach ($serviceNetworks as $networkName => $networkDetails) { @@ -586,7 +646,7 @@ function getTopLevelNetworks(Service|Application $resource) $networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) { return $value == $networkName || $key == $networkName; }); - if (!$networkExists) { + if (! $networkExists) { $topLevelNetworks->put($networkDetails, null); } } @@ -595,11 +655,11 @@ function getTopLevelNetworks(Service|Application $resource) $definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) { return $value == $definedNetwork; }); - if (!$definedNetworkExists) { + if (! $definedNetworkExists) { foreach ($definedNetwork as $network) { - $topLevelNetworks->put($network, [ + $topLevelNetworks->put($network, [ 'name' => $network, - 'external' => true + 'external' => true, ]); } } @@ -607,9 +667,10 @@ function getTopLevelNetworks(Service|Application $resource) return $service; }); + return $topLevelNetworks->keys(); } - } else if ($resource->getMorphClass() === 'App\Models\Application') { + } elseif ($resource->getMorphClass() === 'App\Models\Application') { try { $yaml = Yaml::parse($resource->docker_compose_raw); } catch (\Exception $e) { @@ -635,7 +696,7 @@ function getTopLevelNetworks(Service|Application $resource) $networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) { return $value == $networkName || $key == $networkName; }); - if (!$networkExists) { + if (! $networkExists) { $topLevelNetworks->put($networkDetails, null); } } @@ -643,16 +704,18 @@ function getTopLevelNetworks(Service|Application $resource) $definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) { return $value == $definedNetwork; }); - if (!$definedNetworkExists) { + if (! $definedNetworkExists) { foreach ($definedNetwork as $network) { - $topLevelNetworks->put($network, [ + $topLevelNetworks->put($network, [ 'name' => $network, - 'external' => true + 'external' => true, ]); } } + return $service; }); + return $topLevelNetworks->keys(); } } @@ -693,7 +756,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS, $resource, $allServices) { // Workarounds for beta users. if ($serviceName === 'registry') { - $tempServiceName = "docker-registry"; + $tempServiceName = 'docker-registry'; } else { $tempServiceName = $serviceName; } @@ -718,10 +781,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if ($serviceLabels->count() > 0) { $removedLabels = collect([]); $serviceLabels = $serviceLabels->filter(function ($serviceLabel, $serviceLabelName) use ($removedLabels) { - if (!str($serviceLabel)->contains('=')) { + if (! str($serviceLabel)->contains('=')) { $removedLabels->put($serviceLabelName, $serviceLabel); + return false; } + return $serviceLabel; }); foreach ($removedLabels as $removedLabelName => $removedLabel) { @@ -742,12 +807,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $savedService = ServiceDatabase::create([ 'name' => $serviceName, 'image' => $image, - 'service_id' => $resource->id + 'service_id' => $resource->id, ]); } else { $savedService = ServiceDatabase::where([ 'name' => $serviceName, - 'service_id' => $resource->id + 'service_id' => $resource->id, ])->first(); } } else { @@ -755,12 +820,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $savedService = ServiceApplication::create([ 'name' => $serviceName, 'image' => $image, - 'service_id' => $resource->id + 'service_id' => $resource->id, ]); } else { $savedService = ServiceApplication::where([ 'name' => $serviceName, - 'service_id' => $resource->id + 'service_id' => $resource->id, ])->first(); } } @@ -769,13 +834,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $savedService = ServiceDatabase::create([ 'name' => $serviceName, 'image' => $image, - 'service_id' => $resource->id + 'service_id' => $resource->id, ]); } else { $savedService = ServiceApplication::create([ 'name' => $serviceName, 'image' => $image, - 'service_id' => $resource->id + 'service_id' => $resource->id, ]); } } @@ -798,7 +863,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) { return $value == $networkName || $key == $networkName; }); - if (!$networkExists) { + if (! $networkExists) { $topLevelNetworks->put($networkDetails, null); } } @@ -822,16 +887,16 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $savedService->ports = $collectedPorts->implode(','); $savedService->save(); - if (!$hasHostNetworkMode) { + if (! $hasHostNetworkMode) { // Add Coolify specific networks $definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) { return $value == $definedNetwork; }); - if (!$definedNetworkExists) { + if (! $definedNetworkExists) { foreach ($definedNetwork as $network) { - $topLevelNetworks->put($network, [ + $topLevelNetworks->put($network, [ 'name' => $network, - 'external' => true + 'external' => true, ]); } } @@ -841,7 +906,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal // networks: // - appwrite $networks->put($serviceNetwork, null); - } else if (gettype($serviceNetwork) === 'array') { + } elseif (gettype($serviceNetwork) === 'array') { // networks: // default: // ipv4_address: 192.168.203.254 @@ -871,7 +936,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } else { $type = Str::of('volume'); } - } else if (is_array($volume)) { + } elseif (is_array($volume)) { $type = data_get_str($volume, 'type'); $source = data_get_str($volume, 'source'); $target = data_get_str($volume, 'target'); @@ -887,7 +952,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } } if ($type?->value() === 'bind') { - if ($source->value() === "/var/run/docker.sock") { + if ($source->value() === '/var/run/docker.sock') { return $volume; } if ($source->value() === '/tmp' || $source->value() === '/tmp/') { @@ -897,7 +962,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal [ 'mount_path' => $target, 'resource_id' => $savedService->id, - 'resource_type' => get_class($savedService) + 'resource_type' => get_class($savedService), ], [ 'fs_path' => $source, @@ -905,10 +970,10 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'content' => $content, 'is_directory' => $isDirectory, 'resource_id' => $savedService->id, - 'resource_type' => get_class($savedService) + 'resource_type' => get_class($savedService), ] ); - } else if ($type->value() === 'volume') { + } elseif ($type->value() === 'volume') { if ($topLevelVolumes->has($source->value())) { $v = $topLevelVolumes->get($source->value()); if (data_get($v, 'driver_opts.type') === 'cifs') { @@ -922,7 +987,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $target = Str::of($volume)->after(':')->beforeLast(':'); $source = $name; $volume = "$source:$target"; - } else if (is_array($volume)) { + } elseif (is_array($volume)) { data_set($volume, 'source', $name); } $topLevelVolumes->put($name, [ @@ -932,17 +997,18 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal [ 'mount_path' => $target, 'resource_id' => $savedService->id, - 'resource_type' => get_class($savedService) + 'resource_type' => get_class($savedService), ], [ 'name' => $name, 'mount_path' => $target, 'resource_id' => $savedService->id, - 'resource_type' => get_class($savedService) + 'resource_type' => get_class($savedService), ] ); } dispatch(new ServerFilesFromServerJob($savedService)); + return $volume; }); data_set($service, 'volumes', $serviceVolumes->toArray()); @@ -959,7 +1025,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal // } // data_set($service, 'env_file', $envFile->toArray()); - // Get variables from the service foreach ($serviceVariables as $variableName => $variable) { if (is_numeric($variableName)) { @@ -1019,9 +1084,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $fqdn = "$fqdn$path"; } - if (!$isDatabase) { + if (! $isDatabase) { if ($savedService->fqdn) { - data_set($savedService, 'fqdn', $savedService->fqdn . ',' . $fqdn); + data_set($savedService, 'fqdn', $savedService->fqdn.','.$fqdn); } else { data_set($savedService, 'fqdn', $fqdn); } @@ -1036,7 +1101,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal ]); } // Caddy needs exact port in some cases. - if ($predefinedPort && !$key->endsWith("_{$predefinedPort}")) { + if ($predefinedPort && ! $key->endsWith("_{$predefinedPort}")) { $fqdns_exploded = str($savedService->fqdn)->explode(','); if ($fqdns_exploded->count() > 1) { continue; @@ -1055,6 +1120,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } } } + // data_forget($service, "environment.$variableName"); // $yaml = data_forget($yaml, "services.$serviceName.environment.$variableName"); // if (count(data_get($yaml, 'services.' . $serviceName . '.environment')) === 0) { @@ -1075,12 +1141,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'service_id' => $resource->id, ])->first(); ['command' => $command, 'forService' => $forService, 'generatedValue' => $generatedValue, 'port' => $port] = parseEnvVariable($value); - if (!is_null($command)) { + if (! is_null($command)) { if ($command?->value() === 'FQDN' || $command?->value() === 'URL') { if (Str::lower($forService) === $serviceName) { $fqdn = generateFqdn($resource->server, $containerName); } else { - $fqdn = generateFqdn($resource->server, Str::lower($forService) . '-' . $resource->uuid); + $fqdn = generateFqdn($resource->server, Str::lower($forService).'-'.$resource->uuid); } if ($port) { $fqdn = "$fqdn:$port"; @@ -1110,13 +1176,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'is_preview' => false, ]); } - if (!$isDatabase) { - if ($command->value() === 'FQDN' && is_null($savedService->fqdn) && !$foundEnv) { + if (! $isDatabase) { + if ($command->value() === 'FQDN' && is_null($savedService->fqdn) && ! $foundEnv) { $savedService->fqdn = $fqdn; $savedService->save(); } // Caddy needs exact port in some cases. - if ($predefinedPort && !$key->endsWith("_{$predefinedPort}") && $command?->value() === 'FQDN' && $resource->server->proxyType() === 'CADDY') { + if ($predefinedPort && ! $key->endsWith("_{$predefinedPort}") && $command?->value() === 'FQDN' && $resource->server->proxyType() === 'CADDY') { $fqdns_exploded = str($savedService->fqdn)->explode(','); if ($fqdns_exploded->count() > 1) { continue; @@ -1138,7 +1204,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } } else { $generatedValue = generateEnvValue($command, $resource); - if (!$foundEnv) { + if (! $foundEnv) { EnvironmentVariable::create([ 'key' => $key, 'value' => $generatedValue, @@ -1153,13 +1219,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if ($value->contains(':-')) { $key = $value->before(':'); $defaultValue = $value->after(':-'); - } else if ($value->contains('-')) { + } elseif ($value->contains('-')) { $key = $value->before('-'); $defaultValue = $value->after('-'); - } else if ($value->contains(':?')) { + } elseif ($value->contains(':?')) { $key = $value->before(':'); $defaultValue = $value->after(':?'); - } else if ($value->contains('?')) { + } elseif ($value->contains('?')) { $key = $value->before('?'); $defaultValue = $value->after('?'); } else { @@ -1193,7 +1259,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } $defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id); $serviceLabels = $serviceLabels->merge($defaultLabels); - if (!$isDatabase && $fqdns->count() > 0) { + if (! $isDatabase && $fqdns->count() > 0) { if ($fqdns) { $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik( uuid: $resource->uuid, @@ -1222,10 +1288,10 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal data_set($service, 'logging', [ 'driver' => 'fluentd', 'options' => [ - 'fluentd-address' => "tcp://127.0.0.1:24224", - 'fluentd-async' => "true", - 'fluentd-sub-second-precision' => "true", - ] + 'fluentd-address' => 'tcp://127.0.0.1:24224', + 'fluentd-async' => 'true', + 'fluentd-sub-second-precision' => 'true', + ], ]); } if ($serviceLabels->count() > 0) { @@ -1237,7 +1303,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } data_set($service, 'labels', $serviceLabels->toArray()); data_forget($service, 'is_database'); - if (!data_get($service, 'restart')) { + if (! data_get($service, 'restart')) { data_set($service, 'restart', RESTART_MODE); } if (data_get($service, 'restart') === 'no' || data_get($service, 'exclude_from_hc')) { @@ -1262,6 +1328,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal // ray($withoutServiceEnvs); // data_set($service, 'environment', $withoutServiceEnvs->toArray()); updateCompose($savedService); + return $service; }); $finalServices = [ @@ -1274,11 +1341,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $resource->docker_compose = Yaml::dump($finalServices, 10, 2); $resource->save(); $resource->saveComposeConfigs(); + return collect($finalServices); } else { return collect([]); } - } else if ($resource->getMorphClass() === 'App\Models\Application') { + } elseif ($resource->getMorphClass() === 'App\Models\Application') { $isSameDockerComposeFile = false; if ($resource->dockerComposePrLocation() === $resource->dockerComposeLocation()) { $isSameDockerComposeFile = true; @@ -1332,10 +1400,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if ($serviceLabels->count() > 0) { $removedLabels = collect([]); $serviceLabels = $serviceLabels->filter(function ($serviceLabel, $serviceLabelName) use ($removedLabels) { - if (!str($serviceLabel)->contains('=')) { + if (! str($serviceLabel)->contains('=')) { $removedLabels->put($serviceLabelName, $serviceLabel); + return false; } + return $serviceLabel; }); foreach ($removedLabels as $removedLabelName => $removedLabel) { @@ -1349,11 +1419,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $serviceVolumes = $serviceVolumes->map(function ($volume) use ($resource, $topLevelVolumes, $pull_request_id) { if (is_string($volume)) { $volume = str($volume); - if ($volume->contains(':') && !$volume->startsWith('/')) { + if ($volume->contains(':') && ! $volume->startsWith('/')) { $name = $volume->before(':'); $mount = $volume->after(':'); if ($name->startsWith('.') || $name->startsWith('~')) { - $dir = base_configuration_dir() . '/applications/' . $resource->uuid; + $dir = base_configuration_dir().'/applications/'.$resource->uuid; if ($name->startsWith('.')) { $name = $name->replaceFirst('.', $dir); } @@ -1361,12 +1431,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $name->replaceFirst('~', $dir); } if ($pull_request_id !== 0) { - $name = $name . "-pr-$pull_request_id"; + $name = $name."-pr-$pull_request_id"; } $volume = str("$name:$mount"); } else { if ($pull_request_id !== 0) { - $name = $name . "-pr-$pull_request_id"; + $name = $name."-pr-$pull_request_id"; $volume = str("$name:$mount"); if ($topLevelVolumes->has($name)) { $v = $topLevelVolumes->get($name); @@ -1405,18 +1475,18 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $name = $volume->before(':'); $mount = $volume->after(':'); if ($pull_request_id !== 0) { - $name = $name . "-pr-$pull_request_id"; + $name = $name."-pr-$pull_request_id"; } $volume = str("$name:$mount"); } } - } else if (is_array($volume)) { + } elseif (is_array($volume)) { $source = data_get($volume, 'source'); $target = data_get($volume, 'target'); $read_only = data_get($volume, 'read_only'); if ($source && $target) { if ((str($source)->startsWith('.') || str($source)->startsWith('~'))) { - $dir = base_configuration_dir() . '/applications/' . $resource->uuid; + $dir = base_configuration_dir().'/applications/'.$resource->uuid; if (str($source, '.')) { $source = str($source)->replaceFirst('.', $dir); } @@ -1424,23 +1494,23 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $source = str($source)->replaceFirst('~', $dir); } if ($pull_request_id !== 0) { - $source = $source . "-pr-$pull_request_id"; + $source = $source."-pr-$pull_request_id"; } if ($read_only) { - data_set($volume, 'source', $source . ':' . $target . ':ro'); + data_set($volume, 'source', $source.':'.$target.':ro'); } else { - data_set($volume, 'source', $source . ':' . $target); + data_set($volume, 'source', $source.':'.$target); } } else { if ($pull_request_id !== 0) { - $source = $source . "-pr-$pull_request_id"; + $source = $source."-pr-$pull_request_id"; } if ($read_only) { - data_set($volume, 'source', $source . ':' . $target . ':ro'); + data_set($volume, 'source', $source.':'.$target.':ro'); } else { - data_set($volume, 'source', $source . ':' . $target); + data_set($volume, 'source', $source.':'.$target); } - if (!str($source)->startsWith('/')) { + if (! str($source)->startsWith('/')) { if ($topLevelVolumes->has($source)) { $v = $topLevelVolumes->get($source); if (data_get($v, 'driver_opts.type') === 'cifs') { @@ -1463,6 +1533,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if (is_array($volume)) { return data_get($volume, 'source'); } + return $volume->value(); }); data_set($service, 'volumes', $serviceVolumes->toArray()); @@ -1470,7 +1541,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if ($pull_request_id !== 0 && count($serviceDependencies) > 0) { $serviceDependencies = $serviceDependencies->map(function ($dependency) use ($pull_request_id) { - return $dependency . "-pr-$pull_request_id"; + return $dependency."-pr-$pull_request_id"; }); data_set($service, 'depends_on', $serviceDependencies->toArray()); } @@ -1492,7 +1563,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) { return $value == $networkName || $key == $networkName; }); - if (!$networkExists) { + if (! $networkExists) { $topLevelNetworks->put($networkDetails, null); } } @@ -1518,17 +1589,17 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) { return $value == $definedNetwork; }); - if (!$definedNetworkExists) { + if (! $definedNetworkExists) { foreach ($definedNetwork as $network) { if ($pull_request_id !== 0) { - $topLevelNetworks->put($network, [ + $topLevelNetworks->put($network, [ 'name' => $network, - 'external' => true + 'external' => true, ]); } else { - $topLevelNetworks->put($network, [ + $topLevelNetworks->put($network, [ 'name' => $network, - 'external' => true + 'external' => true, ]); } } @@ -1539,7 +1610,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal // networks: // - appwrite $networks->put($serviceNetwork, null); - } else if (gettype($serviceNetwork) === 'array') { + } elseif (gettype($serviceNetwork) === 'array') { // networks: // default: // ipv4_address: 192.168.203.254 @@ -1553,9 +1624,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if (data_get($resource, 'settings.connect_to_docker_network')) { $network = $resource->destination->network; $networks->put($network, null); - $topLevelNetworks->put($network, [ + $topLevelNetworks->put($network, [ 'name' => $network, - 'external' => true + 'external' => true, ]); } data_set($service, 'networks', $networks->toArray()); @@ -1612,6 +1683,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $fqdn = "$fqdn$path"; } } + continue; } if ($value?->startsWith('$')) { @@ -1628,12 +1700,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'application_id' => $resource->id, ])->first(); ['command' => $command, 'forService' => $forService, 'generatedValue' => $generatedValue, 'port' => $port] = parseEnvVariable($value); - if (!is_null($command)) { + if (! is_null($command)) { if ($command?->value() === 'FQDN' || $command?->value() === 'URL') { if (Str::lower($forService) === $serviceName) { $fqdn = generateFqdn($server, $containerName); } else { - $fqdn = generateFqdn($server, Str::lower($forService) . '-' . $resource->uuid); + $fqdn = generateFqdn($server, Str::lower($forService).'-'.$resource->uuid); } if ($port) { $fqdn = "$fqdn:$port"; @@ -1654,7 +1726,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } } else { $generatedValue = generateEnvValue($command); - if (!$foundEnv) { + if (! $foundEnv) { EnvironmentVariable::create([ 'key' => $key, 'value' => $generatedValue, @@ -1669,13 +1741,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if ($value->contains(':-')) { $key = $value->before(':'); $defaultValue = $value->after(':-'); - } else if ($value->contains('-')) { + } elseif ($value->contains('-')) { $key = $value->before('-'); $defaultValue = $value->after('-'); - } else if ($value->contains(':?')) { + } elseif ($value->contains(':?')) { $key = $value->before(':'); $defaultValue = $value->after(':?'); - } else if ($value->contains('?')) { + } elseif ($value->contains('?')) { $key = $value->before('?'); $defaultValue = $value->after('?'); } else { @@ -1743,6 +1815,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $preview_fqdn = "$schema://$preview_fqdn"; $preview->fqdn = $preview_fqdn; $preview->save(); + return $preview_fqdn; }); } @@ -1771,10 +1844,10 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal data_set($service, 'logging', [ 'driver' => 'fluentd', 'options' => [ - 'fluentd-address' => "tcp://127.0.0.1:24224", - 'fluentd-async' => "true", - 'fluentd-sub-second-precision' => "true", - ] + 'fluentd-address' => 'tcp://127.0.0.1:24224', + 'fluentd-async' => 'true', + 'fluentd-sub-second-precision' => 'true', + ], ]); } if ($serviceLabels->count() > 0) { @@ -1786,7 +1859,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } data_set($service, 'labels', $serviceLabels->toArray()); data_forget($service, 'is_database'); - if (!data_get($service, 'restart')) { + if (! data_get($service, 'restart')) { data_set($service, 'restart', RESTART_MODE); } data_set($service, 'container_name', $containerName); @@ -1797,7 +1870,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal }); if ($pull_request_id !== 0) { $services->each(function ($service, $serviceName) use ($pull_request_id, $services) { - $services[$serviceName . "-pr-$pull_request_id"] = $service; + $services[$serviceName."-pr-$pull_request_id"] = $service; data_forget($services, $serviceName); }); } @@ -1816,6 +1889,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $resource->docker_compose = Yaml::dump($finalServices, 10, 2); } $resource->save(); + return collect($finalServices); } } @@ -1854,6 +1928,7 @@ function parseEnvVariable(Str|string $value) } } } + return [ 'command' => $command, 'forService' => $forService, @@ -1939,6 +2014,7 @@ function generateEnvValue(string $command, ?Service $service = null) $generatedValue = Str::random(16); break; } + return $generatedValue; } @@ -1960,7 +2036,7 @@ function getRealtime() function validate_dns_entry(string $fqdn, Server $server) { - # https://www.cloudflare.com/ips-v4/# + // https://www.cloudflare.com/ips-v4/# $cloudflare_ips = collect(['173.245.48.0/20', '103.21.244.0/22', '103.22.200.0/22', '103.31.4.0/22', '141.101.64.0/18', '108.162.192.0/18', '190.93.240.0/20', '188.114.96.0/20', '197.234.240.0/22', '198.41.128.0/17', '162.158.0.0/15', '104.16.0.0/13', '172.64.0.0/13', '131.0.72.0/22']); $url = Url::fromString($fqdn); @@ -1970,7 +2046,7 @@ function validate_dns_entry(string $fqdn, Server $server) } $settings = InstanceSettings::get(); $is_dns_validation_enabled = data_get($settings, 'is_dns_validation_enabled'); - if (!$is_dns_validation_enabled) { + if (! $is_dns_validation_enabled) { return true; } $dns_servers = data_get($settings, 'custom_dns_servers'); @@ -1988,7 +2064,7 @@ function validate_dns_entry(string $fqdn, Server $server) $query = new DNSQuery($dns_server); $results = $query->query($host, $type); if ($results === false || $query->hasError()) { - ray("Error: " . $query->getLasterror()); + ray('Error: '.$query->getLasterror()); } else { foreach ($results as $result) { if ($result->getType() == $type) { @@ -1998,7 +2074,7 @@ function validate_dns_entry(string $fqdn, Server $server) break; } if ($result->getData() === $ip) { - ray($host . " has IP address " . $result->getData()); + ray($host.' has IP address '.$result->getData()); ray($result->getString()); $found_matching_ip = true; break; @@ -2010,39 +2086,43 @@ function validate_dns_entry(string $fqdn, Server $server) } } ray("Found match: $found_matching_ip"); + return $found_matching_ip; } function ip_match($ip, $cidrs, &$match = null) { foreach ((array) $cidrs as $cidr) { - list($subnet, $mask) = explode('/', $cidr); + [$subnet, $mask] = explode('/', $cidr); if (((ip2long($ip) & ($mask = ~((1 << (32 - $mask)) - 1))) == (ip2long($subnet) & $mask))) { $match = $cidr; + return true; } } + return false; } function check_domain_usage(ServiceApplication|Application|null $resource = null, ?string $domain = null) { if ($resource) { if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose') { - $domains = data_get(json_decode($resource->docker_compose_domains, true), "*.domain"); + $domains = data_get(json_decode($resource->docker_compose_domains, true), '*.domain'); ray($domains); $domains = collect($domains); } else { $domains = collect($resource->fqdns); } - } else if ($domain) { + } elseif ($domain) { $domains = collect($domain); } else { - throw new \RuntimeException("No resource or FQDN provided."); + throw new \RuntimeException('No resource or FQDN provided.'); } $domains = $domains->map(function ($domain) { if (str($domain)->endsWith('/')) { $domain = str($domain)->beforeLast('/'); } + return str($domain); }); $apps = Application::all(); @@ -2058,7 +2138,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null if ($resource->uuid !== $app->uuid) { throw new \RuntimeException("Domain $naked_domain is already in use by another resource called:

{$app->name}."); } - } else if ($domain) { + } elseif ($domain) { throw new \RuntimeException("Domain $naked_domain is already in use by another resource called:

{$app->name}."); } } @@ -2077,7 +2157,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null if ($resource->uuid !== $app->uuid) { throw new \RuntimeException("Domain $naked_domain is already in use by another resource called:

{$app->name}."); } - } else if ($domain) { + } elseif ($domain) { throw new \RuntimeException("Domain $naked_domain is already in use by another resource called:

{$app->name}."); } } @@ -2101,15 +2181,17 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null function parseCommandsByLineForSudo(Collection $commands, Server $server): array { $commands = $commands->map(function ($line) { - if (!str($line)->startsWith('cd') && !str($line)->startsWith('command') && !str($line)->startsWith('echo') && !str($line)->startsWith('true')) { + if (! str($line)->startsWith('cd') && ! str($line)->startsWith('command') && ! str($line)->startsWith('echo') && ! str($line)->startsWith('true')) { return "sudo $line"; } + return $line; }); $commands = $commands->map(function ($line) use ($server) { if (Str::startsWith($line, 'sudo mkdir -p')) { - return "$line && sudo chown -R $server->user:$server->user " . Str::after($line, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($line, 'sudo mkdir -p'); + return "$line && sudo chown -R $server->user:$server->user ".Str::after($line, 'sudo mkdir -p').' && sudo chmod -R o-rwx '.Str::after($line, 'sudo mkdir -p'); } + return $line; }); $commands = $commands->map(function ($line) { @@ -2126,6 +2208,7 @@ function parseCommandsByLineForSudo(Collection $commands, Server $server): array if (str($line)->contains(' | ')) { $line = $line->replace(' | ', ' | sudo '); } + return $line->value(); }); @@ -2133,11 +2216,11 @@ function parseCommandsByLineForSudo(Collection $commands, Server $server): array } function parseLineForSudo(string $command, Server $server): string { - if (!str($command)->startSwith('cd') && !str($command)->startSwith('command')) { + if (! str($command)->startSwith('cd') && ! str($command)->startSwith('command')) { $command = "sudo $command"; } if (Str::startsWith($command, 'sudo mkdir -p')) { - $command = "$command && sudo chown -R $server->user:$server->user " . Str::after($command, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($command, 'sudo mkdir -p'); + $command = "$command && sudo chown -R $server->user:$server->user ".Str::after($command, 'sudo mkdir -p').' && sudo chmod -R o-rwx '.Str::after($command, 'sudo mkdir -p'); } if (str($command)->contains('$(') || str($command)->contains('`')) { $command = str($command)->replace('$(', '$(sudo ')->replace('`', '`sudo ')->value(); @@ -2167,6 +2250,7 @@ function get_public_ips() $validate_ipv4 = filter_var($ipv4, FILTER_VALIDATE_IP); if ($validate_ipv4 == false) { echo "Invalid ipv4: $ipv4\n"; + return; } $settings->update(['public_ipv4' => $ipv4]); @@ -2177,6 +2261,7 @@ function get_public_ips() $validate_ipv6 = filter_var($ipv6, FILTER_VALIDATE_IP); if ($validate_ipv6 == false) { echo "Invalid ipv6: $ipv6\n"; + return; } $settings->update(['public_ipv6' => $ipv6]); @@ -2185,3 +2270,14 @@ function get_public_ips() echo "Error: {$e->getMessage()}\n"; } } + +function isAnyDeploymentInprogress() { + // Only use it in the deployment script + $count = ApplicationDeploymentQueue::whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS, ApplicationDeploymentStatus::QUEUED])->count(); + if ($count > 0) { + echo "There are $count deployments in progress. Exiting...\n"; + exit(1); + } + echo "No deployments in progress.\n"; + exit(0); +} diff --git a/bootstrap/helpers/socialite.php b/bootstrap/helpers/socialite.php index 0798717e8..a23dc24d3 100644 --- a/bootstrap/helpers/socialite.php +++ b/bootstrap/helpers/socialite.php @@ -13,7 +13,8 @@ function get_socialite_provider(string $provider) $oauth_setting->client_secret, $oauth_setting->redirect_uri, ['tenant' => $oauth_setting->tenant], - ); + ); + return Socialite::driver('azure')->setConfig($azure_config); } diff --git a/bootstrap/helpers/subscriptions.php b/bootstrap/helpers/subscriptions.php index 5158c4e7e..224a65f0a 100644 --- a/bootstrap/helpers/subscriptions.php +++ b/bootstrap/helpers/subscriptions.php @@ -7,7 +7,7 @@ function getSubscriptionLink($type) { $checkout_id = config("subscription.lemon_squeezy_checkout_id_$type"); - if (!$checkout_id) { + if (! $checkout_id) { return null; } $user_id = auth()->user()->id; @@ -27,6 +27,7 @@ function getSubscriptionLink($type) if ($name) { $url .= "&checkout[name]={$name}"; } + return $url; } @@ -47,11 +48,11 @@ function getEndDate() function isSubscriptionActive() { - if (!isCloud()) { + if (! isCloud()) { return false; } $team = currentTeam(); - if (!$team) { + if (! $team) { return false; } $subscription = $team?->subscription; @@ -68,26 +69,29 @@ function isSubscriptionActive() if (isStripe()) { return $subscription->stripe_invoice_paid === true; } + return false; } function isSubscriptionOnGracePeriod() { $team = currentTeam(); - if (!$team) { + if (! $team) { return false; } $subscription = $team?->subscription; - if (!$subscription) { + if (! $subscription) { return false; } if (isLemon()) { $is_still_grace_period = $subscription->lemon_ends_at && Carbon::parse($subscription->lemon_ends_at) > Carbon::now(); + return $is_still_grace_period; } if (isStripe()) { return $subscription->stripe_cancel_at_period_end; } + return false; } function subscriptionProvider() @@ -110,14 +114,15 @@ function getStripeCustomerPortalSession(Team $team) { Stripe::setApiKey(config('subscription.stripe_api_key')); $return_url = route('subscription.show'); - $stripe_customer_id = data_get($team,'subscription.stripe_customer_id'); - if (!$stripe_customer_id) { + $stripe_customer_id = data_get($team, 'subscription.stripe_customer_id'); + if (! $stripe_customer_id) { return null; } $session = \Stripe\BillingPortal\Session::create([ 'customer' => $stripe_customer_id, 'return_url' => $return_url, ]); + return $session; } function allowedPathsForUnsubscribedAccounts() @@ -128,7 +133,7 @@ function allowedPathsForUnsubscribedAccounts() 'logout', 'waitlist', 'force-password-reset', - 'livewire/update' + 'livewire/update', ]; } function allowedPathsForBoardingAccounts() @@ -136,14 +141,15 @@ function allowedPathsForBoardingAccounts() return [ ...allowedPathsForUnsubscribedAccounts(), 'onboarding', - 'livewire/update' + 'livewire/update', ]; } -function allowedPathsForInvalidAccounts() { +function allowedPathsForInvalidAccounts() +{ return [ 'logout', 'verify', 'force-password-reset', - 'livewire/update' + 'livewire/update', ]; } diff --git a/bootstrap/includeHelpers.php b/bootstrap/includeHelpers.php index cc272b2c0..fb6e84e99 100644 --- a/bootstrap/includeHelpers.php +++ b/bootstrap/includeHelpers.php @@ -1,5 +1,6 @@ (bool)env('APP_DEBUG', false), + 'debug' => (bool) env('APP_DEBUG', false), /* |-------------------------------------------------------------------------- @@ -142,7 +142,7 @@ 'maintenance' => [ 'driver' => 'cache', - 'store' => 'redis', + 'store' => 'redis', ], /* diff --git a/config/cache.php b/config/cache.php index a0eba14c1..b82efddc6 100644 --- a/config/cache.php +++ b/config/cache.php @@ -105,6 +105,6 @@ | */ - 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache_'), + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), ]; diff --git a/config/constants.php b/config/constants.php index 51bc63b7b..444d144a8 100644 --- a/config/constants.php +++ b/config/constants.php @@ -1,11 +1,12 @@ [ 'base_url' => 'https://coolify.io/docs', 'contact' => 'https://coolify.io/docs/contact', ], 'ssh' => [ - 'mux_persist_time' => env('SSH_MUX_PERSIST_TIME', "1m"), + 'mux_persist_time' => env('SSH_MUX_PERSIST_TIME', '1m'), 'connection_timeout' => 10, 'server_interval' => 20, 'command_timeout' => 7200, diff --git a/config/database.php b/config/database.php index 504a5b2f3..248c6150a 100644 --- a/config/database.php +++ b/config/database.php @@ -125,7 +125,7 @@ 'options' => [ 'cluster' => env('REDIS_CLUSTER', 'redis'), - 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), ], 'default' => [ diff --git a/config/filesystems.php b/config/filesystems.php index 918e43342..c2df26c84 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -45,7 +45,7 @@ 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), - 'url' => env('APP_URL') . '/storage', + 'url' => env('APP_URL').'/storage', 'visibility' => 'public', 'throw' => false, ], diff --git a/config/horizon.php b/config/horizon.php index 15f7f5696..ef7df3f1b 100644 --- a/config/horizon.php +++ b/config/horizon.php @@ -56,7 +56,7 @@ 'prefix' => env( 'HORIZON_PREFIX', - Str::slug(env('APP_NAME', 'laravel'), '_') . '_horizon:' + Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:' ), /* diff --git a/config/livewire.php b/config/livewire.php index cf9bcd206..02725e944 100644 --- a/config/livewire.php +++ b/config/livewire.php @@ -54,7 +54,7 @@ 'temporary_file_upload' => [ 'disk' => null, // Example: 'local', 's3' | Default: 'default' 'rules' => [ // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB) - 'file', 'max:256000' + 'file', 'max:256000', ], 'directory' => null, // Example: 'tmp' | Default: 'livewire-tmp' 'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1' diff --git a/config/logging.php b/config/logging.php index a97262cb3..4c3df4ce1 100644 --- a/config/logging.php +++ b/config/logging.php @@ -85,7 +85,7 @@ 'handler_with' => [ 'host' => env('PAPERTRAIL_URL'), 'port' => env('PAPERTRAIL_PORT'), - 'connectionString' => 'tls://' . env('PAPERTRAIL_URL') . ':' . env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), ], ], diff --git a/config/mail.php b/config/mail.php index ec2125fab..26af507d9 100644 --- a/config/mail.php +++ b/config/mail.php @@ -44,8 +44,8 @@ 'timeout' => null, 'local_domain' => env('MAIL_EHLO_DOMAIN'), ], - 'resend'=> [ - 'transport' => 'resend' + 'resend' => [ + 'transport' => 'resend', ], 'ses' => [ 'transport' => 'ses', diff --git a/config/sentry.php b/config/sentry.php index 660b5e54f..33a24edfb 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.295', + 'release' => '4.0.0-beta.297', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), @@ -79,6 +79,6 @@ 'enable_tracing' => env('SENTRY_ENABLE_TRACING', false), 'traces_sample_rate' => 0.2, - 'profiles_sample_rate' => env('SENTRY_PROFILES_SAMPLE_RATE') === null ? null : (float)env('SENTRY_PROFILES_SAMPLE_RATE'), + 'profiles_sample_rate' => env('SENTRY_PROFILES_SAMPLE_RATE') === null ? null : (float) env('SENTRY_PROFILES_SAMPLE_RATE'), ]; diff --git a/config/services.php b/config/services.php index db81eef9a..9fd55870f 100644 --- a/config/services.php +++ b/config/services.php @@ -31,11 +31,11 @@ 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), ], - 'azure' => [ + 'azure' => [ 'client_id' => env('AZURE_CLIENT_ID'), 'client_secret' => env('AZURE_CLIENT_SECRET'), 'redirect' => env('AZURE_REDIRECT_URI'), 'tenant' => env('AZURE_TENANT_ID'), 'proxy' => env('AZURE_PROXY'), - ], + ], ]; diff --git a/config/session.php b/config/session.php index 447670931..c7b176a5a 100644 --- a/config/session.php +++ b/config/session.php @@ -128,7 +128,7 @@ 'cookie' => env( 'SESSION_COOKIE', - Str::slug(env('APP_NAME', 'laravel'), '_') . '_session' + Str::slug(env('APP_NAME', 'laravel'), '_').'_session' ), /* diff --git a/config/subscription.php b/config/subscription.php index f8bf77ce0..07665075f 100644 --- a/config/subscription.php +++ b/config/subscription.php @@ -1,7 +1,7 @@ env('SUBSCRIPTION_PROVIDER', null), // stripe, paddle, lemon + 'provider' => env('SUBSCRIPTION_PROVIDER', null), // stripe, paddle, lemon // Stripe 'stripe_api_key' => env('STRIPE_API_KEY', null), 'stripe_webhook_secret' => env('STRIPE_WEBHOOK_SECRET', null), @@ -35,8 +35,6 @@ 'paddle_price_id_ultimate_monthly' => env('PADDLE_PRICE_ID_ULTIMATE_MONTHLY', null), 'paddle_price_id_ultimate_yearly' => env('PADDLE_PRICE_ID_ULTIMATE_YEARLY', null), - - // Lemon 'lemon_squeezy_api_key' => env('LEMON_SQUEEZY_API_KEY', null), 'lemon_squeezy_webhook_secret' => env('LEMON_SQUEEZY_WEBHOOK_SECRET', null), @@ -46,7 +44,7 @@ 'lemon_squeezy_checkout_id_pro_yearly' => env('LEMON_SQUEEZY_CHECKOUT_ID_PRO_YEARLY', null), 'lemon_squeezy_checkout_id_ultimate_monthly' => env('LEMON_SQUEEZY_CHECKOUT_ID_ULTIMATE_MONTHLY', null), 'lemon_squeezy_checkout_id_ultimate_yearly' => env('LEMON_SQUEEZY_CHECKOUT_ID_ULTIMATE_YEARLY', null), - 'lemon_squeezy_basic_plan_ids' => env('LEMON_SQUEEZY_BASIC_PLAN_IDS', ""), - 'lemon_squeezy_pro_plan_ids' => env('LEMON_SQUEEZY_PRO_PLAN_IDS', ""), - 'lemon_squeezy_ultimate_plan_ids' => env('LEMON_SQUEEZY_ULTIMATE_PLAN_IDS', ""), + 'lemon_squeezy_basic_plan_ids' => env('LEMON_SQUEEZY_BASIC_PLAN_IDS', ''), + 'lemon_squeezy_pro_plan_ids' => env('LEMON_SQUEEZY_PRO_PLAN_IDS', ''), + 'lemon_squeezy_ultimate_plan_ids' => env('LEMON_SQUEEZY_ULTIMATE_PLAN_IDS', ''), ]; diff --git a/config/version.php b/config/version.php index 7778ab41b..06c1e6c66 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ integer('health_check_retries')->default(10); $table->integer('health_check_start_period')->default(5); - $table->string('limits_memory')->default("0"); - $table->string('limits_memory_swap')->default("0"); + $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_memory_reservation')->default('0'); - $table->string('limits_cpus')->default("0"); - $table->string('limits_cpuset')->nullable()->default("0"); + $table->string('limits_cpus')->default('0'); + $table->string('limits_cpuset')->nullable()->default('0'); $table->integer('limits_cpu_shares')->default(1024); $table->string('status')->default('exited'); diff --git a/database/migrations/2023_08_07_142950_create_standalone_postgresqls_table.php b/database/migrations/2023_08_07_142950_create_standalone_postgresqls_table.php index ddaf19a7d..fc33acaef 100644 --- a/database/migrations/2023_08_07_142950_create_standalone_postgresqls_table.php +++ b/database/migrations/2023_08_07_142950_create_standalone_postgresqls_table.php @@ -31,13 +31,13 @@ public function up(): void $table->integer('public_port')->nullable(); $table->text('ports_mappings')->nullable(); - $table->string('limits_memory')->default("0"); - $table->string('limits_memory_swap')->default("0"); + $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_memory_reservation')->default('0'); - $table->string('limits_cpus')->default("0"); - $table->string('limits_cpuset')->nullable()->default("0"); + $table->string('limits_cpus')->default('0'); + $table->string('limits_cpuset')->nullable()->default('0'); $table->integer('limits_cpu_shares')->default(1024); $table->timestamp('started_at')->nullable(); diff --git a/database/migrations/2023_08_22_071049_update_webhooks_type.php b/database/migrations/2023_08_22_071049_update_webhooks_type.php index 7f60ca973..13f0276f9 100644 --- a/database/migrations/2023_08_22_071049_update_webhooks_type.php +++ b/database/migrations/2023_08_22_071049_update_webhooks_type.php @@ -14,7 +14,7 @@ public function up(): void Schema::table('webhooks', function (Blueprint $table) { $table->string('type')->change(); }); - DB::statement("ALTER TABLE webhooks DROP CONSTRAINT webhooks_type_check"); + DB::statement('ALTER TABLE webhooks DROP CONSTRAINT webhooks_type_check'); } /** diff --git a/database/migrations/2023_08_22_071054_add_stripe_reasons.php b/database/migrations/2023_08_22_071054_add_stripe_reasons.php index 98f85c921..efd611aac 100644 --- a/database/migrations/2023_08_22_071054_add_stripe_reasons.php +++ b/database/migrations/2023_08_22_071054_add_stripe_reasons.php @@ -15,7 +15,6 @@ public function up(): void $table->string('stripe_feedback')->nullable()->after('stripe_cancel_at_period_end'); $table->string('stripe_comment')->nullable()->after('stripe_feedback'); - }); } diff --git a/database/migrations/2023_08_22_071059_add_stripe_trial_ended.php b/database/migrations/2023_08_22_071059_add_stripe_trial_ended.php index 591f8382d..c22317e6b 100644 --- a/database/migrations/2023_08_22_071059_add_stripe_trial_ended.php +++ b/database/migrations/2023_08_22_071059_add_stripe_trial_ended.php @@ -14,7 +14,6 @@ public function up(): void Schema::table('subscriptions', function (Blueprint $table) { $table->boolean('stripe_trial_already_ended')->default(false)->after('stripe_cancel_at_period_end'); - }); } diff --git a/database/migrations/2023_10_12_132430_create_standalone_redis_table.php b/database/migrations/2023_10_12_132430_create_standalone_redis_table.php index e6c94dbb6..772ee7cfd 100644 --- a/database/migrations/2023_10_12_132430_create_standalone_redis_table.php +++ b/database/migrations/2023_10_12_132430_create_standalone_redis_table.php @@ -28,13 +28,13 @@ public function up(): void $table->integer('public_port')->nullable(); $table->text('ports_mappings')->nullable(); - $table->string('limits_memory')->default("0"); - $table->string('limits_memory_swap')->default("0"); + $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_memory_reservation')->default('0'); - $table->string('limits_cpus')->default("0"); - $table->string('limits_cpuset')->nullable()->default("0"); + $table->string('limits_cpus')->default('0'); + $table->string('limits_cpuset')->nullable()->default('0'); $table->integer('limits_cpu_shares')->default(1024); $table->timestamp('started_at')->nullable(); diff --git a/database/migrations/2023_10_19_101331_create_standalone_mongodbs_table.php b/database/migrations/2023_10_19_101331_create_standalone_mongodbs_table.php index 30f5c24af..26173ffc0 100644 --- a/database/migrations/2023_10_19_101331_create_standalone_mongodbs_table.php +++ b/database/migrations/2023_10_19_101331_create_standalone_mongodbs_table.php @@ -30,13 +30,13 @@ public function up(): void $table->integer('public_port')->nullable(); $table->text('ports_mappings')->nullable(); - $table->string('limits_memory')->default("0"); - $table->string('limits_memory_swap')->default("0"); + $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_memory_reservation')->default('0'); - $table->string('limits_cpus')->default("0"); - $table->string('limits_cpuset')->nullable()->default("0"); + $table->string('limits_cpus')->default('0'); + $table->string('limits_cpuset')->nullable()->default('0'); $table->integer('limits_cpu_shares')->default(1024); $table->timestamp('started_at')->nullable(); diff --git a/database/migrations/2023_10_24_103548_create_standalone_mysqls_table.php b/database/migrations/2023_10_24_103548_create_standalone_mysqls_table.php index 2b069424a..f27d4690e 100644 --- a/database/migrations/2023_10_24_103548_create_standalone_mysqls_table.php +++ b/database/migrations/2023_10_24_103548_create_standalone_mysqls_table.php @@ -30,13 +30,13 @@ public function up(): void $table->integer('public_port')->nullable(); $table->text('ports_mappings')->nullable(); - $table->string('limits_memory')->default("0"); - $table->string('limits_memory_swap')->default("0"); + $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_memory_reservation')->default('0'); - $table->string('limits_cpus')->default("0"); - $table->string('limits_cpuset')->nullable()->default("0"); + $table->string('limits_cpus')->default('0'); + $table->string('limits_cpuset')->nullable()->default('0'); $table->integer('limits_cpu_shares')->default(1024); $table->timestamp('started_at')->nullable(); diff --git a/database/migrations/2023_10_24_120523_create_standalone_mariadbs_table.php b/database/migrations/2023_10_24_120523_create_standalone_mariadbs_table.php index 4d7b89f4c..a0350bcde 100644 --- a/database/migrations/2023_10_24_120523_create_standalone_mariadbs_table.php +++ b/database/migrations/2023_10_24_120523_create_standalone_mariadbs_table.php @@ -30,13 +30,13 @@ public function up(): void $table->integer('public_port')->nullable(); $table->text('ports_mappings')->nullable(); - $table->string('limits_memory')->default("0"); - $table->string('limits_memory_swap')->default("0"); + $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_memory_reservation')->default('0'); - $table->string('limits_cpus')->default("0"); - $table->string('limits_cpuset')->nullable()->default("0"); + $table->string('limits_cpus')->default('0'); + $table->string('limits_cpuset')->nullable()->default('0'); $table->integer('limits_cpu_shares')->default(1024); $table->timestamp('started_at')->nullable(); diff --git a/database/migrations/2023_12_17_155616_add_custom_docker_compose_start_command.php b/database/migrations/2023_12_17_155616_add_custom_docker_compose_start_command.php index e9e1031b8..eeb2769fe 100644 --- a/database/migrations/2023_12_17_155616_add_custom_docker_compose_start_command.php +++ b/database/migrations/2023_12_17_155616_add_custom_docker_compose_start_command.php @@ -15,8 +15,6 @@ public function up(): void $table->string('docker_compose_custom_start_command')->nullable(); $table->string('docker_compose_custom_build_command')->nullable(); - - }); } diff --git a/database/migrations/2024_01_12_123422_update_cpuset_limits.php b/database/migrations/2024_01_12_123422_update_cpuset_limits.php index 5f94559bc..d1956eb9f 100644 --- a/database/migrations/2024_01_12_123422_update_cpuset_limits.php +++ b/database/migrations/2024_01_12_123422_update_cpuset_limits.php @@ -49,22 +49,22 @@ public function up(): void public function down(): void { Schema::table('applications', function (Blueprint $table) { - $table->string('limits_cpuset')->nullable()->default("0")->change(); + $table->string('limits_cpuset')->nullable()->default('0')->change(); }); Schema::table('standalone_postgresqls', function (Blueprint $table) { - $table->string('limits_cpuset')->nullable()->default("0")->change(); + $table->string('limits_cpuset')->nullable()->default('0')->change(); }); Schema::table('standalone_redis', function (Blueprint $table) { - $table->string('limits_cpuset')->nullable()->default("0")->change(); + $table->string('limits_cpuset')->nullable()->default('0')->change(); }); Schema::table('standalone_mariadbs', function (Blueprint $table) { - $table->string('limits_cpuset')->nullable()->default("0")->change(); + $table->string('limits_cpuset')->nullable()->default('0')->change(); }); Schema::table('standalone_mysqls', function (Blueprint $table) { - $table->string('limits_cpuset')->nullable()->default("0")->change(); + $table->string('limits_cpuset')->nullable()->default('0')->change(); }); Schema::table('standalone_mongodbs', function (Blueprint $table) { - $table->string('limits_cpuset')->nullable()->default("0")->change(); + $table->string('limits_cpuset')->nullable()->default('0')->change(); }); Application::where('limits_cpuset', null)->update(['limits_cpuset' => '0']); StandalonePostgresql::where('limits_cpuset', null)->update(['limits_cpuset' => '0']); diff --git a/database/migrations/2024_04_10_071920_create_standalone_keydbs_table.php b/database/migrations/2024_04_10_071920_create_standalone_keydbs_table.php index e336db0d8..4cea93121 100644 --- a/database/migrations/2024_04_10_071920_create_standalone_keydbs_table.php +++ b/database/migrations/2024_04_10_071920_create_standalone_keydbs_table.php @@ -32,12 +32,12 @@ public function up(): void $table->integer('public_port')->nullable(); $table->text('ports_mappings')->nullable(); - $table->string('limits_memory')->default("0"); - $table->string('limits_memory_swap')->default("0"); + $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_memory_reservation')->default('0'); - $table->string('limits_cpus')->default("0"); + $table->string('limits_cpus')->default('0'); $table->string('limits_cpuset')->nullable()->default(null); $table->integer('limits_cpu_shares')->default(1024); diff --git a/database/migrations/2024_04_10_082220_create_standalone_dragonflies_table.php b/database/migrations/2024_04_10_082220_create_standalone_dragonflies_table.php index 55f070a74..84bd6ea6f 100644 --- a/database/migrations/2024_04_10_082220_create_standalone_dragonflies_table.php +++ b/database/migrations/2024_04_10_082220_create_standalone_dragonflies_table.php @@ -31,12 +31,12 @@ public function up(): void $table->integer('public_port')->nullable(); $table->text('ports_mappings')->nullable(); - $table->string('limits_memory')->default("0"); - $table->string('limits_memory_swap')->default("0"); + $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_memory_reservation')->default('0'); - $table->string('limits_cpus')->default("0"); + $table->string('limits_cpus')->default('0'); $table->string('limits_cpuset')->nullable()->default(null); $table->integer('limits_cpu_shares')->default(1024); diff --git a/database/migrations/2024_04_10_091519_create_standalone_clickhouses_table.php b/database/migrations/2024_04_10_091519_create_standalone_clickhouses_table.php index e2732d443..7433948b9 100644 --- a/database/migrations/2024_04_10_091519_create_standalone_clickhouses_table.php +++ b/database/migrations/2024_04_10_091519_create_standalone_clickhouses_table.php @@ -32,12 +32,12 @@ public function up(): void $table->integer('public_port')->nullable(); $table->text('ports_mappings')->nullable(); - $table->string('limits_memory')->default("0"); - $table->string('limits_memory_swap')->default("0"); + $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_memory_reservation')->default('0'); - $table->string('limits_cpus')->default("0"); + $table->string('limits_cpus')->default('0'); $table->string('limits_cpuset')->nullable()->default(null); $table->integer('limits_cpu_shares')->default(1024); diff --git a/database/migrations/2024_06_11_081614_add_www_non_www_redirect.php b/database/migrations/2024_06_11_081614_add_www_non_www_redirect.php new file mode 100644 index 000000000..21ee4efcc --- /dev/null +++ b/database/migrations/2024_06_11_081614_add_www_non_www_redirect.php @@ -0,0 +1,28 @@ +string('redirect')->enum('www', 'non-www', 'both')->default('both')->after('domain'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('applications', function (Blueprint $table) { + $table->dropColumn('redirect'); + }); + } +}; diff --git a/database/seeders/ApplicationSeeder.php b/database/seeders/ApplicationSeeder.php index 34a54c8eb..f75400ce9 100644 --- a/database/seeders/ApplicationSeeder.php +++ b/database/seeders/ApplicationSeeder.php @@ -27,7 +27,7 @@ public function run(): void 'destination_id' => 0, 'destination_type' => StandaloneDocker::class, 'source_id' => 1, - 'source_type' => GithubApp::class + 'source_type' => GithubApp::class, ]); Application::create([ 'name' => 'Dockerfile Example', @@ -42,7 +42,7 @@ public function run(): void 'destination_id' => 0, 'destination_type' => StandaloneDocker::class, 'source_id' => 0, - 'source_type' => GithubApp::class + 'source_type' => GithubApp::class, ]); Application::create([ 'name' => 'Pure Dockerfile Example', @@ -60,7 +60,7 @@ public function run(): void 'dockerfile' => 'FROM nginx EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] -' +', ]); } } diff --git a/database/seeders/GitlabAppSeeder.php b/database/seeders/GitlabAppSeeder.php index 340e7d44f..af63f2ed7 100644 --- a/database/seeders/GitlabAppSeeder.php +++ b/database/seeders/GitlabAppSeeder.php @@ -32,7 +32,7 @@ public function run(): void 'public_key' => 'dfjasiourj', 'webhook_token' => '4u3928u4y392', 'private_key_id' => 2, - 'team_id' => 0 + 'team_id' => 0, ]); } } diff --git a/database/seeders/LocalFileVolumeSeeder.php b/database/seeders/LocalFileVolumeSeeder.php index 68a425dbf..4fea46544 100644 --- a/database/seeders/LocalFileVolumeSeeder.php +++ b/database/seeders/LocalFileVolumeSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class LocalFileVolumeSeeder extends Seeder diff --git a/database/seeders/OauthSettingSeeder.php b/database/seeders/OauthSettingSeeder.php index 4d33468c7..16abf9e04 100644 --- a/database/seeders/OauthSettingSeeder.php +++ b/database/seeders/OauthSettingSeeder.php @@ -3,7 +3,6 @@ namespace Database\Seeders; use App\Models\OauthSetting; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class OauthSettingSeeder extends Seeder diff --git a/database/seeders/PrivateKeySeeder.php b/database/seeders/PrivateKeySeeder.php index 76ddce8e0..8a70cf56d 100644 --- a/database/seeders/PrivateKeySeeder.php +++ b/database/seeders/PrivateKeySeeder.php @@ -13,26 +13,26 @@ class PrivateKeySeeder extends Seeder public function run(): void { PrivateKey::create([ - "id" => 0, - "team_id" => 0, - "name" => "Testing-host", - "description" => "This is a test docker container", - "private_key" => "-----BEGIN OPENSSH PRIVATE KEY----- + 'id' => 0, + 'team_id' => 0, + 'name' => 'Testing-host', + 'description' => 'This is a test docker container', + 'private_key' => '-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== -----END OPENSSH PRIVATE KEY----- -" +', ]); PrivateKey::create([ - "id" => 1, - "team_id" => 0, - "name" => "development-github-app", - "description" => "This is the key for using the development GitHub app", - "private_key" => "-----BEGIN RSA PRIVATE KEY----- + 'id' => 1, + 'team_id' => 0, + 'name' => 'development-github-app', + 'description' => 'This is the key for using the development GitHub app', + 'private_key' => '-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAstJo/SfYh3tquc2BA29a1X3pdPpXazRgtKsb5fHOwQs1rE04 VyJYW6QCToSH4WS1oKt6iI4ma4uivn8rnkZFdw3mpcLp2ofcoeV3YPKX6pN/RiJC if+g8gCaFywOxy2pjXOLPZeFJSXFqc4UOymbhESUyDnMfk4/RvnubMiv3jINo4Ow @@ -58,15 +58,15 @@ public function run(): void oV2PBC0CgYAXOm08kFOQA+bPBdLAte8Ga89frh6asH/Z8ucfsz9/zMMG/hhq5nF3 7TItY9Pblc2Fp805J13G96zWLX4YGyLwXXkYs+Ae7QoqjonTw7/mUDARY1Zxs9m/ a1C8EDKapCw5hAhizEFOUQKOygL8Ipn+tmEUkORYdZ8Q8cWFCv9nIw== ------END RSA PRIVATE KEY-----", - "is_git_related" => true +-----END RSA PRIVATE KEY-----', + 'is_git_related' => true, ]); PrivateKey::create([ - "id" => 2, - "team_id" => 0, - "name" => "development-gitlab-app", - "description" => "This is the key for using the development Gitlab app", - "private_key" => "asdf" + 'id' => 2, + 'team_id' => 0, + 'name' => 'development-gitlab-app', + 'description' => 'This is the key for using the development Gitlab app', + 'private_key' => 'asdf', ]); } } diff --git a/database/seeders/ProductionSeeder.php b/database/seeders/ProductionSeeder.php index 16dc3583e..5db2e826c 100644 --- a/database/seeders/ProductionSeeder.php +++ b/database/seeders/ProductionSeeder.php @@ -15,7 +15,6 @@ use App\Models\User; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Process; use Illuminate\Support\Facades\Storage; class ProductionSeeder extends Seeder @@ -42,7 +41,7 @@ public function run(): void } if (InstanceSettings::find(0) == null) { InstanceSettings::create([ - 'id' => 0 + 'id' => 0, ]); } if (GithubApp::find(0) == null) { @@ -66,10 +65,10 @@ public function run(): void ]); } - if (!isCloud() && config('coolify.is_windows_docker_desktop') == false) { + if (! isCloud() && config('coolify.is_windows_docker_desktop') == false) { echo "Checking localhost key.\n"; // Save SSH Keys for the Coolify Host - $coolify_key_name = "id.root@host.docker.internal"; + $coolify_key_name = 'id.root@host.docker.internal'; $coolify_key = Storage::disk('ssh-keys')->get("{$coolify_key_name}"); if ($coolify_key) { @@ -80,7 +79,7 @@ public function run(): void ], [ 'name' => 'localhost\'s key', - 'description' => 'The private key for the Coolify host machine (localhost).', 'private_key' => $coolify_key + 'description' => 'The private key for the Coolify host machine (localhost).', 'private_key' => $coolify_key, ] ); } else { @@ -93,16 +92,16 @@ public function run(): void if (Server::find(0) == null) { $server_details = [ 'id' => 0, - 'name' => "localhost", + 'name' => 'localhost', 'description' => "This is the server where Coolify is running on. Don't delete this!", 'user' => 'root', - 'ip' => "host.docker.internal", + 'ip' => 'host.docker.internal', 'team_id' => 0, - 'private_key_id' => 0 + 'private_key_id' => 0, ]; $server_details['proxy'] = ServerMetadata::from([ 'type' => ProxyTypes::TRAEFIK_V2->value, - 'status' => ProxyStatus::EXITED->value + 'status' => ProxyStatus::EXITED->value, ]); $server = Server::create($server_details); $server->settings->is_reachable = true; @@ -130,32 +129,32 @@ public function run(): void 'team_id' => 0, ], [ - "name" => "Testing-host", - "description" => "This is a a docker container with SSH access", - "private_key" => "-----BEGIN OPENSSH PRIVATE KEY----- + 'name' => 'Testing-host', + 'description' => 'This is a a docker container with SSH access', + 'private_key' => '-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== -----END OPENSSH PRIVATE KEY----- -" +', ] ); if (Server::find(0) == null) { $server_details = [ 'id' => 0, 'uuid' => 'coolify-testing-host', - 'name' => "localhost", + 'name' => 'localhost', 'description' => "This is the server where Coolify is running on. Don't delete this!", 'user' => 'root', - 'ip' => "coolify-testing-host", + 'ip' => 'coolify-testing-host', 'team_id' => 0, - 'private_key_id' => 0 + 'private_key_id' => 0, ]; $server_details['proxy'] = ServerMetadata::from([ 'type' => ProxyTypes::TRAEFIK_V2->value, - 'status' => ProxyStatus::EXITED->value + 'status' => ProxyStatus::EXITED->value, ]); $server = Server::create($server_details); $server->settings->is_reachable = true; diff --git a/database/seeders/ProjectSeeder.php b/database/seeders/ProjectSeeder.php index 304417ed5..33cd8cd06 100644 --- a/database/seeders/ProjectSeeder.php +++ b/database/seeders/ProjectSeeder.php @@ -10,8 +10,8 @@ class ProjectSeeder extends Seeder public function run(): void { Project::create([ - 'name' => "My first project", - 'description' => "This is a test project in development", + 'name' => 'My first project', + 'description' => 'This is a test project in development', 'team_id' => 0, ]); } diff --git a/database/seeders/ServerSeeder.php b/database/seeders/ServerSeeder.php index 99ffa37ef..12594bcb9 100644 --- a/database/seeders/ServerSeeder.php +++ b/database/seeders/ServerSeeder.php @@ -11,9 +11,9 @@ public function run(): void { Server::create([ 'id' => 0, - 'name' => "localhost", - 'description' => "This is a test docker container in development mode", - 'ip' => "coolify-testing-host", + 'name' => 'localhost', + 'description' => 'This is a test docker container in development mode', + 'ip' => 'coolify-testing-host', 'team_id' => 0, 'private_key_id' => 0, ]); diff --git a/database/seeders/ServiceApplicationSeeder.php b/database/seeders/ServiceApplicationSeeder.php index 94d523cf4..04648f83c 100644 --- a/database/seeders/ServiceApplicationSeeder.php +++ b/database/seeders/ServiceApplicationSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class ServiceApplicationSeeder extends Seeder diff --git a/database/seeders/ServiceDatabaseSeeder.php b/database/seeders/ServiceDatabaseSeeder.php index 396f658bd..f56db41ca 100644 --- a/database/seeders/ServiceDatabaseSeeder.php +++ b/database/seeders/ServiceDatabaseSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class ServiceDatabaseSeeder extends Seeder diff --git a/database/seeders/ServiceSeeder.php b/database/seeders/ServiceSeeder.php index 674400f07..201b128e7 100644 --- a/database/seeders/ServiceSeeder.php +++ b/database/seeders/ServiceSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class ServiceSeeder extends Seeder diff --git a/database/seeders/StandaloneDockerSeeder.php b/database/seeders/StandaloneDockerSeeder.php index 9f67de710..1967bf2d0 100644 --- a/database/seeders/StandaloneDockerSeeder.php +++ b/database/seeders/StandaloneDockerSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use App\Models\Destination; use App\Models\StandaloneDocker; use Illuminate\Database\Seeder; diff --git a/database/seeders/StandaloneRedisSeeder.php b/database/seeders/StandaloneRedisSeeder.php index cbe10bb00..e7bf3373e 100644 --- a/database/seeders/StandaloneRedisSeeder.php +++ b/database/seeders/StandaloneRedisSeeder.php @@ -3,7 +3,6 @@ namespace Database\Seeders; use App\Models\StandaloneDocker; -use App\Models\StandalonePostgresql; use App\Models\StandaloneRedis; use Illuminate\Database\Seeder; diff --git a/database/seeders/SwarmDockerSeeder.php b/database/seeders/SwarmDockerSeeder.php index 8a204e159..85d31b140 100644 --- a/database/seeders/SwarmDockerSeeder.php +++ b/database/seeders/SwarmDockerSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use App\Models\Destination; use App\Models\SwarmDocker; use Illuminate\Database\Seeder; diff --git a/database/seeders/TestTeamSeeder.php b/database/seeders/TestTeamSeeder.php index 1d660c713..940c45cc5 100644 --- a/database/seeders/TestTeamSeeder.php +++ b/database/seeders/TestTeamSeeder.php @@ -16,9 +16,9 @@ public function run(): void 'email' => '1@example.com', ]); $team = Team::create([ - 'name' => "1@example.com", + 'name' => '1@example.com', 'personal_team' => false, - 'show_boarding' => true + 'show_boarding' => true, ]); $user->teams()->attach($team, ['role' => 'owner']); @@ -28,9 +28,9 @@ public function run(): void 'email' => '2@example.com', ]); $team = Team::create([ - 'name' => "2@example.com", + 'name' => '2@example.com', 'personal_team' => false, - 'show_boarding' => true + 'show_boarding' => true, ]); $user->teams()->attach($team, ['role' => 'owner']); $user = User::factory()->create([ diff --git a/database/seeders/WaitlistSeeder.php b/database/seeders/WaitlistSeeder.php index ce259253e..de6837c60 100644 --- a/database/seeders/WaitlistSeeder.php +++ b/database/seeders/WaitlistSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class WaitlistSeeder extends Seeder diff --git a/database/migrations/new_services.php b/database/seeders/new_services.php similarity index 100% rename from database/migrations/new_services.php rename to database/seeders/new_services.php diff --git a/other/logos/arcjet.svg b/other/logos/arcjet.svg new file mode 100644 index 000000000..0586403c2 --- /dev/null +++ b/other/logos/arcjet.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/other/logos/bc.png b/other/logos/bc.png new file mode 100644 index 000000000..ddb5ef07a Binary files /dev/null and b/other/logos/bc.png differ diff --git a/other/logos/hetzner.jpg b/other/logos/hetzner.jpg new file mode 100644 index 000000000..9825cbd7a Binary files /dev/null and b/other/logos/hetzner.jpg differ diff --git a/other/logos/logto.webp b/other/logos/logto.webp new file mode 100644 index 000000000..b50791792 Binary files /dev/null and b/other/logos/logto.webp differ diff --git a/other/logos/quant.svg b/other/logos/quant.svg new file mode 100644 index 000000000..b7386b1b4 --- /dev/null +++ b/other/logos/quant.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/index.php b/public/index.php index f3c2ebcd3..1d69f3a28 100644 --- a/public/index.php +++ b/public/index.php @@ -16,7 +16,7 @@ | */ -if (file_exists($maintenance = __DIR__ . '/../storage/framework/maintenance.php')) { +if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) { require $maintenance; } @@ -31,7 +31,7 @@ | */ -require __DIR__ . '/../vendor/autoload.php'; +require __DIR__.'/../vendor/autoload.php'; /* |-------------------------------------------------------------------------- @@ -44,7 +44,7 @@ | */ -$app = require_once __DIR__ . '/../bootstrap/app.php'; +$app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); diff --git a/public/svgs/homepage.png b/public/svgs/homepage.png new file mode 100644 index 000000000..67e9d0d1b Binary files /dev/null and b/public/svgs/homepage.png differ diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php index e04d3633d..297640111 100644 --- a/resources/views/auth/register.blade.php +++ b/resources/views/auth/register.blade.php @@ -1,3 +1,13 @@ +environment('local') ? $localValue : ''); +} + +$name = getOldOrLocal('name', 'test3 normal user'); +$email = getOldOrLocal('email', 'test3@example.com'); +?> +
@@ -11,27 +21,16 @@
@csrf - @env('local') - -
- - +
- @else - - -
- - -
- @endenv Register {{ __('auth.already_registered') }} diff --git a/resources/views/components/forms/select.blade.php b/resources/views/components/forms/select.blade.php index 0a19923fc..02308ceb5 100644 --- a/resources/views/components/forms/select.blade.php +++ b/resources/views/components/forms/select.blade.php @@ -9,8 +9,8 @@ @endif @endif - merge(['class' => $defaultClass]) }} @required($required) wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300' + wire:dirty.class="dark:focus:ring-warning dark:ring-warning" wire:loading.attr="disabled" name={{ $id }} @if ($attributes->whereStartsWith('wire:model')->first()) {{ $attributes->whereStartsWith('wire:model')->first() }} @else wire:model={{ $id }} @endif> {{ $slot }} diff --git a/resources/views/components/popup-small.blade.php b/resources/views/components/popup-small.blade.php index ba6839bab..1bd996727 100644 --- a/resources/views/components/popup-small.blade.php +++ b/resources/views/components/popup-small.blade.php @@ -8,7 +8,7 @@ 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>
+ class="flex flex-row 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 hover:dark:bg-coolgray-100 lg:p-8 sm:rounded">
@if (isset($icon)) @@ -23,7 +23,7 @@ class="w-full mb-1 text-base font-bold leading-none -translate-y-1 text-neutral-
{{ $description }}
-
+
+ + + + + + + +
Set Direction
+
+ This will reset the container labels. Are you sure? +
+
@endif @if ($application->build_pack !== 'dockercompose') diff --git a/resources/views/livewire/project/database/clickhouse/general.blade.php b/resources/views/livewire/project/database/clickhouse/general.blade.php index 91f12a94b..78a2c4952 100644 --- a/resources/views/livewire/project/database/clickhouse/general.blade.php +++ b/resources/views/livewire/project/database/clickhouse/general.blade.php @@ -15,8 +15,8 @@ @if ($database->started_at)
- +
@@ -34,9 +34,6 @@
- -
@endif +
+

Proxy

+
+ + + Proxy Logs + + + + Proxy Logs + + +
+

Advanced

diff --git a/resources/views/livewire/project/database/dragonfly/general.blade.php b/resources/views/livewire/project/database/dragonfly/general.blade.php index f9552f037..c6ee13fa3 100644 --- a/resources/views/livewire/project/database/dragonfly/general.blade.php +++ b/resources/views/livewire/project/database/dragonfly/general.blade.php @@ -16,9 +16,6 @@
- -
@endif
+
+

Proxy

+
+ + + Proxy Logs + + + + Proxy Logs + + +
+
{{-- --}} diff --git a/resources/views/livewire/project/database/keydb/general.blade.php b/resources/views/livewire/project/database/keydb/general.blade.php index 152b3c6cd..d1134d73d 100644 --- a/resources/views/livewire/project/database/keydb/general.blade.php +++ b/resources/views/livewire/project/database/keydb/general.blade.php @@ -17,9 +17,6 @@
- -
@endif +
+

Proxy

+
+ + + Proxy Logs + + + + Proxy Logs + + +
+
diff --git a/resources/views/livewire/project/database/mariadb/general.blade.php b/resources/views/livewire/project/database/mariadb/general.blade.php index 7f0596b5c..ddb35a6bc 100644 --- a/resources/views/livewire/project/database/mariadb/general.blade.php +++ b/resources/views/livewire/project/database/mariadb/general.blade.php @@ -45,9 +45,6 @@
- -
@endif +
+

Proxy

+
+ + + Proxy Logs + + + + Proxy Logs + + +
+

Advanced

diff --git a/resources/views/livewire/project/database/mongodb/general.blade.php b/resources/views/livewire/project/database/mongodb/general.blade.php index b405985a8..a5017b21f 100644 --- a/resources/views/livewire/project/database/mongodb/general.blade.php +++ b/resources/views/livewire/project/database/mongodb/general.blade.php @@ -18,9 +18,11 @@ @if ($database->started_at)
+ placeholder="If empty: postgres" + helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." /> + required + helper="If you change this in the database, please sync it here, otherwise automations (like backups) won't work." /> @@ -39,9 +41,6 @@
- -
@endif
+
+

Proxy

+
+ + + Proxy Logs + + + + Proxy Logs + + +
+

Advanced

diff --git a/resources/views/livewire/project/database/mysql/general.blade.php b/resources/views/livewire/project/database/mysql/general.blade.php index 539948b2e..85cbef0dd 100644 --- a/resources/views/livewire/project/database/mysql/general.blade.php +++ b/resources/views/livewire/project/database/mysql/general.blade.php @@ -45,9 +45,6 @@
- -
@endif
+
+

Proxy

+
+ + + Proxy Logs + + + + Proxy Logs + + +
+

Advanced

diff --git a/resources/views/livewire/project/database/postgresql/general.blade.php b/resources/views/livewire/project/database/postgresql/general.blade.php index e0ce81d8e..718c44f97 100644 --- a/resources/views/livewire/project/database/postgresql/general.blade.php +++ b/resources/views/livewire/project/database/postgresql/general.blade.php @@ -26,7 +26,8 @@
-
If you change the values in the database, please sync it here, otherwise automations (like backups) won't work. +
If you change the values in the database, please sync it here, otherwise + automations (like backups) won't work.
@if ($database->started_at)
@@ -57,10 +58,8 @@
- -
+ @@ -70,6 +69,23 @@ type="password" readonly wire:model="db_url_public" /> @endif
+
+

Proxy

+
+ + + Proxy Logs + + + + Proxy Logs + + +
+

Advanced

diff --git a/resources/views/livewire/project/database/redis/general.blade.php b/resources/views/livewire/project/database/redis/general.blade.php index 48d3eadd4..ceb12a802 100644 --- a/resources/views/livewire/project/database/redis/general.blade.php +++ b/resources/views/livewire/project/database/redis/general.blade.php @@ -17,9 +17,6 @@
- -
@endif
+
+

Proxy

+
+ + + Proxy Logs + + + + Proxy Logs + + +
+

Advanced