Revert "rector: arrrrr"

This reverts commit 16c0cd10d8.
This commit is contained in:
Andras Bacsai
2025-01-07 15:31:43 +01:00
parent da07b4fdcf
commit 1fe4dd722b
349 changed files with 3689 additions and 4184 deletions

View File

@@ -4,7 +4,6 @@ namespace App\Actions\Application;
use App\Actions\Server\CleanupDocker; use App\Actions\Server\CleanupDocker;
use App\Models\Application; use App\Models\Application;
use Exception;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
class StopApplication class StopApplication
@@ -24,7 +23,7 @@ class StopApplication
if ($server->isSwarm()) { if ($server->isSwarm()) {
instant_remote_process(["docker stack rm {$application->uuid}"], $server); instant_remote_process(["docker stack rm {$application->uuid}"], $server);
return null; return;
} }
$containersToStop = $application->getContainersToStop($previewDeployments); $containersToStop = $application->getContainersToStop($previewDeployments);
@@ -37,10 +36,8 @@ class StopApplication
if ($dockerCleanup) { if ($dockerCleanup) {
CleanupDocker::dispatch($server, true); CleanupDocker::dispatch($server, true);
} }
} catch (Exception $e) { } catch (\Exception $e) {
return $e->getMessage(); return $e->getMessage();
} }
return null;
} }
} }

View File

@@ -4,7 +4,6 @@ namespace App\Actions\Application;
use App\Models\Application; use App\Models\Application;
use App\Models\Server; use App\Models\Server;
use Exception;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
class StopApplicationOneServer class StopApplicationOneServer
@@ -14,7 +13,7 @@ class StopApplicationOneServer
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 null; return;
} }
if (! $server->isFunctional()) { if (! $server->isFunctional()) {
return 'Server is not functional'; return 'Server is not functional';
@@ -32,10 +31,8 @@ class StopApplicationOneServer
} }
} }
} }
} catch (Exception $e) { } catch (\Exception $e) {
return $e->getMessage(); return $e->getMessage();
} }
return null;
} }
} }

View File

@@ -4,7 +4,6 @@ namespace App\Actions\CoolifyTask;
use App\Data\CoolifyTaskArgs; use App\Data\CoolifyTaskArgs;
use App\Jobs\CoolifyTask; use App\Jobs\CoolifyTask;
use Illuminate\Database\Eloquent\Model;
use Spatie\Activitylog\Models\Activity; use Spatie\Activitylog\Models\Activity;
/** /**
@@ -18,36 +17,36 @@ class PrepareCoolifyTask
protected CoolifyTaskArgs $remoteProcessArgs; protected CoolifyTaskArgs $remoteProcessArgs;
public function __construct(CoolifyTaskArgs $coolifyTaskArgs) public function __construct(CoolifyTaskArgs $remoteProcessArgs)
{ {
$this->remoteProcessArgs = $coolifyTaskArgs; $this->remoteProcessArgs = $remoteProcessArgs;
if ($coolifyTaskArgs->model instanceof Model) { if ($remoteProcessArgs->model) {
$properties = $coolifyTaskArgs->toArray(); $properties = $remoteProcessArgs->toArray();
unset($properties['model']); unset($properties['model']);
$this->activity = activity() $this->activity = activity()
->withProperties($properties) ->withProperties($properties)
->performedOn($coolifyTaskArgs->model) ->performedOn($remoteProcessArgs->model)
->event($coolifyTaskArgs->type) ->event($remoteProcessArgs->type)
->log('[]'); ->log('[]');
} else { } else {
$this->activity = activity() $this->activity = activity()
->withProperties($coolifyTaskArgs->toArray()) ->withProperties($remoteProcessArgs->toArray())
->event($coolifyTaskArgs->type) ->event($remoteProcessArgs->type)
->log('[]'); ->log('[]');
} }
} }
public function __invoke(): Activity public function __invoke(): Activity
{ {
$coolifyTask = new CoolifyTask( $job = new CoolifyTask(
activity: $this->activity, activity: $this->activity,
ignore_errors: $this->remoteProcessArgs->ignore_errors, ignore_errors: $this->remoteProcessArgs->ignore_errors,
call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish,
call_event_data: $this->remoteProcessArgs->call_event_data, call_event_data: $this->remoteProcessArgs->call_event_data,
); );
dispatch($coolifyTask); dispatch($job);
$this->activity->refresh(); $this->activity->refresh();
return $this->activity; return $this->activity;

View File

@@ -11,10 +11,7 @@ use Illuminate\Process\ProcessResult;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Process; use Illuminate\Support\Facades\Process;
use JsonException;
use RuntimeException;
use Spatie\Activitylog\Models\Activity; use Spatie\Activitylog\Models\Activity;
use Throwable;
class RunRemoteProcess class RunRemoteProcess
{ {
@@ -24,9 +21,9 @@ class RunRemoteProcess
public bool $ignore_errors; public bool $ignore_errors;
public $call_event_on_finish; public $call_event_on_finish = null;
public $call_event_data; public $call_event_data = null;
protected $time_start; protected $time_start;
@@ -44,7 +41,7 @@ class RunRemoteProcess
public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null, $call_event_data = null) public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null, $call_event_data = null)
{ {
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value && $activity->getExtraProperty('type') !== ActivityTypes::COMMAND->value) { if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value && $activity->getExtraProperty('type') !== ActivityTypes::COMMAND->value) {
throw new RuntimeException('Incompatible Activity to run a remote command.'); throw new \RuntimeException('Incompatible Activity to run a remote command.');
} }
$this->activity = $activity; $this->activity = $activity;
@@ -66,7 +63,7 @@ class RunRemoteProcess
associative: true, associative: true,
flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE
); );
} catch (JsonException $exception) { } catch (\JsonException $exception) {
return ''; return '';
} }
@@ -82,12 +79,12 @@ class RunRemoteProcess
$status = ProcessStatus::IN_PROGRESS; $status = ProcessStatus::IN_PROGRESS;
$timeout = config('constants.ssh.command_timeout'); $timeout = config('constants.ssh.command_timeout');
$invokedProcess = Process::timeout($timeout)->start($this->getCommand(), $this->handleOutput(...)); $process = Process::timeout($timeout)->start($this->getCommand(), $this->handleOutput(...));
$this->activity->properties = $this->activity->properties->merge([ $this->activity->properties = $this->activity->properties->merge([
'process_id' => $invokedProcess->id(), 'process_id' => $process->id(),
]); ]);
$processResult = $invokedProcess->wait(); $processResult = $process->wait();
// $processResult = Process::timeout($timeout)->run($this->getCommand(), $this->handleOutput(...)); // $processResult = Process::timeout($timeout)->run($this->getCommand(), $this->handleOutput(...));
if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) { if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
$status = ProcessStatus::ERROR; $status = ProcessStatus::ERROR;
@@ -114,7 +111,7 @@ class RunRemoteProcess
]); ]);
$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 {
@@ -127,7 +124,7 @@ class RunRemoteProcess
'userId' => $this->activity->causer_id, 'userId' => $this->activity->causer_id,
])); ]));
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
Log::error('Error calling event: '.$e->getMessage()); Log::error('Error calling event: '.$e->getMessage());
} }
} }

View File

@@ -16,9 +16,9 @@ class StartClickhouse
public string $configuration_dir; public string $configuration_dir;
public function handle(StandaloneClickhouse $standaloneClickhouse) public function handle(StandaloneClickhouse $database)
{ {
$this->database = $standaloneClickhouse; $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;
@@ -103,12 +103,12 @@ class StartClickhouse
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$standaloneClickhouse->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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, $standaloneClickhouse->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()

View File

@@ -25,28 +25,28 @@ class StartDatabase
return 'Server is not functional'; return 'Server is not functional';
} }
switch ($database->getMorphClass()) { switch ($database->getMorphClass()) {
case StandalonePostgresql::class: case \App\Models\StandalonePostgresql::class:
$activity = StartPostgresql::run($database); $activity = StartPostgresql::run($database);
break; break;
case StandaloneRedis::class: case \App\Models\StandaloneRedis::class:
$activity = StartRedis::run($database); $activity = StartRedis::run($database);
break; break;
case StandaloneMongodb::class: case \App\Models\StandaloneMongodb::class:
$activity = StartMongodb::run($database); $activity = StartMongodb::run($database);
break; break;
case StandaloneMysql::class: case \App\Models\StandaloneMysql::class:
$activity = StartMysql::run($database); $activity = StartMysql::run($database);
break; break;
case StandaloneMariadb::class: case \App\Models\StandaloneMariadb::class:
$activity = StartMariadb::run($database); $activity = StartMariadb::run($database);
break; break;
case StandaloneKeydb::class: case \App\Models\StandaloneKeydb::class:
$activity = StartKeydb::run($database); $activity = StartKeydb::run($database);
break; break;
case StandaloneDragonfly::class: case \App\Models\StandaloneDragonfly::class:
$activity = StartDragonfly::run($database); $activity = StartDragonfly::run($database);
break; break;
case StandaloneClickhouse::class: case \App\Models\StandaloneClickhouse::class:
$activity = StartClickhouse::run($database); $activity = StartClickhouse::run($database);
break; break;
} }

View File

@@ -28,7 +28,7 @@ class StartDatabaseProxy
$server = data_get($database, 'destination.server'); $server = data_get($database, 'destination.server');
$containerName = data_get($database, 'uuid'); $containerName = data_get($database, 'uuid');
$proxyContainerName = "{$database->uuid}-proxy"; $proxyContainerName = "{$database->uuid}-proxy";
if ($database->getMorphClass() === ServiceDatabase::class) { if ($database->getMorphClass() === \App\Models\ServiceDatabase::class) {
$databaseType = $database->databaseType(); $databaseType = $database->databaseType();
// $connectPredefined = data_get($database, 'service.connect_to_docker_network'); // $connectPredefined = data_get($database, 'service.connect_to_docker_network');
$network = $database->service->uuid; $network = $database->service->uuid;
@@ -36,54 +36,54 @@ class StartDatabaseProxy
$proxyContainerName = "{$database->service->uuid}-proxy"; $proxyContainerName = "{$database->service->uuid}-proxy";
switch ($databaseType) { switch ($databaseType) {
case 'standalone-mariadb': case 'standalone-mariadb':
$type = StandaloneMariadb::class; $type = \App\Models\StandaloneMariadb::class;
$containerName = "mariadb-{$database->service->uuid}"; $containerName = "mariadb-{$database->service->uuid}";
break; break;
case 'standalone-mongodb': case 'standalone-mongodb':
$type = StandaloneMongodb::class; $type = \App\Models\StandaloneMongodb::class;
$containerName = "mongodb-{$database->service->uuid}"; $containerName = "mongodb-{$database->service->uuid}";
break; break;
case 'standalone-mysql': case 'standalone-mysql':
$type = StandaloneMysql::class; $type = \App\Models\StandaloneMysql::class;
$containerName = "mysql-{$database->service->uuid}"; $containerName = "mysql-{$database->service->uuid}";
break; break;
case 'standalone-postgresql': case 'standalone-postgresql':
$type = StandalonePostgresql::class; $type = \App\Models\StandalonePostgresql::class;
$containerName = "postgresql-{$database->service->uuid}"; $containerName = "postgresql-{$database->service->uuid}";
break; break;
case 'standalone-redis': case 'standalone-redis':
$type = StandaloneRedis::class; $type = \App\Models\StandaloneRedis::class;
$containerName = "redis-{$database->service->uuid}"; $containerName = "redis-{$database->service->uuid}";
break; break;
case 'standalone-keydb': case 'standalone-keydb':
$type = StandaloneKeydb::class; $type = \App\Models\StandaloneKeydb::class;
$containerName = "keydb-{$database->service->uuid}"; $containerName = "keydb-{$database->service->uuid}";
break; break;
case 'standalone-dragonfly': case 'standalone-dragonfly':
$type = StandaloneDragonfly::class; $type = \App\Models\StandaloneDragonfly::class;
$containerName = "dragonfly-{$database->service->uuid}"; $containerName = "dragonfly-{$database->service->uuid}";
break; break;
case 'standalone-clickhouse': case 'standalone-clickhouse':
$type = StandaloneClickhouse::class; $type = \App\Models\StandaloneClickhouse::class;
$containerName = "clickhouse-{$database->service->uuid}"; $containerName = "clickhouse-{$database->service->uuid}";
break; break;
} }
} }
if ($type === StandaloneRedis::class) { if ($type === \App\Models\StandaloneRedis::class) {
$internalPort = 6379; $internalPort = 6379;
} elseif ($type === StandalonePostgresql::class) { } elseif ($type === \App\Models\StandalonePostgresql::class) {
$internalPort = 5432; $internalPort = 5432;
} elseif ($type === StandaloneMongodb::class) { } elseif ($type === \App\Models\StandaloneMongodb::class) {
$internalPort = 27017; $internalPort = 27017;
} elseif ($type === StandaloneMysql::class) { } elseif ($type === \App\Models\StandaloneMysql::class) {
$internalPort = 3306; $internalPort = 3306;
} elseif ($type === StandaloneMariadb::class) { } elseif ($type === \App\Models\StandaloneMariadb::class) {
$internalPort = 3306; $internalPort = 3306;
} elseif ($type === StandaloneKeydb::class) { } elseif ($type === \App\Models\StandaloneKeydb::class) {
$internalPort = 6379; $internalPort = 6379;
} elseif ($type === StandaloneDragonfly::class) { } elseif ($type === \App\Models\StandaloneDragonfly::class) {
$internalPort = 6379; $internalPort = 6379;
} elseif ($type === StandaloneClickhouse::class) { } elseif ($type === \App\Models\StandaloneClickhouse::class) {
$internalPort = 9000; $internalPort = 9000;
} }
$configuration_dir = database_proxy_dir($database->uuid); $configuration_dir = database_proxy_dir($database->uuid);

View File

@@ -16,9 +16,9 @@ class StartDragonfly
public string $configuration_dir; public string $configuration_dir;
public function handle(StandaloneDragonfly $standaloneDragonfly) public function handle(StandaloneDragonfly $database)
{ {
$this->database = $standaloneDragonfly; $this->database = $database;
$startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}"; $startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}";
@@ -100,12 +100,12 @@ class StartDragonfly
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$standaloneDragonfly->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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, $standaloneDragonfly->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()

View File

@@ -17,9 +17,9 @@ class StartKeydb
public string $configuration_dir; public string $configuration_dir;
public function handle(StandaloneKeydb $standaloneKeydb) public function handle(StandaloneKeydb $database)
{ {
$this->database = $standaloneKeydb; $this->database = $database;
$startCommand = "keydb-server --requirepass {$this->database->keydb_password} --appendonly yes"; $startCommand = "keydb-server --requirepass {$this->database->keydb_password} --appendonly yes";
@@ -92,7 +92,7 @@ class StartKeydb
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) || $this->database->keydb_conf !== null) { 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',
@@ -110,12 +110,12 @@ class StartKeydb
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$standaloneKeydb->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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, $standaloneKeydb->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()

View File

@@ -16,9 +16,9 @@ class StartMariadb
public string $configuration_dir; public string $configuration_dir;
public function handle(StandaloneMariadb $standaloneMariadb) public function handle(StandaloneMariadb $database)
{ {
$this->database = $standaloneMariadb; $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;
@@ -87,7 +87,7 @@ class StartMariadb
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) || $this->database->mariadb_conf !== null) { 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',
@@ -105,12 +105,12 @@ class StartMariadb
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$standaloneMariadb->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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, $standaloneMariadb->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()

View File

@@ -16,9 +16,9 @@ class StartMongodb
public string $configuration_dir; public string $configuration_dir;
public function handle(StandaloneMongodb $standaloneMongodb) public function handle(StandaloneMongodb $database)
{ {
$this->database = $standaloneMongodb; $this->database = $database;
$startCommand = 'mongod'; $startCommand = 'mongod';
@@ -99,7 +99,7 @@ class StartMongodb
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) || $this->database->mongo_conf !== null) { 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',
@@ -125,12 +125,12 @@ class StartMongodb
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$standaloneMongodb->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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, $standaloneMongodb->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()

View File

@@ -16,9 +16,9 @@ class StartMysql
public string $configuration_dir; public string $configuration_dir;
public function handle(StandaloneMysql $standaloneMysql) public function handle(StandaloneMysql $database)
{ {
$this->database = $standaloneMysql; $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;
@@ -87,7 +87,7 @@ class StartMysql
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) || $this->database->mysql_conf !== null) { 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',
@@ -105,12 +105,12 @@ class StartMysql
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$standaloneMysql->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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, $standaloneMysql->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()

View File

@@ -18,9 +18,9 @@ class StartPostgresql
public string $configuration_dir; public string $configuration_dir;
public function handle(StandalonePostgresql $standalonePostgresql) public function handle(StandalonePostgresql $database)
{ {
$this->database = $standalonePostgresql; $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;
if (isDev()) { if (isDev()) {
@@ -97,6 +97,7 @@ class StartPostgresql
if (count($volume_names) > 0) { if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names; $docker_compose['volumes'] = $volume_names;
} }
if (count($this->init_scripts) > 0) {
foreach ($this->init_scripts as $init_script) { foreach ($this->init_scripts as $init_script) {
$docker_compose['services'][$container_name]['volumes'][] = [ $docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind', 'type' => 'bind',
@@ -105,6 +106,7 @@ class StartPostgresql
'read_only' => true, 'read_only' => true,
]; ];
} }
}
if (filled($this->database->postgres_conf)) { if (filled($this->database->postgres_conf)) {
$docker_compose['services'][$container_name]['volumes'][] = [ $docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind', 'type' => 'bind',
@@ -127,12 +129,12 @@ class StartPostgresql
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$standalonePostgresql->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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, $standalonePostgresql->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()

View File

@@ -17,9 +17,9 @@ class StartRedis
public string $configuration_dir; public string $configuration_dir;
public function handle(StandaloneRedis $standaloneRedis) public function handle(StandaloneRedis $database)
{ {
$this->database = $standaloneRedis; $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;
@@ -96,7 +96,7 @@ class StartRedis
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) || $this->database->redis_conf !== null) { 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',
@@ -114,12 +114,12 @@ class StartRedis
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$standaloneRedis->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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, $standaloneRedis->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()

View File

@@ -26,9 +26,11 @@ class StopDatabase
} }
$this->stopContainer($database, $database->uuid, 300); $this->stopContainer($database, $database->uuid, 300);
if (! $isDeleteOperation && $dockerCleanup) { if (! $isDeleteOperation) {
if ($dockerCleanup) {
CleanupDocker::dispatch($server, true); CleanupDocker::dispatch($server, true);
} }
}
if ($database->is_public) { if ($database->is_public) {
StopDatabaseProxy::run($database); StopDatabaseProxy::run($database);
@@ -41,10 +43,10 @@ class StopDatabase
{ {
$server = $database->destination->server; $server = $database->destination->server;
$invokedProcess = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName"); $process = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
$startTime = time(); $startTime = time();
while ($invokedProcess->running()) { while ($process->running()) {
if (time() - $startTime >= $timeout) { if (time() - $startTime >= $timeout) {
$this->forceStopContainer($containerName, $server); $this->forceStopContainer($containerName, $server);
break; break;
@@ -64,4 +66,10 @@ class StopDatabase
{ {
instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false); instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false);
} }
private function deleteConnectedNetworks($uuid, $server)
{
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
instant_remote_process(["docker network rm {$uuid}"], $server, false);
}
} }

View File

@@ -24,7 +24,7 @@ class StopDatabaseProxy
{ {
$server = data_get($database, 'destination.server'); $server = data_get($database, 'destination.server');
$uuid = $database->uuid; $uuid = $database->uuid;
if ($database->getMorphClass() === ServiceDatabase::class) { if ($database->getMorphClass() === \App\Models\ServiceDatabase::class) {
$uuid = $database->service->uuid; $uuid = $database->service->uuid;
$server = data_get($database, 'service.server'); $server = data_get($database, 'service.server');
} }

View File

@@ -47,20 +47,20 @@ class GetContainersStatus
$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);
}); });
if (! $this->containers instanceof Collection) { if ($this->containers === null) {
['containers' => $this->containers, 'containerReplicates' => $this->containerReplicates] = $this->server->getContainers(); ['containers' => $this->containers, 'containerReplicates' => $this->containerReplicates] = $this->server->getContainers();
} }
if (is_null($this->containers)) { if (is_null($this->containers)) {
return null; return;
} }
if ($this->containerReplicates) { if ($this->containerReplicates) {
foreach ($this->containerReplicates as $containerReplicate) { foreach ($this->containerReplicates as $containerReplica) {
$name = data_get($containerReplicate, 'Name'); $name = data_get($containerReplica, 'Name');
$this->containers = $this->containers->map(function ($container) use ($name, $containerReplicate) { $this->containers = $this->containers->map(function ($container) use ($name, $containerReplica) {
if (data_get($container, 'Spec.Name') === $name) { if (data_get($container, 'Spec.Name') === $name) {
$replicas = data_get($containerReplicate, 'Replicas'); $replicas = data_get($containerReplica, 'Replicas');
$running = str($replicas)->explode('/')[0]; $running = str($replicas)->explode('/')[0];
$total = str($replicas)->explode('/')[1]; $total = str($replicas)->explode('/')[1];
if ($running === $total) { if ($running === $total) {
@@ -102,7 +102,7 @@ class GetContainersStatus
if (str($applicationId)->contains('-')) { if (str($applicationId)->contains('-')) {
$applicationId = str($applicationId)->before('-'); $applicationId = str($applicationId)->before('-');
} }
$preview = ApplicationPreview::query()->where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first(); $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
if ($preview) { if ($preview) {
$foundApplicationPreviews[] = $preview->id; $foundApplicationPreviews[] = $preview->id;
$statusFromDb = $preview->status; $statusFromDb = $preview->status;
@@ -136,7 +136,7 @@ class GetContainersStatus
if ($type === 'service') { if ($type === 'service') {
$database_id = data_get($labels, 'coolify.service.subId'); $database_id = data_get($labels, 'coolify.service.subId');
if ($database_id) { if ($database_id) {
$service_db = ServiceDatabase::query()->where('id', $database_id)->first(); $service_db = ServiceDatabase::where('id', $database_id)->first();
if ($service_db) { if ($service_db) {
$uuid = data_get($service_db, 'service.uuid'); $uuid = data_get($service_db, 'service.uuid');
if ($uuid) { if ($uuid) {
@@ -145,9 +145,9 @@ class GetContainersStatus
$foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) { $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
if ($this->server->isSwarm()) { if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
} } else {
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);
@@ -173,9 +173,9 @@ class GetContainersStatus
$foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) { $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
if ($this->server->isSwarm()) { if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
} } else {
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);
@@ -223,16 +223,18 @@ class GetContainersStatus
foreach ($apps as $app) { foreach ($apps as $app) {
if (in_array("$app->id-$app->name", $foundServices)) { if (in_array("$app->id-$app->name", $foundServices)) {
continue; continue;
} } else {
$exitedServices->push($app); $exitedServices->push($app);
} }
}
foreach ($dbs as $db) { foreach ($dbs as $db) {
if (in_array("$db->id-$db->name", $foundServices)) { if (in_array("$db->id-$db->name", $foundServices)) {
continue; continue;
} } else {
$exitedServices->push($db); $exitedServices->push($db);
} }
} }
}
$exitedServices = $exitedServices->unique('uuid'); $exitedServices = $exitedServices->unique('uuid');
foreach ($exitedServices as $exitedService) { foreach ($exitedServices as $exitedService) {
if (str($exitedService->status)->startsWith('exited')) { if (str($exitedService->status)->startsWith('exited')) {
@@ -241,12 +243,18 @@ class GetContainersStatus
$name = data_get($exitedService, 'name'); $name = data_get($exitedService, 'name');
$fqdn = data_get($exitedService, 'fqdn'); $fqdn = data_get($exitedService, 'fqdn');
if ($name) { if ($name) {
$containerName = $fqdn ? "$name, available at $fqdn" : $name; if ($fqdn) {
} elseif ($fqdn) { $containerName = "$name, available at $fqdn";
} else {
$containerName = $name;
}
} else {
if ($fqdn) {
$containerName = $fqdn; $containerName = $fqdn;
} else { } else {
$containerName = null; $containerName = null;
} }
}
$projectUuid = data_get($service, 'environment.project.uuid'); $projectUuid = data_get($service, 'environment.project.uuid');
$serviceUuid = data_get($service, 'uuid'); $serviceUuid = data_get($service, 'uuid');
$environmentName = data_get($service, 'environment.name'); $environmentName = data_get($service, 'environment.name');
@@ -261,8 +269,8 @@ class GetContainersStatus
} }
$notRunningApplications = $this->applications->pluck('id')->diff($foundApplications); $notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
foreach ($notRunningApplications as $notRunningApplication) { foreach ($notRunningApplications as $applicationId) {
$application = $this->applications->where('id', $notRunningApplication)->first(); $application = $this->applications->where('id', $applicationId)->first();
if (str($application->status)->startsWith('exited')) { if (str($application->status)->startsWith('exited')) {
continue; continue;
} }
@@ -286,8 +294,8 @@ class GetContainersStatus
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
} }
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews); $notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
foreach ($notRunningApplicationPreviews as $notRunningApplicationPreview) { foreach ($notRunningApplicationPreviews as $previewId) {
$preview = $previews->where('id', $notRunningApplicationPreview)->first(); $preview = $previews->where('id', $previewId)->first();
if (str($preview->status)->startsWith('exited')) { if (str($preview->status)->startsWith('exited')) {
continue; continue;
} }
@@ -311,21 +319,21 @@ class GetContainersStatus
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
} }
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases); $notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
foreach ($notRunningDatabases as $notRunningDatabase) { foreach ($notRunningDatabases as $database) {
$notRunningDatabase = $databases->where('id', $notRunningDatabase)->first(); $database = $databases->where('id', $database)->first();
if (str($notRunningDatabase->status)->startsWith('exited')) { if (str($database->status)->startsWith('exited')) {
continue; continue;
} }
$notRunningDatabase->update(['status' => 'exited']); $database->update(['status' => 'exited']);
$name = data_get($notRunningDatabase, 'name'); $name = data_get($database, 'name');
$fqdn = data_get($notRunningDatabase, 'fqdn'); $fqdn = data_get($database, 'fqdn');
$containerName = $name; $containerName = $name;
$projectUuid = data_get($notRunningDatabase, 'environment.project.uuid'); $projectUuid = data_get($database, 'environment.project.uuid');
$environmentName = data_get($notRunningDatabase, 'environment.name'); $environmentName = data_get($database, 'environment.name');
$databaseUuid = data_get($notRunningDatabase, '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;
@@ -334,7 +342,5 @@ class GetContainersStatus
} }
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url)); // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
} }
return null;
} }
} }

View File

@@ -34,10 +34,10 @@ class CreateNewUser implements CreatesNewUsers
'password' => ['required', Password::defaults(), 'confirmed'], 'password' => ['required', Password::defaults(), 'confirmed'],
])->validate(); ])->validate();
if (User::query()->count() == 0) { if (User::count() == 0) {
// If this is the first user, make them the root user // If this is the first user, make them the root user
// Team is already created in the database/seeders/ProductionSeeder.php // Team is already created in the database/seeders/ProductionSeeder.php
$user = User::query()->create([ $user = User::create([
'id' => 0, 'id' => 0,
'name' => $input['name'], 'name' => $input['name'],
'email' => strtolower($input['email']), 'email' => strtolower($input['email']),
@@ -50,7 +50,7 @@ class CreateNewUser implements CreatesNewUsers
$settings->is_registration_enabled = false; $settings->is_registration_enabled = false;
$settings->save(); $settings->save();
} else { } else {
$user = User::query()->create([ $user = User::create([
'name' => $input['name'], 'name' => $input['name'],
'email' => strtolower($input['email']), 'email' => strtolower($input['email']),
'password' => Hash::make($input['password']), 'password' => Hash::make($input['password']),

View File

@@ -3,7 +3,6 @@
namespace App\Actions\Proxy; namespace App\Actions\Proxy;
use App\Models\Server; use App\Models\Server;
use Exception;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
class CheckConfiguration class CheckConfiguration
@@ -26,7 +25,7 @@ class CheckConfiguration
$proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value(); $proxy_configuration = str(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

@@ -4,7 +4,6 @@ namespace App\Actions\Proxy;
use App\Enums\ProxyTypes; use App\Enums\ProxyTypes;
use App\Models\Server; use App\Models\Server;
use Exception;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
@@ -33,22 +32,25 @@ class CheckProxy
} }
['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 {
return false; return false;
} }
}
if ($server->isSwarm()) { if ($server->isSwarm()) {
$status = getContainerStatus($server, 'coolify-proxy_traefik'); $status = getContainerStatus($server, 'coolify-proxy_traefik');
$server->proxy->set('status', $status); $server->proxy->set('status', $status);
$server->save(); $server->save();
if ($status === 'running') {
return $status !== 'running'; return false;
} }
return true;
} 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');
@@ -63,7 +65,9 @@ class CheckProxy
if ($server->id === 0) { if ($server->id === 0) {
$ip = 'host.docker.internal'; $ip = 'host.docker.internal';
} }
$portsToCheck = ['80', '443']; $portsToCheck = ['80', '443'];
try { try {
if ($server->proxyType() !== ProxyTypes::NONE->value) { if ($server->proxyType() !== ProxyTypes::NONE->value) {
$proxyCompose = CheckConfiguration::run($server); $proxyCompose = CheckConfiguration::run($server);
@@ -84,23 +88,24 @@ class CheckProxy
} else { } else {
$portsToCheck = []; $portsToCheck = [];
} }
} catch (Exception $e) { } catch (\Exception $e) {
Log::error('Error checking proxy: '.$e->getMessage()); Log::error('Error checking proxy: '.$e->getMessage());
} }
if ($portsToCheck === []) { if (count($portsToCheck) === 0) {
return false; return false;
} }
foreach ($portsToCheck as $portToCheck) { foreach ($portsToCheck as $port) {
$connection = @fsockopen($ip, $portToCheck); $connection = @fsockopen($ip, $port);
if (is_resource($connection) && fclose($connection)) { if (is_resource($connection) && fclose($connection)) {
if ($fromUI) { if ($fromUI) {
throw new Exception("Port {$portToCheck} is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>"); throw new \Exception("Port $port is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
} } else {
return false; return false;
} }
} }
}
return true; return true;
} }
} }
}

View File

@@ -5,7 +5,6 @@ namespace App\Actions\Proxy;
use App\Enums\ProxyTypes; use App\Enums\ProxyTypes;
use App\Events\ProxyStarted; use App\Events\ProxyStarted;
use App\Models\Server; use App\Models\Server;
use Exception;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Spatie\Activitylog\Models\Activity; use Spatie\Activitylog\Models\Activity;
@@ -23,7 +22,7 @@ class StartProxy
$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);
@@ -39,9 +38,11 @@ class StartProxy
"echo 'Successfully started coolify-proxy.'", "echo 'Successfully started coolify-proxy.'",
]); ]);
} else { } else {
if (isDev() && $proxyType === ProxyTypes::CADDY->value) { if (isDev()) {
if ($proxyType === ProxyTypes::CADDY->value) {
$proxy_path = '/data/coolify/proxy/caddy'; $proxy_path = '/data/coolify/proxy/caddy';
} }
}
$caddyfile = 'import /dynamic/*.caddy'; $caddyfile = 'import /dynamic/*.caddy';
$commands = $commands->merge([ $commands = $commands->merge([
"mkdir -p $proxy_path/dynamic", "mkdir -p $proxy_path/dynamic",
@@ -64,7 +65,7 @@ class StartProxy
if ($async) { if ($async) {
return remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server); return remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server);
} } else {
instant_remote_process($commands, $server); instant_remote_process($commands, $server);
$server->proxy->set('status', 'running'); $server->proxy->set('status', 'running');
$server->proxy->set('type', $proxyType); $server->proxy->set('type', $proxyType);
@@ -74,3 +75,4 @@ class StartProxy
return 'OK'; return 'OK';
} }
} }
}

View File

@@ -6,7 +6,6 @@ use App\Events\CloudflareTunnelConfigured;
use App\Models\Server; use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Throwable;
class ConfigureCloudflared class ConfigureCloudflared
{ {
@@ -40,7 +39,7 @@ class ConfigureCloudflared
'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) {
$server->settings->is_cloudflare_tunnel = false; $server->settings->is_cloudflare_tunnel = false;
$server->settings->save(); $server->settings->save();
throw $e; throw $e;

View File

@@ -4,7 +4,6 @@ namespace App\Actions\Server;
use App\Models\Server; use App\Models\Server;
use App\Models\StandaloneDocker; use App\Models\StandaloneDocker;
use Exception;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
class InstallDocker class InstallDocker
@@ -16,7 +15,7 @@ class InstallDocker
$dockerVersion = config('constants.docker.minimum_required_version'); $dockerVersion = config('constants.docker.minimum_required_version');
$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>.');
} }
$config = base64_encode('{ $config = base64_encode('{
"log-driver": "json-file", "log-driver": "json-file",
@@ -25,9 +24,9 @@ class InstallDocker
"max-file": "3" "max-file": "3"
} }
}'); }');
$builder = StandaloneDocker::query()->where('server_id', $server->id); $found = StandaloneDocker::where('server_id', $server->id);
if ($builder->count() == 0 && $server->id) { if ($found->count() == 0 && $server->id) {
StandaloneDocker::query()->create([ StandaloneDocker::create([
'name' => 'coolify', 'name' => 'coolify',
'network' => 'coolify', 'network' => 'coolify',
'server_id' => $server->id, 'server_id' => $server->id,
@@ -46,7 +45,7 @@ class InstallDocker
]); ]);
return remote_process($command, $server); return remote_process($command, $server);
} } 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...'",
@@ -74,7 +73,7 @@ class InstallDocker
'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');
} }
$command = $command->merge([ $command = $command->merge([
"echo 'Installing Docker Engine...'", "echo 'Installing Docker Engine...'",
@@ -107,3 +106,4 @@ class InstallDocker
return remote_process($command, $server); return remote_process($command, $server);
} }
} }
}

View File

@@ -14,7 +14,6 @@ use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql; use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis; use App\Models\StandaloneRedis;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Throwable;
class ResourcesCheck class ResourcesCheck
{ {
@@ -24,21 +23,19 @@ class ResourcesCheck
{ {
$seconds = 60; $seconds = 60;
try { try {
Application::query()->where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']); Application::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
ServiceApplication::query()->where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']); ServiceApplication::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
ServiceDatabase::query()->where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']); ServiceDatabase::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
StandalonePostgresql::query()->where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']); StandalonePostgresql::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
StandaloneRedis::query()->where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']); StandaloneRedis::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
StandaloneMongodb::query()->where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']); StandaloneMongodb::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
StandaloneMysql::query()->where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']); StandaloneMysql::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
StandaloneMariadb::query()->where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']); StandaloneMariadb::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
StandaloneKeydb::query()->where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']); StandaloneKeydb::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
StandaloneDragonfly::query()->where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']); StandaloneDragonfly::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
StandaloneClickhouse::query()->where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']); StandaloneClickhouse::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
} catch (Throwable $e) { } catch (\Throwable $e) {
return handleError($e); return handleError($e);
} }
return null;
} }
} }

View File

@@ -16,7 +16,6 @@ use App\Models\ServiceDatabase;
use App\Notifications\Container\ContainerRestarted; use App\Notifications\Container\ContainerRestarted;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Throwable;
class ServerCheck class ServerCheck
{ {
@@ -62,11 +61,11 @@ class ServerCheck
} }
if (isset($containerReplicates)) { if (isset($containerReplicates)) {
foreach ($containerReplicates as $containerReplicate) { foreach ($containerReplicates as $containerReplica) {
$name = data_get($containerReplicate, 'Name'); $name = data_get($containerReplica, 'Name');
$this->containers = $this->containers->map(function ($container) use ($name, $containerReplicate) { $this->containers = $this->containers->map(function ($container) use ($name, $containerReplica) {
if (data_get($container, 'Spec.Name') === $name) { if (data_get($container, 'Spec.Name') === $name) {
$replicas = data_get($containerReplicate, 'Replicas'); $replicas = data_get($containerReplica, 'Replicas');
$running = str($replicas)->explode('/')[0]; $running = str($replicas)->explode('/')[0];
$total = str($replicas)->explode('/')[1]; $total = str($replicas)->explode('/')[1];
if ($running === $total) { if ($running === $total) {
@@ -96,9 +95,9 @@ class ServerCheck
$foundProxyContainer = $this->containers->filter(function ($value, $key) { $foundProxyContainer = $this->containers->filter(function ($value, $key) {
if ($this->server->isSwarm()) { if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik'; return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
} } else {
return data_get($value, 'Name') === '/coolify-proxy'; return data_get($value, 'Name') === '/coolify-proxy';
}
})->first(); })->first();
if (! $foundProxyContainer) { if (! $foundProxyContainer) {
try { try {
@@ -107,7 +106,7 @@ class ServerCheck
StartProxy::run($this->server, false); StartProxy::run($this->server, false);
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server)); $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
} }
} else { } else {
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status'); $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
@@ -117,11 +116,9 @@ class ServerCheck
} }
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
return handleError($e); return handleError($e);
} }
return null;
} }
private function checkLogDrainContainer() private function checkLogDrainContainer()
@@ -144,11 +141,13 @@ class ServerCheck
foreach ($this->containers as $container) { foreach ($this->containers as $container) {
if ($this->isSentinel) { if ($this->isSentinel) {
$labels = Arr::undot(data_get($container, 'labels')); $labels = Arr::undot(data_get($container, 'labels'));
} elseif ($this->server->isSwarm()) { } else {
if ($this->server->isSwarm()) {
$labels = Arr::undot(data_get($container, 'Spec.Labels')); $labels = Arr::undot(data_get($container, 'Spec.Labels'));
} else { } else {
$labels = Arr::undot(data_get($container, 'Config.Labels')); $labels = Arr::undot(data_get($container, 'Config.Labels'));
} }
}
$managed = data_get($labels, 'coolify.managed'); $managed = data_get($labels, 'coolify.managed');
if (! $managed) { if (! $managed) {
continue; continue;
@@ -178,12 +177,12 @@ class ServerCheck
if (str($applicationId)->contains('-')) { if (str($applicationId)->contains('-')) {
$applicationId = str($applicationId)->before('-'); $applicationId = str($applicationId)->before('-');
} }
$preview = ApplicationPreview::query()->where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first(); $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
if ($preview) { if ($preview) {
$preview->update(['status' => $containerStatus]); $preview->update(['status' => $containerStatus]);
} }
} else { } else {
$application = Application::query()->where('id', $applicationId)->first(); $application = Application::where('id', $applicationId)->first();
if ($application) { if ($application) {
$application->update([ $application->update([
'status' => $containerStatus, 'status' => $containerStatus,
@@ -195,14 +194,14 @@ class ServerCheck
// Service // Service
$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 = Service::query()->where('id', $serviceId)->first(); $service = Service::where('id', $serviceId)->first();
if (! $service) { if (! $service) {
continue; continue;
} }
if ($subType === 'application') { if ($subType === 'application') {
$service = ServiceApplication::query()->where('id', $subId)->first(); $service = ServiceApplication::where('id', $subId)->first();
} else { } else {
$service = ServiceDatabase::query()->where('id', $subId)->first(); $service = ServiceDatabase::where('id', $subId)->first();
} }
if ($service) { if ($service) {
$service->update([ $service->update([
@@ -215,12 +214,14 @@ class ServerCheck
$foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) { $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
if ($this->isSentinel) { if ($this->isSentinel) {
return data_get($value, 'name') === $uuid.'-proxy'; return data_get($value, 'name') === $uuid.'-proxy';
} } else {
if ($this->server->isSwarm()) { if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
} } else {
return data_get($value, 'Name') === "/$uuid-proxy"; return data_get($value, 'Name') === "/$uuid-proxy";
}
}
})->first(); })->first();
if (! $foundTcpProxy) { if (! $foundTcpProxy) {
StartDatabaseProxy::run($service); StartDatabaseProxy::run($service);
@@ -245,12 +246,14 @@ class ServerCheck
$foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) { $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
if ($this->isSentinel) { if ($this->isSentinel) {
return data_get($value, 'name') === $uuid.'-proxy'; return data_get($value, 'name') === $uuid.'-proxy';
} } else {
if ($this->server->isSwarm()) { if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid"; return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
} } else {
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);

View File

@@ -3,9 +3,7 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use App\Models\Server; use App\Models\Server;
use Exception;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Throwable;
class StartLogDrain class StartLogDrain
{ {
@@ -33,10 +31,9 @@ class StartLogDrain
try { try {
if ($type === 'none') { if ($type === 'none') {
return 'No log drain is enabled.'; return 'No log drain is enabled.';
} } elseif ($type === 'newrelic') {
if ($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("
[SERVICE] [SERVICE]
@@ -71,7 +68,7 @@ class StartLogDrain
"); ");
} elseif ($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]
@@ -92,7 +89,7 @@ class StartLogDrain
'); ');
} elseif ($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("
[SERVICE] [SERVICE]
@@ -132,12 +129,12 @@ class StartLogDrain
"); ");
} elseif ($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);
$parsers = base64_encode($server->settings->logdrain_custom_config_parser); $parsers = base64_encode($server->settings->logdrain_custom_config_parser);
} else { } else {
throw new Exception('Unknown log drain type.'); throw new \Exception('Unknown log drain type.');
} }
if ($type !== 'custom') { if ($type !== 'custom') {
$parsers = base64_encode(" $parsers = base64_encode("
@@ -210,7 +207,7 @@ Files:
"touch $config_path/.env", "touch $config_path/.env",
]; ];
} else { } else {
throw new Exception('Unknown log drain type.'); throw new \Exception('Unknown log drain type.');
} }
$restart_command = [ $restart_command = [
"echo 'Starting Fluent Bit'", "echo 'Starting Fluent Bit'",
@@ -219,7 +216,7 @@ Files:
$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

@@ -3,7 +3,6 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use App\Models\Server; use App\Models\Server;
use Exception;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
class StartSentinel class StartSentinel
@@ -28,7 +27,7 @@ class StartSentinel
$mountDir = '/data/coolify/sentinel'; $mountDir = '/data/coolify/sentinel';
$image = "ghcr.io/coollabsio/sentinel:$version"; $image = "ghcr.io/coollabsio/sentinel:$version";
if (! $endpoint) { if (! $endpoint) {
throw new Exception('You should set FQDN in Instance Settings.'); throw new \Exception('You should set FQDN in Instance Settings.');
} }
$environments = [ $environments = [
'TOKEN' => $token, 'TOKEN' => $token,

View File

@@ -4,7 +4,6 @@ namespace App\Actions\Server;
use App\Models\Server; use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
use Throwable;
class StopLogDrain class StopLogDrain
{ {
@@ -14,7 +13,7 @@ class StopLogDrain
{ {
try { try {
return instant_remote_process(['docker rm -f coolify-log-drain'], $server, false); return instant_remote_process(['docker rm -f coolify-log-drain'], $server, false);
} catch (Throwable $e) { } catch (\Throwable $e) {
return handleError($e); return handleError($e);
} }
} }

View File

@@ -25,7 +25,7 @@ class UpdateCoolify
return; return;
} }
$settings = instanceSettings(); $settings = instanceSettings();
$this->server = Server::query()->find(0); $this->server = Server::find(0);
if (! $this->server) { if (! $this->server) {
return; return;
} }

View File

@@ -3,7 +3,6 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use App\Models\Server; use App\Models\Server;
use Exception;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
class ValidateServer class ValidateServer
@@ -35,7 +34,7 @@ class ValidateServer
$server->update([ $server->update([
'validation_logs' => $this->error, 'validation_logs' => $this->error,
]); ]);
throw new Exception($this->error); throw new \Exception($this->error);
} }
$this->supported_os_type = $server->validateOS(); $this->supported_os_type = $server->validateOS();
if (! $this->supported_os_type) { if (! $this->supported_os_type) {
@@ -43,7 +42,7 @@ class ValidateServer
$server->update([ $server->update([
'validation_logs' => $this->error, 'validation_logs' => $this->error,
]); ]);
throw new Exception($this->error); throw new \Exception($this->error);
} }
$this->docker_installed = $server->validateDockerEngine(); $this->docker_installed = $server->validateDockerEngine();
@@ -53,17 +52,18 @@ class ValidateServer
$server->update([ $server->update([
'validation_logs' => $this->error, 'validation_logs' => $this->error,
]); ]);
throw new Exception($this->error); throw new \Exception($this->error);
} }
$this->docker_version = $server->validateDockerEngineVersion(); $this->docker_version = $server->validateDockerEngineVersion();
if ($this->docker_version) { if ($this->docker_version) {
return 'OK'; return 'OK';
} } else {
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: <a target="_blank" class="text-black underline dark:text-white" href="https://docs.docker.com/engine/install/#server">documentation</a>.'; $this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: <a target="_blank" class="text-black underline dark:text-white" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
$server->update([ $server->update([
'validation_logs' => $this->error, 'validation_logs' => $this->error,
]); ]);
throw new Exception($this->error); throw new \Exception($this->error);
}
} }
} }

View File

@@ -4,7 +4,6 @@ namespace App\Actions\Service;
use App\Actions\Server\CleanupDocker; use App\Actions\Server\CleanupDocker;
use App\Models\Service; use App\Models\Service;
use Exception;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
@@ -33,11 +32,12 @@ class DeleteService
$storagesToDelete->push($storage); $storagesToDelete->push($storage);
} }
} }
foreach ($storagesToDelete as $storageToDelete) { foreach ($storagesToDelete as $storage) {
$commands[] = "docker volume rm -f $storageToDelete->name"; $commands[] = "docker volume rm -f $storage->name";
} }
// Execute volume deletion first, this must be done first otherwise volumes will not be deleted. // Execute volume deletion first, this must be done first otherwise volumes will not be deleted.
if (! empty($commands)) {
foreach ($commands as $command) { foreach ($commands as $command) {
$result = instant_remote_process([$command], $server, false); $result = instant_remote_process([$command], $server, false);
if ($result !== null && $result !== 0) { if ($result !== null && $result !== 0) {
@@ -45,14 +45,15 @@ class DeleteService
} }
} }
} }
}
if ($deleteConnectedNetworks) { if ($deleteConnectedNetworks) {
$service->delete_connected_networks($service->uuid); $service->delete_connected_networks($service->uuid);
} }
instant_remote_process(["docker rm -f $service->uuid"], $server, throwError: false); instant_remote_process(["docker rm -f $service->uuid"], $server, throwError: false);
} catch (Exception $e) { } catch (\Exception $e) {
throw new Exception($e->getMessage(), $e->getCode(), $e); throw new \Exception($e->getMessage());
} finally { } finally {
if ($deleteConfigurations) { if ($deleteConfigurations) {
$service->delete_configurations(); $service->delete_configurations();

View File

@@ -4,7 +4,6 @@ namespace App\Actions\Service;
use App\Actions\Server\CleanupDocker; use App\Actions\Server\CleanupDocker;
use App\Models\Service; use App\Models\Service;
use Exception;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
class StopService class StopService
@@ -30,10 +29,8 @@ class StopService
CleanupDocker::dispatch($server, true); CleanupDocker::dispatch($server, true);
} }
} }
} catch (Exception $e) { } catch (\Exception $e) {
return $e->getMessage(); return $e->getMessage();
} }
return null;
} }
} }

View File

@@ -20,11 +20,12 @@ class ComplexStatusCheck
$application->update(['status' => 'exited:unhealthy']); $application->update(['status' => 'exited:unhealthy']);
continue; continue;
} } else {
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']); $application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
continue; continue;
} }
}
$container = instant_remote_process(["docker container inspect $(docker container ls -q --filter 'label=coolify.applicationId={$application->id}' --filter 'label=coolify.pullRequestId=0') --format '{{json .}}'"], $server, false); $container = instant_remote_process(["docker container inspect $(docker container ls -q --filter 'label=coolify.applicationId={$application->id}' --filter 'label=coolify.pullRequestId=0') --format '{{json .}}'"], $server, false);
$container = format_docker_command_output_to_json($container); $container = format_docker_command_output_to_json($container);
if ($container->count() === 1) { if ($container->count() === 1) {
@@ -43,7 +44,8 @@ class ComplexStatusCheck
$additional_server->updateExistingPivot($server->id, ['status' => "$containerStatus:$containerHealth"]); $additional_server->updateExistingPivot($server->id, ['status' => "$containerStatus:$containerHealth"]);
} }
} }
} elseif ($is_main_server) { } else {
if ($is_main_server) {
$application->update(['status' => 'exited:unhealthy']); $application->update(['status' => 'exited:unhealthy']);
continue; continue;
@@ -55,3 +57,4 @@ class ComplexStatusCheck
} }
} }
} }
}

View File

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

View File

@@ -3,7 +3,6 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Models\User; use App\Models\User;
use Exception;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class AdminRemoveUser extends Command class AdminRemoveUser extends Command
@@ -47,7 +46,7 @@ class AdminRemoveUser extends Command
$team->delete(); $team->delete();
} }
$user->delete(); $user->delete();
} 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());

View File

@@ -15,7 +15,7 @@ class CheckApplicationDeploymentQueue extends Command
public function handle() public function handle()
{ {
$seconds = $this->option('seconds'); $seconds = $this->option('seconds');
$deployments = ApplicationDeploymentQueue::query()->whereIn('status', [ $deployments = ApplicationDeploymentQueue::whereIn('status', [
ApplicationDeploymentStatus::IN_PROGRESS, ApplicationDeploymentStatus::IN_PROGRESS,
ApplicationDeploymentStatus::QUEUED, ApplicationDeploymentStatus::QUEUED,
])->where('created_at', '<=', now()->subSeconds($seconds))->get(); ])->where('created_at', '<=', now()->subSeconds($seconds))->get();
@@ -40,11 +40,11 @@ class CheckApplicationDeploymentQueue extends Command
} }
} }
private function cancelDeployment(ApplicationDeploymentQueue $applicationDeploymentQueue) private function cancelDeployment(ApplicationDeploymentQueue $deployment)
{ {
$applicationDeploymentQueue->update(['status' => ApplicationDeploymentStatus::FAILED]); $deployment->update(['status' => ApplicationDeploymentStatus::FAILED]);
if ($applicationDeploymentQueue->server?->isFunctional()) { if ($deployment->server?->isFunctional()) {
remote_process(['docker rm -f '.$applicationDeploymentQueue->deployment_uuid], $applicationDeploymentQueue->server, false); remote_process(['docker rm -f '.$deployment->deployment_uuid], $deployment->server, false);
} }
} }
} }

View File

@@ -3,7 +3,6 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Models\ApplicationDeploymentQueue; use App\Models\ApplicationDeploymentQueue;
use App\Models\Server;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class CleanupApplicationDeploymentQueue extends Command class CleanupApplicationDeploymentQueue extends Command
@@ -15,9 +14,9 @@ class CleanupApplicationDeploymentQueue extends Command
public function handle() public function handle()
{ {
$team_id = $this->option('team-id'); $team_id = $this->option('team-id');
$servers = Server::query()->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::query()->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

@@ -18,14 +18,19 @@ class CleanupDatabase extends Command
} else { } else {
echo "Running database cleanup in dry-run mode...\n"; echo "Running database cleanup in dry-run mode...\n";
} }
$keep_days = isCloud() ? $this->option('keep-days') ?? 60 : $this->option('keep-days') ?? 60; if (isCloud()) {
// Later on we can increase this to 180 days or dynamically set
$keep_days = $this->option('keep-days') ?? 60;
} else {
$keep_days = $this->option('keep-days') ?? 60;
}
echo "Keep days: $keep_days\n"; echo "Keep days: $keep_days\n";
// Cleanup failed jobs table // Cleanup failed jobs table
$builder = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(1)); $failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(1));
$count = $builder->count(); $count = $failed_jobs->count();
echo "Delete $count entries from failed_jobs.\n"; echo "Delete $count entries from failed_jobs.\n";
if ($this->option('yes')) { if ($this->option('yes')) {
$builder->delete(); $failed_jobs->delete();
} }
// Cleanup sessions table // Cleanup sessions table

View File

@@ -21,7 +21,6 @@ use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql; use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis; use App\Models\StandaloneRedis;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Throwable;
class CleanupStuckedResources extends Command class CleanupStuckedResources extends Command
{ {
@@ -43,18 +42,18 @@ class CleanupStuckedResources extends Command
foreach ($servers as $server) { foreach ($servers as $server) {
CleanupHelperContainersJob::dispatch($server); CleanupHelperContainersJob::dispatch($server);
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stucked resources: {$e->getMessage()}\n"; echo "Error in cleaning stucked resources: {$e->getMessage()}\n";
} }
try { try {
$applicationsDeploymentQueue = ApplicationDeploymentQueue::query()->get(); $applicationsDeploymentQueue = ApplicationDeploymentQueue::get();
foreach ($applicationsDeploymentQueue as $applicationDeploymentQueue) { foreach ($applicationsDeploymentQueue as $applicationDeploymentQueue) {
if (is_null($applicationDeploymentQueue->application)) { if (is_null($applicationDeploymentQueue->application)) {
echo "Deleting stuck application deployment queue: {$applicationDeploymentQueue->id}\n"; echo "Deleting stuck application deployment queue: {$applicationDeploymentQueue->id}\n";
$applicationDeploymentQueue->delete(); $applicationDeploymentQueue->delete();
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck application deployment queue: {$e->getMessage()}\n"; echo "Error in cleaning stuck application deployment queue: {$e->getMessage()}\n";
} }
try { try {
@@ -63,18 +62,18 @@ class CleanupStuckedResources extends Command
echo "Deleting stuck application: {$application->name}\n"; echo "Deleting stuck application: {$application->name}\n";
$application->forceDelete(); $application->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck application: {$e->getMessage()}\n"; echo "Error in cleaning stuck application: {$e->getMessage()}\n";
} }
try { try {
$applicationsPreviews = ApplicationPreview::query()->get(); $applicationsPreviews = ApplicationPreview::get();
foreach ($applicationsPreviews as $applicationPreview) { foreach ($applicationsPreviews as $applicationPreview) {
if (! data_get($applicationPreview, 'application')) { if (! data_get($applicationPreview, 'application')) {
echo "Deleting stuck application preview: {$applicationPreview->uuid}\n"; echo "Deleting stuck application preview: {$applicationPreview->uuid}\n";
$applicationPreview->delete(); $applicationPreview->delete();
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck application: {$e->getMessage()}\n"; echo "Error in cleaning stuck application: {$e->getMessage()}\n";
} }
try { try {
@@ -83,16 +82,16 @@ class CleanupStuckedResources extends Command
echo "Deleting stuck postgresql: {$postgresql->name}\n"; echo "Deleting stuck postgresql: {$postgresql->name}\n";
$postgresql->forceDelete(); $postgresql->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck postgresql: {$e->getMessage()}\n"; echo "Error in cleaning stuck postgresql: {$e->getMessage()}\n";
} }
try { try {
$redis = StandaloneRedis::withTrashed()->whereNotNull('deleted_at')->get(); $redis = StandaloneRedis::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($redis as $redi) { foreach ($redis as $redis) {
echo "Deleting stuck redis: {$redi->name}\n"; echo "Deleting stuck redis: {$redis->name}\n";
$redi->forceDelete(); $redis->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck redis: {$e->getMessage()}\n"; echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
} }
try { try {
@@ -101,7 +100,7 @@ class CleanupStuckedResources extends Command
echo "Deleting stuck keydb: {$keydb->name}\n"; echo "Deleting stuck keydb: {$keydb->name}\n";
$keydb->forceDelete(); $keydb->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck keydb: {$e->getMessage()}\n"; echo "Error in cleaning stuck keydb: {$e->getMessage()}\n";
} }
try { try {
@@ -110,7 +109,7 @@ class CleanupStuckedResources extends Command
echo "Deleting stuck dragonfly: {$dragonfly->name}\n"; echo "Deleting stuck dragonfly: {$dragonfly->name}\n";
$dragonfly->forceDelete(); $dragonfly->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck dragonfly: {$e->getMessage()}\n"; echo "Error in cleaning stuck dragonfly: {$e->getMessage()}\n";
} }
try { try {
@@ -119,7 +118,7 @@ class CleanupStuckedResources extends Command
echo "Deleting stuck clickhouse: {$clickhouse->name}\n"; echo "Deleting stuck clickhouse: {$clickhouse->name}\n";
$clickhouse->forceDelete(); $clickhouse->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck clickhouse: {$e->getMessage()}\n"; echo "Error in cleaning stuck clickhouse: {$e->getMessage()}\n";
} }
try { try {
@@ -128,7 +127,7 @@ class CleanupStuckedResources extends Command
echo "Deleting stuck mongodb: {$mongodb->name}\n"; echo "Deleting stuck mongodb: {$mongodb->name}\n";
$mongodb->forceDelete(); $mongodb->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck mongodb: {$e->getMessage()}\n"; echo "Error in cleaning stuck mongodb: {$e->getMessage()}\n";
} }
try { try {
@@ -137,7 +136,7 @@ class CleanupStuckedResources extends Command
echo "Deleting stuck mysql: {$mysql->name}\n"; echo "Deleting stuck mysql: {$mysql->name}\n";
$mysql->forceDelete(); $mysql->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck mysql: {$e->getMessage()}\n"; echo "Error in cleaning stuck mysql: {$e->getMessage()}\n";
} }
try { try {
@@ -146,7 +145,7 @@ class CleanupStuckedResources extends Command
echo "Deleting stuck mariadb: {$mariadb->name}\n"; echo "Deleting stuck mariadb: {$mariadb->name}\n";
$mariadb->forceDelete(); $mariadb->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck mariadb: {$e->getMessage()}\n"; echo "Error in cleaning stuck mariadb: {$e->getMessage()}\n";
} }
try { try {
@@ -155,7 +154,7 @@ class CleanupStuckedResources extends Command
echo "Deleting stuck service: {$service->name}\n"; echo "Deleting stuck service: {$service->name}\n";
$service->forceDelete(); $service->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck service: {$e->getMessage()}\n"; echo "Error in cleaning stuck service: {$e->getMessage()}\n";
} }
try { try {
@@ -164,7 +163,7 @@ class CleanupStuckedResources extends Command
echo "Deleting stuck serviceapp: {$serviceApp->name}\n"; echo "Deleting stuck serviceapp: {$serviceApp->name}\n";
$serviceApp->forceDelete(); $serviceApp->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck serviceapp: {$e->getMessage()}\n"; echo "Error in cleaning stuck serviceapp: {$e->getMessage()}\n";
} }
try { try {
@@ -173,7 +172,7 @@ class CleanupStuckedResources extends Command
echo "Deleting stuck serviceapp: {$serviceDb->name}\n"; echo "Deleting stuck serviceapp: {$serviceDb->name}\n";
$serviceDb->forceDelete(); $serviceDb->forceDelete();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck serviceapp: {$e->getMessage()}\n"; echo "Error in cleaning stuck serviceapp: {$e->getMessage()}\n";
} }
try { try {
@@ -184,7 +183,7 @@ class CleanupStuckedResources extends Command
$scheduled_task->delete(); $scheduled_task->delete();
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck scheduledtasks: {$e->getMessage()}\n"; echo "Error in cleaning stuck scheduledtasks: {$e->getMessage()}\n";
} }
@@ -196,7 +195,7 @@ class CleanupStuckedResources extends Command
$scheduled_backup->delete(); $scheduled_backup->delete();
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning stuck scheduledbackups: {$e->getMessage()}\n"; echo "Error in cleaning stuck scheduledbackups: {$e->getMessage()}\n";
} }
@@ -223,7 +222,7 @@ class CleanupStuckedResources extends Command
continue; continue;
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in application: {$e->getMessage()}\n"; echo "Error in application: {$e->getMessage()}\n";
} }
try { try {
@@ -248,32 +247,32 @@ class CleanupStuckedResources extends Command
continue; continue;
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in postgresql: {$e->getMessage()}\n"; echo "Error in postgresql: {$e->getMessage()}\n";
} }
try { try {
$redis = StandaloneRedis::all(); $redis = StandaloneRedis::all();
foreach ($redis as $redi) { foreach ($redis as $redis) {
if (! data_get($redi, 'environment')) { if (! data_get($redis, 'environment')) {
echo 'Redis without environment: '.$redi->name.'\n'; echo 'Redis without environment: '.$redis->name.'\n';
$redi->forceDelete(); $redis->forceDelete();
continue; continue;
} }
if (! $redi->destination()) { if (! $redis->destination()) {
echo 'Redis without destination: '.$redi->name.'\n'; echo 'Redis without destination: '.$redis->name.'\n';
$redi->forceDelete(); $redis->forceDelete();
continue; continue;
} }
if (! data_get($redi, 'destination.server')) { if (! data_get($redis, 'destination.server')) {
echo 'Redis without server: '.$redi->name.'\n'; echo 'Redis without server: '.$redis->name.'\n';
$redi->forceDelete(); $redis->forceDelete();
continue; continue;
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in redis: {$e->getMessage()}\n"; echo "Error in redis: {$e->getMessage()}\n";
} }
@@ -299,7 +298,7 @@ class CleanupStuckedResources extends Command
continue; continue;
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in mongodb: {$e->getMessage()}\n"; echo "Error in mongodb: {$e->getMessage()}\n";
} }
@@ -325,7 +324,7 @@ class CleanupStuckedResources extends Command
continue; continue;
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in mysql: {$e->getMessage()}\n"; echo "Error in mysql: {$e->getMessage()}\n";
} }
@@ -351,7 +350,7 @@ class CleanupStuckedResources extends Command
continue; continue;
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in mariadb: {$e->getMessage()}\n"; echo "Error in mariadb: {$e->getMessage()}\n";
} }
@@ -377,33 +376,33 @@ class CleanupStuckedResources extends Command
continue; continue;
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in service: {$e->getMessage()}\n"; echo "Error in service: {$e->getMessage()}\n";
} }
try { try {
$serviceApplications = ServiceApplication::all(); $serviceApplications = ServiceApplication::all();
foreach ($serviceApplications as $serviceApplication) { foreach ($serviceApplications as $service) {
if (! data_get($serviceApplication, 'service')) { if (! data_get($service, 'service')) {
echo 'ServiceApplication without service: '.$serviceApplication->name.'\n'; echo 'ServiceApplication without service: '.$service->name.'\n';
$serviceApplication->forceDelete(); $service->forceDelete();
continue; continue;
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in serviceApplications: {$e->getMessage()}\n"; echo "Error in serviceApplications: {$e->getMessage()}\n";
} }
try { try {
$serviceDatabases = ServiceDatabase::all(); $serviceDatabases = ServiceDatabase::all();
foreach ($serviceDatabases as $serviceDatabase) { foreach ($serviceDatabases as $service) {
if (! data_get($serviceDatabase, 'service')) { if (! data_get($service, 'service')) {
echo 'ServiceDatabase without service: '.$serviceDatabase->name.'\n'; echo 'ServiceDatabase without service: '.$service->name.'\n';
$serviceDatabase->forceDelete(); $service->forceDelete();
continue; continue;
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in ServiceDatabases: {$e->getMessage()}\n"; echo "Error in ServiceDatabases: {$e->getMessage()}\n";
} }
} }

View File

@@ -14,7 +14,7 @@ class CleanupUnreachableServers extends Command
public function handle() public function handle()
{ {
echo "Running unreachable server cleanup...\n"; echo "Running unreachable server cleanup...\n";
$servers = Server::query()->where('unreachable_count', 3)->where('unreachable_notification_sent', true)->where('updated_at', '<', now()->subDays(7))->get(); $servers = Server::where('unreachable_count', 3)->where('unreachable_notification_sent', true)->where('updated_at', '<', now()->subDays(7))->get();
if ($servers->count() > 0) { if ($servers->count() > 0) {
foreach ($servers as $server) { foreach ($servers as $server) {
echo "Cleanup unreachable server ($server->id) with name $server->name"; echo "Cleanup unreachable server ($server->id) with name $server->name";

View File

@@ -4,7 +4,6 @@ namespace App\Console\Commands;
use App\Models\Team; use App\Models\Team;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Stripe\StripeClient;
class CloudCheckSubscription extends Command class CloudCheckSubscription extends Command
{ {
@@ -27,19 +26,19 @@ class CloudCheckSubscription extends Command
*/ */
public function handle() public function handle()
{ {
$stripeClient = new StripeClient(config('subscription.stripe_api_key')); $stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key'));
$activeSubscribers = Team::query()->whereRelation('subscription', 'stripe_invoice_paid', true)->get(); $activeSubscribers = Team::whereRelation('subscription', 'stripe_invoice_paid', true)->get();
foreach ($activeSubscribers as $activeSubscriber) { foreach ($activeSubscribers as $team) {
$stripeSubscriptionId = $activeSubscriber->subscription->stripe_subscription_id; $stripeSubscriptionId = $team->subscription->stripe_subscription_id;
$stripeInvoicePaid = $activeSubscriber->subscription->stripe_invoice_paid; $stripeInvoicePaid = $team->subscription->stripe_invoice_paid;
$stripeCustomerId = $activeSubscriber->subscription->stripe_customer_id; $stripeCustomerId = $team->subscription->stripe_customer_id;
if (! $stripeSubscriptionId) { if (! $stripeSubscriptionId) {
echo "Team {$activeSubscriber->id} has no subscription, but invoice status is: {$stripeInvoicePaid}\n"; echo "Team {$team->id} has no subscription, but invoice status is: {$stripeInvoicePaid}\n";
echo "Link on Stripe: https://dashboard.stripe.com/customers/{$stripeCustomerId}\n"; echo "Link on Stripe: https://dashboard.stripe.com/customers/{$stripeCustomerId}\n";
continue; continue;
} }
$subscription = $stripeClient->subscriptions->retrieve($stripeSubscriptionId); $subscription = $stripe->subscriptions->retrieve($stripeSubscriptionId);
if ($subscription->status === 'active') { if ($subscription->status === 'active') {
continue; continue;
} }

View File

@@ -4,9 +4,7 @@ namespace App\Console\Commands;
use App\Events\ServerReachabilityChanged; use App\Events\ServerReachabilityChanged;
use App\Models\Team; use App\Models\Team;
use Exception;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Stripe\StripeClient;
class CloudCleanupSubscriptions extends Command class CloudCleanupSubscriptions extends Command
{ {
@@ -23,7 +21,7 @@ class CloudCleanupSubscriptions extends Command
return; return;
} }
$this->info('Cleaning up subcriptions teams'); $this->info('Cleaning up subcriptions teams');
$stripeClient = new StripeClient(config('subscription.stripe_api_key')); $stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key'));
$teams = Team::all()->filter(function ($team) { $teams = Team::all()->filter(function ($team) {
return $team->id !== 0; return $team->id !== 0;
@@ -49,8 +47,8 @@ class CloudCleanupSubscriptions extends Command
$this->disableServers($team); $this->disableServers($team);
continue; continue;
} } else {
$subscription = $stripeClient->subscriptions->retrieve(data_get($team, 'subscription.stripe_subscription_id'), []); $subscription = $stripe->subscriptions->retrieve(data_get($team, 'subscription.stripe_subscription_id'), []);
$status = data_get($subscription, 'status'); $status = data_get($subscription, 'status');
if ($status === 'active' || $status === 'past_due') { if ($status === 'active' || $status === 'past_due') {
$team->subscription->update([ $team->subscription->update([
@@ -75,7 +73,8 @@ class CloudCleanupSubscriptions extends Command
$this->disableServers($team); $this->disableServers($team);
} }
} }
} catch (Exception $e) { }
} catch (\Exception $e) {
$this->error($e->getMessage()); $this->error($e->getMessage());
return; return;

View File

@@ -33,7 +33,7 @@ class Dev extends Command
// Generate OpenAPI documentation // Generate OpenAPI documentation
echo "Generating OpenAPI documentation.\n"; echo "Generating OpenAPI documentation.\n";
// https://github.com/OAI/OpenAPI-Specification/releases // https://github.com/OAI/OpenAPI-Specification/releases
$processResult = Process::run([ $process = Process::run([
'/var/www/html/vendor/bin/openapi', '/var/www/html/vendor/bin/openapi',
'app', 'app',
'-o', '-o',
@@ -41,11 +41,11 @@ class Dev extends Command
'--version', '--version',
'3.1.0', '3.1.0',
]); ]);
$error = $processResult->errorOutput(); $error = $process->errorOutput();
$error = preg_replace('/^.*an object literal,.*$/m', '', $error); $error = preg_replace('/^.*an object literal,.*$/m', '', $error);
$error = preg_replace('/^\h*\v+/m', '', $error); $error = preg_replace('/^\h*\v+/m', '', $error);
echo $error; echo $error;
echo $processResult->output(); echo $process->output();
// Convert YAML to JSON // Convert YAML to JSON
$yaml = file_get_contents('openapi.yaml'); $yaml = file_get_contents('openapi.yaml');
$json = json_encode(Yaml::parse($yaml), JSON_PRETTY_PRINT); $json = json_encode(Yaml::parse($yaml), JSON_PRETTY_PRINT);
@@ -69,7 +69,7 @@ class Dev extends Command
} }
// Seed database if it's empty // Seed database if it's empty
$settings = InstanceSettings::query()->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');

View File

@@ -12,6 +12,7 @@ use App\Notifications\Application\DeploymentFailed;
use App\Notifications\Application\DeploymentSuccess; use App\Notifications\Application\DeploymentSuccess;
use App\Notifications\Application\StatusChanged; use App\Notifications\Application\StatusChanged;
use App\Notifications\Database\BackupFailed; use App\Notifications\Database\BackupFailed;
use App\Notifications\Database\BackupSuccess;
use App\Notifications\Test; use App\Notifications\Test;
use Exception; use Exception;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -42,7 +43,7 @@ class Emails extends Command
/** /**
* Execute the console command. * Execute the console command.
*/ */
private ?MailMessage $mailMessage = null; private ?MailMessage $mail = null;
private ?string $email = null; private ?string $email = null;
@@ -68,13 +69,15 @@ class Emails extends Command
$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';
} elseif (! in_array($type, $emailsGathered)) { } else {
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->mailMessage = new MailMessage; $this->mail = new MailMessage;
$this->mailMessage->subject('Test Email'); $this->mail->subject('Test Email');
switch ($type) { switch ($type) {
case 'updates': case 'updates':
$teams = Team::all(); $teams = Team::all();
@@ -99,18 +102,18 @@ class Emails extends Command
$confirmed = confirm('Are you sure?'); $confirmed = confirm('Are you sure?');
if ($confirmed) { if ($confirmed) {
foreach ($emails as $email) { foreach ($emails as $email) {
$this->mailMessage = new MailMessage; $this->mail = new MailMessage;
$this->mailMessage->subject('One-click Services, Docker Compose support'); $this->mail->subject('One-click Services, Docker Compose support');
$unsubscribeUrl = route('unsubscribe.marketing.emails', [ $unsubscribeUrl = route('unsubscribe.marketing.emails', [
'token' => encrypt($email), 'token' => encrypt($email),
]); ]);
$this->mailMessage->view('emails.updates', ['unsubscribeUrl' => $unsubscribeUrl]); $this->mail->view('emails.updates', ['unsubscribeUrl' => $unsubscribeUrl]);
$this->sendEmail($email); $this->sendEmail($email);
} }
} }
break; break;
case 'emails-test': case 'emails-test':
$this->mailMessage = (new Test)->toMail(); $this->mail = (new Test)->toMail();
$this->sendEmail(); $this->sendEmail();
break; break;
case 'application-deployment-success-daily': case 'application-deployment-success-daily':
@@ -120,41 +123,41 @@ class Emails extends Command
if ($deployments->isEmpty()) { if ($deployments->isEmpty()) {
continue; continue;
} }
$this->mailMessage = (new DeploymentSuccess($application, 'test'))->toMail(); $this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
$this->sendEmail(); $this->sendEmail();
} }
break; break;
case 'application-deployment-success': case 'application-deployment-success':
$application = Application::all()->first(); $application = Application::all()->first();
$this->mailMessage = (new DeploymentSuccess($application, 'test'))->toMail(); $this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
$this->sendEmail(); $this->sendEmail();
break; break;
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::query()->create([ $preview = ApplicationPreview::create([
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => 1, 'pull_request_id' => 1,
'pull_request_html_url' => 'http://example.com', 'pull_request_html_url' => 'http://example.com',
'fqdn' => $application->fqdn, 'fqdn' => $application->fqdn,
]); ]);
} }
$this->mailMessage = (new DeploymentFailed($application, 'test'))->toMail(); $this->mail = (new DeploymentFailed($application, 'test'))->toMail();
$this->sendEmail(); $this->sendEmail();
$this->mailMessage = (new DeploymentFailed($application, 'test', $preview))->toMail(); $this->mail = (new DeploymentFailed($application, 'test', $preview))->toMail();
$this->sendEmail(); $this->sendEmail();
break; break;
case 'application-status-changed': case 'application-status-changed':
$application = Application::all()->first(); $application = Application::all()->first();
$this->mailMessage = (new StatusChanged($application))->toMail(); $this->mail = (new StatusChanged($application))->toMail();
$this->sendEmail(); $this->sendEmail();
break; break;
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::query()->create([ $backup = ScheduledDatabaseBackup::create([
'enabled' => true, 'enabled' => true,
'frequency' => 'daily', 'frequency' => 'daily',
'save_s3' => false, 'save_s3' => false,
@@ -164,14 +167,14 @@ class Emails extends Command
]); ]);
} }
$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->mailMessage = (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::query()->create([ $backup = ScheduledDatabaseBackup::create([
'enabled' => true, 'enabled' => true,
'frequency' => 'daily', 'frequency' => 'daily',
'save_s3' => false, 'save_s3' => false,
@@ -198,10 +201,10 @@ class Emails extends Command
// $this->sendEmail(); // $this->sendEmail();
// break; // break;
case 'realusers-before-trial': case 'realusers-before-trial':
$this->mailMessage = new MailMessage; $this->mail = new MailMessage;
$this->mailMessage->view('emails.before-trial-conversion'); $this->mail->view('emails.before-trial-conversion');
$this->mailMessage->subject('Trial period has been added for all subscription plans.'); $this->mail->subject('Trial period has been added for all subscription plans.');
$teams = Team::query()->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;
@@ -229,7 +232,7 @@ class Emails extends Command
break; break;
case 'realusers-server-lost-connection': case 'realusers-server-lost-connection':
$serverId = text('Server Id'); $serverId = text('Server Id');
$server = Server::query()->find($serverId); $server = Server::find($serverId);
if (! $server) { if (! $server) {
throw new Exception('Server not found'); throw new Exception('Server not found');
} }
@@ -244,13 +247,13 @@ class Emails extends Command
foreach ($admins as $admin) { foreach ($admins as $admin) {
$this->info($admin); $this->info($admin);
} }
$this->mailMessage = new MailMessage; $this->mail = new MailMessage;
$this->mailMessage->view('emails.server-lost-connection', [ $this->mail->view('emails.server-lost-connection', [
'name' => $server->name, 'name' => $server->name,
]); ]);
$this->mailMessage->subject('Action required: Server '.$server->name.' lost connection.'); $this->mail->subject('Action required: Server '.$server->name.' lost connection.');
foreach ($admins as $admin) { foreach ($admins as $email) {
$this->sendEmail($admin); $this->sendEmail($email);
} }
break; break;
} }
@@ -266,8 +269,8 @@ class Emails extends Command
[], [],
fn (Message $message) => $message fn (Message $message) => $message
->to($this->email) ->to($this->email)
->subject($this->mailMessage->subject) ->subject($this->mail->subject)
->html((string) $this->mailMessage->render()) ->html((string) $this->mail->render())
); );
$this->info("Email sent to $this->email successfully. 📧"); $this->info("Email sent to $this->email successfully. 📧");
} }

View File

@@ -16,7 +16,8 @@ class Horizon extends Command
$this->info('Horizon is enabled on this server.'); $this->info('Horizon is enabled on this server.');
$this->call('horizon'); $this->call('horizon');
exit(0); exit(0);
} } else {
exit(0); exit(0);
} }
} }
}

View File

@@ -15,7 +15,6 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Throwable;
class Init extends Command class Init extends Command
{ {
@@ -23,7 +22,7 @@ class Init extends Command
protected $description = 'Cleanup instance related stuffs'; protected $description = 'Cleanup instance related stuffs';
public $servers; public $servers = null;
public function handle() public function handle()
{ {
@@ -36,7 +35,8 @@ class Init extends Command
} }
$this->servers = Server::all(); $this->servers = Server::all();
if (! isCloud()) { if (isCloud()) {
} else {
$this->send_alive_signal(); $this->send_alive_signal();
get_public_ips(); get_public_ips();
} }
@@ -61,14 +61,14 @@ class Init extends Command
try { try {
$this->pullHelperImage(); $this->pullHelperImage();
} catch (Throwable $e) { } catch (\Throwable $e) {
// //
} }
if (isCloud()) { if (isCloud()) {
try { try {
$this->pullTemplatesFromCDN(); $this->pullTemplatesFromCDN();
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Could not pull templates from CDN: {$e->getMessage()}\n"; echo "Could not pull templates from CDN: {$e->getMessage()}\n";
} }
} }
@@ -76,13 +76,13 @@ class Init extends Command
if (! isCloud()) { if (! isCloud()) {
try { try {
$this->pullTemplatesFromCDN(); $this->pullTemplatesFromCDN();
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Could not pull templates from CDN: {$e->getMessage()}\n"; echo "Could not pull templates from CDN: {$e->getMessage()}\n";
} }
try { try {
$localhost = $this->servers->where('id', 0)->first(); $localhost = $this->servers->where('id', 0)->first();
$localhost->setupDynamicProxyConfiguration(); $localhost->setupDynamicProxyConfiguration();
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Could not setup dynamic configuration: {$e->getMessage()}\n"; echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
} }
$settings = instanceSettings(); $settings = instanceSettings();
@@ -119,8 +119,8 @@ class Init extends Command
private function update_user_emails() private function update_user_emails()
{ {
try { try {
User::query()->whereRaw('email ~ \'[A-Z]\'')->get()->each(fn (User $user) => $user->update(['email' => strtolower($user->email)])); User::whereRaw('email ~ \'[A-Z]\'')->get()->each(fn (User $user) => $user->update(['email' => strtolower($user->email)]));
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in updating user emails: {$e->getMessage()}\n"; echo "Error in updating user emails: {$e->getMessage()}\n";
} }
} }
@@ -128,8 +128,8 @@ class Init extends Command
private function update_traefik_labels() private function update_traefik_labels()
{ {
try { try {
Server::query()->where('proxy->type', 'TRAEFIK_V2')->update(['proxy->type' => 'TRAEFIK']); Server::where('proxy->type', 'TRAEFIK_V2')->update(['proxy->type' => 'TRAEFIK']);
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in updating traefik labels: {$e->getMessage()}\n"; echo "Error in updating traefik labels: {$e->getMessage()}\n";
} }
} }
@@ -149,12 +149,10 @@ class Init extends Command
return instant_remote_process([ return instant_remote_process([
"rm -f $file", "rm -f $file",
], $server, false); ], $server, false);
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning up unnecessary dynamic proxy configuration: {$e->getMessage()}\n"; echo "Error in cleaning up unnecessary dynamic proxy configuration: {$e->getMessage()}\n";
} }
} }
return null;
} }
private function cleanup_unused_network_from_coolify_proxy() private function cleanup_unused_network_from_coolify_proxy()
@@ -170,19 +168,19 @@ class Init extends Command
['networks' => $networks, 'allNetworks' => $allNetworks] = collectDockerNetworksByServer($server); ['networks' => $networks, 'allNetworks' => $allNetworks] = collectDockerNetworksByServer($server);
$removeNetworks = $allNetworks->diff($networks); $removeNetworks = $allNetworks->diff($networks);
$commands = collect(); $commands = collect();
foreach ($removeNetworks as $removeNetwork) { foreach ($removeNetworks as $network) {
$out = instant_remote_process(["docker network inspect -f json {$removeNetwork} | jq '.[].Containers | if . == {} then null else . end'"], $server, false); $out = instant_remote_process(["docker network inspect -f json $network | jq '.[].Containers | if . == {} then null else . end'"], $server, false);
if ($out === null || $out === '' || $out === '0') { if (empty($out)) {
$commands->push("docker network disconnect {$removeNetwork} coolify-proxy >/dev/null 2>&1 || true"); $commands->push("docker network disconnect $network coolify-proxy >/dev/null 2>&1 || true");
$commands->push("docker network rm {$removeNetwork} >/dev/null 2>&1 || true"); $commands->push("docker network rm $network >/dev/null 2>&1 || true");
} else { } else {
$data = collect(json_decode($out, true)); $data = collect(json_decode($out, true));
if ($data->count() === 1) { if ($data->count() === 1) {
// If only coolify-proxy itself is connected to that network (it should not be possible, but who knows) // If only coolify-proxy itself is connected to that network (it should not be possible, but who knows)
$isCoolifyProxyItself = data_get($data->first(), 'Name') === 'coolify-proxy'; $isCoolifyProxyItself = data_get($data->first(), 'Name') === 'coolify-proxy';
if ($isCoolifyProxyItself) { if ($isCoolifyProxyItself) {
$commands->push("docker network disconnect {$removeNetwork} coolify-proxy >/dev/null 2>&1 || true"); $commands->push("docker network disconnect $network coolify-proxy >/dev/null 2>&1 || true");
$commands->push("docker network rm {$removeNetwork} >/dev/null 2>&1 || true"); $commands->push("docker network rm $network >/dev/null 2>&1 || true");
} }
} }
} }
@@ -190,7 +188,7 @@ class Init extends Command
if ($commands->isNotEmpty()) { if ($commands->isNotEmpty()) {
remote_process(command: $commands, type: ActivityTypes::INLINE->value, server: $server, ignore_errors: false); remote_process(command: $commands, type: ActivityTypes::INLINE->value, server: $server, ignore_errors: false);
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in cleaning up unused networks from coolify proxy: {$e->getMessage()}\n"; echo "Error in cleaning up unused networks from coolify proxy: {$e->getMessage()}\n";
} }
} }
@@ -204,20 +202,20 @@ class Init extends Command
if ($database && $database->trashed()) { if ($database && $database->trashed()) {
echo "Restoring coolify db backup\n"; echo "Restoring coolify db backup\n";
$database->restore(); $database->restore();
$scheduledBackup = ScheduledDatabaseBackup::query()->find(0); $scheduledBackup = ScheduledDatabaseBackup::find(0);
if (! $scheduledBackup) { if (! $scheduledBackup) {
ScheduledDatabaseBackup::query()->create([ ScheduledDatabaseBackup::create([
'id' => 0, 'id' => 0,
'enabled' => true, 'enabled' => true,
'save_s3' => false, 'save_s3' => false,
'frequency' => '0 0 * * *', 'frequency' => '0 0 * * *',
'database_id' => $database->id, 'database_id' => $database->id,
'database_type' => StandalonePostgresql::class, 'database_type' => \App\Models\StandalonePostgresql::class,
'team_id' => 0, 'team_id' => 0,
]); ]);
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in restoring coolify db backup: {$e->getMessage()}\n"; echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
} }
} }
@@ -236,7 +234,7 @@ class Init extends Command
} }
try { try {
Http::get("https://undead.coolify.io/v4/alive?appId=$id&version=$version"); Http::get("https://undead.coolify.io/v4/alive?appId=$id&version=$version");
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error in sending live signal: {$e->getMessage()}\n"; echo "Error in sending live signal: {$e->getMessage()}\n";
} }
} }
@@ -248,12 +246,12 @@ class Init extends Command
if (isCloud()) { if (isCloud()) {
return; return;
} }
$queued_inprogress_deployments = ApplicationDeploymentQueue::query()->whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS->value, ApplicationDeploymentStatus::QUEUED->value])->get(); $queued_inprogress_deployments = ApplicationDeploymentQueue::whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS->value, ApplicationDeploymentStatus::QUEUED->value])->get();
foreach ($queued_inprogress_deployments as $queued_inprogress_deployment) { foreach ($queued_inprogress_deployments as $deployment) {
$queued_inprogress_deployment->status = ApplicationDeploymentStatus::FAILED->value; $deployment->status = ApplicationDeploymentStatus::FAILED->value;
$queued_inprogress_deployment->save(); $deployment->save();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
echo "Error: {$e->getMessage()}\n"; echo "Error: {$e->getMessage()}\n";
} }
} }

View File

@@ -16,8 +16,9 @@ class Migration extends Command
$this->info('Migration is enabled on this server.'); $this->info('Migration is enabled on this server.');
$this->call('migrate', ['--force' => true, '--isolated' => true]); $this->call('migrate', ['--force' => true, '--isolated' => true]);
exit(0); exit(0);
} } else {
$this->info('Migration is disabled on this server.'); $this->info('Migration is disabled on this server.');
exit(0); exit(0);
} }
} }
}

View File

@@ -16,7 +16,7 @@ class OpenApi extends Command
// Generate OpenAPI documentation // Generate OpenAPI documentation
echo "Generating OpenAPI documentation.\n"; echo "Generating OpenAPI documentation.\n";
// https://github.com/OAI/OpenAPI-Specification/releases // https://github.com/OAI/OpenAPI-Specification/releases
$processResult = Process::run([ $process = Process::run([
'/var/www/html/vendor/bin/openapi', '/var/www/html/vendor/bin/openapi',
'app', 'app',
'-o', '-o',
@@ -24,10 +24,10 @@ class OpenApi extends Command
'--version', '--version',
'3.1.0', '3.1.0',
]); ]);
$error = $processResult->errorOutput(); $error = $process->errorOutput();
$error = preg_replace('/^.*an object literal,.*$/m', '', $error); $error = preg_replace('/^.*an object literal,.*$/m', '', $error);
$error = preg_replace('/^\h*\v+/m', '', $error); $error = preg_replace('/^\h*\v+/m', '', $error);
echo $error; echo $error;
echo $processResult->output(); echo $process->output();
} }
} }

View File

@@ -3,7 +3,6 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Models\User; use App\Models\User;
use Exception;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class RootChangeEmail extends Command class RootChangeEmail extends Command
@@ -32,9 +31,9 @@ class RootChangeEmail extends Command
$email = $this->ask('Give me a new email for root user'); $email = $this->ask('Give me a new email for root user');
$this->info('Updating root email...'); $this->info('Updating root email...');
try { try {
User::query()->find(0)->update(['email' => $email]); User::find(0)->update(['email' => $email]);
$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

@@ -3,7 +3,6 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Models\User; use App\Models\User;
use Exception;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
@@ -33,16 +32,16 @@ class RootResetPassword extends Command
$this->info('You are about to reset the root password.'); $this->info('You are about to reset the root password.');
$password = password('Give me a new password for root user: '); $password = password('Give me a new password for root user: ');
$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...');
try { try {
User::query()->find(0)->update(['password' => Hash::make($password)]); User::find(0)->update(['password' => Hash::make($password)]);
$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

@@ -16,7 +16,8 @@ class Scheduler extends Command
$this->info('Scheduler is enabled on this server.'); $this->info('Scheduler is enabled on this server.');
$this->call('schedule:work'); $this->call('schedule:work');
exit(0); exit(0);
} } else {
exit(0); exit(0);
} }
} }
}

View File

@@ -16,8 +16,9 @@ class Seeder extends Command
$this->info('Seeder is enabled on this server.'); $this->info('Seeder is enabled on this server.');
$this->call('db:seed', ['--class' => 'ProductionSeeder', '--force' => true]); $this->call('db:seed', ['--class' => 'ProductionSeeder', '--force' => true]);
exit(0); exit(0);
} } else {
$this->info('Seeder is disabled on this server.'); $this->info('Seeder is disabled on this server.');
exit(0); exit(0);
} }
} }
}

View File

@@ -62,8 +62,8 @@ class ServicesDelete extends Command
options: $servers->pluck('name', 'id')->sortKeys(), options: $servers->pluck('name', 'id')->sortKeys(),
); );
foreach ($serversToDelete as $serverToDelete) { foreach ($serversToDelete as $server) {
$toDelete = $servers->where('id', $serverToDelete)->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?');
@@ -88,8 +88,8 @@ class ServicesDelete extends Command
$applications->pluck('name', 'id')->sortKeys(), $applications->pluck('name', 'id')->sortKeys(),
); );
foreach ($applicationsToDelete as $applicationToDelete) { foreach ($applicationsToDelete as $application) {
$toDelete = $applications->where('id', $applicationToDelete)->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? ');
@@ -114,8 +114,8 @@ class ServicesDelete extends Command
$databases->pluck('name', 'id')->sortKeys(), $databases->pluck('name', 'id')->sortKeys(),
); );
foreach ($databasesToDelete as $databaseToDelete) { foreach ($databasesToDelete as $database) {
$toDelete = $databases->where('id', $databaseToDelete)->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?');
@@ -140,8 +140,8 @@ class ServicesDelete extends Command
$services->pluck('name', 'id')->sortKeys(), $services->pluck('name', 'id')->sortKeys(),
); );
foreach ($servicesToDelete as $serviceToDelete) { foreach ($servicesToDelete as $service) {
$toDelete = $services->where('id', $serviceToDelete)->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?');

View File

@@ -45,7 +45,7 @@ class ServicesGenerate extends Command
$data = collect(explode(PHP_EOL, $content))->mapWithKeys(function ($line): array { $data = collect(explode(PHP_EOL, $content))->mapWithKeys(function ($line): array {
preg_match('/^#(?<key>.*):(?<value>.*)$/U', $line, $m); preg_match('/^#(?<key>.*):(?<value>.*)$/U', $line, $m);
return $m !== [] ? [trim($m['key']) => trim($m['value'])] : []; return $m ? [trim($m['key']) => trim($m['value'])] : [];
}); });
if (str($data->get('ignore'))->toBoolean()) { if (str($data->get('ignore'))->toBoolean()) {

View File

@@ -6,7 +6,6 @@ use Illuminate\Console\Command;
use Illuminate\Http\Client\PendingRequest; use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Pool; use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Throwable;
use function Laravel\Prompts\confirm; use function Laravel\Prompts\confirm;
@@ -115,8 +114,7 @@ class SyncBunny extends Command
$this->info('Service template uploaded & purged...'); $this->info('Service template uploaded & purged...');
return; return;
} } elseif ($only_version) {
if ($only_version) {
if ($nightly) { if ($nightly) {
$this->info('About to sync NIGHLTY versions.json to BunnyCDN.'); $this->info('About to sync NIGHLTY versions.json to BunnyCDN.');
} else { } else {
@@ -125,6 +123,7 @@ class SyncBunny extends Command
$file = file_get_contents($versions_location); $file = file_get_contents($versions_location);
$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;
@@ -153,7 +152,7 @@ class SyncBunny extends Command
$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

@@ -6,11 +6,13 @@ use App\Jobs\CheckAndStartSentinelJob;
use App\Jobs\CheckForUpdatesJob; use App\Jobs\CheckForUpdatesJob;
use App\Jobs\CheckHelperImageJob; use App\Jobs\CheckHelperImageJob;
use App\Jobs\CleanupInstanceStuffsJob; use App\Jobs\CleanupInstanceStuffsJob;
use App\Jobs\CleanupStaleMultiplexedConnections;
use App\Jobs\DatabaseBackupJob; use App\Jobs\DatabaseBackupJob;
use App\Jobs\DockerCleanupJob; use App\Jobs\DockerCleanupJob;
use App\Jobs\PullTemplatesFromCDN; use App\Jobs\PullTemplatesFromCDN;
use App\Jobs\ScheduledTaskJob; use App\Jobs\ScheduledTaskJob;
use App\Jobs\ServerCheckJob; use App\Jobs\ServerCheckJob;
use App\Jobs\ServerCleanupMux;
use App\Jobs\ServerStorageCheckJob; use App\Jobs\ServerStorageCheckJob;
use App\Jobs\UpdateCoolifyJob; use App\Jobs\UpdateCoolifyJob;
use App\Models\InstanceSettings; use App\Models\InstanceSettings;
@@ -26,9 +28,9 @@ class Kernel extends ConsoleKernel
{ {
private $allServers; private $allServers;
private Schedule $schedule; private Schedule $scheduleInstance;
private InstanceSettings $instanceSettings; private InstanceSettings $settings;
private string $updateCheckFrequency; private string $updateCheckFrequency;
@@ -36,13 +38,13 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule): void protected function schedule(Schedule $schedule): void
{ {
$this->schedule = $schedule; $this->scheduleInstance = $schedule;
$this->allServers = Server::query()->where('ip', '!=', '1.2.3.4'); $this->allServers = Server::where('ip', '!=', '1.2.3.4');
$this->instanceSettings = instanceSettings(); $this->settings = instanceSettings();
$this->updateCheckFrequency = $this->instanceSettings->update_check_frequency ?: '0 * * * *'; $this->updateCheckFrequency = $this->settings->update_check_frequency ?: '0 * * * *';
$this->instanceTimezone = $this->instanceSettings->instance_timezone ?: config('app.timezone'); $this->instanceTimezone = $this->settings->instance_timezone ?: config('app.timezone');
if (validate_timezone($this->instanceTimezone) === false) { if (validate_timezone($this->instanceTimezone) === false) {
$this->instanceTimezone = config('app.timezone'); $this->instanceTimezone = config('app.timezone');
@@ -52,9 +54,9 @@ class Kernel extends ConsoleKernel
if (isDev()) { if (isDev()) {
// Instance Jobs // Instance Jobs
$this->schedule->command('horizon:snapshot')->everyMinute(); $this->scheduleInstance->command('horizon:snapshot')->everyMinute();
$this->schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer(); $this->scheduleInstance->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
$this->schedule->job(new CheckHelperImageJob)->everyTenMinutes()->onOneServer(); $this->scheduleInstance->job(new CheckHelperImageJob)->everyTenMinutes()->onOneServer();
// Server Jobs // Server Jobs
$this->checkResources(); $this->checkResources();
@@ -62,16 +64,16 @@ class Kernel extends ConsoleKernel
$this->checkScheduledBackups(); $this->checkScheduledBackups();
$this->checkScheduledTasks(); $this->checkScheduledTasks();
$this->schedule->command('uploads:clear')->everyTwoMinutes(); $this->scheduleInstance->command('uploads:clear')->everyTwoMinutes();
} else { } else {
// Instance Jobs // Instance Jobs
$this->schedule->command('horizon:snapshot')->everyFiveMinutes(); $this->scheduleInstance->command('horizon:snapshot')->everyFiveMinutes();
$this->schedule->command('cleanup:unreachable-servers')->daily()->onOneServer(); $this->scheduleInstance->command('cleanup:unreachable-servers')->daily()->onOneServer();
$this->schedule->job(new PullTemplatesFromCDN)->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer(); $this->scheduleInstance->job(new PullTemplatesFromCDN)->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer();
$this->schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer(); $this->scheduleInstance->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
$this->scheduleUpdates(); $this->scheduleUpdates();
// Server Jobs // Server Jobs
@@ -82,8 +84,8 @@ class Kernel extends ConsoleKernel
$this->checkScheduledBackups(); $this->checkScheduledBackups();
$this->checkScheduledTasks(); $this->checkScheduledTasks();
$this->schedule->command('cleanup:database --yes')->daily(); $this->scheduleInstance->command('cleanup:database --yes')->daily();
$this->schedule->command('uploads:clear')->everyTwoMinutes(); $this->scheduleInstance->command('uploads:clear')->everyTwoMinutes();
} }
} }
@@ -92,12 +94,12 @@ class Kernel extends ConsoleKernel
$servers = $this->allServers->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_reachable', true)->get(); $servers = $this->allServers->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_reachable', true)->get();
foreach ($servers as $server) { foreach ($servers as $server) {
if ($server->isSentinelEnabled()) { if ($server->isSentinelEnabled()) {
$this->schedule->job(function () use ($server) { $this->scheduleInstance->job(function () use ($server) {
CheckAndStartSentinelJob::dispatch($server); CheckAndStartSentinelJob::dispatch($server);
})->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer(); })->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer();
} }
} }
$this->schedule->job(new CheckHelperImageJob) $this->scheduleInstance->job(new CheckHelperImageJob)
->cron($this->updateCheckFrequency) ->cron($this->updateCheckFrequency)
->timezone($this->instanceTimezone) ->timezone($this->instanceTimezone)
->onOneServer(); ->onOneServer();
@@ -105,14 +107,14 @@ class Kernel extends ConsoleKernel
private function scheduleUpdates(): void private function scheduleUpdates(): void
{ {
$this->schedule->job(new CheckForUpdatesJob) $this->scheduleInstance->job(new CheckForUpdatesJob)
->cron($this->updateCheckFrequency) ->cron($this->updateCheckFrequency)
->timezone($this->instanceTimezone) ->timezone($this->instanceTimezone)
->onOneServer(); ->onOneServer();
if ($this->instanceSettings->is_auto_update_enabled) { if ($this->settings->is_auto_update_enabled) {
$autoUpdateFrequency = $this->instanceSettings->auto_update_frequency; $autoUpdateFrequency = $this->settings->auto_update_frequency;
$this->schedule->job(new UpdateCoolifyJob) $this->scheduleInstance->job(new UpdateCoolifyJob)
->cron($autoUpdateFrequency) ->cron($autoUpdateFrequency)
->timezone($this->instanceTimezone) ->timezone($this->instanceTimezone)
->onOneServer(); ->onOneServer();
@@ -123,7 +125,7 @@ class Kernel extends ConsoleKernel
{ {
if (isCloud()) { if (isCloud()) {
$servers = $this->allServers->whereHas('team.subscription')->get(); $servers = $this->allServers->whereHas('team.subscription')->get();
$own = Team::query()->find(0)->servers; $own = Team::find(0)->servers;
$servers = $servers->merge($own); $servers = $servers->merge($own);
} else { } else {
$servers = $this->allServers->get(); $servers = $this->allServers->get();
@@ -140,23 +142,23 @@ class Kernel extends ConsoleKernel
if (Carbon::parse($lastSentinelUpdate)->isBefore(now()->subSeconds($server->waitBeforeDoingSshCheck()))) { if (Carbon::parse($lastSentinelUpdate)->isBefore(now()->subSeconds($server->waitBeforeDoingSshCheck()))) {
// Check container status every minute if Sentinel does not activated // Check container status every minute if Sentinel does not activated
if (isCloud()) { if (isCloud()) {
$this->schedule->job(new ServerCheckJob($server))->timezone($serverTimezone)->everyFiveMinutes()->onOneServer(); $this->scheduleInstance->job(new ServerCheckJob($server))->timezone($serverTimezone)->everyFiveMinutes()->onOneServer();
} else { } else {
$this->schedule->job(new ServerCheckJob($server))->timezone($serverTimezone)->everyMinute()->onOneServer(); $this->scheduleInstance->job(new ServerCheckJob($server))->timezone($serverTimezone)->everyMinute()->onOneServer();
} }
// $this->scheduleInstance->job(new \App\Jobs\ServerCheckNewJob($server))->everyFiveMinutes()->onOneServer(); // $this->scheduleInstance->job(new \App\Jobs\ServerCheckNewJob($server))->everyFiveMinutes()->onOneServer();
$this->schedule->job(new ServerStorageCheckJob($server))->cron($server->settings->server_disk_usage_check_frequency)->timezone($serverTimezone)->onOneServer(); $this->scheduleInstance->job(new ServerStorageCheckJob($server))->cron($server->settings->server_disk_usage_check_frequency)->timezone($serverTimezone)->onOneServer();
} }
$this->schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer(); $this->scheduleInstance->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer();
// Cleanup multiplexed connections every hour // Cleanup multiplexed connections every hour
// $this->scheduleInstance->job(new ServerCleanupMux($server))->hourly()->onOneServer(); // $this->scheduleInstance->job(new ServerCleanupMux($server))->hourly()->onOneServer();
// Temporary solution until we have better memory management for Sentinel // Temporary solution until we have better memory management for Sentinel
if ($server->isSentinelEnabled()) { if ($server->isSentinelEnabled()) {
$this->schedule->job(function () use ($server) { $this->scheduleInstance->job(function () use ($server) {
$server->restartContainer('coolify-sentinel'); $server->restartContainer('coolify-sentinel');
})->daily()->onOneServer(); })->daily()->onOneServer();
} }
@@ -165,7 +167,7 @@ class Kernel extends ConsoleKernel
private function checkScheduledBackups(): void private function checkScheduledBackups(): void
{ {
$scheduled_backups = ScheduledDatabaseBackup::query()->where('enabled', true)->get(); $scheduled_backups = ScheduledDatabaseBackup::where('enabled', true)->get();
if ($scheduled_backups->isEmpty()) { if ($scheduled_backups->isEmpty()) {
return; return;
} }
@@ -185,15 +187,15 @@ class Kernel extends ConsoleKernel
$scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency]; $scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency];
} }
$serverTimezone = data_get($server->settings, 'server_timezone', $this->instanceTimezone); $serverTimezone = data_get($server->settings, 'server_timezone', $this->instanceTimezone);
$this->schedule->job(new DatabaseBackupJob( $this->scheduleInstance->job(new DatabaseBackupJob(
scheduledDatabaseBackup: $scheduled_backup backup: $scheduled_backup
))->cron($scheduled_backup->frequency)->timezone($serverTimezone)->onOneServer(); ))->cron($scheduled_backup->frequency)->timezone($serverTimezone)->onOneServer();
} }
} }
private function checkScheduledTasks(): void private function checkScheduledTasks(): void
{ {
$scheduled_tasks = ScheduledTask::query()->where('enabled', true)->get(); $scheduled_tasks = ScheduledTask::where('enabled', true)->get();
if ($scheduled_tasks->isEmpty()) { if ($scheduled_tasks->isEmpty()) {
return; return;
} }
@@ -206,12 +208,16 @@ class Kernel extends ConsoleKernel
continue; continue;
} }
if ($application && str($application->status)->contains('running') === false) { if ($application) {
if (str($application->status)->contains('running') === false) {
continue; continue;
} }
if ($service && str($service->status)->contains('running') === false) { }
if ($service) {
if (str($service->status)->contains('running') === false) {
continue; continue;
} }
}
$server = $scheduled_task->server(); $server = $scheduled_task->server();
if (! $server) { if (! $server) {
@@ -222,7 +228,7 @@ class Kernel extends ConsoleKernel
$scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency]; $scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
} }
$serverTimezone = data_get($server->settings, 'server_timezone', $this->instanceTimezone); $serverTimezone = data_get($server->settings, 'server_timezone', $this->instanceTimezone);
$this->schedule->job(new ScheduledTaskJob( $this->scheduleInstance->job(new ScheduledTaskJob(
task: $scheduled_task task: $scheduled_task
))->cron($scheduled_task->frequency)->timezone($serverTimezone)->onOneServer(); ))->cron($scheduled_task->frequency)->timezone($serverTimezone)->onOneServer();
} }

View File

@@ -9,7 +9,7 @@ use Spatie\LaravelData\Data;
class ServerMetadata extends Data class ServerMetadata extends Data
{ {
public function __construct( public function __construct(
public ?ProxyTypes $proxyTypes, public ?ProxyTypes $type,
public ?ProxyStatus $proxyStatus public ?ProxyStatus $status
) {} ) {}
} }

View File

@@ -2,7 +2,6 @@
namespace App\Events; namespace App\Events;
use Exception;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
@@ -21,7 +20,7 @@ class ApplicationStatusChanged implements ShouldBroadcast
$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,7 +2,6 @@
namespace App\Events; namespace App\Events;
use Exception;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
@@ -21,7 +20,7 @@ class BackupCreated implements ShouldBroadcast
$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,7 +2,6 @@
namespace App\Events; namespace App\Events;
use Exception;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
@@ -21,7 +20,7 @@ class CloudflareTunnelConfigured implements ShouldBroadcast
$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,7 +2,6 @@
namespace App\Events; namespace App\Events;
use Exception;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
@@ -22,7 +21,7 @@ class DatabaseProxyStopped implements ShouldBroadcast
$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

@@ -13,7 +13,7 @@ class DatabaseStatusChanged implements ShouldBroadcast
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
public $userId; public $userId = null;
public function __construct($userId = null) public function __construct($userId = null)
{ {
@@ -21,7 +21,7 @@ class DatabaseStatusChanged implements ShouldBroadcast
$userId = Auth::id() ?? null; $userId = Auth::id() ?? null;
} }
if (is_null($userId)) { if (is_null($userId)) {
return; return false;
} }
$this->userId = $userId; $this->userId = $userId;

View File

@@ -2,7 +2,6 @@
namespace App\Events; namespace App\Events;
use Exception;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
@@ -18,7 +17,7 @@ class FileStorageChanged implements ShouldBroadcast
public function __construct($teamId = null) public function __construct($teamId = 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,7 +2,6 @@
namespace App\Events; namespace App\Events;
use Exception;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
@@ -21,7 +20,7 @@ class ProxyStatusChanged implements ShouldBroadcast
$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

@@ -17,15 +17,18 @@ class RestoreJobFinished
$tmpPath = data_get($data, 'tmpPath'); $tmpPath = data_get($data, 'tmpPath');
$container = data_get($data, 'container'); $container = data_get($data, 'container');
$serverId = data_get($data, 'serverId'); $serverId = data_get($data, 'serverId');
if (filled($scriptPath) && filled($tmpPath) && filled($container) && filled($serverId) && (str($tmpPath)->startsWith('/tmp/') if (filled($scriptPath) && filled($tmpPath) && filled($container) && filled($serverId)) {
if (str($tmpPath)->startsWith('/tmp/')
&& str($scriptPath)->startsWith('/tmp/') && str($scriptPath)->startsWith('/tmp/')
&& ! str($tmpPath)->contains('..') && ! str($tmpPath)->contains('..')
&& ! str($scriptPath)->contains('..') && ! str($scriptPath)->contains('..')
&& strlen($tmpPath) > 5 // longer than just "/tmp/" && strlen($tmpPath) > 5 // longer than just "/tmp/"
&& strlen($scriptPath) > 5)) { && strlen($scriptPath) > 5
) {
$commands[] = "docker exec {$container} sh -c 'rm {$scriptPath}'"; $commands[] = "docker exec {$container} sh -c 'rm {$scriptPath}'";
$commands[] = "docker exec {$container} sh -c 'rm {$tmpPath}'"; $commands[] = "docker exec {$container} sh -c 'rm {$tmpPath}'";
instant_remote_process($commands, Server::query()->find($serverId), throwError: true); instant_remote_process($commands, Server::find($serverId), throwError: true);
}
} }
} }
} }

View File

@@ -2,7 +2,6 @@
namespace App\Events; namespace App\Events;
use Exception;
use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
@@ -21,7 +20,7 @@ class ScheduledTaskDone implements ShouldBroadcast
$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

@@ -21,7 +21,7 @@ class ServiceStatusChanged implements ShouldBroadcast
$userId = Auth::id() ?? null; $userId = Auth::id() ?? null;
} }
if (is_null($userId)) { if (is_null($userId)) {
return; return false;
} }
$this->userId = $userId; $this->userId = $userId;
} }

View File

@@ -16,7 +16,7 @@ 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.
* *
* @var array<class-string<Throwable>, \Psr\Log\LogLevel::*> * @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/ */
protected $levels = [ protected $levels = [
// //
@@ -25,7 +25,7 @@ class Handler extends ExceptionHandler
/** /**
* 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,
@@ -42,15 +42,15 @@ class Handler extends ExceptionHandler
'password_confirmation', 'password_confirmation',
]; ];
private InstanceSettings $instanceSettings; private InstanceSettings $settings;
protected function unauthenticated($request, AuthenticationException $authenticationException) protected function unauthenticated($request, AuthenticationException $exception)
{ {
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $authenticationException)) { if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
return response()->json(['message' => $authenticationException->getMessage()], 401); return response()->json(['message' => $exception->getMessage()], 401);
} }
return redirect()->guest($authenticationException->redirectTo($request) ?? route('login')); return redirect()->guest($exception->redirectTo($request) ?? route('login'));
} }
/** /**
@@ -58,21 +58,21 @@ class Handler extends ExceptionHandler
*/ */
public function register(): void public function register(): void
{ {
$this->reportable(function (Throwable $throwable) { $this->reportable(function (Throwable $e) {
if (isDev()) { if (isDev()) {
return; return;
} }
if ($throwable instanceof RuntimeException) { if ($e instanceof RuntimeException) {
return; return;
} }
$this->instanceSettings = instanceSettings(); $this->settings = instanceSettings();
if ($this->instanceSettings->do_not_track) { if ($this->settings->do_not_track) {
return; return;
} }
app('sentry')->configureScope( app('sentry')->configureScope(
function (Scope $scope) { function (Scope $scope) {
$email = auth()?->user() ? auth()->user()->email : 'guest'; $email = auth()?->user() ? auth()->user()->email : 'guest';
$instanceAdmin = User::query()->find(0)->email ?? 'admin@localhost'; $instanceAdmin = User::find(0)->email ?? 'admin@localhost';
$scope->setUser( $scope->setUser(
[ [
'email' => $email, 'email' => $email,
@@ -81,10 +81,10 @@ class Handler extends ExceptionHandler
); );
} }
); );
if (str($throwable->getMessage())->contains('No space left on device')) { if (str($e->getMessage())->contains('No space left on device')) {
return; return;
} }
Integration::captureUnhandledException($throwable); Integration::captureUnhandledException($e);
}); });
} }
} }

View File

@@ -6,13 +6,12 @@ use App\Models\PrivateKey;
use App\Models\Server; use App\Models\Server;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Process; use Illuminate\Support\Facades\Process;
use RuntimeException;
class SshMultiplexingHelper class SshMultiplexingHelper
{ {
public static function serverSshConfiguration(Server $server) public static function serverSshConfiguration(Server $server)
{ {
$privateKey = PrivateKey::query()->findOrFail($server->private_key_id); $privateKey = PrivateKey::findOrFail($server->private_key_id);
$sshKeyLocation = $privateKey->getKeyLocation(); $sshKeyLocation = $privateKey->getKeyLocation();
$muxFilename = '/var/www/html/storage/app/ssh/mux/mux_'.$server->uuid; $muxFilename = '/var/www/html/storage/app/ssh/mux/mux_'.$server->uuid;
@@ -36,9 +35,9 @@ class SshMultiplexingHelper
$checkCommand .= '-o ProxyCommand="cloudflared access ssh --hostname %h" '; $checkCommand .= '-o ProxyCommand="cloudflared access ssh --hostname %h" ';
} }
$checkCommand .= "{$server->user}@{$server->ip}"; $checkCommand .= "{$server->user}@{$server->ip}";
$processResult = Process::run($checkCommand); $process = Process::run($checkCommand);
if ($processResult->exitCode() !== 0) { if ($process->exitCode() !== 0) {
return self::establishNewMultiplexedConnection($server); return self::establishNewMultiplexedConnection($server);
} }
@@ -61,9 +60,12 @@ class SshMultiplexingHelper
} }
$establishCommand .= self::getCommonSshOptions($server, $sshKeyLocation, $connectionTimeout, $serverInterval); $establishCommand .= self::getCommonSshOptions($server, $sshKeyLocation, $connectionTimeout, $serverInterval);
$establishCommand .= "{$server->user}@{$server->ip}"; $establishCommand .= "{$server->user}@{$server->ip}";
$processResult = Process::run($establishCommand); $establishProcess = Process::run($establishCommand);
if ($establishProcess->exitCode() !== 0) {
return false;
}
return $processResult->exitCode() === 0; return true;
} }
public static function removeMuxFile(Server $server) public static function removeMuxFile(Server $server)
@@ -101,14 +103,15 @@ class SshMultiplexingHelper
} }
$scp_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'), isScp: true); $scp_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'), isScp: true);
$scp_command .= "{$source} {$server->user}@{$server->ip}:{$dest}";
return $scp_command."{$source} {$server->user}@{$server->ip}:{$dest}"; return $scp_command;
} }
public static function generateSshCommand(Server $server, string $command) public static function generateSshCommand(Server $server, string $command)
{ {
if ($server->settings->force_disabled) { if ($server->settings->force_disabled) {
throw new RuntimeException('Server is disabled.'); throw new \RuntimeException('Server is disabled.');
} }
$sshConfig = self::serverSshConfiguration($server); $sshConfig = self::serverSshConfiguration($server);
@@ -137,9 +140,11 @@ class SshMultiplexingHelper
$delimiter = base64_encode($delimiter); $delimiter = base64_encode($delimiter);
$command = str_replace($delimiter, '', $command); $command = str_replace($delimiter, '', $command);
return $ssh_command.("{$server->user}@{$server->ip} 'bash -se' << \\$delimiter".PHP_EOL $ssh_command .= "{$server->user}@{$server->ip} 'bash -se' << \\$delimiter".PHP_EOL
.$command.PHP_EOL .$command.PHP_EOL
.$delimiter); .$delimiter;
return $ssh_command;
} }
private static function isMultiplexingEnabled(): bool private static function isMultiplexingEnabled(): bool
@@ -151,9 +156,9 @@ class SshMultiplexingHelper
{ {
$keyLocation = $privateKey->getKeyLocation(); $keyLocation = $privateKey->getKeyLocation();
$checkKeyCommand = "ls $keyLocation 2>/dev/null"; $checkKeyCommand = "ls $keyLocation 2>/dev/null";
$processResult = Process::run($checkKeyCommand); $keyCheckProcess = Process::run($checkKeyCommand);
if ($processResult->exitCode() !== 0) { if ($keyCheckProcess->exitCode() !== 0) {
$privateKey->storeInFileSystem(); $privateKey->storeInFileSystem();
} }
} }

View File

@@ -15,7 +15,6 @@ use App\Models\PrivateKey;
use App\Models\Project; use App\Models\Project;
use App\Models\Server; use App\Models\Server;
use App\Models\Service; use App\Models\Service;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
@@ -90,7 +89,7 @@ class ApplicationsController extends Controller
if (is_null($teamId)) { if (is_null($teamId)) {
return invalidTokenResponse(); return invalidTokenResponse();
} }
$projects = Project::query()->where('team_id', $teamId)->get(); $projects = Project::where('team_id', $teamId)->get();
$applications = collect(); $applications = collect();
$applications->push($projects->pluck('applications')->flatten()); $applications->push($projects->pluck('applications')->flatten());
$applications = $applications->flatten(); $applications = $applications->flatten();
@@ -718,7 +717,7 @@ class ApplicationsController extends Controller
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
@@ -732,10 +731,12 @@ class ApplicationsController extends Controller
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -824,12 +825,15 @@ class ApplicationsController extends Controller
if ($request->build_pack === 'dockercompose') { if ($request->build_pack === 'dockercompose') {
$request->offsetSet('ports_exposes', '80'); $request->offsetSet('ports_exposes', '80');
} }
$return = $this->validateDataApplications($request, $server); $return = $this->validateDataApplications($request, $server);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$application = new Application; $application = new Application;
removeUnnecessaryFieldsFromRequest($request); removeUnnecessaryFieldsFromRequest($request);
$application->fill($request->all()); $application->fill($request->all());
$dockerComposeDomainsJson = collect(); $dockerComposeDomainsJson = collect();
if ($request->has('docker_compose_domains')) { if ($request->has('docker_compose_domains')) {
@@ -844,6 +848,7 @@ class ApplicationsController extends Controller
if ($dockerComposeDomainsJson->count() > 0) { if ($dockerComposeDomainsJson->count() > 0) {
$application->docker_compose_domains = $dockerComposeDomainsJson; $application->docker_compose_domains = $dockerComposeDomainsJson;
} }
$application->fqdn = $fqdn; $application->fqdn = $fqdn;
$application->destination_id = $destination->id; $application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass(); $application->destination_type = $destination->getMorphClass();
@@ -863,24 +868,27 @@ class ApplicationsController extends Controller
$application->save(); $application->save();
} }
$application->isConfigurationChanged(true); $application->isConfigurationChanged(true);
if ($instantDeploy) { if ($instantDeploy) {
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
queue_application_deployment( queue_application_deployment(
application: $application, application: $application,
deployment_uuid: $deployment_uuid, deployment_uuid: $deployment_uuid,
no_questions_asked: true, no_questions_asked: true,
is_api: true, is_api: true,
); );
} elseif ($application->build_pack === 'dockercompose') { } else {
if ($application->build_pack === 'dockercompose') {
LoadComposeFile::dispatch($application); LoadComposeFile::dispatch($application);
} }
}
return response()->json(serializeApiResponse([ return response()->json(serializeApiResponse([
'uuid' => data_get($application, 'uuid'), 'uuid' => data_get($application, 'uuid'),
'domains' => data_get($application, 'domains'), 'domains' => data_get($application, 'domains'),
]))->setStatusCode(201); ]))->setStatusCode(201);
} } elseif ($type === 'private-gh-app') {
if ($type === 'private-gh-app') {
$validationRules = [ $validationRules = [
'git_repository' => 'string|required', 'git_repository' => 'string|required',
'git_branch' => 'string|required', 'git_branch' => 'string|required',
@@ -892,6 +900,7 @@ class ApplicationsController extends Controller
'docker_compose_raw' => 'string|nullable', 'docker_compose_raw' => 'string|nullable',
]; ];
$validationRules = array_merge(sharedDataApplications(), $validationRules); $validationRules = array_merge(sharedDataApplications(), $validationRules);
$validator = customApiValidator($request->all(), $validationRules); $validator = customApiValidator($request->all(), $validationRules);
if ($validator->fails()) { if ($validator->fails()) {
return response()->json([ return response()->json([
@@ -899,14 +908,16 @@ class ApplicationsController extends Controller
'errors' => $validator->errors(), 'errors' => $validator->errors(),
], 422); ], 422);
} }
if (! $request->has('name')) { if (! $request->has('name')) {
$request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch));
} }
if ($request->build_pack === 'dockercompose') { if ($request->build_pack === 'dockercompose') {
$request->offsetSet('ports_exposes', '80'); $request->offsetSet('ports_exposes', '80');
} }
$return = $this->validateDataApplications($request, $server); $return = $this->validateDataApplications($request, $server);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$githubApp = GithubApp::whereTeamId($teamId)->where('uuid', $githubAppUuid)->first(); $githubApp = GithubApp::whereTeamId($teamId)->where('uuid', $githubAppUuid)->first();
@@ -919,7 +930,9 @@ class ApplicationsController extends Controller
} }
$application = new Application; $application = new Application;
removeUnnecessaryFieldsFromRequest($request); removeUnnecessaryFieldsFromRequest($request);
$application->fill($request->all()); $application->fill($request->all());
$dockerComposeDomainsJson = collect(); $dockerComposeDomainsJson = collect();
if ($request->has('docker_compose_domains')) { if ($request->has('docker_compose_domains')) {
$yaml = Yaml::parse($application->docker_compose_raw); $yaml = Yaml::parse($application->docker_compose_raw);
@@ -956,24 +969,28 @@ class ApplicationsController extends Controller
$application->save(); $application->save();
} }
$application->isConfigurationChanged(true); $application->isConfigurationChanged(true);
if ($instantDeploy) { if ($instantDeploy) {
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
queue_application_deployment( queue_application_deployment(
application: $application, application: $application,
deployment_uuid: $deployment_uuid, deployment_uuid: $deployment_uuid,
no_questions_asked: true, no_questions_asked: true,
is_api: true, is_api: true,
); );
} elseif ($application->build_pack === 'dockercompose') { } else {
if ($application->build_pack === 'dockercompose') {
LoadComposeFile::dispatch($application); LoadComposeFile::dispatch($application);
} }
}
return response()->json(serializeApiResponse([ return response()->json(serializeApiResponse([
'uuid' => data_get($application, 'uuid'), 'uuid' => data_get($application, 'uuid'),
'domains' => data_get($application, 'domains'), 'domains' => data_get($application, 'domains'),
]))->setStatusCode(201); ]))->setStatusCode(201);
} } elseif ($type === 'private-deploy-key') {
if ($type === 'private-deploy-key') {
$validationRules = [ $validationRules = [
'git_repository' => 'string|required', 'git_repository' => 'string|required',
'git_branch' => 'string|required', 'git_branch' => 'string|required',
@@ -984,8 +1001,10 @@ class ApplicationsController extends Controller
'docker_compose_location' => 'string', 'docker_compose_location' => 'string',
'docker_compose_raw' => 'string|nullable', 'docker_compose_raw' => 'string|nullable',
]; ];
$validationRules = array_merge(sharedDataApplications(), $validationRules); $validationRules = array_merge(sharedDataApplications(), $validationRules);
$validator = customApiValidator($request->all(), $validationRules); $validator = customApiValidator($request->all(), $validationRules);
if ($validator->fails()) { if ($validator->fails()) {
return response()->json([ return response()->json([
'message' => 'Validation failed.', 'message' => 'Validation failed.',
@@ -998,17 +1017,21 @@ class ApplicationsController extends Controller
if ($request->build_pack === 'dockercompose') { if ($request->build_pack === 'dockercompose') {
$request->offsetSet('ports_exposes', '80'); $request->offsetSet('ports_exposes', '80');
} }
$return = $this->validateDataApplications($request, $server); $return = $this->validateDataApplications($request, $server);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$privateKey = PrivateKey::whereTeamId($teamId)->where('uuid', $request->private_key_uuid)->first(); $privateKey = PrivateKey::whereTeamId($teamId)->where('uuid', $request->private_key_uuid)->first();
if (! $privateKey) { if (! $privateKey) {
return response()->json(['message' => 'Private Key not found.'], 404); return response()->json(['message' => 'Private Key not found.'], 404);
} }
$application = new Application; $application = new Application;
removeUnnecessaryFieldsFromRequest($request); removeUnnecessaryFieldsFromRequest($request);
$application->fill($request->all()); $application->fill($request->all());
$dockerComposeDomainsJson = collect(); $dockerComposeDomainsJson = collect();
if ($request->has('docker_compose_domains')) { if ($request->has('docker_compose_domains')) {
$yaml = Yaml::parse($application->docker_compose_raw); $yaml = Yaml::parse($application->docker_compose_raw);
@@ -1043,29 +1066,33 @@ class ApplicationsController extends Controller
$application->save(); $application->save();
} }
$application->isConfigurationChanged(true); $application->isConfigurationChanged(true);
if ($instantDeploy) { if ($instantDeploy) {
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
queue_application_deployment( queue_application_deployment(
application: $application, application: $application,
deployment_uuid: $deployment_uuid, deployment_uuid: $deployment_uuid,
no_questions_asked: true, no_questions_asked: true,
is_api: true, is_api: true,
); );
} elseif ($application->build_pack === 'dockercompose') { } else {
if ($application->build_pack === 'dockercompose') {
LoadComposeFile::dispatch($application); LoadComposeFile::dispatch($application);
} }
}
return response()->json(serializeApiResponse([ return response()->json(serializeApiResponse([
'uuid' => data_get($application, 'uuid'), 'uuid' => data_get($application, 'uuid'),
'domains' => data_get($application, 'domains'), 'domains' => data_get($application, 'domains'),
]))->setStatusCode(201); ]))->setStatusCode(201);
} } elseif ($type === 'dockerfile') {
if ($type === 'dockerfile') {
$validationRules = [ $validationRules = [
'dockerfile' => 'string|required', 'dockerfile' => 'string|required',
]; ];
$validationRules = array_merge(sharedDataApplications(), $validationRules); $validationRules = array_merge(sharedDataApplications(), $validationRules);
$validator = customApiValidator($request->all(), $validationRules); $validator = customApiValidator($request->all(), $validationRules);
if ($validator->fails()) { if ($validator->fails()) {
return response()->json([ return response()->json([
'message' => 'Validation failed.', 'message' => 'Validation failed.',
@@ -1075,8 +1102,9 @@ class ApplicationsController extends Controller
if (! $request->has('name')) { if (! $request->has('name')) {
$request->offsetSet('name', 'dockerfile-'.new Cuid2); $request->offsetSet('name', 'dockerfile-'.new Cuid2);
} }
$return = $this->validateDataApplications($request, $server); $return = $this->validateDataApplications($request, $server);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
if (! isBase64Encoded($request->dockerfile)) { if (! isBase64Encoded($request->dockerfile)) {
@@ -1098,10 +1126,12 @@ class ApplicationsController extends Controller
} }
$dockerFile = base64_decode($request->dockerfile); $dockerFile = base64_decode($request->dockerfile);
removeUnnecessaryFieldsFromRequest($request); removeUnnecessaryFieldsFromRequest($request);
$port = get_port_from_dockerfile($request->dockerfile); $port = get_port_from_dockerfile($request->dockerfile);
if (! $port) { if (! $port) {
$port = 80; $port = 80;
} }
$application = new Application; $application = new Application;
$application->fill($request->all()); $application->fill($request->all());
$application->fqdn = $fqdn; $application->fqdn = $fqdn;
@@ -1111,6 +1141,7 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id; $application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass(); $application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id; $application->environment_id = $environment->id;
$application->git_repository = 'coollabsio/coolify'; $application->git_repository = 'coollabsio/coolify';
$application->git_branch = 'main'; $application->git_branch = 'main';
$application->save(); $application->save();
@@ -1124,6 +1155,7 @@ class ApplicationsController extends Controller
$application->save(); $application->save();
} }
$application->isConfigurationChanged(true); $application->isConfigurationChanged(true);
if ($instantDeploy) { if ($instantDeploy) {
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
@@ -1139,8 +1171,7 @@ class ApplicationsController extends Controller
'uuid' => data_get($application, 'uuid'), 'uuid' => data_get($application, 'uuid'),
'domains' => data_get($application, 'domains'), 'domains' => data_get($application, 'domains'),
]))->setStatusCode(201); ]))->setStatusCode(201);
} } elseif ($type === 'dockerimage') {
if ($type === 'dockerimage') {
$validationRules = [ $validationRules = [
'docker_registry_image_name' => 'string|required', 'docker_registry_image_name' => 'string|required',
'docker_registry_image_tag' => 'string', 'docker_registry_image_tag' => 'string',
@@ -1148,6 +1179,7 @@ class ApplicationsController extends Controller
]; ];
$validationRules = array_merge(sharedDataApplications(), $validationRules); $validationRules = array_merge(sharedDataApplications(), $validationRules);
$validator = customApiValidator($request->all(), $validationRules); $validator = customApiValidator($request->all(), $validationRules);
if ($validator->fails()) { if ($validator->fails()) {
return response()->json([ return response()->json([
'message' => 'Validation failed.', 'message' => 'Validation failed.',
@@ -1158,7 +1190,7 @@ class ApplicationsController extends Controller
$request->offsetSet('name', 'docker-image-'.new Cuid2); $request->offsetSet('name', 'docker-image-'.new Cuid2);
} }
$return = $this->validateDataApplications($request, $server); $return = $this->validateDataApplications($request, $server);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
if (! $request->docker_registry_image_tag) { if (! $request->docker_registry_image_tag) {
@@ -1166,12 +1198,14 @@ class ApplicationsController extends Controller
} }
$application = new Application; $application = new Application;
removeUnnecessaryFieldsFromRequest($request); removeUnnecessaryFieldsFromRequest($request);
$application->fill($request->all()); $application->fill($request->all());
$application->fqdn = $fqdn; $application->fqdn = $fqdn;
$application->build_pack = 'dockerimage'; $application->build_pack = 'dockerimage';
$application->destination_id = $destination->id; $application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass(); $application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id; $application->environment_id = $environment->id;
$application->git_repository = 'coollabsio/coolify'; $application->git_repository = 'coollabsio/coolify';
$application->git_branch = 'main'; $application->git_branch = 'main';
$application->save(); $application->save();
@@ -1185,6 +1219,7 @@ class ApplicationsController extends Controller
$application->save(); $application->save();
} }
$application->isConfigurationChanged(true); $application->isConfigurationChanged(true);
if ($instantDeploy) { if ($instantDeploy) {
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
@@ -1200,14 +1235,16 @@ class ApplicationsController extends Controller
'uuid' => data_get($application, 'uuid'), 'uuid' => data_get($application, 'uuid'),
'domains' => data_get($application, 'domains'), 'domains' => data_get($application, 'domains'),
]))->setStatusCode(201); ]))->setStatusCode(201);
} } elseif ($type === 'dockercompose') {
if ($type === 'dockercompose') {
$allowedFields = ['project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'instant_deploy', 'docker_compose_raw']; $allowedFields = ['project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'instant_deploy', 'docker_compose_raw'];
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1223,6 +1260,7 @@ class ApplicationsController extends Controller
]; ];
$validationRules = array_merge(sharedDataApplications(), $validationRules); $validationRules = array_merge(sharedDataApplications(), $validationRules);
$validator = customApiValidator($request->all(), $validationRules); $validator = customApiValidator($request->all(), $validationRules);
if ($validator->fails()) { if ($validator->fails()) {
return response()->json([ return response()->json([
'message' => 'Validation failed.', 'message' => 'Validation failed.',
@@ -1230,7 +1268,7 @@ class ApplicationsController extends Controller
], 422); ], 422);
} }
$return = $this->validateDataApplications($request, $server); $return = $this->validateDataApplications($request, $server);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
if (! isBase64Encoded($request->docker_compose_raw)) { if (! isBase64Encoded($request->docker_compose_raw)) {
@@ -1252,19 +1290,23 @@ class ApplicationsController extends Controller
} }
$dockerCompose = base64_decode($request->docker_compose_raw); $dockerCompose = base64_decode($request->docker_compose_raw);
$dockerComposeRaw = Yaml::dump(Yaml::parse($dockerCompose), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK); $dockerComposeRaw = Yaml::dump(Yaml::parse($dockerCompose), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
// $isValid = validateComposeFile($dockerComposeRaw, $server_id); // $isValid = validateComposeFile($dockerComposeRaw, $server_id);
// if ($isValid !== 'OK') { // if ($isValid !== 'OK') {
// return $this->dispatch('error', "Invalid docker-compose file.\n$isValid"); // return $this->dispatch('error', "Invalid docker-compose file.\n$isValid");
// } // }
$service = new Service; $service = new Service;
removeUnnecessaryFieldsFromRequest($request); removeUnnecessaryFieldsFromRequest($request);
$service->fill($request->all()); $service->fill($request->all());
$service->docker_compose_raw = $dockerComposeRaw; $service->docker_compose_raw = $dockerComposeRaw;
$service->environment_id = $environment->id; $service->environment_id = $environment->id;
$service->server_id = $server->id; $service->server_id = $server->id;
$service->destination_id = $destination->id; $service->destination_id = $destination->id;
$service->destination_type = $destination->getMorphClass(); $service->destination_type = $destination->getMorphClass();
$service->save(); $service->save();
$service->name = "service-$service->uuid"; $service->name = "service-$service->uuid";
$service->parse(isNew: true); $service->parse(isNew: true);
if ($instantDeploy) { if ($instantDeploy) {
@@ -1404,7 +1446,7 @@ class ApplicationsController extends Controller
public function delete_by_uuid(Request $request) public function delete_by_uuid(Request $request)
{ {
$teamId = getTeamIdFromToken(); $teamId = getTeamIdFromToken();
filter_var($request->query->get('cleanup', true), FILTER_VALIDATE_BOOLEAN); $cleanup = filter_var($request->query->get('cleanup', true), FILTER_VALIDATE_BOOLEAN);
if (is_null($teamId)) { if (is_null($teamId)) {
return invalidTokenResponse(); return invalidTokenResponse();
} }
@@ -1559,7 +1601,7 @@ class ApplicationsController extends Controller
], 400); ], 400);
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
@@ -1621,14 +1663,16 @@ class ApplicationsController extends Controller
} }
} }
$return = $this->validateDataApplications($request, $server); $return = $this->validateDataApplications($request, $server);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1651,7 +1695,7 @@ class ApplicationsController extends Controller
return $domain; return $domain;
}); });
if ($errors !== []) { if (count($errors) > 0) {
return response()->json([ return response()->json([
'message' => 'Validation failed.', 'message' => 'Validation failed.',
'errors' => $errors, 'errors' => $errors,
@@ -1710,11 +1754,11 @@ class ApplicationsController extends Controller
$application->save(); $application->save();
if ($instantDeploy) { if ($instantDeploy) {
$cuid2 = new Cuid2; $deployment_uuid = new Cuid2;
queue_application_deployment( queue_application_deployment(
application: $application, application: $application,
deployment_uuid: $cuid2, deployment_uuid: $deployment_uuid,
is_api: true, is_api: true,
); );
} }
@@ -1890,7 +1934,7 @@ class ApplicationsController extends Controller
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
@@ -1911,10 +1955,12 @@ class ApplicationsController extends Controller
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1948,12 +1994,12 @@ class ApplicationsController extends Controller
$env->save(); $env->save();
return response()->json($this->removeSensitiveData($env))->setStatusCode(201); return response()->json($this->removeSensitiveData($env))->setStatusCode(201);
} } else {
return response()->json([ return response()->json([
'message' => 'Environment variable not found.', 'message' => 'Environment variable not found.',
], 404); ], 404);
} }
} else {
$env = $application->environment_variables->where('key', $key)->first(); $env = $application->environment_variables->where('key', $key)->first();
if ($env) { if ($env) {
$env->value = $request->value; $env->value = $request->value;
@@ -1975,12 +2021,17 @@ class ApplicationsController extends Controller
$env->save(); $env->save();
return response()->json($this->removeSensitiveData($env))->setStatusCode(201); return response()->json($this->removeSensitiveData($env))->setStatusCode(201);
} } else {
return response()->json([ return response()->json([
'message' => 'Environment variable not found.', 'message' => 'Environment variable not found.',
], 404); ], 404);
} }
}
return response()->json([
'message' => 'Something is not okay. Are you okay?',
], 500);
}
#[OA\Patch( #[OA\Patch(
summary: 'Update Envs (Bulk)', summary: 'Update Envs (Bulk)',
@@ -2072,7 +2123,7 @@ class ApplicationsController extends Controller
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
@@ -2278,10 +2329,12 @@ class ApplicationsController extends Controller
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -2298,7 +2351,7 @@ class ApplicationsController extends Controller
return response()->json([ return response()->json([
'message' => 'Environment variable already exists. Use PATCH request to update it.', 'message' => 'Environment variable already exists. Use PATCH request to update it.',
], 409); ], 409);
} } else {
$env = $application->environment_variables()->create([ $env = $application->environment_variables()->create([
'key' => $request->key, 'key' => $request->key,
'value' => $request->value, 'value' => $request->value,
@@ -2315,12 +2368,13 @@ class ApplicationsController extends Controller
'uuid' => $env->uuid, 'uuid' => $env->uuid,
])->setStatusCode(201); ])->setStatusCode(201);
} }
} else {
$env = $application->environment_variables->where('key', $key)->first(); $env = $application->environment_variables->where('key', $key)->first();
if ($env) { if ($env) {
return response()->json([ return response()->json([
'message' => 'Environment variable already exists. Use PATCH request to update it.', 'message' => 'Environment variable already exists. Use PATCH request to update it.',
], 409); ], 409);
} } else {
$env = $application->environment_variables()->create([ $env = $application->environment_variables()->create([
'key' => $request->key, 'key' => $request->key,
'value' => $request->value, 'value' => $request->value,
@@ -2337,6 +2391,12 @@ class ApplicationsController extends Controller
'uuid' => $env->uuid, 'uuid' => $env->uuid,
])->setStatusCode(201); ])->setStatusCode(201);
} }
}
return response()->json([
'message' => 'Something went wrong.',
], 500);
}
#[OA\Delete( #[OA\Delete(
summary: 'Delete Env', summary: 'Delete Env',
@@ -2412,7 +2472,7 @@ class ApplicationsController extends Controller
'message' => 'Application not found.', 'message' => 'Application not found.',
], 404); ], 404);
} }
$found_env = EnvironmentVariable::query()->where('uuid', $request->env_uuid) $found_env = EnvironmentVariable::where('uuid', $request->env_uuid)
->where('resourceable_type', Application::class) ->where('resourceable_type', Application::class)
->where('resourceable_id', $application->id) ->where('resourceable_id', $application->id)
->first(); ->first();
@@ -2515,11 +2575,11 @@ class ApplicationsController extends Controller
return response()->json(['message' => 'Application not found.'], 404); return response()->json(['message' => 'Application not found.'], 404);
} }
$cuid2 = new Cuid2; $deployment_uuid = new Cuid2;
queue_application_deployment( queue_application_deployment(
application: $application, application: $application,
deployment_uuid: $cuid2, deployment_uuid: $deployment_uuid,
force_rebuild: $force, force_rebuild: $force,
is_api: true, is_api: true,
no_questions_asked: $instant_deploy no_questions_asked: $instant_deploy
@@ -2528,7 +2588,7 @@ class ApplicationsController extends Controller
return response()->json( return response()->json(
[ [
'message' => 'Deployment request queued.', 'message' => 'Deployment request queued.',
'deployment_uuid' => $cuid2->toString(), 'deployment_uuid' => $deployment_uuid->toString(),
], ],
200 200
); );
@@ -2676,11 +2736,11 @@ class ApplicationsController extends Controller
return response()->json(['message' => 'Application not found.'], 404); return response()->json(['message' => 'Application not found.'], 404);
} }
$cuid2 = new Cuid2; $deployment_uuid = new Cuid2;
queue_application_deployment( queue_application_deployment(
application: $application, application: $application,
deployment_uuid: $cuid2, deployment_uuid: $deployment_uuid,
restart_only: true, restart_only: true,
is_api: true, is_api: true,
); );
@@ -2688,7 +2748,7 @@ class ApplicationsController extends Controller
return response()->json( return response()->json(
[ [
'message' => 'Restart request queued.', 'message' => 'Restart request queued.',
'deployment_uuid' => $cuid2->toString(), 'deployment_uuid' => $deployment_uuid->toString(),
], ],
); );
} }
@@ -2775,7 +2835,7 @@ class ApplicationsController extends Controller
return response()->json(['message' => 'Application not found.'], 404); return response()->json(['message' => 'Application not found.'], 404);
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
@@ -2783,10 +2843,12 @@ class ApplicationsController extends Controller
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -2869,7 +2931,7 @@ class ApplicationsController extends Controller
return str($domain)->trim()->lower(); return str($domain)->trim()->lower();
}); });
if ($errors !== []) { if (count($errors) > 0) {
return response()->json([ return response()->json([
'message' => 'Validation failed.', 'message' => 'Validation failed.',
'errors' => $errors, 'errors' => $errors,
@@ -2884,7 +2946,5 @@ class ApplicationsController extends Controller
], 422); ], 422);
} }
} }
return null;
} }
} }

View File

@@ -12,7 +12,6 @@ use App\Http\Controllers\Controller;
use App\Jobs\DeleteResourceJob; use App\Jobs\DeleteResourceJob;
use App\Models\Project; use App\Models\Project;
use App\Models\Server; use App\Models\Server;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
@@ -74,7 +73,7 @@ class DatabasesController extends Controller
if (is_null($teamId)) { if (is_null($teamId)) {
return invalidTokenResponse(); return invalidTokenResponse();
} }
$projects = Project::query()->where('team_id', $teamId)->get(); $projects = Project::where('team_id', $teamId)->get();
$databases = collect(); $databases = collect();
foreach ($projects as $project) { foreach ($projects as $project) {
$databases = $databases->merge($project->databases()); $databases = $databases->merge($project->databases());
@@ -247,7 +246,7 @@ class DatabasesController extends Controller
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
@@ -277,9 +276,11 @@ class DatabasesController extends Controller
if (! $database) { if (! $database) {
return response()->json(['message' => 'Database not found.'], 404); return response()->json(['message' => 'Database not found.'], 404);
} }
if ($request->is_public && $request->public_port && isPublicPortAlreadyUsed($database->destination->server, $request->public_port, $database->id)) { if ($request->is_public && $request->public_port) {
if (isPublicPortAlreadyUsed($database->destination->server, $request->public_port, $database->id)) {
return response()->json(['message' => 'Public port already used by another database.'], 400); return response()->json(['message' => 'Public port already used by another database.'], 400);
} }
}
switch ($database->type()) { switch ($database->type()) {
case 'standalone-postgresql': case 'standalone-postgresql':
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf']; $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf'];
@@ -471,10 +472,12 @@ class DatabasesController extends Controller
break; break;
} }
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1016,7 +1019,7 @@ class DatabasesController extends Controller
return $this->create_database($request, NewDatabaseTypes::MONGODB); return $this->create_database($request, NewDatabaseTypes::MONGODB);
} }
public function create_database(Request $request, NewDatabaseTypes $newDatabaseTypes) public function create_database(Request $request, NewDatabaseTypes $type)
{ {
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf'];
@@ -1026,15 +1029,17 @@ class DatabasesController extends Controller
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($extraFields !== []) { if (! empty($extraFields)) {
$errors = collect([]); $errors = collect([]);
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1075,9 +1080,11 @@ class DatabasesController extends Controller
return response()->json(['message' => 'Server has multiple destinations and you do not set destination_uuid.'], 400); return response()->json(['message' => 'Server has multiple destinations and you do not set destination_uuid.'], 400);
} }
$destination = $destinations->first(); $destination = $destinations->first();
if ($request->has('public_port') && $request->is_public && isPublicPortAlreadyUsed($server, $request->public_port)) { if ($request->has('public_port') && $request->is_public) {
if (isPublicPortAlreadyUsed($server, $request->public_port)) {
return response()->json(['message' => 'Public port already used by another database.'], 400); return response()->json(['message' => 'Public port already used by another database.'], 400);
} }
}
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
'name' => 'string|max:255', 'name' => 'string|max:255',
'description' => 'string|nullable', 'description' => 'string|nullable',
@@ -1104,7 +1111,8 @@ class DatabasesController extends Controller
'errors' => $validator->errors(), 'errors' => $validator->errors(),
], 422); ], 422);
} }
if ($request->public_port && ($request->public_port < 1024 || $request->public_port > 65535)) { if ($request->public_port) {
if ($request->public_port < 1024 || $request->public_port > 65535) {
return response()->json([ return response()->json([
'message' => 'Validation failed.', 'message' => 'Validation failed.',
'errors' => [ 'errors' => [
@@ -1112,7 +1120,8 @@ class DatabasesController extends Controller
], ],
], 422); ], 422);
} }
if ($newDatabaseTypes === NewDatabaseTypes::POSTGRESQL) { }
if ($type === NewDatabaseTypes::POSTGRESQL) {
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf']; $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf'];
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
'postgres_user' => 'string', 'postgres_user' => 'string',
@@ -1123,10 +1132,12 @@ class DatabasesController extends Controller
'postgres_conf' => 'string', 'postgres_conf' => 'string',
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1169,18 +1180,19 @@ class DatabasesController extends Controller
} }
return response()->json(serializeApiResponse($payload))->setStatusCode(201); return response()->json(serializeApiResponse($payload))->setStatusCode(201);
} } elseif ($type === NewDatabaseTypes::MARIADB) {
if ($newDatabaseTypes === NewDatabaseTypes::MARIADB) {
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database']; $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database'];
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
'clickhouse_admin_user' => 'string', 'clickhouse_admin_user' => 'string',
'clickhouse_admin_password' => 'string', 'clickhouse_admin_password' => 'string',
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1213,6 +1225,7 @@ class DatabasesController extends Controller
if ($instantDeploy) { if ($instantDeploy) {
StartDatabase::dispatch($database); StartDatabase::dispatch($database);
} }
$database->refresh(); $database->refresh();
$payload = [ $payload = [
'uuid' => $database->uuid, 'uuid' => $database->uuid,
@@ -1223,8 +1236,7 @@ class DatabasesController extends Controller
} }
return response()->json(serializeApiResponse($payload))->setStatusCode(201); return response()->json(serializeApiResponse($payload))->setStatusCode(201);
} } elseif ($type === NewDatabaseTypes::MYSQL) {
if ($newDatabaseTypes === NewDatabaseTypes::MYSQL) {
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf'];
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
'mysql_root_password' => 'string', 'mysql_root_password' => 'string',
@@ -1234,10 +1246,12 @@ class DatabasesController extends Controller
'mysql_conf' => 'string', 'mysql_conf' => 'string',
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1270,6 +1284,7 @@ class DatabasesController extends Controller
if ($instantDeploy) { if ($instantDeploy) {
StartDatabase::dispatch($database); StartDatabase::dispatch($database);
} }
$database->refresh(); $database->refresh();
$payload = [ $payload = [
'uuid' => $database->uuid, 'uuid' => $database->uuid,
@@ -1280,18 +1295,19 @@ class DatabasesController extends Controller
} }
return response()->json(serializeApiResponse($payload))->setStatusCode(201); return response()->json(serializeApiResponse($payload))->setStatusCode(201);
} } elseif ($type === NewDatabaseTypes::REDIS) {
if ($newDatabaseTypes === NewDatabaseTypes::REDIS) {
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'redis_password', 'redis_conf']; $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'redis_password', 'redis_conf'];
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
'redis_password' => 'string', 'redis_password' => 'string',
'redis_conf' => 'string', 'redis_conf' => 'string',
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1324,6 +1340,7 @@ class DatabasesController extends Controller
if ($instantDeploy) { if ($instantDeploy) {
StartDatabase::dispatch($database); StartDatabase::dispatch($database);
} }
$database->refresh(); $database->refresh();
$payload = [ $payload = [
'uuid' => $database->uuid, 'uuid' => $database->uuid,
@@ -1334,17 +1351,19 @@ class DatabasesController extends Controller
} }
return response()->json(serializeApiResponse($payload))->setStatusCode(201); return response()->json(serializeApiResponse($payload))->setStatusCode(201);
} } elseif ($type === NewDatabaseTypes::DRAGONFLY) {
if ($newDatabaseTypes === NewDatabaseTypes::DRAGONFLY) {
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'dragonfly_password']; $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'dragonfly_password'];
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
'dragonfly_password' => 'string', 'dragonfly_password' => 'string',
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1352,6 +1371,7 @@ class DatabasesController extends Controller
'errors' => $errors, 'errors' => $errors,
], 422); ], 422);
} }
removeUnnecessaryFieldsFromRequest($request); removeUnnecessaryFieldsFromRequest($request);
$database = create_standalone_dragonfly($environment->id, $destination->uuid, $request->all()); $database = create_standalone_dragonfly($environment->id, $destination->uuid, $request->all());
if ($instantDeploy) { if ($instantDeploy) {
@@ -1361,18 +1381,19 @@ class DatabasesController extends Controller
return response()->json(serializeApiResponse([ return response()->json(serializeApiResponse([
'uuid' => $database->uuid, 'uuid' => $database->uuid,
]))->setStatusCode(201); ]))->setStatusCode(201);
} } elseif ($type === NewDatabaseTypes::KEYDB) {
if ($newDatabaseTypes === NewDatabaseTypes::KEYDB) {
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'keydb_password', 'keydb_conf']; $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'keydb_password', 'keydb_conf'];
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
'keydb_password' => 'string', 'keydb_password' => 'string',
'keydb_conf' => 'string', 'keydb_conf' => 'string',
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1405,6 +1426,7 @@ class DatabasesController extends Controller
if ($instantDeploy) { if ($instantDeploy) {
StartDatabase::dispatch($database); StartDatabase::dispatch($database);
} }
$database->refresh(); $database->refresh();
$payload = [ $payload = [
'uuid' => $database->uuid, 'uuid' => $database->uuid,
@@ -1415,18 +1437,19 @@ class DatabasesController extends Controller
} }
return response()->json(serializeApiResponse($payload))->setStatusCode(201); return response()->json(serializeApiResponse($payload))->setStatusCode(201);
} } elseif ($type === NewDatabaseTypes::CLICKHOUSE) {
if ($newDatabaseTypes === NewDatabaseTypes::CLICKHOUSE) {
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'clickhouse_admin_user', 'clickhouse_admin_password']; $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'clickhouse_admin_user', 'clickhouse_admin_password'];
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
'clickhouse_admin_user' => 'string', 'clickhouse_admin_user' => 'string',
'clickhouse_admin_password' => 'string', 'clickhouse_admin_password' => 'string',
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1439,6 +1462,7 @@ class DatabasesController extends Controller
if ($instantDeploy) { if ($instantDeploy) {
StartDatabase::dispatch($database); StartDatabase::dispatch($database);
} }
$database->refresh(); $database->refresh();
$payload = [ $payload = [
'uuid' => $database->uuid, 'uuid' => $database->uuid,
@@ -1449,7 +1473,7 @@ class DatabasesController extends Controller
} }
return response()->json(serializeApiResponse($payload))->setStatusCode(201); return response()->json(serializeApiResponse($payload))->setStatusCode(201);
} } elseif ($type === NewDatabaseTypes::MONGODB) {
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database']; $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database'];
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
'mongo_conf' => 'string', 'mongo_conf' => 'string',
@@ -1458,10 +1482,12 @@ class DatabasesController extends Controller
'mongo_initdb_database' => 'string', 'mongo_initdb_database' => 'string',
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -1494,6 +1520,7 @@ class DatabasesController extends Controller
if ($instantDeploy) { if ($instantDeploy) {
StartDatabase::dispatch($database); StartDatabase::dispatch($database);
} }
$database->refresh(); $database->refresh();
$payload = [ $payload = [
'uuid' => $database->uuid, 'uuid' => $database->uuid,
@@ -1506,6 +1533,9 @@ class DatabasesController extends Controller
return response()->json(serializeApiResponse($payload))->setStatusCode(201); return response()->json(serializeApiResponse($payload))->setStatusCode(201);
} }
return response()->json(['message' => 'Invalid database type requested.'], 400);
}
#[OA\Delete( #[OA\Delete(
summary: 'Delete', summary: 'Delete',
description: 'Delete database by UUID.', description: 'Delete database by UUID.',
@@ -1564,7 +1594,7 @@ class DatabasesController extends Controller
public function delete_by_uuid(Request $request) public function delete_by_uuid(Request $request)
{ {
$teamId = getTeamIdFromToken(); $teamId = getTeamIdFromToken();
filter_var($request->query->get('cleanup', true), FILTER_VALIDATE_BOOLEAN); $cleanup = filter_var($request->query->get('cleanup', true), FILTER_VALIDATE_BOOLEAN);
if (is_null($teamId)) { if (is_null($teamId)) {
return invalidTokenResponse(); return invalidTokenResponse();
} }

View File

@@ -5,10 +5,8 @@ namespace App\Http\Controllers\Api;
use App\Actions\Database\StartDatabase; use App\Actions\Database\StartDatabase;
use App\Actions\Service\StartService; use App\Actions\Service\StartService;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Application;
use App\Models\ApplicationDeploymentQueue; use App\Models\ApplicationDeploymentQueue;
use App\Models\Server; use App\Models\Server;
use App\Models\Service;
use App\Models\Tag; use App\Models\Tag;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
@@ -67,7 +65,7 @@ class DeployController extends Controller
return invalidTokenResponse(); return invalidTokenResponse();
} }
$servers = Server::whereTeamId($teamId)->get(); $servers = Server::whereTeamId($teamId)->get();
$deployments_per_server = ApplicationDeploymentQueue::query()->whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $servers->pluck('id'))->get()->sortBy('id'); $deployments_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $servers->pluck('id'))->get()->sortBy('id');
$deployments_per_server = $deployments_per_server->map(function ($deployment) { $deployments_per_server = $deployments_per_server->map(function ($deployment) {
return $this->removeSensitiveData($deployment); return $this->removeSensitiveData($deployment);
}); });
@@ -123,7 +121,7 @@ class DeployController extends Controller
if (! $uuid) { if (! $uuid) {
return response()->json(['message' => 'UUID is required.'], 400); return response()->json(['message' => 'UUID is required.'], 400);
} }
$deployment = ApplicationDeploymentQueue::query()->where('deployment_uuid', $uuid)->first(); $deployment = ApplicationDeploymentQueue::where('deployment_uuid', $uuid)->first();
if (! $deployment) { if (! $deployment) {
return response()->json(['message' => 'Deployment not found.'], 404); return response()->json(['message' => 'Deployment not found.'], 404);
} }
@@ -198,8 +196,7 @@ class DeployController extends Controller
} }
if ($tags) { if ($tags) {
return $this->by_tags($tags, $teamId, $force); return $this->by_tags($tags, $teamId, $force);
} } elseif ($uuids) {
if ($uuids) {
return $this->by_uuids($uuids, $teamId, $force); return $this->by_uuids($uuids, $teamId, $force);
} }
@@ -248,7 +245,7 @@ class DeployController extends Controller
$deployments = collect(); $deployments = collect();
$payload = collect(); $payload = collect();
foreach ($tags as $tag) { foreach ($tags as $tag) {
$found_tag = Tag::query()->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;
@@ -260,15 +257,15 @@ class DeployController extends Controller
continue; continue;
} }
foreach ($applications as $application) { foreach ($applications as $resource) {
['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($application, $force); ['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
if ($deployment_uuid) { if ($deployment_uuid) {
$deployments->push(['resource_uuid' => $application->uuid, 'deployment_uuid' => $deployment_uuid->toString()]); $deployments->push(['resource_uuid' => $resource->uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
} }
$message = $message->merge($return_message); $message = $message->merge($return_message);
} }
foreach ($services as $service) { foreach ($services as $resource) {
['message' => $return_message] = $this->deploy_resource($service, $force); ['message' => $return_message] = $this->deploy_resource($resource, $force);
$message = $message->merge($return_message); $message = $message->merge($return_message);
} }
} }
@@ -292,7 +289,7 @@ class DeployController extends Controller
return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid]; return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid];
} }
switch ($resource?->getMorphClass()) { switch ($resource?->getMorphClass()) {
case Application::class: case \App\Models\Application::class:
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
queue_application_deployment( queue_application_deployment(
application: $resource, application: $resource,
@@ -301,7 +298,7 @@ class DeployController extends Controller
); );
$message = "Application {$resource->name} deployment queued."; $message = "Application {$resource->name} deployment queued.";
break; break;
case Service::class: case \App\Models\Service::class:
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.";
break; break;

View File

@@ -4,7 +4,6 @@ namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Project; use App\Models\Project;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
@@ -225,7 +224,7 @@ class ProjectController extends Controller
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
@@ -234,10 +233,12 @@ class ProjectController extends Controller
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -246,7 +247,7 @@ class ProjectController extends Controller
], 422); ], 422);
} }
$project = Project::query()->create([ $project = Project::create([
'name' => $request->name, 'name' => $request->name,
'description' => $request->description, 'description' => $request->description,
'team_id' => $teamId, 'team_id' => $teamId,
@@ -321,7 +322,7 @@ class ProjectController extends Controller
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
@@ -330,10 +331,12 @@ class ProjectController extends Controller
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([

View File

@@ -43,7 +43,7 @@ class ResourcesController extends Controller
if (is_null($teamId)) { if (is_null($teamId)) {
return invalidTokenResponse(); return invalidTokenResponse();
} }
$projects = Project::query()->where('team_id', $teamId)->get(); $projects = Project::where('team_id', $teamId)->get();
$resources = collect(); $resources = collect();
$resources->push($projects->pluck('applications')->flatten()); $resources->push($projects->pluck('applications')->flatten());
$resources->push($projects->pluck('services')->flatten()); $resources->push($projects->pluck('services')->flatten());

View File

@@ -4,7 +4,6 @@ namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\PrivateKey; use App\Models\PrivateKey;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
@@ -59,7 +58,7 @@ class SecurityController extends Controller
if (is_null($teamId)) { if (is_null($teamId)) {
return invalidTokenResponse(); return invalidTokenResponse();
} }
$keys = PrivateKey::query()->where('team_id', $teamId)->get(); $keys = PrivateKey::where('team_id', $teamId)->get();
return response()->json($this->removeSensitiveData($keys)); return response()->json($this->removeSensitiveData($keys));
} }
@@ -103,7 +102,7 @@ class SecurityController extends Controller
return invalidTokenResponse(); return invalidTokenResponse();
} }
$key = PrivateKey::query()->where('team_id', $teamId)->where('uuid', $request->uuid)->first(); $key = PrivateKey::where('team_id', $teamId)->where('uuid', $request->uuid)->first();
if (is_null($key)) { if (is_null($key)) {
return response()->json([ return response()->json([
@@ -173,7 +172,7 @@ class SecurityController extends Controller
return invalidTokenResponse(); return invalidTokenResponse();
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
@@ -196,7 +195,7 @@ class SecurityController extends Controller
if (! $request->description) { if (! $request->description) {
$request->offsetSet('description', 'Created by Coolify via API'); $request->offsetSet('description', 'Created by Coolify via API');
} }
$privateKey = PrivateKey::query()->create([ $key = PrivateKey::create([
'team_id' => $teamId, 'team_id' => $teamId,
'name' => $request->name, 'name' => $request->name,
'description' => $request->description, 'description' => $request->description,
@@ -204,7 +203,7 @@ class SecurityController extends Controller
]); ]);
return response()->json(serializeApiResponse([ return response()->json(serializeApiResponse([
'uuid' => $privateKey->uuid, 'uuid' => $key->uuid,
]))->setStatusCode(201); ]))->setStatusCode(201);
} }
@@ -268,7 +267,7 @@ class SecurityController extends Controller
return invalidTokenResponse(); return invalidTokenResponse();
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
@@ -279,10 +278,12 @@ class SecurityController extends Controller
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -290,7 +291,7 @@ class SecurityController extends Controller
'errors' => $errors, 'errors' => $errors,
], 422); ], 422);
} }
$foundKey = PrivateKey::query()->where('team_id', $teamId)->where('uuid', $request->uuid)->first(); $foundKey = PrivateKey::where('team_id', $teamId)->where('uuid', $request->uuid)->first();
if (is_null($foundKey)) { if (is_null($foundKey)) {
return response()->json([ return response()->json([
'message' => 'Private Key not found.', 'message' => 'Private Key not found.',
@@ -354,7 +355,7 @@ class SecurityController extends Controller
return response()->json(['message' => 'UUID is required.'], 422); return response()->json(['message' => 'UUID is required.'], 422);
} }
$key = PrivateKey::query()->where('team_id', $teamId)->where('uuid', $request->uuid)->first(); $key = PrivateKey::where('team_id', $teamId)->where('uuid', $request->uuid)->first();
if (is_null($key)) { if (is_null($key)) {
return response()->json(['message' => 'Private Key not found.'], 404); return response()->json(['message' => 'Private Key not found.'], 404);
} }

View File

@@ -11,7 +11,6 @@ use App\Models\Application;
use App\Models\PrivateKey; use App\Models\PrivateKey;
use App\Models\Project; use App\Models\Project;
use App\Models\Server as ModelsServer; use App\Models\Server as ModelsServer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
use Stringable; use Stringable;
@@ -295,7 +294,7 @@ class ServersController extends Controller
return response()->json(serializeApiResponse($domains)); return response()->json(serializeApiResponse($domains));
} }
$projects = Project::query()->where('team_id', $teamId)->get(); $projects = Project::where('team_id', $teamId)->get();
$domains = collect(); $domains = collect();
$applications = $projects->pluck('applications')->flatten(); $applications = $projects->pluck('applications')->flatten();
$settings = instanceSettings(); $settings = instanceSettings();
@@ -306,8 +305,8 @@ class ServersController extends Controller
$f = str($fqdn)->replace('http://', '')->replace('https://', '')->explode('/'); $f = str($fqdn)->replace('http://', '')->replace('https://', '')->explode('/');
return str(str($f[0])->explode(':')[0]); return str(str($f[0])->explode(':')[0]);
})->filter(function (Stringable $stringable) { })->filter(function (Stringable $fqdn) {
return $stringable->isNotEmpty(); return $fqdn->isNotEmpty();
}); });
if ($ip === 'host.docker.internal') { if ($ip === 'host.docker.internal') {
@@ -342,13 +341,13 @@ class ServersController extends Controller
foreach ($services as $service) { foreach ($services as $service) {
$service_applications = $service->applications; $service_applications = $service->applications;
if ($service_applications->count() > 0) { if ($service_applications->count() > 0) {
foreach ($service_applications as $service_application) { foreach ($service_applications as $application) {
$fqdn = str($service_application->fqdn)->explode(',')->map(function ($fqdn) { $fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
$f = str($fqdn)->replace('http://', '')->replace('https://', '')->explode('/'); $f = str($fqdn)->replace('http://', '')->replace('https://', '')->explode('/');
return str(str($f[0])->explode(':')[0]); return str(str($f[0])->explode(':')[0]);
})->filter(function (Stringable $stringable) { })->filter(function (Stringable $fqdn) {
return $stringable->isNotEmpty(); return $fqdn->isNotEmpty();
}); });
if ($ip === 'host.docker.internal') { if ($ip === 'host.docker.internal') {
if ($settings->public_ipv4) { if ($settings->public_ipv4) {
@@ -460,7 +459,7 @@ class ServersController extends Controller
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
@@ -476,10 +475,12 @@ class ServersController extends Controller
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -615,7 +616,7 @@ class ServersController extends Controller
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
@@ -631,10 +632,12 @@ class ServersController extends Controller
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([

View File

@@ -11,7 +11,6 @@ use App\Models\EnvironmentVariable;
use App\Models\Project; use App\Models\Project;
use App\Models\Server; use App\Models\Server;
use App\Models\Service; use App\Models\Service;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use OpenApi\Attributes as OA; use OpenApi\Attributes as OA;
@@ -76,7 +75,7 @@ class ServicesController extends Controller
if (is_null($teamId)) { if (is_null($teamId)) {
return invalidTokenResponse(); return invalidTokenResponse();
} }
$projects = Project::query()->where('team_id', $teamId)->get(); $projects = Project::where('team_id', $teamId)->get();
$services = collect(); $services = collect();
foreach ($projects as $project) { foreach ($projects as $project) {
$services->push($project->services()->get()); $services->push($project->services()->get());
@@ -246,7 +245,7 @@ class ServicesController extends Controller
} }
$return = validateIncomingRequest($request); $return = validateIncomingRequest($request);
if ($return instanceof JsonResponse) { if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return; return $return;
} }
$validator = customApiValidator($request->all(), [ $validator = customApiValidator($request->all(), [
@@ -262,10 +261,12 @@ class ServicesController extends Controller
]); ]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields); $extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || $extraFields !== []) { if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors(); $errors = $validator->errors();
foreach ($extraFields as $extraField) { if (! empty($extraFields)) {
$errors->add($extraField, 'This field is not allowed.'); foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
} }
return response()->json([ return response()->json([
@@ -314,7 +315,7 @@ class ServicesController extends Controller
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null); $oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
if ($oneClickDotEnvs) { if ($oneClickDotEnvs) {
$oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) { $oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
return $value !== '' && $value !== '0'; return ! empty($value);
}); });
} }
if ($oneClickService) { if ($oneClickService) {
@@ -330,7 +331,7 @@ class ServicesController extends Controller
if ($oneClickServiceName === 'cloudflared') { if ($oneClickServiceName === 'cloudflared') {
data_set($service_payload, 'connect_to_docker_network', true); data_set($service_payload, 'connect_to_docker_network', true);
} }
$service = Service::query()->create($service_payload); $service = Service::create($service_payload);
$service->name = "$oneClickServiceName-".$service->uuid; $service->name = "$oneClickServiceName-".$service->uuid;
$service->save(); $service->save();
if ($oneClickDotEnvs?->count() > 0) { if ($oneClickDotEnvs?->count() > 0) {
@@ -342,7 +343,7 @@ class ServicesController extends Controller
$command = $value->after('SERVICE_')->beforeLast('_'); $command = $value->after('SERVICE_')->beforeLast('_');
$generatedValue = generateEnvValue($command->value(), $service); $generatedValue = generateEnvValue($command->value(), $service);
} }
EnvironmentVariable::query()->create([ EnvironmentVariable::create([
'key' => $key, 'key' => $key,
'value' => $generatedValue, 'value' => $generatedValue,
'resourceable_id' => $service->id, 'resourceable_id' => $service->id,
@@ -372,9 +373,11 @@ class ServicesController extends Controller
} }
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} else {
return response()->json(['message' => 'Invalid service type.', 'valid_service_types' => $serviceKeys], 400);
} }
return response()->json(['message' => 'Invalid service type.', 'valid_service_types' => $serviceKeys], 400); return response()->json(['message' => 'Invalid service type.'], 400);
} }
#[OA\Get( #[OA\Get(
@@ -425,7 +428,7 @@ class ServicesController extends Controller
if (! $request->uuid) { if (! $request->uuid) {
return response()->json(['message' => 'UUID is required.'], 404); return response()->json(['message' => 'UUID is required.'], 404);
} }
$service = Service::query()->whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }
@@ -490,7 +493,7 @@ class ServicesController extends Controller
if (! $request->uuid) { if (! $request->uuid) {
return response()->json(['message' => 'UUID is required.'], 404); return response()->json(['message' => 'UUID is required.'], 404);
} }
$service = Service::query()->whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }
@@ -563,7 +566,7 @@ class ServicesController extends Controller
if (is_null($teamId)) { if (is_null($teamId)) {
return invalidTokenResponse(); return invalidTokenResponse();
} }
$service = Service::query()->whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }
@@ -667,7 +670,7 @@ class ServicesController extends Controller
return invalidTokenResponse(); return invalidTokenResponse();
} }
$service = Service::query()->whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }
@@ -788,7 +791,7 @@ class ServicesController extends Controller
return invalidTokenResponse(); return invalidTokenResponse();
} }
$service = Service::query()->whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }
@@ -904,7 +907,7 @@ class ServicesController extends Controller
return invalidTokenResponse(); return invalidTokenResponse();
} }
$service = Service::query()->whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }
@@ -1006,12 +1009,12 @@ class ServicesController extends Controller
return invalidTokenResponse(); return invalidTokenResponse();
} }
$service = Service::query()->whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }
$env = EnvironmentVariable::query()->where('uuid', $request->env_uuid) $env = EnvironmentVariable::where('uuid', $request->env_uuid)
->where('resourceable_type', Service::class) ->where('resourceable_type', Service::class)
->where('resourceable_id', $service->id) ->where('resourceable_id', $service->id)
->first(); ->first();
@@ -1086,7 +1089,7 @@ class ServicesController extends Controller
if (! $uuid) { if (! $uuid) {
return response()->json(['message' => 'UUID is required.'], 400); return response()->json(['message' => 'UUID is required.'], 400);
} }
$service = Service::query()->whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }
@@ -1164,7 +1167,7 @@ class ServicesController extends Controller
if (! $uuid) { if (! $uuid) {
return response()->json(['message' => 'UUID is required.'], 400); return response()->json(['message' => 'UUID is required.'], 400);
} }
$service = Service::query()->whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }
@@ -1242,7 +1245,7 @@ class ServicesController extends Controller
if (! $uuid) { if (! $uuid) {
return response()->json(['message' => 'UUID is required.'], 400); return response()->json(['message' => 'UUID is required.'], 400);
} }
$service = Service::query()->whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
if (! $service) { if (! $service) {
return response()->json(['message' => 'Service not found.'], 404); return response()->json(['message' => 'Service not found.'], 404);
} }

View File

@@ -39,9 +39,9 @@ class Controller extends BaseController
return view('auth.verify-email'); return view('auth.verify-email');
} }
public function email_verify(EmailVerificationRequest $emailVerificationRequest) public function email_verify(EmailVerificationRequest $request)
{ {
$emailVerificationRequest->fulfill(); $request->fulfill();
return redirect(RouteServiceProvider::HOME); return redirect(RouteServiceProvider::HOME);
} }
@@ -139,10 +139,9 @@ class Controller extends BaseController
refreshSession($invitation->team); refreshSession($invitation->team);
return redirect()->route('team.index'); return redirect()->route('team.index');
} } else {
abort(400, 'Invitation expired.'); abort(400, 'Invitation expired.');
}
return null;
} }
public function revoke_invitation() public function revoke_invitation()

View File

@@ -46,7 +46,10 @@ class MagicController extends Controller
public function newProject() public function newProject()
{ {
$project = Project::query()->firstOrCreate(['name' => request()->query('name') ?? generate_random_name()], ['team_id' => currentTeam()->id]); $project = Project::firstOrCreate(
['name' => request()->query('name') ?? generate_random_name()],
['team_id' => currentTeam()->id]
);
return response()->json([ return response()->json([
'project_uuid' => $project->uuid, 'project_uuid' => $project->uuid,
@@ -55,7 +58,10 @@ class MagicController extends Controller
public function newEnvironment() public function newEnvironment()
{ {
$environment = Environment::query()->firstOrCreate(['name' => request()->query('name') ?? generate_random_name()], ['project_id' => Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->firstOrFail()->id]); $environment = Environment::firstOrCreate(
['name' => request()->query('name') ?? generate_random_name()],
['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,
@@ -64,10 +70,12 @@ class MagicController extends Controller
public function newTeam() public function newTeam()
{ {
$team = Team::query()->create([ $team = Team::create(
[
'name' => request()->query('name') ?? generate_random_name(), 'name' => request()->query('name') ?? generate_random_name(),
'personal_team' => false, 'personal_team' => false,
]); ],
);
auth()->user()->teams()->attach($team, ['role' => 'admin']); auth()->user()->teams()->attach($team, ['role' => 'admin']);
refreshSession(); refreshSession();

View File

@@ -3,7 +3,6 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\User; use App\Models\User;
use Exception;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -27,7 +26,7 @@ class OauthController extends Controller
abort(403, 'Registration is disabled'); abort(403, 'Registration is disabled');
} }
$user = User::query()->create([ $user = User::create([
'name' => $oauthUser->name, 'name' => $oauthUser->name,
'email' => $oauthUser->email, 'email' => $oauthUser->email,
]); ]);
@@ -35,7 +34,7 @@ class OauthController extends Controller
Auth::login($user); Auth::login($user);
return redirect('/'); return redirect('/');
} catch (Exception $e) { } catch (\Exception $e) {
$errorCode = $e instanceof HttpException ? 'auth.failed' : 'auth.failed.callback'; $errorCode = $e instanceof HttpException ? 'auth.failed' : 'auth.failed.callback';
return redirect()->route('login')->withErrors([__($errorCode)]); return redirect()->route('login')->withErrors([__($errorCode)]);

View File

@@ -17,13 +17,13 @@ class UploadController extends BaseController
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);
} }
$fileReceiver = new FileReceiver('file', $request, HandlerFactory::classFromRequest($request)); $receiver = new FileReceiver('file', $request, HandlerFactory::classFromRequest($request));
if ($fileReceiver->isUploaded() === false) { if ($receiver->isUploaded() === false) {
throw new UploadMissingFileException; throw new UploadMissingFileException;
} }
$save = $fileReceiver->receive(); $save = $receiver->receive();
if ($save->isFinished()) { if ($save->isFinished()) {
return $this->saveFile($save->getFile(), $resource); return $this->saveFile($save->getFile(), $resource);
@@ -57,22 +57,22 @@ class UploadController extends BaseController
// 'mime_type' => $mime // 'mime_type' => $mime
// ]); // ]);
// } // }
protected function saveFile(UploadedFile $uploadedFile, $resource) protected function saveFile(UploadedFile $file, $resource)
{ {
$mime = str_replace('/', '-', $uploadedFile->getMimeType()); $mime = str_replace('/', '-', $file->getMimeType());
$filePath = "upload/{$resource->uuid}"; $filePath = "upload/{$resource->uuid}";
$finalPath = storage_path('app/'.$filePath); $finalPath = storage_path('app/'.$filePath);
$uploadedFile->move($finalPath, 'restore'); $file->move($finalPath, 'restore');
return response()->json([ return response()->json([
'mime_type' => $mime, 'mime_type' => $mime,
]); ]);
} }
protected function createFilename(UploadedFile $uploadedFile) protected function createFilename(UploadedFile $file)
{ {
$extension = $uploadedFile->getClientOriginalExtension(); $extension = $file->getClientOriginalExtension();
$filename = str_replace('.'.$extension, '', $uploadedFile->getClientOriginalName()); // Filename without extension $filename = str_replace('.'.$extension, '', $file->getClientOriginalName()); // Filename without extension
$filename .= '_'.md5(time()).'.'.$extension; $filename .= '_'.md5(time()).'.'.$extension;

View File

@@ -30,7 +30,7 @@ class Bitbucket extends Controller
$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 null; return;
} }
$return_payloads = collect([]); $return_payloads = collect([]);
$payload = $request->collect(); $payload = $request->collect();
@@ -63,7 +63,7 @@ class Bitbucket extends Controller
$pull_request_html_url = data_get($payload, 'pullrequest.links.html.href'); $pull_request_html_url = data_get($payload, 'pullrequest.links.html.href');
$commit = data_get($payload, 'pullrequest.source.commit.hash'); $commit = data_get($payload, 'pullrequest.source.commit.hash');
} }
$applications = Application::query()->where('git_repository', 'like', "%$full_name%"); $applications = Application::where('git_repository', 'like', "%$full_name%");
$applications = $applications->where('git_branch', $branch)->get(); $applications = $applications->where('git_branch', $branch)->get();
if ($applications->isEmpty()) { if ($applications->isEmpty()) {
return response([ return response([
@@ -122,10 +122,10 @@ class Bitbucket extends Controller
if ($x_bitbucket_event === 'pullrequest:created') { if ($x_bitbucket_event === 'pullrequest:created') {
if ($application->isPRDeployable()) { if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
$found = ApplicationPreview::query()->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) {
if ($application->build_pack === 'dockercompose') { if ($application->build_pack === 'dockercompose') {
$pr_app = ApplicationPreview::query()->create([ $pr_app = ApplicationPreview::create([
'git_type' => 'bitbucket', 'git_type' => 'bitbucket',
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => $pull_request_id, 'pull_request_id' => $pull_request_id,
@@ -134,7 +134,7 @@ class Bitbucket extends Controller
]); ]);
$pr_app->generate_preview_fqdn_compose(); $pr_app->generate_preview_fqdn_compose();
} else { } else {
ApplicationPreview::query()->create([ ApplicationPreview::create([
'git_type' => 'bitbucket', 'git_type' => 'bitbucket',
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => $pull_request_id, 'pull_request_id' => $pull_request_id,
@@ -165,7 +165,7 @@ class Bitbucket extends Controller
} }
} }
if ($x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') { if ($x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
$found = ApplicationPreview::query()->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();
$container_name = generateApplicationContainerName($application, $pull_request_id); $container_name = generateApplicationContainerName($application, $pull_request_id);

View File

@@ -25,7 +25,7 @@ class Gitea extends Controller
return Str::contains($file, $x_gitea_delivery); return Str::contains($file, $x_gitea_delivery);
})->first(); })->first();
if ($gitea_delivery_found) { if ($gitea_delivery_found) {
return null; return;
} }
$data = [ $data = [
'attributes' => $request->attributes->all(), 'attributes' => $request->attributes->all(),
@@ -40,7 +40,7 @@ class Gitea extends Controller
$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 null; return;
} }
$x_gitea_event = Str::lower($request->header('X-Gitea-Event')); $x_gitea_event = Str::lower($request->header('X-Gitea-Event'));
$x_hub_signature_256 = Str::after($request->header('X-Hub-Signature-256'), 'sha256='); $x_hub_signature_256 = Str::after($request->header('X-Hub-Signature-256'), 'sha256=');
@@ -76,7 +76,7 @@ class Gitea extends Controller
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::query()->where('git_repository', 'like', "%$full_name%"); $applications = Application::where('git_repository', 'like', "%$full_name%");
if ($x_gitea_event === 'push') { if ($x_gitea_event === 'push') {
$applications = $applications->where('git_branch', $branch)->get(); $applications = $applications->where('git_branch', $branch)->get();
if ($applications->isEmpty()) { if ($applications->isEmpty()) {
@@ -155,10 +155,10 @@ class Gitea extends Controller
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') { if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
if ($application->isPRDeployable()) { if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
$found = ApplicationPreview::query()->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) {
if ($application->build_pack === 'dockercompose') { if ($application->build_pack === 'dockercompose') {
$pr_app = ApplicationPreview::query()->create([ $pr_app = ApplicationPreview::create([
'git_type' => 'gitea', 'git_type' => 'gitea',
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => $pull_request_id, 'pull_request_id' => $pull_request_id,
@@ -167,7 +167,7 @@ class Gitea extends Controller
]); ]);
$pr_app->generate_preview_fqdn_compose(); $pr_app->generate_preview_fqdn_compose();
} else { } else {
ApplicationPreview::query()->create([ ApplicationPreview::create([
'git_type' => 'gitea', 'git_type' => 'gitea',
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => $pull_request_id, 'pull_request_id' => $pull_request_id,
@@ -198,7 +198,7 @@ class Gitea extends Controller
} }
} }
if ($action === 'closed') { if ($action === 'closed') {
$found = ApplicationPreview::query()->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();
$container_name = generateApplicationContainerName($application, $pull_request_id); $container_name = generateApplicationContainerName($application, $pull_request_id);

View File

@@ -31,7 +31,7 @@ class Github extends Controller
return Str::contains($file, $x_github_delivery); return Str::contains($file, $x_github_delivery);
})->first(); })->first();
if ($github_delivery_found) { if ($github_delivery_found) {
return null; return;
} }
$data = [ $data = [
'attributes' => $request->attributes->all(), 'attributes' => $request->attributes->all(),
@@ -46,7 +46,7 @@ class Github extends Controller
$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 null; return;
} }
$x_github_event = Str::lower($request->header('X-GitHub-Event')); $x_github_event = Str::lower($request->header('X-GitHub-Event'));
$x_hub_signature_256 = Str::after($request->header('X-Hub-Signature-256'), 'sha256='); $x_hub_signature_256 = Str::after($request->header('X-Hub-Signature-256'), 'sha256=');
@@ -82,7 +82,7 @@ class Github extends Controller
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::query()->where('git_repository', 'like', "%$full_name%"); $applications = Application::where('git_repository', 'like', "%$full_name%");
if ($x_github_event === 'push') { if ($x_github_event === 'push') {
$applications = $applications->where('git_branch', $branch)->get(); $applications = $applications->where('git_branch', $branch)->get();
if ($applications->isEmpty()) { if ($applications->isEmpty()) {
@@ -161,10 +161,10 @@ class Github extends Controller
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') { if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
if ($application->isPRDeployable()) { if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
$found = ApplicationPreview::query()->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) {
if ($application->build_pack === 'dockercompose') { if ($application->build_pack === 'dockercompose') {
$pr_app = ApplicationPreview::query()->create([ $pr_app = ApplicationPreview::create([
'git_type' => 'github', 'git_type' => 'github',
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => $pull_request_id, 'pull_request_id' => $pull_request_id,
@@ -173,7 +173,7 @@ class Github extends Controller
]); ]);
$pr_app->generate_preview_fqdn_compose(); $pr_app->generate_preview_fqdn_compose();
} else { } else {
ApplicationPreview::query()->create([ ApplicationPreview::create([
'git_type' => 'github', 'git_type' => 'github',
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => $pull_request_id, 'pull_request_id' => $pull_request_id,
@@ -204,7 +204,7 @@ class Github extends Controller
} }
} }
if ($action === 'closed') { if ($action === 'closed') {
$found = ApplicationPreview::query()->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();
$container_name = generateApplicationContainerName($application, $pull_request_id); $container_name = generateApplicationContainerName($application, $pull_request_id);
@@ -245,7 +245,7 @@ class Github extends Controller
return Str::contains($file, $x_github_delivery); return Str::contains($file, $x_github_delivery);
})->first(); })->first();
if ($github_delivery_found) { if ($github_delivery_found) {
return null; return;
} }
$data = [ $data = [
'attributes' => $request->attributes->all(), 'attributes' => $request->attributes->all(),
@@ -260,7 +260,7 @@ class Github extends Controller
$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 null; return;
} }
$x_github_event = Str::lower($request->header('X-GitHub-Event')); $x_github_event = Str::lower($request->header('X-GitHub-Event'));
$x_github_hook_installation_target_id = $request->header('X-GitHub-Hook-Installation-Target-Id'); $x_github_hook_installation_target_id = $request->header('X-GitHub-Hook-Installation-Target-Id');
@@ -270,15 +270,17 @@ class Github extends Controller
// Just pong // Just pong
return response('pong'); return response('pong');
} }
$github_app = GithubApp::query()->where('app_id', $x_github_hook_installation_target_id)->first(); $github_app = GithubApp::where('app_id', $x_github_hook_installation_target_id)->first();
if (is_null($github_app)) { if (is_null($github_app)) {
return response('Nothing to do. No GitHub App found.'); return response('Nothing to do. No GitHub App found.');
} }
$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' && ! hash_equals($x_hub_signature_256, $hmac)) { if (config('app.env') !== 'local') {
if (! hash_equals($x_hub_signature_256, $hmac)) {
return response('Invalid signature.'); return response('Invalid signature.');
} }
}
if ($x_github_event === 'installation' || $x_github_event === 'installation_repositories') { if ($x_github_event === 'installation' || $x_github_event === 'installation_repositories') {
// Installation handled by setup redirect url. Repositories queried on-demand. // Installation handled by setup redirect url. Repositories queried on-demand.
$action = data_get($payload, 'action'); $action = data_get($payload, 'action');
@@ -310,7 +312,7 @@ class Github extends Controller
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::query()->where('repository_project_id', $id)->whereRelation('source', 'is_public', false); $applications = Application::where('repository_project_id', $id)->whereRelation('source', 'is_public', false);
if ($x_github_event === 'push') { if ($x_github_event === 'push') {
$applications = $applications->where('git_branch', $branch)->get(); $applications = $applications->where('git_branch', $branch)->get();
if ($applications->isEmpty()) { if ($applications->isEmpty()) {
@@ -379,9 +381,9 @@ class Github extends Controller
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') { if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
if ($application->isPRDeployable()) { if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
$found = ApplicationPreview::query()->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::query()->create([ ApplicationPreview::create([
'git_type' => 'github', 'git_type' => 'github',
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => $pull_request_id, 'pull_request_id' => $pull_request_id,
@@ -411,7 +413,7 @@ class Github extends Controller
} }
} }
if ($action === 'closed' || $action === 'close') { if ($action === 'closed' || $action === 'close') {
$found = ApplicationPreview::query()->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) {
$containers = getCurrentApplicationContainerStatus($application->destination->server, $application->id, $pull_request_id); $containers = getCurrentApplicationContainerStatus($application->destination->server, $application->id, $pull_request_id);
if ($containers->isNotEmpty()) { if ($containers->isNotEmpty()) {
@@ -451,7 +453,7 @@ class Github extends Controller
try { try {
$code = $request->get('code'); $code = $request->get('code');
$state = $request->get('state'); $state = $request->get('state');
$github_app = GithubApp::query()->where('uuid', $state)->firstOrFail(); $github_app = GithubApp::where('uuid', $state)->firstOrFail();
$api_url = data_get($github_app, 'api_url'); $api_url = data_get($github_app, 'api_url');
$data = Http::withBody(null)->accept('application/vnd.github+json')->post("$api_url/app-manifests/$code/conversions")->throw()->json(); $data = Http::withBody(null)->accept('application/vnd.github+json')->post("$api_url/app-manifests/$code/conversions")->throw()->json();
$id = data_get($data, 'id'); $id = data_get($data, 'id');
@@ -460,7 +462,7 @@ class Github extends Controller
$client_secret = data_get($data, 'client_secret'); $client_secret = data_get($data, 'client_secret');
$private_key = data_get($data, 'pem'); $private_key = data_get($data, 'pem');
$webhook_secret = data_get($data, 'webhook_secret'); $webhook_secret = data_get($data, 'webhook_secret');
$private_key = PrivateKey::query()->create([ $private_key = PrivateKey::create([
'name' => "github-app-{$slug}", 'name' => "github-app-{$slug}",
'private_key' => $private_key, 'private_key' => $private_key,
'team_id' => $github_app->team_id, 'team_id' => $github_app->team_id,
@@ -499,11 +501,11 @@ class Github extends Controller
$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 null; return;
} }
$source = $request->get('source'); $source = $request->get('source');
$setup_action = $request->get('setup_action'); $setup_action = $request->get('setup_action');
$github_app = GithubApp::query()->where('uuid', $source)->firstOrFail(); $github_app = GithubApp::where('uuid', $source)->firstOrFail();
if ($setup_action === 'install') { if ($setup_action === 'install') {
$github_app->installation_id = $installation_id; $github_app->installation_id = $installation_id;
$github_app->save(); $github_app->save();

View File

@@ -31,7 +31,7 @@ class Gitlab extends Controller
$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 null; return;
} }
$return_payloads = collect([]); $return_payloads = collect([]);
@@ -93,7 +93,7 @@ class Gitlab extends Controller
return response($return_payloads); return response($return_payloads);
} }
} }
$applications = Application::query()->where('git_repository', 'like', "%$full_name%"); $applications = Application::where('git_repository', 'like', "%$full_name%");
if ($x_gitlab_event === 'push') { if ($x_gitlab_event === 'push') {
$applications = $applications->where('git_branch', $branch)->get(); $applications = $applications->where('git_branch', $branch)->get();
if ($applications->isEmpty()) { if ($applications->isEmpty()) {
@@ -181,10 +181,10 @@ class Gitlab extends Controller
if ($action === 'open' || $action === 'opened' || $action === 'synchronize' || $action === 'reopened' || $action === 'reopen' || $action === 'update') { if ($action === 'open' || $action === 'opened' || $action === 'synchronize' || $action === 'reopened' || $action === 'reopen' || $action === 'update') {
if ($application->isPRDeployable()) { if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2; $deployment_uuid = new Cuid2;
$found = ApplicationPreview::query()->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) {
if ($application->build_pack === 'dockercompose') { if ($application->build_pack === 'dockercompose') {
$pr_app = ApplicationPreview::query()->create([ $pr_app = ApplicationPreview::create([
'git_type' => 'gitlab', 'git_type' => 'gitlab',
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => $pull_request_id, 'pull_request_id' => $pull_request_id,
@@ -193,7 +193,7 @@ class Gitlab extends Controller
]); ]);
$pr_app->generate_preview_fqdn_compose(); $pr_app->generate_preview_fqdn_compose();
} else { } else {
ApplicationPreview::query()->create([ ApplicationPreview::create([
'git_type' => 'gitlab', 'git_type' => 'gitlab',
'application_id' => $application->id, 'application_id' => $application->id,
'pull_request_id' => $pull_request_id, 'pull_request_id' => $pull_request_id,
@@ -223,7 +223,7 @@ class Gitlab extends Controller
]); ]);
} }
} elseif ($action === 'closed' || $action === 'close' || $action === 'merge') { } elseif ($action === 'closed' || $action === 'close' || $action === 'merge') {
$found = ApplicationPreview::query()->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();
$container_name = generateApplicationContainerName($application, $pull_request_id); $container_name = generateApplicationContainerName($application, $pull_request_id);

View File

@@ -40,7 +40,7 @@ class Stripe extends Controller
return response('Webhook received. Cool cool cool cool cool.', 200); return response('Webhook received. Cool cool cool cool cool.', 200);
} }
$this->webhook = Webhook::query()->create([ $this->webhook = Webhook::create([
'type' => 'stripe', 'type' => 'stripe',
'payload' => $request->getContent(), 'payload' => $request->getContent(),
]); ]);

View File

@@ -2,35 +2,7 @@
namespace App\Http; namespace App\Http;
use App\Http\Middleware\ApiAbility;
use App\Http\Middleware\ApiSensitiveData;
use App\Http\Middleware\Authenticate;
use App\Http\Middleware\CheckForcePasswordReset;
use App\Http\Middleware\DecideWhatToDoWithUser;
use App\Http\Middleware\EncryptCookies;
use App\Http\Middleware\PreventRequestsDuringMaintenance;
use App\Http\Middleware\RedirectIfAuthenticated;
use App\Http\Middleware\TrimStrings;
use App\Http\Middleware\TrustProxies;
use App\Http\Middleware\ValidateSignature;
use App\Http\Middleware\VerifyCsrfToken;
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
use Illuminate\Auth\Middleware\RequirePassword;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Foundation\Http\Kernel as HttpKernel; use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Http\Middleware\HandleCors;
use Illuminate\Http\Middleware\SetCacheHeaders;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Laravel\Sanctum\Http\Middleware\CheckAbilities;
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;
class Kernel extends HttpKernel class Kernel extends HttpKernel
{ {
@@ -43,12 +15,12 @@ class Kernel extends HttpKernel
*/ */
protected $middleware = [ protected $middleware = [
// \App\Http\Middleware\TrustHosts::class, // \App\Http\Middleware\TrustHosts::class,
TrustProxies::class, \App\Http\Middleware\TrustProxies::class,
HandleCors::class, \Illuminate\Http\Middleware\HandleCors::class,
PreventRequestsDuringMaintenance::class, \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
ValidatePostSize::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
TrimStrings::class, \App\Http\Middleware\TrimStrings::class,
ConvertEmptyStringsToNull::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
]; ];
@@ -59,21 +31,21 @@ class Kernel extends HttpKernel
*/ */
protected $middlewareGroups = [ protected $middlewareGroups = [
'web' => [ 'web' => [
EncryptCookies::class, \App\Http\Middleware\EncryptCookies::class,
AddQueuedCookiesToResponse::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
StartSession::class, \Illuminate\Session\Middleware\StartSession::class,
ShareErrorsFromSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class,
VerifyCsrfToken::class, \App\Http\Middleware\VerifyCsrfToken::class,
SubstituteBindings::class, \Illuminate\Routing\Middleware\SubstituteBindings::class,
CheckForcePasswordReset::class, \App\Http\Middleware\CheckForcePasswordReset::class,
DecideWhatToDoWithUser::class, \App\Http\Middleware\DecideWhatToDoWithUser::class,
], ],
'api' => [ 'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
SubstituteBindings::class, \Illuminate\Routing\Middleware\SubstituteBindings::class,
], ],
]; ];
@@ -85,19 +57,19 @@ class Kernel extends HttpKernel
* @var array<string, class-string|string> * @var array<string, class-string|string>
*/ */
protected $middlewareAliases = [ protected $middlewareAliases = [
'auth' => Authenticate::class, 'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => AuthenticateWithBasicAuth::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => AuthenticateSession::class, 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'cache.headers' => SetCacheHeaders::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => Authorize::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => RedirectIfAuthenticated::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => RequirePassword::class, 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => ValidateSignature::class, 'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => ThrottleRequests::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => EnsureEmailIsVerified::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'abilities' => CheckAbilities::class, 'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class,
'ability' => CheckForAnyAbility::class, 'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class,
'api.ability' => ApiAbility::class, 'api.ability' => \App\Http\Middleware\ApiAbility::class,
'api.sensitive' => ApiSensitiveData::class, 'api.sensitive' => \App\Http\Middleware\ApiSensitiveData::class,
]; ];
} }

View File

@@ -2,8 +2,6 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Exception;
use Illuminate\Auth\AuthenticationException;
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility; use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility;
class ApiAbility extends CheckForAnyAbility class ApiAbility extends CheckForAnyAbility
@@ -16,11 +14,11 @@ class ApiAbility extends CheckForAnyAbility
} }
return parent::handle($request, $next, ...$abilities); return parent::handle($request, $next, ...$abilities);
} catch (AuthenticationException $e) { } catch (\Illuminate\Auth\AuthenticationException $e) {
return response()->json([ return response()->json([
'message' => 'Unauthenticated.', 'message' => 'Unauthenticated.',
], 401); ], 401);
} catch (Exception $e) { } catch (\Exception $e) {
return response()->json([ return response()->json([
'message' => 'Missing required permissions: '.implode(', ', $abilities), 'message' => 'Missing required permissions: '.implode(', ', $abilities),
], 403); ], 403);

View File

@@ -18,12 +18,14 @@ class ApiAllowed
return response()->json(['success' => true, 'message' => 'API is disabled.'], 403); return response()->json(['success' => true, 'message' => 'API is disabled.'], 403);
} }
if (! isDev() && $settings->allowed_ips) { if (! isDev()) {
if ($settings->allowed_ips) {
$allowedIps = explode(',', $settings->allowed_ips); $allowedIps = explode(',', $settings->allowed_ips);
if (! in_array($request->ip(), $allowedIps)) { if (! in_array($request->ip(), $allowedIps)) {
return response()->json(['success' => true, 'message' => 'You are not allowed to access the API.'], 403); return response()->json(['success' => true, 'message' => 'You are not allowed to access the API.'], 403);
} }
} }
}
return $next($request); return $next($request);
} }

View File

@@ -11,7 +11,7 @@ class CheckForcePasswordReset
/** /**
* Handle an incoming request. * Handle an incoming request.
* *
* @param Closure(Request):Response $next * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/ */
public function handle(Request $request, Closure $next): Response public function handle(Request $request, Closure $next): Response
{ {

View File

@@ -33,13 +33,15 @@ class DecideWhatToDoWithUser
return redirect()->route('verify.email'); return redirect()->route('verify.email');
} }
if (! isSubscriptionActive() && ! isSubscriptionOnGracePeriod() && ! in_array($request->path(), allowedPathsForUnsubscribedAccounts())) { if (! isSubscriptionActive() && ! isSubscriptionOnGracePeriod()) {
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);

View File

@@ -13,11 +13,11 @@ class RedirectIfAuthenticated
/** /**
* Handle an incoming request. * Handle an incoming request.
* *
* @param Closure(Request):Response $next * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/ */
public function handle(Request $request, Closure $next, string ...$guards): Response public function handle(Request $request, Closure $next, string ...$guards): Response
{ {
$guards = $guards === [] ? [null] : $guards; $guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) { foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) { if (Auth::guard($guard)->check()) {

View File

@@ -88,7 +88,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private bool $is_this_additional_server = false; private bool $is_this_additional_server = false;
private ?ApplicationPreview $applicationPreview = null; private ?ApplicationPreview $preview = null;
private ?string $git_type = null; private ?string $git_type = null;
@@ -174,8 +174,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->nixpacks_plan_json = collect([]); $this->nixpacks_plan_json = collect([]);
$this->application_deployment_queue = ApplicationDeploymentQueue::query()->find($application_deployment_queue_id); $this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
$this->application = Application::query()->find($this->application_deployment_queue->application_id); $this->application = Application::find($this->application_deployment_queue->application_id);
$this->build_pack = data_get($this->application, 'build_pack'); $this->build_pack = data_get($this->application, 'build_pack');
$this->build_args = collect([]); $this->build_args = collect([]);
@@ -199,7 +199,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if ($source) { if ($source) {
$this->source = $source->getMorphClass()::where('id', $this->application->source->id)->first(); $this->source = $source->getMorphClass()::where('id', $this->application->source->id)->first();
} }
$this->server = Server::query()->find($this->application_deployment_queue->server_id); $this->server = Server::find($this->application_deployment_queue->server_id);
$this->timeout = $this->server->settings->dynamic_timeout; $this->timeout = $this->server->settings->dynamic_timeout;
$this->destination = $this->server->destinations()->where('id', $this->application_deployment_queue->destination_id)->first(); $this->destination = $this->server->destinations()->where('id', $this->application_deployment_queue->destination_id)->first();
$this->server = $this->mainServer = $this->destination->server; $this->server = $this->mainServer = $this->destination->server;
@@ -225,15 +225,17 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
// Set preview fqdn // Set preview fqdn
if ($this->pull_request_id !== 0) { if ($this->pull_request_id !== 0) {
$this->applicationPreview = $this->application->generate_preview_fqdn($this->pull_request_id); $this->preview = $this->application->generate_preview_fqdn($this->pull_request_id);
if ($this->application->is_github_based()) { if ($this->application->is_github_based()) {
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->applicationPreview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::IN_PROGRESS); ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::IN_PROGRESS);
} }
if ($this->application->build_pack === 'dockerfile' && data_get($this->application, 'dockerfile_location')) { if ($this->application->build_pack === 'dockerfile') {
if (data_get($this->application, 'dockerfile_location')) {
$this->dockerfile_location = $this->application->dockerfile_location; $this->dockerfile_location = $this->application->dockerfile_location;
} }
} }
} }
}
public function tags(): array public function tags(): array
{ {
@@ -261,15 +263,15 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if (count($allContainers) > 0) { if (count($allContainers) > 0) {
$allContainers = $allContainers[0]; $allContainers = $allContainers[0];
$allContainers = collect($allContainers)->sort()->values(); $allContainers = collect($allContainers)->sort()->values();
foreach ($allContainers as $allContainer) { foreach ($allContainers as $container) {
$containerName = data_get($allContainer, 'Name'); $containerName = data_get($container, 'Name');
if ($containerName === 'coolify-proxy') { if ($containerName === 'coolify-proxy') {
continue; continue;
} }
if (preg_match('/-(\d{12})/', $containerName)) { if (preg_match('/-(\d{12})/', $containerName)) {
continue; continue;
} }
$containerIp = data_get($allContainer, 'IPv4Address'); $containerIp = data_get($container, 'IPv4Address');
if ($containerName && $containerIp) { if ($containerName && $containerIp) {
$containerIp = str($containerIp)->before('/'); $containerIp = str($containerIp)->before('/');
$ips->put($containerName, $containerIp->value()); $ips->put($containerName, $containerIp->value());
@@ -310,7 +312,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->decide_what_to_do(); $this->decide_what_to_do();
} catch (Exception $e) { } catch (Exception $e) {
if ($this->pull_request_id !== 0 && $this->application->is_github_based()) { if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->applicationPreview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR); ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR);
} }
$this->fail($e); $this->fail($e);
throw $e; throw $e;
@@ -338,8 +340,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->just_restart(); $this->just_restart();
return; return;
} } elseif ($this->pull_request_id !== 0) {
if ($this->pull_request_id !== 0) {
$this->deploy_pull_request(); $this->deploy_pull_request();
} elseif ($this->application->dockerfile) { } elseif ($this->application->dockerfile) {
$this->deploy_simple_dockerfile(); $this->deploy_simple_dockerfile();
@@ -363,8 +364,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
GetContainersStatus::dispatch($this->server); GetContainersStatus::dispatch($this->server);
} }
$this->next(ApplicationDeploymentStatus::FINISHED->value); $this->next(ApplicationDeploymentStatus::FINISHED->value);
if ($this->pull_request_id !== 0 && $this->application->is_github_based()) { if ($this->pull_request_id !== 0) {
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->applicationPreview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED); if ($this->application->is_github_based()) {
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
}
} }
$this->run_post_deployment_command(); $this->run_post_deployment_command();
$this->application->isConfigurationChanged(true); $this->application->isConfigurationChanged(true);
@@ -467,7 +470,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$yaml = $composeFile = $this->application->docker_compose_raw; $yaml = $composeFile = $this->application->docker_compose_raw;
$this->save_environment_variables(); $this->save_environment_variables();
} else { } else {
$composeFile = $this->application->parse(pull_request_id: $this->pull_request_id, preview_id: data_get($this->applicationPreview, 'id')); $composeFile = $this->application->parse(pull_request_id: $this->pull_request_id, preview_id: data_get($this->preview, 'id'));
$this->save_environment_variables(); $this->save_environment_variables();
if (! is_null($this->env_filename)) { if (! is_null($this->env_filename)) {
$services = collect(data_get($composeFile, 'services', [])); $services = collect(data_get($composeFile, 'services', []));
@@ -550,7 +553,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
['command' => $command, 'hidden' => true], ['command' => $command, 'hidden' => true],
); );
} }
} elseif ($this->docker_compose_custom_start_command) { } else {
if ($this->docker_compose_custom_start_command) {
$this->write_deployment_configurations(); $this->write_deployment_configurations();
$this->execute_remote_command( $this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), 'hidden' => true], [executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), 'hidden' => true],
@@ -578,6 +582,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->write_deployment_configurations(); $this->write_deployment_configurations();
} }
} }
}
$this->application_deployment_queue->addLogEntry('New container started.'); $this->application_deployment_queue->addLogEntry('New container started.');
} }
@@ -683,7 +688,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->server = $this->build_server; $this->server = $this->build_server;
} }
} }
if ($this->docker_compose_base64 !== null) { if (isset($this->docker_compose_base64)) {
if ($this->use_build_server) { if ($this->use_build_server) {
$this->server = $this->original_server; $this->server = $this->original_server;
} }
@@ -768,7 +773,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
} }
} catch (Exception $e) { } catch (Exception $e) {
$this->application_deployment_queue->addLogEntry('Failed to push image to docker registry. Please check debug logs for more information.'); $this->application_deployment_queue->addLogEntry('Failed to push image to docker registry. Please check debug logs for more information.');
throw new RuntimeException($e->getMessage(), 69420, $e); if ($forceFail) {
throw new RuntimeException($e->getMessage(), 69420);
}
} }
} }
@@ -839,8 +846,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->rolling_update(); $this->rolling_update();
return true; return true;
} } else {
$this->application_deployment_queue->addLogEntry('Configuration changed. Rebuilding image.'); $this->application_deployment_queue->addLogEntry('Configuration changed. Rebuilding image.');
}
} else { } else {
$this->application_deployment_queue->addLogEntry("Image not found ({$this->production_image_name}). Building new image."); $this->application_deployment_queue->addLogEntry("Image not found ({$this->production_image_name}). Building new image.");
} }
@@ -900,11 +908,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
} }
} }
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) { if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
$envs->push("COOLIFY_FQDN={$this->applicationPreview->fqdn}"); $envs->push("COOLIFY_FQDN={$this->preview->fqdn}");
$envs->push("COOLIFY_DOMAIN_URL={$this->applicationPreview->fqdn}"); $envs->push("COOLIFY_DOMAIN_URL={$this->preview->fqdn}");
} }
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) { if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
$url = str($this->applicationPreview->fqdn)->replace('http://', '')->replace('https://', ''); $url = str($this->preview->fqdn)->replace('http://', '')->replace('https://', '');
$envs->push("COOLIFY_URL={$url}"); $envs->push("COOLIFY_URL={$url}");
$envs->push("COOLIFY_DOMAIN_FQDN={$url}"); $envs->push("COOLIFY_DOMAIN_FQDN={$url}");
} }
@@ -919,21 +927,25 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
add_coolify_default_environment_variables($this->application, $envs, $this->application->environment_variables_preview); add_coolify_default_environment_variables($this->application, $envs, $this->application->environment_variables_preview);
foreach ($sorted_environment_variables_preview as $sorted_environment_variable_preview) { foreach ($sorted_environment_variables_preview as $env) {
$real_value = $sorted_environment_variable_preview->real_value; $real_value = $env->real_value;
if ($sorted_environment_variable_preview->version === '4.0.0-beta.239') { if ($env->version === '4.0.0-beta.239') {
$real_value = $sorted_environment_variable_preview->real_value; $real_value = $env->real_value;
} elseif ($sorted_environment_variable_preview->is_literal || $sorted_environment_variable_preview->is_multiline) { } else {
if ($env->is_literal || $env->is_multiline) {
$real_value = '\''.$real_value.'\''; $real_value = '\''.$real_value.'\'';
} else { } else {
$real_value = escapeEnvVariables($sorted_environment_variable_preview->real_value); $real_value = escapeEnvVariables($env->real_value);
} }
$envs->push($sorted_environment_variable_preview->key.'='.$real_value); }
$envs->push($env->key.'='.$real_value);
} }
// Add PORT if not exists, use the first port as default // Add PORT if not exists, use the first port as default
if ($this->build_pack !== 'dockercompose' && $this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) { if ($this->build_pack !== 'dockercompose') {
if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) {
$envs->push("PORT={$ports[0]}"); $envs->push("PORT={$ports[0]}");
} }
}
// Add HOST if not exists // Add HOST if not exists
if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) { if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
$envs->push('HOST=0.0.0.0'); $envs->push('HOST=0.0.0.0');
@@ -974,21 +986,25 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
add_coolify_default_environment_variables($this->application, $envs, $this->application->environment_variables); add_coolify_default_environment_variables($this->application, $envs, $this->application->environment_variables);
foreach ($sorted_environment_variables as $sorted_environment_variable) { foreach ($sorted_environment_variables as $env) {
$real_value = $sorted_environment_variable->real_value; $real_value = $env->real_value;
if ($sorted_environment_variable->version === '4.0.0-beta.239') { if ($env->version === '4.0.0-beta.239') {
$real_value = $sorted_environment_variable->real_value; $real_value = $env->real_value;
} elseif ($sorted_environment_variable->is_literal || $sorted_environment_variable->is_multiline) { } else {
if ($env->is_literal || $env->is_multiline) {
$real_value = '\''.$real_value.'\''; $real_value = '\''.$real_value.'\'';
} else { } else {
$real_value = escapeEnvVariables($sorted_environment_variable->real_value); $real_value = escapeEnvVariables($env->real_value);
} }
$envs->push($sorted_environment_variable->key.'='.$real_value); }
$envs->push($env->key.'='.$real_value);
} }
// Add PORT if not exists, use the first port as default // Add PORT if not exists, use the first port as default
if ($this->build_pack !== 'dockercompose' && $this->application->environment_variables->where('key', 'PORT')->isEmpty()) { if ($this->build_pack !== 'dockercompose') {
if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) {
$envs->push("PORT={$ports[0]}"); $envs->push("PORT={$ports[0]}");
} }
}
// Add HOST if not exists // Add HOST if not exists
if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) { if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
$envs->push('HOST=0.0.0.0'); $envs->push('HOST=0.0.0.0');
@@ -1051,7 +1067,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private function elixir_finetunes() private function elixir_finetunes()
{ {
$envType = $this->pull_request_id === 0 ? 'environment_variables' : 'environment_variables_preview'; if ($this->pull_request_id === 0) {
$envType = 'environment_variables';
} else {
$envType = 'environment_variables_preview';
}
$mix_env = $this->application->{$envType}->where('key', 'MIX_ENV')->first(); $mix_env = $this->application->{$envType}->where('key', 'MIX_ENV')->first();
if ($mix_env) { if ($mix_env) {
if ($mix_env->is_build_time === false) { if ($mix_env->is_build_time === false) {
@@ -1086,7 +1106,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private function laravel_finetunes() private function laravel_finetunes()
{ {
$envType = $this->pull_request_id === 0 ? 'environment_variables' : 'environment_variables_preview'; if ($this->pull_request_id === 0) {
$envType = 'environment_variables';
} else {
$envType = 'environment_variables_preview';
}
$nixpacks_php_fallback_path = $this->application->{$envType}->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first(); $nixpacks_php_fallback_path = $this->application->{$envType}->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
$nixpacks_php_root_dir = $this->application->{$envType}->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first(); $nixpacks_php_root_dir = $this->application->{$envType}->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
@@ -1170,7 +1194,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->application_deployment_queue->addLogEntry('Custom healthcheck found, skipping default healthcheck.'); $this->application_deployment_queue->addLogEntry('Custom healthcheck found, skipping default healthcheck.');
} }
// ray('New container name: ', $this->container_name); // ray('New container name: ', $this->container_name);
if ($this->container_name !== '' && $this->container_name !== '0') { if ($this->container_name) {
$counter = 1; $counter = 1;
$this->application_deployment_queue->addLogEntry('Waiting for healthcheck to pass on the new container.'); $this->application_deployment_queue->addLogEntry('Waiting for healthcheck to pass on the new container.');
if ($this->full_healthcheck_url) { if ($this->full_healthcheck_url) {
@@ -1320,11 +1344,13 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
throw new RuntimeException('Docker config file (~/.docker/config.json) not found on the build server. Please run "docker login" to login to the docker registry on the server.'); throw new RuntimeException('Docker config file (~/.docker/config.json) not found on the build server. Please run "docker login" to login to the docker registry on the server.');
} }
$runCommand = "docker run -d --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}"; $runCommand = "docker run -d --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
} elseif ($this->dockerConfigFileExists === 'OK') { } else {
if ($this->dockerConfigFileExists === 'OK') {
$runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}"; $runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
} else { } else {
$runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}"; $runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
} }
}
$this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage."); $this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage.");
$this->execute_remote_command( $this->execute_remote_command(
[ [
@@ -1363,7 +1389,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
return; return;
} }
foreach ($destination_ids as $destination_id) { foreach ($destination_ids as $destination_id) {
$destination = StandaloneDocker::query()->find($destination_id); $destination = StandaloneDocker::find($destination_id);
$server = $destination->server; $server = $destination->server;
if ($server->team_id !== $this->mainServer->team_id) { if ($server->team_id !== $this->mainServer->team_id) {
$this->application_deployment_queue->addLogEntry("Skipping deployment to {$server->name}. Not in the same team?!"); $this->application_deployment_queue->addLogEntry("Skipping deployment to {$server->name}. Not in the same team?!");
@@ -1391,13 +1417,17 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private function set_coolify_variables() private function set_coolify_variables()
{ {
$this->coolify_variables = "SOURCE_COMMIT={$this->commit} "; $this->coolify_variables = "SOURCE_COMMIT={$this->commit} ";
$fqdn = $this->pull_request_id === 0 ? $this->application->fqdn : $this->applicationPreview->fqdn; if ($this->pull_request_id === 0) {
$fqdn = $this->application->fqdn;
} else {
$fqdn = $this->preview->fqdn;
}
if (isset($fqdn)) { if (isset($fqdn)) {
$this->coolify_variables .= "COOLIFY_FQDN={$fqdn} "; $this->coolify_variables .= "COOLIFY_FQDN={$fqdn} ";
$url = str($fqdn)->replace('http://', '')->replace('https://', ''); $url = str($fqdn)->replace('http://', '')->replace('https://', '');
$this->coolify_variables .= "COOLIFY_URL={$url} "; $this->coolify_variables .= "COOLIFY_URL={$url} ";
} }
if (property_exists($this->application, 'git_branch') && $this->application->git_branch !== null) { if (isset($this->application->git_branch)) {
$this->coolify_variables .= "COOLIFY_BRANCH={$this->application->git_branch} "; $this->coolify_variables .= "COOLIFY_BRANCH={$this->application->git_branch} ";
} }
} }
@@ -1568,8 +1598,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if ($this->application->install_command) { if ($this->application->install_command) {
$nixpacks_command .= " --install-cmd \"{$this->application->install_command}\""; $nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
} }
$nixpacks_command .= " {$this->workdir}";
return $nixpacks_command." {$this->workdir}"; return $nixpacks_command;
} }
private function generate_nixpacks_env_variables() private function generate_nixpacks_env_variables()
@@ -1635,7 +1666,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}); });
if ($found_caddy_labels->count() === 0) { if ($found_caddy_labels->count() === 0) {
if ($this->pull_request_id !== 0) { if ($this->pull_request_id !== 0) {
$domains = str(data_get($this->applicationPreview, 'fqdn'))->explode(','); $domains = str(data_get($this->preview, 'fqdn'))->explode(',');
} else { } else {
$domains = str(data_get($this->application, 'fqdn'))->explode(','); $domains = str(data_get($this->application, 'fqdn'))->explode(',');
} }
@@ -1651,11 +1682,13 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
} }
$this->application->custom_labels = base64_encode($labels->implode("\n")); $this->application->custom_labels = base64_encode($labels->implode("\n"));
$this->application->save(); $this->application->save();
} elseif (! $this->application->settings->is_container_label_readonly_enabled) { } else {
$labels = collect(generateLabelsApplication($this->application, $this->applicationPreview)); if (! $this->application->settings->is_container_label_readonly_enabled) {
$labels = collect(generateLabelsApplication($this->application, $this->preview));
}
} }
if ($this->pull_request_id !== 0) { if ($this->pull_request_id !== 0) {
$labels = collect(generateLabelsApplication($this->application, $this->applicationPreview)); $labels = collect(generateLabelsApplication($this->application, $this->preview));
} }
if ($this->application->settings->is_container_label_escape_enabled) { if ($this->application->settings->is_container_label_escape_enabled) {
$labels = $labels->map(function ($value, $key) { $labels = $labels->map(function ($value, $key) {
@@ -1841,7 +1874,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose); $docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
} }
} }
} elseif (count($custom_compose) > 0) { } else {
if (count($custom_compose) > 0) {
$ipv4 = data_get($custom_compose, 'ip.0'); $ipv4 = data_get($custom_compose, 'ip.0');
$ipv6 = data_get($custom_compose, 'ip6.0'); $ipv6 = data_get($custom_compose, 'ip6.0');
data_forget($custom_compose, 'ip'); data_forget($custom_compose, 'ip');
@@ -1858,6 +1892,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$docker_compose['services'][$this->container_name] = array_merge_recursive($docker_compose['services'][$this->container_name], $custom_compose); $docker_compose['services'][$this->container_name] = array_merge_recursive($docker_compose['services'][$this->container_name], $custom_compose);
} }
} }
}
$this->docker_compose = Yaml::dump($docker_compose, 10); $this->docker_compose = Yaml::dump($docker_compose, 10);
$this->docker_compose_base64 = base64_encode($this->docker_compose); $this->docker_compose_base64 = base64_encode($this->docker_compose);
@@ -2070,8 +2105,9 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
'hidden' => true, 'hidden' => true,
] ]
); );
} elseif ($this->application->dockerfile) { } else {
// Pure Dockerfile based deployment // Pure Dockerfile based deployment
if ($this->application->dockerfile) {
if ($this->force_rebuild) { if ($this->force_rebuild) {
$build_command = "docker build --no-cache --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"; $build_command = "docker build --no-cache --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
} else { } else {
@@ -2092,7 +2128,8 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
'hidden' => true, 'hidden' => true,
] ]
); );
} elseif ($this->application->build_pack === 'nixpacks') { } else {
if ($this->application->build_pack === 'nixpacks') {
$this->nixpacks_plan = base64_encode($this->nixpacks_plan); $this->nixpacks_plan = base64_encode($this->nixpacks_plan);
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]); $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
if ($this->force_rebuild) { if ($this->force_rebuild) {
@@ -2147,6 +2184,8 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
] ]
); );
} }
}
}
$this->application_deployment_queue->addLogEntry('Building docker image completed.'); $this->application_deployment_queue->addLogEntry('Building docker image completed.');
} }
@@ -2175,7 +2214,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
["docker kill $containerName", 'hidden' => true, 'ignore_errors' => true] ["docker kill $containerName", 'hidden' => true, 'ignore_errors' => true]
); );
} }
} catch (Exception $error) { } catch (\Exception $error) {
$this->application_deployment_queue->addLogEntry("Error stopping container $containerName: ".$error->getMessage(), 'stderr'); $this->application_deployment_queue->addLogEntry("Error stopping container $containerName: ".$error->getMessage(), 'stderr');
} }
@@ -2228,7 +2267,8 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
[executeInDocker($this->deployment_uuid, "docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} pull"), 'hidden' => true], [executeInDocker($this->deployment_uuid, "docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} pull"), 'hidden' => true],
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} up --build -d"), 'hidden' => true], [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} up --build -d"), 'hidden' => true],
); );
} elseif ($this->use_build_server) { } else {
if ($this->use_build_server) {
$this->execute_remote_command( $this->execute_remote_command(
["{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", 'hidden' => true], ["{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", 'hidden' => true],
); );
@@ -2237,6 +2277,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), 'hidden' => true], [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), 'hidden' => true],
); );
} }
}
$this->application_deployment_queue->addLogEntry('New container started.'); $this->application_deployment_queue->addLogEntry('New container started.');
} }
@@ -2357,7 +2398,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
]); ]);
} }
if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::FAILED->value) { if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::FAILED->value) {
$this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->applicationPreview)); $this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview));
return; return;
} }
@@ -2365,20 +2406,20 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if (! $this->only_this_server) { if (! $this->only_this_server) {
$this->deploy_to_additional_destinations(); $this->deploy_to_additional_destinations();
} }
$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->applicationPreview)); $this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
} }
} }
public function failed(Throwable $throwable): void public function failed(Throwable $exception): void
{ {
$this->next(ApplicationDeploymentStatus::FAILED->value); $this->next(ApplicationDeploymentStatus::FAILED->value);
$this->application_deployment_queue->addLogEntry('Oops something is not okay, are you okay? 😢', 'stderr'); $this->application_deployment_queue->addLogEntry('Oops something is not okay, are you okay? 😢', 'stderr');
if (str($throwable->getMessage())->isNotEmpty()) { if (str($exception->getMessage())->isNotEmpty()) {
$this->application_deployment_queue->addLogEntry($throwable->getMessage(), 'stderr'); $this->application_deployment_queue->addLogEntry($exception->getMessage(), 'stderr');
} }
if ($this->application->build_pack !== 'dockercompose') { if ($this->application->build_pack !== 'dockercompose') {
$code = $throwable->getCode(); $code = $exception->getCode();
if ($code !== 69420) { if ($code !== 69420) {
// 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one // 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one
if ($this->application->settings->is_consistent_container_name_enabled || str($this->application->settings->custom_internal_name)->isNotEmpty()) { if ($this->application->settings->is_consistent_container_name_enabled || str($this->application->settings->custom_internal_name)->isNotEmpty()) {

View File

@@ -11,7 +11,6 @@ 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\SerializesModels; use Illuminate\Queue\SerializesModels;
use Throwable;
class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
{ {
@@ -23,8 +22,8 @@ class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
public function __construct( public function __construct(
public Application $application, public Application $application,
public ApplicationPreview $applicationPreview, public ApplicationPreview $preview,
public ProcessStatus $processStatus, public ProcessStatus $status,
public ?string $deployment_uuid = null public ?string $deployment_uuid = null
) { ) {
$this->onQueue('high'); $this->onQueue('high');
@@ -34,42 +33,39 @@ class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
{ {
try { try {
if ($this->application->is_public_repository()) { if ($this->application->is_public_repository()) {
return null; return;
} }
if ($this->processStatus === ProcessStatus::CLOSED) { if ($this->status === ProcessStatus::CLOSED) {
$this->delete_comment(); $this->delete_comment();
return null; return;
} } elseif ($this->status === ProcessStatus::IN_PROGRESS) {
if ($this->processStatus === ProcessStatus::IN_PROGRESS) {
$this->body = "The preview deployment is in progress. 🟡\n\n"; $this->body = "The preview deployment is in progress. 🟡\n\n";
} elseif ($this->processStatus === 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->applicationPreview->fqdn) { if ($this->preview->fqdn) {
$this->body .= "[Open Preview]({$this->applicationPreview->fqdn}) | "; $this->body .= "[Open Preview]({$this->preview->fqdn}) | ";
} }
} elseif ($this->processStatus === 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';
if ($this->applicationPreview->pull_request_issue_comment_id) { if ($this->preview->pull_request_issue_comment_id) {
$this->update_comment(); $this->update_comment();
} else { } else {
$this->create_comment(); $this->create_comment();
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
return $e; return $e;
} }
return null;
} }
private function update_comment() private function update_comment()
{ {
['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->applicationPreview->pull_request_issue_comment_id}", method: 'patch', data: [ ['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->preview->pull_request_issue_comment_id}", method: 'patch', data: [
'body' => $this->body, 'body' => $this->body,
], throwError: false); ], throwError: false);
if (data_get($data, 'message') === 'Not Found') { if (data_get($data, 'message') === 'Not Found') {
@@ -79,15 +75,15 @@ class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
private function create_comment() private function create_comment()
{ {
['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/{$this->applicationPreview->pull_request_id}/comments", method: 'post', data: [ ['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/{$this->preview->pull_request_id}/comments", method: 'post', data: [
'body' => $this->body, 'body' => $this->body,
]); ]);
$this->applicationPreview->pull_request_issue_comment_id = $data['id']; $this->preview->pull_request_issue_comment_id = $data['id'];
$this->applicationPreview->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->applicationPreview->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

@@ -34,14 +34,14 @@ class CheckAndStartSentinelJob implements ShouldBeEncrypted, ShouldQueue
} }
// If sentinel is running, check if it needs an update // If sentinel is running, check if it needs an update
$runningVersion = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/version"'], $this->server, false); $runningVersion = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/version"'], $this->server, false);
if ($runningVersion === null || $runningVersion === '' || $runningVersion === '0') { if (empty($runningVersion)) {
$runningVersion = '0.0.0'; $runningVersion = '0.0.0';
} }
if ($latestVersion === '0.0.0' && $runningVersion === '0.0.0') { if ($latestVersion === '0.0.0' && $runningVersion === '0.0.0') {
StartSentinel::run(server: $this->server, restart: true, latestVersion: 'latest'); StartSentinel::run(server: $this->server, restart: true, latestVersion: 'latest');
return; return;
} } else {
if (version_compare($runningVersion, $latestVersion, '<')) { if (version_compare($runningVersion, $latestVersion, '<')) {
StartSentinel::run(server: $this->server, restart: true, latestVersion: $latestVersion); StartSentinel::run(server: $this->server, restart: true, latestVersion: $latestVersion);
@@ -49,3 +49,4 @@ class CheckAndStartSentinelJob implements ShouldBeEncrypted, ShouldQueue
} }
} }
} }
}

View File

@@ -10,7 +10,6 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Throwable;
class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
{ {
@@ -38,7 +37,7 @@ class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
$settings->update(['new_version_available' => false]); $settings->update(['new_version_available' => false]);
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
// Consider implementing a notification to administrators // Consider implementing a notification to administrators
} }
} }

View File

@@ -9,7 +9,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Throwable;
class CheckHelperImageJob implements ShouldBeEncrypted, ShouldQueue class CheckHelperImageJob implements ShouldBeEncrypted, ShouldQueue
{ {
@@ -17,6 +16,8 @@ class CheckHelperImageJob implements ShouldBeEncrypted, ShouldQueue
public $timeout = 1000; public $timeout = 1000;
public function __construct() {}
public function handle(): void public function handle(): void
{ {
try { try {
@@ -30,7 +31,7 @@ class CheckHelperImageJob implements ShouldBeEncrypted, ShouldQueue
$settings->update(['helper_version' => $latest_version]); $settings->update(['helper_version' => $latest_version]);
} }
} }
} catch (Throwable $e) { } catch (\Throwable $e) {
send_internal_notification('CheckHelperImageJob failed with: '.$e->getMessage()); send_internal_notification('CheckHelperImageJob failed with: '.$e->getMessage());
throw $e; throw $e;
} }

View File

@@ -10,7 +10,6 @@ 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\SerializesModels; use Illuminate\Queue\SerializesModels;
use Throwable;
class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue
{ {
@@ -28,7 +27,7 @@ class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, S
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());
} }
} }

View File

@@ -12,12 +12,13 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Throwable;
class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct() {}
public function middleware(): array public function middleware(): array
{ {
return [(new WithoutOverlapping('cleanup-instance-stuffs'))->dontRelease()]; return [(new WithoutOverlapping('cleanup-instance-stuffs'))->dontRelease()];
@@ -27,7 +28,7 @@ class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, Sho
{ {
try { try {
$this->cleanupInvitationLink(); $this->cleanupInvitationLink();
} catch (Throwable $e) { } catch (\Throwable $e) {
Log::error('CleanupInstanceStuffsJob failed with error: '.$e->getMessage()); Log::error('CleanupInstanceStuffsJob failed with error: '.$e->getMessage());
} }
} }

View File

@@ -28,7 +28,7 @@ class CleanupStaleMultiplexedConnections implements ShouldQueue
foreach ($muxFiles as $muxFile) { foreach ($muxFiles as $muxFile) {
$serverUuid = $this->extractServerUuidFromMuxFile($muxFile); $serverUuid = $this->extractServerUuidFromMuxFile($muxFile);
$server = Server::query()->where('uuid', $serverUuid)->first(); $server = Server::where('uuid', $serverUuid)->first();
if (! $server) { if (! $server) {
$this->removeMultiplexFile($muxFile); $this->removeMultiplexFile($muxFile);
@@ -57,7 +57,7 @@ class CleanupStaleMultiplexedConnections implements ShouldQueue
private function cleanupNonExistentServerConnections() private function cleanupNonExistentServerConnections()
{ {
$muxFiles = Storage::disk('ssh-mux')->files(); $muxFiles = Storage::disk('ssh-mux')->files();
$existingServerUuids = Server::query()->pluck('uuid')->toArray(); $existingServerUuids = Server::pluck('uuid')->toArray();
foreach ($muxFiles as $muxFile) { foreach ($muxFiles as $muxFile) {
$serverUuid = $this->extractServerUuidFromMuxFile($muxFile); $serverUuid = $this->extractServerUuidFromMuxFile($muxFile);

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