Merge branch 'next' into es-translation

This commit is contained in:
Andras Bacsai 2024-06-19 16:07:06 +02:00 committed by GitHub
commit 919f56a292
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
556 changed files with 7636 additions and 3843 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

@ -5,13 +5,13 @@ # About the Project
Coolify is an open-source & self-hostable alternative to Heroku / Netlify / Vercel / etc.
It helps you to manage your servers, applications, databases on your own hardware, all you need is SSH connection. You can manage VPS, Bare Metal, Raspberry PI's anything.
It helps you manage your servers, applications, and databases on your own hardware; you only need an SSH connection. You can manage VPS, Bare Metal, Raspberry PIs, and anything else.
Imagine if you could have the ease of a cloud but with your own servers. That is **Coolify**.
Imagine having the ease of a cloud but with your own servers. That is **Coolify**.
No vendor lock-in, which means that all the configuration for your applications/databases/etc are saved to your server. So if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You just lose the automations and all the magic. 🪄️
No vendor lock-in, which means that all the configurations for your applications/databases/etc are saved to your server. So, if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You lose the automations and all the magic. 🪄️
For more information, take a look at our landing page [here](https://coolify.io).
For more information, take a look at our landing page at [coolify.io](https://coolify.io).
# Installation
@ -22,32 +22,40 @@ # Installation
# Support
Contact us [here](https://coolify.io/docs/contact).
Contact us at [coolify.io/docs/contact](https://coolify.io/docs/contact).
# Donations
To stay completely free, open-source, no feature behind paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the future development of the project.
To stay completely free and open-source, with no feature behind the paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the project's future development.
https://coolify.io/sponsorships
[coolify.io/sponsorships](https://coolify.io/sponsorships)
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="150"/></a>
<a href="https://logto.io/?ref=coolify" target="_blank"><img src="./other/logos/logto.webp" alt="logto logo" width="150"/></a>
<a href="https://bc.direct/?ref=coolify.io" target="_blank"><img src="./other/logos/bc.png" alt="bc direct logo" width="200"/></a>
<a href="https://www.quantcdn.io/?ref=coolify.io" target="_blank"><img src="./other/logos/quant.svg" alt="quantcdn logo" width="150"/></a>
<a href="https://arcjet.com/?ref=coolify.io" target="_blank"><img src="./other/logos/arcjet.svg" alt="arcjet logo" width="200"/></a>
<a href="https://supa.guide/?ref=coolify.io" target="_blank"><img src="./other/logos/supaguide.png" alt="supaguide logo" width="200"/></a>
<a href="https://tigrisdata.com/?ref=coolify.io" target="_blank"><img src="./other/logos/tigris.svg" alt="tigris logo" width="140"/></a>
<a href="https://fractalnetworks.co/?ref=coolify.io" target="_blank"><img src="./other/logos/fractal.svg" alt="fractal logo" width="180"/></a>
<a href="https://coolify.ad.vin/?ref=coolify.io" target="_blank"><img src="./other/logos/advin.png" alt="advin logo" width="250"/></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">
<a href="https://serpapi.com/?ref=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>
<a href="https://typebot.io/?ref=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
<a href="https://www.runpod.io/?ref=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>
<a href="https://www.flint.sh/en/home?utm_source=coolify.io"> <img src="https://github.com/Flint-company.png" width="60px" alt="FlintCompany"/></a>
<a href="https://americancloud.com/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
<a href="https://x.com/mrsmith9ja?utm_source=coolify.io"><img width="60px" alt="Thompson Edolo" src="https://github.com/verygreenboi.png"/></a>
<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
<a href="https://lightspeed.run/?ref=coolify.io"><img src="https://github.com/lightspeedrun.png" width="60px" alt="Lightspeed.run"/></a>
<a href="https://www.flint.sh/en/home?ref=coolify.io"> <img src="https://github.com/Flint-company.png" width="60px" alt="FlintCompany"/></a>
<a href="https://americancloud.com/?ref=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
<a href="https://cryptojobslist.com/?ref=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
<a href="https://codext.link/coolify-io?ref=coolify.io"><img src="./other/logos/codext.jpg" width="60px" alt="Codext" /></a>
<a href="https://x.com/mrsmith9ja?ref=coolify.io"><img width="60px" alt="Thompson Edolo" src="https://github.com/verygreenboi.png"/></a>
<a href="https://www.uxwizz.com/?ref=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
<a href="https://github.com/Flowko"><img src="https://barrad.me/_ipx/f_webp&s_300x300/younes.jpg" width="60px" alt="Younes Barrad" /></a>
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Automaze" /></a>
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
@ -79,9 +87,9 @@ ## Individuals
# Cloud
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
If you do not want to self-host Coolify, there is a paid cloud version available: [app.coolify.io](https://app.coolify.io)
For more information & pricing, take a look at our landing page [here](https://coolify.io).
For more information & pricing, take a look at our landing page [coolify.io](https://coolify.io).
## Why should I use the Cloud version?
The recommended way to use Coolify is to have one server for Coolify and one (or more) for the resources you are deploying. A server is around 4-5$/month.
@ -105,7 +113,7 @@ # Recognitions
</a>
</p>
<a href="https://www.producthunt.com/posts/coolify?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-coolify" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=338273&theme=light" alt="Coolify - An&#0032;open&#0045;source&#0032;&#0038;&#0032;self&#0045;hostable&#0032;Heroku&#0044;&#0032;Netlify&#0032;alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<a href="https://www.producthunt.com/posts/coolify?ref=badge-featured&utm_medium=badge&utm_souce=badge-coolify" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=338273&theme=light" alt="Coolify - An&#0032;open&#0045;source&#0032;&#0038;&#0032;self&#0045;hostable&#0032;Heroku&#0044;&#0032;Netlify&#0032;alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></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

@ -2,6 +2,7 @@
namespace App\Actions\Database;
use App\Events\DatabaseStatusChanged;
use App\Models\ServiceDatabase;
use App\Models\StandaloneClickhouse;
use App\Models\StandaloneDragonfly;
@ -28,5 +29,6 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
instant_remote_process(["docker rm -f {$uuid}-proxy"], $server);
$database->is_public = false;
$database->save();
DatabaseStatusChanged::dispatch();
}
}

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

View File

@ -16,12 +16,12 @@ class CreateNewUser implements CreatesNewUsers
/**
* Validate and create a newly registered user.
*
* @param array<string, string> $input
* @param array<string, string> $input
*/
public function create(array $input): User
{
$settings = InstanceSettings::get();
if (!$settings->is_registration_enabled) {
if (! $settings->is_registration_enabled) {
abort(403);
}
Validator::make($input, [
@ -66,6 +66,7 @@ public function create(array $input): User
}
// Set session variable
session(['currentTeam' => $user->currentTeam = $team]);
return $user;
}
}

View File

@ -14,7 +14,7 @@ class ResetUserPassword implements ResetsUserPasswords
/**
* Validate and reset the user's forgotten password.
*
* @param array<string, string> $input
* @param array<string, string> $input
*/
public function reset(User $user, array $input): void
{

View File

@ -14,7 +14,7 @@ class UpdateUserPassword implements UpdatesUserPasswords
/**
* Validate and update the user's password.
*
* @param array<string, string> $input
* @param array<string, string> $input
*/
public function update(User $user, array $input): void
{

View File

@ -13,7 +13,7 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
/**
* Validate and update the given user's profile information.
*
* @param array<string, string> $input
* @param array<string, string> $input
*/
public function update(User $user, array $input): void
{
@ -45,7 +45,7 @@ public function update(User $user, array $input): void
/**
* Update the given verified user's profile information.
*
* @param array<string, string> $input
* @param array<string, string> $input
*/
protected function updateVerifiedUser(User $user, array $input): void
{

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,21 +2,25 @@
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) {
instant_remote_process(['docker rm -f coolify-sentinel'], $server, false);
StopSentinel::run($server);
}
$metrics_history = $server->settings->metrics_history_days;
$refresh_rate = $server->settings->metrics_refresh_rate_seconds;
$token = $server->settings->metrics_token;
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"
"docker run --rm --pull always -d -e \"TOKEN={$token}\" -e \"SCHEDULER=true\" -e \"METRICS_HISTORY={$metrics_history}\" -e \"REFRESH_RATE={$refresh_rate}\" --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',
], $server, false);
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Actions\Server;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
class StopSentinel
{
use AsAction;
public function handle(Server $server)
{
instant_remote_process(['docker rm -f coolify-sentinel'], $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) {
@ -38,9 +41,6 @@ public function handle($manual_update = false)
}
$this->update();
} catch (\Throwable $e) {
ray('InstanceAutoUpdateJob failed');
ray($e->getMessage());
send_internal_notification('InstanceAutoUpdateJob failed: ' . $e->getMessage());
throw $e;
}
}
@ -49,15 +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);
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
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

@ -26,7 +26,6 @@ class ServicesGenerate extends Command
*/
public function handle()
{
// ray()->clearAll();
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
$files = array_filter($files, function ($file) {
return strpos($file, '.yaml') !== false;
@ -51,18 +50,20 @@ 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");
$documentation = collect(preg_grep('/^# documentation:/', explode("\n", $content)))->values();
if ($documentation->count() > 0) {
$documentation = str($documentation[0])->after('# documentation:')->trim()->value();
$documentation = str($documentation)->append('?utm_source=coolify.io');
} else {
$documentation = 'https://coolify.io/docs';
}
@ -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,17 +4,14 @@
use App\Jobs\CheckLogDrainContainerJob;
use App\Jobs\CleanupInstanceStuffsJob;
use App\Jobs\DatabaseBackupJob;
use App\Jobs\ScheduledTaskJob;
use App\Jobs\InstanceAutoUpdateJob;
use App\Jobs\ContainerStatusJob;
use App\Jobs\DatabaseBackupJob;
use App\Jobs\PullCoolifyImageJob;
use App\Jobs\PullHelperImageJob;
use App\Jobs\PullSentinelImageJob;
use App\Jobs\PullTemplatesAndVersions;
use App\Jobs\PullTemplatesFromCDN;
use App\Jobs\PullVersionsFromCDN;
use App\Jobs\ScheduledTaskJob;
use App\Jobs\ServerStatusJob;
use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledTask;
use App\Models\Server;
@ -25,6 +22,7 @@
class Kernel extends ConsoleKernel
{
private $all_servers;
protected function schedule(Schedule $schedule): void
{
$this->all_servers = Server::all();
@ -32,46 +30,44 @@ protected function schedule(Schedule $schedule): void
// Instance Jobs
$schedule->command('horizon:snapshot')->everyMinute();
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
$schedule->job(new PullVersionsFromCDN)->everyTenMinutes()->onOneServer();
$schedule->job(new PullTemplatesFromCDN)->everyTwoHours()->onOneServer();
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
// Server Jobs
$this->check_scheduled_backups($schedule);
$this->check_resources($schedule);
$this->check_scheduled_backups($schedule);
// $this->pull_helper_image($schedule);
$this->check_scheduled_tasks($schedule);
$schedule->command('uploads:clear')->everyTwoMinutes();
} else {
// Instance Jobs
$schedule->command('horizon:snapshot')->everyFiveMinutes();
$schedule->command('cleanup:unreachable-servers')->daily();
$schedule->job(new PullVersionsFromCDN)->everyTenMinutes()->onOneServer();
$schedule->job(new PullTemplatesFromCDN)->everyTwoHours()->onOneServer();
$schedule->job(new PullCoolifyImageJob)->everyTenMinutes()->onOneServer();
$schedule->job(new PullTemplatesFromCDN)->everyThirtyMinutes()->onOneServer();
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
// Server Jobs
$this->instance_auto_update($schedule);
$this->check_scheduled_backups($schedule);
$this->check_resources($schedule);
$this->pull_helper_image($schedule);
$this->pull_images($schedule);
$this->check_scheduled_tasks($schedule);
$schedule->command('cleanup:database --yes')->daily();
$schedule->command('uploads:clear')->everyTwoMinutes();
}
}
private function pull_helper_image($schedule)
private function pull_images($schedule)
{
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
foreach ($servers as $server) {
if (config('coolify.is_sentinel_enabled')) {
if ($server->isMetricsEnabled()) {
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
}
$schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
}
}
private function check_resources($schedule)
{
if (isCloud()) {
@ -93,16 +89,7 @@ private function check_resources($schedule)
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
}
}
private function instance_auto_update($schedule)
{
if (isDev() || isCloud()) {
return;
}
$settings = InstanceSettings::get();
if ($settings->is_auto_update_enabled) {
$schedule->job(new InstanceAutoUpdateJob)->everyTenMinutes()->onOneServer();
}
}
private function check_scheduled_backups($schedule)
{
$scheduled_backups = ScheduledDatabaseBackup::all();
@ -110,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;
}
@ -141,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) {
@ -167,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

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

View File

@ -9,8 +9,7 @@
class ServerMetadata extends Data
{
public function __construct(
public ?ProxyTypes $type,
public ?ProxyTypes $type,
public ?ProxyStatus $status
) {
}
) {}
}

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,8 +9,6 @@
class ProxyStarted
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public $data)
{
}
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

@ -4,7 +4,4 @@
use Exception;
class ProcessException extends Exception
{
}
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

@ -20,25 +20,26 @@ public function manual(Request $request)
$epoch = now()->valueOf();
$data = [
'attributes' => $request->attributes->all(),
'request' => $request->request->all(),
'query' => $request->query->all(),
'server' => $request->server->all(),
'files' => $request->files->all(),
'cookies' => $request->cookies->all(),
'headers' => $request->headers->all(),
'content' => $request->getContent(),
'request' => $request->request->all(),
'query' => $request->query->all(),
'server' => $request->server->all(),
'files' => $request->files->all(),
'cookies' => $request->cookies->all(),
'headers' => $request->headers->all(),
'content' => $request->getContent(),
];
$json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Bitbicket::manual_bitbucket", $json);
return;
}
$return_payloads = collect([]);
$payload = $request->collect();
$headers = $request->headers->all();
$x_bitbucket_token = data_get($headers, 'x-hub-signature.0', "");
$x_bitbucket_event = data_get($headers, 'x-event-key.0', "");
$x_bitbucket_token = data_get($headers, 'x-hub-signature.0', '');
$x_bitbucket_event = data_get($headers, 'x-event-key.0', '');
$handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']);
if (!$handled_events->contains($x_bitbucket_event)) {
if (! $handled_events->contains($x_bitbucket_event)) {
return response([
'status' => 'failed',
'message' => 'Nothing to do. Event not handled.',
@ -48,13 +49,13 @@ public function manual(Request $request)
$branch = data_get($payload, 'push.changes.0.new.name');
$full_name = data_get($payload, 'repository.full_name');
$commit = data_get($payload, 'push.changes.0.new.target.hash');
if (!$branch) {
if (! $branch) {
return response([
'status' => 'failed',
'message' => 'Nothing to do. No branch found in the request.',
]);
}
ray('Manual webhook bitbucket push event with branch: ' . $branch);
ray('Manual webhook bitbucket push event with branch: '.$branch);
}
if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
$branch = data_get($payload, 'pullrequest.destination.branch.name');
@ -76,30 +77,32 @@ public function manual(Request $request)
$webhook_secret = data_get($application, 'manual_webhook_secret_bitbucket');
$payload = $request->getContent();
list($algo, $hash) = explode('=', $x_bitbucket_token, 2);
[$algo, $hash] = explode('=', $x_bitbucket_token, 2);
$payloadHash = hash_hmac($algo, $payload, $webhook_secret);
if (!hash_equals($hash, $payloadHash) && !isDev()) {
if (! hash_equals($hash, $payloadHash) && ! isDev()) {
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
'message' => 'Invalid signature.',
]);
ray('Invalid signature');
continue;
}
$isFunctional = $application->destination->server->isFunctional();
if (!$isFunctional) {
if (! $isFunctional) {
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
'message' => 'Server is not functional.',
]);
ray('Server is not functional: ' . $application->destination->server->name);
ray('Server is not functional: '.$application->destination->server->name);
continue;
}
if ($x_bitbucket_event === 'repo:push') {
if ($application->isDeployable()) {
ray('Deploying ' . $application->name . ' with branch ' . $branch);
ray('Deploying '.$application->name.' with branch '.$branch);
$deployment_uuid = new Cuid2(7);
queue_application_deployment(
application: $application,
@ -123,16 +126,27 @@ 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) {
ApplicationPreview::create([
'git_type' => 'bitbucket',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
if (! $found) {
if ($application->build_pack === 'dockercompose') {
$pr_app = ApplicationPreview::create([
'git_type' => 'bitbucket',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
'docker_compose_domains' => $application->docker_compose_domains,
]);
$pr_app->generate_preview_fqdn_compose();
} else {
ApplicationPreview::create([
'git_type' => 'bitbucket',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
}
queue_application_deployment(
application: $application,
@ -178,9 +192,11 @@ public function manual(Request $request)
}
}
ray($return_payloads);
return response($return_payloads);
} catch (Exception $e) {
ray($e);
return handleError($e);
}
}

View File

@ -27,20 +27,22 @@ public function manual(Request $request)
})->first();
if ($gitea_delivery_found) {
ray('Webhook already found');
return;
}
$data = [
'attributes' => $request->attributes->all(),
'request' => $request->request->all(),
'query' => $request->query->all(),
'server' => $request->server->all(),
'files' => $request->files->all(),
'cookies' => $request->cookies->all(),
'headers' => $request->headers->all(),
'content' => $request->getContent(),
'request' => $request->request->all(),
'query' => $request->query->all(),
'server' => $request->server->all(),
'files' => $request->files->all(),
'cookies' => $request->cookies->all(),
'headers' => $request->headers->all(),
'content' => $request->getContent(),
];
$json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitea::manual_{$x_gitea_delivery}", $json);
return;
}
$x_gitea_event = Str::lower($request->header('X-Gitea-Event'));
@ -66,7 +68,7 @@ public function manual(Request $request)
$modified_files = data_get($payload, 'commits.*.modified');
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
ray($changed_files);
ray('Manual Webhook Gitea Push Event with branch: ' . $branch);
ray('Manual Webhook Gitea Push Event with branch: '.$branch);
}
if ($x_gitea_event === 'pull_request') {
$action = data_get($payload, 'action');
@ -75,9 +77,9 @@ public function manual(Request $request)
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
$branch = data_get($payload, 'pull_request.head.ref');
$base_branch = data_get($payload, 'pull_request.base.ref');
ray('Webhook Gitea Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
ray('Webhook Gitea Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id);
}
if (!$branch) {
if (! $branch) {
return response('Nothing to do. No branch found in the request.');
}
$applications = Application::where('git_repository', 'like', "%$full_name%");
@ -96,29 +98,31 @@ public function manual(Request $request)
foreach ($applications as $application) {
$webhook_secret = data_get($application, 'manual_webhook_secret_gitea');
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
if (!hash_equals($x_hub_signature_256, $hmac) && !isDev()) {
if (! hash_equals($x_hub_signature_256, $hmac) && ! isDev()) {
ray('Invalid signature');
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
'message' => 'Invalid signature.',
]);
continue;
}
$isFunctional = $application->destination->server->isFunctional();
if (!$isFunctional) {
if (! $isFunctional) {
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
'message' => 'Server is not functional.',
]);
continue;
}
if ($x_gitea_event === 'push') {
if ($application->isDeployable()) {
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
ray('Deploying ' . $application->name . ' with branch ' . $branch);
ray('Deploying '.$application->name.' with branch '.$branch);
$deployment_uuid = new Cuid2(7);
queue_application_deployment(
application: $application,
@ -160,13 +164,25 @@ 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) {
ApplicationPreview::create([
'git_type' => 'gitea',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
if (! $found) {
if ($application->build_pack === 'dockercompose') {
$pr_app = ApplicationPreview::create([
'git_type' => 'gitea',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
'docker_compose_domains' => $application->docker_compose_domains,
]);
$pr_app->generate_preview_fqdn_compose();
} else {
ApplicationPreview::create([
'git_type' => 'gitea',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
}
queue_application_deployment(
application: $application,
@ -213,9 +229,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,20 +33,22 @@ public function manual(Request $request)
})->first();
if ($github_delivery_found) {
ray('Webhook already found');
return;
}
$data = [
'attributes' => $request->attributes->all(),
'request' => $request->request->all(),
'query' => $request->query->all(),
'server' => $request->server->all(),
'files' => $request->files->all(),
'cookies' => $request->cookies->all(),
'headers' => $request->headers->all(),
'content' => $request->getContent(),
'request' => $request->request->all(),
'query' => $request->query->all(),
'server' => $request->server->all(),
'files' => $request->files->all(),
'cookies' => $request->cookies->all(),
'headers' => $request->headers->all(),
'content' => $request->getContent(),
];
$json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::manual_{$x_github_delivery}", $json);
return;
}
$x_github_event = Str::lower($request->header('X-GitHub-Event'));
@ -71,7 +73,7 @@ public function manual(Request $request)
$removed_files = data_get($payload, 'commits.*.removed');
$modified_files = data_get($payload, 'commits.*.modified');
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
ray('Manual Webhook GitHub Push Event with branch: ' . $branch);
ray('Manual Webhook GitHub Push Event with branch: '.$branch);
}
if ($x_github_event === 'pull_request') {
$action = data_get($payload, 'action');
@ -80,9 +82,9 @@ public function manual(Request $request)
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
$branch = data_get($payload, 'pull_request.head.ref');
$base_branch = data_get($payload, 'pull_request.base.ref');
ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
ray('Webhook GitHub Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id);
}
if (!$branch) {
if (! $branch) {
return response('Nothing to do. No branch found in the request.');
}
$applications = Application::where('git_repository', 'like', "%$full_name%");
@ -101,29 +103,31 @@ public function manual(Request $request)
foreach ($applications as $application) {
$webhook_secret = data_get($application, 'manual_webhook_secret_github');
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
if (!hash_equals($x_hub_signature_256, $hmac) && !isDev()) {
if (! hash_equals($x_hub_signature_256, $hmac) && ! isDev()) {
ray('Invalid signature');
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
'message' => 'Invalid signature.',
]);
continue;
}
$isFunctional = $application->destination->server->isFunctional();
if (!$isFunctional) {
if (! $isFunctional) {
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
'message' => 'Server is not functional.',
]);
continue;
}
if ($x_github_event === 'push') {
if ($application->isDeployable()) {
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
ray('Deploying ' . $application->name . ' with branch ' . $branch);
ray('Deploying '.$application->name.' with branch '.$branch);
$deployment_uuid = new Cuid2(7);
queue_application_deployment(
application: $application,
@ -165,13 +169,24 @@ 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) {
ApplicationPreview::create([
'git_type' => 'github',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
if (! $found) {
if ($application->build_pack === 'dockercompose') {
$pr_app = ApplicationPreview::create([
'git_type' => 'github',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
'docker_compose_domains' => $application->docker_compose_domains,
]);
$pr_app->generate_preview_fqdn_compose();
} else {
ApplicationPreview::create([
'git_type' => 'github',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
}
queue_application_deployment(
application: $application,
@ -218,12 +233,15 @@ public function manual(Request $request)
}
}
ray($return_payloads);
return response($return_payloads);
} catch (Exception $e) {
ray($e->getMessage());
return handleError($e);
}
}
public function normal(Request $request)
{
try {
@ -239,20 +257,22 @@ public function normal(Request $request)
})->first();
if ($github_delivery_found) {
ray('Webhook already found');
return;
}
$data = [
'attributes' => $request->attributes->all(),
'request' => $request->request->all(),
'query' => $request->query->all(),
'server' => $request->server->all(),
'files' => $request->files->all(),
'cookies' => $request->cookies->all(),
'headers' => $request->headers->all(),
'content' => $request->getContent(),
'request' => $request->request->all(),
'query' => $request->query->all(),
'server' => $request->server->all(),
'files' => $request->files->all(),
'cookies' => $request->cookies->all(),
'headers' => $request->headers->all(),
'content' => $request->getContent(),
];
$json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::normal_{$x_github_delivery}", $json);
return;
}
$x_github_event = Str::lower($request->header('X-GitHub-Event'));
@ -270,7 +290,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 +300,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 +313,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 +322,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 +343,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 +399,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,
@ -410,11 +432,12 @@ public function normal(Request $request)
if ($action === 'closed' || $action === 'close') {
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if ($found) {
$container_name = generateApplicationContainerName($application, $pull_request_id);
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
ApplicationPullRequestUpdateJob::dispatchSync(application: $application, preview: $found, status: ProcessStatus::CLOSED);
$found->delete();
$container_name = generateApplicationContainerName($application, $pull_request_id);
// ray('Stopping container: ' . $container_name);
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
$return_payloads->push([
'application' => $application->name,
'status' => 'success',
@ -430,13 +453,15 @@ public function normal(Request $request)
}
}
}
ray($return_payloads);
return response($return_payloads);
} catch (Exception $e) {
ray($e->getMessage());
return handleError($e);
}
}
public function redirect(Request $request)
{
try {
@ -464,11 +489,13 @@ public function redirect(Request $request)
$github_app->webhook_secret = $webhook_secret;
$github_app->private_key_id = $private_key->id;
$github_app->save();
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
} catch (Exception $e) {
return handleError($e);
}
}
public function install(Request $request)
{
try {
@ -478,16 +505,17 @@ public function install(Request $request)
$epoch = now()->valueOf();
$data = [
'attributes' => $request->attributes->all(),
'request' => $request->request->all(),
'query' => $request->query->all(),
'server' => $request->server->all(),
'files' => $request->files->all(),
'cookies' => $request->cookies->all(),
'headers' => $request->headers->all(),
'content' => $request->getContent(),
'request' => $request->request->all(),
'query' => $request->query->all(),
'server' => $request->server->all(),
'files' => $request->files->all(),
'cookies' => $request->cookies->all(),
'headers' => $request->headers->all(),
'content' => $request->getContent(),
];
$json = json_encode($data);
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::install_{$installation_id}", $json);
return;
}
$source = $request->get('source');
@ -497,6 +525,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

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

View File

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

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

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

@ -13,7 +13,7 @@ class RedirectIfAuthenticated
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{
@ -24,6 +24,7 @@ public function handle(Request $request, Closure $next, string ...$guards): Resp
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}

View File

@ -20,7 +20,7 @@ class TrustProxies extends Middleware
* @var int
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |

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(
@ -24,32 +25,34 @@ public function __construct(
public ApplicationPreview $preview,
public ProcessStatus $status,
public ?string $deployment_uuid = null
) {
}
) {}
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) {
@ -59,7 +62,8 @@ public function handle()
}
} catch (\Throwable $e) {
ray($e);
throw $e;
return $e;
}
}
@ -82,6 +86,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,12 @@
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 __construct(public Server $server) {}
public function middleware(): array
{
return [(new WithoutOverlapping($this->server->id))->dontRelease()];
@ -31,6 +30,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 +40,16 @@ public function healthcheck()
return false;
}
}
public function handle(): void
public function handle()
{
// ray("checking log drain statuses for {$this->server->id}");
try {
if (!$this->server->isFunctional()) {
if (! $this->server->isFunctional()) {
return;
};
$containers = instant_remote_process(["docker container ls -q"], $this->server, false);
if (!$containers) {
}
$containers = instant_remote_process(['docker container ls -q'], $this->server, false);
if (! $containers) {
return;
}
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server);
@ -57,7 +58,7 @@ public function handle(): void
$foundLogDrainContainer = $containers->filter(function ($value, $key) {
return data_get($value, 'Name') === '/coolify-log-drain';
})->first();
if (!$foundLogDrainContainer || !$this->healthcheck()) {
if (! $foundLogDrainContainer || ! $this->healthcheck()) {
ray('Log drain container not found or unhealthy. Restarting...');
InstallLogDrain::run($this->server);
Sleep::for(10)->seconds();
@ -66,9 +67,10 @@ public function handle(): void
$this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
$this->server->update(['log_drain_notification_sent' => false]);
}
return;
}
if (!$this->server->log_drain_notification_sent) {
if (! $this->server->log_drain_notification_sent) {
ray('Log drain container still unhealthy. Sending notification...');
// $this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
$this->server->update(['log_drain_notification_sent' => true]);
@ -80,9 +82,12 @@ public function handle(): void
}
}
} catch (\Throwable $e) {
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());
handleError($e);
return handleError($e);
}
}
}

View File

@ -10,20 +10,18 @@
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class CheckResaleLicenseJob implements ShouldQueue, ShouldBeEncrypted
class CheckResaleLicenseJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct()
{
}
public function __construct() {}
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,29 +11,27 @@
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;
public function __construct(public Server $server)
{
}
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,14 +12,11 @@
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;
public function __construct()
{
}
public function __construct() {}
// public function uniqueId(): string
// {
@ -31,13 +28,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 +46,7 @@ private function cleanup_waitlist()
$item->delete();
}
}
private function cleanup_invitation_link()
{
$invitation = TeamInvitation::all();

View File

@ -12,18 +12,19 @@
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 __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;
@ -20,11 +20,10 @@ class CoolifyTask implements ShouldQueue, ShouldBeEncrypted
*/
public function __construct(
public Activity $activity,
public bool $ignore_errors = false,
public bool $ignore_errors = false,
public $call_event_on_finish = null,
public $call_event_data = null
) {
}
) {}
/**
* Execute the job.
@ -35,7 +34,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;

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