Merge branch 'next' into feat/monaco-editor
This commit is contained in:
commit
73bc07c7fb
59
README.md
59
README.md
@ -1,17 +1,21 @@
|
|||||||
|

|
||||||
|
|
||||||
[](https://console.algora.io/org/coollabsio/bounties/new)
|
[](https://console.algora.io/org/coollabsio/bounties/new)
|
||||||
[](https://console.algora.io/org/coollabsio/bounties?status=open)
|
[](https://console.algora.io/org/coollabsio/bounties?status=open)
|
||||||
[](https://console.algora.io/org/coollabsio/bounties?status=completed)
|
[](https://console.algora.io/org/coollabsio/bounties?status=completed)
|
||||||
|
|
||||||
# About the Project
|
# About the Project
|
||||||
|
|
||||||
Coolify is an open-source & self-hostable alternative to Heroku / Netlify / Vercel / etc.
|
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
|
# Installation
|
||||||
|
|
||||||
@ -22,36 +26,41 @@ You can find the installation script source [here](./scripts/install.sh).
|
|||||||
|
|
||||||
# Support
|
# Support
|
||||||
|
|
||||||
Contact us [here](https://coolify.io/docs/contact).
|
Contact us at [coolify.io/docs/contact](https://coolify.io/docs/contact).
|
||||||
|
|
||||||
# Donations
|
# 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!
|
Thank you so much!
|
||||||
|
|
||||||
Special thanks to our biggest sponsors!
|
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="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
|
||||||
<a href="http://htznr.li/CoolifyXHetzner" target="_blank"><img src="./other/logos/hetzner.jpg" alt="hetzner logo" width="200"/></a>
|
<a href="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="200"/></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/?utm_source=coolify.io" target="_blank"><img src="./other/logos/bc.png" alt="bc direct logo" width="200"/></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/?utm_source=coolify.io" target="_blank"><img src="./other/logos/quant.svg" alt="quantcdn 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/?utm_source=coolify.io" target="_blank"><img src="./other/logos/arcjet.svg" alt="arcjet logo" width="200"/></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>
|
||||||
|
<a href="https://trieve.ai/?ref=coolify.io" target="_blank"><img src="./other/logos/trieve_bg.png" alt="trieve logo" width="180"/></a>
|
||||||
|
|
||||||
## Github Sponsors ($40+)
|
## Github Sponsors ($40+)
|
||||||
<a href="https://serpapi.com/?utm_source=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>
|
<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/?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://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/?utm_source=coolify.io">
|
<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>
|
<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://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?utm_source=coolify.io"> <img src="https://github.com/Flint-company.png" width="60px" alt="FlintCompany"/></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/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></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/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
|
<a href="https://cryptojobslist.com/?ref=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://codext.link/coolify-io?ref=coolify.io"><img src="./other/logos/codext.jpg" width="60px" alt="Codext" /></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://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/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/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>
|
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
||||||
@ -83,9 +92,9 @@ Special thanks to our biggest sponsors!
|
|||||||
|
|
||||||
# Cloud
|
# 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?
|
## 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.
|
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.
|
||||||
@ -109,7 +118,7 @@ By subscribing to the cloud version, you get the Coolify server for the same pri
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</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>
|
<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>
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Events\DatabaseStatusChanged;
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
use App\Models\StandaloneClickhouse;
|
use App\Models\StandaloneClickhouse;
|
||||||
use App\Models\StandaloneDragonfly;
|
use App\Models\StandaloneDragonfly;
|
||||||
@ -28,5 +29,6 @@ class StopDatabaseProxy
|
|||||||
instant_remote_process(["docker rm -f {$uuid}-proxy"], $server);
|
instant_remote_process(["docker rm -f {$uuid}-proxy"], $server);
|
||||||
$database->is_public = false;
|
$database->is_public = false;
|
||||||
$database->save();
|
$database->save();
|
||||||
|
DatabaseStatusChanged::dispatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,15 @@ class StartSentinel
|
|||||||
public function handle(Server $server, $version = 'latest', bool $restart = false)
|
public function handle(Server $server, $version = 'latest', bool $restart = false)
|
||||||
{
|
{
|
||||||
if ($restart) {
|
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([
|
instant_remote_process([
|
||||||
"docker run --rm --pull always -d -e \"SCHEDULER=true\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
|
"docker run --rm --pull always -d -e \"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',
|
'chown -R 9999:root /data/coolify/metrics /data/coolify/logs',
|
||||||
'chmod -R 700 /data/coolify/metrics /data/coolify/logs',
|
'chmod -R 700 /data/coolify/metrics /data/coolify/logs',
|
||||||
], $server, false);
|
], $server, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ namespace App\Console\Commands;
|
|||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
use App\Jobs\CleanupHelperContainersJob;
|
use App\Jobs\CleanupHelperContainersJob;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
|
use App\Models\Environment;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@ -24,6 +25,8 @@ class Init extends Command
|
|||||||
get_public_ips();
|
get_public_ips();
|
||||||
$full_cleanup = $this->option('full-cleanup');
|
$full_cleanup = $this->option('full-cleanup');
|
||||||
$cleanup_deployments = $this->option('cleanup-deployments');
|
$cleanup_deployments = $this->option('cleanup-deployments');
|
||||||
|
|
||||||
|
$this->replace_slash_in_environment_name();
|
||||||
if ($cleanup_deployments) {
|
if ($cleanup_deployments) {
|
||||||
echo "Running cleanup deployments.\n";
|
echo "Running cleanup deployments.\n";
|
||||||
$this->cleanup_in_progress_application_deployments();
|
$this->cleanup_in_progress_application_deployments();
|
||||||
@ -150,4 +153,15 @@ class Init extends Command
|
|||||||
echo "Error: {$e->getMessage()}\n";
|
echo "Error: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function replace_slash_in_environment_name()
|
||||||
|
{
|
||||||
|
$environments = Environment::all();
|
||||||
|
foreach ($environments as $environment) {
|
||||||
|
if (str_contains($environment->name, '/')) {
|
||||||
|
$environment->name = str_replace('/', '-', $environment->name);
|
||||||
|
$environment->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ class Kernel extends ConsoleKernel
|
|||||||
{
|
{
|
||||||
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
if (config('coolify.is_sentinel_enabled')) {
|
if ($server->isSentinelEnabled()) {
|
||||||
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
|
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
|
||||||
}
|
}
|
||||||
$schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
|
$schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
|
||||||
|
@ -11,6 +11,5 @@ class ServerMetadata extends Data
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
public ?ProxyTypes $type,
|
public ?ProxyTypes $type,
|
||||||
public ?ProxyStatus $status
|
public ?ProxyStatus $status
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,5 @@ class ProxyStarted
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public function __construct(public $data)
|
public function __construct(public $data) {}
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,4 @@ namespace App\Exceptions;
|
|||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
class ProcessException extends Exception
|
class ProcessException extends Exception {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
183
app/Http/Controllers/Api/Applications.php
Normal file
183
app/Http/Controllers/Api/Applications.php
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Actions\Application\StopApplication;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Project;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Applications extends Controller
|
||||||
|
{
|
||||||
|
public function applications(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$projects = Project::where('team_id', $teamId)->get();
|
||||||
|
$applications = collect();
|
||||||
|
$applications->push($projects->pluck('applications')->flatten());
|
||||||
|
$applications = $applications->flatten();
|
||||||
|
|
||||||
|
return response()->json($applications);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function application_by_uuid(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$uuid = $request->route('uuid');
|
||||||
|
if (! $uuid) {
|
||||||
|
return response()->json(['error' => 'UUID is required.'], 400);
|
||||||
|
}
|
||||||
|
$application = Application::where('uuid', $uuid)->first();
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json(['error' => 'Application not found.'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json($application);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update_by_uuid(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->collect()->count() == 0) {
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'No data provided.',
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
$application = Application::where('uuid', $request->uuid)->first();
|
||||||
|
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Application not found',
|
||||||
|
], 404);
|
||||||
|
}
|
||||||
|
ray($request->collect());
|
||||||
|
|
||||||
|
// if ($request->has('domains')) {
|
||||||
|
// $existingDomains = explode(',', $application->fqdn);
|
||||||
|
// $newDomains = $request->domains;
|
||||||
|
// $filteredNewDomains = array_filter($newDomains, function ($domain) use ($existingDomains) {
|
||||||
|
// return ! in_array($domain, $existingDomains);
|
||||||
|
// });
|
||||||
|
// $mergedDomains = array_unique(array_merge($existingDomains, $filteredNewDomains));
|
||||||
|
// $application->fqdn = implode(',', $mergedDomains);
|
||||||
|
// $application->custom_labels = base64_encode(implode("\n ", generateLabelsApplication($application)));
|
||||||
|
// $application->save();
|
||||||
|
// }
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'Application updated successfully.',
|
||||||
|
'application' => serialize_api_response($application),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function action_deploy(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$force = $request->query->get('force') ?? false;
|
||||||
|
$instant_deploy = $request->query->get('instant_deploy') ?? false;
|
||||||
|
$uuid = $request->route('uuid');
|
||||||
|
if (! $uuid) {
|
||||||
|
return response()->json(['error' => 'UUID is required.'], 400);
|
||||||
|
}
|
||||||
|
$application = Application::where('uuid', $uuid)->first();
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json(['error' => 'Application not found.'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: $force,
|
||||||
|
is_api: true,
|
||||||
|
no_questions_asked: $instant_deploy
|
||||||
|
);
|
||||||
|
|
||||||
|
return response()->json(
|
||||||
|
[
|
||||||
|
'message' => 'Deployment request queued.',
|
||||||
|
'deployment_uuid' => $deployment_uuid->toString(),
|
||||||
|
'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(),
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function action_stop(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$uuid = $request->route('uuid');
|
||||||
|
$sync = $request->query->get('sync') ?? false;
|
||||||
|
if (! $uuid) {
|
||||||
|
return response()->json(['error' => 'UUID is required.'], 400);
|
||||||
|
}
|
||||||
|
$application = Application::where('uuid', $uuid)->first();
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json(['error' => 'Application not found.'], 404);
|
||||||
|
}
|
||||||
|
if ($sync) {
|
||||||
|
StopApplication::run($application);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Stopped the application.'], 200);
|
||||||
|
} else {
|
||||||
|
StopApplication::dispatch($application);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Stopping request queued.'], 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function action_restart(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$uuid = $request->route('uuid');
|
||||||
|
if (! $uuid) {
|
||||||
|
return response()->json(['error' => 'UUID is required.'], 400);
|
||||||
|
}
|
||||||
|
$application = Application::where('uuid', $uuid)->first();
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json(['error' => 'Application not found.'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
restart_only: true,
|
||||||
|
is_api: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
return response()->json(
|
||||||
|
[
|
||||||
|
'message' => 'Restart request queued.',
|
||||||
|
'deployment_uuid' => $deployment_uuid->toString(),
|
||||||
|
'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(),
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -38,7 +38,25 @@ class Deploy extends Controller
|
|||||||
'status',
|
'status',
|
||||||
])->sortBy('id')->toArray();
|
])->sortBy('id')->toArray();
|
||||||
|
|
||||||
return response()->json($deployments_per_server, 200);
|
return response()->json(serialize_api_response($deployments_per_server), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deployment_by_uuid(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$uuid = $request->route('uuid');
|
||||||
|
if (! $uuid) {
|
||||||
|
return response()->json(['error' => 'UUID is required.'], 400);
|
||||||
|
}
|
||||||
|
$deployment = ApplicationDeploymentQueue::where('deployment_uuid', $uuid)->first()->makeHidden('logs');
|
||||||
|
if (! $deployment) {
|
||||||
|
return response()->json(['error' => 'Deployment not found.'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json(serialize_api_response($deployment), 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deploy(Request $request)
|
public function deploy(Request $request)
|
||||||
|
@ -3,102 +3,52 @@
|
|||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\Application;
|
||||||
use App\Models\Project as ModelsProject;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
class Domains extends Controller
|
class Domains extends Controller
|
||||||
{
|
{
|
||||||
public function domains(Request $request)
|
public function deleteDomains(Request $request)
|
||||||
{
|
{
|
||||||
$teamId = get_team_id_from_token();
|
$teamId = get_team_id_from_token();
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
return invalid_token();
|
return invalid_token();
|
||||||
}
|
}
|
||||||
$projects = ModelsProject::where('team_id', $teamId)->get();
|
$validator = Validator::make($request->all(), [
|
||||||
$domains = collect();
|
'uuid' => 'required|string|exists:applications,uuid',
|
||||||
$applications = $projects->pluck('applications')->flatten();
|
'domains' => 'required|array',
|
||||||
$settings = InstanceSettings::get();
|
'domains.*' => 'required|string|distinct',
|
||||||
if ($applications->count() > 0) {
|
]);
|
||||||
foreach ($applications as $application) {
|
|
||||||
$ip = $application->destination->server->ip;
|
|
||||||
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
|
|
||||||
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
|
|
||||||
});
|
|
||||||
if ($ip === 'host.docker.internal') {
|
|
||||||
if ($settings->public_ipv4) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $settings->public_ipv4,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if ($settings->public_ipv6) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $settings->public_ipv6,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $ip,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $ip,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$services = $projects->pluck('services')->flatten();
|
|
||||||
if ($services->count() > 0) {
|
|
||||||
foreach ($services as $service) {
|
|
||||||
$service_applications = $service->applications;
|
|
||||||
if ($service_applications->count() > 0) {
|
|
||||||
foreach ($service_applications as $application) {
|
|
||||||
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
|
|
||||||
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
|
|
||||||
});
|
|
||||||
if ($ip === 'host.docker.internal') {
|
|
||||||
if ($settings->public_ipv4) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $settings->public_ipv4,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if ($settings->public_ipv6) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $settings->public_ipv6,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $ip,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $ip,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$domains = $domains->groupBy('ip')->map(function ($domain) {
|
|
||||||
return $domain->pluck('domain')->flatten();
|
|
||||||
})->map(function ($domain, $ip) {
|
|
||||||
return [
|
|
||||||
'ip' => $ip,
|
|
||||||
'domains' => $domain,
|
|
||||||
];
|
|
||||||
})->values();
|
|
||||||
|
|
||||||
return response()->json($domains);
|
if ($validator->fails()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Validation failed',
|
||||||
|
'errors' => $validator->errors(),
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$application = Application::where('uuid', $request->uuid)->first();
|
||||||
|
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Application not found',
|
||||||
|
], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingDomains = explode(',', $application->fqdn);
|
||||||
|
$domainsToDelete = $request->domains;
|
||||||
|
$updatedDomains = array_diff($existingDomains, $domainsToDelete);
|
||||||
|
$application->fqdn = implode(',', $updatedDomains);
|
||||||
|
$application->custom_labels = base64_encode(implode("\n ", generateLabelsApplication($application)));
|
||||||
|
$application->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Domains updated successfully',
|
||||||
|
'application' => $application,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\Project;
|
||||||
use App\Models\Server as ModelsServer;
|
use App\Models\Server as ModelsServer;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
@ -59,4 +62,106 @@ class Server extends Controller
|
|||||||
|
|
||||||
return response()->json($server);
|
return response()->json($server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function get_domains_by_server(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$uuid = $request->query->get('uuid');
|
||||||
|
if ($uuid) {
|
||||||
|
$domains = Application::getDomainsByUuid($uuid);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'uuid' => $uuid,
|
||||||
|
'domains' => $domains,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$projects = Project::where('team_id', $teamId)->get();
|
||||||
|
$domains = collect();
|
||||||
|
$applications = $projects->pluck('applications')->flatten();
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
if ($applications->count() > 0) {
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$ip = $application->destination->server->ip;
|
||||||
|
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
|
||||||
|
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
|
||||||
|
});
|
||||||
|
if ($ip === 'host.docker.internal') {
|
||||||
|
if ($settings->public_ipv4) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $settings->public_ipv4,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($settings->public_ipv6) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $settings->public_ipv6,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $ip,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $ip,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$services = $projects->pluck('services')->flatten();
|
||||||
|
if ($services->count() > 0) {
|
||||||
|
foreach ($services as $service) {
|
||||||
|
$service_applications = $service->applications;
|
||||||
|
if ($service_applications->count() > 0) {
|
||||||
|
foreach ($service_applications as $application) {
|
||||||
|
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
|
||||||
|
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
|
||||||
|
});
|
||||||
|
if ($ip === 'host.docker.internal') {
|
||||||
|
if ($settings->public_ipv4) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $settings->public_ipv4,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($settings->public_ipv6) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $settings->public_ipv6,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $ip,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $ip,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$domains = $domains->groupBy('ip')->map(function ($domain) {
|
||||||
|
return $domain->pluck('domain')->flatten();
|
||||||
|
})->map(function ($domain, $ip) {
|
||||||
|
return [
|
||||||
|
'ip' => $ip,
|
||||||
|
'domains' => $domain,
|
||||||
|
];
|
||||||
|
})->values();
|
||||||
|
|
||||||
|
return response()->json($domains);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
|
|
||||||
class OauthController extends Controller
|
class OauthController extends Controller
|
||||||
{
|
{
|
||||||
@ -20,6 +22,11 @@ class OauthController extends Controller
|
|||||||
$oauthUser = get_socialite_provider($provider)->user();
|
$oauthUser = get_socialite_provider($provider)->user();
|
||||||
$user = User::whereEmail($oauthUser->email)->first();
|
$user = User::whereEmail($oauthUser->email)->first();
|
||||||
if (! $user) {
|
if (! $user) {
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
if (! $settings->is_registration_enabled) {
|
||||||
|
abort(403, 'Registration is disabled');
|
||||||
|
}
|
||||||
|
|
||||||
$user = User::create([
|
$user = User::create([
|
||||||
'name' => $oauthUser->name,
|
'name' => $oauthUser->name,
|
||||||
'email' => $oauthUser->email,
|
'email' => $oauthUser->email,
|
||||||
@ -31,7 +38,9 @@ class OauthController extends Controller
|
|||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return redirect()->route('login')->withErrors([__('auth.failed.callback')]);
|
$errorCode = $e instanceof HttpException ? 'auth.failed' : 'auth.failed.callback';
|
||||||
|
|
||||||
|
return redirect()->route('login')->withErrors([__($errorCode)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,12 +130,23 @@ class Bitbucket extends Controller
|
|||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found) {
|
if (! $found) {
|
||||||
ApplicationPreview::create([
|
if ($application->build_pack === 'dockercompose') {
|
||||||
'git_type' => 'bitbucket',
|
$pr_app = ApplicationPreview::create([
|
||||||
'application_id' => $application->id,
|
'git_type' => 'bitbucket',
|
||||||
'pull_request_id' => $pull_request_id,
|
'application_id' => $application->id,
|
||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_id' => $pull_request_id,
|
||||||
]);
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'bitbucket',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
|
@ -165,12 +165,24 @@ class Gitea extends Controller
|
|||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found) {
|
if (! $found) {
|
||||||
ApplicationPreview::create([
|
if ($application->build_pack === 'dockercompose') {
|
||||||
'git_type' => 'gitea',
|
$pr_app = ApplicationPreview::create([
|
||||||
'application_id' => $application->id,
|
'git_type' => 'gitea',
|
||||||
'pull_request_id' => $pull_request_id,
|
'application_id' => $application->id,
|
||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_id' => $pull_request_id,
|
||||||
]);
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'gitea',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
|
@ -170,12 +170,23 @@ class Github extends Controller
|
|||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found) {
|
if (! $found) {
|
||||||
ApplicationPreview::create([
|
if ($application->build_pack === 'dockercompose') {
|
||||||
'git_type' => 'github',
|
$pr_app = ApplicationPreview::create([
|
||||||
'application_id' => $application->id,
|
'git_type' => 'github',
|
||||||
'pull_request_id' => $pull_request_id,
|
'application_id' => $application->id,
|
||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_id' => $pull_request_id,
|
||||||
]);
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'github',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
|
@ -180,12 +180,23 @@ class Gitlab extends Controller
|
|||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (! $found) {
|
if (! $found) {
|
||||||
ApplicationPreview::create([
|
if ($application->build_pack === 'dockercompose') {
|
||||||
'git_type' => 'gitlab',
|
$pr_app = ApplicationPreview::create([
|
||||||
'application_id' => $application->id,
|
'git_type' => 'gitlab',
|
||||||
'pull_request_id' => $pull_request_id,
|
'application_id' => $application->id,
|
||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_id' => $pull_request_id,
|
||||||
]);
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'gitlab',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
|
@ -9,6 +9,7 @@ use App\Events\ApplicationStatusChanged;
|
|||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
|
use App\Models\EnvironmentVariable;
|
||||||
use App\Models\GithubApp;
|
use App\Models\GithubApp;
|
||||||
use App\Models\GitlabApp;
|
use App\Models\GitlabApp;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@ -339,7 +340,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
private function post_deployment()
|
private function post_deployment()
|
||||||
{
|
{
|
||||||
if ($this->server->isProxyShouldRun()) {
|
if ($this->server->isProxyShouldRun()) {
|
||||||
GetContainersStatus::dispatch($this->server);
|
GetContainersStatus::dispatch($this->server)->onQueue('high');
|
||||||
// dispatch(new ContainerStatusJob($this->server));
|
// dispatch(new ContainerStatusJob($this->server));
|
||||||
}
|
}
|
||||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||||
@ -827,6 +828,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||||
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
||||||
}
|
}
|
||||||
|
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
||||||
|
$envs->push("COOLIFY_CONTAINER_NAME={$this->container_name}");
|
||||||
|
}
|
||||||
foreach ($sorted_environment_variables_preview as $env) {
|
foreach ($sorted_environment_variables_preview as $env) {
|
||||||
$real_value = $env->real_value;
|
$real_value = $env->real_value;
|
||||||
if ($env->version === '4.0.0-beta.239') {
|
if ($env->version === '4.0.0-beta.239') {
|
||||||
@ -868,6 +872,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
if ($this->application->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
if ($this->application->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||||
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
||||||
}
|
}
|
||||||
|
if ($this->application->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
||||||
|
$envs->push("COOLIFY_CONTAINER_NAME={$this->container_name}");
|
||||||
|
}
|
||||||
foreach ($sorted_environment_variables as $env) {
|
foreach ($sorted_environment_variables as $env) {
|
||||||
$real_value = $env->real_value;
|
$real_value = $env->real_value;
|
||||||
if ($env->version === '4.0.0-beta.239') {
|
if ($env->version === '4.0.0-beta.239') {
|
||||||
@ -877,7 +884,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$real_value = '\''.$real_value.'\'';
|
$real_value = '\''.$real_value.'\'';
|
||||||
} else {
|
} else {
|
||||||
$real_value = escapeEnvVariables($env->real_value);
|
$real_value = escapeEnvVariables($env->real_value);
|
||||||
ray($real_value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$envs->push($env->key.'='.$real_value);
|
$envs->push($env->key.'='.$real_value);
|
||||||
@ -946,9 +952,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function framework_based_notification()
|
private function laravel_finetunes()
|
||||||
{
|
{
|
||||||
// Laravel old env variables
|
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0) {
|
||||||
$nixpacks_php_fallback_path = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
|
$nixpacks_php_fallback_path = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
|
||||||
$nixpacks_php_root_dir = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
|
$nixpacks_php_root_dir = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
|
||||||
@ -956,9 +961,22 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$nixpacks_php_fallback_path = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
|
$nixpacks_php_fallback_path = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
|
||||||
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
|
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
|
||||||
}
|
}
|
||||||
if ($nixpacks_php_fallback_path?->value === '/index.php' && $nixpacks_php_root_dir?->value === '/app/public' && $this->newVersionIsHealthy === false) {
|
if (! $nixpacks_php_fallback_path) {
|
||||||
$this->application_deployment_queue->addLogEntry('There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/resources/laravel', 'stderr');
|
$nixpacks_php_fallback_path = new EnvironmentVariable();
|
||||||
|
$nixpacks_php_fallback_path->key = 'NIXPACKS_PHP_FALLBACK_PATH';
|
||||||
|
$nixpacks_php_fallback_path->value = '/index.php';
|
||||||
|
$nixpacks_php_fallback_path->application_id = $this->application->id;
|
||||||
|
$nixpacks_php_fallback_path->save();
|
||||||
}
|
}
|
||||||
|
if (! $nixpacks_php_root_dir) {
|
||||||
|
$nixpacks_php_root_dir = new EnvironmentVariable();
|
||||||
|
$nixpacks_php_root_dir->key = 'NIXPACKS_PHP_ROOT_DIR';
|
||||||
|
$nixpacks_php_root_dir->value = '/app/public';
|
||||||
|
$nixpacks_php_root_dir->application_id = $this->application->id;
|
||||||
|
$nixpacks_php_root_dir->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$nixpacks_php_fallback_path, $nixpacks_php_root_dir];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function rolling_update()
|
private function rolling_update()
|
||||||
@ -1005,7 +1023,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->application_deployment_queue->addLogEntry('Rolling update completed.');
|
$this->application_deployment_queue->addLogEntry('Rolling update completed.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->framework_based_notification();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function health_check()
|
private function health_check()
|
||||||
@ -1366,17 +1383,20 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
throw new RuntimeException('Nixpacks failed to detect the application type. Please check the documentation of Nixpacks: https://nixpacks.com/docs/providers');
|
throw new RuntimeException('Nixpacks failed to detect the application type. Please check the documentation of Nixpacks: https://nixpacks.com/docs/providers');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->saved_outputs->get('nixpacks_plan')) {
|
if ($this->saved_outputs->get('nixpacks_plan')) {
|
||||||
$this->nixpacks_plan = $this->saved_outputs->get('nixpacks_plan');
|
$this->nixpacks_plan = $this->saved_outputs->get('nixpacks_plan');
|
||||||
if ($this->nixpacks_plan) {
|
if ($this->nixpacks_plan) {
|
||||||
$this->application_deployment_queue->addLogEntry("Found application type: {$this->nixpacks_type}.");
|
$this->application_deployment_queue->addLogEntry("Found application type: {$this->nixpacks_type}.");
|
||||||
$this->application_deployment_queue->addLogEntry("If you need further customization, please check the documentation of Nixpacks: https://nixpacks.com/docs/providers/{$this->nixpacks_type}");
|
$this->application_deployment_queue->addLogEntry("If you need further customization, please check the documentation of Nixpacks: https://nixpacks.com/docs/providers/{$this->nixpacks_type}");
|
||||||
$parsed = Toml::Parse($this->nixpacks_plan);
|
$parsed = Toml::Parse($this->nixpacks_plan);
|
||||||
|
|
||||||
// Do any modifications here
|
// Do any modifications here
|
||||||
$this->generate_env_variables();
|
$this->generate_env_variables();
|
||||||
$merged_envs = $this->env_args->merge(collect(data_get($parsed, 'variables', [])));
|
$merged_envs = $this->env_args->merge(collect(data_get($parsed, 'variables', [])));
|
||||||
$aptPkgs = data_get($parsed, 'phases.setup.aptPkgs', []);
|
$aptPkgs = data_get($parsed, 'phases.setup.aptPkgs', []);
|
||||||
if (count($aptPkgs) === 0) {
|
if (count($aptPkgs) === 0) {
|
||||||
|
$aptPkgs = ['curl', 'wget'];
|
||||||
data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']);
|
data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']);
|
||||||
} else {
|
} else {
|
||||||
if (! in_array('curl', $aptPkgs)) {
|
if (! in_array('curl', $aptPkgs)) {
|
||||||
@ -1388,6 +1408,12 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
data_set($parsed, 'phases.setup.aptPkgs', $aptPkgs);
|
data_set($parsed, 'phases.setup.aptPkgs', $aptPkgs);
|
||||||
}
|
}
|
||||||
data_set($parsed, 'variables', $merged_envs->toArray());
|
data_set($parsed, 'variables', $merged_envs->toArray());
|
||||||
|
$is_laravel = data_get($parsed, 'variables.IS_LARAVEL', false);
|
||||||
|
if ($is_laravel) {
|
||||||
|
$variables = $this->laravel_finetunes();
|
||||||
|
data_set($parsed, 'variables.NIXPACKS_PHP_FALLBACK_PATH', $variables[0]->value);
|
||||||
|
data_set($parsed, 'variables.NIXPACKS_PHP_ROOT_DIR', $variables[1]->value);
|
||||||
|
}
|
||||||
$this->nixpacks_plan = json_encode($parsed, JSON_PRETTY_PRINT);
|
$this->nixpacks_plan = json_encode($parsed, JSON_PRETTY_PRINT);
|
||||||
$this->application_deployment_queue->addLogEntry("Final Nixpacks plan: {$this->nixpacks_plan}", hidden: true);
|
$this->application_deployment_queue->addLogEntry("Final Nixpacks plan: {$this->nixpacks_plan}", hidden: true);
|
||||||
}
|
}
|
||||||
@ -1841,13 +1867,25 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
||||||
if ($this->force_rebuild) {
|
if ($this->force_rebuild) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
||||||
]);
|
]);
|
||||||
|
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->build_image_name} {$this->workdir}";
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
||||||
]);
|
]);
|
||||||
|
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->build_image_name} {$this->workdir}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$base64_build_command = base64_encode($build_command);
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
||||||
|
]
|
||||||
|
);
|
||||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
||||||
} else {
|
} else {
|
||||||
if ($this->force_rebuild) {
|
if ($this->force_rebuild) {
|
||||||
@ -1866,7 +1904,6 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||||
WORKDIR /usr/share/nginx/html/
|
WORKDIR /usr/share/nginx/html/
|
||||||
LABEL coolify.deploymentId={$this->deployment_uuid}
|
LABEL coolify.deploymentId={$this->deployment_uuid}
|
||||||
@ -1929,13 +1966,24 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
||||||
if ($this->force_rebuild) {
|
if ($this->force_rebuild) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
||||||
]);
|
]);
|
||||||
|
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
||||||
]);
|
]);
|
||||||
|
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||||
}
|
}
|
||||||
|
$base64_build_command = base64_encode($build_command);
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
||||||
|
]
|
||||||
|
);
|
||||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
||||||
} else {
|
} else {
|
||||||
if ($this->force_rebuild) {
|
if ($this->force_rebuild) {
|
||||||
@ -2184,10 +2232,14 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
ray($code);
|
ray($code);
|
||||||
if ($code !== 69420) {
|
if ($code !== 69420) {
|
||||||
// 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one
|
// 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one
|
||||||
$this->application_deployment_queue->addLogEntry('Deployment failed. Removing the new version of your application.', 'stderr');
|
if ($this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name)) {
|
||||||
$this->execute_remote_command(
|
// do not remove already running container
|
||||||
["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true]
|
} else {
|
||||||
);
|
$this->application_deployment_queue->addLogEntry('Deployment failed. Removing the new version of your application.', 'stderr');
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,7 @@ class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
public ApplicationPreview $preview,
|
public ApplicationPreview $preview,
|
||||||
public ProcessStatus $status,
|
public ProcessStatus $status,
|
||||||
public ?string $deployment_uuid = null
|
public ?string $deployment_uuid = null
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
@ -19,9 +19,7 @@ class CheckLogDrainContainerJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
|
@ -14,9 +14,7 @@ class CheckResaleLicenseJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
@ -15,9 +15,7 @@ class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, S
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
@ -16,10 +16,7 @@ class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, Sho
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {}
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// public function uniqueId(): string
|
// public function uniqueId(): string
|
||||||
// {
|
// {
|
||||||
|
@ -23,9 +23,7 @@ class ContainerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
return isDev() ? 1 : 3;
|
return isDev() ? 1 : 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
|
@ -23,8 +23,7 @@ class CoolifyTask implements ShouldBeEncrypted, ShouldQueue
|
|||||||
public bool $ignore_errors = false,
|
public bool $ignore_errors = false,
|
||||||
public $call_event_on_finish = null,
|
public $call_event_on_finish = null,
|
||||||
public $call_event_data = null
|
public $call_event_data = null
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
|
@ -18,9 +18,7 @@ class DatabaseBackupStatusJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
@ -28,9 +28,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
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()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
@ -22,9 +22,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public ?int $usageBefore = null;
|
public ?int $usageBefore = null;
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
@ -62,7 +60,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
Log::info('No need to clean up '.$this->server->name);
|
Log::info('No need to clean up '.$this->server->name);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('DockerCleanupJob failed with: '.$e->getMessage());
|
// send_internal_notification('DockerCleanupJob failed with: '.$e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,7 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
return isDev() ? 1 : 3;
|
return isDev() ? 1 : 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public GithubApp $github_app)
|
public function __construct(public GithubApp $github_app) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
|
@ -19,9 +19,7 @@ class InstanceAutoUpdateJob implements ShouldBeEncrypted, ShouldBeUnique, Should
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
@ -19,9 +19,7 @@ class PullCoolifyImageJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public $timeout = 1000;
|
public $timeout = 1000;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
@ -27,9 +27,7 @@ class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
return $this->server->uuid;
|
return $this->server->uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
@ -28,9 +28,7 @@ class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
return $this->server->uuid;
|
return $this->server->uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
@ -52,7 +50,7 @@ class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
ray('Sentinel image is up to date');
|
ray('Sentinel image is up to date');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('PullSentinelImageJob failed with: '.$e->getMessage());
|
// send_internal_notification('PullSentinelImageJob failed with: '.$e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,7 @@ class PullTemplatesFromCDN implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public $timeout = 10;
|
public $timeout = 10;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
@ -17,9 +17,7 @@ class PullVersionsFromCDN implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public $timeout = 10;
|
public $timeout = 10;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
@ -14,9 +14,7 @@ class SendConfirmationForWaitlistJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct(public string $email, public string $uuid)
|
public function __construct(public string $email, public string $uuid) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
@ -31,8 +31,7 @@ class SendMessageToDiscordJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
public string $text,
|
public string $text,
|
||||||
public string $webhookUrl
|
public string $webhookUrl
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
|
@ -33,8 +33,7 @@ class SendMessageToTelegramJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
public string $token,
|
public string $token,
|
||||||
public string $chatId,
|
public string $chatId,
|
||||||
public ?string $topicId = null,
|
public ?string $topicId = null,
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
|
@ -16,9 +16,7 @@ class ServerFilesFromServerJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct(public ServiceApplication|ServiceDatabase|Application $resource)
|
public function __construct(public ServiceApplication|ServiceDatabase|Application $resource) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
@ -24,9 +24,7 @@ class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
return isDev() ? 1 : 3;
|
return isDev() ? 1 : 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public Team $team)
|
public function __construct(public Team $team) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
|
@ -25,9 +25,7 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
return isDev() ? 1 : 3;
|
return isDev() ? 1 : 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
@ -48,7 +46,7 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
if ($this->server->isFunctional()) {
|
if ($this->server->isFunctional()) {
|
||||||
$this->cleanup(notify: false);
|
$this->cleanup(notify: false);
|
||||||
$this->remove_unnecessary_coolify_yaml();
|
$this->remove_unnecessary_coolify_yaml();
|
||||||
if (config('coolify.is_sentinel_enabled')) {
|
if ($this->server->isSentinelEnabled()) {
|
||||||
$this->server->checkSentinel();
|
$this->server->checkSentinel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,7 @@ class ServerStorageSaveJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct(public LocalFileVolume $localFileVolume)
|
public function __construct(public LocalFileVolume $localFileVolume) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
@ -15,9 +15,7 @@ class SubscriptionInvoiceFailedJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct(protected Team $team)
|
public function __construct(protected Team $team) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
@ -17,8 +17,7 @@ class SubscriptionTrialEndedJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public Team $team
|
public Team $team
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
@ -17,8 +17,7 @@ class SubscriptionTrialEndsSoonJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public Team $team
|
public Team $team
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
@ -9,9 +9,7 @@ use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
|
|||||||
|
|
||||||
class MaintenanceModeDisabledNotification
|
class MaintenanceModeDisabledNotification
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(EventsMaintenanceModeDisabled $event): void
|
public function handle(EventsMaintenanceModeDisabled $event): void
|
||||||
{
|
{
|
||||||
|
@ -9,9 +9,7 @@ class ProxyStartedNotification
|
|||||||
{
|
{
|
||||||
public Server $server;
|
public Server $server;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(ProxyStarted $event): void
|
public function handle(ProxyStarted $event): void
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,7 @@ use Livewire\Component;
|
|||||||
|
|
||||||
class Index extends Component
|
class Index extends Component
|
||||||
{
|
{
|
||||||
protected $listeners = ['serverInstalled' => 'validateServer'];
|
protected $listeners = ['refreshBoardingIndex' => 'validateServer'];
|
||||||
|
|
||||||
public string $currentState = 'welcome';
|
public string $currentState = 'welcome';
|
||||||
|
|
||||||
|
@ -54,9 +54,9 @@ class DeploymentNavbar extends Component
|
|||||||
|
|
||||||
public function cancel()
|
public function cancel()
|
||||||
{
|
{
|
||||||
|
$kill_command = "docker rm -f {$this->application_deployment_queue->deployment_uuid}";
|
||||||
|
$server_id = $this->application_deployment_queue->server_id ?? $this->application->destination->server_id;
|
||||||
try {
|
try {
|
||||||
$kill_command = "docker rm -f {$this->application_deployment_queue->deployment_uuid}";
|
|
||||||
$server_id = $this->application_deployment_queue->server_id ?? $this->application->destination->server_id;
|
|
||||||
$server = Server::find($server_id);
|
$server = Server::find($server_id);
|
||||||
if ($this->application_deployment_queue->logs) {
|
if ($this->application_deployment_queue->logs) {
|
||||||
$previous_logs = json_decode($this->application_deployment_queue->logs, associative: true, flags: JSON_THROW_ON_ERROR);
|
$previous_logs = json_decode($this->application_deployment_queue->logs, associative: true, flags: JSON_THROW_ON_ERROR);
|
||||||
@ -84,6 +84,7 @@ class DeploymentNavbar extends Component
|
|||||||
'current_process_id' => null,
|
'current_process_id' => null,
|
||||||
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
||||||
]);
|
]);
|
||||||
|
next_after_cancel($server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,7 +347,9 @@ class General extends Component
|
|||||||
public function submit($showToaster = true)
|
public function submit($showToaster = true)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->set_redirect();
|
if ($this->application->isDirty('redirect')) {
|
||||||
|
$this->set_redirect();
|
||||||
|
}
|
||||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||||
|
@ -45,7 +45,7 @@ class Heading extends Component
|
|||||||
public function check_status($showNotification = false)
|
public function check_status($showNotification = false)
|
||||||
{
|
{
|
||||||
if ($this->application->destination->server->isFunctional()) {
|
if ($this->application->destination->server->isFunctional()) {
|
||||||
GetContainersStatus::dispatch($this->application->destination->server);
|
GetContainersStatus::dispatch($this->application->destination->server)->onQueue('high');
|
||||||
// dispatch(new ContainerStatusJob($this->application->destination->server));
|
// dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||||
} else {
|
} else {
|
||||||
dispatch(new ServerStatusJob($this->application->destination->server));
|
dispatch(new ServerStatusJob($this->application->destination->server));
|
||||||
|
@ -131,6 +131,12 @@ class Previews extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function add_and_deploy(int $pull_request_id, ?string $pull_request_html_url = null)
|
||||||
|
{
|
||||||
|
$this->add($pull_request_id, $pull_request_html_url);
|
||||||
|
$this->deploy($pull_request_id, $pull_request_html_url);
|
||||||
|
}
|
||||||
|
|
||||||
public function deploy(int $pull_request_id, ?string $pull_request_html_url = null)
|
public function deploy(int $pull_request_id, ?string $pull_request_html_url = null)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@ -180,7 +186,7 @@ class Previews extends Component
|
|||||||
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
|
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GetContainersStatus::dispatchSync($this->application->destination->server);
|
GetContainersStatus::dispatchSync($this->application->destination->server)->onQueue('high');
|
||||||
$this->dispatch('reloadWindow');
|
$this->dispatch('reloadWindow');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
@ -12,7 +12,6 @@ use App\Actions\Database\StartPostgresql;
|
|||||||
use App\Actions\Database\StartRedis;
|
use App\Actions\Database\StartRedis;
|
||||||
use App\Actions\Database\StopDatabase;
|
use App\Actions\Database\StopDatabase;
|
||||||
use App\Actions\Docker\GetContainersStatus;
|
use App\Actions\Docker\GetContainersStatus;
|
||||||
use App\Jobs\ContainerStatusJob;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Heading extends Component
|
class Heading extends Component
|
||||||
|
@ -25,7 +25,17 @@ class General extends Component
|
|||||||
|
|
||||||
public ?string $db_url_public = null;
|
public ?string $db_url_public = null;
|
||||||
|
|
||||||
protected $listeners = ['refresh', 'save_init_script', 'delete_init_script'];
|
public function getListeners()
|
||||||
|
{
|
||||||
|
$userId = auth()->user()->id;
|
||||||
|
|
||||||
|
return [
|
||||||
|
"echo-private:user.{$userId},DatabaseStatusChanged" => 'database_stopped',
|
||||||
|
'refresh',
|
||||||
|
'save_init_script',
|
||||||
|
'delete_init_script',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'database.name' => 'required',
|
'database.name' => 'required',
|
||||||
@ -69,6 +79,11 @@ class General extends Component
|
|||||||
$this->server = data_get($this->database, 'destination.server');
|
$this->server = data_get($this->database, 'destination.server');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function database_stopped()
|
||||||
|
{
|
||||||
|
$this->dispatch('success', 'Database proxy stopped. Database is no longer publicly accessible.');
|
||||||
|
}
|
||||||
|
|
||||||
public function instantSaveAdvanced()
|
public function instantSaveAdvanced()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -128,8 +128,8 @@ class PublicGitRepository extends Component
|
|||||||
) {
|
) {
|
||||||
$this->repository_url = $this->repository_url.'.git';
|
$this->repository_url = $this->repository_url.'.git';
|
||||||
}
|
}
|
||||||
if (str($this->repository_url)->contains('github.com')) {
|
if (str($this->repository_url)->contains('github.com') && str($this->repository_url)->endsWith('.git')) {
|
||||||
$this->repository_url = str($this->repository_url)->before('.git')->value();
|
$this->repository_url = str($this->repository_url)->beforeLast('.git')->value();
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@ -140,7 +140,6 @@ class PublicGitRepository extends Component
|
|||||||
$this->get_branch();
|
$this->get_branch();
|
||||||
$this->selected_branch = $this->git_branch;
|
$this->selected_branch = $this->git_branch;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e->getMessage());
|
|
||||||
if (! $this->branch_found && $this->git_branch == 'main') {
|
if (! $this->branch_found && $this->git_branch == 'main') {
|
||||||
try {
|
try {
|
||||||
$this->git_branch = 'master';
|
$this->git_branch = 'master';
|
||||||
|
@ -176,10 +176,12 @@ class Select extends Component
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if (count($this->servers) === 1) {
|
if (count($this->servers) === 1) {
|
||||||
// $server = $this->servers->first();
|
$server = $this->servers->first();
|
||||||
// $this->setServer($server);
|
if ($server instanceof Server) {
|
||||||
// }
|
$this->setServer($server);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (! is_null($this->server)) {
|
if (! is_null($this->server)) {
|
||||||
$foundServer = $this->servers->where('id', $this->server->id)->first();
|
$foundServer = $this->servers->where('id', $this->server->id)->first();
|
||||||
if ($foundServer) {
|
if ($foundServer) {
|
||||||
@ -195,6 +197,13 @@ class Select extends Component
|
|||||||
$this->server = $server;
|
$this->server = $server;
|
||||||
$this->standaloneDockers = $server->standaloneDockers;
|
$this->standaloneDockers = $server->standaloneDockers;
|
||||||
$this->swarmDockers = $server->swarmDockers;
|
$this->swarmDockers = $server->swarmDockers;
|
||||||
|
$count = count($this->standaloneDockers) + count($this->swarmDockers);
|
||||||
|
if ($count === 1) {
|
||||||
|
$docker = $this->standaloneDockers->first() ?? $this->swarmDockers->first();
|
||||||
|
if ($docker) {
|
||||||
|
$this->setDestination($docker->uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
$this->current_step = 'destinations';
|
$this->current_step = 'destinations';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
app/Livewire/Project/Resource/EnvironmentSelect.php
Normal file
35
app/Livewire/Project/Resource/EnvironmentSelect.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Resource;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class EnvironmentSelect extends Component
|
||||||
|
{
|
||||||
|
public Collection $environments;
|
||||||
|
|
||||||
|
public string $project_uuid = '';
|
||||||
|
|
||||||
|
public string $selectedEnvironment = '';
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->selectedEnvironment = request()->route('environment_name');
|
||||||
|
$this->project_uuid = request()->route('project_uuid');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatedSelectedEnvironment($value)
|
||||||
|
{
|
||||||
|
if ($value === 'edit') {
|
||||||
|
return redirect()->route('project.show', [
|
||||||
|
'project_uuid' => $this->project_uuid,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
return redirect()->route('project.resource.index', [
|
||||||
|
'project_uuid' => $this->project_uuid,
|
||||||
|
'environment_name' => $value,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,8 @@ class EditCompose extends Component
|
|||||||
|
|
||||||
public $serviceId;
|
public $serviceId;
|
||||||
|
|
||||||
|
protected $listeners = ['refreshEnvs' => 'mount'];
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'service.docker_compose_raw' => 'required',
|
'service.docker_compose_raw' => 'required',
|
||||||
'service.docker_compose' => 'required',
|
'service.docker_compose' => 'required',
|
||||||
|
@ -75,7 +75,6 @@ class StackForm extends Component
|
|||||||
$this->service->parse();
|
$this->service->parse();
|
||||||
$this->service->refresh();
|
$this->service->refresh();
|
||||||
$this->service->saveComposeConfigs();
|
$this->service->saveComposeConfigs();
|
||||||
$this->dispatch('refreshStacks');
|
|
||||||
$this->dispatch('refreshEnvs');
|
$this->dispatch('refreshEnvs');
|
||||||
$this->dispatch('success', 'Service saved.');
|
$this->dispatch('success', 'Service saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
@ -112,7 +112,6 @@ class All extends Component
|
|||||||
$this->resource->environment_variables_preview()->whereNotIn('key', array_keys($variables))->delete();
|
$this->resource->environment_variables_preview()->whereNotIn('key', array_keys($variables))->delete();
|
||||||
} else {
|
} else {
|
||||||
$variables = parseEnvFormatToArray($this->variables);
|
$variables = parseEnvFormatToArray($this->variables);
|
||||||
ray($variables, $this->variables);
|
|
||||||
$this->resource->environment_variables()->whereNotIn('key', array_keys($variables))->delete();
|
$this->resource->environment_variables()->whereNotIn('key', array_keys($variables))->delete();
|
||||||
}
|
}
|
||||||
foreach ($variables as $key => $variable) {
|
foreach ($variables as $key => $variable) {
|
||||||
|
@ -64,7 +64,7 @@ class Logs extends Component
|
|||||||
return;
|
return;
|
||||||
$server = data_get($this->resource, 'destination.server');
|
$server = data_get($this->resource, 'destination.server');
|
||||||
if ($server->isFunctional()) {
|
if ($server->isFunctional()) {
|
||||||
$this->cpu = $server->getMetrics();
|
$this->cpu = $server->getCpuMetrics();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
64
app/Livewire/Project/Shared/Metrics.php
Normal file
64
app/Livewire/Project/Shared/Metrics.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Metrics extends Component
|
||||||
|
{
|
||||||
|
public $resource;
|
||||||
|
|
||||||
|
public $chartId = 'container-cpu';
|
||||||
|
|
||||||
|
public $data;
|
||||||
|
|
||||||
|
public $categories;
|
||||||
|
|
||||||
|
public int $interval = 5;
|
||||||
|
|
||||||
|
public bool $poll = true;
|
||||||
|
|
||||||
|
public function pollData()
|
||||||
|
{
|
||||||
|
if ($this->poll || $this->interval <= 10) {
|
||||||
|
$this->loadData();
|
||||||
|
if ($this->interval > 10) {
|
||||||
|
$this->poll = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadData()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$metrics = $this->resource->getMetrics($this->interval);
|
||||||
|
$cpuMetrics = collect($metrics)->map(function ($metric) {
|
||||||
|
return [$metric[0], $metric[1]];
|
||||||
|
});
|
||||||
|
$memoryMetrics = collect($metrics)->map(function ($metric) {
|
||||||
|
return [$metric[0], $metric[2]];
|
||||||
|
});
|
||||||
|
$this->dispatch("refreshChartData-{$this->chartId}-cpu", [
|
||||||
|
'seriesData' => $cpuMetrics,
|
||||||
|
]);
|
||||||
|
$this->dispatch("refreshChartData-{$this->chartId}-memory", [
|
||||||
|
'seriesData' => $memoryMetrics,
|
||||||
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setInterval()
|
||||||
|
{
|
||||||
|
if ($this->interval <= 10) {
|
||||||
|
$this->poll = true;
|
||||||
|
}
|
||||||
|
$this->loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.metrics');
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,8 @@ class Show extends Component
|
|||||||
{
|
{
|
||||||
public Project $project;
|
public Project $project;
|
||||||
|
|
||||||
|
public $environments;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$projectUuid = request()->route('project_uuid');
|
$projectUuid = request()->route('project_uuid');
|
||||||
@ -18,7 +20,8 @@ class Show extends Component
|
|||||||
if (! $project) {
|
if (! $project) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$project->load(['environments']);
|
|
||||||
|
$this->environments = $project->environments->sortBy('created_at');
|
||||||
$this->project = $project;
|
$this->project = $project;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
62
app/Livewire/Server/Charts.php
Normal file
62
app/Livewire/Server/Charts.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Server;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Charts extends Component
|
||||||
|
{
|
||||||
|
public Server $server;
|
||||||
|
|
||||||
|
public $chartId = 'server';
|
||||||
|
|
||||||
|
public $data;
|
||||||
|
|
||||||
|
public $categories;
|
||||||
|
|
||||||
|
public int $interval = 5;
|
||||||
|
|
||||||
|
public bool $poll = true;
|
||||||
|
|
||||||
|
public function pollData()
|
||||||
|
{
|
||||||
|
if ($this->poll || $this->interval <= 10) {
|
||||||
|
$this->loadData();
|
||||||
|
if ($this->interval > 10) {
|
||||||
|
$this->poll = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadData()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$cpuMetrics = $this->server->getCpuMetrics($this->interval);
|
||||||
|
$memoryMetrics = $this->server->getMemoryMetrics($this->interval);
|
||||||
|
$cpuMetrics = collect($cpuMetrics)->map(function ($metric) {
|
||||||
|
return [$metric[0], $metric[1]];
|
||||||
|
});
|
||||||
|
$memoryMetrics = collect($memoryMetrics)->map(function ($metric) {
|
||||||
|
return [$metric[0], $metric[1]];
|
||||||
|
});
|
||||||
|
$this->dispatch("refreshChartData-{$this->chartId}-cpu", [
|
||||||
|
'seriesData' => $cpuMetrics,
|
||||||
|
]);
|
||||||
|
$this->dispatch("refreshChartData-{$this->chartId}-memory", [
|
||||||
|
'seriesData' => $memoryMetrics,
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setInterval()
|
||||||
|
{
|
||||||
|
if ($this->interval <= 10) {
|
||||||
|
$this->poll = true;
|
||||||
|
}
|
||||||
|
$this->loadData();
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,7 @@ class ConfigureCloudflareTunnels extends Component
|
|||||||
$server->settings->is_cloudflare_tunnel = true;
|
$server->settings->is_cloudflare_tunnel = true;
|
||||||
$server->settings->save();
|
$server->settings->save();
|
||||||
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
||||||
$this->dispatch('serverInstalled');
|
$this->dispatch('refreshServerShow');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ class ConfigureCloudflareTunnels extends Component
|
|||||||
$server->save();
|
$server->save();
|
||||||
$server->settings->save();
|
$server->settings->save();
|
||||||
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
||||||
$this->dispatch('serverInstalled');
|
$this->dispatch('refreshServerShow');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Server;
|
namespace App\Livewire\Server;
|
||||||
|
|
||||||
|
use App\Actions\Server\StartSentinel;
|
||||||
|
use App\Actions\Server\StopSentinel;
|
||||||
|
use App\Jobs\PullSentinelImageJob;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@ -36,7 +39,12 @@ class Form extends Component
|
|||||||
'server.settings.is_build_server' => 'required|boolean',
|
'server.settings.is_build_server' => 'required|boolean',
|
||||||
'server.settings.concurrent_builds' => 'required|integer|min:1',
|
'server.settings.concurrent_builds' => 'required|integer|min:1',
|
||||||
'server.settings.dynamic_timeout' => 'required|integer|min:1',
|
'server.settings.dynamic_timeout' => 'required|integer|min:1',
|
||||||
|
'server.settings.is_metrics_enabled' => 'required|boolean',
|
||||||
|
'server.settings.metrics_token' => 'required',
|
||||||
|
'server.settings.metrics_refresh_rate_seconds' => 'required|integer|min:1',
|
||||||
|
'server.settings.metrics_history_days' => 'required|integer|min:1',
|
||||||
'wildcard_domain' => 'nullable|url',
|
'wildcard_domain' => 'nullable|url',
|
||||||
|
'server.settings.is_server_api_enabled' => 'required|boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
@ -52,7 +60,11 @@ class Form extends Component
|
|||||||
'server.settings.is_build_server' => 'Build Server',
|
'server.settings.is_build_server' => 'Build Server',
|
||||||
'server.settings.concurrent_builds' => 'Concurrent Builds',
|
'server.settings.concurrent_builds' => 'Concurrent Builds',
|
||||||
'server.settings.dynamic_timeout' => 'Dynamic Timeout',
|
'server.settings.dynamic_timeout' => 'Dynamic Timeout',
|
||||||
|
'server.settings.is_metrics_enabled' => 'Metrics',
|
||||||
|
'server.settings.metrics_token' => 'Metrics Token',
|
||||||
|
'server.settings.metrics_refresh_rate_seconds' => 'Metrics Interval',
|
||||||
|
'server.settings.metrics_history_days' => 'Metrics History',
|
||||||
|
'server.settings.is_server_api_enabled' => 'Server API',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@ -69,18 +81,59 @@ class Form extends Component
|
|||||||
|
|
||||||
public function updatedServerSettingsIsBuildServer()
|
public function updatedServerSettingsIsBuildServer()
|
||||||
{
|
{
|
||||||
$this->dispatch('serverInstalled');
|
$this->dispatch('refreshServerShow');
|
||||||
$this->dispatch('serverRefresh');
|
$this->dispatch('serverRefresh');
|
||||||
$this->dispatch('proxyStatusUpdated');
|
$this->dispatch('proxyStatusUpdated');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkPortForServerApi()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($this->server->settings->is_server_api_enabled === true) {
|
||||||
|
$this->server->checkServerApi();
|
||||||
|
$this->dispatch('success', 'Server API is reachable.');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
refresh_server_connection($this->server->privateKey);
|
refresh_server_connection($this->server->privateKey);
|
||||||
$this->validateServer(false);
|
$this->validateServer(false);
|
||||||
$this->server->settings->save();
|
$this->server->settings->save();
|
||||||
|
$this->server->save();
|
||||||
$this->dispatch('success', 'Server updated.');
|
$this->dispatch('success', 'Server updated.');
|
||||||
|
$this->dispatch('refreshServerShow');
|
||||||
|
if ($this->server->isSentinelEnabled()) {
|
||||||
|
PullSentinelImageJob::dispatchSync($this->server);
|
||||||
|
ray('Sentinel is enabled');
|
||||||
|
if ($this->server->settings->isDirty('is_metrics_enabled')) {
|
||||||
|
$this->dispatch('reloadWindow');
|
||||||
|
}
|
||||||
|
if ($this->server->settings->isDirty('is_server_api_enabled') && $this->server->settings->is_server_api_enabled === true) {
|
||||||
|
ray('Starting sentinel');
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ray('Sentinel is not enabled');
|
||||||
|
StopSentinel::dispatch($this->server);
|
||||||
|
}
|
||||||
|
// $this->checkPortForServerApi();
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function restartSentinel()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$version = get_latest_sentinel_version();
|
||||||
|
StartSentinel::run($this->server, $version, true);
|
||||||
|
$this->dispatch('success', 'Sentinel restarted.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class Show extends Component
|
|||||||
|
|
||||||
public $parameters = [];
|
public $parameters = [];
|
||||||
|
|
||||||
protected $listeners = ['serverInstalled' => '$refresh'];
|
protected $listeners = ['refreshServerShow' => '$refresh'];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
@ -143,7 +143,8 @@ class ValidateAndInstall extends Component
|
|||||||
} else {
|
} else {
|
||||||
$this->docker_version = $this->server->validateDockerEngineVersion();
|
$this->docker_version = $this->server->validateDockerEngineVersion();
|
||||||
if ($this->docker_version) {
|
if ($this->docker_version) {
|
||||||
$this->dispatch('serverInstalled');
|
$this->dispatch('refreshServerShow');
|
||||||
|
$this->dispatch('refreshBoardingIndex');
|
||||||
$this->dispatch('success', 'Server validated.');
|
$this->dispatch('success', 'Server validated.');
|
||||||
} else {
|
} else {
|
||||||
$this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
$this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||||
|
@ -228,18 +228,13 @@ class Application extends BaseModel
|
|||||||
|
|
||||||
public function gitCommitLink($link): string
|
public function gitCommitLink($link): string
|
||||||
{
|
{
|
||||||
if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) {
|
if (! is_null(data_get($this, 'source.html_url')) && ! is_null(data_get($this, 'git_repository')) && ! is_null(data_get($this, 'git_branch'))) {
|
||||||
if (str($this->source->html_url)->contains('bitbucket')) {
|
if (str($this->source->html_url)->contains('bitbucket')) {
|
||||||
return "{$this->source->html_url}/{$this->git_repository}/commits/{$link}";
|
return "{$this->source->html_url}/{$this->git_repository}/commits/{$link}";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "{$this->source->html_url}/{$this->git_repository}/commit/{$link}";
|
return "{$this->source->html_url}/{$this->git_repository}/commit/{$link}";
|
||||||
}
|
}
|
||||||
if (strpos($this->git_repository, 'git@') === 0) {
|
|
||||||
$git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
|
|
||||||
|
|
||||||
return "https://{$git_repository}/commit/{$link}";
|
|
||||||
}
|
|
||||||
if (str($this->git_repository)->contains('bitbucket')) {
|
if (str($this->git_repository)->contains('bitbucket')) {
|
||||||
$git_repository = str_replace('.git', '', $this->git_repository);
|
$git_repository = str_replace('.git', '', $this->git_repository);
|
||||||
$url = Url::fromString($git_repository);
|
$url = Url::fromString($git_repository);
|
||||||
@ -248,6 +243,14 @@ class Application extends BaseModel
|
|||||||
|
|
||||||
return $url->__toString();
|
return $url->__toString();
|
||||||
}
|
}
|
||||||
|
if (strpos($this->git_repository, 'git@') === 0) {
|
||||||
|
$git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
|
||||||
|
if (data_get($this, 'source.html_url')) {
|
||||||
|
return "{$this->source->html_url}/{$git_repository}/commit/{$link}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{$git_repository}/commit/{$link}";
|
||||||
|
}
|
||||||
|
|
||||||
return $this->git_repository;
|
return $this->git_repository;
|
||||||
}
|
}
|
||||||
@ -532,7 +535,7 @@ class Application extends BaseModel
|
|||||||
|
|
||||||
public function get_last_successful_deployment()
|
public function get_last_successful_deployment()
|
||||||
{
|
{
|
||||||
return ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', 'finished')->where('pull_request_id', 0)->orderBy('created_at', 'desc')->first();
|
return ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', ApplicationDeploymentStatus::FINISHED)->where('pull_request_id', 0)->orderBy('created_at', 'desc')->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_last_days_deployments()
|
public function get_last_days_deployments()
|
||||||
@ -1167,4 +1170,44 @@ class Application extends BaseModel
|
|||||||
|
|
||||||
return $preview;
|
return $preview;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getDomainsByUuid(string $uuid): array
|
||||||
|
{
|
||||||
|
$application = self::where('uuid', $uuid)->first();
|
||||||
|
|
||||||
|
if ($application) {
|
||||||
|
return $application->fqdns;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ class Environment extends Model
|
|||||||
protected function name(): Attribute
|
protected function name(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
set: fn (string $value) => strtolower($value),
|
set: fn (string $value) => str($value)->lower()->trim()->replace('/', '-')->toString(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,4 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
class Kubernetes extends BaseModel
|
class Kubernetes extends BaseModel {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
@ -112,4 +112,14 @@ class Project extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->postgresqls()->get()->merge($this->redis()->get())->merge($this->mongodbs()->get())->merge($this->mysqls()->get())->merge($this->mariadbs()->get())->merge($this->keydbs()->get())->merge($this->dragonflies()->get())->merge($this->clickhouses()->get());
|
return $this->postgresqls()->get()->merge($this->redis()->get())->merge($this->mongodbs()->get())->merge($this->mysqls()->get())->merge($this->mariadbs()->get())->merge($this->keydbs()->get())->merge($this->dragonflies()->get())->merge($this->clickhouses()->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function default_environment()
|
||||||
|
{
|
||||||
|
$default = $this->environments()->where('name', 'production')->first();
|
||||||
|
if (! $default) {
|
||||||
|
$default = $this->environments()->sortBy('created_at')->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,11 @@ namespace App\Models;
|
|||||||
use App\Actions\Server\InstallDocker;
|
use App\Actions\Server\InstallDocker;
|
||||||
use App\Enums\ProxyTypes;
|
use App\Enums\ProxyTypes;
|
||||||
use App\Jobs\PullSentinelImageJob;
|
use App\Jobs\PullSentinelImageJob;
|
||||||
use App\Notifications\Server\Revived;
|
|
||||||
use App\Notifications\Server\Unreachable;
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Support\Stringable;
|
use Illuminate\Support\Stringable;
|
||||||
@ -462,10 +461,44 @@ $schema://$host {
|
|||||||
Storage::disk('ssh-mux')->delete($this->muxFilename());
|
Storage::disk('ssh-mux')->delete($this->muxFilename());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isSentinelEnabled()
|
||||||
|
{
|
||||||
|
return $this->isMetricsEnabled() || $this->isServerApiEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isMetricsEnabled()
|
||||||
|
{
|
||||||
|
return $this->settings->is_metrics_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isServerApiEnabled()
|
||||||
|
{
|
||||||
|
return $this->settings->is_server_api_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkServerApi()
|
||||||
|
{
|
||||||
|
if ($this->isServerApiEnabled()) {
|
||||||
|
$server_ip = $this->ip;
|
||||||
|
if (isDev()) {
|
||||||
|
if ($this->id === 0) {
|
||||||
|
$server_ip = 'localhost';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$command = "curl -s http://{$server_ip}:12172/api/health";
|
||||||
|
$process = Process::timeout(5)->run($command);
|
||||||
|
if ($process->failed()) {
|
||||||
|
ray($process->exitCode(), $process->output(), $process->errorOutput());
|
||||||
|
throw new \Exception("Server API is not reachable on http://{$server_ip}:12172");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function checkSentinel()
|
public function checkSentinel()
|
||||||
{
|
{
|
||||||
ray("Checking sentinel on server: {$this->name}");
|
ray("Checking sentinel on server: {$this->name}");
|
||||||
if ($this->is_metrics_enabled) {
|
if ($this->isSentinelEnabled()) {
|
||||||
$sentinel_found = instant_remote_process(['docker inspect coolify-sentinel'], $this, false);
|
$sentinel_found = instant_remote_process(['docker inspect coolify-sentinel'], $this, false);
|
||||||
$sentinel_found = json_decode($sentinel_found, true);
|
$sentinel_found = json_decode($sentinel_found, true);
|
||||||
$status = data_get($sentinel_found, '0.State.Status', 'exited');
|
$status = data_get($sentinel_found, '0.State.Status', 'exited');
|
||||||
@ -478,21 +511,57 @@ $schema://$host {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMetrics()
|
public function getCpuMetrics(int $mins = 5)
|
||||||
{
|
{
|
||||||
if ($this->is_metrics_enabled) {
|
if ($this->isMetricsEnabled()) {
|
||||||
$from = now()->subMinutes(5)->toIso8601ZuluString();
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
$cpu = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl http://localhost:8888/api/cpu/history?from=$from'"], $this, false);
|
$cpu = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$this->settings->metrics_token}\" http://localhost:8888/api/cpu/history?from=$from'"], $this, false);
|
||||||
|
if (str($cpu)->contains('error')) {
|
||||||
|
$error = json_decode($cpu, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
$cpu = str($cpu)->explode("\n")->skip(1)->all();
|
$cpu = str($cpu)->explode("\n")->skip(1)->all();
|
||||||
$parsedCollection = collect($cpu)->flatMap(function ($item) {
|
$parsedCollection = collect($cpu)->flatMap(function ($item) {
|
||||||
return collect(explode("\n", trim($item)))->map(function ($line) {
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
[$time, $value] = explode(',', trim($line));
|
[$time, $cpu_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 0);
|
||||||
|
|
||||||
return [(int) $time, (float) $value];
|
return [(int) $time, (float) $cpu_usage_percent];
|
||||||
});
|
});
|
||||||
})->toArray();
|
});
|
||||||
|
|
||||||
return $parsedCollection;
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMemoryMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
if ($this->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$memory = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$this->settings->metrics_token}\" http://localhost:8888/api/memory/history?from=$from'"], $this, false);
|
||||||
|
if (str($memory)->contains('error')) {
|
||||||
|
$error = json_decode($memory, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$memory = str($memory)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($memory)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $used, $free, $usedPercent] = explode(',', trim($line));
|
||||||
|
$usedPercent = number_format($usedPercent, 0);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $usedPercent];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class Service extends BaseModel
|
class Service extends BaseModel
|
||||||
{
|
{
|
||||||
@ -837,14 +838,34 @@ class Service extends BaseModel
|
|||||||
$commands[] = "mkdir -p $workdir";
|
$commands[] = "mkdir -p $workdir";
|
||||||
$commands[] = "cd $workdir";
|
$commands[] = "cd $workdir";
|
||||||
|
|
||||||
|
$json = Yaml::parse($this->docker_compose);
|
||||||
|
$envs_from_coolify = $this->environment_variables()->get();
|
||||||
|
foreach ($json['services'] as $service => $config) {
|
||||||
|
$envs = collect($config['environment']);
|
||||||
|
$envs->push("COOLIFY_CONTAINER_NAME=$service-{$this->uuid}");
|
||||||
|
foreach ($envs_from_coolify as $env) {
|
||||||
|
$envs = $envs->map(function ($value) use ($env) {
|
||||||
|
if (str($value)->startsWith($env->key)) {
|
||||||
|
return "{$env->key}={$env->real_value}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$envs = $envs->unique();
|
||||||
|
data_set($json, "services.$service.environment", $envs->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->docker_compose = Yaml::dump($json, 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
|
||||||
$docker_compose_base64 = base64_encode($this->docker_compose);
|
$docker_compose_base64 = base64_encode($this->docker_compose);
|
||||||
|
|
||||||
$commands[] = "echo $docker_compose_base64 | base64 -d | tee docker-compose.yml > /dev/null";
|
$commands[] = "echo $docker_compose_base64 | base64 -d | tee docker-compose.yml > /dev/null";
|
||||||
$envs = $this->environment_variables()->get();
|
|
||||||
$commands[] = 'rm -f .env || true';
|
$commands[] = 'rm -f .env || true';
|
||||||
foreach ($envs as $env) {
|
|
||||||
|
foreach ($envs_from_coolify as $env) {
|
||||||
$commands[] = "echo '{$env->key}={$env->real_value}' >> .env";
|
$commands[] = "echo '{$env->key}={$env->real_value}' >> .env";
|
||||||
}
|
}
|
||||||
if ($envs->count() === 0) {
|
if ($envs_from_coolify->count() === 0) {
|
||||||
$commands[] = 'touch .env';
|
$commands[] = 'touch .env';
|
||||||
}
|
}
|
||||||
instant_remote_process($commands, $this->server);
|
instant_remote_process($commands, $this->server);
|
||||||
|
@ -226,4 +226,33 @@ class StandaloneClickhouse extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,4 +226,33 @@ class StandaloneDragonfly extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,4 +226,33 @@ class StandaloneKeydb extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,4 +226,33 @@ class StandaloneMariadb extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,4 +246,33 @@ class StandaloneMongodb extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,4 +227,33 @@ class StandaloneMysql extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,4 +227,33 @@ class StandalonePostgresql extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,4 +222,33 @@ class StandaloneRedis extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetrics(int $mins = 5)
|
||||||
|
{
|
||||||
|
$server = $this->destination->server;
|
||||||
|
$container_name = $this->uuid;
|
||||||
|
if ($server->isMetricsEnabled()) {
|
||||||
|
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||||
|
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
|
||||||
|
if (str($metrics)->contains('error')) {
|
||||||
|
$error = json_decode($metrics, true);
|
||||||
|
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
|
||||||
|
if ($error == 'Unauthorized') {
|
||||||
|
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||||
|
}
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
$metrics = str($metrics)->explode("\n")->skip(1)->all();
|
||||||
|
$parsedCollection = collect($metrics)->flatMap(function ($item) {
|
||||||
|
return collect(explode("\n", trim($item)))->map(function ($line) {
|
||||||
|
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
|
||||||
|
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
|
||||||
|
|
||||||
|
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return $parsedCollection->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,7 @@ class ContainerRestarted extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public function __construct(public string $name, public Server $server, public ?string $url = null)
|
public function __construct(public string $name, public Server $server, public ?string $url = null) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
|
@ -14,9 +14,7 @@ class ContainerStopped extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public function __construct(public string $name, public Server $server, public ?string $url = null)
|
public function __construct(public string $name, public Server $server, public ?string $url = null) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
|
@ -16,9 +16,7 @@ class DailyBackup extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public function __construct(public $databases)
|
public function __construct(public $databases) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
|
@ -14,9 +14,7 @@ class GeneralNotification extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public function __construct(public string $message)
|
public function __construct(public string $message) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
|
@ -15,9 +15,7 @@ class DockerCleanup extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public function __construct(public Server $server, public string $message)
|
public function __construct(public Server $server, public string $message) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
|
@ -17,9 +17,7 @@ class ForceDisabled extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
|
@ -17,9 +17,7 @@ class ForceEnabled extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
|
@ -17,9 +17,7 @@ class HighDiskUsage extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public function __construct(public Server $server, public int $disk_usage, public int $cleanup_after_percentage)
|
public function __construct(public Server $server, public int $disk_usage, public int $cleanup_after_percentage) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
|
@ -24,7 +24,7 @@ class Revived extends Notification implements ShouldQueue
|
|||||||
if ($this->server->unreachable_notification_sent === false) {
|
if ($this->server->unreachable_notification_sent === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GetContainersStatus::dispatch($server);
|
GetContainersStatus::dispatch($server)->onQueue('high');
|
||||||
// dispatch(new ContainerStatusJob($server));
|
// dispatch(new ContainerStatusJob($server));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,10 +17,7 @@ class Unreachable extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server) {}
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
|
@ -13,9 +13,7 @@ class Test extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 5;
|
public $tries = 5;
|
||||||
|
|
||||||
public function __construct(public ?string $emails = null)
|
public function __construct(public ?string $emails = null) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
|
@ -22,9 +22,7 @@ class InvitationLink extends Notification implements ShouldQueue
|
|||||||
return [TransactionalEmailChannel::class];
|
return [TransactionalEmailChannel::class];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public User $user)
|
public function __construct(public User $user) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
{
|
{
|
||||||
|
@ -14,9 +14,7 @@ class Test extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 5;
|
public $tries = 5;
|
||||||
|
|
||||||
public function __construct(public string $emails)
|
public function __construct(public string $emails) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function via(): array
|
public function via(): array
|
||||||
{
|
{
|
||||||
|
@ -9,9 +9,7 @@ use Laravel\Sanctum\Sanctum;
|
|||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
public function register(): void
|
public function register(): void {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
|
34
app/View/Components/ApexCharts.php
Normal file
34
app/View/Components/ApexCharts.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Components;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
|
||||||
|
class ApexCharts extends Component
|
||||||
|
{
|
||||||
|
public string $chartId;
|
||||||
|
|
||||||
|
public $seriesData;
|
||||||
|
|
||||||
|
public $categories;
|
||||||
|
|
||||||
|
public $seriesName;
|
||||||
|
|
||||||
|
public function __construct($chartId, $seriesData, $categories, $seriesName = '')
|
||||||
|
{
|
||||||
|
$this->chartId = $chartId;
|
||||||
|
$this->seriesData = $seriesData;
|
||||||
|
$this->categories = $categories;
|
||||||
|
$this->seriesName = $seriesName ?? 'Series';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the view / contents that represent the component.
|
||||||
|
*/
|
||||||
|
public function render(): View|Closure|string
|
||||||
|
{
|
||||||
|
return view('components.apex-charts');
|
||||||
|
}
|
||||||
|
}
|
@ -22,8 +22,7 @@ class Input extends Component
|
|||||||
public bool $allowToPeak = true,
|
public bool $allowToPeak = true,
|
||||||
public bool $isMultiline = false,
|
public bool $isMultiline = false,
|
||||||
public string $defaultClass = 'input',
|
public string $defaultClass = 'input',
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
public function render(): View|Closure|string
|
public function render(): View|Closure|string
|
||||||
{
|
{
|
||||||
|
@ -16,9 +16,7 @@ class ResourceView extends Component
|
|||||||
public ?string $logo = null,
|
public ?string $logo = null,
|
||||||
public ?string $documentation = null,
|
public ?string $documentation = null,
|
||||||
public bool $upgrade = false,
|
public bool $upgrade = false,
|
||||||
) {
|
) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the view / contents that represent the component.
|
* Get the view / contents that represent the component.
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user