Merge branch 'next' into feat/monaco-editor
This commit is contained in:
commit
73bc07c7fb
59
README.md
59
README.md
@ -1,17 +1,21 @@
|
|||||||
|
![Latest Release Version](https://img.shields.io/badge/dynamic/json?labelColor=grey&color=6366f1&label=Latest_released_version&url=https%3A%2F%2Fcdn.coollabs.io%2Fcoolify%2Fversions.json&query=coolify.v4.version&style=for-the-badge
|
||||||
|
)
|
||||||
|
|
||||||
[![Bounty Issues](https://img.shields.io/static/v1?labelColor=grey&color=6366f1&label=Algora&message=%F0%9F%92%8E+Bounty+issues&style=for-the-badge)](https://console.algora.io/org/coollabsio/bounties/new)
|
[![Bounty Issues](https://img.shields.io/static/v1?labelColor=grey&color=6366f1&label=Algora&message=%F0%9F%92%8E+Bounty+issues&style=for-the-badge)](https://console.algora.io/org/coollabsio/bounties/new)
|
||||||
[![Open Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Fcoollabsio%2Fbounties%3Fstatus%3Dopen&style=for-the-badge)](https://console.algora.io/org/coollabsio/bounties?status=open)
|
[![Open Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Fcoollabsio%2Fbounties%3Fstatus%3Dopen&style=for-the-badge)](https://console.algora.io/org/coollabsio/bounties?status=open)
|
||||||
[![Rewarded Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Fcoollabsio%2Fbounties%3Fstatus%3Dcompleted&style=for-the-badge)](https://console.algora.io/org/coollabsio/bounties?status=completed)
|
[![Rewarded Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Fcoollabsio%2Fbounties%3Fstatus%3Dcompleted&style=for-the-badge)](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 @@ # Installation
|
|||||||
|
|
||||||
# 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 @@ ## Individuals
|
|||||||
|
|
||||||
# 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 @@ # Recognitions
|
|||||||
</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 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
|
|||||||
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 @@
|
|||||||
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 @@ public function handle()
|
|||||||
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 @@ private function cleanup_in_progress_application_deployments()
|
|||||||
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 @@ private function pull_images($schedule)
|
|||||||
{
|
{
|
||||||
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
$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 @@
|
|||||||
|
|
||||||
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 @@ public function deployments(Request $request)
|
|||||||
'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 @@ public function server_by_uuid(Request $request)
|
|||||||
|
|
||||||
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 @@ public function callback(string $provider)
|
|||||||
$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 @@ public function callback(string $provider)
|
|||||||
} 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,6 +130,16 @@ public function manual(Request $request)
|
|||||||
$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) {
|
||||||
|
if ($application->build_pack === 'dockercompose') {
|
||||||
|
$pr_app = ApplicationPreview::create([
|
||||||
|
'git_type' => 'bitbucket',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
ApplicationPreview::create([
|
ApplicationPreview::create([
|
||||||
'git_type' => 'bitbucket',
|
'git_type' => 'bitbucket',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
@ -137,6 +147,7 @@ public function manual(Request $request)
|
|||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
|
@ -165,6 +165,16 @@ public function manual(Request $request)
|
|||||||
$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) {
|
||||||
|
if ($application->build_pack === 'dockercompose') {
|
||||||
|
$pr_app = ApplicationPreview::create([
|
||||||
|
'git_type' => 'gitea',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
ApplicationPreview::create([
|
ApplicationPreview::create([
|
||||||
'git_type' => 'gitea',
|
'git_type' => 'gitea',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
@ -172,6 +182,8 @@ public function manual(Request $request)
|
|||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
|
@ -170,6 +170,16 @@ public function manual(Request $request)
|
|||||||
$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) {
|
||||||
|
if ($application->build_pack === 'dockercompose') {
|
||||||
|
$pr_app = ApplicationPreview::create([
|
||||||
|
'git_type' => 'github',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
ApplicationPreview::create([
|
ApplicationPreview::create([
|
||||||
'git_type' => 'github',
|
'git_type' => 'github',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
@ -177,6 +187,7 @@ public function manual(Request $request)
|
|||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
|
@ -180,6 +180,16 @@ public function manual(Request $request)
|
|||||||
$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) {
|
||||||
|
if ($application->build_pack === 'dockercompose') {
|
||||||
|
$pr_app = ApplicationPreview::create([
|
||||||
|
'git_type' => 'gitlab',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
ApplicationPreview::create([
|
ApplicationPreview::create([
|
||||||
'git_type' => 'gitlab',
|
'git_type' => 'gitlab',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
@ -187,6 +197,7 @@ public function manual(Request $request)
|
|||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
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 @@ private function decide_what_to_do()
|
|||||||
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 @@ private function save_environment_variables()
|
|||||||
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 @@ private function save_environment_variables()
|
|||||||
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 @@ private function save_environment_variables()
|
|||||||
$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 @@ private function save_environment_variables()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@ private function framework_based_notification()
|
|||||||
$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 @@ private function rolling_update()
|
|||||||
$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 @@ private function generate_nixpacks_confs()
|
|||||||
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 @@ private function generate_nixpacks_confs()
|
|||||||
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 @@ private function build_image()
|
|||||||
$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 @@ private function build_image()
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$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 @@ private function build_image()
|
|||||||
$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,6 +2232,9 @@ public function failed(Throwable $exception): void
|
|||||||
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
|
||||||
|
if ($this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name)) {
|
||||||
|
// do not remove already running container
|
||||||
|
} else {
|
||||||
$this->application_deployment_queue->addLogEntry('Deployment failed. Removing the new version of your application.', 'stderr');
|
$this->application_deployment_queue->addLogEntry('Deployment failed. Removing the new version of your application.', 'stderr');
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true]
|
["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true]
|
||||||
@ -2192,3 +2243,4 @@ public function failed(Throwable $exception): void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -25,8 +25,7 @@ public function __construct(
|
|||||||
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 @@ public function backoff(): int
|
|||||||
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 @@ public function __construct(
|
|||||||
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 @@ public function handle(): void
|
|||||||
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 @@ public function backoff(): int
|
|||||||
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 @@ public function uniqueId(): string
|
|||||||
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 @@ public function uniqueId(): string
|
|||||||
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 @@ public function handle(): void
|
|||||||
}
|
}
|
||||||
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 @@ public function __construct(
|
|||||||
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 @@ public function backoff(): int
|
|||||||
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 @@ public function backoff(): int
|
|||||||
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 @@ public function handle()
|
|||||||
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 @@
|
|||||||
|
|
||||||
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 @@
|
|||||||
|
|
||||||
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 @@ public function force_start()
|
|||||||
|
|
||||||
public function cancel()
|
public function cancel()
|
||||||
{
|
{
|
||||||
try {
|
|
||||||
$kill_command = "docker rm -f {$this->application_deployment_queue->deployment_uuid}";
|
$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_id = $this->application_deployment_queue->server_id ?? $this->application->destination->server_id;
|
||||||
|
try {
|
||||||
$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 @@ public function cancel()
|
|||||||
'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 @@ public function set_redirect()
|
|||||||
public function submit($showToaster = true)
|
public function submit($showToaster = true)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
if ($this->application->isDirty('redirect')) {
|
||||||
$this->set_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 @@ public function mount()
|
|||||||
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 @@ public function add(int $pull_request_id, ?string $pull_request_html_url = null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 @@ public function stop(int $pull_request_id)
|
|||||||
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\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 @@ public function mount()
|
|||||||
$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 @@ public function load_branch()
|
|||||||
) {
|
) {
|
||||||
$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 @@ public function load_branch()
|
|||||||
$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 @@ public function setType(string $type)
|
|||||||
|
|
||||||
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 @@ public function setServer(Server $server)
|
|||||||
$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 @@ public function submit()
|
|||||||
$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 @@ public function saveVariables($isPreview)
|
|||||||
$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 @@ public function loadMetrics()
|
|||||||
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 @@ public function mount()
|
|||||||
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 @@ public function alreadyConfigured()
|
|||||||
$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 @@ public function submit()
|
|||||||
$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 @@ public function serverInstalled()
|
|||||||
|
|
||||||
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 @@ public function validateDockerVersion()
|
|||||||
} 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 @@ public function gitCommits(): Attribute
|
|||||||
|
|
||||||
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 @@ public function gitCommitLink($link): string
|
|||||||
|
|
||||||
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 @@ public function isDeploymentInprogress()
|
|||||||
|
|
||||||
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 @@ public function generate_preview_fqdn(int $pull_request_id)
|
|||||||
|
|
||||||
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 @@ public function services()
|
|||||||
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 @@ public function databases()
|
|||||||
{
|
{
|
||||||
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 @@
|
|||||||
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 @@ public function forceDisableServer()
|
|||||||
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 @@ public function checkSentinel()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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\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 @@ public function saveComposeConfigs()
|
|||||||
$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 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
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 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
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 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
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 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
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 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
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 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
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 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
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 @@ public function scheduledBackups()
|
|||||||
{
|
{
|
||||||
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 @@ public function __construct(public Server $server)
|
|||||||
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 @@ public function via(): array
|
|||||||
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 @@
|
|||||||
|
|
||||||
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 @@ public function __construct(
|
|||||||
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 @@ public function __construct(
|
|||||||
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…
Reference in New Issue
Block a user