Merge branch 'next' into es-translation
This commit is contained in:
		
							
								
								
									
										25
									
								
								.github/workflows/fix-php-code-style-issues.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								.github/workflows/fix-php-code-style-issues.yml
									
									
									
									
										vendored
									
									
										Normal 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 | ||||
							
								
								
									
										52
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								README.md
									
									
									
									
									
								
							| @@ -5,13 +5,13 @@ | ||||
| 
 | ||||
| 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 @@ You can find the installation script source [here](./scripts/install.sh). | ||||
| 
 | ||||
| # 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 @@ Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)! | ||||
| 
 | ||||
| # 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 @@ By subscribing to the cloud version, you get the Coolify server for the same pri | ||||
| </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 open-source & self-hostable Heroku, Netlify 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 open-source & self-hostable Heroku, Netlify 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> | ||||
| 
 | ||||
|   | ||||
| @@ -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; | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -9,6 +9,7 @@ use Lorisleiva\Actions\Concerns\AsAction; | ||||
| class StopApplicationOneServer | ||||
| { | ||||
|     use AsAction; | ||||
| 
 | ||||
|     public function handle(Application $application, Server $server) | ||||
|     { | ||||
|         if ($application->destination->server->isSwarm()) { | ||||
| @@ -32,6 +33,7 @@ class StopApplicationOneServer | ||||
|             } | ||||
|         } catch (\Exception $e) { | ||||
|             ray($e->getMessage()); | ||||
| 
 | ||||
|             return $e->getMessage(); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -14,6 +14,7 @@ use Spatie\Activitylog\Models\Activity; | ||||
| class PrepareCoolifyTask | ||||
| { | ||||
|     protected Activity $activity; | ||||
| 
 | ||||
|     protected CoolifyTaskArgs $remoteProcessArgs; | ||||
| 
 | ||||
|     public function __construct(CoolifyTaskArgs $remoteProcessArgs) | ||||
| @@ -28,12 +29,12 @@ class PrepareCoolifyTask | ||||
|                 ->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 @@ class PrepareCoolifyTask | ||||
|         $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; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -69,7 +69,7 @@ class RunRemoteProcess | ||||
|         return collect($decoded) | ||||
|             ->sortBy(fn ($i) => $i['order']) | ||||
|             ->map(fn ($i) => $i['output']) | ||||
|             ->implode(""); | ||||
|             ->implode(''); | ||||
|     } | ||||
| 
 | ||||
|     public function __invoke(): ProcessResult | ||||
| @@ -116,7 +116,7 @@ class RunRemoteProcess | ||||
|             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 @@ class RunRemoteProcess | ||||
|                 ray($e); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $processResult; | ||||
|     } | ||||
| 
 | ||||
| @@ -182,6 +183,7 @@ class RunRemoteProcess | ||||
|         if ($description === null || count($description) === 0) { | ||||
|             return 1; | ||||
|         } | ||||
| 
 | ||||
|         return end($description)['order'] + 1; | ||||
|     } | ||||
| 
 | ||||
|   | ||||
| @@ -4,22 +4,23 @@ namespace App\Actions\Database; | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| @@ -57,7 +58,7 @@ class StartClickhouse | ||||
|                         '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,15 +66,15 @@ class StartClickhouse | ||||
|                     '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)) { | ||||
|             data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset); | ||||
| @@ -82,10 +83,10 @@ class StartClickhouse | ||||
|             $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 @@ class StartClickhouse | ||||
|         $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'); | ||||
|     } | ||||
| 
 | ||||
| @@ -125,6 +127,7 @@ class StartClickhouse | ||||
|                 $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes; | ||||
|     } | ||||
| 
 | ||||
| @@ -141,6 +144,7 @@ class StartClickhouse | ||||
|                 'external' => false, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes_names; | ||||
|     } | ||||
| 
 | ||||
|   | ||||
| @@ -101,7 +101,7 @@ class StartDatabaseProxy | ||||
|        } | ||||
|     } | ||||
|     EOF; | ||||
|         $dockerfile = <<< EOF | ||||
|         $dockerfile = <<< 'EOF' | ||||
|     FROM nginx:stable-alpine | ||||
| 
 | ||||
|     COPY nginx.conf /etc/nginx/nginx.conf | ||||
| @@ -113,7 +113,7 @@ class StartDatabaseProxy | ||||
|                         'context' => $configuration_dir, | ||||
|                         'dockerfile' => 'Dockerfile', | ||||
|                     ], | ||||
|                     'image' => "nginx:stable-alpine", | ||||
|                     'image' => 'nginx:stable-alpine', | ||||
|                     'container_name' => $proxyContainerName, | ||||
|                     'restart' => RESTART_MODE, | ||||
|                     'ports' => [ | ||||
| @@ -130,17 +130,17 @@ class StartDatabaseProxy | ||||
|                         '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); | ||||
|   | ||||
| @@ -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) | ||||
|     { | ||||
| @@ -48,7 +48,7 @@ class StartDragonfly | ||||
|                         $this->database->destination->network, | ||||
|                     ], | ||||
|                     'ulimits' => [ | ||||
|                         'memlock'=> '-1' | ||||
|                         'memlock' => '-1', | ||||
|                     ], | ||||
|                     'labels' => [ | ||||
|                         'coolify.managed' => 'true', | ||||
| @@ -58,7 +58,7 @@ class StartDragonfly | ||||
|                         '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,15 +66,15 @@ class StartDragonfly | ||||
|                     '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)) { | ||||
|             data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset); | ||||
| @@ -83,10 +83,10 @@ class StartDragonfly | ||||
|             $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 @@ class StartDragonfly | ||||
|         $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'); | ||||
|     } | ||||
| 
 | ||||
| @@ -126,6 +127,7 @@ class StartDragonfly | ||||
|                 $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes; | ||||
|     } | ||||
| 
 | ||||
| @@ -142,6 +144,7 @@ class StartDragonfly | ||||
|                 'external' => false, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes_names; | ||||
|     } | ||||
| 
 | ||||
|   | ||||
| @@ -5,17 +5,18 @@ namespace App\Actions\Database; | ||||
| 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) | ||||
|     { | ||||
| @@ -56,7 +57,7 @@ class StartKeydb | ||||
|                         '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,15 +65,15 @@ class StartKeydb | ||||
|                     '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)) { | ||||
|             data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset); | ||||
| @@ -81,10 +82,10 @@ class StartKeydb | ||||
|             $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) { | ||||
| @@ -119,6 +120,7 @@ class StartKeydb | ||||
|         $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'); | ||||
|     } | ||||
| 
 | ||||
| @@ -133,6 +135,7 @@ class StartKeydb | ||||
|                 $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes; | ||||
|     } | ||||
| 
 | ||||
| @@ -149,6 +152,7 @@ class StartKeydb | ||||
|                 'external' => false, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes_names; | ||||
|     } | ||||
| 
 | ||||
| @@ -165,6 +169,7 @@ class StartKeydb | ||||
| 
 | ||||
|         return $environment_variables->all(); | ||||
|     } | ||||
| 
 | ||||
|     private function add_custom_keydb() | ||||
|     { | ||||
|         if (is_null($this->database->keydb_conf) || empty($this->database->keydb_conf)) { | ||||
|   | ||||
| @@ -4,15 +4,17 @@ namespace App\Actions\Database; | ||||
| 
 | ||||
| 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) | ||||
| @@ -46,11 +48,11 @@ class StartMariadb | ||||
|                         '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,15 +60,15 @@ class StartMariadb | ||||
|                     '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)) { | ||||
|             data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset); | ||||
| @@ -75,10 +77,10 @@ class StartMariadb | ||||
|             $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 +114,7 @@ class StartMariadb | ||||
|         $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'); | ||||
|     } | ||||
| 
 | ||||
| @@ -126,6 +129,7 @@ class StartMariadb | ||||
|                 $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes; | ||||
|     } | ||||
| 
 | ||||
| @@ -142,6 +146,7 @@ class StartMariadb | ||||
|                 'external' => false, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes_names; | ||||
|     } | ||||
| 
 | ||||
| @@ -166,8 +171,10 @@ class StartMariadb | ||||
|         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)) { | ||||
|   | ||||
| @@ -4,22 +4,24 @@ namespace App\Actions\Database; | ||||
| 
 | ||||
| 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; | ||||
| @@ -51,14 +53,14 @@ class StartMongodb | ||||
|                     ], | ||||
|                     '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,15 +68,15 @@ class StartMongodb | ||||
|                     '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)) { | ||||
|             data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset); | ||||
| @@ -83,10 +85,10 @@ class StartMongodb | ||||
|             $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) { | ||||
| @@ -129,6 +131,7 @@ class StartMongodb | ||||
|         $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'); | ||||
|     } | ||||
| 
 | ||||
| @@ -143,6 +146,7 @@ class StartMongodb | ||||
|                 $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes; | ||||
|     } | ||||
| 
 | ||||
| @@ -159,6 +163,7 @@ class StartMongodb | ||||
|                 'external' => false, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes_names; | ||||
|     } | ||||
| 
 | ||||
| @@ -180,8 +185,10 @@ class StartMongodb | ||||
|         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 @@ class StartMongodb | ||||
|         $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}\"}]});"; | ||||
|   | ||||
| @@ -4,15 +4,17 @@ namespace App\Actions\Database; | ||||
| 
 | ||||
| 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) | ||||
| @@ -46,11 +48,11 @@ class StartMysql | ||||
|                         '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,15 +60,15 @@ class StartMysql | ||||
|                     '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)) { | ||||
|             data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset); | ||||
| @@ -75,10 +77,10 @@ class StartMysql | ||||
|             $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 +114,7 @@ class StartMysql | ||||
|         $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'); | ||||
|     } | ||||
| 
 | ||||
| @@ -126,6 +129,7 @@ class StartMysql | ||||
|                 $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes; | ||||
|     } | ||||
| 
 | ||||
| @@ -142,6 +146,7 @@ class StartMysql | ||||
|                 'external' => false, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes_names; | ||||
|     } | ||||
| 
 | ||||
| @@ -166,8 +171,10 @@ class StartMysql | ||||
|         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)) { | ||||
|   | ||||
| @@ -4,16 +4,19 @@ namespace App\Actions\Database; | ||||
| 
 | ||||
| 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) | ||||
| @@ -25,7 +28,7 @@ class StartPostgresql | ||||
|         $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 @@ class StartPostgresql | ||||
|                     ], | ||||
|                     '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,15 +67,15 @@ class StartPostgresql | ||||
|                     '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)) { | ||||
|             data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset); | ||||
| @@ -81,10 +84,10 @@ class StartPostgresql | ||||
|             $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) { | ||||
| @@ -133,6 +136,7 @@ class StartPostgresql | ||||
|         $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'); | ||||
|     } | ||||
| 
 | ||||
| @@ -147,6 +151,7 @@ class StartPostgresql | ||||
|                 $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes; | ||||
|     } | ||||
| 
 | ||||
| @@ -163,6 +168,7 @@ class StartPostgresql | ||||
|                 'external' => false, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes_names; | ||||
|     } | ||||
| 
 | ||||
| @@ -187,6 +193,7 @@ class StartPostgresql | ||||
|         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 @@ class StartPostgresql | ||||
|             $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)) { | ||||
|   | ||||
| @@ -5,17 +5,18 @@ namespace App\Actions\Database; | ||||
| 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) | ||||
|     { | ||||
| @@ -55,12 +56,12 @@ class StartRedis | ||||
|                         '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,15 +69,15 @@ class StartRedis | ||||
|                     '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)) { | ||||
|             data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset); | ||||
| @@ -85,10 +86,10 @@ class StartRedis | ||||
|             $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) { | ||||
| @@ -123,6 +124,7 @@ class StartRedis | ||||
|         $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,6 +139,7 @@ class StartRedis | ||||
|                 $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes; | ||||
|     } | ||||
| 
 | ||||
| @@ -153,6 +156,7 @@ class StartRedis | ||||
|                 'external' => false, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $local_persistent_volumes_names; | ||||
|     } | ||||
| 
 | ||||
| @@ -169,6 +173,7 @@ class StartRedis | ||||
| 
 | ||||
|         return $environment_variables->all(); | ||||
|     } | ||||
| 
 | ||||
|     private function add_custom_redis() | ||||
|     { | ||||
|         if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) { | ||||
|   | ||||
| @@ -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 @@ class StopDatabaseProxy | ||||
|         instant_remote_process(["docker rm -f {$uuid}-proxy"], $server); | ||||
|         $database->is_public = false; | ||||
|         $database->save(); | ||||
|         DatabaseStatusChanged::dispatch(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -17,7 +17,9 @@ use Lorisleiva\Actions\Concerns\AsAction; | ||||
| class GetContainersStatus | ||||
| { | ||||
|     use AsAction; | ||||
| 
 | ||||
|     public $applications; | ||||
| 
 | ||||
|     public $server; | ||||
| 
 | ||||
|     public function handle(Server $server) | ||||
| @@ -28,7 +30,7 @@ class GetContainersStatus | ||||
|         $this->server = $server; | ||||
|         if (! $this->server->isFunctional()) { | ||||
|             return 'Server is not ready.'; | ||||
|         }; | ||||
|         } | ||||
|         $this->applications = $this->server->applications(); | ||||
|         $skip_these_applications = collect([]); | ||||
|         foreach ($this->applications as $application) { | ||||
| @@ -239,7 +241,7 @@ class GetContainersStatus | ||||
|                 $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 @@ class GetContainersStatus | ||||
|                 $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 @@ class GetContainersStatus | ||||
|                 $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 @@ class GetContainersStatus | ||||
|                 $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; | ||||
|                 } | ||||
| @@ -351,9 +353,11 @@ class GetContainersStatus | ||||
|         } 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,7 +365,7 @@ class GetContainersStatus | ||||
|             $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); | ||||
|             $containers = instant_remote_process(['docker container ls -q'], $this->server, false); | ||||
|             if (! $containers) { | ||||
|                 return; | ||||
|             } | ||||
| @@ -390,6 +394,7 @@ class GetContainersStatus | ||||
|                             data_set($container, 'State.Health.Status', 'unhealthy'); | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     return $container; | ||||
|                 }); | ||||
|             } | ||||
| @@ -569,7 +574,7 @@ class GetContainersStatus | ||||
|             $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 @@ class GetContainersStatus | ||||
|             $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 @@ class GetContainersStatus | ||||
|             $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 @@ class GetContainersStatus | ||||
|             $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; | ||||
|             } | ||||
|   | ||||
| @@ -66,6 +66,7 @@ class CreateNewUser implements CreatesNewUsers | ||||
|         } | ||||
|         // Set session variable
 | ||||
|         session(['currentTeam' => $user->currentTeam = $team]); | ||||
| 
 | ||||
|         return $user; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,10 +6,10 @@ use App\Models\InstanceSettings; | ||||
| use Illuminate\Support\Facades\Http; | ||||
| use Lorisleiva\Actions\Concerns\AsAction; | ||||
| 
 | ||||
| 
 | ||||
| class CheckResaleLicense | ||||
| { | ||||
|     use AsAction; | ||||
| 
 | ||||
|     public function handle() | ||||
|     { | ||||
|         try { | ||||
| @@ -18,6 +18,7 @@ class CheckResaleLicense | ||||
|                 $settings->update([ | ||||
|                     'is_resale_license_active' => true, | ||||
|                 ]); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             // if (!$settings->resale_license) {
 | ||||
| @@ -38,6 +39,7 @@ class CheckResaleLicense | ||||
|                 $settings->update([ | ||||
|                     'is_resale_license_active' => true, | ||||
|                 ]); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             $data = Http::withHeaders([ | ||||
| @@ -51,6 +53,7 @@ class CheckResaleLicense | ||||
|                 $settings->update([ | ||||
|                     'is_resale_license_active' => true, | ||||
|                 ]); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             if (data_get($data, 'license_key.status') === 'active') { | ||||
|   | ||||
| @@ -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(); | ||||
| @@ -26,8 +27,9 @@ class CheckConfiguration | ||||
|             $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"); | ||||
|             throw new \Exception('Could not generate proxy configuration'); | ||||
|         } | ||||
| 
 | ||||
|         return $proxy_configuration; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ use Lorisleiva\Actions\Concerns\AsAction; | ||||
| class CheckProxy | ||||
| { | ||||
|     use AsAction; | ||||
| 
 | ||||
|     public function handle(Server $server, $fromUI = false) | ||||
|     { | ||||
|         if (! $server->isFunctional()) { | ||||
| @@ -18,6 +19,7 @@ class CheckProxy | ||||
|                 $server->proxy = null; | ||||
|                 $server->save(); | ||||
|             } | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
|         $proxyType = $server->proxyType(); | ||||
| @@ -30,7 +32,7 @@ class CheckProxy | ||||
|         } | ||||
|         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 @@ class CheckProxy | ||||
|             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 @@ class CheckProxy | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -11,6 +11,7 @@ use Spatie\Activitylog\Models\Activity; | ||||
| class StartProxy | ||||
| { | ||||
|     use AsAction; | ||||
| 
 | ||||
|     public function handle(Server $server, bool $async = true): string|Activity | ||||
|     { | ||||
|         try { | ||||
| @@ -22,7 +23,7 @@ class StartProxy | ||||
|             $proxy_path = $server->proxyPath(); | ||||
|             $configuration = CheckConfiguration::run($server); | ||||
|             if (! $configuration) { | ||||
|                 throw new \Exception("Configuration is not synced"); | ||||
|                 throw new \Exception('Configuration is not synced'); | ||||
|             } | ||||
|             SaveConfiguration::run($server, $configuration); | ||||
|             $docker_compose_yml_base64 = base64_encode($configuration); | ||||
| @@ -34,11 +35,11 @@ class StartProxy | ||||
|                     "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 @@ class StartProxy | ||||
|                     "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 @@ class StartProxy | ||||
|                 $server->proxy->set('type', $proxyType); | ||||
|                 $server->save(); | ||||
|                 ProxyStarted::dispatch($server); | ||||
| 
 | ||||
|                 return 'OK'; | ||||
|             } | ||||
|         } catch (\Throwable $e) { | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -9,18 +9,19 @@ use Symfony\Component\Yaml\Yaml; | ||||
| 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 @@ class ConfigureCloudflared | ||||
|             $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 @@ class ConfigureCloudflared | ||||
|             throw $e; | ||||
|         } finally { | ||||
|             $commands = collect([ | ||||
|                 "rm -fr /tmp/cloudflared", | ||||
|                 'rm -fr /tmp/cloudflared', | ||||
|             ]); | ||||
|             instant_remote_process($commands, $server); | ||||
|         } | ||||
|   | ||||
| @@ -2,13 +2,14 @@ | ||||
| 
 | ||||
| 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(); | ||||
| @@ -36,40 +37,41 @@ class InstallDocker | ||||
|         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', | ||||
|                 ]); | ||||
|             } 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', | ||||
|                 ]); | ||||
|             } 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 @@ class InstallDocker | ||||
|                 "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); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -2,12 +2,13 @@ | ||||
| 
 | ||||
| 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) { | ||||
| @@ -25,8 +26,9 @@ class InstallLogDrain | ||||
|             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); | ||||
|             } elseif ($type === 'newrelic') { | ||||
|                 if (! $server->settings->is_logdrain_newrelic_enabled) { | ||||
| @@ -63,7 +65,7 @@ class InstallLogDrain | ||||
|                 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 @@ class InstallLogDrain | ||||
|     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,7 +81,7 @@ class InstallLogDrain | ||||
|     Match               * | ||||
|     Host                otel.highlight.io | ||||
|     Port                24224 | ||||
| ");
 | ||||
| '); | ||||
|             } elseif ($type === 'axiom') { | ||||
|                 if (! $server->settings->is_logdrain_axiom_enabled) { | ||||
|                     throw new \Exception('Axiom log drain is not enabled.'); | ||||
| @@ -133,7 +135,7 @@ class InstallLogDrain | ||||
|     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 @@ services: | ||||
|     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. | ||||
| 
 | ||||
| @@ -191,7 +193,7 @@ Files: | ||||
|                 ]; | ||||
|             } 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 @@ Files: | ||||
|                 "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); | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										16
									
								
								app/Actions/Server/StopSentinel.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/Actions/Server/StopSentinel.php
									
									
									
									
									
										Normal 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); | ||||
|     } | ||||
| } | ||||
| @@ -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) | ||||
| @@ -38,9 +41,6 @@ class UpdateCoolify | ||||
|             } | ||||
|             $this->update(); | ||||
|         } catch (\Throwable $e) { | ||||
|             ray('InstanceAutoUpdateJob failed'); | ||||
|             ray($e->getMessage()); | ||||
|             send_internal_notification('InstanceAutoUpdateJob failed: ' . $e->getMessage()); | ||||
|             throw $e; | ||||
|         } | ||||
|     } | ||||
| @@ -49,15 +49,15 @@ class UpdateCoolify | ||||
|     { | ||||
|         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; | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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); | ||||
|         $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 @@ class StartService | ||||
|             } | ||||
|         } | ||||
|         $activity = remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged'); | ||||
| 
 | ||||
|         return $activity; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,12 +2,13 @@ | ||||
| 
 | ||||
| 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 { | ||||
| @@ -33,6 +34,7 @@ class StopService | ||||
|         } catch (\Exception $e) { | ||||
|             echo $e->getMessage(); | ||||
|             ray($e->getMessage()); | ||||
| 
 | ||||
|             return $e->getMessage(); | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -8,6 +8,7 @@ use Lorisleiva\Actions\Concerns\AsAction; | ||||
| class ComplexStatusCheck | ||||
| { | ||||
|     use AsAction; | ||||
| 
 | ||||
|     public function handle(Application $application) | ||||
|     { | ||||
|         $servers = $application->additional_servers; | ||||
| @@ -17,9 +18,11 @@ class ComplexStatusCheck | ||||
|             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 @@ class ComplexStatusCheck | ||||
|             } else { | ||||
|                 if ($is_main_server) { | ||||
|                     $application->update(['status' => 'exited:unhealthy']); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } else { | ||||
|                     $application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -8,17 +8,20 @@ use Lorisleiva\Actions\Concerns\AsAction; | ||||
| 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); | ||||
|     } | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
| 
 | ||||
| namespace App\Console\Commands; | ||||
| 
 | ||||
| use App\Models\Server; | ||||
| use App\Models\User; | ||||
| use Illuminate\Console\Command; | ||||
| 
 | ||||
| @@ -32,6 +31,7 @@ class AdminRemoveUser extends Command | ||||
|             $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 @@ class AdminRemoveUser extends Command | ||||
|             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 @@ class AdminRemoveUser extends Command | ||||
|         } catch (\Exception $e) { | ||||
|             $this->error('Failed to remove user.'); | ||||
|             $this->error($e->getMessage()); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ use Illuminate\Console\Command; | ||||
| class CleanupApplicationDeploymentQueue extends Command | ||||
| { | ||||
|     protected $signature = 'cleanup:application-deployment-queue {--team-id=}'; | ||||
| 
 | ||||
|     protected $description = 'CleanupApplicationDeploymentQueue'; | ||||
| 
 | ||||
|     public function handle() | ||||
| @@ -15,7 +16,7 @@ class CleanupApplicationDeploymentQueue extends Command | ||||
|         $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); | ||||
|   | ||||
| @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\DB; | ||||
| class CleanupDatabase extends Command | ||||
| { | ||||
|     protected $signature = 'cleanup:database {--yes}'; | ||||
| 
 | ||||
|     protected $description = 'Cleanup database'; | ||||
| 
 | ||||
|     public function handle() | ||||
|   | ||||
| @@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Redis; | ||||
| class CleanupQueue extends Command | ||||
| { | ||||
|     protected $signature = 'cleanup:queue'; | ||||
| 
 | ||||
|     protected $description = 'Cleanup Queue'; | ||||
| 
 | ||||
|     public function handle() | ||||
|   | ||||
| @@ -20,6 +20,7 @@ use Illuminate\Console\Command; | ||||
| class CleanupStuckedResources extends Command | ||||
| { | ||||
|     protected $signature = 'cleanup:stucked-resources'; | ||||
| 
 | ||||
|     protected $description = 'Cleanup Stucked Resources'; | ||||
| 
 | ||||
|     public function handle() | ||||
| @@ -28,6 +29,7 @@ class CleanupStuckedResources extends Command | ||||
|         echo "Running cleanup stucked resources.\n"; | ||||
|         $this->cleanup_stucked_resources(); | ||||
|     } | ||||
| 
 | ||||
|     private function cleanup_stucked_resources() | ||||
|     { | ||||
| 
 | ||||
| @@ -158,16 +160,19 @@ class CleanupStuckedResources extends Command | ||||
|                 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'; | ||||
|                     $application->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (! data_get($application, 'destination.server')) { | ||||
|                     echo 'Application without server: '.$application->name.'\n'; | ||||
|                     $application->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| @@ -180,16 +185,19 @@ class CleanupStuckedResources extends Command | ||||
|                 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'; | ||||
|                     $postgresql->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (! data_get($postgresql, 'destination.server')) { | ||||
|                     echo 'Postgresql without server: '.$postgresql->name.'\n'; | ||||
|                     $postgresql->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| @@ -202,16 +210,19 @@ class CleanupStuckedResources extends Command | ||||
|                 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'; | ||||
|                     $redis->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (! data_get($redis, 'destination.server')) { | ||||
|                     echo 'Redis without server: '.$redis->name.'\n'; | ||||
|                     $redis->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| @@ -225,16 +236,19 @@ class CleanupStuckedResources extends Command | ||||
|                 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'; | ||||
|                     $mongodb->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (! data_get($mongodb, 'destination.server')) { | ||||
|                     echo 'Mongodb without server:  '.$mongodb->name.'\n'; | ||||
|                     $mongodb->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| @@ -248,16 +262,19 @@ class CleanupStuckedResources extends Command | ||||
|                 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'; | ||||
|                     $mysql->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (! data_get($mysql, 'destination.server')) { | ||||
|                     echo 'Mysql without server: '.$mysql->name.'\n'; | ||||
|                     $mysql->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| @@ -271,16 +288,19 @@ class CleanupStuckedResources extends Command | ||||
|                 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'; | ||||
|                     $mariadb->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (! data_get($mariadb, 'destination.server')) { | ||||
|                     echo 'Mariadb without server: '.$mariadb->name.'\n'; | ||||
|                     $mariadb->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| @@ -294,16 +314,19 @@ class CleanupStuckedResources extends Command | ||||
|                 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'; | ||||
|                     $service->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (! data_get($service, 'server')) { | ||||
|                     echo 'Service without server: '.$service->name.'\n'; | ||||
|                     $service->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| @@ -316,6 +339,7 @@ class CleanupStuckedResources extends Command | ||||
|                 if (! data_get($service, 'service')) { | ||||
|                     echo 'ServiceApplication without service: '.$service->name.'\n'; | ||||
|                     $service->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| @@ -328,6 +352,7 @@ class CleanupStuckedResources extends Command | ||||
|                 if (! data_get($service, 'service')) { | ||||
|                     echo 'ServiceDatabase without service: '.$service->name.'\n'; | ||||
|                     $service->forceDelete(); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ use Illuminate\Console\Command; | ||||
| class CleanupUnreachableServers extends Command | ||||
| { | ||||
|     protected $signature = 'cleanup:unreachable-servers'; | ||||
| 
 | ||||
|     protected $description = 'Cleanup Unreachable Servers (7 days)'; | ||||
| 
 | ||||
|     public function handle() | ||||
| @@ -19,7 +20,7 @@ class CleanupUnreachableServers extends Command | ||||
|                 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', | ||||
|                 ]); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Process; | ||||
| class Dev extends Command | ||||
| { | ||||
|     protected $signature = 'dev:init'; | ||||
| 
 | ||||
|     protected $description = 'Init the app in dev mode'; | ||||
| 
 | ||||
|     public function handle() | ||||
|   | ||||
| @@ -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,7 +73,7 @@ class Emails extends Command | ||||
|         ); | ||||
|         $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)) { | ||||
|                 $this->email = text('Email Address to send to:'); | ||||
| @@ -82,12 +82,13 @@ class Emails extends Command | ||||
|         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; | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
|                 $emails = []; | ||||
| @@ -99,7 +100,7 @@ class Emails extends Command | ||||
|                     } | ||||
|                 } | ||||
|                 $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 @@ class Emails extends Command | ||||
|                         $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); | ||||
|                     } | ||||
|                 } | ||||
| @@ -246,6 +247,7 @@ class Emails extends Command | ||||
|                 $teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get(); | ||||
|                 if (! $teams || $teams->isEmpty()) { | ||||
|                     echo 'No teams found.'.PHP_EOL; | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
|                 $emails = []; | ||||
| @@ -257,7 +259,7 @@ class Emails extends Command | ||||
|                     } | ||||
|                 } | ||||
|                 $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); | ||||
|                 } | ||||
| @@ -296,7 +298,8 @@ class Emails extends Command | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|     private function sendEmail(string $email = null) | ||||
| 
 | ||||
|     private function sendEmail(?string $email = null) | ||||
|     { | ||||
|         if ($email) { | ||||
|             $this->email = $email; | ||||
|   | ||||
| @@ -7,7 +7,9 @@ use Illuminate\Console\Command; | ||||
| class Horizon extends Command | ||||
| { | ||||
|     protected $signature = 'start:horizon'; | ||||
| 
 | ||||
|     protected $description = 'Start Horizon'; | ||||
| 
 | ||||
|     public function handle() | ||||
|     { | ||||
|         if (config('coolify.is_horizon_enabled')) { | ||||
|   | ||||
| @@ -15,6 +15,7 @@ use Illuminate\Support\Facades\Http; | ||||
| 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 @@ class Init extends Command | ||||
|         if ($cleanup_deployments) { | ||||
|             echo "Running cleanup deployments.\n"; | ||||
|             $this->cleanup_in_progress_application_deployments(); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|         if ($full_cleanup) { | ||||
| @@ -52,6 +54,7 @@ class Init extends Command | ||||
|                     $settings->update(['is_auto_update_enabled' => false]); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|         $this->cleanup_stucked_helper_containers(); | ||||
| @@ -82,6 +85,7 @@ class Init extends Command | ||||
|             echo "Error in restoring coolify db backup: {$e->getMessage()}\n"; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function cleanup_stucked_helper_containers() | ||||
|     { | ||||
|         $servers = Server::all(); | ||||
| @@ -91,6 +95,7 @@ class Init extends Command | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function alive() | ||||
|     { | ||||
|         $id = config('app.id'); | ||||
| @@ -99,6 +104,7 @@ class Init extends Command | ||||
|         $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 { | ||||
|   | ||||
| @@ -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 @@ class NotifyDemo extends Command | ||||
| 
 | ||||
|         if (blank($channel)) { | ||||
|             $this->showHelp(); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|   | ||||
| @@ -35,6 +35,7 @@ class RootChangeEmail extends Command | ||||
|             $this->info('Root user\'s email updated successfully.'); | ||||
|         } catch (\Exception $e) { | ||||
|             $this->error('Failed to update root user\'s email.'); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -34,6 +34,7 @@ class RootResetPassword extends Command | ||||
|         $passwordAgain = password('Again'); | ||||
|         if ($password != $passwordAgain) { | ||||
|             $this->error('Passwords do not match.'); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|         $this->info('Updating root password...'); | ||||
| @@ -42,6 +43,7 @@ class RootResetPassword extends Command | ||||
|             $this->info('Root password updated successfully.'); | ||||
|         } catch (\Exception $e) { | ||||
|             $this->error('Failed to update root password.'); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -7,7 +7,9 @@ use Illuminate\Console\Command; | ||||
| class Scheduler extends Command | ||||
| { | ||||
|     protected $signature = 'start:scheduler'; | ||||
| 
 | ||||
|     protected $description = 'Start Scheduler'; | ||||
| 
 | ||||
|     public function handle() | ||||
|     { | ||||
|         if (config('coolify.is_scheduler_enabled')) { | ||||
|   | ||||
| @@ -48,11 +48,13 @@ class ServicesDelete extends Command | ||||
|             $this->deleteServer(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function deleteServer() | ||||
|     { | ||||
|         $servers = Server::all(); | ||||
|         if ($servers->count() === 0) { | ||||
|             $this->error('There are no applications to delete.'); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|         $serversToDelete = multiselect( | ||||
| @@ -64,7 +66,7 @@ class ServicesDelete extends Command | ||||
|             $toDelete = $servers->where('id', $server)->first(); | ||||
|             if ($toDelete) { | ||||
|                 $this->info($toDelete); | ||||
|                 $confirmed = confirm("Are you sure you want to delete all selected resources?"); | ||||
|                 $confirmed = confirm('Are you sure you want to delete all selected resources?'); | ||||
|                 if (! $confirmed) { | ||||
|                     break; | ||||
|                 } | ||||
| @@ -72,11 +74,13 @@ class ServicesDelete extends Command | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function deleteApplication() | ||||
|     { | ||||
|         $applications = Application::all(); | ||||
|         if ($applications->count() === 0) { | ||||
|             $this->error('There are no applications to delete.'); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|         $applicationsToDelete = multiselect( | ||||
| @@ -88,7 +92,7 @@ class ServicesDelete extends Command | ||||
|             $toDelete = $applications->where('id', $application)->first(); | ||||
|             if ($toDelete) { | ||||
|                 $this->info($toDelete); | ||||
|                 $confirmed = confirm("Are you sure you want to delete all selected resources? "); | ||||
|                 $confirmed = confirm('Are you sure you want to delete all selected resources? '); | ||||
|                 if (! $confirmed) { | ||||
|                     break; | ||||
|                 } | ||||
| @@ -96,11 +100,13 @@ class ServicesDelete extends Command | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function deleteDatabase() | ||||
|     { | ||||
|         $databases = StandalonePostgresql::all(); | ||||
|         if ($databases->count() === 0) { | ||||
|             $this->error('There are no databases to delete.'); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|         $databasesToDelete = multiselect( | ||||
| @@ -112,7 +118,7 @@ class ServicesDelete extends Command | ||||
|             $toDelete = $databases->where('id', $database)->first(); | ||||
|             if ($toDelete) { | ||||
|                 $this->info($toDelete); | ||||
|                 $confirmed = confirm("Are you sure you want to delete all selected resources?"); | ||||
|                 $confirmed = confirm('Are you sure you want to delete all selected resources?'); | ||||
|                 if (! $confirmed) { | ||||
|                     return; | ||||
|                 } | ||||
| @@ -120,11 +126,13 @@ class ServicesDelete extends Command | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function deleteService() | ||||
|     { | ||||
|         $services = Service::all(); | ||||
|         if ($services->count() === 0) { | ||||
|             $this->error('There are no services to delete.'); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
|         $servicesToDelete = multiselect( | ||||
| @@ -136,7 +144,7 @@ class ServicesDelete extends Command | ||||
|             $toDelete = $services->where('id', $service)->first(); | ||||
|             if ($toDelete) { | ||||
|                 $this->info($toDelete); | ||||
|                 $confirmed = confirm("Are you sure you want to delete all selected resources?"); | ||||
|                 $confirmed = confirm('Are you sure you want to delete all selected resources?'); | ||||
|                 if (! $confirmed) { | ||||
|                     return; | ||||
|                 } | ||||
|   | ||||
| @@ -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; | ||||
| @@ -57,12 +56,14 @@ class ServicesGenerate extends Command | ||||
|         } | ||||
|         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 @@ class ServicesGenerate extends Command | ||||
|             $env_file_base64 = base64_encode($env_file_content); | ||||
|             $payload['envs'] = $env_file_base64; | ||||
|         } | ||||
| 
 | ||||
|         return $payload; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -33,30 +33,31 @@ class SyncBunny extends Command | ||||
|         $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__).'/../../..'); | ||||
| 
 | ||||
|         $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); | ||||
| 
 | ||||
|             return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw(); | ||||
|         }); | ||||
|         PendingRequest::macro('purge', function ($url) use ($that) { | ||||
| @@ -65,9 +66,10 @@ class SyncBunny extends Command | ||||
|                 'Accept' => 'application/json', | ||||
|             ]; | ||||
|             $that->info('Purging: '.$url); | ||||
| 
 | ||||
|             return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [ | ||||
|                 "url" => $url, | ||||
|                 "async" => false | ||||
|                 'url' => $url, | ||||
|                 'async' => false, | ||||
|             ]); | ||||
|         }); | ||||
|         try { | ||||
| @@ -76,7 +78,7 @@ class SyncBunny extends Command | ||||
|             } | ||||
|             if ($only_template) { | ||||
|                 $this->info('About to sync service-templates.json to BunnyCDN.'); | ||||
|                 $confirmed = confirm("Are you sure you want to sync?"); | ||||
|                 $confirmed = confirm('Are you sure you want to sync?'); | ||||
|                 if (! $confirmed) { | ||||
|                     return; | ||||
|                 } | ||||
| @@ -85,6 +87,7 @@ class SyncBunny extends Command | ||||
|                     $pool->purge("$bunny_cdn/$bunny_cdn_path/$service_template"), | ||||
|                 ]); | ||||
|                 $this->info('Service template uploaded & purged...'); | ||||
| 
 | ||||
|                 return; | ||||
|             } elseif ($only_version) { | ||||
|                 $this->info('About to sync versions.json to BunnyCDN.'); | ||||
| @@ -101,10 +104,10 @@ class SyncBunny extends Command | ||||
|                     $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 @@ class SyncBunny extends Command | ||||
|                 $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()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -13,7 +13,9 @@ use Illuminate\Support\Str; | ||||
| 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 @@ class WaitlistInvite extends Command | ||||
|             $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(); | ||||
| @@ -52,6 +56,7 @@ class WaitlistInvite extends Command | ||||
|             } | ||||
|             if (! $this->next_patient) { | ||||
|                 $this->error("{$this->argument('email')} not found in the waitlist."); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|         } else { | ||||
| @@ -60,6 +65,7 @@ class WaitlistInvite extends Command | ||||
|         if ($this->next_patient) { | ||||
|             if ($this->option('only-email')) { | ||||
|                 $this->send_email(); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             $this->register_user(); | ||||
| @@ -69,6 +75,7 @@ class WaitlistInvite extends Command | ||||
|             $this->info('No verified user found in the waitlist. 👀'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function register_user() | ||||
|     { | ||||
|         $already_registered = User::whereEmail($this->next_patient->email)->first(); | ||||
| @@ -85,11 +92,13 @@ class WaitlistInvite extends Command | ||||
|             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 @@ class WaitlistInvite extends Command | ||||
|         ]); | ||||
|         $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. 📧'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,17 +4,14 @@ namespace App\Console; | ||||
| 
 | ||||
| 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 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel; | ||||
| class Kernel extends ConsoleKernel | ||||
| { | ||||
|     private $all_servers; | ||||
| 
 | ||||
|     protected function schedule(Schedule $schedule): void | ||||
|     { | ||||
|         $this->all_servers = Server::all(); | ||||
| @@ -32,46 +30,44 @@ class Kernel extends ConsoleKernel | ||||
|             // 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 @@ class Kernel extends ConsoleKernel | ||||
|             $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(); | ||||
| @@ -116,6 +103,7 @@ class Kernel extends ConsoleKernel | ||||
|             if (is_null(data_get($scheduled_backup, 'database'))) { | ||||
|                 ray('database not found'); | ||||
|                 $scheduled_backup->delete(); | ||||
| 
 | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
| @@ -144,6 +132,7 @@ class Kernel extends ConsoleKernel | ||||
|             if (! $application && ! $service) { | ||||
|                 ray('application/service attached to scheduled task does not exist'); | ||||
|                 $scheduled_task->delete(); | ||||
| 
 | ||||
|                 continue; | ||||
|             } | ||||
|             if ($application) { | ||||
|   | ||||
| @@ -11,6 +11,5 @@ class ServerMetadata extends Data | ||||
|     public function __construct( | ||||
|         public ?ProxyTypes $type, | ||||
|         public ?ProxyStatus $status | ||||
|     ) { | ||||
|     } | ||||
|     ) {} | ||||
| } | ||||
|   | ||||
| @@ -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 @@ use Illuminate\Queue\SerializesModels; | ||||
| 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; | ||||
|     } | ||||
|   | ||||
| @@ -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 @@ use Illuminate\Queue\SerializesModels; | ||||
| 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; | ||||
|     } | ||||
|   | ||||
| @@ -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 @@ use Illuminate\Queue\SerializesModels; | ||||
| 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; | ||||
|     } | ||||
|   | ||||
| @@ -9,8 +9,6 @@ use Illuminate\Queue\SerializesModels; | ||||
| class ProxyStarted | ||||
| { | ||||
|     use Dispatchable, InteractsWithSockets, SerializesModels; | ||||
|     public function __construct(public $data) | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
|     public function __construct(public $data) {} | ||||
| } | ||||
|   | ||||
| @@ -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 @@ use Illuminate\Queue\SerializesModels; | ||||
| 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; | ||||
|     } | ||||
|   | ||||
| @@ -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 @@ use Illuminate\Queue\SerializesModels; | ||||
| 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; | ||||
|     } | ||||
|   | ||||
| @@ -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 @@ use Illuminate\Queue\SerializesModels; | ||||
| class TestEvent implements ShouldBroadcast | ||||
| { | ||||
|     use Dispatchable, InteractsWithSockets, SerializesModels; | ||||
| 
 | ||||
|     public $teamId; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->teamId = auth()->user()->currentTeam()->id; | ||||
|   | ||||
| @@ -13,7 +13,6 @@ use Throwable; | ||||
| 
 | ||||
| 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 @@ class Handler extends ExceptionHandler | ||||
|         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 @@ class Handler extends ExceptionHandler | ||||
|                     $scope->setUser( | ||||
|                         [ | ||||
|                             'email' => $email, | ||||
|                             'instanceAdmin' => $instanceAdmin | ||||
|                             'instanceAdmin' => $instanceAdmin, | ||||
|                         ] | ||||
|                     ); | ||||
|                 } | ||||
|   | ||||
| @@ -4,7 +4,4 @@ namespace App\Exceptions; | ||||
| 
 | ||||
| use Exception; | ||||
| 
 | ||||
| class ProcessException extends Exception | ||||
| { | ||||
| 
 | ||||
| } | ||||
| class ProcessException extends Exception {} | ||||
|   | ||||
| @@ -27,18 +27,20 @@ class Deploy extends Controller | ||||
|             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(); | ||||
| @@ -57,8 +59,10 @@ class Deploy extends Controller | ||||
|         } 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 @@ class Deploy extends Controller | ||||
|         } | ||||
|         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); | ||||
| @@ -107,6 +114,7 @@ class Deploy extends Controller | ||||
|             $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 @@ class Deploy extends Controller | ||||
|             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; | ||||
| @@ -200,6 +210,7 @@ class Deploy extends Controller | ||||
|             StartService::run($resource); | ||||
|             $message = "Service {$resource->name} started. It could take a while, be patient."; | ||||
|         } | ||||
| 
 | ||||
|         return ['message' => $message, 'deployment_uuid' => $deployment_uuid]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -15,8 +15,10 @@ class Project extends Controller | ||||
|             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 @@ class Project extends Controller | ||||
|             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 @@ class Project extends Controller | ||||
|         } | ||||
|         $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); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -30,9 +30,10 @@ class Resources extends Controller | ||||
|                 $payload['status'] = $resource->status; | ||||
|             } | ||||
|             $payload['type'] = $resource->type(); | ||||
| 
 | ||||
|             return $payload; | ||||
|         }); | ||||
| 
 | ||||
|         return response()->json($resources); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|   | ||||
| @@ -17,10 +17,13 @@ class Server extends Controller | ||||
|         $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 @@ class Server extends Controller | ||||
|                 } else { | ||||
|                     $payload['status'] = $resource->status; | ||||
|                 } | ||||
| 
 | ||||
|                 return $payload; | ||||
|             }); | ||||
|         } else { | ||||
|             $server->load(['settings']); | ||||
|         } | ||||
| 
 | ||||
|         return response()->json($server); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,8 +14,10 @@ class Team extends Controller | ||||
|             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 @@ class Team extends Controller | ||||
|         $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 @@ class Team extends Controller | ||||
|         $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 @@ class Team extends Controller | ||||
|             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 @@ class Team extends Controller | ||||
|             return invalid_token(); | ||||
|         } | ||||
|         $team = auth()->user()->currentTeam(); | ||||
| 
 | ||||
|         return response()->json($team->members); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,33 +14,42 @@ use Illuminate\Routing\Controller as BaseController; | ||||
| 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([ | ||||
| @@ -60,10 +69,13 @@ class Controller extends BaseController | ||||
|             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'); | ||||
| @@ -90,9 +102,11 @@ class Controller extends BaseController | ||||
|                 } | ||||
|                 Auth::login($user); | ||||
|                 session(['currentTeam' => $team]); | ||||
| 
 | ||||
|                 return redirect()->route('dashboard'); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return redirect()->route('login')->with('error', 'Invalid credentials.'); | ||||
|     } | ||||
| 
 | ||||
| @@ -108,11 +122,12 @@ class Controller extends BaseController | ||||
|                 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 @@ class Controller extends BaseController | ||||
|                     return redirect()->route('login'); | ||||
|                 } | ||||
|                 refreshSession($invitation->team); | ||||
| 
 | ||||
|                 return redirect()->route('team.index'); | ||||
|             } else { | ||||
|                 abort(401); | ||||
| @@ -143,6 +159,7 @@ class Controller extends BaseController | ||||
|                 abort(401); | ||||
|             } | ||||
|             $invitation->delete(); | ||||
| 
 | ||||
|             return redirect()->route('team.index'); | ||||
|         } catch (\Throwable $e) { | ||||
|             throw $e; | ||||
|   | ||||
| @@ -12,21 +12,21 @@ 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(), | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
| @@ -35,11 +35,12 @@ class MagicController extends Controller | ||||
|         $project = Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->first(); | ||||
|         if (! $project) { | ||||
|             return response()->json([ | ||||
|                 'environments' => [] | ||||
|                 'environments' => [], | ||||
|             ]); | ||||
|         } | ||||
| 
 | ||||
|         return response()->json([ | ||||
|             'environments' => $project->environments | ||||
|             'environments' => $project->environments, | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
| @@ -49,8 +50,9 @@ class MagicController extends Controller | ||||
|             ['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 @@ class MagicController extends Controller | ||||
|             ['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 @@ class MagicController extends Controller | ||||
|         ); | ||||
|         auth()->user()->teams()->attach($team, ['role' => 'admin']); | ||||
|         refreshSession(); | ||||
| 
 | ||||
|         return redirect(request()->header('Referer')); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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(); | ||||
|     } | ||||
| 
 | ||||
| @@ -26,9 +26,11 @@ class OauthController extends Controller { | ||||
|                 ]); | ||||
|             } | ||||
|             Auth::login($user); | ||||
| 
 | ||||
|             return redirect('/'); | ||||
|         } catch (\Exception $e) { | ||||
|             ray($e->getMessage()); | ||||
| 
 | ||||
|             return redirect()->route('login')->withErrors([__('auth.failed.callback')]); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -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 @@ class UploadController extends BaseController | ||||
|         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 @@ class UploadController extends BaseController | ||||
|         } | ||||
| 
 | ||||
|         $handler = $save->handler(); | ||||
| 
 | ||||
|         return response()->json([ | ||||
|             "done" => $handler->getPercentageDone(), | ||||
|             'status' => true | ||||
|             'done' => $handler->getPercentageDone(), | ||||
|             'status' => true, | ||||
|         ]); | ||||
|     } | ||||
|     // protected function saveFileToS3($file)
 | ||||
| @@ -64,19 +62,20 @@ class UploadController extends BaseController | ||||
|     { | ||||
|         $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; | ||||
|     } | ||||
|   | ||||
| @@ -30,13 +30,14 @@ class Bitbucket extends Controller | ||||
|                 ]; | ||||
|                 $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)) { | ||||
|                 return response([ | ||||
| @@ -76,7 +77,7 @@ class Bitbucket extends Controller | ||||
|                 $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()) { | ||||
|                     $return_payloads->push([ | ||||
| @@ -85,6 +86,7 @@ class Bitbucket extends Controller | ||||
|                         'message' => 'Invalid signature.', | ||||
|                     ]); | ||||
|                     ray('Invalid signature'); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 $isFunctional = $application->destination->server->isFunctional(); | ||||
| @@ -95,6 +97,7 @@ class Bitbucket extends Controller | ||||
|                         'message' => 'Server is not functional.', | ||||
|                     ]); | ||||
|                     ray('Server is not functional: '.$application->destination->server->name); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if ($x_bitbucket_event === 'repo:push') { | ||||
| @@ -127,6 +130,16 @@ class Bitbucket extends Controller | ||||
|                         $deployment_uuid = new Cuid2(7); | ||||
|                         $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); | ||||
|                         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, | ||||
| @@ -134,6 +147,7 @@ class Bitbucket extends Controller | ||||
|                                     'pull_request_html_url' => $pull_request_html_url, | ||||
|                                 ]); | ||||
|                             } | ||||
|                         } | ||||
|                         queue_application_deployment( | ||||
|                             application: $application, | ||||
|                             pull_request_id: $pull_request_id, | ||||
| @@ -178,9 +192,11 @@ class Bitbucket extends Controller | ||||
|                 } | ||||
|             } | ||||
|             ray($return_payloads); | ||||
| 
 | ||||
|             return response($return_payloads); | ||||
|         } catch (Exception $e) { | ||||
|             ray($e); | ||||
| 
 | ||||
|             return handleError($e); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -27,6 +27,7 @@ class Gitea extends Controller | ||||
|                 })->first(); | ||||
|                 if ($gitea_delivery_found) { | ||||
|                     ray('Webhook already found'); | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
|                 $data = [ | ||||
| @@ -41,6 +42,7 @@ class Gitea extends Controller | ||||
|                 ]; | ||||
|                 $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')); | ||||
| @@ -103,6 +105,7 @@ class Gitea extends Controller | ||||
|                         'status' => 'failed', | ||||
|                         'message' => 'Invalid signature.', | ||||
|                     ]); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 $isFunctional = $application->destination->server->isFunctional(); | ||||
| @@ -112,6 +115,7 @@ class Gitea extends Controller | ||||
|                         'status' => 'failed', | ||||
|                         'message' => 'Server is not functional.', | ||||
|                     ]); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if ($x_gitea_event === 'push') { | ||||
| @@ -161,6 +165,16 @@ class Gitea extends Controller | ||||
|                             $deployment_uuid = new Cuid2(7); | ||||
|                             $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); | ||||
|                             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, | ||||
| @@ -168,6 +182,8 @@ class Gitea extends Controller | ||||
|                                         'pull_request_html_url' => $pull_request_html_url, | ||||
|                                     ]); | ||||
|                                 } | ||||
| 
 | ||||
|                             } | ||||
|                             queue_application_deployment( | ||||
|                                 application: $application, | ||||
|                                 pull_request_id: $pull_request_id, | ||||
| @@ -213,9 +229,11 @@ class Gitea extends Controller | ||||
|                 } | ||||
|             } | ||||
|             ray($return_payloads); | ||||
| 
 | ||||
|             return response($return_payloads); | ||||
|         } catch (Exception $e) { | ||||
|             ray($e->getMessage()); | ||||
| 
 | ||||
|             return handleError($e); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -33,6 +33,7 @@ class Github extends Controller | ||||
|                 })->first(); | ||||
|                 if ($github_delivery_found) { | ||||
|                     ray('Webhook already found'); | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
|                 $data = [ | ||||
| @@ -47,6 +48,7 @@ class Github extends Controller | ||||
|                 ]; | ||||
|                 $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')); | ||||
| @@ -108,6 +110,7 @@ class Github extends Controller | ||||
|                         'status' => 'failed', | ||||
|                         'message' => 'Invalid signature.', | ||||
|                     ]); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 $isFunctional = $application->destination->server->isFunctional(); | ||||
| @@ -117,6 +120,7 @@ class Github extends Controller | ||||
|                         'status' => 'failed', | ||||
|                         'message' => 'Server is not functional.', | ||||
|                     ]); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if ($x_github_event === 'push') { | ||||
| @@ -166,6 +170,16 @@ class Github extends Controller | ||||
|                             $deployment_uuid = new Cuid2(7); | ||||
|                             $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); | ||||
|                             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, | ||||
| @@ -173,6 +187,7 @@ class Github extends Controller | ||||
|                                         'pull_request_html_url' => $pull_request_html_url, | ||||
|                                     ]); | ||||
|                                 } | ||||
|                             } | ||||
|                             queue_application_deployment( | ||||
|                                 application: $application, | ||||
|                                 pull_request_id: $pull_request_id, | ||||
| @@ -218,12 +233,15 @@ class Github extends Controller | ||||
|                 } | ||||
|             } | ||||
|             ray($return_payloads); | ||||
| 
 | ||||
|             return response($return_payloads); | ||||
|         } catch (Exception $e) { | ||||
|             ray($e->getMessage()); | ||||
| 
 | ||||
|             return handleError($e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function normal(Request $request) | ||||
|     { | ||||
|         try { | ||||
| @@ -239,6 +257,7 @@ class Github extends Controller | ||||
|                 })->first(); | ||||
|                 if ($github_delivery_found) { | ||||
|                     ray('Webhook already found'); | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
|                 $data = [ | ||||
| @@ -253,6 +272,7 @@ class Github extends Controller | ||||
|                 ]; | ||||
|                 $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')); | ||||
| @@ -280,6 +300,7 @@ class Github extends Controller | ||||
|                 if ($action === 'new_permissions_accepted') { | ||||
|                     GithubAppPermissionJob::dispatch($github_app); | ||||
|                 } | ||||
| 
 | ||||
|                 return response('cool'); | ||||
|             } | ||||
|             if ($x_github_event === 'push') { | ||||
| @@ -329,6 +350,7 @@ class Github extends Controller | ||||
|                         'application_uuid' => $application->uuid, | ||||
|                         'application_name' => $application->name, | ||||
|                     ]); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if ($x_github_event === 'push') { | ||||
| @@ -410,11 +432,12 @@ class Github extends Controller | ||||
|                     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 @@ class Github extends Controller | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             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 @@ class Github extends Controller | ||||
|             $github_app->webhook_secret = $webhook_secret; | ||||
|             $github_app->private_key_id = $private_key->id; | ||||
|             $github_app->save(); | ||||
| 
 | ||||
|             return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]); | ||||
|         } catch (Exception $e) { | ||||
|             return handleError($e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function install(Request $request) | ||||
|     { | ||||
|         try { | ||||
| @@ -488,6 +515,7 @@ class Github extends Controller | ||||
|                 ]; | ||||
|                 $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 @@ class Github extends Controller | ||||
|                 $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); | ||||
|   | ||||
| @@ -31,6 +31,7 @@ class Gitlab extends Controller | ||||
|                 ]; | ||||
|                 $json = json_encode($data); | ||||
|                 Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitlab::manual_gitlab", $json); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             $return_payloads = collect([]); | ||||
| @@ -44,6 +45,7 @@ class Gitlab extends Controller | ||||
|                     'status' => 'failed', | ||||
|                     'message' => 'Event not allowed. Only push and merge_request events are allowed.', | ||||
|                 ]); | ||||
| 
 | ||||
|                 return response($return_payloads); | ||||
|             } | ||||
| 
 | ||||
| @@ -58,6 +60,7 @@ class Gitlab extends Controller | ||||
|                         'status' => 'failed', | ||||
|                         'message' => 'Nothing to do. No branch found in the request.', | ||||
|                     ]); | ||||
| 
 | ||||
|                     return response($return_payloads); | ||||
|                 } | ||||
|                 $added_files = data_get($payload, 'commits.*.added'); | ||||
| @@ -78,6 +81,7 @@ class Gitlab extends Controller | ||||
|                         '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); | ||||
| @@ -90,6 +94,7 @@ class Gitlab extends Controller | ||||
|                         '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 @@ class Gitlab extends Controller | ||||
|                         'status' => 'failed', | ||||
|                         'message' => "Nothing to do. No applications found with branch '$base_branch'.", | ||||
|                     ]); | ||||
| 
 | ||||
|                     return response($return_payloads); | ||||
|                 } | ||||
|             } | ||||
| @@ -112,6 +118,7 @@ class Gitlab extends Controller | ||||
|                         'message' => 'Invalid signature.', | ||||
|                     ]); | ||||
|                     ray('Invalid signature'); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 $isFunctional = $application->destination->server->isFunctional(); | ||||
| @@ -122,6 +129,7 @@ class Gitlab extends Controller | ||||
|                         'message' => 'Server is not functional', | ||||
|                     ]); | ||||
|                     ray('Server is not functional: '.$application->destination->server->name); | ||||
| 
 | ||||
|                     continue; | ||||
|                 } | ||||
|                 if ($x_gitlab_event === 'push') { | ||||
| @@ -172,6 +180,16 @@ class Gitlab extends Controller | ||||
|                             $deployment_uuid = new Cuid2(7); | ||||
|                             $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); | ||||
|                             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, | ||||
| @@ -179,6 +197,7 @@ class Gitlab extends Controller | ||||
|                                         'pull_request_html_url' => $pull_request_html_url, | ||||
|                                     ]); | ||||
|                                 } | ||||
|                             } | ||||
|                             queue_application_deployment( | ||||
|                                 application: $application, | ||||
|                                 pull_request_id: $pull_request_id, | ||||
| @@ -202,7 +221,7 @@ class Gitlab extends Controller | ||||
|                             ]); | ||||
|                             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 @@ class Gitlab extends Controller | ||||
|                                 'status' => 'success', | ||||
|                                 'message' => 'Preview Deployment closed', | ||||
|                             ]); | ||||
| 
 | ||||
|                             return response($return_payloads); | ||||
|                         } | ||||
|                         $return_payloads->push([ | ||||
| @@ -230,9 +250,11 @@ class Gitlab extends Controller | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return response($return_payloads); | ||||
|         } catch (Exception $e) { | ||||
|             ray($e->getMessage()); | ||||
| 
 | ||||
|             return handleError($e); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -36,6 +36,7 @@ class Stripe extends Controller | ||||
|                 ]; | ||||
|                 $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 @@ class Stripe extends Controller | ||||
|             ); | ||||
|             $webhook = Webhook::create([ | ||||
|                 'type' => 'stripe', | ||||
|                 'payload' => $request->getContent() | ||||
|                 'payload' => $request->getContent(), | ||||
|             ]); | ||||
|             $type = data_get($event, 'type'); | ||||
|             $data = data_get($event, 'data.object'); | ||||
| @@ -108,11 +109,13 @@ class Stripe extends Controller | ||||
|                     $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); | ||||
| 
 | ||||
|                         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); | ||||
| 
 | ||||
|                         return response('No team found in Coolify.'); | ||||
|                     } | ||||
|                     if (! $subscription->stripe_invoice_paid) { | ||||
| @@ -127,10 +130,12 @@ class Stripe extends Controller | ||||
|                     $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); | ||||
| 
 | ||||
|                         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); | ||||
| 
 | ||||
|                         return; | ||||
|                     } | ||||
|                     send_internal_notification('Subscription payment failed for customer: '.$customerId); | ||||
| @@ -152,10 +157,12 @@ class Stripe extends Controller | ||||
|                     if (! $subscription) { | ||||
|                         if ($status === 'incomplete_expired') { | ||||
|                             send_internal_notification('Subscription incomplete expired for customer: '.$customerId); | ||||
|                             return response("Subscription incomplete expired", 200); | ||||
| 
 | ||||
|                             return response('Subscription incomplete expired', 200); | ||||
|                         } | ||||
|                         send_internal_notification('No subscription found for: '.$customerId); | ||||
|                         return response("No subscription found", 400); | ||||
| 
 | ||||
|                         return response('No subscription found', 400); | ||||
|                     } | ||||
|                     $trialEndedAlready = data_get($subscription, 'stripe_trial_already_ended'); | ||||
|                     $cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end'); | ||||
| @@ -264,6 +271,7 @@ class Stripe extends Controller | ||||
|                 'status' => 'failed', | ||||
|                 'failure_reason' => $e->getMessage(), | ||||
|             ]); | ||||
| 
 | ||||
|             return response($e->getMessage(), 400); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -22,21 +22,26 @@ class Waitlist extends Controller | ||||
|                         $found->verified = true; | ||||
|                         $found->save(); | ||||
|                         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); | ||||
| 
 | ||||
|                         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()); | ||||
|             ray($e->getMessage()); | ||||
| 
 | ||||
|             return redirect()->route('dashboard'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function cancel(Request $request) | ||||
|     { | ||||
|         $email = request()->get('email'); | ||||
| @@ -46,12 +51,15 @@ class Waitlist extends Controller | ||||
|             if ($found && ! $found->verified) { | ||||
|                 $found->delete(); | ||||
|                 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()); | ||||
|             ray($e->getMessage()); | ||||
| 
 | ||||
|             return redirect()->route('dashboard'); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -20,6 +20,7 @@ class CheckForcePasswordReset | ||||
|                 auth()->logout(); | ||||
|                 request()->session()->invalidate(); | ||||
|                 request()->session()->regenerateToken(); | ||||
| 
 | ||||
|                 return $next($request); | ||||
|             } | ||||
|             $force_password_reset = auth()->user()->force_password_reset; | ||||
| @@ -27,9 +28,11 @@ class CheckForcePasswordReset | ||||
|                 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); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,8 +5,8 @@ namespace App\Http\Middleware; | ||||
| 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 | ||||
| { | ||||
| @@ -23,12 +23,14 @@ class DecideWhatToDoWithUser | ||||
|             if (! isCloud() && showBoarding() && ! in_array($request->path(), allowedPathsForBoardingAccounts())) { | ||||
|                 return redirect()->route('onboarding'); | ||||
|             } | ||||
| 
 | ||||
|             return $next($request); | ||||
|         } | ||||
|         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()) { | ||||
| @@ -36,6 +38,7 @@ class DecideWhatToDoWithUser | ||||
|                 if (Str::startsWith($request->path(), 'invitations')) { | ||||
|                     return $next($request); | ||||
|                 } | ||||
| 
 | ||||
|                 return redirect()->route('subscription.index'); | ||||
|             } | ||||
|         } | ||||
| @@ -43,6 +46,7 @@ class DecideWhatToDoWithUser | ||||
|             if (Str::startsWith($request->path(), 'invitations')) { | ||||
|                 return $next($request); | ||||
|             } | ||||
| 
 | ||||
|             return redirect()->route('onboarding'); | ||||
|         } | ||||
|         if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') { | ||||
| @@ -51,6 +55,7 @@ class DecideWhatToDoWithUser | ||||
|         if (isSubscriptionActive() && $request->routeIs('subscription.index')) { | ||||
|             return redirect(RouteServiceProvider::HOME); | ||||
|         } | ||||
| 
 | ||||
|         return $next($request); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -13,6 +13,6 @@ class PreventRequestsDuringMaintenance extends Middleware | ||||
|      */ | ||||
|     protected $except = [ | ||||
|         'webhooks/*', | ||||
|         '/api/health' | ||||
|         '/api/health', | ||||
|     ]; | ||||
| } | ||||
|   | ||||
| @@ -24,6 +24,7 @@ class RedirectIfAuthenticated | ||||
|                 return redirect(RouteServiceProvider::HOME); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $next($request); | ||||
|     } | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -12,11 +12,12 @@ use Illuminate\Foundation\Bus\Dispatchable; | ||||
| 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,17 +25,19 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted | ||||
|         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; | ||||
|             } elseif ($this->status === ProcessStatus::IN_PROGRESS) { | ||||
|                 $this->body = "The preview deployment is in progress. 🟡\n\n"; | ||||
| @@ -48,8 +51,8 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted | ||||
|             } | ||||
|             $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 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted | ||||
|             } | ||||
|         } catch (\Throwable $e) { | ||||
|             ray($e); | ||||
|             throw $e; | ||||
| 
 | ||||
|             return $e; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @@ -82,6 +86,7 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted | ||||
|         $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'); | ||||
|   | ||||
| @@ -10,19 +10,23 @@ use Illuminate\Foundation\Bus\Dispatchable; | ||||
| 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'); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -15,13 +15,12 @@ use Illuminate\Queue\Middleware\WithoutOverlapping; | ||||
| 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 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted | ||||
|     { | ||||
|         return $this->server->id; | ||||
|     } | ||||
| 
 | ||||
|     public function healthcheck() | ||||
|     { | ||||
|         $status = instant_remote_process(["docker inspect --format='{{json .State.Status}}' coolify-log-drain"], $this->server, false); | ||||
| @@ -40,14 +40,15 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     public function handle(): void | ||||
| 
 | ||||
|     public function handle() | ||||
|     { | ||||
|         // ray("checking log drain statuses for {$this->server->id}");
 | ||||
|         try { | ||||
|             if (! $this->server->isFunctional()) { | ||||
|                 return; | ||||
|             }; | ||||
|             $containers = instant_remote_process(["docker container ls -q"], $this->server, false); | ||||
|             } | ||||
|             $containers = instant_remote_process(['docker container ls -q'], $this->server, false); | ||||
|             if (! $containers) { | ||||
|                 return; | ||||
|             } | ||||
| @@ -66,6 +67,7 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted | ||||
|                         $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) { | ||||
| @@ -80,9 +82,12 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted | ||||
|                 } | ||||
|             } | ||||
|         } catch (\Throwable $e) { | ||||
|             if (! isCloud()) { | ||||
|                 send_internal_notification("CheckLogDrainContainerJob failed on ({$this->server->id}) with: ".$e->getMessage()); | ||||
|             } | ||||
|             ray($e->getMessage()); | ||||
|             handleError($e); | ||||
| 
 | ||||
|             return handleError($e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -10,13 +10,11 @@ use Illuminate\Foundation\Bus\Dispatchable; | ||||
| 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 | ||||
|     { | ||||
|   | ||||
| @@ -11,13 +11,11 @@ use Illuminate\Foundation\Bus\Dispatchable; | ||||
| 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 | ||||
|     { | ||||
|   | ||||
| @@ -12,14 +12,11 @@ use Illuminate\Foundation\Bus\Dispatchable; | ||||
| 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
 | ||||
|     // {
 | ||||
| @@ -49,6 +46,7 @@ class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique, ShouldBeE | ||||
|             $item->delete(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function cleanup_invitation_link() | ||||
|     { | ||||
|         $invitation = TeamInvitation::all(); | ||||
|   | ||||
| @@ -12,18 +12,19 @@ use Illuminate\Queue\InteractsWithQueue; | ||||
| 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))]; | ||||
|   | ||||
| @@ -11,7 +11,7 @@ use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| use Spatie\Activitylog\Models\Activity; | ||||
| 
 | ||||
| class CoolifyTask implements ShouldQueue, ShouldBeEncrypted | ||||
| class CoolifyTask implements ShouldBeEncrypted, ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
| @@ -23,8 +23,7 @@ class CoolifyTask implements ShouldQueue, ShouldBeEncrypted | ||||
|         public bool $ignore_errors = false, | ||||
|         public $call_event_on_finish = null, | ||||
|         public $call_event_data = null | ||||
|     ) { | ||||
|     } | ||||
|     ) {} | ||||
| 
 | ||||
|     /** | ||||
|      * Execute the job. | ||||
| @@ -35,7 +34,7 @@ class CoolifyTask implements ShouldQueue, ShouldBeEncrypted | ||||
|             '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(); | ||||
|   | ||||
| @@ -25,26 +25,37 @@ use Illuminate\Queue\InteractsWithQueue; | ||||
| 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 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|                 $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) { | ||||
|                 ray('database not running'); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') { | ||||
| @@ -223,20 +236,20 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|                     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); | ||||
|                 try { | ||||
|                     if (str($databaseType)->contains('postgres')) { | ||||
|                         $this->backup_file = "/pg-dump-$database-" . Carbon::now()->timestamp . ".dmp"; | ||||
|                         $this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp'; | ||||
|                         $this->backup_location = $this->backup_dir.$this->backup_file; | ||||
|                         $this->backup_log = ScheduledDatabaseBackupExecution::create([ | ||||
|                             'database_name' => $database, | ||||
| @@ -255,7 +268,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|                                 $databaseName = $database; | ||||
|                             } | ||||
|                         } | ||||
|                         $this->backup_file = "/mongo-dump-$databaseName-" . Carbon::now()->timestamp . ".tar.gz"; | ||||
|                         $this->backup_file = "/mongo-dump-$databaseName-".Carbon::now()->timestamp.'.tar.gz'; | ||||
|                         $this->backup_location = $this->backup_dir.$this->backup_file; | ||||
|                         $this->backup_log = ScheduledDatabaseBackupExecution::create([ | ||||
|                             'database_name' => $databaseName, | ||||
| @@ -264,7 +277,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|                         ]); | ||||
|                         $this->backup_standalone_mongodb($database); | ||||
|                     } elseif (str($databaseType)->contains('mysql')) { | ||||
|                         $this->backup_file = "/mysql-dump-$database-" . Carbon::now()->timestamp . ".dmp"; | ||||
|                         $this->backup_file = "/mysql-dump-$database-".Carbon::now()->timestamp.'.dmp'; | ||||
|                         $this->backup_location = $this->backup_dir.$this->backup_file; | ||||
|                         $this->backup_log = ScheduledDatabaseBackupExecution::create([ | ||||
|                             'database_name' => $database, | ||||
| @@ -273,7 +286,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|                         ]); | ||||
|                         $this->backup_standalone_mysql($database); | ||||
|                     } elseif (str($databaseType)->contains('mariadb')) { | ||||
|                         $this->backup_file = "/mariadb-dump-$database-" . Carbon::now()->timestamp . ".dmp"; | ||||
|                         $this->backup_file = "/mariadb-dump-$database-".Carbon::now()->timestamp.'.dmp'; | ||||
|                         $this->backup_location = $this->backup_dir.$this->backup_file; | ||||
|                         $this->backup_log = ScheduledDatabaseBackupExecution::create([ | ||||
|                             'database_name' => $database, | ||||
| @@ -301,7 +314,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|                             'status' => 'failed', | ||||
|                             'message' => $this->backup_output, | ||||
|                             'size' => $size, | ||||
|                             'filename' => null | ||||
|                             'filename' => null, | ||||
|                         ]); | ||||
|                     } | ||||
|                     send_internal_notification('DatabaseBackupJob failed with: '.$e->getMessage()); | ||||
| @@ -315,13 +328,14 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|             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 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|                     $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"; | ||||
| @@ -362,10 +376,11 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|             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); | ||||
| @@ -379,10 +394,11 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|             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); | ||||
| @@ -397,10 +413,11 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|             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); | ||||
| @@ -415,6 +432,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|             throw $e; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function add_to_backup_output($output): void | ||||
|     { | ||||
|         if ($this->backup_output) { | ||||
|   | ||||
| @@ -3,27 +3,22 @@ | ||||
| namespace App\Jobs; | ||||
| 
 | ||||
| use App\Models\ScheduledDatabaseBackup; | ||||
| use App\Models\Server; | ||||
| use App\Models\Team; | ||||
| use App\Notifications\Database\DailyBackup; | ||||
| use App\Notifications\Server\HighDiskUsage; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Contracts\Queue\ShouldBeEncrypted; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Foundation\Bus\Dispatchable; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Queue\Middleware\WithoutOverlapping; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| class DatabaseBackupStatusJob implements ShouldQueue, ShouldBeEncrypted | ||||
| class DatabaseBackupStatusJob implements ShouldBeEncrypted, ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
|     public $tries = 1; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|     } | ||||
|     public function __construct() {} | ||||
| 
 | ||||
|     public function handle() | ||||
|     { | ||||
| @@ -42,13 +37,6 @@ class DatabaseBackupStatusJob implements ShouldQueue, ShouldBeEncrypted | ||||
|         //     }
 | ||||
|         // }
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         // $scheduled_backups = ScheduledDatabaseBackup::all();
 | ||||
|         // $databases = collect();
 | ||||
|         // $teams = collect();
 | ||||
|   | ||||
| @@ -24,13 +24,11 @@ use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| use Illuminate\Support\Facades\Artisan; | ||||
| 
 | ||||
| class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted | ||||
| class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
|     public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource, public bool $deleteConfigurations = false) | ||||
|     { | ||||
|     } | ||||
|     public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource, public bool $deleteConfigurations = false) {} | ||||
| 
 | ||||
|     public function handle() | ||||
|     { | ||||
|   | ||||
| @@ -10,21 +10,20 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Foundation\Bus\Dispatchable; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Queue\Middleware\WithoutOverlapping; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| use Illuminate\Support\Facades\Log; | ||||
| use RuntimeException; | ||||
| 
 | ||||
| class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted | ||||
| class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
|     public $timeout = 300; | ||||
| 
 | ||||
|     public ?int $usageBefore = null; | ||||
| 
 | ||||
|     public function __construct(public Server $server) | ||||
|     { | ||||
|     } | ||||
|     public function __construct(public Server $server) {} | ||||
| 
 | ||||
|     public function handle(): void | ||||
|     { | ||||
|         try { | ||||
| @@ -32,6 +31,7 @@ class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|             $this->server->applications()->each(function ($application) use (&$isInprogress) { | ||||
|                 if ($application->isDeploymentInprogress()) { | ||||
|                     $isInprogress = true; | ||||
| 
 | ||||
|                     return; | ||||
|                 } | ||||
|             }); | ||||
| @@ -60,7 +60,7 @@ class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted | ||||
|                 Log::info('No need to clean up '.$this->server->name); | ||||
|             } | ||||
|         } catch (\Throwable $e) { | ||||
|             send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage()); | ||||
|             // send_internal_notification('DockerCleanupJob failed with: '.$e->getMessage());
 | ||||
|             ray($e->getMessage()); | ||||
|             throw $e; | ||||
|         } | ||||
|   | ||||
| @@ -12,18 +12,19 @@ use Illuminate\Queue\Middleware\WithoutOverlapping; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| use Illuminate\Support\Facades\Http; | ||||
| 
 | ||||
| class GithubAppPermissionJob implements ShouldQueue, ShouldBeEncrypted | ||||
| class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
|     public $tries = 4; | ||||
| 
 | ||||
|     public function backoff(): int | ||||
|     { | ||||
|         return isDev() ? 1 : 3; | ||||
|     } | ||||
|     public function __construct(public GithubApp $github_app) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     public function __construct(public GithubApp $github_app) {} | ||||
| 
 | ||||
|     public function middleware(): array | ||||
|     { | ||||
|         return [(new WithoutOverlapping($this->github_app->uuid))]; | ||||
| @@ -40,7 +41,7 @@ class GithubAppPermissionJob implements ShouldQueue, ShouldBeEncrypted | ||||
|             $github_access_token = generate_github_jwt_token($this->github_app); | ||||
|             $response = Http::withHeaders([ | ||||
|                 'Authorization' => "Bearer $github_access_token", | ||||
|                 'Accept' => 'application/vnd.github+json' | ||||
|                 'Accept' => 'application/vnd.github+json', | ||||
|             ])->get("{$this->github_app->api_url}/app"); | ||||
|             $response = $response->json(); | ||||
|             $permissions = data_get($response, 'permissions'); | ||||
|   | ||||
| @@ -11,16 +11,15 @@ use Illuminate\Foundation\Bus\Dispatchable; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted | ||||
| class InstanceAutoUpdateJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
|     public $timeout = 600; | ||||
| 
 | ||||
|     public $tries = 1; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|     } | ||||
|     public function __construct() {} | ||||
| 
 | ||||
|     public function handle(): void | ||||
|     { | ||||
|   | ||||
							
								
								
									
										58
									
								
								app/Jobs/PullCoolifyImageJob.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								app/Jobs/PullCoolifyImageJob.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Jobs; | ||||
| 
 | ||||
| use App\Models\InstanceSettings; | ||||
| use App\Models\Server; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Contracts\Queue\ShouldBeEncrypted; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Foundation\Bus\Dispatchable; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| use Illuminate\Support\Facades\File; | ||||
| use Illuminate\Support\Facades\Http; | ||||
| 
 | ||||
| class PullCoolifyImageJob implements ShouldBeEncrypted, ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
|     public $timeout = 1000; | ||||
| 
 | ||||
|     public function __construct() {} | ||||
| 
 | ||||
|     public function handle(): void | ||||
|     { | ||||
|         try { | ||||
|             if (isDev() || isCloud()) { | ||||
|                 return; | ||||
|             } | ||||
|             $server = Server::findOrFail(0); | ||||
|             $response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json'); | ||||
|             if ($response->successful()) { | ||||
|                 $versions = $response->json(); | ||||
|                 File::put(base_path('versions.json'), json_encode($versions, JSON_PRETTY_PRINT)); | ||||
|             } | ||||
|             $latest_version = get_latest_version_of_coolify(); | ||||
|             instant_remote_process(["docker pull -q ghcr.io/coollabsio/coolify:{$latest_version}"], $server, false); | ||||
| 
 | ||||
|             $settings = InstanceSettings::get(); | ||||
|             $current_version = config('version'); | ||||
|             if (! $settings->is_auto_update_enabled) { | ||||
|                 return; | ||||
|             } | ||||
|             if ($latest_version === $current_version) { | ||||
|                 return; | ||||
|             } | ||||
|             if (version_compare($latest_version, $current_version, '<')) { | ||||
|                 return; | ||||
|             } | ||||
|             instant_remote_process([ | ||||
|                 'curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh', | ||||
|                 "bash /data/coolify/source/upgrade.sh $latest_version", | ||||
|             ], $server); | ||||
|         } catch (\Throwable $e) { | ||||
|             throw $e; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -11,7 +11,7 @@ use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Queue\Middleware\WithoutOverlapping; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| class PullHelperImageJob implements ShouldQueue, ShouldBeEncrypted | ||||
| class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
| @@ -26,9 +26,9 @@ class PullHelperImageJob implements ShouldQueue, ShouldBeEncrypted | ||||
|     { | ||||
|         return $this->server->uuid; | ||||
|     } | ||||
|     public function __construct(public Server $server) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     public function __construct(public Server $server) {} | ||||
| 
 | ||||
|     public function handle(): void | ||||
|     { | ||||
|         try { | ||||
|   | ||||
| @@ -12,7 +12,7 @@ use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Queue\Middleware\WithoutOverlapping; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| 
 | ||||
| class PullSentinelImageJob implements ShouldQueue, ShouldBeEncrypted | ||||
| class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
| @@ -27,15 +27,16 @@ class PullSentinelImageJob implements ShouldQueue, ShouldBeEncrypted | ||||
|     { | ||||
|         return $this->server->uuid; | ||||
|     } | ||||
|     public function __construct(public Server $server) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     public function __construct(public Server $server) {} | ||||
| 
 | ||||
|     public function handle(): void | ||||
|     { | ||||
|         try { | ||||
|             $version = get_latest_sentinel_version(); | ||||
|             if (! $version) { | ||||
|                 ray('Failed to get latest Sentinel version'); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             $local_version = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/version"'], $this->server, false); | ||||
| @@ -44,6 +45,7 @@ class PullSentinelImageJob implements ShouldQueue, ShouldBeEncrypted | ||||
|             } | ||||
|             if (version_compare($local_version, $version, '<')) { | ||||
|                 StartSentinel::run($this->server, $version, true); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|             ray('Sentinel image is up to date'); | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Andras Bacsai
					Andras Bacsai