Merge pull request #2423 from coollabsio/next

v4.0.0-beta.297
This commit is contained in:
Andras Bacsai 2024-06-11 13:28:49 +02:00 committed by GitHub
commit de7380fb0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
454 changed files with 5605 additions and 3290 deletions

View File

@ -0,0 +1,25 @@
name: Fix PHP code style issues
on: [push]
permissions:
contents: write
jobs:
php-code-styling:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- name: Fix PHP code style issues
uses: aglipanci/laravel-pint-action@2.4
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Fix styling

View File

@ -31,15 +31,19 @@ # Donations
Thank you so much!
Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
Special thanks to our biggest sponsors!
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
<a href="http://htznr.li/CoolifyXHetzner" target="_blank"><img src="./other/logos/hetzner.jpg" alt="hetzner logo" width="200"/></a>
<a href="https://logto.io/?ref=coolify" target="_blank"><img src="./other/logos/logto.webp" alt="logto logo" width="200"/></a>
<a href="https://bc.direct/?utm_source=coolify.io" target="_blank"><img src="./other/logos/bc.png" alt="bc direct logo" width="200"/></a>
<a href="https://www.quantcdn.io/?utm_source=coolify.io" target="_blank"><img src="./other/logos/quant.svg" alt="quantcdn logo" width="200"/></a>
<a href="https://arcjet.com/?utm_source=coolify.io" target="_blank"><img src="./other/logos/arcjet.svg" alt="arcjet logo" width="200"/></a>
## Github Sponsors ($40+)
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
<a href="https://serpapi.com/?utm_source=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>
<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
<a href="https://www.quantcdn.io/?utm_source=coolify.io"><img src="https://github.com/quantcdn.png" width="60px" alt="QuantCDN"/></a>
<a href="https://www.runpod.io/?utm_source=coolify.io">
<svg style="width:60px;height:60px;background:#fff;" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 200 200"><g><path d="M74.5 51.1c-25.4 14.9-27 16-29.6 20.2-1.8 3-1.9 5.3-1.9 32.3 0 21.7.3 29.4 1.3 30.6 1.9 2.5 46.7 27.9 48.5 27.6 1.5-.3 1.7-3.1 2-27.7.2-21.9 0-27.8-1.1-29.5-.8-1.2-9.9-6.8-20.2-12.6-10.3-5.8-19.4-11.5-20.2-12.7-1.8-2.6-.9-5.9 1.8-7.4 1.6-.8 6.3 0 21.8 4C87.8 78.7 98 81 99.6 81c4.4 0 49.9-25.9 49.9-28.4 0-1.6-3.4-2.8-24-8.2-13.2-3.5-25.1-6.3-26.5-6.3-1.4.1-12.4 5.9-24.5 13z"></path><path d="m137.2 68.1-3.3 2.1 6.3 3.7c3.5 2 6.3 4.3 6.3 5.1 0 .9-8 6.1-19.4 12.6-10.6 6-20 11.9-20.7 12.9-1.2 1.6-1.4 7.2-1.2 29.4.3 24.8.5 27.6 2 27.9 1.8.3 46.6-25.1 48.6-27.6.9-1.2 1.2-8.8 1.2-30.2s-.3-29-1.2-30.2c-1.6-1.9-12.1-7.8-13.9-7.8-.8 0-2.9 1-4.7 2.1z"></path></g></svg></a>
<a href="https://lightspeed.run/?utm_source=coolify.io"><img src="https://github.com/lightspeedrun.png" width="60px" alt="Lightspeed.run"/></a>

View File

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

View File

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

View File

@ -14,6 +14,7 @@
class PrepareCoolifyTask
{
protected Activity $activity;
protected CoolifyTaskArgs $remoteProcessArgs;
public function __construct(CoolifyTaskArgs $remoteProcessArgs)
@ -28,12 +29,12 @@ public function __construct(CoolifyTaskArgs $remoteProcessArgs)
->withProperties($properties)
->performedOn($remoteProcessArgs->model)
->event($remoteProcessArgs->type)
->log("[]");
->log('[]');
} else {
$this->activity = activity()
->withProperties($remoteProcessArgs->toArray())
->event($remoteProcessArgs->type)
->log("[]");
->log('[]');
}
}
@ -42,6 +43,7 @@ public function __invoke(): Activity
$job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish, call_event_data: $this->remoteProcessArgs->call_event_data);
dispatch($job);
$this->activity->refresh();
return $this->activity;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,12 +2,13 @@
namespace App\Actions\Server;
use Lorisleiva\Actions\Concerns\AsAction;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
class StartSentinel
{
use AsAction;
public function handle(Server $server, $version = 'latest', bool $restart = false)
{
if ($restart) {
@ -15,8 +16,8 @@ public function handle(Server $server, $version = 'latest', bool $restart = fals
}
instant_remote_process([
"docker run --rm --pull always -d -e \"SCHEDULER=true\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
"chown -R 9999:root /data/coolify/metrics /data/coolify/logs",
"chmod -R 700 /data/coolify/metrics /data/coolify/logs"
'chown -R 9999:root /data/coolify/metrics /data/coolify/logs',
'chmod -R 700 /data/coolify/metrics /data/coolify/logs',
], $server, false);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@
class CleanupApplicationDeploymentQueue extends Command
{
protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
protected $description = 'CleanupApplicationDeploymentQueue';
public function handle()
@ -15,10 +16,10 @@ public function handle()
$team_id = $this->option('team-id');
$servers = \App\Models\Server::where('team_id', $team_id)->get();
foreach ($servers as $server) {
$deployments = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->where("server_id", $server->id)->get();
$deployments = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->where('server_id', $server->id)->get();
foreach ($deployments as $deployment) {
$deployment->update(['status' => 'failed']);
instant_remote_process(['docker rm -f ' . $deployment->deployment_uuid], $server, false);
instant_remote_process(['docker rm -f '.$deployment->deployment_uuid], $server, false);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,10 +17,13 @@ public function servers(Request $request)
$servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) {
$server['is_reachable'] = $server->settings->is_reachable;
$server['is_usable'] = $server->settings->is_usable;
return $server;
});
return response()->json($servers);
}
public function server_by_uuid(Request $request)
{
$with_resources = $request->query('resources');
@ -47,11 +50,13 @@ public function server_by_uuid(Request $request)
} else {
$payload['status'] = $resource->status;
}
return $payload;
});
} else {
$server->load(['settings']);
}
return response()->json($server);
}
}

View File

@ -14,8 +14,10 @@ public function teams(Request $request)
return invalid_token();
}
$teams = auth()->user()->teams;
return response()->json($teams);
}
public function team_by_id(Request $request)
{
$id = $request->id;
@ -26,10 +28,12 @@ public function team_by_id(Request $request)
$teams = auth()->user()->teams;
$team = $teams->where('id', $id)->first();
if (is_null($team)) {
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid"], 404);
return response()->json(['error' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid'], 404);
}
return response()->json($team);
}
public function members_by_id(Request $request)
{
$id = $request->id;
@ -40,10 +44,12 @@ public function members_by_id(Request $request)
$teams = auth()->user()->teams;
$team = $teams->where('id', $id)->first();
if (is_null($team)) {
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid-members"], 404);
return response()->json(['error' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid-members'], 404);
}
return response()->json($team->members);
}
public function current_team(Request $request)
{
$teamId = get_team_id_from_token();
@ -51,8 +57,10 @@ public function current_team(Request $request)
return invalid_token();
}
$team = auth()->user()->currentTeam();
return response()->json($team);
}
public function current_team_members(Request $request)
{
$teamId = get_team_id_from_token();
@ -60,6 +68,7 @@ public function current_team_members(Request $request)
return invalid_token();
}
$team = auth()->user()->currentTeam();
return response()->json($team->members);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,6 +20,7 @@ public function handle(Request $request, Closure $next): Response
auth()->logout();
request()->session()->invalidate();
request()->session()->regenerateToken();
return $next($request);
}
$force_password_reset = auth()->user()->force_password_reset;
@ -27,9 +28,11 @@ public function handle(Request $request, Closure $next): Response
if ($request->routeIs('auth.force-password-reset') || $request->path() === 'force-password-reset' || $request->path() === 'livewire/update' || $request->path() === 'logout') {
return $next($request);
}
return redirect()->route('auth.force-password-reset');
}
}
return $next($request);
}
}

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class CleanupHelperContainersJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted
class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -22,18 +22,18 @@ public function __construct(public Server $server)
public function handle(): void
{
try {
ray('Cleaning up helper containers on ' . $this->server->name);
ray('Cleaning up helper containers on '.$this->server->name);
$containers = instant_remote_process(['docker container ps --filter "ancestor=ghcr.io/coollabsio/coolify-helper:next" --filter "ancestor=ghcr.io/coollabsio/coolify-helper:latest" --format \'{{json .}}\''], $this->server, false);
$containers = format_docker_command_output_to_json($containers);
if ($containers->count() > 0) {
foreach ($containers as $container) {
$containerId = data_get($container,'ID');
ray('Removing container ' . $containerId);
instant_remote_process(['docker container rm -f ' . $containerId], $this->server, false);
$containerId = data_get($container, 'ID');
ray('Removing container '.$containerId);
instant_remote_process(['docker container rm -f '.$containerId], $this->server, false);
}
}
} catch (\Throwable $e) {
send_internal_notification('CleanupHelperContainersJob failed with error: ' . $e->getMessage());
send_internal_notification('CleanupHelperContainersJob failed with error: '.$e->getMessage());
ray($e->getMessage());
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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