Fix styling

This commit is contained in:
Thijmen 2024-06-10 20:43:34 +00:00 committed by github-actions[bot]
parent 41fb6a1fc9
commit d86274cc37
429 changed files with 5307 additions and 2831 deletions

View File

@ -3,17 +3,17 @@
namespace App\Actions\Application; namespace App\Actions\Application;
use App\Models\Application; use App\Models\Application;
use App\Models\StandaloneDocker;
use App\Notifications\Application\StatusChanged;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
class StopApplication class StopApplication
{ {
use AsAction; use AsAction;
public function handle(Application $application) public function handle(Application $application)
{ {
if ($application->destination->server->isSwarm()) { if ($application->destination->server->isSwarm()) {
instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server); instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server);
return; return;
} }
@ -23,7 +23,7 @@ public function handle(Application $application)
$servers->push($server); $servers->push($server);
}); });
foreach ($servers as $server) { foreach ($servers as $server) {
if (!$server->isFunctional()) { if (! $server->isFunctional()) {
return 'Server is not functional'; return 'Server is not functional';
} }
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0); $containers = getCurrentApplicationContainerStatus($server, $application->id, 0);

View File

@ -9,12 +9,13 @@
class StopApplicationOneServer class StopApplicationOneServer
{ {
use AsAction; use AsAction;
public function handle(Application $application, Server $server) public function handle(Application $application, Server $server)
{ {
if ($application->destination->server->isSwarm()) { if ($application->destination->server->isSwarm()) {
return; return;
} }
if (!$server->isFunctional()) { if (! $server->isFunctional()) {
return 'Server is not functional'; return 'Server is not functional';
} }
try { try {
@ -32,6 +33,7 @@ public function handle(Application $application, Server $server)
} }
} catch (\Exception $e) { } catch (\Exception $e) {
ray($e->getMessage()); ray($e->getMessage());
return $e->getMessage(); return $e->getMessage();
} }
} }

View File

@ -14,6 +14,7 @@
class PrepareCoolifyTask class PrepareCoolifyTask
{ {
protected Activity $activity; protected Activity $activity;
protected CoolifyTaskArgs $remoteProcessArgs; protected CoolifyTaskArgs $remoteProcessArgs;
public function __construct(CoolifyTaskArgs $remoteProcessArgs) public function __construct(CoolifyTaskArgs $remoteProcessArgs)
@ -28,12 +29,12 @@ public function __construct(CoolifyTaskArgs $remoteProcessArgs)
->withProperties($properties) ->withProperties($properties)
->performedOn($remoteProcessArgs->model) ->performedOn($remoteProcessArgs->model)
->event($remoteProcessArgs->type) ->event($remoteProcessArgs->type)
->log("[]"); ->log('[]');
} else { } else {
$this->activity = activity() $this->activity = activity()
->withProperties($remoteProcessArgs->toArray()) ->withProperties($remoteProcessArgs->toArray())
->event($remoteProcessArgs->type) ->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); $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); dispatch($job);
$this->activity->refresh(); $this->activity->refresh();
return $this->activity; return $this->activity;
} }
} }

View File

@ -69,7 +69,7 @@ public static function decodeOutput(?Activity $activity = null): string
return collect($decoded) return collect($decoded)
->sortBy(fn ($i) => $i['order']) ->sortBy(fn ($i) => $i['order'])
->map(fn ($i) => $i['output']) ->map(fn ($i) => $i['output'])
->implode(""); ->implode('');
} }
public function __invoke(): ProcessResult public function __invoke(): ProcessResult
@ -91,7 +91,7 @@ public function __invoke(): ProcessResult
if ($processResult->exitCode() == 0) { if ($processResult->exitCode() == 0) {
$status = ProcessStatus::FINISHED; $status = ProcessStatus::FINISHED;
} }
if ($processResult->exitCode() != 0 && !$this->ignore_errors) { if ($processResult->exitCode() != 0 && ! $this->ignore_errors) {
$status = ProcessStatus::ERROR; $status = ProcessStatus::ERROR;
} }
// if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) { // 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, 'status' => $status->value,
]); ]);
$this->activity->save(); $this->activity->save();
if ($processResult->exitCode() != 0 && !$this->ignore_errors) { if ($processResult->exitCode() != 0 && ! $this->ignore_errors) {
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode()); throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
} }
if ($this->call_event_on_finish) { if ($this->call_event_on_finish) {
try { try {
if ($this->call_event_data) { if ($this->call_event_data) {
event(resolve("App\\Events\\$this->call_event_on_finish", [ event(resolve("App\\Events\\$this->call_event_on_finish", [
"data" => $this->call_event_data, 'data' => $this->call_event_data,
])); ]));
} else { } else {
event(resolve("App\\Events\\$this->call_event_on_finish", [ event(resolve("App\\Events\\$this->call_event_on_finish", [
@ -127,6 +127,7 @@ public function __invoke(): ProcessResult
ray($e); ray($e);
} }
} }
return $processResult; return $processResult;
} }
@ -182,6 +183,7 @@ protected function getLatestCounter(): int
if ($description === null || count($description) === 0) { if ($description === null || count($description) === 0) {
return 1; return 1;
} }
return end($description)['order'] + 1; return end($description)['order'] + 1;
} }

View File

@ -4,24 +4,25 @@
use App\Models\StandaloneClickhouse; use App\Models\StandaloneClickhouse;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
class StartClickhouse class StartClickhouse
{ {
use AsAction; use AsAction;
public StandaloneClickhouse $database; public StandaloneClickhouse $database;
public array $commands = []; public array $commands = [];
public string $configuration_dir; public string $configuration_dir;
public function handle(StandaloneClickhouse $database) public function handle(StandaloneClickhouse $database)
{ {
$this->database = $database; $this->database = $database;
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name; $this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [ $this->commands = [
"echo 'Starting {$database->name}.'", "echo 'Starting {$database->name}.'",
@ -57,7 +58,7 @@ public function handle(StandaloneClickhouse $database)
'interval' => '5s', 'interval' => '5s',
'timeout' => '5s', 'timeout' => '5s',
'retries' => 10, 'retries' => 10,
'start_period' => '5s' 'start_period' => '5s',
], ],
'mem_limit' => $this->database->limits_memory, 'mem_limit' => $this->database->limits_memory,
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
@ -65,27 +66,27 @@ public function handle(StandaloneClickhouse $database)
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (float) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ],
], ],
'networks' => [ 'networks' => [
$this->database->destination->network => [ $this->database->destination->network => [
'external' => true, 'external' => true,
'name' => $this->database->destination->network, 'name' => $this->database->destination->network,
'attachable' => true, '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); data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
} }
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) { if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
$docker_compose['services'][$container_name]['logging'] = [ $docker_compose['services'][$container_name]['logging'] = [
'driver' => 'fluentd', 'driver' => 'fluentd',
'options' => [ 'options' => [
'fluentd-address' => "tcp://127.0.0.1:24224", 'fluentd-address' => 'tcp://127.0.0.1:24224',
'fluentd-async' => "true", 'fluentd-async' => 'true',
'fluentd-sub-second-precision' => "true", 'fluentd-sub-second-precision' => 'true',
] ],
]; ];
} }
if (count($this->database->ports_mappings_array) > 0) { 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 pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo 'Database started.'"; $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');
} }
@ -119,12 +121,13 @@ private function generate_local_persistent_volumes()
$local_persistent_volumes = []; $local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) { foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) { 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 { } else {
$volume_name = $persistentStorage->name; $volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
} }
} }
return $local_persistent_volumes; return $local_persistent_volumes;
} }
@ -141,6 +144,7 @@ private function generate_local_persistent_volumes_only_volume_names()
'external' => false, 'external' => false,
]; ];
} }
return $local_persistent_volumes_names; return $local_persistent_volumes_names;
} }

View File

@ -69,19 +69,19 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
} }
if ($type === 'App\Models\StandaloneRedis') { if ($type === 'App\Models\StandaloneRedis') {
$internalPort = 6379; $internalPort = 6379;
} else if ($type === 'App\Models\StandalonePostgresql') { } elseif ($type === 'App\Models\StandalonePostgresql') {
$internalPort = 5432; $internalPort = 5432;
} else if ($type === 'App\Models\StandaloneMongodb') { } elseif ($type === 'App\Models\StandaloneMongodb') {
$internalPort = 27017; $internalPort = 27017;
} else if ($type === 'App\Models\StandaloneMysql') { } elseif ($type === 'App\Models\StandaloneMysql') {
$internalPort = 3306; $internalPort = 3306;
} else if ($type === 'App\Models\StandaloneMariadb') { } elseif ($type === 'App\Models\StandaloneMariadb') {
$internalPort = 3306; $internalPort = 3306;
} else if ($type === 'App\Models\StandaloneKeydb') { } elseif ($type === 'App\Models\StandaloneKeydb') {
$internalPort = 6379; $internalPort = 6379;
} else if ($type === 'App\Models\StandaloneDragonfly') { } elseif ($type === 'App\Models\StandaloneDragonfly') {
$internalPort = 6379; $internalPort = 6379;
} else if ($type === 'App\Models\StandaloneClickhouse') { } elseif ($type === 'App\Models\StandaloneClickhouse') {
$internalPort = 9000; $internalPort = 9000;
} }
$configuration_dir = database_proxy_dir($database->uuid); $configuration_dir = database_proxy_dir($database->uuid);
@ -101,7 +101,7 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
} }
} }
EOF; EOF;
$dockerfile = <<< EOF $dockerfile = <<< 'EOF'
FROM nginx:stable-alpine FROM nginx:stable-alpine
COPY nginx.conf /etc/nginx/nginx.conf COPY nginx.conf /etc/nginx/nginx.conf
@ -113,7 +113,7 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
'context' => $configuration_dir, 'context' => $configuration_dir,
'dockerfile' => 'Dockerfile', 'dockerfile' => 'Dockerfile',
], ],
'image' => "nginx:stable-alpine", 'image' => 'nginx:stable-alpine',
'container_name' => $proxyContainerName, 'container_name' => $proxyContainerName,
'restart' => RESTART_MODE, 'restart' => RESTART_MODE,
'ports' => [ 'ports' => [
@ -130,17 +130,17 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
'interval' => '5s', 'interval' => '5s',
'timeout' => '5s', 'timeout' => '5s',
'retries' => 3, 'retries' => 3,
'start_period' => '1s' 'start_period' => '1s',
],
], ],
]
], ],
'networks' => [ 'networks' => [
$network => [ $network => [
'external' => true, 'external' => true,
'name' => $network, 'name' => $network,
'attachable' => true, 'attachable' => true,
] ],
] ],
]; ];
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2)); $dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
$nginxconf_base64 = base64_encode($nginxconf); $nginxconf_base64 = base64_encode($nginxconf);

View File

@ -3,19 +3,19 @@
namespace App\Actions\Database; namespace App\Actions\Database;
use App\Models\StandaloneDragonfly; use App\Models\StandaloneDragonfly;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
class StartDragonfly class StartDragonfly
{ {
use AsAction; use AsAction;
public StandaloneDragonfly $database; public StandaloneDragonfly $database;
public array $commands = [];
public string $configuration_dir;
public array $commands = [];
public string $configuration_dir;
public function handle(StandaloneDragonfly $database) public function handle(StandaloneDragonfly $database)
{ {
@ -24,7 +24,7 @@ public function handle(StandaloneDragonfly $database)
$startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}"; $startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}";
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name; $this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [ $this->commands = [
"echo 'Starting {$database->name}.'", "echo 'Starting {$database->name}.'",
@ -48,7 +48,7 @@ public function handle(StandaloneDragonfly $database)
$this->database->destination->network, $this->database->destination->network,
], ],
'ulimits' => [ 'ulimits' => [
'memlock'=> '-1' 'memlock' => '-1',
], ],
'labels' => [ 'labels' => [
'coolify.managed' => 'true', 'coolify.managed' => 'true',
@ -58,7 +58,7 @@ public function handle(StandaloneDragonfly $database)
'interval' => '5s', 'interval' => '5s',
'timeout' => '5s', 'timeout' => '5s',
'retries' => 10, 'retries' => 10,
'start_period' => '5s' 'start_period' => '5s',
], ],
'mem_limit' => $this->database->limits_memory, 'mem_limit' => $this->database->limits_memory,
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
@ -66,27 +66,27 @@ public function handle(StandaloneDragonfly $database)
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (float) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ],
], ],
'networks' => [ 'networks' => [
$this->database->destination->network => [ $this->database->destination->network => [
'external' => true, 'external' => true,
'name' => $this->database->destination->network, 'name' => $this->database->destination->network,
'attachable' => true, '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); data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
} }
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) { if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
$docker_compose['services'][$container_name]['logging'] = [ $docker_compose['services'][$container_name]['logging'] = [
'driver' => 'fluentd', 'driver' => 'fluentd',
'options' => [ 'options' => [
'fluentd-address' => "tcp://127.0.0.1:24224", 'fluentd-address' => 'tcp://127.0.0.1:24224',
'fluentd-async' => "true", 'fluentd-async' => 'true',
'fluentd-sub-second-precision' => "true", 'fluentd-sub-second-precision' => 'true',
] ],
]; ];
} }
if (count($this->database->ports_mappings_array) > 0) { 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 pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo 'Database started.'"; $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');
} }
@ -120,12 +121,13 @@ private function generate_local_persistent_volumes()
$local_persistent_volumes = []; $local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) { foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) { 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 { } else {
$volume_name = $persistentStorage->name; $volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
} }
} }
return $local_persistent_volumes; return $local_persistent_volumes;
} }
@ -142,6 +144,7 @@ private function generate_local_persistent_volumes_only_volume_names()
'external' => false, 'external' => false,
]; ];
} }
return $local_persistent_volumes_names; return $local_persistent_volumes_names;
} }

View File

@ -5,17 +5,18 @@
use App\Models\StandaloneKeydb; use App\Models\StandaloneKeydb;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
class StartKeydb class StartKeydb
{ {
use AsAction; use AsAction;
public StandaloneKeydb $database; public StandaloneKeydb $database;
public array $commands = [];
public string $configuration_dir;
public array $commands = [];
public string $configuration_dir;
public function handle(StandaloneKeydb $database) public function handle(StandaloneKeydb $database)
{ {
@ -24,7 +25,7 @@ public function handle(StandaloneKeydb $database)
$startCommand = "keydb-server --requirepass {$this->database->keydb_password} --appendonly yes"; $startCommand = "keydb-server --requirepass {$this->database->keydb_password} --appendonly yes";
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name; $this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [ $this->commands = [
"echo 'Starting {$database->name}.'", "echo 'Starting {$database->name}.'",
@ -56,7 +57,7 @@ public function handle(StandaloneKeydb $database)
'interval' => '5s', 'interval' => '5s',
'timeout' => '5s', 'timeout' => '5s',
'retries' => 10, 'retries' => 10,
'start_period' => '5s' 'start_period' => '5s',
], ],
'mem_limit' => $this->database->limits_memory, 'mem_limit' => $this->database->limits_memory,
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
@ -64,27 +65,27 @@ public function handle(StandaloneKeydb $database)
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (float) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ],
], ],
'networks' => [ 'networks' => [
$this->database->destination->network => [ $this->database->destination->network => [
'external' => true, 'external' => true,
'name' => $this->database->destination->network, 'name' => $this->database->destination->network,
'attachable' => true, '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); data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
} }
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) { if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
$docker_compose['services'][$container_name]['logging'] = [ $docker_compose['services'][$container_name]['logging'] = [
'driver' => 'fluentd', 'driver' => 'fluentd',
'options' => [ 'options' => [
'fluentd-address' => "tcp://127.0.0.1:24224", 'fluentd-address' => 'tcp://127.0.0.1:24224',
'fluentd-async' => "true", 'fluentd-async' => 'true',
'fluentd-sub-second-precision' => "true", 'fluentd-sub-second-precision' => 'true',
] ],
]; ];
} }
if (count($this->database->ports_mappings_array) > 0) { if (count($this->database->ports_mappings_array) > 0) {
@ -101,10 +102,10 @@ public function handle(StandaloneKeydb $database)
if (count($volume_names) > 0) { if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names; $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'][] = [ $docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind', 'type' => 'bind',
'source' => $this->configuration_dir . '/keydb.conf', 'source' => $this->configuration_dir.'/keydb.conf',
'target' => '/etc/keydb/keydb.conf', 'target' => '/etc/keydb/keydb.conf',
'read_only' => true, '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 pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo 'Database started.'"; $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');
} }
@ -127,12 +129,13 @@ private function generate_local_persistent_volumes()
$local_persistent_volumes = []; $local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) { foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) { 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 { } else {
$volume_name = $persistentStorage->name; $volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
} }
} }
return $local_persistent_volumes; return $local_persistent_volumes;
} }
@ -149,6 +152,7 @@ private function generate_local_persistent_volumes_only_volume_names()
'external' => false, 'external' => false,
]; ];
} }
return $local_persistent_volumes_names; return $local_persistent_volumes_names;
} }
@ -165,6 +169,7 @@ private function generate_environment_variables()
return $environment_variables->all(); return $environment_variables->all();
} }
private function add_custom_keydb() private function add_custom_keydb()
{ {
if (is_null($this->database->keydb_conf) || empty($this->database->keydb_conf)) { if (is_null($this->database->keydb_conf) || empty($this->database->keydb_conf)) {

View File

@ -4,15 +4,17 @@
use App\Models\StandaloneMariadb; use App\Models\StandaloneMariadb;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
class StartMariadb class StartMariadb
{ {
use AsAction; use AsAction;
public StandaloneMariadb $database; public StandaloneMariadb $database;
public array $commands = []; public array $commands = [];
public string $configuration_dir; public string $configuration_dir;
public function handle(StandaloneMariadb $database) public function handle(StandaloneMariadb $database)
@ -20,7 +22,7 @@ public function handle(StandaloneMariadb $database)
$this->database = $database; $this->database = $database;
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name; $this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [ $this->commands = [
"echo 'Starting {$database->name}.'", "echo 'Starting {$database->name}.'",
@ -46,11 +48,11 @@ public function handle(StandaloneMariadb $database)
'coolify.managed' => 'true', 'coolify.managed' => 'true',
], ],
'healthcheck' => [ 'healthcheck' => [
'test' => ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"], 'test' => ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized'],
'interval' => '5s', 'interval' => '5s',
'timeout' => '5s', 'timeout' => '5s',
'retries' => 10, 'retries' => 10,
'start_period' => '5s' 'start_period' => '5s',
], ],
'mem_limit' => $this->database->limits_memory, 'mem_limit' => $this->database->limits_memory,
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
@ -58,27 +60,27 @@ public function handle(StandaloneMariadb $database)
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (float) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ],
], ],
'networks' => [ 'networks' => [
$this->database->destination->network => [ $this->database->destination->network => [
'external' => true, 'external' => true,
'name' => $this->database->destination->network, 'name' => $this->database->destination->network,
'attachable' => true, '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); data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
} }
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) { if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
$docker_compose['services'][$container_name]['logging'] = [ $docker_compose['services'][$container_name]['logging'] = [
'driver' => 'fluentd', 'driver' => 'fluentd',
'options' => [ 'options' => [
'fluentd-address' => "tcp://127.0.0.1:24224", 'fluentd-address' => 'tcp://127.0.0.1:24224',
'fluentd-async' => "true", 'fluentd-async' => 'true',
'fluentd-sub-second-precision' => "true", 'fluentd-sub-second-precision' => 'true',
] ],
]; ];
} }
if (count($this->database->ports_mappings_array) > 0) { if (count($this->database->ports_mappings_array) > 0) {
@ -95,10 +97,10 @@ public function handle(StandaloneMariadb $database)
if (count($volume_names) > 0) { if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names; $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'][] = [ $docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind', 'type' => 'bind',
'source' => $this->configuration_dir . '/custom-config.cnf', 'source' => $this->configuration_dir.'/custom-config.cnf',
'target' => '/etc/mysql/conf.d/custom-config.cnf', 'target' => '/etc/mysql/conf.d/custom-config.cnf',
'read_only' => true, '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 pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo 'Database started.'"; $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');
} }
@ -120,12 +123,13 @@ private function generate_local_persistent_volumes()
$local_persistent_volumes = []; $local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) { foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) { 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 { } else {
$volume_name = $persistentStorage->name; $volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
} }
} }
return $local_persistent_volumes; return $local_persistent_volumes;
} }
@ -142,6 +146,7 @@ private function generate_local_persistent_volumes_only_volume_names()
'external' => false, 'external' => false,
]; ];
} }
return $local_persistent_volumes_names; 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()) { if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
$environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}"); $environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}");
} }
return $environment_variables->all(); return $environment_variables->all();
} }
private function add_custom_mysql() private function add_custom_mysql()
{ {
if (is_null($this->database->mariadb_conf) || empty($this->database->mariadb_conf)) { if (is_null($this->database->mariadb_conf) || empty($this->database->mariadb_conf)) {

View File

@ -4,25 +4,27 @@
use App\Models\StandaloneMongodb; use App\Models\StandaloneMongodb;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
class StartMongodb class StartMongodb
{ {
use AsAction; use AsAction;
public StandaloneMongodb $database; public StandaloneMongodb $database;
public array $commands = []; public array $commands = [];
public string $configuration_dir; public string $configuration_dir;
public function handle(StandaloneMongodb $database) public function handle(StandaloneMongodb $database)
{ {
$this->database = $database; $this->database = $database;
$startCommand = "mongod"; $startCommand = 'mongod';
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name; $this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [ $this->commands = [
"echo 'Starting {$database->name}.'", "echo 'Starting {$database->name}.'",
@ -51,14 +53,14 @@ public function handle(StandaloneMongodb $database)
], ],
'healthcheck' => [ 'healthcheck' => [
'test' => [ 'test' => [
"CMD", 'CMD',
"echo", 'echo',
"ok" 'ok',
], ],
'interval' => '5s', 'interval' => '5s',
'timeout' => '5s', 'timeout' => '5s',
'retries' => 10, 'retries' => 10,
'start_period' => '5s' 'start_period' => '5s',
], ],
'mem_limit' => $this->database->limits_memory, 'mem_limit' => $this->database->limits_memory,
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
@ -66,27 +68,27 @@ public function handle(StandaloneMongodb $database)
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (float) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ],
], ],
'networks' => [ 'networks' => [
$this->database->destination->network => [ $this->database->destination->network => [
'external' => true, 'external' => true,
'name' => $this->database->destination->network, 'name' => $this->database->destination->network,
'attachable' => true, '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); data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
} }
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) { if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
$docker_compose['services'][$container_name]['logging'] = [ $docker_compose['services'][$container_name]['logging'] = [
'driver' => 'fluentd', 'driver' => 'fluentd',
'options' => [ 'options' => [
'fluentd-address' => "tcp://127.0.0.1:24224", 'fluentd-address' => 'tcp://127.0.0.1:24224',
'fluentd-async' => "true", 'fluentd-async' => 'true',
'fluentd-sub-second-precision' => "true", 'fluentd-sub-second-precision' => 'true',
] ],
]; ];
} }
if (count($this->database->ports_mappings_array) > 0) { if (count($this->database->ports_mappings_array) > 0) {
@ -103,19 +105,19 @@ public function handle(StandaloneMongodb $database)
if (count($volume_names) > 0) { if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names; $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'][] = [ $docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind', 'type' => 'bind',
'source' => $this->configuration_dir . '/mongod.conf', 'source' => $this->configuration_dir.'/mongod.conf',
'target' => '/etc/mongo/mongod.conf', 'target' => '/etc/mongo/mongod.conf',
'read_only' => true, '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(); $this->add_default_database();
$docker_compose['services'][$container_name]['volumes'][] = [ $docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind', 'type' => 'bind',
'source' => $this->configuration_dir . '/docker-entrypoint-initdb.d', 'source' => $this->configuration_dir.'/docker-entrypoint-initdb.d',
'target' => '/docker-entrypoint-initdb.d', 'target' => '/docker-entrypoint-initdb.d',
'read_only' => true, '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 pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo 'Database started.'"; $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');
} }
@ -137,12 +140,13 @@ private function generate_local_persistent_volumes()
$local_persistent_volumes = []; $local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) { foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) { 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 { } else {
$volume_name = $persistentStorage->name; $volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
} }
} }
return $local_persistent_volumes; return $local_persistent_volumes;
} }
@ -159,6 +163,7 @@ private function generate_local_persistent_volumes_only_volume_names()
'external' => false, 'external' => false,
]; ];
} }
return $local_persistent_volumes_names; 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()) { 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}"); $environment_variables->push("MONGO_INITDB_DATABASE={$this->database->mongo_initdb_database}");
} }
return $environment_variables->all(); return $environment_variables->all();
} }
private function add_custom_mongo_conf() private function add_custom_mongo_conf()
{ {
if (is_null($this->database->mongo_conf) || empty($this->database->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); $content_base64 = base64_encode($content);
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null"; $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
} }
private function add_default_database() 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}\"}]});"; $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}\"}]});";

View File

@ -4,15 +4,17 @@
use App\Models\StandaloneMysql; use App\Models\StandaloneMysql;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
class StartMysql class StartMysql
{ {
use AsAction; use AsAction;
public StandaloneMysql $database; public StandaloneMysql $database;
public array $commands = []; public array $commands = [];
public string $configuration_dir; public string $configuration_dir;
public function handle(StandaloneMysql $database) public function handle(StandaloneMysql $database)
@ -20,7 +22,7 @@ public function handle(StandaloneMysql $database)
$this->database = $database; $this->database = $database;
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name; $this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [ $this->commands = [
"echo 'Starting {$database->name}.'", "echo 'Starting {$database->name}.'",
@ -46,11 +48,11 @@ public function handle(StandaloneMysql $database)
'coolify.managed' => 'true', 'coolify.managed' => 'true',
], ],
'healthcheck' => [ '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', 'interval' => '5s',
'timeout' => '5s', 'timeout' => '5s',
'retries' => 10, 'retries' => 10,
'start_period' => '5s' 'start_period' => '5s',
], ],
'mem_limit' => $this->database->limits_memory, 'mem_limit' => $this->database->limits_memory,
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
@ -58,27 +60,27 @@ public function handle(StandaloneMysql $database)
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (float) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ],
], ],
'networks' => [ 'networks' => [
$this->database->destination->network => [ $this->database->destination->network => [
'external' => true, 'external' => true,
'name' => $this->database->destination->network, 'name' => $this->database->destination->network,
'attachable' => true, '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); data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
} }
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) { if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
$docker_compose['services'][$container_name]['logging'] = [ $docker_compose['services'][$container_name]['logging'] = [
'driver' => 'fluentd', 'driver' => 'fluentd',
'options' => [ 'options' => [
'fluentd-address' => "tcp://127.0.0.1:24224", 'fluentd-address' => 'tcp://127.0.0.1:24224',
'fluentd-async' => "true", 'fluentd-async' => 'true',
'fluentd-sub-second-precision' => "true", 'fluentd-sub-second-precision' => 'true',
] ],
]; ];
} }
if (count($this->database->ports_mappings_array) > 0) { if (count($this->database->ports_mappings_array) > 0) {
@ -95,10 +97,10 @@ public function handle(StandaloneMysql $database)
if (count($volume_names) > 0) { if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names; $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'][] = [ $docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind', 'type' => 'bind',
'source' => $this->configuration_dir . '/custom-config.cnf', 'source' => $this->configuration_dir.'/custom-config.cnf',
'target' => '/etc/mysql/conf.d/custom-config.cnf', 'target' => '/etc/mysql/conf.d/custom-config.cnf',
'read_only' => true, '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 pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo 'Database started.'"; $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() private function generate_local_persistent_volumes()
@ -120,12 +123,13 @@ private function generate_local_persistent_volumes()
$local_persistent_volumes = []; $local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) { foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) { 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 { } else {
$volume_name = $persistentStorage->name; $volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
} }
} }
return $local_persistent_volumes; return $local_persistent_volumes;
} }
@ -142,6 +146,7 @@ private function generate_local_persistent_volumes_only_volume_names()
'external' => false, 'external' => false,
]; ];
} }
return $local_persistent_volumes_names; 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()) { if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
$environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}"); $environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}");
} }
return $environment_variables->all(); return $environment_variables->all();
} }
private function add_custom_mysql() private function add_custom_mysql()
{ {
if (is_null($this->database->mysql_conf) || empty($this->database->mysql_conf)) { if (is_null($this->database->mysql_conf) || empty($this->database->mysql_conf)) {

View File

@ -4,28 +4,31 @@
use App\Models\StandalonePostgresql; use App\Models\StandalonePostgresql;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
class StartPostgresql class StartPostgresql
{ {
use AsAction; use AsAction;
public StandalonePostgresql $database; public StandalonePostgresql $database;
public array $commands = []; public array $commands = [];
public array $init_scripts = []; public array $init_scripts = [];
public string $configuration_dir; public string $configuration_dir;
public function handle(StandalonePostgresql $database) public function handle(StandalonePostgresql $database)
{ {
$this->database = $database; $this->database = $database;
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name; $this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [ $this->commands = [
"echo 'Starting {$database->name}.'", "echo 'Starting {$database->name}.'",
"mkdir -p $this->configuration_dir", "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(); $persistent_storages = $this->generate_local_persistent_volumes();
@ -50,13 +53,13 @@ public function handle(StandalonePostgresql $database)
], ],
'healthcheck' => [ 'healthcheck' => [
'test' => [ 'test' => [
"CMD-SHELL", 'CMD-SHELL',
"psql -U {$this->database->postgres_user} -d {$this->database->postgres_db} -c 'SELECT 1' || exit 1" "psql -U {$this->database->postgres_user} -d {$this->database->postgres_db} -c 'SELECT 1' || exit 1",
], ],
'interval' => '5s', 'interval' => '5s',
'timeout' => '5s', 'timeout' => '5s',
'retries' => 10, 'retries' => 10,
'start_period' => '5s' 'start_period' => '5s',
], ],
'mem_limit' => $this->database->limits_memory, 'mem_limit' => $this->database->limits_memory,
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
@ -64,27 +67,27 @@ public function handle(StandalonePostgresql $database)
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (float) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ],
], ],
'networks' => [ 'networks' => [
$this->database->destination->network => [ $this->database->destination->network => [
'external' => true, 'external' => true,
'name' => $this->database->destination->network, 'name' => $this->database->destination->network,
'attachable' => true, '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); data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
} }
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) { if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
$docker_compose['services'][$container_name]['logging'] = [ $docker_compose['services'][$container_name]['logging'] = [
'driver' => 'fluentd', 'driver' => 'fluentd',
'options' => [ 'options' => [
'fluentd-address' => "tcp://127.0.0.1:24224", 'fluentd-address' => 'tcp://127.0.0.1:24224',
'fluentd-async' => "true", 'fluentd-async' => 'true',
'fluentd-sub-second-precision' => "true", 'fluentd-sub-second-precision' => 'true',
] ],
]; ];
} }
if (count($this->database->ports_mappings_array) > 0) { if (count($this->database->ports_mappings_array) > 0) {
@ -106,15 +109,15 @@ public function handle(StandalonePostgresql $database)
$docker_compose['services'][$container_name]['volumes'][] = [ $docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind', 'type' => 'bind',
'source' => $init_script, 'source' => $init_script,
'target' => '/docker-entrypoint-initdb.d/' . basename($init_script), 'target' => '/docker-entrypoint-initdb.d/'.basename($init_script),
'read_only' => true, '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'][] = [ $docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind', 'type' => 'bind',
'source' => $this->configuration_dir . '/custom-postgres.conf', 'source' => $this->configuration_dir.'/custom-postgres.conf',
'target' => '/etc/postgresql/postgresql.conf', 'target' => '/etc/postgresql/postgresql.conf',
'read_only' => true, '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 pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo 'Database started.'"; $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');
} }
@ -141,12 +145,13 @@ private function generate_local_persistent_volumes()
$local_persistent_volumes = []; $local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) { foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) { 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 { } else {
$volume_name = $persistentStorage->name; $volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
} }
} }
return $local_persistent_volumes; return $local_persistent_volumes;
} }
@ -163,6 +168,7 @@ private function generate_local_persistent_volumes_only_volume_names()
'external' => false, 'external' => false,
]; ];
} }
return $local_persistent_volumes_names; 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()) { if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_DB'))->isEmpty()) {
$environment_variables->push("POSTGRES_DB={$this->database->postgres_db}"); $environment_variables->push("POSTGRES_DB={$this->database->postgres_db}");
} }
return $environment_variables->all(); return $environment_variables->all();
} }
@ -203,6 +210,7 @@ private function generate_init_scripts()
$this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}"; $this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
} }
} }
private function add_custom_conf() private function add_custom_conf()
{ {
if (is_null($this->database->postgres_conf) || empty($this->database->postgres_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'; $filename = 'custom-postgres.conf';
$content = $this->database->postgres_conf; $content = $this->database->postgres_conf;
if (!str($content)->contains('listen_addresses')) { if (! str($content)->contains('listen_addresses')) {
$content .= "\nlisten_addresses = '*'"; $content .= "\nlisten_addresses = '*'";
$this->database->postgres_conf = $content; $this->database->postgres_conf = $content;
$this->database->save(); $this->database->save();

View File

@ -5,17 +5,18 @@
use App\Models\StandaloneRedis; use App\Models\StandaloneRedis;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
class StartRedis class StartRedis
{ {
use AsAction; use AsAction;
public StandaloneRedis $database; public StandaloneRedis $database;
public array $commands = [];
public string $configuration_dir;
public array $commands = [];
public string $configuration_dir;
public function handle(StandaloneRedis $database) public function handle(StandaloneRedis $database)
{ {
@ -24,7 +25,7 @@ public function handle(StandaloneRedis $database)
$startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes"; $startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name; $this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [ $this->commands = [
"echo 'Starting {$database->name}.'", "echo 'Starting {$database->name}.'",
@ -55,12 +56,12 @@ public function handle(StandaloneRedis $database)
'test' => [ 'test' => [
'CMD-SHELL', 'CMD-SHELL',
'redis-cli', 'redis-cli',
'ping' 'ping',
], ],
'interval' => '5s', 'interval' => '5s',
'timeout' => '5s', 'timeout' => '5s',
'retries' => 10, 'retries' => 10,
'start_period' => '5s' 'start_period' => '5s',
], ],
'mem_limit' => $this->database->limits_memory, 'mem_limit' => $this->database->limits_memory,
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
@ -68,27 +69,27 @@ public function handle(StandaloneRedis $database)
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (float) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ],
], ],
'networks' => [ 'networks' => [
$this->database->destination->network => [ $this->database->destination->network => [
'external' => true, 'external' => true,
'name' => $this->database->destination->network, 'name' => $this->database->destination->network,
'attachable' => true, '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); data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
} }
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) { if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
$docker_compose['services'][$container_name]['logging'] = [ $docker_compose['services'][$container_name]['logging'] = [
'driver' => 'fluentd', 'driver' => 'fluentd',
'options' => [ 'options' => [
'fluentd-address' => "tcp://127.0.0.1:24224", 'fluentd-address' => 'tcp://127.0.0.1:24224',
'fluentd-async' => "true", 'fluentd-async' => 'true',
'fluentd-sub-second-precision' => "true", 'fluentd-sub-second-precision' => 'true',
] ],
]; ];
} }
if (count($this->database->ports_mappings_array) > 0) { if (count($this->database->ports_mappings_array) > 0) {
@ -105,10 +106,10 @@ public function handle(StandaloneRedis $database)
if (count($volume_names) > 0) { if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names; $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'][] = [ $docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind', 'type' => 'bind',
'source' => $this->configuration_dir . '/redis.conf', 'source' => $this->configuration_dir.'/redis.conf',
'target' => '/usr/local/etc/redis/redis.conf', 'target' => '/usr/local/etc/redis/redis.conf',
'read_only' => true, '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 pull";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
$this->commands[] = "echo 'Database started.'"; $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');
} }
@ -131,12 +133,13 @@ private function generate_local_persistent_volumes()
$local_persistent_volumes = []; $local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) { foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) { 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 { } else {
$volume_name = $persistentStorage->name; $volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
} }
} }
return $local_persistent_volumes; return $local_persistent_volumes;
} }
@ -153,6 +156,7 @@ private function generate_local_persistent_volumes_only_volume_names()
'external' => false, 'external' => false,
]; ];
} }
return $local_persistent_volumes_names; return $local_persistent_volumes_names;
} }
@ -169,6 +173,7 @@ private function generate_environment_variables()
return $environment_variables->all(); return $environment_variables->all();
} }
private function add_custom_redis() private function add_custom_redis()
{ {
if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) { if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) {

View File

@ -19,7 +19,7 @@ class StopDatabase
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database) public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
{ {
$server = $database->destination->server; $server = $database->destination->server;
if (!$server->isFunctional()) { if (! $server->isFunctional()) {
return 'Server is not functional'; return 'Server is not functional';
} }
instant_remote_process( instant_remote_process(

View File

@ -17,7 +17,9 @@
class GetContainersStatus class GetContainersStatus
{ {
use AsAction; use AsAction;
public $applications; public $applications;
public $server; public $server;
public function handle(Server $server) public function handle(Server $server)
@ -26,9 +28,9 @@ public function handle(Server $server)
// $server = Server::find(0); // $server = Server::find(0);
// } // }
$this->server = $server; $this->server = $server;
if (!$this->server->isFunctional()) { if (! $this->server->isFunctional()) {
return 'Server is not ready.'; return 'Server is not ready.';
}; }
$this->applications = $this->server->applications(); $this->applications = $this->server->applications();
$skip_these_applications = collect([]); $skip_these_applications = collect([]);
foreach ($this->applications as $application) { 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) { $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(); $this->old_way();
// if ($this->server->isSwarm()) { // if ($this->server->isSwarm()) {
@ -133,7 +135,7 @@ private function sentinel()
return data_get($value, 'name') === "$uuid-proxy"; return data_get($value, 'name') === "$uuid-proxy";
} }
})->first(); })->first();
if (!$foundTcpProxy) { if (! $foundTcpProxy) {
StartDatabaseProxy::run($service_db); StartDatabaseProxy::run($service_db);
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server)); // $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"; return data_get($value, 'name') === "$uuid-proxy";
} }
})->first(); })->first();
if (!$foundTcpProxy) { if (! $foundTcpProxy) {
StartDatabaseProxy::run($database); StartDatabaseProxy::run($database);
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
} }
@ -177,7 +179,7 @@ private function sentinel()
$subType = data_get($labels, 'coolify.service.subType'); $subType = data_get($labels, 'coolify.service.subType');
$subId = data_get($labels, 'coolify.service.subId'); $subId = data_get($labels, 'coolify.service.subId');
$service = $services->where('id', $serviceLabelId)->first(); $service = $services->where('id', $serviceLabelId)->first();
if (!$service) { if (! $service) {
continue; continue;
} }
if ($subType === 'application') { if ($subType === 'application') {
@ -239,7 +241,7 @@ private function sentinel()
$environmentName = data_get($service, 'environment.name'); $environmentName = data_get($service, 'environment.name');
if ($projectUuid && $serviceUuid && $environmentName) { if ($projectUuid && $serviceUuid && $environmentName) {
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid; $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
} else { } else {
$url = null; $url = null;
} }
@ -265,7 +267,7 @@ private function sentinel()
$environment = data_get($application, 'environment.name'); $environment = data_get($application, 'environment.name');
if ($projectUuid && $applicationUuid && $environment) { if ($projectUuid && $applicationUuid && $environment) {
$url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid; $url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
} else { } else {
$url = null; $url = null;
} }
@ -290,7 +292,7 @@ private function sentinel()
$applicationUuid = data_get($preview, 'application.uuid'); $applicationUuid = data_get($preview, 'application.uuid');
if ($projectUuid && $applicationUuid && $environmentName) { if ($projectUuid && $applicationUuid && $environmentName) {
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid; $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
} else { } else {
$url = null; $url = null;
} }
@ -315,7 +317,7 @@ private function sentinel()
$databaseUuid = data_get($database, 'uuid'); $databaseUuid = data_get($database, 'uuid');
if ($projectUuid && $databaseUuid && $environmentName) { if ($projectUuid && $databaseUuid && $environmentName) {
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid; $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
} else { } else {
$url = null; $url = null;
} }
@ -332,7 +334,7 @@ private function sentinel()
return data_get($value, 'name') === 'coolify-proxy'; return data_get($value, 'name') === 'coolify-proxy';
} }
})->first(); })->first();
if (!$foundProxyContainer) { if (! $foundProxyContainer) {
try { try {
$shouldStart = CheckProxy::run($this->server); $shouldStart = CheckProxy::run($this->server);
if ($shouldStart) { if ($shouldStart) {
@ -351,9 +353,11 @@ private function sentinel()
} catch (\Exception $e) { } catch (\Exception $e) {
// send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage()); // send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
return handleError($e); return handleError($e);
} }
} }
private function old_way() private function old_way()
{ {
if ($this->server->isSwarm()) { if ($this->server->isSwarm()) {
@ -361,8 +365,8 @@ private function old_way()
$containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false); $containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
} else { } else {
// Precheck for containers // Precheck for containers
$containers = instant_remote_process(["docker container ls -q"], $this->server, false); $containers = instant_remote_process(['docker container ls -q'], $this->server, false);
if (!$containers) { if (! $containers) {
return; return;
} }
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false); $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'); data_set($container, 'State.Health.Status', 'unhealthy');
} }
} }
return $container; return $container;
}); });
} }
@ -463,7 +468,7 @@ private function old_way()
return data_get($value, 'Name') === "/$uuid-proxy"; return data_get($value, 'Name') === "/$uuid-proxy";
} }
})->first(); })->first();
if (!$foundTcpProxy) { if (! $foundTcpProxy) {
StartDatabaseProxy::run($service_db); StartDatabaseProxy::run($service_db);
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server)); // $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"; return data_get($value, 'Name') === "/$uuid-proxy";
} }
})->first(); })->first();
if (!$foundTcpProxy) { if (! $foundTcpProxy) {
StartDatabaseProxy::run($database); StartDatabaseProxy::run($database);
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server)); $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
} }
@ -507,7 +512,7 @@ private function old_way()
$subType = data_get($labels, 'coolify.service.subType'); $subType = data_get($labels, 'coolify.service.subType');
$subId = data_get($labels, 'coolify.service.subId'); $subId = data_get($labels, 'coolify.service.subId');
$service = $services->where('id', $serviceLabelId)->first(); $service = $services->where('id', $serviceLabelId)->first();
if (!$service) { if (! $service) {
continue; continue;
} }
if ($subType === 'application') { if ($subType === 'application') {
@ -569,7 +574,7 @@ private function old_way()
$environmentName = data_get($service, 'environment.name'); $environmentName = data_get($service, 'environment.name');
if ($projectUuid && $serviceUuid && $environmentName) { if ($projectUuid && $serviceUuid && $environmentName) {
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid; $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
} else { } else {
$url = null; $url = null;
} }
@ -595,7 +600,7 @@ private function old_way()
$environment = data_get($application, 'environment.name'); $environment = data_get($application, 'environment.name');
if ($projectUuid && $applicationUuid && $environment) { if ($projectUuid && $applicationUuid && $environment) {
$url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid; $url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
} else { } else {
$url = null; $url = null;
} }
@ -620,7 +625,7 @@ private function old_way()
$applicationUuid = data_get($preview, 'application.uuid'); $applicationUuid = data_get($preview, 'application.uuid');
if ($projectUuid && $applicationUuid && $environmentName) { if ($projectUuid && $applicationUuid && $environmentName) {
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid; $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
} else { } else {
$url = null; $url = null;
} }
@ -645,7 +650,7 @@ private function old_way()
$databaseUuid = data_get($database, 'uuid'); $databaseUuid = data_get($database, 'uuid');
if ($projectUuid && $databaseUuid && $environmentName) { if ($projectUuid && $databaseUuid && $environmentName) {
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid; $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
} else { } else {
$url = null; $url = null;
} }
@ -661,7 +666,7 @@ private function old_way()
return data_get($value, 'Name') === '/coolify-proxy'; return data_get($value, 'Name') === '/coolify-proxy';
} }
})->first(); })->first();
if (!$foundProxyContainer) { if (! $foundProxyContainer) {
try { try {
$shouldStart = CheckProxy::run($this->server); $shouldStart = CheckProxy::run($this->server);
if ($shouldStart) { if ($shouldStart) {

View File

@ -21,7 +21,7 @@ class CreateNewUser implements CreatesNewUsers
public function create(array $input): User public function create(array $input): User
{ {
$settings = InstanceSettings::get(); $settings = InstanceSettings::get();
if (!$settings->is_registration_enabled) { if (! $settings->is_registration_enabled) {
abort(403); abort(403);
} }
Validator::make($input, [ Validator::make($input, [
@ -66,6 +66,7 @@ public function create(array $input): User
} }
// Set session variable // Set session variable
session(['currentTeam' => $user->currentTeam = $team]); session(['currentTeam' => $user->currentTeam = $team]);
return $user; return $user;
} }
} }

View File

@ -6,10 +6,10 @@
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
class CheckResaleLicense class CheckResaleLicense
{ {
use AsAction; use AsAction;
public function handle() public function handle()
{ {
try { try {
@ -18,6 +18,7 @@ public function handle()
$settings->update([ $settings->update([
'is_resale_license_active' => true, 'is_resale_license_active' => true,
]); ]);
return; return;
} }
// if (!$settings->resale_license) { // if (!$settings->resale_license) {
@ -38,6 +39,7 @@ public function handle()
$settings->update([ $settings->update([
'is_resale_license_active' => true, 'is_resale_license_active' => true,
]); ]);
return; return;
} }
$data = Http::withHeaders([ $data = Http::withHeaders([
@ -51,6 +53,7 @@ public function handle()
$settings->update([ $settings->update([
'is_resale_license_active' => true, 'is_resale_license_active' => true,
]); ]);
return; return;
} }
if (data_get($data, 'license_key.status') === 'active') { if (data_get($data, 'license_key.status') === 'active') {

View File

@ -2,13 +2,14 @@
namespace App\Actions\Proxy; namespace App\Actions\Proxy;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\Server; use App\Models\Server;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Lorisleiva\Actions\Concerns\AsAction;
class CheckConfiguration class CheckConfiguration
{ {
use AsAction; use AsAction;
public function handle(Server $server, bool $reset = false) public function handle(Server $server, bool $reset = false)
{ {
$proxyType = $server->proxyType(); $proxyType = $server->proxyType();
@ -22,12 +23,13 @@ public function handle(Server $server, bool $reset = false)
]; ];
$proxy_configuration = instant_remote_process($payload, $server, 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; $proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
} }
if (!$proxy_configuration || is_null($proxy_configuration)) { if (! $proxy_configuration || is_null($proxy_configuration)) {
throw new \Exception("Could not generate proxy configuration"); throw new \Exception('Could not generate proxy configuration');
} }
return $proxy_configuration; return $proxy_configuration;
} }
} }

View File

@ -8,9 +8,10 @@
class CheckProxy class CheckProxy
{ {
use AsAction; use AsAction;
public function handle(Server $server, $fromUI = false) public function handle(Server $server, $fromUI = false)
{ {
if (!$server->isFunctional()) { if (! $server->isFunctional()) {
return false; return false;
} }
if ($server->isBuildServer()) { if ($server->isBuildServer()) {
@ -18,6 +19,7 @@ public function handle(Server $server, $fromUI = false)
$server->proxy = null; $server->proxy = null;
$server->save(); $server->save();
} }
return false; return false;
} }
$proxyType = $server->proxyType(); $proxyType = $server->proxyType();
@ -25,12 +27,12 @@ public function handle(Server $server, $fromUI = false)
return false; return false;
} }
['uptime' => $uptime, 'error' => $error] = $server->validateConnection(); ['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
if (!$uptime) { if (! $uptime) {
throw new \Exception($error); throw new \Exception($error);
} }
if (!$server->isProxyShouldRun()) { if (! $server->isProxyShouldRun()) {
if ($fromUI) { 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 { } else {
return false; return false;
} }
@ -42,12 +44,14 @@ public function handle(Server $server, $fromUI = false)
if ($status === 'running') { if ($status === 'running') {
return false; return false;
} }
return true; return true;
} else { } else {
$status = getContainerStatus($server, 'coolify-proxy'); $status = getContainerStatus($server, 'coolify-proxy');
if ($status === 'running') { if ($status === 'running') {
$server->proxy->set('status', 'running'); $server->proxy->set('status', 'running');
$server->save(); $server->save();
return false; return false;
} }
if ($server->settings->is_cloudflare_tunnel) { if ($server->settings->is_cloudflare_tunnel) {
@ -76,6 +80,7 @@ public function handle(Server $server, $fromUI = false)
return false; return false;
} }
} }
return true; return true;
} }
} }

View File

@ -11,6 +11,7 @@
class StartProxy class StartProxy
{ {
use AsAction; use AsAction;
public function handle(Server $server, bool $async = true): string|Activity public function handle(Server $server, bool $async = true): string|Activity
{ {
try { try {
@ -21,8 +22,8 @@ public function handle(Server $server, bool $async = true): string|Activity
$commands = collect([]); $commands = collect([]);
$proxy_path = $server->proxyPath(); $proxy_path = $server->proxyPath();
$configuration = CheckConfiguration::run($server); $configuration = CheckConfiguration::run($server);
if (!$configuration) { if (! $configuration) {
throw new \Exception("Configuration is not synced"); throw new \Exception('Configuration is not synced');
} }
SaveConfiguration::run($server, $configuration); SaveConfiguration::run($server, $configuration);
$docker_compose_yml_base64 = base64_encode($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", "cd $proxy_path",
"echo 'Creating required Docker Compose file.'", "echo 'Creating required Docker Compose file.'",
"echo 'Starting coolify-proxy.'", "echo 'Starting coolify-proxy.'",
"docker stack deploy -c docker-compose.yml coolify-proxy", 'docker stack deploy -c docker-compose.yml coolify-proxy',
"echo 'Proxy started successfully.'" "echo 'Proxy started successfully.'",
]); ]);
} else { } else {
$caddfile = "import /dynamic/*.caddy"; $caddfile = 'import /dynamic/*.caddy';
$commands = $commands->merge([ $commands = $commands->merge([
"mkdir -p $proxy_path/dynamic", "mkdir -p $proxy_path/dynamic",
"cd $proxy_path", "cd $proxy_path",
@ -47,16 +48,17 @@ public function handle(Server $server, bool $async = true): string|Activity
"echo 'Pulling docker image.'", "echo 'Pulling docker image.'",
'docker compose pull', 'docker compose pull',
"echo 'Stopping existing coolify-proxy.'", "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.'", "echo 'Starting coolify-proxy.'",
'docker compose up -d --remove-orphans', 'docker compose up -d --remove-orphans',
"echo 'Proxy started successfully.'" "echo 'Proxy started successfully.'",
]); ]);
$commands = $commands->merge(connectProxyToNetworks($server)); $commands = $commands->merge(connectProxyToNetworks($server));
} }
if ($async) { if ($async) {
$activity = remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server); $activity = remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server);
return $activity; return $activity;
} else { } else {
instant_remote_process($commands, $server); 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->proxy->set('type', $proxyType);
$server->save(); $server->save();
ProxyStarted::dispatch($server); ProxyStarted::dispatch($server);
return 'OK'; return 'OK';
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {

View File

@ -2,12 +2,13 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\Server; use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
class CleanupDocker class CleanupDocker
{ {
use AsAction; use AsAction;
public function handle(Server $server, bool $force = true) public function handle(Server $server, bool $force = true)
{ {
if ($force) { if ($force) {

View File

@ -9,18 +9,19 @@
class ConfigureCloudflared class ConfigureCloudflared
{ {
use AsAction; use AsAction;
public function handle(Server $server, string $cloudflare_token) public function handle(Server $server, string $cloudflare_token)
{ {
try { try {
$config = [ $config = [
"services" => [ 'services' => [
"coolify-cloudflared" => [ 'coolify-cloudflared' => [
"container_name" => "coolify-cloudflared", 'container_name' => 'coolify-cloudflared',
"image" => "cloudflare/cloudflared:latest", 'image' => 'cloudflare/cloudflared:latest',
"restart" => RESTART_MODE, 'restart' => RESTART_MODE,
"network_mode" => "host", 'network_mode' => 'host',
"command" => "tunnel run", 'command' => 'tunnel run',
"environment" => [ 'environment' => [
"TUNNEL_TOKEN={$cloudflare_token}", "TUNNEL_TOKEN={$cloudflare_token}",
], ],
], ],
@ -29,12 +30,12 @@ public function handle(Server $server, string $cloudflare_token)
$config = Yaml::dump($config, 12, 2); $config = Yaml::dump($config, 12, 2);
$docker_compose_yml_base64 = base64_encode($config); $docker_compose_yml_base64 = base64_encode($config);
$commands = collect([ $commands = collect([
"mkdir -p /tmp/cloudflared", 'mkdir -p /tmp/cloudflared',
"cd /tmp/cloudflared", 'cd /tmp/cloudflared',
"echo '$docker_compose_yml_base64' | base64 -d | tee docker-compose.yml > /dev/null", "echo '$docker_compose_yml_base64' | base64 -d | tee docker-compose.yml > /dev/null",
"docker compose pull", 'docker compose pull',
"docker compose down -v --remove-orphans > /dev/null 2>&1", 'docker compose down -v --remove-orphans > /dev/null 2>&1',
"docker compose up -d --remove-orphans", 'docker compose up -d --remove-orphans',
]); ]);
instant_remote_process($commands, $server); instant_remote_process($commands, $server);
} catch (\Throwable $e) { } catch (\Throwable $e) {
@ -42,7 +43,7 @@ public function handle(Server $server, string $cloudflare_token)
throw $e; throw $e;
} finally { } finally {
$commands = collect([ $commands = collect([
"rm -fr /tmp/cloudflared", 'rm -fr /tmp/cloudflared',
]); ]);
instant_remote_process($commands, $server); instant_remote_process($commands, $server);
} }

View File

@ -2,20 +2,21 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\Server; use App\Models\Server;
use App\Models\StandaloneDocker; use App\Models\StandaloneDocker;
use Lorisleiva\Actions\Concerns\AsAction;
class InstallDocker class InstallDocker
{ {
use AsAction; use AsAction;
public function handle(Server $server) public function handle(Server $server)
{ {
$supported_os_type = $server->validateOS(); $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: <a target="_blank" class="underline" href="https://coolify.io/docs/installation#manually">documentation</a>.'); throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/installation#manually">documentation</a>.');
} }
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'; $dockerVersion = '24.0';
$config = base64_encode('{ $config = base64_encode('{
"log-driver": "json-file", "log-driver": "json-file",
@ -36,40 +37,41 @@ public function handle(Server $server)
if (isDev() && $server->id === 0) { if (isDev() && $server->id === 0) {
$command = $command->merge([ $command = $command->merge([
"echo 'Installing Prerequisites...'", "echo 'Installing Prerequisites...'",
"sleep 1", 'sleep 1',
"echo 'Installing Docker Engine...'", "echo 'Installing Docker Engine...'",
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'", "echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
"sleep 4", 'sleep 4',
"echo 'Restarting Docker Engine...'", "echo 'Restarting Docker Engine...'",
"ls -l /tmp" 'ls -l /tmp',
]); ]);
return remote_process($command, $server); return remote_process($command, $server);
} else { } else {
if ($supported_os_type->contains('debian')) { if ($supported_os_type->contains('debian')) {
$command = $command->merge([ $command = $command->merge([
"echo 'Installing Prerequisites...'", "echo 'Installing Prerequisites...'",
"apt-get update -y", 'apt-get update -y',
"command -v curl >/dev/null || apt install -y curl", 'command -v curl >/dev/null || apt install -y curl',
"command -v wget >/dev/null || apt install -y wget", 'command -v wget >/dev/null || apt install -y wget',
"command -v git >/dev/null || apt install -y git", 'command -v git >/dev/null || apt install -y git',
"command -v jq >/dev/null || apt install -y jq", '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([ $command = $command->merge([
"echo 'Installing Prerequisites...'", "echo 'Installing Prerequisites...'",
"command -v curl >/dev/null || dnf install -y curl", 'command -v curl >/dev/null || dnf install -y curl',
"command -v wget >/dev/null || dnf install -y wget", 'command -v wget >/dev/null || dnf install -y wget',
"command -v git >/dev/null || dnf install -y git", 'command -v git >/dev/null || dnf install -y git',
"command -v jq >/dev/null || dnf install -y jq", '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([ $command = $command->merge([
"echo 'Installing Prerequisites...'", "echo 'Installing Prerequisites...'",
"zypper update -y", 'zypper update -y',
"command -v curl >/dev/null || zypper install -y curl", 'command -v curl >/dev/null || zypper install -y curl',
"command -v wget >/dev/null || zypper install -y wget", 'command -v wget >/dev/null || zypper install -y wget',
"command -v git >/dev/null || zypper install -y git", 'command -v git >/dev/null || zypper install -y git',
"command -v jq >/dev/null || zypper install -y jq", 'command -v jq >/dev/null || zypper install -y jq',
]); ]);
} else { } else {
throw new \Exception('Unsupported OS'); throw new \Exception('Unsupported OS');
@ -78,29 +80,30 @@ public function handle(Server $server)
"echo 'Installing Docker Engine...'", "echo 'Installing Docker Engine...'",
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}", "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)...'", "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", "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", "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", '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", '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", "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...'", "echo 'Restarting Docker Engine...'",
"systemctl enable docker >/dev/null 2>&1 || true", 'systemctl enable docker >/dev/null 2>&1 || true',
"systemctl restart docker", 'systemctl restart docker',
]); ]);
if ($server->isSwarm()) { if ($server->isSwarm()) {
$command = $command->merge([ $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 { } else {
$command = $command->merge([ $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([ $command = $command->merge([
"echo 'Done!'", "echo 'Done!'",
]); ]);
} }
return remote_process($command, $server); return remote_process($command, $server);
} }
} }

View File

@ -2,21 +2,22 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\Server; use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
class InstallLogDrain class InstallLogDrain
{ {
use AsAction; use AsAction;
public function handle(Server $server) public function handle(Server $server)
{ {
if ($server->settings->is_logdrain_newrelic_enabled) { if ($server->settings->is_logdrain_newrelic_enabled) {
$type = 'newrelic'; $type = 'newrelic';
} else if ($server->settings->is_logdrain_highlight_enabled) { } elseif ($server->settings->is_logdrain_highlight_enabled) {
$type = 'highlight'; $type = 'highlight';
} else if ($server->settings->is_logdrain_axiom_enabled) { } elseif ($server->settings->is_logdrain_axiom_enabled) {
$type = 'axiom'; $type = 'axiom';
} else if ($server->settings->is_logdrain_custom_enabled) { } elseif ($server->settings->is_logdrain_custom_enabled) {
$type = 'custom'; $type = 'custom';
} else { } else {
$type = 'none'; $type = 'none';
@ -25,11 +26,12 @@ public function handle(Server $server)
if ($type === 'none') { if ($type === 'none') {
$command = [ $command = [
"echo 'Stopping old Fluent Bit'", "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); return instant_remote_process($command, $server);
} else if ($type === 'newrelic') { } elseif ($type === 'newrelic') {
if (!$server->settings->is_logdrain_newrelic_enabled) { if (! $server->settings->is_logdrain_newrelic_enabled) {
throw new \Exception('New Relic log drain is not enabled.'); throw new \Exception('New Relic log drain is not enabled.');
} }
$config = base64_encode(" $config = base64_encode("
@ -59,11 +61,11 @@ public function handle(Server $server)
# https://log-api.newrelic.com/log/v1 - US # https://log-api.newrelic.com/log/v1 - US
base_uri \${BASE_URI} base_uri \${BASE_URI}
"); ");
} else if ($type === 'highlight') { } elseif ($type === 'highlight') {
if (!$server->settings->is_logdrain_highlight_enabled) { if (! $server->settings->is_logdrain_highlight_enabled) {
throw new \Exception('Highlight log drain is not enabled.'); throw new \Exception('Highlight log drain is not enabled.');
} }
$config = base64_encode(" $config = base64_encode('
[SERVICE] [SERVICE]
Flush 5 Flush 5
Daemon off Daemon off
@ -71,7 +73,7 @@ public function handle(Server $server)
Parsers_File parsers.conf Parsers_File parsers.conf
[INPUT] [INPUT]
Name forward Name forward
tag \${HIGHLIGHT_PROJECT_ID} tag ${HIGHLIGHT_PROJECT_ID}
Buffer_Chunk_Size 1M Buffer_Chunk_Size 1M
Buffer_Max_Size 6M Buffer_Max_Size 6M
[OUTPUT] [OUTPUT]
@ -79,9 +81,9 @@ public function handle(Server $server)
Match * Match *
Host otel.highlight.io Host otel.highlight.io
Port 24224 Port 24224
"); ');
} else if ($type === 'axiom') { } elseif ($type === 'axiom') {
if (!$server->settings->is_logdrain_axiom_enabled) { if (! $server->settings->is_logdrain_axiom_enabled) {
throw new \Exception('Axiom log drain is not enabled.'); throw new \Exception('Axiom log drain is not enabled.');
} }
$config = base64_encode(" $config = base64_encode("
@ -116,8 +118,8 @@ public function handle(Server $server)
json_date_format iso8601 json_date_format iso8601
tls On tls On
"); ");
} else if ($type === 'custom') { } elseif ($type === 'custom') {
if (!$server->settings->is_logdrain_custom_enabled) { if (! $server->settings->is_logdrain_custom_enabled) {
throw new \Exception('Custom log drain is not enabled.'); throw new \Exception('Custom log drain is not enabled.');
} }
$config = base64_encode($server->settings->logdrain_custom_config); $config = base64_encode($server->settings->logdrain_custom_config);
@ -133,7 +135,7 @@ public function handle(Server $server)
Regex /^(?!\s*$).+/ Regex /^(?!\s*$).+/
"); ");
} }
$compose = base64_encode(" $compose = base64_encode('
services: services:
coolify-log-drain: coolify-log-drain:
image: cr.fluentbit.io/fluent/fluent-bit:2.0 image: cr.fluentbit.io/fluent/fluent-bit:2.0
@ -147,7 +149,7 @@ public function handle(Server $server)
ports: ports:
- 127.0.0.1:24224:24224 - 127.0.0.1:24224:24224
restart: unless-stopped restart: unless-stopped
"); ');
$readme = base64_encode('# New Relic Log Drain $readme = base64_encode('# New Relic Log Drain
This log drain is based on [Fluent Bit](https://fluentbit.io/) and New Relic Log Forwarder. 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_uri = $server->settings->logdrain_newrelic_base_uri;
$base_path = config('coolify.base_config_path'); $base_path = config('coolify.base_config_path');
$config_path = $base_path . '/log-drains'; $config_path = $base_path.'/log-drains';
$fluent_bit_config = $config_path . '/fluent-bit.conf'; $fluent_bit_config = $config_path.'/fluent-bit.conf';
$parsers_config = $config_path . '/parsers.conf'; $parsers_config = $config_path.'/parsers.conf';
$compose_path = $config_path . '/docker-compose.yml'; $compose_path = $config_path.'/docker-compose.yml';
$readme_path = $config_path . '/README.md'; $readme_path = $config_path.'/README.md';
$command = [ $command = [
"echo 'Saving configuration'", "echo 'Saving configuration'",
"mkdir -p $config_path", "mkdir -p $config_path",
@ -180,18 +182,18 @@ public function handle(Server $server)
"echo LICENSE_KEY=$license_key >> $config_path/.env", "echo LICENSE_KEY=$license_key >> $config_path/.env",
"echo BASE_URI=$base_uri >> $config_path/.env", "echo BASE_URI=$base_uri >> $config_path/.env",
]; ];
} else if ($type === 'highlight') { } elseif ($type === 'highlight') {
$add_envs_command = [ $add_envs_command = [
"echo HIGHLIGHT_PROJECT_ID={$server->settings->logdrain_highlight_project_id} >> $config_path/.env", "echo HIGHLIGHT_PROJECT_ID={$server->settings->logdrain_highlight_project_id} >> $config_path/.env",
]; ];
} else if ($type === 'axiom') { } elseif ($type === 'axiom') {
$add_envs_command = [ $add_envs_command = [
"echo AXIOM_DATASET_NAME={$server->settings->logdrain_axiom_dataset_name} >> $config_path/.env", "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", "echo AXIOM_API_KEY={$server->settings->logdrain_axiom_api_key} >> $config_path/.env",
]; ];
} else if ($type === 'custom') { } elseif ($type === 'custom') {
$add_envs_command = [ $add_envs_command = [
"touch $config_path/.env" "touch $config_path/.env",
]; ];
} else { } else {
throw new \Exception('Unknown log drain type.'); 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", "cd $config_path && docker compose up -d --remove-orphans",
]; ];
$command = array_merge($command, $add_envs_command, $restart_command); $command = array_merge($command, $add_envs_command, $restart_command);
return instant_remote_process($command, $server); return instant_remote_process($command, $server);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e); return handleError($e);

View File

@ -2,12 +2,13 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\Server; use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
class StartSentinel class StartSentinel
{ {
use AsAction; use AsAction;
public function handle(Server $server, $version = 'latest', bool $restart = false) public function handle(Server $server, $version = 'latest', bool $restart = false)
{ {
if ($restart) { if ($restart) {
@ -15,8 +16,8 @@ public function handle(Server $server, $version = 'latest', bool $restart = fals
} }
instant_remote_process([ 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", "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", 'chown -R 9999:root /data/coolify/metrics /data/coolify/logs',
"chmod -R 700 /data/coolify/metrics /data/coolify/logs" 'chmod -R 700 /data/coolify/metrics /data/coolify/logs',
], $server, false); ], $server, false);
} }
} }

View File

@ -2,15 +2,18 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\InstanceSettings; use App\Models\InstanceSettings;
use App\Models\Server; use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
class UpdateCoolify class UpdateCoolify
{ {
use AsAction; use AsAction;
public ?Server $server = null; public ?Server $server = null;
public ?string $latestVersion = null; public ?string $latestVersion = null;
public ?string $currentVersion = null; public ?string $currentVersion = null;
public function handle($manual_update = false) public function handle($manual_update = false)
@ -19,14 +22,14 @@ public function handle($manual_update = false)
$settings = InstanceSettings::get(); $settings = InstanceSettings::get();
ray('Running InstanceAutoUpdateJob'); ray('Running InstanceAutoUpdateJob');
$this->server = Server::find(0); $this->server = Server::find(0);
if (!$this->server) { if (! $this->server) {
return; return;
} }
CleanupDocker::run($this->server, false); CleanupDocker::run($this->server, false);
$this->latestVersion = get_latest_version_of_coolify(); $this->latestVersion = get_latest_version_of_coolify();
$this->currentVersion = config('version'); $this->currentVersion = config('version');
if (!$manual_update) { if (! $manual_update) {
if (!$settings->is_auto_update_enabled) { if (! $settings->is_auto_update_enabled) {
return; return;
} }
if ($this->latestVersion === $this->currentVersion) { if ($this->latestVersion === $this->currentVersion) {
@ -46,14 +49,15 @@ private function update()
{ {
if (isDev()) { if (isDev()) {
remote_process([ remote_process([
"sleep 10" 'sleep 10',
], $this->server); ], $this->server);
return; return;
} }
remote_process([ remote_process([
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh", 'curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh',
"bash /data/coolify/source/upgrade.sh $this->latestVersion" "bash /data/coolify/source/upgrade.sh $this->latestVersion",
], $this->server); ], $this->server);
return;
} }
} }

View File

@ -2,12 +2,13 @@
namespace App\Actions\Service; namespace App\Actions\Service;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\Service; use App\Models\Service;
use Lorisleiva\Actions\Concerns\AsAction;
class DeleteService class DeleteService
{ {
use AsAction; use AsAction;
public function handle(Service $service) public function handle(Service $service)
{ {
try { try {

View File

@ -2,26 +2,27 @@
namespace App\Actions\Service; namespace App\Actions\Service;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\Service; use App\Models\Service;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
class StartService class StartService
{ {
use AsAction; use AsAction;
public function handle(Service $service) public function handle(Service $service)
{ {
ray('Starting service: ' . $service->name); ray('Starting service: '.$service->name);
$service->saveComposeConfigs(); $service->saveComposeConfigs();
$commands[] = "cd " . $service->workdir(); $commands[] = 'cd '.$service->workdir();
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'"; $commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
$commands[] = "echo 'Creating Docker network.'"; $commands[] = "echo 'Creating Docker network.'";
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid"; $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[] = "echo 'Pulling images.'";
$commands[] = "docker compose pull"; $commands[] = 'docker compose pull';
$commands[] = "echo 'Starting containers.'"; $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"; $commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
if (data_get($service, 'connect_to_docker_network')) { if (data_get($service, 'connect_to_docker_network')) {
$compose = data_get($service, 'docker_compose', []); $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'); $activity = remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged');
return $activity; return $activity;
} }
} }

View File

@ -2,20 +2,21 @@
namespace App\Actions\Service; namespace App\Actions\Service;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\Service; use App\Models\Service;
use Lorisleiva\Actions\Concerns\AsAction;
class StopService class StopService
{ {
use AsAction; use AsAction;
public function handle(Service $service) public function handle(Service $service)
{ {
try { try {
$server = $service->destination->server; $server = $service->destination->server;
if (!$server->isFunctional()) { if (! $server->isFunctional()) {
return 'Server is not functional'; return 'Server is not functional';
} }
ray('Stopping service: ' . $service->name); ray('Stopping service: '.$service->name);
$applications = $service->applications()->get(); $applications = $service->applications()->get();
foreach ($applications as $application) { foreach ($applications as $application) {
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server); instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server);
@ -33,6 +34,7 @@ public function handle(Service $service)
} catch (\Exception $e) { } catch (\Exception $e) {
echo $e->getMessage(); echo $e->getMessage();
ray($e->getMessage()); ray($e->getMessage());
return $e->getMessage(); return $e->getMessage();
} }

View File

@ -8,18 +8,21 @@
class ComplexStatusCheck class ComplexStatusCheck
{ {
use AsAction; use AsAction;
public function handle(Application $application) public function handle(Application $application)
{ {
$servers = $application->additional_servers; $servers = $application->additional_servers;
$servers->push($application->destination->server); $servers->push($application->destination->server);
foreach ($servers as $server) { foreach ($servers as $server) {
$is_main_server = $application->destination->server->id === $server->id; $is_main_server = $application->destination->server->id === $server->id;
if (!$server->isFunctional()) { if (! $server->isFunctional()) {
if ($is_main_server) { if ($is_main_server) {
$application->update(['status' => 'exited:unhealthy']); $application->update(['status' => 'exited:unhealthy']);
continue; continue;
} else { } else {
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']); $application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
continue; continue;
} }
} }
@ -44,9 +47,11 @@ public function handle(Application $application)
} else { } else {
if ($is_main_server) { if ($is_main_server) {
$application->update(['status' => 'exited:unhealthy']); $application->update(['status' => 'exited:unhealthy']);
continue; continue;
} else { } else {
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']); $application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
continue; continue;
} }
} }

View File

@ -8,17 +8,20 @@
class PullImage class PullImage
{ {
use AsAction; use AsAction;
public function handle(Service $resource) public function handle(Service $resource)
{ {
$resource->saveComposeConfigs(); $resource->saveComposeConfigs();
$commands[] = "cd " . $resource->workdir(); $commands[] = 'cd '.$resource->workdir();
$commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'"; $commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'";
$commands[] = "docker compose pull"; $commands[] = 'docker compose pull';
$server = data_get($resource, 'server'); $server = data_get($resource, 'server');
if (!$server) return; if (! $server) {
return;
}
instant_remote_process($commands, $resource->server); instant_remote_process($commands, $resource->server);
} }

View File

@ -2,7 +2,6 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Models\Server;
use App\Models\User; use App\Models\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@ -29,9 +28,10 @@ public function handle()
{ {
try { try {
$email = $this->argument('email'); $email = $this->argument('email');
$confirm = $this->confirm('Are you sure you want to remove user with email: ' . $email . '?'); $confirm = $this->confirm('Are you sure you want to remove user with email: '.$email.'?');
if (!$confirm) { if (! $confirm) {
$this->info('User removal cancelled.'); $this->info('User removal cancelled.');
return; return;
} }
$this->info("Removing user with email: $email"); $this->info("Removing user with email: $email");
@ -40,6 +40,7 @@ public function handle()
foreach ($teams as $team) { foreach ($teams as $team) {
if ($team->members->count() > 1) { 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.'); $this->error('User is a member of a team with more than one member. Please remove user from team first.');
return; return;
} }
$team->delete(); $team->delete();
@ -48,6 +49,7 @@ public function handle()
} catch (\Exception $e) { } catch (\Exception $e) {
$this->error('Failed to remove user.'); $this->error('Failed to remove user.');
$this->error($e->getMessage()); $this->error($e->getMessage());
return; return;
} }
} }

View File

@ -8,6 +8,7 @@
class CleanupApplicationDeploymentQueue extends Command class CleanupApplicationDeploymentQueue extends Command
{ {
protected $signature = 'cleanup:application-deployment-queue {--team-id=}'; protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
protected $description = 'CleanupApplicationDeploymentQueue'; protected $description = 'CleanupApplicationDeploymentQueue';
public function handle() public function handle()
@ -15,10 +16,10 @@ public function handle()
$team_id = $this->option('team-id'); $team_id = $this->option('team-id');
$servers = \App\Models\Server::where('team_id', $team_id)->get(); $servers = \App\Models\Server::where('team_id', $team_id)->get();
foreach ($servers as $server) { 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) { foreach ($deployments as $deployment) {
$deployment->update(['status' => 'failed']); $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);
} }
} }
} }

View File

@ -8,6 +8,7 @@
class CleanupDatabase extends Command class CleanupDatabase extends Command
{ {
protected $signature = 'cleanup:database {--yes}'; protected $signature = 'cleanup:database {--yes}';
protected $description = 'Cleanup database'; protected $description = 'Cleanup database';
public function handle() public function handle()

View File

@ -8,6 +8,7 @@
class CleanupQueue extends Command class CleanupQueue extends Command
{ {
protected $signature = 'cleanup:queue'; protected $signature = 'cleanup:queue';
protected $description = 'Cleanup Queue'; protected $description = 'Cleanup Queue';
public function handle() public function handle()

View File

@ -20,6 +20,7 @@
class CleanupStuckedResources extends Command class CleanupStuckedResources extends Command
{ {
protected $signature = 'cleanup:stucked-resources'; protected $signature = 'cleanup:stucked-resources';
protected $description = 'Cleanup Stucked Resources'; protected $description = 'Cleanup Stucked Resources';
public function handle() public function handle()
@ -28,6 +29,7 @@ public function handle()
echo "Running cleanup stucked resources.\n"; echo "Running cleanup stucked resources.\n";
$this->cleanup_stucked_resources(); $this->cleanup_stucked_resources();
} }
private function cleanup_stucked_resources() private function cleanup_stucked_resources()
{ {
@ -142,7 +144,7 @@ private function cleanup_stucked_resources()
try { try {
$scheduled_tasks = ScheduledTask::all(); $scheduled_tasks = ScheduledTask::all();
foreach ($scheduled_tasks as $scheduled_task) { 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"; echo "Deleting stuck scheduledtask: {$scheduled_task->name}\n";
$scheduled_task->delete(); $scheduled_task->delete();
} }
@ -155,19 +157,22 @@ private function cleanup_stucked_resources()
try { try {
$applications = Application::all(); $applications = Application::all();
foreach ($applications as $application) { foreach ($applications as $application) {
if (!data_get($application, 'environment')) { if (! data_get($application, 'environment')) {
echo 'Application without environment: ' . $application->name . '\n'; echo 'Application without environment: '.$application->name.'\n';
$application->forceDelete(); $application->forceDelete();
continue; continue;
} }
if (!$application->destination()) { if (! $application->destination()) {
echo 'Application without destination: ' . $application->name . '\n'; echo 'Application without destination: '.$application->name.'\n';
$application->forceDelete(); $application->forceDelete();
continue; continue;
} }
if (!data_get($application, 'destination.server')) { if (! data_get($application, 'destination.server')) {
echo 'Application without server: ' . $application->name . '\n'; echo 'Application without server: '.$application->name.'\n';
$application->forceDelete(); $application->forceDelete();
continue; continue;
} }
} }
@ -177,19 +182,22 @@ private function cleanup_stucked_resources()
try { try {
$postgresqls = StandalonePostgresql::all()->where('id', '!=', 0); $postgresqls = StandalonePostgresql::all()->where('id', '!=', 0);
foreach ($postgresqls as $postgresql) { foreach ($postgresqls as $postgresql) {
if (!data_get($postgresql, 'environment')) { if (! data_get($postgresql, 'environment')) {
echo 'Postgresql without environment: ' . $postgresql->name . '\n'; echo 'Postgresql without environment: '.$postgresql->name.'\n';
$postgresql->forceDelete(); $postgresql->forceDelete();
continue; continue;
} }
if (!$postgresql->destination()) { if (! $postgresql->destination()) {
echo 'Postgresql without destination: ' . $postgresql->name . '\n'; echo 'Postgresql without destination: '.$postgresql->name.'\n';
$postgresql->forceDelete(); $postgresql->forceDelete();
continue; continue;
} }
if (!data_get($postgresql, 'destination.server')) { if (! data_get($postgresql, 'destination.server')) {
echo 'Postgresql without server: ' . $postgresql->name . '\n'; echo 'Postgresql without server: '.$postgresql->name.'\n';
$postgresql->forceDelete(); $postgresql->forceDelete();
continue; continue;
} }
} }
@ -199,19 +207,22 @@ private function cleanup_stucked_resources()
try { try {
$redis = StandaloneRedis::all(); $redis = StandaloneRedis::all();
foreach ($redis as $redis) { foreach ($redis as $redis) {
if (!data_get($redis, 'environment')) { if (! data_get($redis, 'environment')) {
echo 'Redis without environment: ' . $redis->name . '\n'; echo 'Redis without environment: '.$redis->name.'\n';
$redis->forceDelete(); $redis->forceDelete();
continue; continue;
} }
if (!$redis->destination()) { if (! $redis->destination()) {
echo 'Redis without destination: ' . $redis->name . '\n'; echo 'Redis without destination: '.$redis->name.'\n';
$redis->forceDelete(); $redis->forceDelete();
continue; continue;
} }
if (!data_get($redis, 'destination.server')) { if (! data_get($redis, 'destination.server')) {
echo 'Redis without server: ' . $redis->name . '\n'; echo 'Redis without server: '.$redis->name.'\n';
$redis->forceDelete(); $redis->forceDelete();
continue; continue;
} }
} }
@ -222,19 +233,22 @@ private function cleanup_stucked_resources()
try { try {
$mongodbs = StandaloneMongodb::all(); $mongodbs = StandaloneMongodb::all();
foreach ($mongodbs as $mongodb) { foreach ($mongodbs as $mongodb) {
if (!data_get($mongodb, 'environment')) { if (! data_get($mongodb, 'environment')) {
echo 'Mongodb without environment: ' . $mongodb->name . '\n'; echo 'Mongodb without environment: '.$mongodb->name.'\n';
$mongodb->forceDelete(); $mongodb->forceDelete();
continue; continue;
} }
if (!$mongodb->destination()) { if (! $mongodb->destination()) {
echo 'Mongodb without destination: ' . $mongodb->name . '\n'; echo 'Mongodb without destination: '.$mongodb->name.'\n';
$mongodb->forceDelete(); $mongodb->forceDelete();
continue; continue;
} }
if (!data_get($mongodb, 'destination.server')) { if (! data_get($mongodb, 'destination.server')) {
echo 'Mongodb without server: ' . $mongodb->name . '\n'; echo 'Mongodb without server: '.$mongodb->name.'\n';
$mongodb->forceDelete(); $mongodb->forceDelete();
continue; continue;
} }
} }
@ -245,19 +259,22 @@ private function cleanup_stucked_resources()
try { try {
$mysqls = StandaloneMysql::all(); $mysqls = StandaloneMysql::all();
foreach ($mysqls as $mysql) { foreach ($mysqls as $mysql) {
if (!data_get($mysql, 'environment')) { if (! data_get($mysql, 'environment')) {
echo 'Mysql without environment: ' . $mysql->name . '\n'; echo 'Mysql without environment: '.$mysql->name.'\n';
$mysql->forceDelete(); $mysql->forceDelete();
continue; continue;
} }
if (!$mysql->destination()) { if (! $mysql->destination()) {
echo 'Mysql without destination: ' . $mysql->name . '\n'; echo 'Mysql without destination: '.$mysql->name.'\n';
$mysql->forceDelete(); $mysql->forceDelete();
continue; continue;
} }
if (!data_get($mysql, 'destination.server')) { if (! data_get($mysql, 'destination.server')) {
echo 'Mysql without server: ' . $mysql->name . '\n'; echo 'Mysql without server: '.$mysql->name.'\n';
$mysql->forceDelete(); $mysql->forceDelete();
continue; continue;
} }
} }
@ -268,19 +285,22 @@ private function cleanup_stucked_resources()
try { try {
$mariadbs = StandaloneMariadb::all(); $mariadbs = StandaloneMariadb::all();
foreach ($mariadbs as $mariadb) { foreach ($mariadbs as $mariadb) {
if (!data_get($mariadb, 'environment')) { if (! data_get($mariadb, 'environment')) {
echo 'Mariadb without environment: ' . $mariadb->name . '\n'; echo 'Mariadb without environment: '.$mariadb->name.'\n';
$mariadb->forceDelete(); $mariadb->forceDelete();
continue; continue;
} }
if (!$mariadb->destination()) { if (! $mariadb->destination()) {
echo 'Mariadb without destination: ' . $mariadb->name . '\n'; echo 'Mariadb without destination: '.$mariadb->name.'\n';
$mariadb->forceDelete(); $mariadb->forceDelete();
continue; continue;
} }
if (!data_get($mariadb, 'destination.server')) { if (! data_get($mariadb, 'destination.server')) {
echo 'Mariadb without server: ' . $mariadb->name . '\n'; echo 'Mariadb without server: '.$mariadb->name.'\n';
$mariadb->forceDelete(); $mariadb->forceDelete();
continue; continue;
} }
} }
@ -291,19 +311,22 @@ private function cleanup_stucked_resources()
try { try {
$services = Service::all(); $services = Service::all();
foreach ($services as $service) { foreach ($services as $service) {
if (!data_get($service, 'environment')) { if (! data_get($service, 'environment')) {
echo 'Service without environment: ' . $service->name . '\n'; echo 'Service without environment: '.$service->name.'\n';
$service->forceDelete(); $service->forceDelete();
continue; continue;
} }
if (!$service->destination()) { if (! $service->destination()) {
echo 'Service without destination: ' . $service->name . '\n'; echo 'Service without destination: '.$service->name.'\n';
$service->forceDelete(); $service->forceDelete();
continue; continue;
} }
if (!data_get($service, 'server')) { if (! data_get($service, 'server')) {
echo 'Service without server: ' . $service->name . '\n'; echo 'Service without server: '.$service->name.'\n';
$service->forceDelete(); $service->forceDelete();
continue; continue;
} }
} }
@ -313,9 +336,10 @@ private function cleanup_stucked_resources()
try { try {
$serviceApplications = ServiceApplication::all(); $serviceApplications = ServiceApplication::all();
foreach ($serviceApplications as $service) { foreach ($serviceApplications as $service) {
if (!data_get($service, 'service')) { if (! data_get($service, 'service')) {
echo 'ServiceApplication without service: ' . $service->name . '\n'; echo 'ServiceApplication without service: '.$service->name.'\n';
$service->forceDelete(); $service->forceDelete();
continue; continue;
} }
} }
@ -325,9 +349,10 @@ private function cleanup_stucked_resources()
try { try {
$serviceDatabases = ServiceDatabase::all(); $serviceDatabases = ServiceDatabase::all();
foreach ($serviceDatabases as $service) { foreach ($serviceDatabases as $service) {
if (!data_get($service, 'service')) { if (! data_get($service, 'service')) {
echo 'ServiceDatabase without service: ' . $service->name . '\n'; echo 'ServiceDatabase without service: '.$service->name.'\n';
$service->forceDelete(); $service->forceDelete();
continue; continue;
} }
} }

View File

@ -8,6 +8,7 @@
class CleanupUnreachableServers extends Command class CleanupUnreachableServers extends Command
{ {
protected $signature = 'cleanup:unreachable-servers'; protected $signature = 'cleanup:unreachable-servers';
protected $description = 'Cleanup Unreachable Servers (7 days)'; protected $description = 'Cleanup Unreachable Servers (7 days)';
public function handle() public function handle()
@ -19,7 +20,7 @@ public function handle()
echo "Cleanup unreachable server ($server->id) with name $server->name"; echo "Cleanup unreachable server ($server->id) with name $server->name";
send_internal_notification("Server $server->name is unreachable for 7 days. Cleaning up..."); send_internal_notification("Server $server->name is unreachable for 7 days. Cleaning up...");
$server->update([ $server->update([
'ip' => '1.2.3.4' 'ip' => '1.2.3.4',
]); ]);
} }
} }

View File

@ -10,6 +10,7 @@
class Dev extends Command class Dev extends Command
{ {
protected $signature = 'dev:init'; protected $signature = 'dev:init';
protected $description = 'Init the app in dev mode'; protected $description = 'Init the app in dev mode';
public function handle() public function handle()
@ -21,7 +22,7 @@ public function handle()
} }
// Seed database if it's empty // Seed database if it's empty
$settings = InstanceSettings::find(0); $settings = InstanceSettings::find(0);
if (!$settings) { if (! $settings) {
echo "Initializing instance, seeding database.\n"; echo "Initializing instance, seeding database.\n";
Artisan::call('migrate --seed'); Artisan::call('migrate --seed');
} else { } else {

View File

@ -2,12 +2,10 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Jobs\DatabaseBackupStatusJob;
use App\Jobs\SendConfirmationForWaitlistJob; use App\Jobs\SendConfirmationForWaitlistJob;
use App\Models\Application; use App\Models\Application;
use App\Models\ApplicationPreview; use App\Models\ApplicationPreview;
use App\Models\ScheduledDatabaseBackup; use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledDatabaseBackupExecution;
use App\Models\Server; use App\Models\Server;
use App\Models\StandalonePostgresql; use App\Models\StandalonePostgresql;
use App\Models\Team; use App\Models\Team;
@ -49,7 +47,9 @@ class Emails extends Command
* Execute the console command. * Execute the console command.
*/ */
private ?MailMessage $mail = null; private ?MailMessage $mail = null;
private ?string $email = null; private ?string $email = null;
public function handle() public function handle()
{ {
$type = select( $type = select(
@ -73,21 +73,22 @@ public function handle()
); );
$emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection']; $emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection'];
if (isDev()) { if (isDev()) {
$this->email = "test@example.com"; $this->email = 'test@example.com';
} else { } else {
if (!in_array($type, $emailsGathered)) { if (! in_array($type, $emailsGathered)) {
$this->email = text('Email Address to send to:'); $this->email = text('Email Address to send to:');
} }
} }
set_transanctional_email_settings(); set_transanctional_email_settings();
$this->mail = new MailMessage(); $this->mail = new MailMessage();
$this->mail->subject("Test Email"); $this->mail->subject('Test Email');
switch ($type) { switch ($type) {
case 'updates': case 'updates':
$teams = Team::all(); $teams = Team::all();
if (!$teams || $teams->isEmpty()) { if (! $teams || $teams->isEmpty()) {
echo 'No teams found.' . PHP_EOL; echo 'No teams found.'.PHP_EOL;
return; return;
} }
$emails = []; $emails = [];
@ -99,7 +100,7 @@ public function handle()
} }
} }
$emails = array_unique($emails); $emails = array_unique($emails);
$this->info("Sending to " . count($emails) . " emails."); $this->info('Sending to '.count($emails).' emails.');
foreach ($emails as $email) { foreach ($emails as $email) {
$this->info($email); $this->info($email);
} }
@ -111,7 +112,7 @@ public function handle()
$unsubscribeUrl = route('unsubscribe.marketing.emails', [ $unsubscribeUrl = route('unsubscribe.marketing.emails', [
'token' => encrypt($email), 'token' => encrypt($email),
]); ]);
$this->mail->view('emails.updates', ["unsubscribeUrl" => $unsubscribeUrl]); $this->mail->view('emails.updates', ['unsubscribeUrl' => $unsubscribeUrl]);
$this->sendEmail($email); $this->sendEmail($email);
} }
} }
@ -157,7 +158,7 @@ public function handle()
case 'application-deployment-failed': case 'application-deployment-failed':
$application = Application::all()->first(); $application = Application::all()->first();
$preview = ApplicationPreview::all()->first(); $preview = ApplicationPreview::all()->first();
if (!$preview) { if (! $preview) {
$preview = ApplicationPreview::create([ $preview = ApplicationPreview::create([
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => 1, 'pull_request_id' => 1,
@ -178,7 +179,7 @@ public function handle()
case 'backup-failed': case 'backup-failed':
$backup = ScheduledDatabaseBackup::all()->first(); $backup = ScheduledDatabaseBackup::all()->first();
$db = StandalonePostgresql::all()->first(); $db = StandalonePostgresql::all()->first();
if (!$backup) { if (! $backup) {
$backup = ScheduledDatabaseBackup::create([ $backup = ScheduledDatabaseBackup::create([
'enabled' => true, 'enabled' => true,
'frequency' => 'daily', 'frequency' => 'daily',
@ -188,14 +189,14 @@ public function handle()
'team_id' => 0, '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->mail = (new BackupFailed($backup, $db, $output))->toMail();
$this->sendEmail(); $this->sendEmail();
break; break;
case 'backup-success': case 'backup-success':
$backup = ScheduledDatabaseBackup::all()->first(); $backup = ScheduledDatabaseBackup::all()->first();
$db = StandalonePostgresql::all()->first(); $db = StandalonePostgresql::all()->first();
if (!$backup) { if (! $backup) {
$backup = ScheduledDatabaseBackup::create([ $backup = ScheduledDatabaseBackup::create([
'enabled' => true, 'enabled' => true,
'frequency' => 'daily', 'frequency' => 'daily',
@ -244,8 +245,9 @@ public function handle()
$this->mail->view('emails.before-trial-conversion'); $this->mail->view('emails.before-trial-conversion');
$this->mail->subject('Trial period has been added for all subscription plans.'); $this->mail->subject('Trial period has been added for all subscription plans.');
$teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get(); $teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get();
if (!$teams || $teams->isEmpty()) { if (! $teams || $teams->isEmpty()) {
echo 'No teams found.' . PHP_EOL; echo 'No teams found.'.PHP_EOL;
return; return;
} }
$emails = []; $emails = [];
@ -257,7 +259,7 @@ public function handle()
} }
} }
$emails = array_unique($emails); $emails = array_unique($emails);
$this->info("Sending to " . count($emails) . " emails."); $this->info('Sending to '.count($emails).' emails.');
foreach ($emails as $email) { foreach ($emails as $email) {
$this->info($email); $this->info($email);
} }
@ -271,7 +273,7 @@ public function handle()
case 'realusers-server-lost-connection': case 'realusers-server-lost-connection':
$serverId = text('Server Id'); $serverId = text('Server Id');
$server = Server::find($serverId); $server = Server::find($serverId);
if (!$server) { if (! $server) {
throw new Exception('Server not found'); throw new Exception('Server not found');
} }
$admins = []; $admins = [];
@ -281,7 +283,7 @@ public function handle()
$admins[] = $member->email; $admins[] = $member->email;
} }
} }
$this->info('Sending to ' . count($admins) . ' admins.'); $this->info('Sending to '.count($admins).' admins.');
foreach ($admins as $admin) { foreach ($admins as $admin) {
$this->info($admin); $this->info($admin);
} }
@ -289,14 +291,15 @@ public function handle()
$this->mail->view('emails.server-lost-connection', [ $this->mail->view('emails.server-lost-connection', [
'name' => $server->name, '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) { foreach ($admins as $email) {
$this->sendEmail($email); $this->sendEmail($email);
} }
break; break;
} }
} }
private function sendEmail(string $email = null)
private function sendEmail(?string $email = null)
{ {
if ($email) { if ($email) {
$this->email = $email; $this->email = $email;
@ -307,7 +310,7 @@ private function sendEmail(string $email = null)
fn (Message $message) => $message fn (Message $message) => $message
->to($this->email) ->to($this->email)
->subject($this->mail->subject) ->subject($this->mail->subject)
->html((string)$this->mail->render()) ->html((string) $this->mail->render())
); );
$this->info("Email sent to $this->email successfully. 📧"); $this->info("Email sent to $this->email successfully. 📧");
} }

View File

@ -7,7 +7,9 @@
class Horizon extends Command class Horizon extends Command
{ {
protected $signature = 'start:horizon'; protected $signature = 'start:horizon';
protected $description = 'Start Horizon'; protected $description = 'Start Horizon';
public function handle() public function handle()
{ {
if (config('coolify.is_horizon_enabled')) { if (config('coolify.is_horizon_enabled')) {

View File

@ -15,6 +15,7 @@
class Init extends Command class Init extends Command
{ {
protected $signature = 'app:init {--full-cleanup} {--cleanup-deployments}'; protected $signature = 'app:init {--full-cleanup} {--cleanup-deployments}';
protected $description = 'Cleanup instance related stuffs'; protected $description = 'Cleanup instance related stuffs';
public function handle() public function handle()
@ -26,6 +27,7 @@ public function handle()
if ($cleanup_deployments) { if ($cleanup_deployments) {
echo "Running cleanup deployments.\n"; echo "Running cleanup deployments.\n";
$this->cleanup_in_progress_application_deployments(); $this->cleanup_in_progress_application_deployments();
return; return;
} }
if ($full_cleanup) { if ($full_cleanup) {
@ -35,7 +37,7 @@ public function handle()
$this->cleanup_stucked_helper_containers(); $this->cleanup_stucked_helper_containers();
$this->call('cleanup:queue'); $this->call('cleanup:queue');
$this->call('cleanup:stucked-resources'); $this->call('cleanup:stucked-resources');
if (!isCloud()) { if (! isCloud()) {
try { try {
$server = Server::find(0)->first(); $server = Server::find(0)->first();
$server->setupDynamicProxyConfiguration(); $server->setupDynamicProxyConfiguration();
@ -45,13 +47,14 @@ public function handle()
} }
$settings = InstanceSettings::get(); $settings = InstanceSettings::get();
if (!is_null(env('AUTOUPDATE', null))) { if (! is_null(env('AUTOUPDATE', null))) {
if (env('AUTOUPDATE') == true) { if (env('AUTOUPDATE') == true) {
$settings->update(['is_auto_update_enabled' => true]); $settings->update(['is_auto_update_enabled' => true]);
} else { } else {
$settings->update(['is_auto_update_enabled' => false]); $settings->update(['is_auto_update_enabled' => false]);
} }
} }
return; return;
} }
$this->cleanup_stucked_helper_containers(); $this->cleanup_stucked_helper_containers();
@ -66,7 +69,7 @@ private function restore_coolify_db_backup()
echo "Restoring coolify db backup\n"; echo "Restoring coolify db backup\n";
$database->restore(); $database->restore();
$scheduledBackup = ScheduledDatabaseBackup::find(0); $scheduledBackup = ScheduledDatabaseBackup::find(0);
if (!$scheduledBackup) { if (! $scheduledBackup) {
ScheduledDatabaseBackup::create([ ScheduledDatabaseBackup::create([
'id' => 0, 'id' => 0,
'enabled' => true, 'enabled' => true,
@ -82,6 +85,7 @@ private function restore_coolify_db_backup()
echo "Error in restoring coolify db backup: {$e->getMessage()}\n"; echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
} }
} }
private function cleanup_stucked_helper_containers() private function cleanup_stucked_helper_containers()
{ {
$servers = Server::all(); $servers = Server::all();
@ -91,6 +95,7 @@ private function cleanup_stucked_helper_containers()
} }
} }
} }
private function alive() private function alive()
{ {
$id = config('app.id'); $id = config('app.id');
@ -99,6 +104,7 @@ private function alive()
$do_not_track = data_get($settings, 'do_not_track'); $do_not_track = data_get($settings, 'do_not_track');
if ($do_not_track == true) { if ($do_not_track == true) {
echo "Skipping alive as do_not_track is enabled\n"; echo "Skipping alive as do_not_track is enabled\n";
return; return;
} }
try { try {

View File

@ -3,6 +3,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use function Termwind\ask; use function Termwind\ask;
use function Termwind\render; use function Termwind\render;
use function Termwind\style; use function Termwind\style;
@ -32,6 +33,7 @@ public function handle()
if (blank($channel)) { if (blank($channel)) {
$this->showHelp(); $this->showHelp();
return; return;
} }

View File

@ -35,6 +35,7 @@ public function handle()
$this->info('Root user\'s email updated successfully.'); $this->info('Root user\'s email updated successfully.');
} catch (\Exception $e) { } catch (\Exception $e) {
$this->error('Failed to update root user\'s email.'); $this->error('Failed to update root user\'s email.');
return; return;
} }
} }

View File

@ -34,6 +34,7 @@ public function handle()
$passwordAgain = password('Again'); $passwordAgain = password('Again');
if ($password != $passwordAgain) { if ($password != $passwordAgain) {
$this->error('Passwords do not match.'); $this->error('Passwords do not match.');
return; return;
} }
$this->info('Updating root password...'); $this->info('Updating root password...');
@ -42,6 +43,7 @@ public function handle()
$this->info('Root password updated successfully.'); $this->info('Root password updated successfully.');
} catch (\Exception $e) { } catch (\Exception $e) {
$this->error('Failed to update root password.'); $this->error('Failed to update root password.');
return; return;
} }
} }

View File

@ -7,7 +7,9 @@
class Scheduler extends Command class Scheduler extends Command
{ {
protected $signature = 'start:scheduler'; protected $signature = 'start:scheduler';
protected $description = 'Start Scheduler'; protected $description = 'Start Scheduler';
public function handle() public function handle()
{ {
if (config('coolify.is_scheduler_enabled')) { if (config('coolify.is_scheduler_enabled')) {

View File

@ -48,11 +48,13 @@ public function handle()
$this->deleteServer(); $this->deleteServer();
} }
} }
private function deleteServer() private function deleteServer()
{ {
$servers = Server::all(); $servers = Server::all();
if ($servers->count() === 0) { if ($servers->count() === 0) {
$this->error('There are no applications to delete.'); $this->error('There are no applications to delete.');
return; return;
} }
$serversToDelete = multiselect( $serversToDelete = multiselect(
@ -64,19 +66,21 @@ private function deleteServer()
$toDelete = $servers->where('id', $server)->first(); $toDelete = $servers->where('id', $server)->first();
if ($toDelete) { if ($toDelete) {
$this->info($toDelete); $this->info($toDelete);
$confirmed = confirm("Are you sure you want to delete all selected resources?"); $confirmed = confirm('Are you sure you want to delete all selected resources?');
if (!$confirmed) { if (! $confirmed) {
break; break;
} }
$toDelete->delete(); $toDelete->delete();
} }
} }
} }
private function deleteApplication() private function deleteApplication()
{ {
$applications = Application::all(); $applications = Application::all();
if ($applications->count() === 0) { if ($applications->count() === 0) {
$this->error('There are no applications to delete.'); $this->error('There are no applications to delete.');
return; return;
} }
$applicationsToDelete = multiselect( $applicationsToDelete = multiselect(
@ -88,19 +92,21 @@ private function deleteApplication()
$toDelete = $applications->where('id', $application)->first(); $toDelete = $applications->where('id', $application)->first();
if ($toDelete) { if ($toDelete) {
$this->info($toDelete); $this->info($toDelete);
$confirmed = confirm("Are you sure you want to delete all selected resources? "); $confirmed = confirm('Are you sure you want to delete all selected resources? ');
if (!$confirmed) { if (! $confirmed) {
break; break;
} }
DeleteResourceJob::dispatch($toDelete); DeleteResourceJob::dispatch($toDelete);
} }
} }
} }
private function deleteDatabase() private function deleteDatabase()
{ {
$databases = StandalonePostgresql::all(); $databases = StandalonePostgresql::all();
if ($databases->count() === 0) { if ($databases->count() === 0) {
$this->error('There are no databases to delete.'); $this->error('There are no databases to delete.');
return; return;
} }
$databasesToDelete = multiselect( $databasesToDelete = multiselect(
@ -112,19 +118,21 @@ private function deleteDatabase()
$toDelete = $databases->where('id', $database)->first(); $toDelete = $databases->where('id', $database)->first();
if ($toDelete) { if ($toDelete) {
$this->info($toDelete); $this->info($toDelete);
$confirmed = confirm("Are you sure you want to delete all selected resources?"); $confirmed = confirm('Are you sure you want to delete all selected resources?');
if (!$confirmed) { if (! $confirmed) {
return; return;
} }
DeleteResourceJob::dispatch($toDelete); DeleteResourceJob::dispatch($toDelete);
} }
} }
} }
private function deleteService() private function deleteService()
{ {
$services = Service::all(); $services = Service::all();
if ($services->count() === 0) { if ($services->count() === 0) {
$this->error('There are no services to delete.'); $this->error('There are no services to delete.');
return; return;
} }
$servicesToDelete = multiselect( $servicesToDelete = multiselect(
@ -136,8 +144,8 @@ private function deleteService()
$toDelete = $services->where('id', $service)->first(); $toDelete = $services->where('id', $service)->first();
if ($toDelete) { if ($toDelete) {
$this->info($toDelete); $this->info($toDelete);
$confirmed = confirm("Are you sure you want to delete all selected resources?"); $confirmed = confirm('Are you sure you want to delete all selected resources?');
if (!$confirmed) { if (! $confirmed) {
return; return;
} }
DeleteResourceJob::dispatch($toDelete); DeleteResourceJob::dispatch($toDelete);

View File

@ -50,12 +50,13 @@ private function process_file($file)
// $this->info($content); // $this->info($content);
$ignore = collect(preg_grep('/^# ignore:/', explode("\n", $content)))->values(); $ignore = collect(preg_grep('/^# ignore:/', explode("\n", $content)))->values();
if ($ignore->count() > 0) { if ($ignore->count() > 0) {
$ignore = (bool)str($ignore[0])->after('# ignore:')->trim()->value(); $ignore = (bool) str($ignore[0])->after('# ignore:')->trim()->value();
} else { } else {
$ignore = false; $ignore = false;
} }
if ($ignore) { if ($ignore) {
$this->info("Ignoring $file"); $this->info("Ignoring $file");
return; return;
} }
$this->info("Processing $file"); $this->info("Processing $file");
@ -125,6 +126,7 @@ private function process_file($file)
$env_file_base64 = base64_encode($env_file_content); $env_file_base64 = base64_encode($env_file_content);
$payload['envs'] = $env_file_base64; $payload['envs'] = $env_file_base64;
} }
return $payload; return $payload;
} }
} }

View File

@ -33,30 +33,31 @@ public function handle()
$that = $this; $that = $this;
$only_template = $this->option('templates'); $only_template = $this->option('templates');
$only_version = $this->option('release'); $only_version = $this->option('release');
$bunny_cdn = "https://cdn.coollabs.io"; $bunny_cdn = 'https://cdn.coollabs.io';
$bunny_cdn_path = "coolify"; $bunny_cdn_path = 'coolify';
$bunny_cdn_storage_name = "coolcdn"; $bunny_cdn_storage_name = 'coolcdn';
$parent_dir = realpath(dirname(__FILE__) . '/../../..'); $parent_dir = realpath(dirname(__FILE__).'/../../..');
$compose_file = "docker-compose.yml"; $compose_file = 'docker-compose.yml';
$compose_file_prod = "docker-compose.prod.yml"; $compose_file_prod = 'docker-compose.prod.yml';
$install_script = "install.sh"; $install_script = 'install.sh';
$upgrade_script = "upgrade.sh"; $upgrade_script = 'upgrade.sh';
$production_env = ".env.production"; $production_env = '.env.production';
$service_template = "service-templates.json"; $service_template = 'service-templates.json';
$versions = "versions.json"; $versions = 'versions.json';
PendingRequest::macro('storage', function ($fileName) use ($that) { PendingRequest::macro('storage', function ($fileName) use ($that) {
$headers = [ $headers = [
'AccessKey' => env('BUNNY_STORAGE_API_KEY'), 'AccessKey' => env('BUNNY_STORAGE_API_KEY'),
'Accept' => 'application/json', '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)); $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(); return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw();
}); });
PendingRequest::macro('purge', function ($url) use ($that) { PendingRequest::macro('purge', function ($url) use ($that) {
@ -64,20 +65,21 @@ public function handle()
'AccessKey' => env('BUNNY_API_KEY'), 'AccessKey' => env('BUNNY_API_KEY'),
'Accept' => 'application/json', 'Accept' => 'application/json',
]; ];
$that->info('Purging: ' . $url); $that->info('Purging: '.$url);
return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [ return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [
"url" => $url, 'url' => $url,
"async" => false 'async' => false,
]); ]);
}); });
try { 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.'); $this->info('About to sync files (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.');
} }
if ($only_template) { if ($only_template) {
$this->info('About to sync service-templates.json to BunnyCDN.'); $this->info('About to sync service-templates.json to BunnyCDN.');
$confirmed = confirm("Are you sure you want to sync?"); $confirmed = confirm('Are you sure you want to sync?');
if (!$confirmed) { if (! $confirmed) {
return; return;
} }
Http::pool(fn (Pool $pool) => [ Http::pool(fn (Pool $pool) => [
@ -85,15 +87,16 @@ public function handle()
$pool->purge("$bunny_cdn/$bunny_cdn_path/$service_template"), $pool->purge("$bunny_cdn/$bunny_cdn_path/$service_template"),
]); ]);
$this->info('Service template uploaded & purged...'); $this->info('Service template uploaded & purged...');
return; return;
} else if ($only_version) { } elseif ($only_version) {
$this->info('About to sync versions.json to BunnyCDN.'); $this->info('About to sync versions.json to BunnyCDN.');
$file = file_get_contents("$parent_dir/$versions"); $file = file_get_contents("$parent_dir/$versions");
$json = json_decode($file, true); $json = json_decode($file, true);
$actual_version = data_get($json, 'coolify.v4.version'); $actual_version = data_get($json, 'coolify.v4.version');
$confirmed = confirm("Are you sure you want to sync to {$actual_version}?"); $confirmed = confirm("Are you sure you want to sync to {$actual_version}?");
if (!$confirmed) { if (! $confirmed) {
return; return;
} }
Http::pool(fn (Pool $pool) => [ Http::pool(fn (Pool $pool) => [
@ -101,10 +104,10 @@ public function handle()
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"), $pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
]); ]);
$this->info('versions.json uploaded & purged...'); $this->info('versions.json uploaded & purged...');
return; return;
} }
Http::pool(fn (Pool $pool) => [ 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")->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"), $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/$upgrade_script"),
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_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) { } catch (\Throwable $e) {
$this->error("Error: " . $e->getMessage()); $this->error('Error: '.$e->getMessage());
} }
} }
} }

View File

@ -13,7 +13,9 @@
class WaitlistInvite extends Command class WaitlistInvite extends Command
{ {
public Waitlist|User|null $next_patient = null; public Waitlist|User|null $next_patient = null;
public string|null $password = null;
public ?string $password = null;
/** /**
* The name and signature of the console command. * The name and signature of the console command.
* *
@ -38,7 +40,9 @@ public function handle()
$this->main(); $this->main();
} }
} }
private function main() {
private function main()
{
if ($this->argument('email')) { if ($this->argument('email')) {
if ($this->option('only-email')) { if ($this->option('only-email')) {
$this->next_patient = User::whereEmail($this->argument('email'))->first(); $this->next_patient = User::whereEmail($this->argument('email'))->first();
@ -50,8 +54,9 @@ private function main() {
} else { } else {
$this->next_patient = Waitlist::where('email', $this->argument('email'))->first(); $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."); $this->error("{$this->argument('email')} not found in the waitlist.");
return; return;
} }
} else { } else {
@ -60,6 +65,7 @@ private function main() {
if ($this->next_patient) { if ($this->next_patient) {
if ($this->option('only-email')) { if ($this->option('only-email')) {
$this->send_email(); $this->send_email();
return; return;
} }
$this->register_user(); $this->register_user();
@ -69,10 +75,11 @@ private function main() {
$this->info('No verified user found in the waitlist. 👀'); $this->info('No verified user found in the waitlist. 👀');
} }
} }
private function register_user() private function register_user()
{ {
$already_registered = User::whereEmail($this->next_patient->email)->first(); $already_registered = User::whereEmail($this->next_patient->email)->first();
if (!$already_registered) { if (! $already_registered) {
$this->password = Str::password(); $this->password = Str::password();
User::create([ User::create([
'name' => Str::of($this->next_patient->email)->before('@'), 'name' => Str::of($this->next_patient->email)->before('@'),
@ -85,11 +92,13 @@ private function register_user()
throw new \Exception('User already registered'); throw new \Exception('User already registered');
} }
} }
private function remove_from_waitlist() private function remove_from_waitlist()
{ {
$this->next_patient->delete(); $this->next_patient->delete();
$this->info("User removed from waitlist successfully."); $this->info('User removed from waitlist successfully.');
} }
private function send_email() private function send_email()
{ {
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password"); $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.'); $mail->subject('Congratulations! You are invited to join Coolify Cloud.');
send_user_an_email($mail, $this->next_patient->email); send_user_an_email($mail, $this->next_patient->email);
$this->info("Email sent successfully. 📧"); $this->info('Email sent successfully. 📧');
} }
} }

View File

@ -4,13 +4,13 @@
use App\Jobs\CheckLogDrainContainerJob; use App\Jobs\CheckLogDrainContainerJob;
use App\Jobs\CleanupInstanceStuffsJob; use App\Jobs\CleanupInstanceStuffsJob;
use App\Jobs\DatabaseBackupJob;
use App\Jobs\ScheduledTaskJob;
use App\Jobs\ContainerStatusJob; use App\Jobs\ContainerStatusJob;
use App\Jobs\DatabaseBackupJob;
use App\Jobs\PullCoolifyImageJob; use App\Jobs\PullCoolifyImageJob;
use App\Jobs\PullHelperImageJob; use App\Jobs\PullHelperImageJob;
use App\Jobs\PullSentinelImageJob; use App\Jobs\PullSentinelImageJob;
use App\Jobs\PullTemplatesFromCDN; use App\Jobs\PullTemplatesFromCDN;
use App\Jobs\ScheduledTaskJob;
use App\Jobs\ServerStatusJob; use App\Jobs\ServerStatusJob;
use App\Models\ScheduledDatabaseBackup; use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledTask; use App\Models\ScheduledTask;
@ -22,6 +22,7 @@
class Kernel extends ConsoleKernel class Kernel extends ConsoleKernel
{ {
private $all_servers; private $all_servers;
protected function schedule(Schedule $schedule): void protected function schedule(Schedule $schedule): void
{ {
$this->all_servers = Server::all(); $this->all_servers = Server::all();
@ -55,6 +56,7 @@ protected function schedule(Schedule $schedule): void
$schedule->command('uploads:clear')->everyTwoMinutes(); $schedule->command('uploads:clear')->everyTwoMinutes();
} }
} }
private function pull_images($schedule) 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'); $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(); $schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
} }
} }
private function check_resources($schedule) private function check_resources($schedule)
{ {
if (isCloud()) { if (isCloud()) {
@ -86,6 +89,7 @@ private function check_resources($schedule)
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer(); $schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
} }
} }
private function check_scheduled_backups($schedule) private function check_scheduled_backups($schedule)
{ {
$scheduled_backups = ScheduledDatabaseBackup::all(); $scheduled_backups = ScheduledDatabaseBackup::all();
@ -93,12 +97,13 @@ private function check_scheduled_backups($schedule)
return; return;
} }
foreach ($scheduled_backups as $scheduled_backup) { foreach ($scheduled_backups as $scheduled_backup) {
if (!$scheduled_backup->enabled) { if (! $scheduled_backup->enabled) {
continue; continue;
} }
if (is_null(data_get($scheduled_backup, 'database'))) { if (is_null(data_get($scheduled_backup, 'database'))) {
ray('database not found'); ray('database not found');
$scheduled_backup->delete(); $scheduled_backup->delete();
continue; continue;
} }
@ -124,9 +129,10 @@ private function check_scheduled_tasks($schedule)
$service = $scheduled_task->service; $service = $scheduled_task->service;
$application = $scheduled_task->application; $application = $scheduled_task->application;
if (!$application && !$service) { if (! $application && ! $service) {
ray('application/service attached to scheduled task does not exist'); ray('application/service attached to scheduled task does not exist');
$scheduled_task->delete(); $scheduled_task->delete();
continue; continue;
} }
if ($application) { if ($application) {
@ -150,7 +156,7 @@ private function check_scheduled_tasks($schedule)
protected function commands(): void protected function commands(): void
{ {
$this->load(__DIR__ . '/Commands'); $this->load(__DIR__.'/Commands');
require base_path('routes/console.php'); require base_path('routes/console.php');
} }

View File

@ -18,12 +18,12 @@ public function __construct(
public ?string $type_uuid = null, public ?string $type_uuid = null,
public ?int $process_id = null, public ?int $process_id = null,
public ?Model $model = null, public ?Model $model = null,
public ?string $status = null , public ?string $status = null,
public bool $ignore_errors = false, public bool $ignore_errors = false,
public $call_event_on_finish = null, public $call_event_on_finish = null,
public $call_event_data = null public $call_event_data = null
) { ) {
if(is_null($status)){ if (is_null($status)) {
$this->status = ProcessStatus::QUEUED->value; $this->status = ProcessStatus::QUEUED->value;
} }
} }

View File

@ -2,9 +2,7 @@
namespace App\Events; namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Foundation\Events\Dispatchable;
@ -13,14 +11,16 @@
class ApplicationStatusChanged implements ShouldBroadcast class ApplicationStatusChanged implements ShouldBroadcast
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
public $teamId; public $teamId;
public function __construct($teamId = null) public function __construct($teamId = null)
{ {
if (is_null($teamId)) { if (is_null($teamId)) {
$teamId = auth()->user()->currentTeam()->id ?? null; $teamId = auth()->user()->currentTeam()->id ?? null;
} }
if (is_null($teamId)) { if (is_null($teamId)) {
throw new \Exception("Team id is null"); throw new \Exception('Team id is null');
} }
$this->teamId = $teamId; $this->teamId = $teamId;
} }

View File

@ -2,9 +2,7 @@
namespace App\Events; namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Foundation\Events\Dispatchable;
@ -13,14 +11,16 @@
class BackupCreated implements ShouldBroadcast class BackupCreated implements ShouldBroadcast
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
public $teamId; public $teamId;
public function __construct($teamId = null) public function __construct($teamId = null)
{ {
if (is_null($teamId)) { if (is_null($teamId)) {
$teamId = auth()->user()->currentTeam()->id ?? null; $teamId = auth()->user()->currentTeam()->id ?? null;
} }
if (is_null($teamId)) { if (is_null($teamId)) {
throw new \Exception("Team id is null"); throw new \Exception('Team id is null');
} }
$this->teamId = $teamId; $this->teamId = $teamId;
} }

View File

@ -2,9 +2,7 @@
namespace App\Events; namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Foundation\Events\Dispatchable;
@ -13,14 +11,16 @@
class DatabaseStatusChanged implements ShouldBroadcast class DatabaseStatusChanged implements ShouldBroadcast
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
public $userId; public $userId;
public function __construct($userId = null) public function __construct($userId = null)
{ {
if (is_null($userId)) { if (is_null($userId)) {
$userId = auth()->user()->id ?? null; $userId = auth()->user()->id ?? null;
} }
if (is_null($userId)) { if (is_null($userId)) {
throw new \Exception("User id is null"); throw new \Exception('User id is null');
} }
$this->userId = $userId; $this->userId = $userId;
} }

View File

@ -9,6 +9,7 @@
class ProxyStarted class ProxyStarted
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public $data) public function __construct(public $data)
{ {

View File

@ -2,9 +2,7 @@
namespace App\Events; namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Foundation\Events\Dispatchable;
@ -13,14 +11,16 @@
class ProxyStatusChanged implements ShouldBroadcast class ProxyStatusChanged implements ShouldBroadcast
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
public $teamId; public $teamId;
public function __construct($teamId = null) public function __construct($teamId = null)
{ {
if (is_null($teamId)) { if (is_null($teamId)) {
$teamId = auth()->user()->currentTeam()->id ?? null; $teamId = auth()->user()->currentTeam()->id ?? null;
} }
if (is_null($teamId)) { if (is_null($teamId)) {
throw new \Exception("Team id is null"); throw new \Exception('Team id is null');
} }
$this->teamId = $teamId; $this->teamId = $teamId;
} }

View File

@ -2,9 +2,7 @@
namespace App\Events; namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Foundation\Events\Dispatchable;
@ -13,14 +11,16 @@
class ServiceStatusChanged implements ShouldBroadcast class ServiceStatusChanged implements ShouldBroadcast
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
public $userId; public $userId;
public function __construct($userId = null) public function __construct($userId = null)
{ {
if (is_null($userId)) { if (is_null($userId)) {
$userId = auth()->user()->id ?? null; $userId = auth()->user()->id ?? null;
} }
if (is_null($userId)) { if (is_null($userId)) {
throw new \Exception("User id is null"); throw new \Exception('User id is null');
} }
$this->userId = $userId; $this->userId = $userId;
} }

View File

@ -2,9 +2,7 @@
namespace App\Events; namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Foundation\Events\Dispatchable;
@ -13,7 +11,9 @@
class TestEvent implements ShouldBroadcast class TestEvent implements ShouldBroadcast
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
public $teamId; public $teamId;
public function __construct() public function __construct()
{ {
$this->teamId = auth()->user()->currentTeam()->id; $this->teamId = auth()->user()->currentTeam()->id;

View File

@ -13,7 +13,6 @@
class Handler extends ExceptionHandler class Handler extends ExceptionHandler
{ {
/** /**
* A list of exception types with their corresponding custom log levels. * A list of exception types with their corresponding custom log levels.
* *
@ -22,14 +21,16 @@ class Handler extends ExceptionHandler
protected $levels = [ protected $levels = [
// //
]; ];
/** /**
* A list of the exception types that are not reported. * A list of the exception types that are not reported.
* *
* @var array<int, class-string<\Throwable>> * @var array<int, class-string<\Throwable>>
*/ */
protected $dontReport = [ protected $dontReport = [
ProcessException::class ProcessException::class,
]; ];
/** /**
* A list of the inputs that are never flashed to the session on validation exceptions. * 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',
'password_confirmation', 'password_confirmation',
]; ];
private InstanceSettings $settings; private InstanceSettings $settings;
protected function unauthenticated($request, AuthenticationException $exception) 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)) { if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
return response()->json(['message' => $exception->getMessage()], 401); return response()->json(['message' => $exception->getMessage()], 401);
} }
return redirect()->guest($exception->redirectTo() ?? route('login')); return redirect()->guest($exception->redirectTo() ?? route('login'));
} }
/** /**
* Register the exception handling callbacks for the application. * Register the exception handling callbacks for the application.
*/ */
@ -72,7 +76,7 @@ function (Scope $scope) {
$scope->setUser( $scope->setUser(
[ [
'email' => $email, 'email' => $email,
'instanceAdmin' => $instanceAdmin 'instanceAdmin' => $instanceAdmin,
] ]
); );
} }

View File

@ -6,5 +6,4 @@
class ProcessException extends Exception class ProcessException extends Exception
{ {
} }

View File

@ -27,18 +27,20 @@ public function deployments(Request $request)
return invalid_token(); return invalid_token();
} }
$servers = Server::whereTeamId($teamId)->get(); $servers = Server::whereTeamId($teamId)->get();
$deployments_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn("server_id", $servers->pluck("id"))->get([ $deployments_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $servers->pluck('id'))->get([
"id", 'id',
"application_id", 'application_id',
"application_name", 'application_name',
"deployment_url", 'deployment_url',
"pull_request_id", 'pull_request_id',
"server_name", 'server_name',
"server_id", 'server_id',
"status" 'status',
])->sortBy('id')->toArray(); ])->sortBy('id')->toArray();
return response()->json($deployments_per_server, 200); return response()->json($deployments_per_server, 200);
} }
public function deploy(Request $request) public function deploy(Request $request)
{ {
$teamId = get_team_id_from_token(); $teamId = get_team_id_from_token();
@ -54,11 +56,13 @@ public function deploy(Request $request)
} }
if ($tags) { if ($tags) {
return $this->by_tags($tags, $teamId, $force); return $this->by_tags($tags, $teamId, $force);
} else if ($uuids) { } elseif ($uuids) {
return $this->by_uuids($uuids, $teamId, $force); 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); 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) private function by_uuids(string $uuid, int $teamId, bool $force = false)
{ {
$uuids = explode(',', $uuid); $uuids = explode(',', $uuid);
@ -82,10 +86,13 @@ private function by_uuids(string $uuid, int $teamId, bool $force = false)
} }
if ($deployments->count() > 0) { if ($deployments->count() > 0) {
$payload->put('deployments', $deployments->toArray()); $payload->put('deployments', $deployments->toArray());
return response()->json($payload->toArray(), 200); 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) public function by_tags(string $tags, int $team_id, bool $force = false)
{ {
$tags = explode(',', $tags); $tags = explode(',', $tags);
@ -99,7 +106,7 @@ public function by_tags(string $tags, int $team_id, bool $force = false)
$payload = collect(); $payload = collect();
foreach ($tags as $tag) { foreach ($tags as $tag) {
$found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first(); $found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first();
if (!$found_tag) { if (! $found_tag) {
// $message->push("Tag {$tag} not found."); // $message->push("Tag {$tag} not found.");
continue; continue;
} }
@ -107,6 +114,7 @@ public function by_tags(string $tags, int $team_id, bool $force = false)
$services = $found_tag->services()->get(); $services = $found_tag->services()->get();
if ($applications->count() === 0 && $services->count() === 0) { if ($applications->count() === 0 && $services->count() === 0) {
$message->push("No resources found for tag {$tag}."); $message->push("No resources found for tag {$tag}.");
continue; continue;
} }
foreach ($applications as $resource) { foreach ($applications as $resource) {
@ -127,11 +135,13 @@ public function by_tags(string $tags, int $team_id, bool $force = false)
if ($deployments->count() > 0) { if ($deployments->count() > 0) {
$payload->put('details', $deployments->toArray()); $payload->put('details', $deployments->toArray());
} }
return response()->json($payload->toArray(), 200); 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 public function deploy_resource($resource, bool $force = false): array
{ {
$message = null; $message = null;
@ -148,58 +158,59 @@ public function deploy_resource($resource, bool $force = false): array
force_rebuild: $force, force_rebuild: $force,
); );
$message = "Application {$resource->name} deployment queued."; $message = "Application {$resource->name} deployment queued.";
} else if ($type === 'App\Models\StandalonePostgresql') { } elseif ($type === 'App\Models\StandalonePostgresql') {
StartPostgresql::run($resource); StartPostgresql::run($resource);
$resource->update([ $resource->update([
'started_at' => now(), 'started_at' => now(),
]); ]);
$message = "Database {$resource->name} started."; $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\StandaloneRedis') { } elseif ($type === 'App\Models\StandaloneRedis') {
StartRedis::run($resource); StartRedis::run($resource);
$resource->update([ $resource->update([
'started_at' => now(), 'started_at' => now(),
]); ]);
$message = "Database {$resource->name} started."; $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\StandaloneKeydb') { } elseif ($type === 'App\Models\StandaloneKeydb') {
StartKeydb::run($resource); StartKeydb::run($resource);
$resource->update([ $resource->update([
'started_at' => now(), 'started_at' => now(),
]); ]);
$message = "Database {$resource->name} started."; $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\StandaloneDragonfly') { } elseif ($type === 'App\Models\StandaloneDragonfly') {
StartDragonfly::run($resource); StartDragonfly::run($resource);
$resource->update([ $resource->update([
'started_at' => now(), 'started_at' => now(),
]); ]);
$message = "Database {$resource->name} started."; $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\StandaloneClickhouse') { } elseif ($type === 'App\Models\StandaloneClickhouse') {
StartClickhouse::run($resource); StartClickhouse::run($resource);
$resource->update([ $resource->update([
'started_at' => now(), 'started_at' => now(),
]); ]);
$message = "Database {$resource->name} started."; $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\StandaloneMongodb') { } elseif ($type === 'App\Models\StandaloneMongodb') {
StartMongodb::run($resource); StartMongodb::run($resource);
$resource->update([ $resource->update([
'started_at' => now(), 'started_at' => now(),
]); ]);
$message = "Database {$resource->name} started."; $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\StandaloneMysql') { } elseif ($type === 'App\Models\StandaloneMysql') {
StartMysql::run($resource); StartMysql::run($resource);
$resource->update([ $resource->update([
'started_at' => now(), 'started_at' => now(),
]); ]);
$message = "Database {$resource->name} started."; $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\StandaloneMariadb') { } elseif ($type === 'App\Models\StandaloneMariadb') {
StartMariadb::run($resource); StartMariadb::run($resource);
$resource->update([ $resource->update([
'started_at' => now(), 'started_at' => now(),
]); ]);
$message = "Database {$resource->name} started."; $message = "Database {$resource->name} started.";
} else if ($type === 'App\Models\Service') { } elseif ($type === 'App\Models\Service') {
StartService::run($resource); StartService::run($resource);
$message = "Service {$resource->name} started. It could take a while, be patient."; $message = "Service {$resource->name} started. It could take a while, be patient.";
} }
return ['message' => $message, 'deployment_uuid' => $deployment_uuid]; return ['message' => $message, 'deployment_uuid' => $deployment_uuid];
} }
} }

View File

@ -38,7 +38,7 @@ public function domains(Request $request)
'ip' => $settings->public_ipv6, 'ip' => $settings->public_ipv6,
]); ]);
} }
if (!$settings->public_ipv4 && !$settings->public_ipv6) { if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
$domains->push([ $domains->push([
'domain' => $fqdn, 'domain' => $fqdn,
'ip' => $ip, 'ip' => $ip,
@ -74,7 +74,7 @@ public function domains(Request $request)
'ip' => $settings->public_ipv6, 'ip' => $settings->public_ipv6,
]); ]);
} }
if (!$settings->public_ipv4 && !$settings->public_ipv6) { if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
$domains->push([ $domains->push([
'domain' => $fqdn, 'domain' => $fqdn,
'ip' => $ip, 'ip' => $ip,

View File

@ -15,8 +15,10 @@ public function projects(Request $request)
return invalid_token(); return invalid_token();
} }
$projects = ModelsProject::whereTeamId($teamId)->select('id', 'name', 'uuid')->get(); $projects = ModelsProject::whereTeamId($teamId)->select('id', 'name', 'uuid')->get();
return response()->json($projects); return response()->json($projects);
} }
public function project_by_uuid(Request $request) public function project_by_uuid(Request $request)
{ {
$teamId = get_team_id_from_token(); $teamId = get_team_id_from_token();
@ -24,8 +26,10 @@ public function project_by_uuid(Request $request)
return invalid_token(); return invalid_token();
} }
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']); $project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']);
return response()->json($project); return response()->json($project);
} }
public function environment_details(Request $request) public function environment_details(Request $request)
{ {
$teamId = get_team_id_from_token(); $teamId = get_team_id_from_token();
@ -34,6 +38,7 @@ public function environment_details(Request $request)
} }
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first(); $project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
$environment = $project->environments()->whereName(request()->environment_name)->first()->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']); $environment = $project->environments()->whereName(request()->environment_name)->first()->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']);
return response()->json($environment); return response()->json($environment);
} }
} }

View File

@ -30,9 +30,10 @@ public function resources(Request $request)
$payload['status'] = $resource->status; $payload['status'] = $resource->status;
} }
$payload['type'] = $resource->type(); $payload['type'] = $resource->type();
return $payload; return $payload;
}); });
return response()->json($resources); return response()->json($resources);
} }
} }

View File

@ -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) { $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_reachable'] = $server->settings->is_reachable;
$server['is_usable'] = $server->settings->is_usable; $server['is_usable'] = $server->settings->is_usable;
return $server; return $server;
}); });
return response()->json($servers); return response()->json($servers);
} }
public function server_by_uuid(Request $request) public function server_by_uuid(Request $request)
{ {
$with_resources = $request->query('resources'); $with_resources = $request->query('resources');
@ -47,11 +50,13 @@ public function server_by_uuid(Request $request)
} else { } else {
$payload['status'] = $resource->status; $payload['status'] = $resource->status;
} }
return $payload; return $payload;
}); });
} else { } else {
$server->load(['settings']); $server->load(['settings']);
} }
return response()->json($server); return response()->json($server);
} }
} }

View File

@ -14,8 +14,10 @@ public function teams(Request $request)
return invalid_token(); return invalid_token();
} }
$teams = auth()->user()->teams; $teams = auth()->user()->teams;
return response()->json($teams); return response()->json($teams);
} }
public function team_by_id(Request $request) public function team_by_id(Request $request)
{ {
$id = $request->id; $id = $request->id;
@ -26,10 +28,12 @@ public function team_by_id(Request $request)
$teams = auth()->user()->teams; $teams = auth()->user()->teams;
$team = $teams->where('id', $id)->first(); $team = $teams->where('id', $id)->first();
if (is_null($team)) { 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); return response()->json($team);
} }
public function members_by_id(Request $request) public function members_by_id(Request $request)
{ {
$id = $request->id; $id = $request->id;
@ -40,10 +44,12 @@ public function members_by_id(Request $request)
$teams = auth()->user()->teams; $teams = auth()->user()->teams;
$team = $teams->where('id', $id)->first(); $team = $teams->where('id', $id)->first();
if (is_null($team)) { 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); return response()->json($team->members);
} }
public function current_team(Request $request) public function current_team(Request $request)
{ {
$teamId = get_team_id_from_token(); $teamId = get_team_id_from_token();
@ -51,8 +57,10 @@ public function current_team(Request $request)
return invalid_token(); return invalid_token();
} }
$team = auth()->user()->currentTeam(); $team = auth()->user()->currentTeam();
return response()->json($team); return response()->json($team);
} }
public function current_team_members(Request $request) public function current_team_members(Request $request)
{ {
$teamId = get_team_id_from_token(); $teamId = get_team_id_from_token();
@ -60,6 +68,7 @@ public function current_team_members(Request $request)
return invalid_token(); return invalid_token();
} }
$team = auth()->user()->currentTeam(); $team = auth()->user()->currentTeam();
return response()->json($team->members); return response()->json($team->members);
} }
} }

View File

@ -14,40 +14,49 @@
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Laravel\Fortify\Fortify;
use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse; use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse;
use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse; use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse;
use Illuminate\Support\Facades\Password; use Laravel\Fortify\Fortify;
class Controller extends BaseController class Controller extends BaseController
{ {
use AuthorizesRequests, ValidatesRequests; use AuthorizesRequests, ValidatesRequests;
public function realtime_test() { public function realtime_test()
{
if (auth()->user()?->currentTeam()->id !== 0) { if (auth()->user()?->currentTeam()->id !== 0) {
return redirect(RouteServiceProvider::HOME); return redirect(RouteServiceProvider::HOME);
} }
TestEvent::dispatch(); TestEvent::dispatch();
return 'Look at your other tab.'; return 'Look at your other tab.';
} }
public function verify() {
public function verify()
{
return view('auth.verify-email'); return view('auth.verify-email');
} }
public function email_verify(EmailVerificationRequest $request) {
public function email_verify(EmailVerificationRequest $request)
{
$request->fulfill(); $request->fulfill();
$name = request()->user()?->name; $name = request()->user()?->name;
// send_internal_notification("User {$name} verified their email address."); // send_internal_notification("User {$name} verified their email address.");
return redirect(RouteServiceProvider::HOME); return redirect(RouteServiceProvider::HOME);
} }
public function forgot_password(Request $request) {
public function forgot_password(Request $request)
{
if (is_transactional_emails_active()) { if (is_transactional_emails_active()) {
$arrayOfRequest = $request->only(Fortify::email()); $arrayOfRequest = $request->only(Fortify::email());
$request->merge([ $request->merge([
'email' => Str::lower($arrayOfRequest['email']), 'email' => Str::lower($arrayOfRequest['email']),
]); ]);
$type = set_transanctional_email_settings(); $type = set_transanctional_email_settings();
if (!$type) { if (! $type) {
return response()->json(['message' => 'Transactional emails are not active'], 400); return response()->json(['message' => 'Transactional emails are not active'], 400);
} }
$request->validate([Fortify::email() => 'required|email']); $request->validate([Fortify::email() => 'required|email']);
@ -60,10 +69,13 @@ public function forgot_password(Request $request) {
if ($status == Password::RESET_THROTTLED) { if ($status == Password::RESET_THROTTLED) {
return response('Already requested a password reset in the past minutes.', 400); return response('Already requested a password reset in the past minutes.', 400);
} }
return app(FailedPasswordResetLinkRequestResponse::class, ['status' => $status]); return app(FailedPasswordResetLinkRequestResponse::class, ['status' => $status]);
} }
return response()->json(['message' => 'Transactional emails are not active'], 400); return response()->json(['message' => 'Transactional emails are not active'], 400);
} }
public function link() public function link()
{ {
$token = request()->get('token'); $token = request()->get('token');
@ -72,7 +84,7 @@ public function link()
$email = Str::of($decrypted)->before('@@@'); $email = Str::of($decrypted)->before('@@@');
$password = Str::of($decrypted)->after('@@@'); $password = Str::of($decrypted)->after('@@@');
$user = User::whereEmail($email)->first(); $user = User::whereEmail($email)->first();
if (!$user) { if (! $user) {
return redirect()->route('login'); return redirect()->route('login');
} }
if (Hash::check($password, $user->password)) { if (Hash::check($password, $user->password)) {
@ -90,9 +102,11 @@ public function link()
} }
Auth::login($user); Auth::login($user);
session(['currentTeam' => $team]); session(['currentTeam' => $team]);
return redirect()->route('dashboard'); return redirect()->route('dashboard');
} }
} }
return redirect()->route('login')->with('error', 'Invalid credentials.'); return redirect()->route('login')->with('error', 'Invalid credentials.');
} }
@ -108,11 +122,12 @@ public function accept_invitation()
if ($resetPassword) { if ($resetPassword) {
$user->update([ $user->update([
'password' => Hash::make($invitationUuid), 'password' => Hash::make($invitationUuid),
'force_password_reset' => true 'force_password_reset' => true,
]); ]);
} }
if ($user->teams()->where('team_id', $invitation->team->id)->exists()) { if ($user->teams()->where('team_id', $invitation->team->id)->exists()) {
$invitation->delete(); $invitation->delete();
return redirect()->route('team.index'); return redirect()->route('team.index');
} }
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]); $user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
@ -121,6 +136,7 @@ public function accept_invitation()
return redirect()->route('login'); return redirect()->route('login');
} }
refreshSession($invitation->team); refreshSession($invitation->team);
return redirect()->route('team.index'); return redirect()->route('team.index');
} else { } else {
abort(401); abort(401);
@ -143,6 +159,7 @@ public function revoke_invitation()
abort(401); abort(401);
} }
$invitation->delete(); $invitation->delete();
return redirect()->route('team.index'); return redirect()->route('team.index');
} catch (\Throwable $e) { } catch (\Throwable $e) {
throw $e; throw $e;

View File

@ -12,34 +12,35 @@ class MagicController extends Controller
public function servers() public function servers()
{ {
return response()->json([ return response()->json([
'servers' => Server::isUsable()->get() 'servers' => Server::isUsable()->get(),
]); ]);
} }
public function destinations() public function destinations()
{ {
return response()->json([ return response()->json([
'destinations' => Server::destinationsByServer(request()->query('server_id'))->sortBy('name') 'destinations' => Server::destinationsByServer(request()->query('server_id'))->sortBy('name'),
]); ]);
} }
public function projects() public function projects()
{ {
return response()->json([ return response()->json([
'projects' => Project::ownedByCurrentTeam()->get() 'projects' => Project::ownedByCurrentTeam()->get(),
]); ]);
} }
public function environments() public function environments()
{ {
$project = Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->first(); $project = Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->first();
if (!$project) { if (! $project) {
return response()->json([ return response()->json([
'environments' => [] 'environments' => [],
]); ]);
} }
return response()->json([ return response()->json([
'environments' => $project->environments 'environments' => $project->environments,
]); ]);
} }
@ -49,8 +50,9 @@ public function newProject()
['name' => request()->query('name') ?? generate_random_name()], ['name' => request()->query('name') ?? generate_random_name()],
['team_id' => currentTeam()->id] ['team_id' => currentTeam()->id]
); );
return response()->json([ 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()], ['name' => request()->query('name') ?? generate_random_name()],
['project_id' => Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->firstOrFail()->id] ['project_id' => Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->firstOrFail()->id]
); );
return response()->json([ return response()->json([
'environment_name' => $environment->name, 'environment_name' => $environment->name,
]); ]);
@ -75,6 +78,7 @@ public function newTeam()
); );
auth()->user()->teams()->attach($team, ['role' => 'admin']); auth()->user()->teams()->attach($team, ['role' => 'admin']);
refreshSession(); refreshSession();
return redirect(request()->header('Referer')); return redirect(request()->header('Referer'));
} }
} }

View File

@ -2,15 +2,15 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\User; use App\Models\User;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
class OauthController extends Controller { class OauthController extends Controller
{
public function redirect(string $provider) public function redirect(string $provider)
{ {
$socialite_provider = get_socialite_provider($provider); $socialite_provider = get_socialite_provider($provider);
return $socialite_provider->redirect(); return $socialite_provider->redirect();
} }
@ -19,16 +19,18 @@ public function callback(string $provider)
try { try {
$oauthUser = get_socialite_provider($provider)->user(); $oauthUser = get_socialite_provider($provider)->user();
$user = User::whereEmail($oauthUser->email)->first(); $user = User::whereEmail($oauthUser->email)->first();
if (!$user) { if (! $user) {
$user = User::create([ $user = User::create([
'name' => $oauthUser->name, 'name' => $oauthUser->name,
'email' => $oauthUser->email, 'email' => $oauthUser->email,
]); ]);
} }
Auth::login($user); Auth::login($user);
return redirect('/'); return redirect('/');
} catch (\Exception $e) { } catch (\Exception $e) {
ray($e->getMessage()); ray($e->getMessage());
return redirect()->route('login')->withErrors([__('auth.failed.callback')]); return redirect()->route('login')->withErrors([__('auth.failed.callback')]);
} }
} }

View File

@ -2,14 +2,11 @@
namespace App\Http\Controllers; 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\Request;
use Illuminate\Http\UploadedFile; use Illuminate\Http\UploadedFile;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException; use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException;
use Pion\Laravel\ChunkUpload\Handler\AbstractHandler;
use Pion\Laravel\ChunkUpload\Handler\HandlerFactory; use Pion\Laravel\ChunkUpload\Handler\HandlerFactory;
use Pion\Laravel\ChunkUpload\Receiver\FileReceiver; use Pion\Laravel\ChunkUpload\Receiver\FileReceiver;
@ -21,7 +18,7 @@ public function upload(Request $request)
if (is_null($resource)) { if (is_null($resource)) {
return response()->json(['error' => 'You do not have permission for this database'], 500); 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) { if ($receiver->isUploaded() === false) {
throw new UploadMissingFileException(); throw new UploadMissingFileException();
@ -34,9 +31,10 @@ public function upload(Request $request)
} }
$handler = $save->handler(); $handler = $save->handler();
return response()->json([ return response()->json([
"done" => $handler->getPercentageDone(), 'done' => $handler->getPercentageDone(),
'status' => true 'status' => true,
]); ]);
} }
// protected function saveFileToS3($file) // protected function saveFileToS3($file)
@ -64,19 +62,20 @@ protected function saveFile(UploadedFile $file, $resource)
{ {
$mime = str_replace('/', '-', $file->getMimeType()); $mime = str_replace('/', '-', $file->getMimeType());
$filePath = "upload/{$resource->uuid}"; $filePath = "upload/{$resource->uuid}";
$finalPath = storage_path("app/" . $filePath); $finalPath = storage_path('app/'.$filePath);
$file->move($finalPath, 'restore'); $file->move($finalPath, 'restore');
return response()->json([ return response()->json([
'mime_type' => $mime 'mime_type' => $mime,
]); ]);
} }
protected function createFilename(UploadedFile $file) protected function createFilename(UploadedFile $file)
{ {
$extension = $file->getClientOriginalExtension(); $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; return $filename;
} }

View File

@ -30,15 +30,16 @@ public function manual(Request $request)
]; ];
$json = json_encode($data); $json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Bitbicket::manual_bitbucket", $json); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Bitbicket::manual_bitbucket", $json);
return; return;
} }
$return_payloads = collect([]); $return_payloads = collect([]);
$payload = $request->collect(); $payload = $request->collect();
$headers = $request->headers->all(); $headers = $request->headers->all();
$x_bitbucket_token = data_get($headers, 'x-hub-signature.0', ""); $x_bitbucket_token = data_get($headers, 'x-hub-signature.0', '');
$x_bitbucket_event = data_get($headers, 'x-event-key.0', ""); $x_bitbucket_event = data_get($headers, 'x-event-key.0', '');
$handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']); $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([ return response([
'status' => 'failed', 'status' => 'failed',
'message' => 'Nothing to do. Event not handled.', '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'); $branch = data_get($payload, 'push.changes.0.new.name');
$full_name = data_get($payload, 'repository.full_name'); $full_name = data_get($payload, 'repository.full_name');
$commit = data_get($payload, 'push.changes.0.new.target.hash'); $commit = data_get($payload, 'push.changes.0.new.target.hash');
if (!$branch) { if (! $branch) {
return response([ return response([
'status' => 'failed', 'status' => 'failed',
'message' => 'Nothing to do. No branch found in the request.', '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') { if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
$branch = data_get($payload, 'pullrequest.destination.branch.name'); $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'); $webhook_secret = data_get($application, 'manual_webhook_secret_bitbucket');
$payload = $request->getContent(); $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); $payloadHash = hash_hmac($algo, $payload, $webhook_secret);
if (!hash_equals($hash, $payloadHash) && !isDev()) { if (! hash_equals($hash, $payloadHash) && ! isDev()) {
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,
'status' => 'failed', 'status' => 'failed',
'message' => 'Invalid signature.', 'message' => 'Invalid signature.',
]); ]);
ray('Invalid signature'); ray('Invalid signature');
continue; continue;
} }
$isFunctional = $application->destination->server->isFunctional(); $isFunctional = $application->destination->server->isFunctional();
if (!$isFunctional) { if (! $isFunctional) {
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,
'status' => 'failed', 'status' => 'failed',
'message' => 'Server is not functional.', 'message' => 'Server is not functional.',
]); ]);
ray('Server is not functional: ' . $application->destination->server->name); ray('Server is not functional: '.$application->destination->server->name);
continue; continue;
} }
if ($x_bitbucket_event === 'repo:push') { if ($x_bitbucket_event === 'repo:push') {
if ($application->isDeployable()) { if ($application->isDeployable()) {
ray('Deploying ' . $application->name . ' with branch ' . $branch); ray('Deploying '.$application->name.' with branch '.$branch);
$deployment_uuid = new Cuid2(7); $deployment_uuid = new Cuid2(7);
queue_application_deployment( queue_application_deployment(
application: $application, application: $application,
@ -123,10 +126,10 @@ public function manual(Request $request)
} }
if ($x_bitbucket_event === 'pullrequest:created') { if ($x_bitbucket_event === 'pullrequest:created') {
if ($application->isPRDeployable()) { 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); $deployment_uuid = new Cuid2(7);
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (!$found) { if (! $found) {
ApplicationPreview::create([ ApplicationPreview::create([
'git_type' => 'bitbucket', 'git_type' => 'bitbucket',
'application_id' => $application->id, 'application_id' => $application->id,
@ -178,9 +181,11 @@ public function manual(Request $request)
} }
} }
ray($return_payloads); ray($return_payloads);
return response($return_payloads); return response($return_payloads);
} catch (Exception $e) { } catch (Exception $e) {
ray($e); ray($e);
return handleError($e); return handleError($e);
} }
} }

View File

@ -27,6 +27,7 @@ public function manual(Request $request)
})->first(); })->first();
if ($gitea_delivery_found) { if ($gitea_delivery_found) {
ray('Webhook already found'); ray('Webhook already found');
return; return;
} }
$data = [ $data = [
@ -41,6 +42,7 @@ public function manual(Request $request)
]; ];
$json = json_encode($data); $json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitea::manual_{$x_gitea_delivery}", $json); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitea::manual_{$x_gitea_delivery}", $json);
return; return;
} }
$x_gitea_event = Str::lower($request->header('X-Gitea-Event')); $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'); $modified_files = data_get($payload, 'commits.*.modified');
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
ray($changed_files); 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') { if ($x_gitea_event === 'pull_request') {
$action = data_get($payload, 'action'); $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'); $pull_request_html_url = data_get($payload, 'pull_request.html_url');
$branch = data_get($payload, 'pull_request.head.ref'); $branch = data_get($payload, 'pull_request.head.ref');
$base_branch = data_get($payload, 'pull_request.base.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.'); return response('Nothing to do. No branch found in the request.');
} }
$applications = Application::where('git_repository', 'like', "%$full_name%"); $applications = Application::where('git_repository', 'like', "%$full_name%");
@ -96,29 +98,31 @@ public function manual(Request $request)
foreach ($applications as $application) { foreach ($applications as $application) {
$webhook_secret = data_get($application, 'manual_webhook_secret_gitea'); $webhook_secret = data_get($application, 'manual_webhook_secret_gitea');
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret); $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'); ray('Invalid signature');
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,
'status' => 'failed', 'status' => 'failed',
'message' => 'Invalid signature.', 'message' => 'Invalid signature.',
]); ]);
continue; continue;
} }
$isFunctional = $application->destination->server->isFunctional(); $isFunctional = $application->destination->server->isFunctional();
if (!$isFunctional) { if (! $isFunctional) {
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,
'status' => 'failed', 'status' => 'failed',
'message' => 'Server is not functional.', 'message' => 'Server is not functional.',
]); ]);
continue; continue;
} }
if ($x_gitea_event === 'push') { if ($x_gitea_event === 'push') {
if ($application->isDeployable()) { if ($application->isDeployable()) {
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) { 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); $deployment_uuid = new Cuid2(7);
queue_application_deployment( queue_application_deployment(
application: $application, application: $application,
@ -160,7 +164,7 @@ public function manual(Request $request)
if ($application->isPRDeployable()) { if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2(7); $deployment_uuid = new Cuid2(7);
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (!$found) { if (! $found) {
ApplicationPreview::create([ ApplicationPreview::create([
'git_type' => 'gitea', 'git_type' => 'gitea',
'application_id' => $application->id, 'application_id' => $application->id,
@ -213,9 +217,11 @@ public function manual(Request $request)
} }
} }
ray($return_payloads); ray($return_payloads);
return response($return_payloads); return response($return_payloads);
} catch (Exception $e) { } catch (Exception $e) {
ray($e->getMessage()); ray($e->getMessage());
return handleError($e); return handleError($e);
} }
} }

View File

@ -33,6 +33,7 @@ public function manual(Request $request)
})->first(); })->first();
if ($github_delivery_found) { if ($github_delivery_found) {
ray('Webhook already found'); ray('Webhook already found');
return; return;
} }
$data = [ $data = [
@ -47,6 +48,7 @@ public function manual(Request $request)
]; ];
$json = json_encode($data); $json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::manual_{$x_github_delivery}", $json); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::manual_{$x_github_delivery}", $json);
return; return;
} }
$x_github_event = Str::lower($request->header('X-GitHub-Event')); $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'); $removed_files = data_get($payload, 'commits.*.removed');
$modified_files = data_get($payload, 'commits.*.modified'); $modified_files = data_get($payload, 'commits.*.modified');
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); $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') { if ($x_github_event === 'pull_request') {
$action = data_get($payload, 'action'); $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'); $pull_request_html_url = data_get($payload, 'pull_request.html_url');
$branch = data_get($payload, 'pull_request.head.ref'); $branch = data_get($payload, 'pull_request.head.ref');
$base_branch = data_get($payload, 'pull_request.base.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.'); return response('Nothing to do. No branch found in the request.');
} }
$applications = Application::where('git_repository', 'like', "%$full_name%"); $applications = Application::where('git_repository', 'like', "%$full_name%");
@ -101,29 +103,31 @@ public function manual(Request $request)
foreach ($applications as $application) { foreach ($applications as $application) {
$webhook_secret = data_get($application, 'manual_webhook_secret_github'); $webhook_secret = data_get($application, 'manual_webhook_secret_github');
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret); $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'); ray('Invalid signature');
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,
'status' => 'failed', 'status' => 'failed',
'message' => 'Invalid signature.', 'message' => 'Invalid signature.',
]); ]);
continue; continue;
} }
$isFunctional = $application->destination->server->isFunctional(); $isFunctional = $application->destination->server->isFunctional();
if (!$isFunctional) { if (! $isFunctional) {
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,
'status' => 'failed', 'status' => 'failed',
'message' => 'Server is not functional.', 'message' => 'Server is not functional.',
]); ]);
continue; continue;
} }
if ($x_github_event === 'push') { if ($x_github_event === 'push') {
if ($application->isDeployable()) { if ($application->isDeployable()) {
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) { 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); $deployment_uuid = new Cuid2(7);
queue_application_deployment( queue_application_deployment(
application: $application, application: $application,
@ -165,7 +169,7 @@ public function manual(Request $request)
if ($application->isPRDeployable()) { if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2(7); $deployment_uuid = new Cuid2(7);
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (!$found) { if (! $found) {
ApplicationPreview::create([ ApplicationPreview::create([
'git_type' => 'github', 'git_type' => 'github',
'application_id' => $application->id, 'application_id' => $application->id,
@ -218,12 +222,15 @@ public function manual(Request $request)
} }
} }
ray($return_payloads); ray($return_payloads);
return response($return_payloads); return response($return_payloads);
} catch (Exception $e) { } catch (Exception $e) {
ray($e->getMessage()); ray($e->getMessage());
return handleError($e); return handleError($e);
} }
} }
public function normal(Request $request) public function normal(Request $request)
{ {
try { try {
@ -239,6 +246,7 @@ public function normal(Request $request)
})->first(); })->first();
if ($github_delivery_found) { if ($github_delivery_found) {
ray('Webhook already found'); ray('Webhook already found');
return; return;
} }
$data = [ $data = [
@ -253,6 +261,7 @@ public function normal(Request $request)
]; ];
$json = json_encode($data); $json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::normal_{$x_github_delivery}", $json); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::normal_{$x_github_delivery}", $json);
return; return;
} }
$x_github_event = Str::lower($request->header('X-GitHub-Event')); $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'); $webhook_secret = data_get($github_app, 'webhook_secret');
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret); $hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
if (config('app.env') !== 'local') { 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.'); return response('Invalid signature.');
} }
} }
@ -280,6 +289,7 @@ public function normal(Request $request)
if ($action === 'new_permissions_accepted') { if ($action === 'new_permissions_accepted') {
GithubAppPermissionJob::dispatch($github_app); GithubAppPermissionJob::dispatch($github_app);
} }
return response('cool'); return response('cool');
} }
if ($x_github_event === 'push') { if ($x_github_event === 'push') {
@ -292,7 +302,7 @@ public function normal(Request $request)
$removed_files = data_get($payload, 'commits.*.removed'); $removed_files = data_get($payload, 'commits.*.removed');
$modified_files = data_get($payload, 'commits.*.modified'); $modified_files = data_get($payload, 'commits.*.modified');
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); $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') { if ($x_github_event === 'pull_request') {
$action = data_get($payload, 'action'); $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'); $pull_request_html_url = data_get($payload, 'pull_request.html_url');
$branch = data_get($payload, 'pull_request.head.ref'); $branch = data_get($payload, 'pull_request.head.ref');
$base_branch = data_get($payload, 'pull_request.base.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.'); return response('Nothing to do. No id or branch found.');
} }
$applications = Application::where('repository_project_id', $id)->whereRelation('source', 'is_public', false); $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) { foreach ($applications as $application) {
$isFunctional = $application->destination->server->isFunctional(); $isFunctional = $application->destination->server->isFunctional();
if (!$isFunctional) { if (! $isFunctional) {
$return_payloads->push([ $return_payloads->push([
'status' => 'failed', 'status' => 'failed',
'message' => 'Server is not functional.', 'message' => 'Server is not functional.',
'application_uuid' => $application->uuid, 'application_uuid' => $application->uuid,
'application_name' => $application->name, 'application_name' => $application->name,
]); ]);
continue; continue;
} }
if ($x_github_event === 'push') { if ($x_github_event === 'push') {
if ($application->isDeployable()) { if ($application->isDeployable()) {
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) { 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); $deployment_uuid = new Cuid2(7);
queue_application_deployment( queue_application_deployment(
application: $application, application: $application,
@ -377,7 +388,7 @@ public function normal(Request $request)
if ($application->isPRDeployable()) { if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2(7); $deployment_uuid = new Cuid2(7);
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (!$found) { if (! $found) {
ApplicationPreview::create([ ApplicationPreview::create([
'git_type' => 'github', 'git_type' => 'github',
'application_id' => $application->id, 'application_id' => $application->id,
@ -431,12 +442,15 @@ public function normal(Request $request)
} }
} }
} }
return response($return_payloads); return response($return_payloads);
} catch (Exception $e) { } catch (Exception $e) {
ray($e->getMessage()); ray($e->getMessage());
return handleError($e); return handleError($e);
} }
} }
public function redirect(Request $request) public function redirect(Request $request)
{ {
try { try {
@ -464,11 +478,13 @@ public function redirect(Request $request)
$github_app->webhook_secret = $webhook_secret; $github_app->webhook_secret = $webhook_secret;
$github_app->private_key_id = $private_key->id; $github_app->private_key_id = $private_key->id;
$github_app->save(); $github_app->save();
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]); return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
} catch (Exception $e) { } catch (Exception $e) {
return handleError($e); return handleError($e);
} }
} }
public function install(Request $request) public function install(Request $request)
{ {
try { try {
@ -488,6 +504,7 @@ public function install(Request $request)
]; ];
$json = json_encode($data); $json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::install_{$installation_id}", $json); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::install_{$installation_id}", $json);
return; return;
} }
$source = $request->get('source'); $source = $request->get('source');
@ -497,6 +514,7 @@ public function install(Request $request)
$github_app->installation_id = $installation_id; $github_app->installation_id = $installation_id;
$github_app->save(); $github_app->save();
} }
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]); return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
} catch (Exception $e) { } catch (Exception $e) {
return handleError($e); return handleError($e);

View File

@ -31,6 +31,7 @@ public function manual(Request $request)
]; ];
$json = json_encode($data); $json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitlab::manual_gitlab", $json); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitlab::manual_gitlab", $json);
return; return;
} }
$return_payloads = collect([]); $return_payloads = collect([]);
@ -39,11 +40,12 @@ public function manual(Request $request)
$x_gitlab_token = data_get($headers, 'x-gitlab-token.0'); $x_gitlab_token = data_get($headers, 'x-gitlab-token.0');
$x_gitlab_event = data_get($payload, 'object_kind'); $x_gitlab_event = data_get($payload, 'object_kind');
$allowed_events = ['push', 'merge_request']; $allowed_events = ['push', 'merge_request'];
if (!in_array($x_gitlab_event, $allowed_events)) { if (! in_array($x_gitlab_event, $allowed_events)) {
$return_payloads->push([ $return_payloads->push([
'status' => 'failed', 'status' => 'failed',
'message' => 'Event not allowed. Only push and merge_request events are allowed.', 'message' => 'Event not allowed. Only push and merge_request events are allowed.',
]); ]);
return response($return_payloads); return response($return_payloads);
} }
@ -53,18 +55,19 @@ public function manual(Request $request)
if (Str::isMatch('/refs\/heads\/*/', $branch)) { if (Str::isMatch('/refs\/heads\/*/', $branch)) {
$branch = Str::after($branch, 'refs/heads/'); $branch = Str::after($branch, 'refs/heads/');
} }
if (!$branch) { if (! $branch) {
$return_payloads->push([ $return_payloads->push([
'status' => 'failed', 'status' => 'failed',
'message' => 'Nothing to do. No branch found in the request.', 'message' => 'Nothing to do. No branch found in the request.',
]); ]);
return response($return_payloads); return response($return_payloads);
} }
$added_files = data_get($payload, 'commits.*.added'); $added_files = data_get($payload, 'commits.*.added');
$removed_files = data_get($payload, 'commits.*.removed'); $removed_files = data_get($payload, 'commits.*.removed');
$modified_files = data_get($payload, 'commits.*.modified'); $modified_files = data_get($payload, 'commits.*.modified');
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); $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') { if ($x_gitlab_event === 'merge_request') {
$action = data_get($payload, 'object_attributes.action'); $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'); $full_name = data_get($payload, 'project.path_with_namespace');
$pull_request_id = data_get($payload, 'object_attributes.iid'); $pull_request_id = data_get($payload, 'object_attributes.iid');
$pull_request_html_url = data_get($payload, 'object_attributes.url'); $pull_request_html_url = data_get($payload, 'object_attributes.url');
if (!$branch) { if (! $branch) {
$return_payloads->push([ $return_payloads->push([
'status' => 'failed', 'status' => 'failed',
'message' => 'Nothing to do. No branch found in the request.', 'message' => 'Nothing to do. No branch found in the request.',
]); ]);
return response($return_payloads); 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%"); $applications = Application::where('git_repository', 'like', "%$full_name%");
if ($x_gitlab_event === 'push') { if ($x_gitlab_event === 'push') {
@ -90,6 +94,7 @@ public function manual(Request $request)
'status' => 'failed', 'status' => 'failed',
'message' => "Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.", '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); return response($return_payloads);
} }
} }
@ -100,6 +105,7 @@ public function manual(Request $request)
'status' => 'failed', 'status' => 'failed',
'message' => "Nothing to do. No applications found with branch '$base_branch'.", 'message' => "Nothing to do. No applications found with branch '$base_branch'.",
]); ]);
return response($return_payloads); return response($return_payloads);
} }
} }
@ -112,23 +118,25 @@ public function manual(Request $request)
'message' => 'Invalid signature.', 'message' => 'Invalid signature.',
]); ]);
ray('Invalid signature'); ray('Invalid signature');
continue; continue;
} }
$isFunctional = $application->destination->server->isFunctional(); $isFunctional = $application->destination->server->isFunctional();
if (!$isFunctional) { if (! $isFunctional) {
$return_payloads->push([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,
'status' => 'failed', 'status' => 'failed',
'message' => 'Server is not functional', 'message' => 'Server is not functional',
]); ]);
ray('Server is not functional: ' . $application->destination->server->name); ray('Server is not functional: '.$application->destination->server->name);
continue; continue;
} }
if ($x_gitlab_event === 'push') { if ($x_gitlab_event === 'push') {
if ($application->isDeployable()) { if ($application->isDeployable()) {
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) { 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); $deployment_uuid = new Cuid2(7);
queue_application_deployment( queue_application_deployment(
application: $application, application: $application,
@ -163,7 +171,7 @@ public function manual(Request $request)
'application_uuid' => $application->uuid, 'application_uuid' => $application->uuid,
'application_name' => $application->name, 'application_name' => $application->name,
]); ]);
ray('Deployments disabled for ' . $application->name); ray('Deployments disabled for '.$application->name);
} }
} }
if ($x_gitlab_event === 'merge_request') { if ($x_gitlab_event === 'merge_request') {
@ -171,7 +179,7 @@ public function manual(Request $request)
if ($application->isPRDeployable()) { if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2(7); $deployment_uuid = new Cuid2(7);
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (!$found) { if (! $found) {
ApplicationPreview::create([ ApplicationPreview::create([
'git_type' => 'gitlab', 'git_type' => 'gitlab',
'application_id' => $application->id, 'application_id' => $application->id,
@ -188,7 +196,7 @@ public function manual(Request $request)
is_webhook: true, is_webhook: true,
git_type: 'gitlab' 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([ $return_payloads->push([
'application' => $application->name, 'application' => $application->name,
'status' => 'success', 'status' => 'success',
@ -200,9 +208,9 @@ public function manual(Request $request)
'status' => 'failed', 'status' => 'failed',
'message' => 'Preview deployments disabled', 'message' => 'Preview deployments disabled',
]); ]);
ray('Preview deployments disabled for ' . $application->name); ray('Preview deployments disabled for '.$application->name);
} }
} else if ($action === 'closed' || $action === 'close' || $action === 'merge') { } elseif ($action === 'closed' || $action === 'close' || $action === 'merge') {
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if ($found) { if ($found) {
$found->delete(); $found->delete();
@ -214,6 +222,7 @@ public function manual(Request $request)
'status' => 'success', 'status' => 'success',
'message' => 'Preview Deployment closed', 'message' => 'Preview Deployment closed',
]); ]);
return response($return_payloads); return response($return_payloads);
} }
$return_payloads->push([ $return_payloads->push([
@ -230,9 +239,11 @@ public function manual(Request $request)
} }
} }
} }
return response($return_payloads); return response($return_payloads);
} catch (Exception $e) { } catch (Exception $e) {
ray($e->getMessage()); ray($e->getMessage());
return handleError($e); return handleError($e);
} }
} }

View File

@ -36,6 +36,7 @@ public function events(Request $request)
]; ];
$json = json_encode($data); $json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Stripe::events_stripe", $json); Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Stripe::events_stripe", $json);
return; return;
} }
$webhookSecret = config('subscription.stripe_webhook_secret'); $webhookSecret = config('subscription.stripe_webhook_secret');
@ -48,7 +49,7 @@ public function events(Request $request)
); );
$webhook = Webhook::create([ $webhook = Webhook::create([
'type' => 'stripe', 'type' => 'stripe',
'payload' => $request->getContent() 'payload' => $request->getContent(),
]); ]);
$type = data_get($event, 'type'); $type = data_get($event, 'type');
$data = data_get($event, 'data.object'); $data = data_get($event, 'data.object');
@ -65,20 +66,20 @@ public function events(Request $request)
$customerId = data_get($data, 'customer'); $customerId = data_get($data, 'customer');
$team = Team::find($teamId); $team = Team::find($teamId);
$found = $team->members->where('id', $userId)->first(); $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}."); 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}."); 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(); $subscription = Subscription::where('team_id', $teamId)->first();
if ($subscription) { if ($subscription) {
send_internal_notification('Old subscription activated for team: ' . $teamId); send_internal_notification('Old subscription activated for team: '.$teamId);
$subscription->update([ $subscription->update([
'stripe_subscription_id' => $subscriptionId, 'stripe_subscription_id' => $subscriptionId,
'stripe_customer_id' => $customerId, 'stripe_customer_id' => $customerId,
'stripe_invoice_paid' => true, 'stripe_invoice_paid' => true,
]); ]);
} else { } else {
send_internal_notification('New subscription for team: ' . $teamId); send_internal_notification('New subscription for team: '.$teamId);
Subscription::create([ Subscription::create([
'team_id' => $teamId, 'team_id' => $teamId,
'stripe_subscription_id' => $subscriptionId, 'stripe_subscription_id' => $subscriptionId,
@ -95,7 +96,7 @@ public function events(Request $request)
break; break;
} }
$subscription = Subscription::where('stripe_customer_id', $customerId)->first(); $subscription = Subscription::where('stripe_customer_id', $customerId)->first();
if (!$subscription) { if (! $subscription) {
Sleep::for(5)->seconds(); Sleep::for(5)->seconds();
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
} }
@ -106,34 +107,38 @@ public function events(Request $request)
case 'invoice.payment_failed': case 'invoice.payment_failed':
$customerId = data_get($data, 'customer'); $customerId = data_get($data, 'customer');
$subscription = Subscription::where('stripe_customer_id', $customerId)->first(); $subscription = Subscription::where('stripe_customer_id', $customerId)->first();
if (!$subscription) { if (! $subscription) {
send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: ' . $customerId); send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: '.$customerId);
return response('No subscription found in Coolify.'); return response('No subscription found in Coolify.');
} }
$team = data_get($subscription, 'team'); $team = data_get($subscription, 'team');
if (!$team) { if (! $team) {
send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: ' . $customerId); send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: '.$customerId);
return response('No team found in Coolify.'); return response('No team found in Coolify.');
} }
if (!$subscription->stripe_invoice_paid) { if (! $subscription->stripe_invoice_paid) {
SubscriptionInvoiceFailedJob::dispatch($team); SubscriptionInvoiceFailedJob::dispatch($team);
send_internal_notification('Invoice payment failed: ' . $customerId); send_internal_notification('Invoice payment failed: '.$customerId);
} else { } else {
send_internal_notification('Invoice payment failed but already paid: ' . $customerId); send_internal_notification('Invoice payment failed but already paid: '.$customerId);
} }
break; break;
case 'payment_intent.payment_failed': case 'payment_intent.payment_failed':
$customerId = data_get($data, 'customer'); $customerId = data_get($data, 'customer');
$subscription = Subscription::where('stripe_customer_id', $customerId)->first(); $subscription = Subscription::where('stripe_customer_id', $customerId)->first();
if (!$subscription) { if (! $subscription) {
send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: ' . $customerId); send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: '.$customerId);
return response('No subscription found in Coolify.'); return response('No subscription found in Coolify.');
} }
if ($subscription->stripe_invoice_paid) { 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; return;
} }
send_internal_notification('Subscription payment failed for customer: ' . $customerId); send_internal_notification('Subscription payment failed for customer: '.$customerId);
break; break;
case 'customer.subscription.updated': case 'customer.subscription.updated':
$customerId = data_get($data, 'customer'); $customerId = data_get($data, 'customer');
@ -145,17 +150,19 @@ public function events(Request $request)
break; break;
} }
$subscription = Subscription::where('stripe_customer_id', $customerId)->first(); $subscription = Subscription::where('stripe_customer_id', $customerId)->first();
if (!$subscription) { if (! $subscription) {
Sleep::for(5)->seconds(); Sleep::for(5)->seconds();
$subscription = Subscription::where('stripe_customer_id', $customerId)->first(); $subscription = Subscription::where('stripe_customer_id', $customerId)->first();
} }
if (!$subscription) { if (! $subscription) {
if ($status === 'incomplete_expired') { if ($status === 'incomplete_expired') {
send_internal_notification('Subscription incomplete expired for customer: ' . $customerId); send_internal_notification('Subscription incomplete expired for customer: '.$customerId);
return response("Subscription incomplete expired", 200);
return response('Subscription incomplete expired', 200);
} }
send_internal_notification('No subscription found for: ' . $customerId); send_internal_notification('No subscription found for: '.$customerId);
return response("No subscription found", 400);
return response('No subscription found', 400);
} }
$trialEndedAlready = data_get($subscription, 'stripe_trial_already_ended'); $trialEndedAlready = data_get($subscription, 'stripe_trial_already_ended');
$cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end'); $cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end');
@ -187,7 +194,7 @@ public function events(Request $request)
$subscription->update([ $subscription->update([
'stripe_invoice_paid' => false, '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 // Trial ended but subscribed, reactive servers
@ -197,9 +204,9 @@ public function events(Request $request)
} }
if ($feedback) { if ($feedback) {
$reason = "Cancellation feedback for {$customerId}: '" . $feedback . "'"; $reason = "Cancellation feedback for {$customerId}: '".$feedback."'";
if ($comment) { if ($comment) {
$reason .= ' with comment: \'' . $comment . "'"; $reason .= ' with comment: \''.$comment."'";
} }
send_internal_notification($reason); send_internal_notification($reason);
} }
@ -207,7 +214,7 @@ public function events(Request $request)
if ($cancelAtPeriodEnd) { if ($cancelAtPeriodEnd) {
// send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id); // send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id);
} else { } else {
send_internal_notification('customer.subscription.updated for customer: ' . $customerId); send_internal_notification('customer.subscription.updated for customer: '.$customerId);
} }
} }
break; break;
@ -226,15 +233,15 @@ public function events(Request $request)
'stripe_invoice_paid' => false, 'stripe_invoice_paid' => false,
'stripe_trial_already_ended' => true, 'stripe_trial_already_ended' => true,
]); ]);
send_internal_notification('customer.subscription.deleted for customer: ' . $customerId); send_internal_notification('customer.subscription.deleted for customer: '.$customerId);
break; break;
case 'customer.subscription.trial_will_end': case 'customer.subscription.trial_will_end':
// Not used for now // Not used for now
$customerId = data_get($data, 'customer'); $customerId = data_get($data, 'customer');
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
$team = data_get($subscription, 'team'); $team = data_get($subscription, 'team');
if (!$team) { if (! $team) {
throw new Exception('No team found for subscription: ' . $subscription->id); throw new Exception('No team found for subscription: '.$subscription->id);
} }
SubscriptionTrialEndsSoonJob::dispatch($team); SubscriptionTrialEndsSoonJob::dispatch($team);
break; break;
@ -242,8 +249,8 @@ public function events(Request $request)
$customerId = data_get($data, 'customer'); $customerId = data_get($data, 'customer');
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
$team = data_get($subscription, 'team'); $team = data_get($subscription, 'team');
if (!$team) { if (! $team) {
throw new Exception('No team found for subscription: ' . $subscription->id); throw new Exception('No team found for subscription: '.$subscription->id);
} }
$team->trialEnded(); $team->trialEnded();
$subscription->update([ $subscription->update([
@ -251,19 +258,20 @@ public function events(Request $request)
'stripe_invoice_paid' => false, 'stripe_invoice_paid' => false,
]); ]);
SubscriptionTrialEndedJob::dispatch($team); SubscriptionTrialEndedJob::dispatch($team);
send_internal_notification('Subscription paused for customer: ' . $customerId); send_internal_notification('Subscription paused for customer: '.$customerId);
break; break;
default: default:
// Unhandled event type // Unhandled event type
} }
} catch (Exception $e) { } catch (Exception $e) {
if ($type !== 'payment_intent.payment_failed') { 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([ $webhook->update([
'status' => 'failed', 'status' => 'failed',
'failure_reason' => $e->getMessage(), 'failure_reason' => $e->getMessage(),
]); ]);
return response($e->getMessage(), 400); return response($e->getMessage(), 400);
} }
} }

View File

@ -17,41 +17,49 @@ public function confirm(Request $request)
try { try {
$found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first(); $found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
if ($found) { if ($found) {
if (!$found->verified) { if (! $found->verified) {
if ($found->created_at > now()->subMinutes(config('constants.waitlist.expiration'))) { if ($found->created_at > now()->subMinutes(config('constants.waitlist.expiration'))) {
$found->verified = true; $found->verified = true;
$found->save(); $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.'; return 'Thank you for confirming your email address. We will notify you when you are next in line.';
} else { } else {
$found->delete(); $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 'Your confirmation code has expired. Please sign up again.';
} }
} }
} }
return redirect()->route('dashboard'); return redirect()->route('dashboard');
} catch (Exception $e) { } catch (Exception $e) {
send_internal_notification('Waitlist confirmation failed: ' . $e->getMessage()); send_internal_notification('Waitlist confirmation failed: '.$e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
return redirect()->route('dashboard'); return redirect()->route('dashboard');
} }
} }
public function cancel(Request $request) public function cancel(Request $request)
{ {
$email = request()->get('email'); $email = request()->get('email');
$confirmation_code = request()->get('confirmation_code'); $confirmation_code = request()->get('confirmation_code');
try { try {
$found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first(); $found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
if ($found && !$found->verified) { if ($found && ! $found->verified) {
$found->delete(); $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 'Your email address has been removed from the waitlist.';
} }
return redirect()->route('dashboard'); return redirect()->route('dashboard');
} catch (Exception $e) { } catch (Exception $e) {
send_internal_notification('Waitlist cancellation failed: ' . $e->getMessage()); send_internal_notification('Waitlist cancellation failed: '.$e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
return redirect()->route('dashboard'); return redirect()->route('dashboard');
} }
} }

View File

@ -44,7 +44,7 @@ class Kernel extends HttpKernel
'api' => [ 'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api', \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Routing\Middleware\SubstituteBindings::class,
], ],
]; ];

View File

@ -20,6 +20,7 @@ public function handle(Request $request, Closure $next): Response
auth()->logout(); auth()->logout();
request()->session()->invalidate(); request()->session()->invalidate();
request()->session()->regenerateToken(); request()->session()->regenerateToken();
return $next($request); return $next($request);
} }
$force_password_reset = auth()->user()->force_password_reset; $force_password_reset = auth()->user()->force_password_reset;
@ -27,9 +28,11 @@ public function handle(Request $request, Closure $next): Response
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 $next($request);
} }
return redirect()->route('auth.force-password-reset'); return redirect()->route('auth.force-password-reset');
} }
} }
return $next($request); return $next($request);
} }
} }

View File

@ -5,8 +5,8 @@
use App\Providers\RouteServiceProvider; use App\Providers\RouteServiceProvider;
use Closure; use Closure;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;
class DecideWhatToDoWithUser class DecideWhatToDoWithUser
{ {
@ -16,33 +16,37 @@ public function handle(Request $request, Closure $next): Response
$currentTeam = auth()->user()?->recreate_personal_team(); $currentTeam = auth()->user()?->recreate_personal_team();
refreshSession($currentTeam); refreshSession($currentTeam);
} }
if(auth()?->user()?->currentTeam()){ if (auth()?->user()?->currentTeam()) {
refreshSession(auth()->user()->currentTeam()); refreshSession(auth()->user()->currentTeam());
} }
if (!auth()->user() || !isCloud() || isInstanceAdmin()) { if (! auth()->user() || ! isCloud() || isInstanceAdmin()) {
if (!isCloud() && showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) { if (! isCloud() && showBoarding() && ! in_array($request->path(), allowedPathsForBoardingAccounts())) {
return redirect()->route('onboarding'); return redirect()->route('onboarding');
} }
return $next($request); return $next($request);
} }
if (!auth()->user()->hasVerifiedEmail()) { if (! auth()->user()->hasVerifiedEmail()) {
if ($request->path() === 'verify' || in_array($request->path(), allowedPathsForInvalidAccounts()) || $request->routeIs('verify.verify')) { if ($request->path() === 'verify' || in_array($request->path(), allowedPathsForInvalidAccounts()) || $request->routeIs('verify.verify')) {
return $next($request); return $next($request);
} }
return redirect()->route('verify.email'); return redirect()->route('verify.email');
} }
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) { if (! isSubscriptionActive() && ! isSubscriptionOnGracePeriod()) {
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) { if (! in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
if (Str::startsWith($request->path(), 'invitations')) { if (Str::startsWith($request->path(), 'invitations')) {
return $next($request); return $next($request);
} }
return redirect()->route('subscription.index'); 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')) { if (Str::startsWith($request->path(), 'invitations')) {
return $next($request); return $next($request);
} }
return redirect()->route('onboarding'); return redirect()->route('onboarding');
} }
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') { 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')) { if (isSubscriptionActive() && $request->routeIs('subscription.index')) {
return redirect(RouteServiceProvider::HOME); return redirect(RouteServiceProvider::HOME);
} }
return $next($request); return $next($request);
} }
} }

View File

@ -13,6 +13,6 @@ class PreventRequestsDuringMaintenance extends Middleware
*/ */
protected $except = [ protected $except = [
'webhooks/*', 'webhooks/*',
'/api/health' '/api/health',
]; ];
} }

View File

@ -24,6 +24,7 @@ public function handle(Request $request, Closure $next, string ...$guards): Resp
return redirect(RouteServiceProvider::HOME); return redirect(RouteServiceProvider::HOME);
} }
} }
return $next($request); return $next($request);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -12,11 +12,12 @@
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public string $build_logs_url; public string $build_logs_url;
public string $body; public string $body;
public function __construct( public function __construct(
@ -32,25 +33,27 @@ public function handle()
try { try {
if ($this->application->is_public_repository()) { if ($this->application->is_public_repository()) {
ray('Public repository. Skipping comment update.'); ray('Public repository. Skipping comment update.');
return; return;
} }
if ($this->status === ProcessStatus::CLOSED) { if ($this->status === ProcessStatus::CLOSED) {
$this->delete_comment(); $this->delete_comment();
return; return;
} else if ($this->status === ProcessStatus::IN_PROGRESS) { } elseif ($this->status === ProcessStatus::IN_PROGRESS) {
$this->body = "The preview deployment is in progress. 🟡\n\n"; $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"; $this->body = "The preview deployment is ready. 🟢\n\n";
if ($this->preview->fqdn) { if ($this->preview->fqdn) {
$this->body .= "[Open Preview]({$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->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 .= '[Open Build Logs]('.$this->build_logs_url.")\n\n\n";
$this->body .= "Last updated at: " . now()->toDateTimeString() . " CET"; $this->body .= 'Last updated at: '.now()->toDateTimeString().' CET';
ray('Updating comment', $this->body); ray('Updating comment', $this->body);
if ($this->preview->pull_request_issue_comment_id) { if ($this->preview->pull_request_issue_comment_id) {
@ -60,6 +63,7 @@ public function handle()
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
ray($e); ray($e);
return $e; return $e;
} }
} }
@ -83,6 +87,7 @@ private function create_comment()
$this->preview->pull_request_issue_comment_id = $data['id']; $this->preview->pull_request_issue_comment_id = $data['id'];
$this->preview->save(); $this->preview->save();
} }
private function delete_comment() 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'); githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->preview->pull_request_issue_comment_id}", method: 'delete');

View File

@ -10,19 +10,23 @@
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class ApplicationRestartJob implements ShouldBeEncrypted, ShouldQueue
class ApplicationRestartJob implements ShouldQueue, ShouldBeEncrypted
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ExecuteRemoteCommand; use Dispatchable, ExecuteRemoteCommand, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 3600; public $timeout = 3600;
public $tries = 1; public $tries = 1;
public string $applicationDeploymentQueueId; public string $applicationDeploymentQueueId;
public function __construct(string $applicationDeploymentQueueId) public function __construct(string $applicationDeploymentQueueId)
{ {
$this->applicationDeploymentQueueId = $applicationDeploymentQueueId; $this->applicationDeploymentQueueId = $applicationDeploymentQueueId;
} }
public function handle() {
public function handle()
{
ray('Restarting application'); ray('Restarting application');
} }
} }

View File

@ -15,13 +15,14 @@
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Sleep; use Illuminate\Support\Sleep;
class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted class CheckLogDrainContainerJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(public Server $server) public function __construct(public Server $server)
{ {
} }
public function middleware(): array public function middleware(): array
{ {
return [(new WithoutOverlapping($this->server->id))->dontRelease()]; return [(new WithoutOverlapping($this->server->id))->dontRelease()];
@ -31,6 +32,7 @@ public function uniqueId(): int
{ {
return $this->server->id; return $this->server->id;
} }
public function healthcheck() public function healthcheck()
{ {
$status = instant_remote_process(["docker inspect --format='{{json .State.Status}}' coolify-log-drain"], $this->server, false); $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; return false;
} }
} }
public function handle() public function handle()
{ {
// ray("checking log drain statuses for {$this->server->id}"); // ray("checking log drain statuses for {$this->server->id}");
try { try {
if (!$this->server->isFunctional()) { if (! $this->server->isFunctional()) {
return; return;
}; }
$containers = instant_remote_process(["docker container ls -q"], $this->server, false); $containers = instant_remote_process(['docker container ls -q'], $this->server, false);
if (!$containers) { if (! $containers) {
return; return;
} }
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server); $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server);
@ -57,7 +60,7 @@ public function handle()
$foundLogDrainContainer = $containers->filter(function ($value, $key) { $foundLogDrainContainer = $containers->filter(function ($value, $key) {
return data_get($value, 'Name') === '/coolify-log-drain'; return data_get($value, 'Name') === '/coolify-log-drain';
})->first(); })->first();
if (!$foundLogDrainContainer || !$this->healthcheck()) { if (! $foundLogDrainContainer || ! $this->healthcheck()) {
ray('Log drain container not found or unhealthy. Restarting...'); ray('Log drain container not found or unhealthy. Restarting...');
InstallLogDrain::run($this->server); InstallLogDrain::run($this->server);
Sleep::for(10)->seconds(); Sleep::for(10)->seconds();
@ -66,9 +69,10 @@ public function handle()
$this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server)); $this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
$this->server->update(['log_drain_notification_sent' => false]); $this->server->update(['log_drain_notification_sent' => false]);
} }
return; return;
} }
if (!$this->server->log_drain_notification_sent) { if (! $this->server->log_drain_notification_sent) {
ray('Log drain container still unhealthy. Sending notification...'); ray('Log drain container still unhealthy. Sending notification...');
// $this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null)); // $this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
$this->server->update(['log_drain_notification_sent' => true]); $this->server->update(['log_drain_notification_sent' => true]);
@ -80,8 +84,11 @@ public function handle()
} }
} }
} catch (\Throwable $e) { } 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()); ray($e->getMessage());
return handleError($e); return handleError($e);
} }
} }

View File

@ -10,7 +10,7 @@
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class CheckResaleLicenseJob implements ShouldQueue, ShouldBeEncrypted class CheckResaleLicenseJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -23,7 +23,7 @@ public function handle(): void
try { try {
CheckResaleLicense::run(); CheckResaleLicense::run();
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('CheckResaleLicenseJob failed with: ' . $e->getMessage()); send_internal_notification('CheckResaleLicenseJob failed with: '.$e->getMessage());
ray($e); ray($e);
throw $e; throw $e;
} }

View File

@ -11,7 +11,7 @@
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class CleanupHelperContainersJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -22,18 +22,18 @@ public function __construct(public Server $server)
public function handle(): void public function handle(): void
{ {
try { 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 = 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); $containers = format_docker_command_output_to_json($containers);
if ($containers->count() > 0) { if ($containers->count() > 0) {
foreach ($containers as $container) { foreach ($containers as $container) {
$containerId = data_get($container,'ID'); $containerId = data_get($container, 'ID');
ray('Removing container ' . $containerId); ray('Removing container '.$containerId);
instant_remote_process(['docker container rm -f ' . $containerId], $this->server, false); instant_remote_process(['docker container rm -f '.$containerId], $this->server, false);
} }
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('CleanupHelperContainersJob failed with error: ' . $e->getMessage()); send_internal_notification('CleanupHelperContainersJob failed with error: '.$e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
} }
} }

View File

@ -12,7 +12,7 @@
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -31,13 +31,13 @@ public function handle(): void
try { try {
// $this->cleanup_waitlist(); // $this->cleanup_waitlist();
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage()); send_internal_notification('CleanupInstanceStuffsJob failed with error: '.$e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
} }
try { try {
$this->cleanup_invitation_link(); $this->cleanup_invitation_link();
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage()); send_internal_notification('CleanupInstanceStuffsJob failed with error: '.$e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
} }
} }
@ -49,6 +49,7 @@ private function cleanup_waitlist()
$item->delete(); $item->delete();
} }
} }
private function cleanup_invitation_link() private function cleanup_invitation_link()
{ {
$invitation = TeamInvitation::all(); $invitation = TeamInvitation::all();

View File

@ -12,18 +12,21 @@
use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted class ContainerStatusJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 4; public $tries = 4;
public function backoff(): int public function backoff(): int
{ {
return isDev() ? 1 : 3; return isDev() ? 1 : 3;
} }
public function __construct(public Server $server) public function __construct(public Server $server)
{ {
} }
public function middleware(): array public function middleware(): array
{ {
return [(new WithoutOverlapping($this->server->uuid))]; return [(new WithoutOverlapping($this->server->uuid))];

View File

@ -11,7 +11,7 @@
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Spatie\Activitylog\Models\Activity; use Spatie\Activitylog\Models\Activity;
class CoolifyTask implements ShouldQueue, ShouldBeEncrypted class CoolifyTask implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -35,7 +35,7 @@ public function handle(): void
'activity' => $this->activity, 'activity' => $this->activity,
'ignore_errors' => $this->ignore_errors, 'ignore_errors' => $this->ignore_errors,
'call_event_on_finish' => $this->call_event_on_finish, '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(); $remote_process();

View File

@ -25,26 +25,37 @@
use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Throwable;
class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public ?Team $team = null; public ?Team $team = null;
public Server $server; public Server $server;
public ScheduledDatabaseBackup $backup; public ScheduledDatabaseBackup $backup;
public StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database; public StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database;
public ?string $container_name = null; public ?string $container_name = null;
public ?string $directory_name = null; public ?string $directory_name = null;
public ?ScheduledDatabaseBackupExecution $backup_log = null; public ?ScheduledDatabaseBackupExecution $backup_log = null;
public string $backup_status = 'failed'; public string $backup_status = 'failed';
public ?string $backup_location = null; public ?string $backup_location = null;
public string $backup_dir; public string $backup_dir;
public string $backup_file; public string $backup_file;
public int $size = 0; public int $size = 0;
public ?string $backup_output = null; public ?string $backup_output = null;
public ?S3Storage $s3 = null; public ?S3Storage $s3 = null;
public function __construct($backup) public function __construct($backup)
@ -84,11 +95,13 @@ public function handle(): void
$this->backup->update(['status' => 'failed']); $this->backup->update(['status' => 'failed']);
StopDatabase::run($this->database); StopDatabase::run($this->database);
$this->database->delete(); $this->database->delete();
return; return;
} }
$status = Str::of(data_get($this->database, 'status')); $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'); ray('database not running');
return; return;
} }
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') { 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(); $serviceName = str($this->database->service->name)->slug();
if (str($databaseType)->contains('postgres')) { if (str($databaseType)->contains('postgres')) {
$this->container_name = "{$this->database->name}-$serviceUuid"; $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_"; $commands[] = "docker exec $this->container_name env | grep POSTGRES_";
$envs = instant_remote_process($commands, $this->server); $envs = instant_remote_process($commands, $this->server);
$envs = str($envs)->explode("\n"); $envs = str($envs)->explode("\n");
@ -120,9 +133,9 @@ public function handle(): void
} else { } else {
$databasesToBackup = $this->database->postgres_user; $databasesToBackup = $this->database->postgres_user;
} }
} else if (str($databaseType)->contains('mysql')) { } elseif (str($databaseType)->contains('mysql')) {
$this->container_name = "{$this->database->name}-$serviceUuid"; $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_"; $commands[] = "docker exec $this->container_name env | grep MYSQL_";
$envs = instant_remote_process($commands, $this->server); $envs = instant_remote_process($commands, $this->server);
$envs = str($envs)->explode("\n"); $envs = str($envs)->explode("\n");
@ -143,9 +156,9 @@ public function handle(): void
} else { } else {
throw new \Exception('MYSQL_DATABASE not found'); 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->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"; $commands[] = "docker exec $this->container_name env";
$envs = instant_remote_process($commands, $this->server); $envs = instant_remote_process($commands, $this->server);
$envs = str($envs)->explode("\n"); $envs = str($envs)->explode("\n");
@ -184,7 +197,7 @@ public function handle(): void
} else { } else {
$databaseName = str($this->database->name)->slug()->value(); $databaseName = str($this->database->name)->slug()->value();
$this->container_name = $this->database->uuid; $this->container_name = $this->database->uuid;
$this->directory_name = $databaseName . '-' . $this->container_name; $this->directory_name = $databaseName.'-'.$this->container_name;
$databaseType = $this->database->type(); $databaseType = $this->database->type();
$databasesToBackup = data_get($this->backup, 'databases_to_backup'); $databasesToBackup = data_get($this->backup, 'databases_to_backup');
} }
@ -192,11 +205,11 @@ public function handle(): void
if (is_null($databasesToBackup)) { if (is_null($databasesToBackup)) {
if (str($databaseType)->contains('postgres')) { if (str($databaseType)->contains('postgres')) {
$databasesToBackup = [$this->database->postgres_db]; $databasesToBackup = [$this->database->postgres_db];
} else if (str($databaseType)->contains('mongodb')) { } elseif (str($databaseType)->contains('mongodb')) {
$databasesToBackup = ['*']; $databasesToBackup = ['*'];
} else if (str($databaseType)->contains('mysql')) { } elseif (str($databaseType)->contains('mysql')) {
$databasesToBackup = [$this->database->mysql_database]; $databasesToBackup = [$this->database->mysql_database];
} else if (str($databaseType)->contains('mariadb')) { } elseif (str($databaseType)->contains('mariadb')) {
$databasesToBackup = [$this->database->mariadb_database]; $databasesToBackup = [$this->database->mariadb_database];
} else { } else {
return; return;
@ -206,16 +219,16 @@ public function handle(): void
// Format: db1,db2,db3 // Format: db1,db2,db3
$databasesToBackup = explode(',', $databasesToBackup); $databasesToBackup = explode(',', $databasesToBackup);
$databasesToBackup = array_map('trim', $databasesToBackup); $databasesToBackup = array_map('trim', $databasesToBackup);
} else if (str($databaseType)->contains('mongodb')) { } elseif (str($databaseType)->contains('mongodb')) {
// Format: db1:collection1,collection2|db2:collection3,collection4 // Format: db1:collection1,collection2|db2:collection3,collection4
$databasesToBackup = explode('|', $databasesToBackup); $databasesToBackup = explode('|', $databasesToBackup);
$databasesToBackup = array_map('trim', $databasesToBackup); $databasesToBackup = array_map('trim', $databasesToBackup);
ray($databasesToBackup); ray($databasesToBackup);
} else if (str($databaseType)->contains('mysql')) { } elseif (str($databaseType)->contains('mysql')) {
// Format: db1,db2,db3 // Format: db1,db2,db3
$databasesToBackup = explode(',', $databasesToBackup); $databasesToBackup = explode(',', $databasesToBackup);
$databasesToBackup = array_map('trim', $databasesToBackup); $databasesToBackup = array_map('trim', $databasesToBackup);
} else if (str($databaseType)->contains('mariadb')) { } elseif (str($databaseType)->contains('mariadb')) {
// Format: db1,db2,db3 // Format: db1,db2,db3
$databasesToBackup = explode(',', $databasesToBackup); $databasesToBackup = explode(',', $databasesToBackup);
$databasesToBackup = array_map('trim', $databasesToBackup); $databasesToBackup = array_map('trim', $databasesToBackup);
@ -223,28 +236,28 @@ public function handle(): void
return; 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') { if ($this->database->name === 'coolify-db') {
$databasesToBackup = ['coolify']; $databasesToBackup = ['coolify'];
$this->directory_name = $this->container_name = "coolify-db"; $this->directory_name = $this->container_name = 'coolify-db';
$ip = Str::slug($this->server->ip); $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) { foreach ($databasesToBackup as $database) {
$size = 0; $size = 0;
ray('Backing up ' . $database); ray('Backing up '.$database);
try { try {
if (str($databaseType)->contains('postgres')) { if (str($databaseType)->contains('postgres')) {
$this->backup_file = "/pg-dump-$database-" . Carbon::now()->timestamp . ".dmp"; $this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp';
$this->backup_location = $this->backup_dir . $this->backup_file; $this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([ $this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database, 'database_name' => $database,
'filename' => $this->backup_location, 'filename' => $this->backup_location,
'scheduled_database_backup_id' => $this->backup->id, 'scheduled_database_backup_id' => $this->backup->id,
]); ]);
$this->backup_standalone_postgresql($database); $this->backup_standalone_postgresql($database);
} else if (str($databaseType)->contains('mongodb')) { } elseif (str($databaseType)->contains('mongodb')) {
if ($database === '*') { if ($database === '*') {
$database = 'all'; $database = 'all';
$databaseName = 'all'; $databaseName = 'all';
@ -255,26 +268,26 @@ public function handle(): void
$databaseName = $database; $databaseName = $database;
} }
} }
$this->backup_file = "/mongo-dump-$databaseName-" . Carbon::now()->timestamp . ".tar.gz"; $this->backup_file = "/mongo-dump-$databaseName-".Carbon::now()->timestamp.'.tar.gz';
$this->backup_location = $this->backup_dir . $this->backup_file; $this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([ $this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $databaseName, 'database_name' => $databaseName,
'filename' => $this->backup_location, 'filename' => $this->backup_location,
'scheduled_database_backup_id' => $this->backup->id, 'scheduled_database_backup_id' => $this->backup->id,
]); ]);
$this->backup_standalone_mongodb($database); $this->backup_standalone_mongodb($database);
} else if (str($databaseType)->contains('mysql')) { } elseif (str($databaseType)->contains('mysql')) {
$this->backup_file = "/mysql-dump-$database-" . Carbon::now()->timestamp . ".dmp"; $this->backup_file = "/mysql-dump-$database-".Carbon::now()->timestamp.'.dmp';
$this->backup_location = $this->backup_dir . $this->backup_file; $this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([ $this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database, 'database_name' => $database,
'filename' => $this->backup_location, 'filename' => $this->backup_location,
'scheduled_database_backup_id' => $this->backup->id, 'scheduled_database_backup_id' => $this->backup->id,
]); ]);
$this->backup_standalone_mysql($database); $this->backup_standalone_mysql($database);
} else if (str($databaseType)->contains('mariadb')) { } elseif (str($databaseType)->contains('mariadb')) {
$this->backup_file = "/mariadb-dump-$database-" . Carbon::now()->timestamp . ".dmp"; $this->backup_file = "/mariadb-dump-$database-".Carbon::now()->timestamp.'.dmp';
$this->backup_location = $this->backup_dir . $this->backup_file; $this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([ $this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database, 'database_name' => $database,
'filename' => $this->backup_location, 'filename' => $this->backup_location,
@ -301,27 +314,28 @@ public function handle(): void
'status' => 'failed', 'status' => 'failed',
'message' => $this->backup_output, 'message' => $this->backup_output,
'size' => $size, '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)); $this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output, $database));
} }
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage()); send_internal_notification('DatabaseBackupJob failed with: '.$e->getMessage());
throw $e; throw $e;
} finally { } finally {
BackupCreated::dispatch($this->team->id); BackupCreated::dispatch($this->team->id);
} }
} }
private function backup_standalone_mongodb(string $databaseWithCollections): void private function backup_standalone_mongodb(string $databaseWithCollections): void
{ {
try { try {
ray($this->database->toArray()); ray($this->database->toArray());
$url = $this->database->get_db_url(useInternal: true); $url = $this->database->get_db_url(useInternal: true);
if ($databaseWithCollections === 'all') { if ($databaseWithCollections === 'all') {
$commands[] = "mkdir -p " . $this->backup_dir; $commands[] = 'mkdir -p '.$this->backup_dir;
if (str($this->database->image)->startsWith('mongo:4.0')) { if (str($this->database->image)->startsWith('mongo:4.0')) {
$commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --archive > $this->backup_location"; $commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --archive > $this->backup_location";
} else { } else {
@ -335,7 +349,7 @@ private function backup_standalone_mongodb(string $databaseWithCollections): voi
$databaseName = $databaseWithCollections; $databaseName = $databaseWithCollections;
$collectionsToExclude = collect(); $collectionsToExclude = collect();
} }
$commands[] = "mkdir -p " . $this->backup_dir; $commands[] = 'mkdir -p '.$this->backup_dir;
if ($collectionsToExclude->count() === 0) { if ($collectionsToExclude->count() === 0) {
if (str($this->database->image)->startsWith('mongo:4.0')) { if (str($this->database->image)->startsWith('mongo:4.0')) {
$commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --archive > $this->backup_location"; $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 { } else {
if (str($this->database->image)->startsWith('mongo:4.0')) { 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 { } 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 === '') { if ($this->backup_output === '') {
$this->backup_output = null; $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) { } catch (\Throwable $e) {
$this->add_to_backup_output($e->getMessage()); $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; throw $e;
} }
} }
private function backup_standalone_postgresql(string $database): void private function backup_standalone_postgresql(string $database): void
{ {
try { 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"; $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 = instant_remote_process($commands, $this->server);
$this->backup_output = trim($this->backup_output); $this->backup_output = trim($this->backup_output);
if ($this->backup_output === '') { if ($this->backup_output === '') {
$this->backup_output = null; $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) { } catch (\Throwable $e) {
$this->add_to_backup_output($e->getMessage()); $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; throw $e;
} }
} }
private function backup_standalone_mysql(string $database): void private function backup_standalone_mysql(string $database): void
{ {
try { 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"; $commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
ray($commands); ray($commands);
$this->backup_output = instant_remote_process($commands, $this->server); $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 === '') { if ($this->backup_output === '') {
$this->backup_output = null; $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) { } catch (\Throwable $e) {
$this->add_to_backup_output($e->getMessage()); $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; throw $e;
} }
} }
private function backup_standalone_mariadb(string $database): void private function backup_standalone_mariadb(string $database): void
{ {
try { 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"; $commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
ray($commands); ray($commands);
$this->backup_output = instant_remote_process($commands, $this->server); $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 === '') { if ($this->backup_output === '') {
$this->backup_output = null; $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) { } catch (\Throwable $e) {
$this->add_to_backup_output($e->getMessage()); $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; throw $e;
} }
} }
private function add_to_backup_output($output): void private function add_to_backup_output($output): void
{ {
if ($this->backup_output) { if ($this->backup_output) {
$this->backup_output = $this->backup_output . "\n" . $output; $this->backup_output = $this->backup_output."\n".$output;
} else { } else {
$this->backup_output = $output; $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}/"; $commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
instant_remote_process($commands, $this->server); instant_remote_process($commands, $this->server);
$this->add_to_backup_output('Uploaded to S3.'); $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) { } catch (\Throwable $e) {
$this->add_to_backup_output($e->getMessage()); $this->add_to_backup_output($e->getMessage());
throw $e; throw $e;

View File

@ -3,19 +3,16 @@
namespace App\Jobs; namespace App\Jobs;
use App\Models\ScheduledDatabaseBackup; use App\Models\ScheduledDatabaseBackup;
use App\Models\Server;
use App\Models\Team; use App\Models\Team;
use App\Notifications\Database\DailyBackup; use App\Notifications\Database\DailyBackup;
use App\Notifications\Server\HighDiskUsage;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted; use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class DatabaseBackupStatusJob implements ShouldQueue, ShouldBeEncrypted class DatabaseBackupStatusJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -42,13 +39,6 @@ public function handle()
// } // }
// } // }
// $scheduled_backups = ScheduledDatabaseBackup::all(); // $scheduled_backups = ScheduledDatabaseBackup::all();
// $databases = collect(); // $databases = collect();
// $teams = collect(); // $teams = collect();

View File

@ -24,7 +24,7 @@
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -60,7 +60,7 @@ public function handle()
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
ray($e->getMessage()); ray($e->getMessage());
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage()); send_internal_notification('ContainerStoppingJob failed with: '.$e->getMessage());
throw $e; throw $e;
} finally { } finally {
Artisan::queue('cleanup:stucked-resources'); Artisan::queue('cleanup:stucked-resources');

View File

@ -10,21 +10,22 @@
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use RuntimeException; use RuntimeException;
class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 300; public $timeout = 300;
public ?int $usageBefore = null; public ?int $usageBefore = null;
public function __construct(public Server $server) public function __construct(public Server $server)
{ {
} }
public function handle(): void public function handle(): void
{ {
try { try {
@ -32,35 +33,36 @@ public function handle(): void
$this->server->applications()->each(function ($application) use (&$isInprogress) { $this->server->applications()->each(function ($application) use (&$isInprogress) {
if ($application->isDeploymentInprogress()) { if ($application->isDeploymentInprogress()) {
$isInprogress = true; $isInprogress = true;
return; return;
} }
}); });
if ($isInprogress) { if ($isInprogress) {
throw new RuntimeException('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping...'); throw new RuntimeException('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping...');
} }
if (!$this->server->isFunctional()) { if (! $this->server->isFunctional()) {
return; return;
} }
$this->usageBefore = $this->server->getDiskUsage(); $this->usageBefore = $this->server->getDiskUsage();
ray('Usage before: ' . $this->usageBefore); ray('Usage before: '.$this->usageBefore);
if ($this->usageBefore >= $this->server->settings->cleanup_after_percentage) { 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); CleanupDocker::run($this->server);
$usageAfter = $this->server->getDiskUsage(); $usageAfter = $this->server->getDiskUsage();
if ($usageAfter < $this->usageBefore) { if ($usageAfter < $this->usageBefore) {
$this->server->team?->notify(new DockerCleanup($this->server, 'Saved ' . ($this->usageBefore - $usageAfter) . '% disk space.')); $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); // 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); // 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 { } 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 { } else {
ray('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); Log::info('No need to clean up '.$this->server->name);
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage()); send_internal_notification('DockerCleanupJob failed with: '.$e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
throw $e; throw $e;
} }

View File

@ -12,18 +12,21 @@
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
class GithubAppPermissionJob implements ShouldQueue, ShouldBeEncrypted class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 4; public $tries = 4;
public function backoff(): int public function backoff(): int
{ {
return isDev() ? 1 : 3; return isDev() ? 1 : 3;
} }
public function __construct(public GithubApp $github_app) public function __construct(public GithubApp $github_app)
{ {
} }
public function middleware(): array public function middleware(): array
{ {
return [(new WithoutOverlapping($this->github_app->uuid))]; return [(new WithoutOverlapping($this->github_app->uuid))];
@ -40,7 +43,7 @@ public function handle()
$github_access_token = generate_github_jwt_token($this->github_app); $github_access_token = generate_github_jwt_token($this->github_app);
$response = Http::withHeaders([ $response = Http::withHeaders([
'Authorization' => "Bearer $github_access_token", 'Authorization' => "Bearer $github_access_token",
'Accept' => 'application/vnd.github+json' 'Accept' => 'application/vnd.github+json',
])->get("{$this->github_app->api_url}/app"); ])->get("{$this->github_app->api_url}/app");
$response = $response->json(); $response = $response->json();
$permissions = data_get($response, 'permissions'); $permissions = data_get($response, 'permissions');
@ -51,7 +54,7 @@ public function handle()
$this->github_app->save(); $this->github_app->save();
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('GithubAppPermissionJob failed with: ' . $e->getMessage()); send_internal_notification('GithubAppPermissionJob failed with: '.$e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
throw $e; throw $e;
} }

View File

@ -11,11 +11,12 @@
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted class InstanceAutoUpdateJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 600; public $timeout = 600;
public $tries = 1; public $tries = 1;
public function __construct() public function __construct()

View File

@ -13,7 +13,7 @@
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
class PullCoolifyImageJob implements ShouldQueue, ShouldBeEncrypted class PullCoolifyImageJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -22,6 +22,7 @@ class PullCoolifyImageJob implements ShouldQueue, ShouldBeEncrypted
public function __construct() public function __construct()
{ {
} }
public function handle(): void public function handle(): void
{ {
try { try {
@ -39,7 +40,7 @@ public function handle(): void
$settings = InstanceSettings::get(); $settings = InstanceSettings::get();
$current_version = config('version'); $current_version = config('version');
if (!$settings->is_auto_update_enabled) { if (! $settings->is_auto_update_enabled) {
return; return;
} }
if ($latest_version === $current_version) { if ($latest_version === $current_version) {
@ -49,8 +50,8 @@ public function handle(): void
return; return;
} }
instant_remote_process([ instant_remote_process([
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh", 'curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh',
"bash /data/coolify/source/upgrade.sh $latest_version" "bash /data/coolify/source/upgrade.sh $latest_version",
], $server); ], $server);
} catch (\Throwable $e) { } catch (\Throwable $e) {
throw $e; throw $e;

View File

@ -11,7 +11,7 @@
use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class PullHelperImageJob implements ShouldQueue, ShouldBeEncrypted class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -26,9 +26,11 @@ public function uniqueId(): string
{ {
return $this->server->uuid; return $this->server->uuid;
} }
public function __construct(public Server $server) public function __construct(public Server $server)
{ {
} }
public function handle(): void public function handle(): void
{ {
try { try {
@ -37,7 +39,7 @@ public function handle(): void
instant_remote_process(["docker pull -q {$helperImage}"], $this->server, false); instant_remote_process(["docker pull -q {$helperImage}"], $this->server, false);
ray('PullHelperImageJob done'); ray('PullHelperImageJob done');
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('PullHelperImageJob failed with: ' . $e->getMessage()); send_internal_notification('PullHelperImageJob failed with: '.$e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
throw $e; throw $e;
} }

View File

@ -12,7 +12,7 @@
use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class PullSentinelImageJob implements ShouldQueue, ShouldBeEncrypted class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -27,15 +27,18 @@ public function uniqueId(): string
{ {
return $this->server->uuid; return $this->server->uuid;
} }
public function __construct(public Server $server) public function __construct(public Server $server)
{ {
} }
public function handle(): void public function handle(): void
{ {
try { try {
$version = get_latest_sentinel_version(); $version = get_latest_sentinel_version();
if (!$version) { if (! $version) {
ray('Failed to get latest Sentinel version'); ray('Failed to get latest Sentinel version');
return; return;
} }
$local_version = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/version"'], $this->server, false); $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, '<')) { if (version_compare($local_version, $version, '<')) {
StartSentinel::run($this->server, $version, true); StartSentinel::run($this->server, $version, true);
return; return;
} }
ray('Sentinel image is up to date'); ray('Sentinel image is up to date');
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('PullSentinelImageJob failed with: ' . $e->getMessage()); send_internal_notification('PullSentinelImageJob failed with: '.$e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
throw $e; throw $e;
} }

View File

@ -2,7 +2,6 @@
namespace App\Jobs; namespace App\Jobs;
use App\Models\Server;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted; use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
@ -12,7 +11,7 @@
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
class PullTemplatesFromCDN implements ShouldQueue, ShouldBeEncrypted class PullTemplatesFromCDN implements ShouldBeEncrypted, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -21,21 +20,22 @@ class PullTemplatesFromCDN implements ShouldQueue, ShouldBeEncrypted
public function __construct() public function __construct()
{ {
} }
public function handle(): void public function handle(): void
{ {
try { try {
if (!isDev()) { if (! isDev()) {
ray('PullTemplatesAndVersions service-templates'); ray('PullTemplatesAndVersions service-templates');
$response = Http::retry(3, 1000)->get(config('constants.services.official')); $response = Http::retry(3, 1000)->get(config('constants.services.official'));
if ($response->successful()) { if ($response->successful()) {
$services = $response->json(); $services = $response->json();
File::put(base_path('templates/service-templates.json'), json_encode($services)); File::put(base_path('templates/service-templates.json'), json_encode($services));
} else { } else {
send_internal_notification('PullTemplatesAndVersions failed with: ' . $response->status() . ' ' . $response->body()); send_internal_notification('PullTemplatesAndVersions failed with: '.$response->status().' '.$response->body());
} }
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('PullTemplatesAndVersions failed with: ' . $e->getMessage()); send_internal_notification('PullTemplatesAndVersions failed with: '.$e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
} }
} }

Some files were not shown because too many files have changed in this diff Show More