Merge branch 'next' into fix-stripprefix-http
60
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)
|
||||
[![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)
|
||||
|
||||
# About the Project
|
||||
|
||||
Coolify is an open-source & self-hostable alternative to Heroku / Netlify / Vercel / etc.
|
||||
|
||||
It helps you to manage your servers, applications, databases on your own hardware, all you need is SSH connection. You can manage VPS, Bare Metal, Raspberry PI's anything.
|
||||
It helps you manage your servers, applications, and databases on your own hardware; you only need an SSH connection. You can manage VPS, Bare Metal, Raspberry PIs, and anything else.
|
||||
|
||||
Imagine if you could have the ease of a cloud but with your own servers. That is **Coolify**.
|
||||
Imagine having the ease of a cloud but with your own servers. That is **Coolify**.
|
||||
|
||||
No vendor lock-in, which means that all the configuration for your applications/databases/etc are saved to your server. So if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You just lose the automations and all the magic. 🪄️
|
||||
No vendor lock-in, which means that all the configurations for your applications/databases/etc are saved to your server. So, if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You lose the automations and all the magic. 🪄️
|
||||
|
||||
For more information, take a look at our landing page [here](https://coolify.io).
|
||||
For more information, take a look at our landing page at [coolify.io](https://coolify.io).
|
||||
|
||||
# Installation
|
||||
|
||||
@ -22,36 +26,42 @@ # Installation
|
||||
|
||||
# Support
|
||||
|
||||
Contact us [here](https://coolify.io/docs/contact).
|
||||
Contact us at [coolify.io/docs/contact](https://coolify.io/docs/contact).
|
||||
|
||||
# Donations
|
||||
To stay completely free, open-source, no feature behind paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the future development of the project.
|
||||
To stay completely free and open-source, with no feature behind the paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the project's future development.
|
||||
|
||||
https://coolify.io/sponsorships
|
||||
[coolify.io/sponsorships](https://coolify.io/sponsorships)
|
||||
|
||||
Thank you so much!
|
||||
|
||||
Special thanks to our biggest sponsors!
|
||||
|
||||
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
|
||||
<a href="http://htznr.li/CoolifyXHetzner" target="_blank"><img src="./other/logos/hetzner.jpg" alt="hetzner logo" width="200"/></a>
|
||||
<a href="https://logto.io/?ref=coolify" target="_blank"><img src="./other/logos/logto.webp" alt="logto logo" width="200"/></a>
|
||||
<a href="https://bc.direct/?utm_source=coolify.io" target="_blank"><img src="./other/logos/bc.png" alt="bc direct logo" width="200"/></a>
|
||||
<a href="https://www.quantcdn.io/?utm_source=coolify.io" target="_blank"><img src="./other/logos/quant.svg" alt="quantcdn logo" width="200"/></a>
|
||||
<a href="https://arcjet.com/?utm_source=coolify.io" target="_blank"><img src="./other/logos/arcjet.svg" alt="arcjet logo" width="200"/></a>
|
||||
|
||||
<a href="http://htznr.li/CoolifyXHetzner" target="_blank"><img src="./other/logos/hetzner.jpg" alt="hetzner logo" width="150"/></a>
|
||||
<a href="https://logto.io/?ref=coolify" target="_blank"><img src="./other/logos/logto.webp" alt="logto logo" width="150"/></a>
|
||||
<a href="https://bc.direct/?ref=coolify.io" target="_blank"><img src="./other/logos/bc.png" alt="bc direct logo" width="200"/></a>
|
||||
<a href="https://www.quantcdn.io/?ref=coolify.io" target="_blank"><img src="./other/logos/quant.svg" alt="quantcdn logo" width="150"/></a>
|
||||
<a href="https://arcjet.com/?ref=coolify.io" target="_blank"><img src="./other/logos/arcjet.svg" alt="arcjet logo" width="200"/></a>
|
||||
<a href="https://supa.guide/?ref=coolify.io" target="_blank"><img src="./other/logos/supaguide.png" alt="supaguide logo" width="200"/></a>
|
||||
<a href="https://tigrisdata.com/?ref=coolify.io" target="_blank"><img src="./other/logos/tigris.svg" alt="tigris logo" width="140"/></a>
|
||||
<a href="https://fractalnetworks.co/?ref=coolify.io" target="_blank"><img src="./other/logos/fractal.svg" alt="fractal logo" width="180"/></a>
|
||||
<a href="https://coolify.ad.vin/?ref=coolify.io" target="_blank"><img src="./other/logos/advin.png" alt="advin logo" width="250"/></a>
|
||||
<a href="https://trieve.ai/?ref=coolify.io" target="_blank"><img src="./other/logos/trieve_bg.png" alt="trieve logo" width="180"/></a>
|
||||
<a href="https://blacksmith.sh/?ref=coolify.io" target="_blank"><img src="./other/logos/blacksmith.png" alt="blacksmith logo" width="180"/></a>
|
||||
|
||||
## 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://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
|
||||
<a href="https://www.runpod.io/?utm_source=coolify.io">
|
||||
<a href="https://serpapi.com/?ref=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>
|
||||
<a href="https://typebot.io/?ref=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
|
||||
<a href="https://www.runpod.io/?ref=coolify.io">
|
||||
<svg style="width:60px;height:60px;background:#fff;" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 200 200"><g><path d="M74.5 51.1c-25.4 14.9-27 16-29.6 20.2-1.8 3-1.9 5.3-1.9 32.3 0 21.7.3 29.4 1.3 30.6 1.9 2.5 46.7 27.9 48.5 27.6 1.5-.3 1.7-3.1 2-27.7.2-21.9 0-27.8-1.1-29.5-.8-1.2-9.9-6.8-20.2-12.6-10.3-5.8-19.4-11.5-20.2-12.7-1.8-2.6-.9-5.9 1.8-7.4 1.6-.8 6.3 0 21.8 4C87.8 78.7 98 81 99.6 81c4.4 0 49.9-25.9 49.9-28.4 0-1.6-3.4-2.8-24-8.2-13.2-3.5-25.1-6.3-26.5-6.3-1.4.1-12.4 5.9-24.5 13z"></path><path d="m137.2 68.1-3.3 2.1 6.3 3.7c3.5 2 6.3 4.3 6.3 5.1 0 .9-8 6.1-19.4 12.6-10.6 6-20 11.9-20.7 12.9-1.2 1.6-1.4 7.2-1.2 29.4.3 24.8.5 27.6 2 27.9 1.8.3 46.6-25.1 48.6-27.6.9-1.2 1.2-8.8 1.2-30.2s-.3-29-1.2-30.2c-1.6-1.9-12.1-7.8-13.9-7.8-.8 0-2.9 1-4.7 2.1z"></path></g></svg></a>
|
||||
<a href="https://lightspeed.run/?utm_source=coolify.io"><img src="https://github.com/lightspeedrun.png" width="60px" alt="Lightspeed.run"/></a>
|
||||
<a href="https://www.flint.sh/en/home?utm_source=coolify.io"> <img src="https://github.com/Flint-company.png" width="60px" alt="FlintCompany"/></a>
|
||||
<a href="https://americancloud.com/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
|
||||
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
|
||||
<a href="https://x.com/mrsmith9ja?utm_source=coolify.io"><img width="60px" alt="Thompson Edolo" src="https://github.com/verygreenboi.png"/></a>
|
||||
<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
|
||||
<a href="https://lightspeed.run/?ref=coolify.io"><img src="https://github.com/lightspeedrun.png" width="60px" alt="Lightspeed.run"/></a>
|
||||
<a href="https://www.flint.sh/en/home?ref=coolify.io"> <img src="https://github.com/Flint-company.png" width="60px" alt="FlintCompany"/></a>
|
||||
<a href="https://americancloud.com/?ref=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
|
||||
<a href="https://cryptojobslist.com/?ref=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
|
||||
<a href="https://codext.link/coolify-io?ref=coolify.io"><img src="./other/logos/codext.jpg" width="60px" alt="Codext" /></a>
|
||||
<a href="https://x.com/mrsmith9ja?ref=coolify.io"><img width="60px" alt="Thompson Edolo" src="https://github.com/verygreenboi.png"/></a>
|
||||
<a href="https://www.uxwizz.com/?ref=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
|
||||
<a href="https://github.com/Flowko"><img src="https://barrad.me/_ipx/f_webp&s_300x300/younes.jpg" width="60px" alt="Younes Barrad" /></a>
|
||||
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Automaze" /></a>
|
||||
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
||||
@ -83,9 +93,9 @@ ## Individuals
|
||||
|
||||
# Cloud
|
||||
|
||||
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
|
||||
If you do not want to self-host Coolify, there is a paid cloud version available: [app.coolify.io](https://app.coolify.io)
|
||||
|
||||
For more information & pricing, take a look at our landing page [here](https://coolify.io).
|
||||
For more information & pricing, take a look at our landing page [coolify.io](https://coolify.io).
|
||||
|
||||
## Why should I use the Cloud version?
|
||||
The recommended way to use Coolify is to have one server for Coolify and one (or more) for the resources you are deploying. A server is around 4-5$/month.
|
||||
@ -109,7 +119,7 @@ # Recognitions
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<a href="https://www.producthunt.com/posts/coolify?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-coolify" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=338273&theme=light" alt="Coolify - An 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>
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Events\DatabaseStatusChanged;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
@ -28,5 +29,6 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
|
||||
instant_remote_process(["docker rm -f {$uuid}-proxy"], $server);
|
||||
$database->is_public = false;
|
||||
$database->save();
|
||||
DatabaseStatusChanged::dispatch();
|
||||
}
|
||||
}
|
||||
|
@ -12,12 +12,15 @@ class StartSentinel
|
||||
public function handle(Server $server, $version = 'latest', bool $restart = false)
|
||||
{
|
||||
if ($restart) {
|
||||
instant_remote_process(['docker rm -f coolify-sentinel'], $server, false);
|
||||
StopSentinel::run($server);
|
||||
}
|
||||
$metrics_history = $server->settings->metrics_history_days;
|
||||
$refresh_rate = $server->settings->metrics_refresh_rate_seconds;
|
||||
$token = $server->settings->metrics_token;
|
||||
instant_remote_process([
|
||||
"docker run --rm --pull always -d -e \"SCHEDULER=true\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
|
||||
"docker run --rm --pull always -d -e \"TOKEN={$token}\" -e \"SCHEDULER=true\" -e \"METRICS_HISTORY={$metrics_history}\" -e \"REFRESH_RATE={$refresh_rate}\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
|
||||
'chown -R 9999:root /data/coolify/metrics /data/coolify/logs',
|
||||
'chmod -R 700 /data/coolify/metrics /data/coolify/logs',
|
||||
], $server, false);
|
||||
], $server, true);
|
||||
}
|
||||
}
|
||||
|
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\Jobs\CleanupHelperContainersJob;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\Environment;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\Server;
|
||||
@ -24,6 +25,8 @@ public function handle()
|
||||
get_public_ips();
|
||||
$full_cleanup = $this->option('full-cleanup');
|
||||
$cleanup_deployments = $this->option('cleanup-deployments');
|
||||
|
||||
$this->replace_slash_in_environment_name();
|
||||
if ($cleanup_deployments) {
|
||||
echo "Running cleanup deployments.\n";
|
||||
$this->cleanup_in_progress_application_deployments();
|
||||
@ -150,4 +153,15 @@ private function cleanup_in_progress_application_deployments()
|
||||
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');
|
||||
foreach ($servers as $server) {
|
||||
if (config('coolify.is_sentinel_enabled')) {
|
||||
if ($server->isSentinelEnabled()) {
|
||||
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
|
||||
}
|
||||
$schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
|
||||
|
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',
|
||||
])->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)
|
||||
|
@ -3,102 +3,52 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Project as ModelsProject;
|
||||
use App\Models\Application;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class Domains extends Controller
|
||||
{
|
||||
public function domains(Request $request)
|
||||
public function deleteDomains(Request $request)
|
||||
{
|
||||
$teamId = get_team_id_from_token();
|
||||
if (is_null($teamId)) {
|
||||
return invalid_token();
|
||||
}
|
||||
$projects = ModelsProject::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,
|
||||
$validator = Validator::make($request->all(), [
|
||||
'uuid' => 'required|string|exists:applications,uuid',
|
||||
'domains' => 'required|array',
|
||||
'domains.*' => 'required|string|distinct',
|
||||
]);
|
||||
}
|
||||
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;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Application;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server as ModelsServer;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@ -59,4 +62,106 @@ public function server_by_uuid(Request $request)
|
||||
|
||||
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;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
class OauthController extends Controller
|
||||
{
|
||||
@ -20,6 +22,11 @@ public function callback(string $provider)
|
||||
$oauthUser = get_socialite_provider($provider)->user();
|
||||
$user = User::whereEmail($oauthUser->email)->first();
|
||||
if (! $user) {
|
||||
$settings = InstanceSettings::get();
|
||||
if (! $settings->is_registration_enabled) {
|
||||
abort(403, 'Registration is disabled');
|
||||
}
|
||||
|
||||
$user = User::create([
|
||||
'name' => $oauthUser->name,
|
||||
'email' => $oauthUser->email,
|
||||
@ -31,7 +38,9 @@ public function callback(string $provider)
|
||||
} catch (\Exception $e) {
|
||||
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);
|
||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
if (! $found) {
|
||||
if ($application->build_pack === 'dockercompose') {
|
||||
$pr_app = ApplicationPreview::create([
|
||||
'git_type' => 'bitbucket',
|
||||
'application_id' => $application->id,
|
||||
'pull_request_id' => $pull_request_id,
|
||||
'pull_request_html_url' => $pull_request_html_url,
|
||||
'docker_compose_domains' => $application->docker_compose_domains,
|
||||
]);
|
||||
$pr_app->generate_preview_fqdn_compose();
|
||||
} else {
|
||||
ApplicationPreview::create([
|
||||
'git_type' => 'bitbucket',
|
||||
'application_id' => $application->id,
|
||||
@ -137,6 +147,7 @@ public function manual(Request $request)
|
||||
'pull_request_html_url' => $pull_request_html_url,
|
||||
]);
|
||||
}
|
||||
}
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
pull_request_id: $pull_request_id,
|
||||
|
@ -165,6 +165,16 @@ public function manual(Request $request)
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
if (! $found) {
|
||||
if ($application->build_pack === 'dockercompose') {
|
||||
$pr_app = ApplicationPreview::create([
|
||||
'git_type' => 'gitea',
|
||||
'application_id' => $application->id,
|
||||
'pull_request_id' => $pull_request_id,
|
||||
'pull_request_html_url' => $pull_request_html_url,
|
||||
'docker_compose_domains' => $application->docker_compose_domains,
|
||||
]);
|
||||
$pr_app->generate_preview_fqdn_compose();
|
||||
} else {
|
||||
ApplicationPreview::create([
|
||||
'git_type' => 'gitea',
|
||||
'application_id' => $application->id,
|
||||
@ -172,6 +182,8 @@ public function manual(Request $request)
|
||||
'pull_request_html_url' => $pull_request_html_url,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
pull_request_id: $pull_request_id,
|
||||
|
@ -170,6 +170,16 @@ public function manual(Request $request)
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
if (! $found) {
|
||||
if ($application->build_pack === 'dockercompose') {
|
||||
$pr_app = ApplicationPreview::create([
|
||||
'git_type' => 'github',
|
||||
'application_id' => $application->id,
|
||||
'pull_request_id' => $pull_request_id,
|
||||
'pull_request_html_url' => $pull_request_html_url,
|
||||
'docker_compose_domains' => $application->docker_compose_domains,
|
||||
]);
|
||||
$pr_app->generate_preview_fqdn_compose();
|
||||
} else {
|
||||
ApplicationPreview::create([
|
||||
'git_type' => 'github',
|
||||
'application_id' => $application->id,
|
||||
@ -177,6 +187,7 @@ public function manual(Request $request)
|
||||
'pull_request_html_url' => $pull_request_html_url,
|
||||
]);
|
||||
}
|
||||
}
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
pull_request_id: $pull_request_id,
|
||||
|
@ -180,6 +180,16 @@ public function manual(Request $request)
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
if (! $found) {
|
||||
if ($application->build_pack === 'dockercompose') {
|
||||
$pr_app = ApplicationPreview::create([
|
||||
'git_type' => 'gitlab',
|
||||
'application_id' => $application->id,
|
||||
'pull_request_id' => $pull_request_id,
|
||||
'pull_request_html_url' => $pull_request_html_url,
|
||||
'docker_compose_domains' => $application->docker_compose_domains,
|
||||
]);
|
||||
$pr_app->generate_preview_fqdn_compose();
|
||||
} else {
|
||||
ApplicationPreview::create([
|
||||
'git_type' => 'gitlab',
|
||||
'application_id' => $application->id,
|
||||
@ -187,6 +197,7 @@ public function manual(Request $request)
|
||||
'pull_request_html_url' => $pull_request_html_url,
|
||||
]);
|
||||
}
|
||||
}
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
pull_request_id: $pull_request_id,
|
||||
|
@ -9,6 +9,7 @@
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\GitlabApp;
|
||||
use App\Models\Server;
|
||||
@ -339,7 +340,7 @@ private function decide_what_to_do()
|
||||
private function post_deployment()
|
||||
{
|
||||
if ($this->server->isProxyShouldRun()) {
|
||||
GetContainersStatus::dispatch($this->server);
|
||||
GetContainersStatus::dispatch($this->server)->onQueue('high');
|
||||
// dispatch(new ContainerStatusJob($this->server));
|
||||
}
|
||||
$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()) {
|
||||
$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) {
|
||||
$real_value = $env->real_value;
|
||||
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()) {
|
||||
$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) {
|
||||
$real_value = $env->real_value;
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
@ -877,7 +884,6 @@ private function save_environment_variables()
|
||||
$real_value = '\''.$real_value.'\'';
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
ray($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) {
|
||||
$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();
|
||||
@ -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_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) {
|
||||
$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');
|
||||
if (! $nixpacks_php_fallback_path) {
|
||||
$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()
|
||||
@ -1005,7 +1023,6 @@ private function rolling_update()
|
||||
$this->application_deployment_queue->addLogEntry('Rolling update completed.');
|
||||
}
|
||||
}
|
||||
$this->framework_based_notification();
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->saved_outputs->get('nixpacks_plan')) {
|
||||
$this->nixpacks_plan = $this->saved_outputs->get('nixpacks_plan');
|
||||
if ($this->nixpacks_plan) {
|
||||
$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}");
|
||||
$parsed = Toml::Parse($this->nixpacks_plan);
|
||||
|
||||
// Do any modifications here
|
||||
$this->generate_env_variables();
|
||||
$merged_envs = $this->env_args->merge(collect(data_get($parsed, 'variables', [])));
|
||||
$aptPkgs = data_get($parsed, 'phases.setup.aptPkgs', []);
|
||||
if (count($aptPkgs) === 0) {
|
||||
$aptPkgs = ['curl', 'wget'];
|
||||
data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']);
|
||||
} else {
|
||||
if (! in_array('curl', $aptPkgs)) {
|
||||
@ -1388,6 +1408,12 @@ private function generate_nixpacks_confs()
|
||||
data_set($parsed, 'phases.setup.aptPkgs', $aptPkgs);
|
||||
}
|
||||
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->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]);
|
||||
if ($this->force_rebuild) {
|
||||
$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 {
|
||||
$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]);
|
||||
} else {
|
||||
if ($this->force_rebuild) {
|
||||
@ -1866,7 +1904,6 @@ private function build_image()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
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]);
|
||||
if ($this->force_rebuild) {
|
||||
$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 {
|
||||
$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]);
|
||||
} else {
|
||||
if ($this->force_rebuild) {
|
||||
@ -2184,6 +2232,9 @@ public function failed(Throwable $exception): void
|
||||
ray($code);
|
||||
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
|
||||
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->execute_remote_command(
|
||||
["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true]
|
||||
@ -2191,4 +2242,5 @@ public function failed(Throwable $exception): void
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public function handle(): void
|
||||
Log::info('No need to clean up '.$this->server->name);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('DockerCleanupJob failed with: '.$e->getMessage());
|
||||
// send_internal_notification('DockerCleanupJob failed with: '.$e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ public function handle(): void
|
||||
}
|
||||
ray('Sentinel image is up to date');
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('PullSentinelImageJob failed with: '.$e->getMessage());
|
||||
// send_internal_notification('PullSentinelImageJob failed with: '.$e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ public function handle()
|
||||
if ($this->server->isFunctional()) {
|
||||
$this->cleanup(notify: false);
|
||||
$this->remove_unnecessary_coolify_yaml();
|
||||
if (config('coolify.is_sentinel_enabled')) {
|
||||
if ($this->server->isSentinelEnabled()) {
|
||||
$this->server->checkSentinel();
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
protected $listeners = ['serverInstalled' => 'validateServer'];
|
||||
protected $listeners = ['refreshBoardingIndex' => 'validateServer'];
|
||||
|
||||
public string $currentState = 'welcome';
|
||||
|
||||
|
51
app/Livewire/MonacoEditor.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
//use Livewire\Component;
|
||||
use Illuminate\View\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class MonacoEditor extends Component
|
||||
{
|
||||
protected $listeners = [
|
||||
'configurationChanged' => '$refresh',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
public ?string $id,
|
||||
public ?string $name,
|
||||
public ?string $type,
|
||||
public ?string $monacoContent,
|
||||
public ?string $value,
|
||||
public ?string $label,
|
||||
public ?string $placeholder,
|
||||
public bool $required,
|
||||
public bool $disabled,
|
||||
public bool $readonly,
|
||||
public bool $allowTab,
|
||||
public bool $spellcheck,
|
||||
public ?string $helper,
|
||||
public bool $realtimeValidation,
|
||||
public bool $allowToPeak,
|
||||
public string $defaultClass,
|
||||
public string $defaultClassInput,
|
||||
public ?string $language
|
||||
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
if (is_null($this->id)) {
|
||||
$this->id = new Cuid2(7);
|
||||
}
|
||||
|
||||
if (is_null($this->name)) {
|
||||
$this->name = $this->id;
|
||||
}
|
||||
|
||||
return view('components.forms.monaco-editor');
|
||||
}
|
||||
}
|
@ -54,9 +54,9 @@ public function force_start()
|
||||
|
||||
public function cancel()
|
||||
{
|
||||
try {
|
||||
$kill_command = "docker rm -f {$this->application_deployment_queue->deployment_uuid}";
|
||||
$server_id = $this->application_deployment_queue->server_id ?? $this->application->destination->server_id;
|
||||
try {
|
||||
$server = Server::find($server_id);
|
||||
if ($this->application_deployment_queue->logs) {
|
||||
$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,
|
||||
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
||||
]);
|
||||
next_after_cancel($server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -347,7 +347,9 @@ public function set_redirect()
|
||||
public function submit($showToaster = true)
|
||||
{
|
||||
try {
|
||||
if ($this->application->isDirty('redirect')) {
|
||||
$this->set_redirect();
|
||||
}
|
||||
$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)->trim()->explode(',')->map(function ($domain) {
|
||||
|
@ -45,7 +45,7 @@ public function mount()
|
||||
public function check_status($showNotification = false)
|
||||
{
|
||||
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));
|
||||
} else {
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
GetContainersStatus::dispatchSync($this->application->destination->server);
|
||||
GetContainersStatus::dispatchSync($this->application->destination->server)->onQueue('high');
|
||||
$this->dispatch('reloadWindow');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
@ -12,7 +12,6 @@
|
||||
use App\Actions\Database\StartRedis;
|
||||
use App\Actions\Database\StopDatabase;
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use Livewire\Component;
|
||||
|
||||
class Heading extends Component
|
||||
|
@ -25,7 +25,17 @@ class General extends Component
|
||||
|
||||
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 = [
|
||||
'database.name' => 'required',
|
||||
@ -69,6 +79,11 @@ public function mount()
|
||||
$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()
|
||||
{
|
||||
try {
|
||||
|
@ -128,8 +128,8 @@ public function load_branch()
|
||||
) {
|
||||
$this->repository_url = $this->repository_url.'.git';
|
||||
}
|
||||
if (str($this->repository_url)->contains('github.com')) {
|
||||
$this->repository_url = str($this->repository_url)->before('.git')->value();
|
||||
if (str($this->repository_url)->contains('github.com') && str($this->repository_url)->endsWith('.git')) {
|
||||
$this->repository_url = str($this->repository_url)->beforeLast('.git')->value();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
@ -140,7 +140,6 @@ public function load_branch()
|
||||
$this->get_branch();
|
||||
$this->selected_branch = $this->git_branch;
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
if (! $this->branch_found && $this->git_branch == 'main') {
|
||||
try {
|
||||
$this->git_branch = 'master';
|
||||
|
@ -176,10 +176,12 @@ public function setType(string $type)
|
||||
|
||||
return;
|
||||
}
|
||||
// if (count($this->servers) === 1) {
|
||||
// $server = $this->servers->first();
|
||||
// $this->setServer($server);
|
||||
// }
|
||||
if (count($this->servers) === 1) {
|
||||
$server = $this->servers->first();
|
||||
if ($server instanceof Server) {
|
||||
$this->setServer($server);
|
||||
}
|
||||
}
|
||||
if (! is_null($this->server)) {
|
||||
$foundServer = $this->servers->where('id', $this->server->id)->first();
|
||||
if ($foundServer) {
|
||||
@ -195,6 +197,13 @@ public function setServer(Server $server)
|
||||
$this->server = $server;
|
||||
$this->standaloneDockers = $server->standaloneDockers;
|
||||
$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';
|
||||
}
|
||||
|
||||
|
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;
|
||||
|
||||
protected $listeners = ['refreshEnvs' => 'mount'];
|
||||
|
||||
protected $rules = [
|
||||
'service.docker_compose_raw' => 'required',
|
||||
'service.docker_compose' => 'required',
|
||||
|
@ -75,7 +75,6 @@ public function submit()
|
||||
$this->service->parse();
|
||||
$this->service->refresh();
|
||||
$this->service->saveComposeConfigs();
|
||||
$this->dispatch('refreshStacks');
|
||||
$this->dispatch('refreshEnvs');
|
||||
$this->dispatch('success', 'Service saved.');
|
||||
} catch (\Throwable $e) {
|
||||
|
@ -112,7 +112,6 @@ public function saveVariables($isPreview)
|
||||
$this->resource->environment_variables_preview()->whereNotIn('key', array_keys($variables))->delete();
|
||||
} else {
|
||||
$variables = parseEnvFormatToArray($this->variables);
|
||||
ray($variables, $this->variables);
|
||||
$this->resource->environment_variables()->whereNotIn('key', array_keys($variables))->delete();
|
||||
}
|
||||
foreach ($variables as $key => $variable) {
|
||||
|
@ -59,15 +59,6 @@ public function loadContainers($server_id)
|
||||
}
|
||||
}
|
||||
|
||||
public function loadMetrics()
|
||||
{
|
||||
return;
|
||||
$server = data_get($this->resource, 'destination.server');
|
||||
if ($server->isFunctional()) {
|
||||
$this->cpu = $server->getMetrics();
|
||||
}
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
try {
|
||||
@ -122,7 +113,6 @@ public function mount()
|
||||
|
||||
}
|
||||
|
||||
$this->loadMetrics();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
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 $environments;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$projectUuid = request()->route('project_uuid');
|
||||
@ -18,7 +20,8 @@ public function mount()
|
||||
if (! $project) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$project->load(['environments']);
|
||||
|
||||
$this->environments = $project->environments->sortBy('created_at');
|
||||
$this->project = $project;
|
||||
}
|
||||
|
||||
|
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->save();
|
||||
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
||||
$this->dispatch('serverInstalled');
|
||||
$this->dispatch('refreshServerShow');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@ -37,7 +37,7 @@ public function submit()
|
||||
$server->save();
|
||||
$server->settings->save();
|
||||
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
||||
$this->dispatch('serverInstalled');
|
||||
$this->dispatch('refreshServerShow');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Actions\Server\StartSentinel;
|
||||
use App\Actions\Server\StopSentinel;
|
||||
use App\Jobs\PullSentinelImageJob;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
@ -36,7 +39,12 @@ class Form extends Component
|
||||
'server.settings.is_build_server' => 'required|boolean',
|
||||
'server.settings.concurrent_builds' => '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',
|
||||
'server.settings.is_server_api_enabled' => 'required|boolean',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
@ -52,7 +60,11 @@ class Form extends Component
|
||||
'server.settings.is_build_server' => 'Build Server',
|
||||
'server.settings.concurrent_builds' => 'Concurrent Builds',
|
||||
'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()
|
||||
@ -69,18 +81,59 @@ public function serverInstalled()
|
||||
|
||||
public function updatedServerSettingsIsBuildServer()
|
||||
{
|
||||
$this->dispatch('serverInstalled');
|
||||
$this->dispatch('refreshServerShow');
|
||||
$this->dispatch('serverRefresh');
|
||||
$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()
|
||||
{
|
||||
try {
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
$this->validateServer(false);
|
||||
$this->server->settings->save();
|
||||
$this->server->save();
|
||||
$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) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class Show extends Component
|
||||
|
||||
public $parameters = [];
|
||||
|
||||
protected $listeners = ['serverInstalled' => '$refresh'];
|
||||
protected $listeners = ['refreshServerShow' => '$refresh'];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
|
@ -143,7 +143,8 @@ public function validateDockerVersion()
|
||||
} else {
|
||||
$this->docker_version = $this->server->validateDockerEngineVersion();
|
||||
if ($this->docker_version) {
|
||||
$this->dispatch('serverInstalled');
|
||||
$this->dispatch('refreshServerShow');
|
||||
$this->dispatch('refreshBoardingIndex');
|
||||
$this->dispatch('success', 'Server validated.');
|
||||
} 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>.';
|
||||
|
@ -29,6 +29,7 @@ class Configuration extends Component
|
||||
'settings.public_port_min' => 'required',
|
||||
'settings.public_port_max' => 'required',
|
||||
'settings.custom_dns_servers' => 'nullable',
|
||||
'settings.instance_name' => 'nullable',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
|
@ -228,18 +228,13 @@ public function gitCommits(): Attribute
|
||||
|
||||
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')) {
|
||||
return "{$this->source->html_url}/{$this->git_repository}/commits/{$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')) {
|
||||
$git_repository = str_replace('.git', '', $this->git_repository);
|
||||
$url = Url::fromString($git_repository);
|
||||
@ -248,6 +243,14 @@ public function gitCommitLink($link): string
|
||||
|
||||
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;
|
||||
}
|
||||
@ -532,7 +535,7 @@ public function isDeploymentInprogress()
|
||||
|
||||
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()
|
||||
@ -1167,4 +1170,44 @@ public function generate_preview_fqdn(int $pull_request_id)
|
||||
|
||||
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
|
||||
{
|
||||
return Attribute::make(
|
||||
set: fn (string $value) => strtolower($value),
|
||||
set: fn (string $value) => str($value)->lower()->trim()->replace('/', '-')->toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -47,4 +47,14 @@ public function getRecepients($notification)
|
||||
|
||||
return explode(',', $recipients);
|
||||
}
|
||||
|
||||
public function getTitleDisplayName(): string
|
||||
{
|
||||
$instanceName = $this->instance_name;
|
||||
if (! $instanceName) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return "[{$instanceName}]";
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
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\Enums\ProxyTypes;
|
||||
use App\Jobs\PullSentinelImageJob;
|
||||
use App\Notifications\Server\Revived;
|
||||
use App\Notifications\Server\Unreachable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Stringable;
|
||||
@ -462,10 +461,44 @@ public function forceDisableServer()
|
||||
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()
|
||||
{
|
||||
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 = json_decode($sentinel_found, true);
|
||||
$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) {
|
||||
$from = now()->subMinutes(5)->toIso8601ZuluString();
|
||||
$cpu = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl http://localhost:8888/api/cpu/history?from=$from'"], $this, false);
|
||||
if ($this->isMetricsEnabled()) {
|
||||
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||
$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();
|
||||
$parsedCollection = collect($cpu)->flatMap(function ($item) {
|
||||
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\SoftDeletes;
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class Service extends BaseModel
|
||||
{
|
||||
@ -837,14 +838,34 @@ public function saveComposeConfigs()
|
||||
$commands[] = "mkdir -p $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);
|
||||
|
||||
$commands[] = "echo $docker_compose_base64 | base64 -d | tee docker-compose.yml > /dev/null";
|
||||
$envs = $this->environment_variables()->get();
|
||||
$commands[] = 'rm -f .env || true';
|
||||
foreach ($envs as $env) {
|
||||
|
||||
foreach ($envs_from_coolify as $env) {
|
||||
$commands[] = "echo '{$env->key}={$env->real_value}' >> .env";
|
||||
}
|
||||
if ($envs->count() === 0) {
|
||||
if ($envs_from_coolify->count() === 0) {
|
||||
$commands[] = 'touch .env';
|
||||
}
|
||||
instant_remote_process($commands, $this->server);
|
||||
|
@ -226,4 +226,33 @@ public function scheduledBackups()
|
||||
{
|
||||
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');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ public function __construct(public Server $server)
|
||||
if ($this->server->unreachable_notification_sent === false) {
|
||||
return;
|
||||
}
|
||||
GetContainersStatus::dispatch($server);
|
||||
GetContainersStatus::dispatch($server)->onQueue('high');
|
||||
// dispatch(new ContainerStatusJob($server));
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,8 @@ public function __construct(
|
||||
public ?string $value = null,
|
||||
public ?string $label = null,
|
||||
public ?string $placeholder = null,
|
||||
public ?string $monacoEditorLanguage = '',
|
||||
public bool $useMonacoEditor = false,
|
||||
public bool $required = false,
|
||||
public bool $disabled = false,
|
||||
public bool $readonly = false,
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
function get_team_id_from_token()
|
||||
{
|
||||
$token = auth()->user()->currentAccessToken();
|
||||
@ -10,3 +12,27 @@ function invalid_token()
|
||||
{
|
||||
return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api-reference/authorization'], 400);
|
||||
}
|
||||
|
||||
function serialize_api_response($data)
|
||||
{
|
||||
if (! $data instanceof Collection) {
|
||||
$data = collect($data);
|
||||
}
|
||||
$data = $data->sortKeys();
|
||||
$created_at = data_get($data, 'created_at');
|
||||
$updated_at = data_get($data, 'updated_at');
|
||||
if ($created_at) {
|
||||
unset($data['created_at']);
|
||||
$data['created_at'] = $created_at;
|
||||
|
||||
}
|
||||
if ($updated_at) {
|
||||
unset($data['updated_at']);
|
||||
$data['updated_at'] = $updated_at;
|
||||
}
|
||||
if (data_get($data, 'id')) {
|
||||
$data = $data->prepend($data['id'], 'id');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
use App\Models\StandaloneDocker;
|
||||
use Spatie\Url\Url;
|
||||
|
||||
function queue_application_deployment(Application $application, string $deployment_uuid, ?int $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null, bool $no_questions_asked = false, ?Server $server = null, ?StandaloneDocker $destination = null, bool $only_this_server = false, bool $rollback = false)
|
||||
function queue_application_deployment(Application $application, string $deployment_uuid, ?int $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $is_api = false, bool $restart_only = false, ?string $git_type = null, bool $no_questions_asked = false, ?Server $server = null, ?StandaloneDocker $destination = null, bool $only_this_server = false, bool $rollback = false)
|
||||
{
|
||||
$application_id = $application->id;
|
||||
$deployment_link = Url::fromString($application->link()."/deployment/{$deployment_uuid}");
|
||||
@ -35,6 +35,7 @@ function queue_application_deployment(Application $application, string $deployme
|
||||
'pull_request_id' => $pull_request_id,
|
||||
'force_rebuild' => $force_rebuild,
|
||||
'is_webhook' => $is_webhook,
|
||||
'is_api' => $is_api,
|
||||
'restart_only' => $restart_only,
|
||||
'commit' => $commit,
|
||||
'rollback' => $rollback,
|
||||
@ -65,7 +66,7 @@ function force_start_deployment(ApplicationDeploymentQueue $deployment)
|
||||
function queue_next_deployment(Application $application)
|
||||
{
|
||||
$server_id = $application->destination->server_id;
|
||||
$next_found = ApplicationDeploymentQueue::where('server_id', $server_id)->where('status', 'queued')->get()->sortBy('created_at')->first();
|
||||
$next_found = ApplicationDeploymentQueue::where('server_id', $server_id)->where('status', ApplicationDeploymentStatus::QUEUED)->get()->sortBy('created_at')->first();
|
||||
if ($next_found) {
|
||||
$next_found->update([
|
||||
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
|
||||
@ -79,7 +80,7 @@ function queue_next_deployment(Application $application)
|
||||
|
||||
function next_queuable(string $server_id, string $application_id): bool
|
||||
{
|
||||
$deployments = ApplicationDeploymentQueue::where('server_id', $server_id)->whereIn('status', ['in_progress', 'queued'])->get()->sortByDesc('created_at');
|
||||
$deployments = ApplicationDeploymentQueue::where('server_id', $server_id)->whereIn('status', ['in_progress', ApplicationDeploymentStatus::QUEUED])->get()->sortByDesc('created_at');
|
||||
$same_application_deployments = $deployments->where('application_id', $application_id);
|
||||
$in_progress = $same_application_deployments->filter(function ($value, $key) {
|
||||
return $value->status === 'in_progress';
|
||||
@ -98,3 +99,26 @@ function next_queuable(string $server_id, string $application_id): bool
|
||||
|
||||
return true;
|
||||
}
|
||||
function next_after_cancel(?Server $server = null)
|
||||
{
|
||||
if ($server) {
|
||||
$next_found = ApplicationDeploymentQueue::where('server_id', data_get($server, 'id'))->where('status', ApplicationDeploymentStatus::QUEUED)->get()->sortBy('created_at');
|
||||
if ($next_found->count() > 0) {
|
||||
foreach ($next_found as $next) {
|
||||
$server = Server::find($next->server_id);
|
||||
$concurrent_builds = $server->settings->concurrent_builds;
|
||||
$inprogress_deployments = ApplicationDeploymentQueue::where('server_id', $next->server_id)->whereIn('status', [ApplicationDeploymentStatus::QUEUED])->get()->sortByDesc('created_at');
|
||||
if ($inprogress_deployments->count() < $concurrent_builds) {
|
||||
$next->update([
|
||||
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
|
||||
]);
|
||||
|
||||
dispatch(new ApplicationDeploymentJob(
|
||||
application_deployment_queue_id: $next->id,
|
||||
));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,10 @@ function backup_dir(): string
|
||||
{
|
||||
return base_configuration_dir().'/backups';
|
||||
}
|
||||
function metrics_dir(): string
|
||||
{
|
||||
return base_configuration_dir().'/metrics';
|
||||
}
|
||||
|
||||
function generate_readme_file(string $name, string $updated_at): string
|
||||
{
|
||||
@ -158,10 +162,10 @@ function get_route_parameters(): array
|
||||
function get_latest_sentinel_version(): string
|
||||
{
|
||||
try {
|
||||
$response = Http::get('https://cdn.coollabs.io/coolify/versions.json');
|
||||
$response = Http::get('https://cdn.coollabs.io/sentinel/versions.json');
|
||||
$versions = $response->json();
|
||||
|
||||
return data_get($versions, 'coolify.sentinel.version');
|
||||
return data_get($versions, 'sentinel.version');
|
||||
} catch (\Throwable $e) {
|
||||
//throw $e;
|
||||
ray($e->getMessage());
|
||||
@ -1250,6 +1254,18 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
]);
|
||||
}
|
||||
}
|
||||
$envs_from_coolify = $resource->environment_variables()->get();
|
||||
$serviceVariables = $serviceVariables->map(function ($variable) use ($envs_from_coolify) {
|
||||
$env_variable_key = str($variable)->before('=');
|
||||
$env_variable_value = str($variable)->after('=');
|
||||
$found_env = $envs_from_coolify->where('key', $env_variable_key)->first();
|
||||
if ($found_env) {
|
||||
$env_variable_value = $found_env->value;
|
||||
}
|
||||
|
||||
return "$env_variable_key=$env_variable_value";
|
||||
});
|
||||
|
||||
}
|
||||
// Add labels to the service
|
||||
if ($savedService->serviceType()) {
|
||||
@ -1314,19 +1330,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
data_forget($service, 'volumes.*.isDirectory');
|
||||
data_forget($service, 'volumes.*.is_directory');
|
||||
data_forget($service, 'exclude_from_hc');
|
||||
|
||||
// Remove unnecessary variables from service.environment
|
||||
// $withoutServiceEnvs = collect([]);
|
||||
// collect(data_get($service, 'environment'))->each(function ($value, $key) use ($withoutServiceEnvs) {
|
||||
// ray($key, $value);
|
||||
// if (!Str::of($key)->startsWith('$SERVICE_') && !Str::of($value)->startsWith('SERVICE_')) {
|
||||
// $k = Str::of($value)->before("=");
|
||||
// $v = Str::of($value)->after("=");
|
||||
// $withoutServiceEnvs->put($k->value(), $v->value());
|
||||
// }
|
||||
// });
|
||||
// ray($withoutServiceEnvs);
|
||||
// data_set($service, 'environment', $withoutServiceEnvs->toArray());
|
||||
data_set($service, 'environment', $serviceVariables->toArray());
|
||||
updateCompose($savedService);
|
||||
|
||||
return $service;
|
||||
@ -2282,3 +2286,10 @@ function isAnyDeploymentInprogress()
|
||||
echo "No deployments in progress.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
function generateSentinelToken()
|
||||
{
|
||||
$token = Str::random(64);
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
@ -22,8 +22,8 @@
|
||||
],
|
||||
'services' => [
|
||||
// Temporary disabled until cache is implemented
|
||||
'official' => 'https://cdn.coollabs.io/coolify/service-templates.json',
|
||||
// 'official' => 'https://raw.githubusercontent.com/coollabsio/coolify/main/templates/service-templates.json',
|
||||
// 'official' => 'https://cdn.coollabs.io/coolify/service-templates.json',
|
||||
'official' => 'https://raw.githubusercontent.com/coollabsio/coolify/main/templates/service-templates.json',
|
||||
],
|
||||
'limits' => [
|
||||
'trial_period' => 0,
|
||||
|
@ -14,5 +14,4 @@
|
||||
'helper_image' => env('HELPER_IMAGE', 'ghcr.io/coollabsio/coolify-helper:latest'),
|
||||
'is_horizon_enabled' => env('HORIZON_ENABLED', true),
|
||||
'is_scheduler_enabled' => env('SCHEDULER_ENABLED', true),
|
||||
'is_sentinel_enabled' => env('SENTINEL_ENABLED', false),
|
||||
];
|
||||
|
@ -182,7 +182,7 @@
|
||||
'defaults' => [
|
||||
's6' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => ['default'],
|
||||
'queue' => ['high', 'default'],
|
||||
'balance' => env('HORIZON_BALANCE', 'auto'),
|
||||
'maxTime' => 0,
|
||||
'maxJobs' => 0,
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => '4.0.0-beta.297',
|
||||
'release' => '4.0.0-beta.298',
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SESSION_DRIVER', 'redis'),
|
||||
'driver' => env('SESSION_DRIVER', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.297';
|
||||
return '4.0.0-beta.298';
|
||||
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('servers', function (Blueprint $table) {
|
||||
$table->dropColumn('is_metrics_enabled');
|
||||
});
|
||||
Schema::table('server_settings', function (Blueprint $table) {
|
||||
$table->boolean('is_metrics_enabled')->default(false);
|
||||
$table->integer('metrics_refresh_rate_seconds')->default(5);
|
||||
$table->integer('metrics_history_days')->default(30);
|
||||
$table->string('metrics_token')->default(generateSentinelToken());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('servers', function (Blueprint $table) {
|
||||
$table->boolean('is_metrics_enabled')->default(true);
|
||||
});
|
||||
Schema::table('server_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('is_metrics_enabled');
|
||||
$table->dropColumn('metrics_refresh_rate_seconds');
|
||||
$table->dropColumn('metrics_history_days');
|
||||
$table->dropColumn('metrics_token');
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('server_settings', function (Blueprint $table) {
|
||||
$table->boolean('is_server_api_enabled')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('server_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('is_server_api_enabled');
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('application_deployment_queues', function (Blueprint $table) {
|
||||
$table->boolean('is_api')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('application_deployment_queues', function (Blueprint $table) {
|
||||
$table->dropColumn('is_api');
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('instance_settings', function (Blueprint $table) {
|
||||
$table->string('instance_name')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('instance_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('instance_name');
|
||||
});
|
||||
}
|
||||
};
|
30
lang/ar.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"auth.login": "تسجيل الدخول",
|
||||
"auth.login.azure": "تسجيل الدخول باستخدام Microsoft",
|
||||
"auth.login.bitbucket": "تسجيل الدخول باستخدام Bitbucket",
|
||||
"auth.login.github": "تسجيل الدخول باستخدام GitHub",
|
||||
"auth.login.gitlab": "تسجيل الدخول باستخدام Gitlab",
|
||||
"auth.login.google": "تسجيل الدخول باستخدام Google",
|
||||
"auth.already_registered": "هل سبق لك التسجيل؟",
|
||||
"auth.confirm_password": "تأكيد كلمة المرور",
|
||||
"auth.forgot_password": "نسيت كلمة المرور",
|
||||
"auth.forgot_password_send_email": "إرسال بريد إلكتروني لإعادة تعيين كلمة المرور",
|
||||
"auth.register_now": "تسجيل",
|
||||
"auth.logout": "تسجيل الخروج",
|
||||
"auth.register": "تسجيل",
|
||||
"auth.registration_disabled": "تم تعطيل التسجيل. يرجى التواصل مع المسؤول.",
|
||||
"auth.reset_password": "إعادة تعيين كلمة المرور",
|
||||
"auth.failed": "هذه البيانات لا تتطابق مع سجلاتنا.",
|
||||
"auth.failed.callback": "فشل في معالجة استدعاء من مزود تسجيل الدخول.",
|
||||
"auth.failed.password": "كلمة المرور المقدمة غير صحيحة.",
|
||||
"auth.failed.email": "لا يمكننا العثور على مستخدم بهذا البريد الإلكتروني.",
|
||||
"auth.throttle": "عدد محاولات تسجيل الدخول كثيرة جدًا. يرجى المحاولة مرة أخرى في :seconds ثانية.",
|
||||
"input.name": "الاسم",
|
||||
"input.email": "البريد الإلكتروني",
|
||||
"input.password": "كلمة المرور",
|
||||
"input.password.again": "كلمة المرور مرة أخرى",
|
||||
"input.code": "الرمز لمرة واحدة",
|
||||
"input.recovery_code": "رمز الاسترداد",
|
||||
"button.save": "حفظ",
|
||||
"repository.url": "<span class='text-helper'>أمثلة</span><br>للمستودعات العامة، استخدم <span class='text-helper'>https://...</span>.<br>للمستودعات الخاصة، استخدم <span class='text-helper'>git@...</span>.<br><br>سيتم تحديد الفرع <span class='text-helper'>main</span> لـ <span class='text-helper'>https://github.com/coollabsio/coolify-examples</span><br>سيتم تحديد الفرع <span class='text-helper'>nodejs-fastify</span> لـ <span class='text-helper'>https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify</span><br>سيتم تحديد الفرع <span class='text-helper'>main</span> لـ <span class='text-helper'>https://gitea.com/sedlav/expressjs.git</span><br>سيتم تحديد الفرع <span class='text-helper'>main</span> لـ <span class='text-helper'>https://gitlab.com/andrasbacsai/nodejs-example.git</span>."
|
||||
}
|
30
lang/de.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"auth.login": "Anmelden",
|
||||
"auth.login.azure": "Mit Microsoft anmelden",
|
||||
"auth.login.bitbucket": "Mit Bitbucket anmelden",
|
||||
"auth.login.github": "Mit GitHub anmelden",
|
||||
"auth.login.gitlab": "Mit GitLab anmelden",
|
||||
"auth.login.google": "Mit Google anmelden",
|
||||
"auth.already_registered": "Bereits registriert?",
|
||||
"auth.confirm_password": "Passwort bestätigen",
|
||||
"auth.forgot_password": "Passwort vergessen",
|
||||
"auth.forgot_password_send_email": "Passwort zurücksetzen E-Mail senden",
|
||||
"auth.register_now": "Registrieren",
|
||||
"auth.logout": "Abmelden",
|
||||
"auth.register": "Registrieren",
|
||||
"auth.registration_disabled": "Registrierung ist deaktiviert. Bitte kontaktiere einen Administrator.",
|
||||
"auth.reset_password": "Passwort zurücksetzen",
|
||||
"auth.failed": "Diese Anmeldedaten wurden nicht gefunden.",
|
||||
"auth.failed.callback": "Fehlerhafte Verarbeitung der Antwort des Anmeldeanbieters.",
|
||||
"auth.failed.password": "Das angegebene Passwort ist inkorrekt.",
|
||||
"auth.failed.email": "Wir können keinen Benutzer mit dieser E-Mail Adresse finden.",
|
||||
"auth.throttle": "Zu viele Anmeldeversuche. Bitte versuche es in :seconds Sekunden erneut.",
|
||||
"input.name": "Name",
|
||||
"input.email": "E-Mail",
|
||||
"input.password": "Passwort",
|
||||
"input.password.again": "Passwort wiederholen",
|
||||
"input.code": "Einmalcode",
|
||||
"input.recovery_code": "Wiederherstellungscode",
|
||||
"button.save": "Speichern",
|
||||
"repository.url": "<span class='text-helper'>Beispiele</span><br>Für öffentliche Repositories benutze <span class='text-helper'>https://...</span>.<br>Für private Repositories benutze <span class='text-helper'>git@...</span>.<br><br>https://github.com/coollabsio/coolify-examples <span class='text-helper'>main</span> Branch wird ausgewählt<br>https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify <span class='text-helper'>nodejs-fastify</span> Branch wird ausgewählt.<br>https://gitea.com/sedlav/expressjs.git <span class='text-helper'>main</span> Branch wird ausgewählt.<br>https://gitlab.com/andrasbacsai/nodejs-example.git <span class='text-helper'>main</span> Branch wird ausgewählt."
|
||||
}
|
30
lang/es.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"auth.login": "Iniciar Sesión",
|
||||
"auth.login.azure": "Acceder con Microsoft",
|
||||
"auth.login.bitbucket": "Acceder con Bitbucket",
|
||||
"auth.login.github": "Acceder con GitHub",
|
||||
"auth.login.gitlab": "Acceder con Gitlab",
|
||||
"auth.login.google": "Acceder con Google",
|
||||
"auth.already_registered": "¿Ya estás registrado?",
|
||||
"auth.confirm_password": "Confirmar contraseña",
|
||||
"auth.forgot_password": "¿Olvidaste tu contraseña?",
|
||||
"auth.forgot_password_send_email": "Enviar correo de recuperación de contraseña",
|
||||
"auth.register_now": "Registrar",
|
||||
"auth.logout": "Cerrar sesión",
|
||||
"auth.register": "Registrar",
|
||||
"auth.registration_disabled": "El registro está desactivado. Por favor contacta con el administrador.",
|
||||
"auth.reset_password": "Cambiar contraseña",
|
||||
"auth.failed": "Las credenciales no coinciden con nuestro registro..",
|
||||
"auth.failed.callback": "Falló el proceso de inicio de sesión con el proveedor.",
|
||||
"auth.failed.password": "La contraseña es incorrecta.",
|
||||
"auth.failed.email": "No encontramos un usuario con ese correo.",
|
||||
"auth.throttle": "Demasiados intentos. Por favor inténtalo en :seconds segundos.",
|
||||
"input.name": "Nombre",
|
||||
"input.email": "Correo",
|
||||
"input.password": "Contraseña",
|
||||
"input.password.again": "Escribe la contraseña otra vez",
|
||||
"input.code": "Código de único uso",
|
||||
"input.recovery_code": "Código de recuperación",
|
||||
"button.save": "Guardar",
|
||||
"repository.url": "<span class='text-helper'>Examples</span><br>Para repositorios públicos, usar <span class='text-helper'>https://...</span>.<br>Para repositorios privados, usar <span class='text-helper'>git@...</span>.<br><br>https://github.com/coollabsio/coolify-examples <span class='text-helper'>main</span> la rama 'main' será seleccionada.<br>https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify <span class='text-helper'>nodejs-fastify</span> la rama 'nodejs-fastify' será seleccionada.<br>https://gitea.com/sedlav/expressjs.git <span class='text-helper'>main</span> la rama 'main' será seleccionada.<br>https://gitlab.com/andrasbacsai/nodejs-example.git <span class='text-helper'>main</span> la rama 'main' será seleccionada."
|
||||
}
|
30
lang/fr.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"auth.login": "Connexion",
|
||||
"auth.login.azure": "Connexion avec Microsoft",
|
||||
"auth.login.bitbucket": "Connexion avec Bitbucket",
|
||||
"auth.login.github": "Connexion avec GitHub",
|
||||
"auth.login.gitlab": "Connexion avec Gitlab",
|
||||
"auth.login.google": "Connexion avec Google",
|
||||
"auth.already_registered": "Déjà enregistré ?",
|
||||
"auth.confirm_password": "Confirmer le mot de passe",
|
||||
"auth.forgot_password": "Mot de passe oublié",
|
||||
"auth.forgot_password_send_email": "Envoyer l'email de réinitialisation de mot de passe",
|
||||
"auth.register_now": "S'enregistrer",
|
||||
"auth.logout": "Déconnexion",
|
||||
"auth.register": "S'enregistrer",
|
||||
"auth.registration_disabled": "L'enregistrement est désactivé. Merci de contacter l'administateur.",
|
||||
"auth.reset_password": "Réinitialiser le mot de passe",
|
||||
"auth.failed": "Aucune correspondance n'a été trouvé pour les informations d'identification renseignées.",
|
||||
"auth.failed.callback": "Erreur lors du processus de retour de la plateforme de connexion.",
|
||||
"auth.failed.password": "Le mot de passe renseigné est incorrect.",
|
||||
"auth.failed.email": "Aucun utilisateur avec cette adresse email n'a été trouvé.",
|
||||
"auth.throttle": "Trop de tentatives de connexion. Merci de réessayer dans :seconds secondes.",
|
||||
"input.name": "Nom",
|
||||
"input.email": "Email",
|
||||
"input.password": "Mot de passe",
|
||||
"input.password.again": "Mot de passe identique",
|
||||
"input.code": "Code à usage unique",
|
||||
"input.recovery_code": "Code de récupération",
|
||||
"button.save": "Sauvegarder",
|
||||
"repository.url": "<span class='text-helper'>Exemples</span><br>Pour les dépôts publiques, utilisez <span class='text-helper'>https://...</span>.<br>Pour les dépôts privés, utilisez <span class='text-helper'>git@...</span>.<br><br>https://github.com/coollabsio/coolify-examples <span class='text-helper'>main</span> sera la branche selectionnée<br>https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify <span class='text-helper'>nodejs-fastify</span> sera la branche selectionnée.<br>https://gitea.com/sedlav/expressjs.git <span class='text-helper'>main</span> sera la branche selectionnée.<br>https://gitlab.com/andrasbacsai/nodejs-example.git <span class='text-helper'>main</span> sera la branche selectionnée."
|
||||
}
|
30
lang/it.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"auth.login": "Accedi",
|
||||
"auth.login.azure": "Accedi con Microsoft",
|
||||
"auth.login.bitbucket": "Accedi con Bitbucket",
|
||||
"auth.login.github": "Accedi con GitHub",
|
||||
"auth.login.gitlab": "Accedi con Gitlab",
|
||||
"auth.login.google": "Accedi con Google",
|
||||
"auth.already_registered": "Già registrato?",
|
||||
"auth.confirm_password": "Conferma password",
|
||||
"auth.forgot_password": "Password dimenticata",
|
||||
"auth.forgot_password_send_email": "Invia email per reimpostare la password",
|
||||
"auth.register_now": "Registrati",
|
||||
"auth.logout": "Esci",
|
||||
"auth.register": "Registrati",
|
||||
"auth.registration_disabled": "La registrazione è disabilitata. Si prega di contattare l'amministratore.",
|
||||
"auth.reset_password": "Reimposta password",
|
||||
"auth.failed": "Queste credenziali non corrispondono ai nostri record.",
|
||||
"auth.failed.callback": "Errore durante l'elaborazione del callback dal provider di accesso.",
|
||||
"auth.failed.password": "La password fornita non è corretta.",
|
||||
"auth.failed.email": "Non possiamo trovare un utente con questo indirizzo email.",
|
||||
"auth.throttle": "Troppi tentativi di accesso. Per favore riprova tra :seconds secondi.",
|
||||
"input.name": "Nome",
|
||||
"input.email": "Email",
|
||||
"input.password": "Password",
|
||||
"input.password.again": "Ripeti password",
|
||||
"input.code": "Codice monouso",
|
||||
"input.recovery_code": "Codice di recupero",
|
||||
"button.save": "Salva",
|
||||
"repository.url": "<span class='text-helper'>Esempi</span><br>Per i repository pubblici, utilizza <span class='text-helper'>https://...</span>.<br>Per i repository privati, utilizza <span class='text-helper'>git@...</span>.<br><br>https://github.com/coollabsio/coolify-examples verrà selezionato il branch <span class='text-helper'>main</span><br>https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify verrà selezionato il branch <span class='text-helper'>nodejs-fastify</span>.<br>https://gitea.com/sedlav/expressjs.git verrà selezionato il branch <span class='text-helper'>main</span>.<br>https://gitlab.com/andrasbacsai/nodejs-example.git verrà selezionato il branch <span class='text-helper'>main</span>."
|
||||
}
|
30
lang/ja.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"auth.login": "ログイン",
|
||||
"auth.login.azure": "Microsoftでログイン",
|
||||
"auth.login.bitbucket": "Bitbucketでログイン",
|
||||
"auth.login.github": "GitHubでログイン",
|
||||
"auth.login.gitlab": "Gitlabでログイン",
|
||||
"auth.login.google": "Googleでログイン",
|
||||
"auth.already_registered": "すでに登録済みですか?",
|
||||
"auth.confirm_password": "パスワードを確認",
|
||||
"auth.forgot_password": "パスワードを忘れた",
|
||||
"auth.forgot_password_send_email": "パスワードリセットメールを送信",
|
||||
"auth.register_now": "今すぐ登録",
|
||||
"auth.logout": "ログアウト",
|
||||
"auth.register": "登録",
|
||||
"auth.registration_disabled": "登録は無効です。管理者に連絡してください。",
|
||||
"auth.reset_password": "パスワードをリセット",
|
||||
"auth.failed": "これらの資格情報は記録と一致しません。",
|
||||
"auth.failed.callback": "ログインプロバイダーからのコールバックの処理に失敗しました。",
|
||||
"auth.failed.password": "提供されたパスワードが正しくありません。",
|
||||
"auth.failed.email": "そのメールアドレスのユーザーが見つかりません。",
|
||||
"auth.throttle": "ログイン試行回数が多すぎます。:seconds秒後にもう一度お試しください。",
|
||||
"input.name": "名前",
|
||||
"input.email": "メール",
|
||||
"input.password": "パスワード",
|
||||
"input.password.again": "パスワード再入力",
|
||||
"input.code": "ワンタイムコード",
|
||||
"input.recovery_code": "リカバリーコード",
|
||||
"button.save": "保存",
|
||||
"repository.url": "<span class='text-helper'>例</span><br>公開リポジトリの場合は<span class='text-helper'>https://...</span>を使用してください。<br>プライベートリポジトリの場合は<span class='text-helper'>git@...</span>を使用してください。<br><br>https://github.com/coollabsio/coolify-examples <span class='text-helper'>main</span>ブランチが選択されます<br>https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify <span class='text-helper'>nodejs-fastify</span>ブランチが選択されます。<br>https://gitea.com/sedlav/expressjs.git <span class='text-helper'>main</span>ブランチが選択されます。<br>https://gitlab.com/andrasbacsai/nodejs-example.git <span class='text-helper'>main</span>ブランチが選択されます。"
|
||||
}
|
30
lang/pt.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"auth.login": "Entrar",
|
||||
"auth.login.azure": "Entrar com Microsoft",
|
||||
"auth.login.bitbucket": "Entrar com Bitbucket",
|
||||
"auth.login.github": "Entrar com GitHub",
|
||||
"auth.login.gitlab": "Entrar com Gitlab",
|
||||
"auth.login.google": "Entrar com Google",
|
||||
"auth.already_registered": "Já tem uma conta?",
|
||||
"auth.confirm_password": "Confirmar senha",
|
||||
"auth.forgot_password": "Esqueceu a senha?",
|
||||
"auth.forgot_password_send_email": "Enviar e-mail de redefinição de senha",
|
||||
"auth.register_now": "Cadastrar-se",
|
||||
"auth.logout": "Sair",
|
||||
"auth.register": "Cadastrar",
|
||||
"auth.registration_disabled": "Cadastro desativado. Por favor, entre em contato com o administrador.",
|
||||
"auth.reset_password": "Redefinir senha",
|
||||
"auth.failed": "Essas credenciais não correspondem aos nossos registros.",
|
||||
"auth.failed.callback": "Falha ao processar o callback do provedor de login.",
|
||||
"auth.failed.password": "A senha fornecida está incorreta.",
|
||||
"auth.failed.email": "Não encontramos um usuário com esse endereço de e-mail.",
|
||||
"auth.throttle": "Muitas tentativas de login. Por favor, tente novamente em :seconds segundos.",
|
||||
"input.name": "Nome",
|
||||
"input.email": "E-mail",
|
||||
"input.password": "Senha",
|
||||
"input.password.again": "Repetir senha",
|
||||
"input.code": "Código único",
|
||||
"input.recovery_code": "Código de recuperação",
|
||||
"button.save": "Salvar",
|
||||
"repository.url": "<span class='text-helper'>Exemplos</span><br>Para repositórios públicos, use <span class='text-helper'>https://...</span>.<br>Para repositórios privados, use <span class='text-helper'>git@...</span>.<br><br>https://github.com/coollabsio/coolify-examples <span class='text-helper'>a branch main</span> será selecionada<br>https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify <span class='text-helper'>a branch nodejs-fastify</span> será selecionada.<br>https://gitea.com/sedlav/expressjs.git <span class='text-helper'>a branch main</span> será selecionada.<br>https://gitlab.com/andrasbacsai/nodejs-example.git <span class='text-helper'>a branch main</span> será selecionada."
|
||||
}
|
30
lang/tr.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"auth.login": "Giriş",
|
||||
"auth.login.azure": "Microsoft ile Giriş Yap",
|
||||
"auth.login.bitbucket": "Bitbucket ile Giriş Yap",
|
||||
"auth.login.github": "GitHub ile Giriş Yap",
|
||||
"auth.login.gitlab": "GitLab ile Giriş Yap",
|
||||
"auth.login.google": "Google ile Giriş Yap",
|
||||
"auth.already_registered": "Zaten kayıtlı mısınız?",
|
||||
"auth.confirm_password": "Şifreyi Onayla",
|
||||
"auth.forgot_password": "Şifremi Unuttum",
|
||||
"auth.forgot_password_send_email": "Şifre sıfırlama e-postası gönder",
|
||||
"auth.register_now": "Kayıt Ol",
|
||||
"auth.logout": "Çıkış Yap",
|
||||
"auth.register": "Kayıt Ol",
|
||||
"auth.registration_disabled": "Kayıt devre dışı bırakıldı. Lütfen yöneticiyle iletişime geçin.",
|
||||
"auth.reset_password": "Şifreyi Sıfırla",
|
||||
"auth.failed": "Bu kimlik bilgileri kayıtlarımızla eşleşmiyor.",
|
||||
"auth.failed.callback": "Giriş sağlayıcıdan gelen istek işlenemedi.",
|
||||
"auth.failed.password": "Sağlanan şifre yanlış.",
|
||||
"auth.failed.email": "Bu e-posta adresiyle bir kullanıcı bulamıyoruz.",
|
||||
"auth.throttle": "Çok fazla giriş denemesi. Lütfen :seconds saniye sonra tekrar deneyin.",
|
||||
"input.name": "İsim",
|
||||
"input.email": "E-posta",
|
||||
"input.password": "Şifre",
|
||||
"input.password.again": "Şifreyi Tekrar Girin",
|
||||
"input.code": "Tek Kullanımlık Kod",
|
||||
"input.recovery_code": "Kurtarma Kodu",
|
||||
"button.save": "Kaydet",
|
||||
"repository.url": "<span class='text-helper'>Örnekler</span><br>Halka açık depolar için <span class='text-helper'>https://...</span> kullanın.<br>Özel depolar için <span class='text-helper'>git@...</span> kullanın.<br><br>https://github.com/coollabsio/coolify-examples <span class='text-helper'>main</span> dalı seçilecek<br>https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify <span class='text-helper'>nodejs-fastify</span> dalı seçilecek.<br>https://gitea.com/sedlav/expressjs.git <span class='text-helper'>main</span> dalı seçilecek.<br>https://gitlab.com/andrasbacsai/nodejs-example.git <span class='text-helper'>main</span> dalı seçilecek."
|
||||
}
|
30
lang/vi.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"auth.login": "Đăng Nhập",
|
||||
"auth.login.azure": "Đăng Nhập Bằng Microsoft",
|
||||
"auth.login.bitbucket": "Đăng Nhập Bằng Bitbucket",
|
||||
"auth.login.github": "Đăng Nhập Bằng GitHub",
|
||||
"auth.login.gitlab": "Đăng Nhập Bằng Gitlab",
|
||||
"auth.login.google": "Đăng Nhập Bằng Google",
|
||||
"auth.already_registered": "Đã đăng ký?",
|
||||
"auth.confirm_password": "Nhập lại mật khẩu",
|
||||
"auth.forgot_password": "Quên mật khẩu",
|
||||
"auth.forgot_password_send_email": "Gửi email đặt lại mật khẩu",
|
||||
"auth.register_now": "Đăng ký ngay",
|
||||
"auth.logout": "Đăng xuất",
|
||||
"auth.register": "Đăng ký",
|
||||
"auth.registration_disabled": "Đăng ký không khả dụng. Vui lòng liên hệ quản trị viên.",
|
||||
"auth.reset_password": "Đặt lại mật khẩu",
|
||||
"auth.failed": "Thông tin đăng nhập không khớp với bất kỳ tài khoản nào.",
|
||||
"auth.failed.callback": "Xử lý thông tin từ nhà cung cấp đăng nhập thất bại.",
|
||||
"auth.failed.password": "Mật khẩu bạn cung cấp không chính xác.",
|
||||
"auth.failed.email": "Không có người dùng nào đã đăng ký với email đó.",
|
||||
"auth.throttle": "Quá nhiều lần đăng nhập thất bại. Vui lòng thử lại sau :seconds giây.",
|
||||
"input.name": "Tên",
|
||||
"input.email": "Email",
|
||||
"input.password": "Mật khẩu",
|
||||
"input.password.again": "Mật khẩu lần nữa",
|
||||
"input.code": "One-time code",
|
||||
"input.recovery_code": "Mã khôi phục",
|
||||
"button.save": "Lưu",
|
||||
"repository.url": "<span class='text-helper'>Ví dụ</span><br>Với repo công khai, sử dụng <span class='text-helper'>https://...</span>.<br>Với repo riêng tư, sử dụng <span class='text-helper'>git@...</span>.<br><br>https://github.com/coollabsio/coolify-examples <span class='text-helper'>nhánh chính</span> sẽ được chọn<br>https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify <span class='text-helper'>nhánh nodejs-fastify</span> sẽ được chọn.<br>https://gitea.com/sedlav/expressjs.git <span class='text-helper'>nhánh chính</span> sẽ được chọn.<br>https://gitlab.com/andrasbacsai/nodejs-example.git <span class='text-helper'>nhánh chính</span> sẽ được chọn."
|
||||
}
|
BIN
other/logos/advin.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
other/logos/blacksmith.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
other/logos/codext.jpg
Normal file
After Width: | Height: | Size: 103 KiB |
BIN
other/logos/fractal.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
40
other/logos/fractal.svg
Normal file
@ -0,0 +1,40 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_2" data-name="Layer 2" viewBox="0 0 503.18 149.2">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
letter-spacing: -.03em;
|
||||
}
|
||||
|
||||
.cls-1, .cls-2 {
|
||||
fill: #fff;
|
||||
font-family: Aileron-Black, Aileron;
|
||||
font-size: 67.77px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
letter-spacing: -.03em;
|
||||
}
|
||||
|
||||
.cls-3 {
|
||||
fill: #ed1c24;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<text class="cls-2"><tspan x="93.62" y="115.07">Networks</tspan></text>
|
||||
<text class="cls-1" transform="translate(94.99 61.31)"><tspan x="0" y="0">Fractal</tspan></text>
|
||||
<g id="_Group_" data-name="&lt;Group&gt;">
|
||||
<path class="cls-3" d="m15.81,91.71l-7.51,4.22c-3.1,1.73-5.04,4.99-5.08,8.53l-.12,8.6c-.05,3.82,4.05,6.27,7.39,4.42l7.51-4.22c3.1-1.73,5.04-4.99,5.08-8.53l.12-8.6c.05-3.82-4.05-6.27-7.39-4.42Z"/>
|
||||
<path class="cls-3" d="m36.24,49.91l-27.53,15.41c-3.1,1.73-5.04,4.99-5.08,8.53l-.12,8.6c-.05,3.82,4.05,6.27,7.39,4.42l27.53-15.41c3.1-1.73,5.04-4.99,5.08-8.53l.12-8.6c.05-3.82-4.05-6.27-7.39-4.42Z"/>
|
||||
<path class="cls-3" d="m56.68,8.1L9.12,34.73c-3.1,1.73-5.04,4.99-5.08,8.53l-.12,8.6c-.05,3.82,4.05,6.27,7.39,4.42l47.56-26.63c3.1-1.73,5.04-4.99,5.08-8.53l.12-8.6c.05-3.82-4.05-6.27-7.39-4.42Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<style xmlns="" class="darkreader darkreader--fallback">html, body, body :not(iframe) {
|
||||
background-color: #181a1b !important;
|
||||
border-color: #776e62 !important;
|
||||
color: #e8e6e3 !important;
|
||||
}
|
||||
div[style*="background-color: rgb(135, 135, 135)"] {
|
||||
background-color: #878787 !important;
|
||||
}</style></svg>
|
After Width: | Height: | Size: 1.6 KiB |
BIN
other/logos/supaguide.png
Normal file
After Width: | Height: | Size: 16 KiB |
16
other/logos/tigris.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<svg width="59" height="24" viewBox="0 0 59 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1046_262)">
|
||||
<path d="M27.1504 1.51153V0.285861H30.2514V12.3573C30.2671 13.4155 30.036 14.4637 29.575 15.4181C29.1341 16.3145 28.4319 17.0568 27.5601 17.5491C26.5655 18.0894 25.4436 18.3562 24.3106 18.3216C22.7757 18.3941 21.2609 17.951 20.0096 17.0624C18.9212 16.2185 18.2113 14.9806 18.0349 13.6176H21.0868C21.2442 14.2081 21.6103 14.7227 22.1194 15.0643C22.7076 15.4371 23.3975 15.6191 24.0941 15.5856C24.4993 15.6034 24.9034 15.5409 25.284 15.4025C25.6647 15.2641 26.0141 15.052 26.3121 14.7785C26.6157 14.4492 26.8479 14.0596 26.993 13.6366C27.1381 13.2135 27.194 12.7636 27.156 12.3182V9.92158C26.8546 10.7287 26.3032 11.4174 25.5809 11.8896C24.7884 12.3964 23.8608 12.6542 22.9197 12.6274C21.9385 12.6408 20.9718 12.3819 20.1301 11.8795C19.2829 11.3571 18.6087 10.5992 18.1889 9.69944C17.7268 8.64345 17.4879 7.50484 17.4879 6.35284C17.4879 5.20084 17.7268 4.06223 18.1889 3.00623C18.6075 2.10763 19.2829 1.35079 20.1301 0.830606C20.9718 0.326047 21.9385 0.0670711 22.9197 0.082699C23.9523 0.131815 26.3266 0.131814 27.1515 1.51042L27.1504 1.51153ZM21.5411 3.79991C20.9651 4.54558 20.6526 5.45981 20.6526 6.40084C20.6526 7.34186 20.9651 8.2561 21.5411 9.00177C22.1707 9.60791 23.0135 9.94502 23.8898 9.94056C24.7649 9.93609 25.6044 9.59005 26.2273 8.97721C26.8245 8.24828 27.1515 7.33516 27.1515 6.39414C27.1515 5.45312 26.8256 4.54 26.2273 3.81107C25.9281 3.49628 25.5653 3.24735 25.1623 3.08214C24.7605 2.91693 24.3262 2.83767 23.892 2.85107C23.4544 2.83209 23.0191 2.90688 22.6128 3.07098C22.2075 3.23507 21.8414 3.484 21.5411 3.80102V3.79991Z" fill="white"/>
|
||||
<path d="M24.6813 23.9318C23.9389 23.9318 23.1988 23.8793 22.4643 23.7744C17.2692 23.022 13.2104 19.8038 12.1187 15.5765C11.8619 14.1342 11.7681 12.6686 11.8374 11.2062V5.40381H14.9484V11.2308C14.8948 12.4252 14.9562 13.6207 15.1315 14.8029C15.9062 17.8191 18.9637 20.1376 22.9086 20.708C25.936 21.1456 29.8217 20.5059 31.7183 17.4161L32.2117 16.4561C33.7667 13.297 37.1791 13.2669 39.0065 13.2479H40.1417C40.471 13.1999 40.7914 13.1006 41.0894 12.9532C41.6442 12.6831 42.112 12.2622 42.4401 11.7409C42.7683 11.2196 42.9436 10.6168 42.9458 10.0006V5.4239H46.0569V10.0006C46.0547 11.5545 45.4853 13.0548 44.455 14.2213C43.4247 15.3878 42.0037 16.1402 40.4565 16.3389H39.0344C36.5652 16.3634 35.5337 16.7329 35.0001 17.8146L34.432 18.9465C32.5108 22.1011 28.8963 23.9318 24.679 23.9318H24.6813Z" fill="white"/>
|
||||
<path d="M35.8999 1.82169V0.227643H32.7743V11.866C32.7743 11.9117 32.7855 11.9564 32.8067 11.9966C32.8279 12.0368 32.8602 12.0702 32.8993 12.0948C32.9384 12.1194 32.9819 12.1328 33.0277 12.135C33.0735 12.1372 33.1192 12.1283 33.1594 12.1071C33.9642 11.732 34.8115 11.4529 35.6833 11.2755C35.7436 11.2632 35.7983 11.2308 35.8385 11.1828C35.8775 11.1359 35.8999 11.0756 35.901 11.0142V6.58485C35.901 4.46281 37.053 3.40123 39.3581 3.40123H40.2668V0.0702489C39.309 0.0501559 36.8052 0.227645 35.901 1.82169H35.8999Z" fill="white"/>
|
||||
<path d="M13.4515 4.07001L13.13 3.36676C12.9458 2.96936 12.6232 2.65234 12.2213 2.47597L11.5103 2.17569L12.2113 1.85532C12.612 1.67336 12.9313 1.34853 13.1054 0.944434L13.4113 0.235596L13.7328 0.934386C13.9136 1.33513 14.2396 1.65327 14.6459 1.82518L15.3525 2.12992L14.6515 2.45029C14.2507 2.63225 13.9315 2.95708 13.7573 3.36118L13.4515 4.07001Z" fill="#50FFAB"/>
|
||||
<path d="M5.92662 6.04346V2.82523H8.97853V0.19751H0.0895996V2.82523H2.75081V6.02895C2.75081 9.30635 4.14281 13.1296 8.97742 13.1296V10.5856C6.50821 10.6001 5.92551 8.70132 5.92551 6.04346H5.92662Z" fill="white"/>
|
||||
<path d="M44.5019 4.07001L44.1805 3.36676C43.9963 2.96825 43.6714 2.65122 43.2673 2.47597L42.5563 2.17569L43.2618 1.85532C43.6614 1.67113 43.9795 1.34741 44.1559 0.944434L44.4618 0.235596L44.7777 0.934386C44.9585 1.33513 45.2856 1.65327 45.6908 1.82518L46.4019 2.12992L45.7008 2.45029C45.3001 2.63225 44.9808 2.95708 44.8067 3.36118L44.5008 4.07001H44.5019Z" fill="#50FFAB"/>
|
||||
<path d="M58.038 7.20003C57.5792 6.71779 57.0054 6.36059 56.3691 6.16189C55.5609 5.92077 54.736 5.73993 53.8999 5.62049C53.1375 5.5111 52.3874 5.32468 51.6629 5.06458C51.453 4.99872 51.2688 4.86812 51.1394 4.69063C51.0099 4.51314 50.9407 4.29882 50.9418 4.08002C50.9462 3.86347 51.0121 3.65249 51.1304 3.47054C51.2487 3.28858 51.4162 3.14347 51.6127 3.05193C52.2277 2.75947 52.9075 2.62551 53.5874 2.66347C54.8588 2.64561 56.1124 2.97156 57.2119 3.60784L58.3483 1.36858C57.6841 0.993514 56.9641 0.725608 56.2151 0.576026C55.3611 0.380677 54.4882 0.282445 53.613 0.281329C52.1786 0.206538 50.7576 0.581608 49.5487 1.35407C49.0798 1.66998 48.6992 2.09863 48.4402 2.59984C48.1812 3.10105 48.0528 3.65919 48.0674 4.22291C48.0071 4.99314 48.2471 5.75668 48.7394 6.35389C49.2015 6.85621 49.7898 7.22682 50.4428 7.42663C51.2767 7.67221 52.1284 7.85305 52.9913 7.96803C53.728 8.06403 54.4536 8.22924 55.1591 8.46031C55.3578 8.51835 55.5308 8.64003 55.6525 8.80635C55.7741 8.97268 55.8367 9.17472 55.8311 9.38012C55.8311 10.3122 54.947 10.7766 53.1788 10.7732C51.5725 10.809 49.9751 10.5299 48.4781 9.95165V12.4119C49.9204 12.95 51.453 13.2056 52.9913 13.1643C54.7595 13.1643 56.157 12.8116 57.184 12.1061C57.6618 11.8036 58.0536 11.385 58.3226 10.8882C58.5916 10.3915 58.7278 9.83556 58.7189 9.27184C58.7702 8.51947 58.5258 7.77603 58.038 7.20003Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1046_262">
|
||||
<rect width="58.7163" height="24" fill="white" transform="translate(0.0494385)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 5.4 KiB |
BIN
other/logos/trieve_bg.png
Normal file
After Width: | Height: | Size: 942 KiB |
190
package-lock.json
generated
@ -19,9 +19,9 @@
|
||||
"laravel-vite-plugin": "0.8.1",
|
||||
"postcss": "8.4.38",
|
||||
"pusher-js": "8.4.0-rc2",
|
||||
"tailwindcss": "3.4.3",
|
||||
"tailwindcss": "3.4.4",
|
||||
"vite": "4.5.3",
|
||||
"vue": "3.4.27"
|
||||
"vue": "3.4.29"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
@ -36,9 +36,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.24.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz",
|
||||
"integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==",
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz",
|
||||
"integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@ -535,51 +535,51 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.27.tgz",
|
||||
"integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.29.tgz",
|
||||
"integrity": "sha512-TFKiRkKKsRCKvg/jTSSKK7mYLJEQdUiUfykbG49rubC9SfDyvT2JrzTReopWlz2MxqeLyxh9UZhvxEIBgAhtrg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.24.4",
|
||||
"@vue/shared": "3.4.27",
|
||||
"@babel/parser": "^7.24.7",
|
||||
"@vue/shared": "3.4.29",
|
||||
"entities": "^4.5.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core/node_modules/@vue/shared": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
|
||||
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
|
||||
"integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/compiler-dom": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz",
|
||||
"integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.29.tgz",
|
||||
"integrity": "sha512-A6+iZ2fKIEGnfPJejdB7b1FlJzgiD+Y/sxxKwJWg1EbJu6ZPgzaPQQ51ESGNv0CP6jm6Z7/pO6Ia8Ze6IKrX7w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.4.27",
|
||||
"@vue/shared": "3.4.27"
|
||||
"@vue/compiler-core": "3.4.29",
|
||||
"@vue/shared": "3.4.29"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-dom/node_modules/@vue/shared": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
|
||||
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
|
||||
"integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz",
|
||||
"integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.29.tgz",
|
||||
"integrity": "sha512-zygDcEtn8ZimDlrEQyLUovoWgKQic6aEQqRXce2WXBvSeHbEbcAsXyCk9oG33ZkyWH4sl9D3tkYc1idoOkdqZQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.24.4",
|
||||
"@vue/compiler-core": "3.4.27",
|
||||
"@vue/compiler-dom": "3.4.27",
|
||||
"@vue/compiler-ssr": "3.4.27",
|
||||
"@vue/shared": "3.4.27",
|
||||
"@babel/parser": "^7.24.7",
|
||||
"@vue/compiler-core": "3.4.29",
|
||||
"@vue/compiler-dom": "3.4.29",
|
||||
"@vue/compiler-ssr": "3.4.29",
|
||||
"@vue/shared": "3.4.29",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.10",
|
||||
"postcss": "^8.4.38",
|
||||
@ -587,25 +587,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
|
||||
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
|
||||
"integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz",
|
||||
"integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.29.tgz",
|
||||
"integrity": "sha512-rFbwCmxJ16tDp3N8XCx5xSQzjhidYjXllvEcqX/lopkoznlNPz3jyy0WGJCyhAaVQK677WWFt3YO/WUEkMMUFQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.4.27",
|
||||
"@vue/shared": "3.4.27"
|
||||
"@vue/compiler-dom": "3.4.29",
|
||||
"@vue/shared": "3.4.29"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
|
||||
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
|
||||
"integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
@ -617,64 +617,74 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.27.tgz",
|
||||
"integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.29.tgz",
|
||||
"integrity": "sha512-s8fmX3YVR/Rk5ig0ic0NuzTNjK2M7iLuVSZyMmCzN/+Mjuqqif1JasCtEtmtoJWF32pAtUjyuT2ljNKNLeOmnQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.4.27",
|
||||
"@vue/shared": "3.4.27"
|
||||
"@vue/reactivity": "3.4.29",
|
||||
"@vue/shared": "3.4.29"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core/node_modules/@vue/reactivity": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.27.tgz",
|
||||
"integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.29.tgz",
|
||||
"integrity": "sha512-w8+KV+mb1a8ornnGQitnMdLfE0kXmteaxLdccm2XwdFxXst4q/Z7SEboCV5SqJNpZbKFeaRBBJBhW24aJyGINg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.4.27"
|
||||
"@vue/shared": "3.4.29"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core/node_modules/@vue/shared": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
|
||||
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
|
||||
"integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/runtime-dom": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz",
|
||||
"integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.29.tgz",
|
||||
"integrity": "sha512-gI10atCrtOLf/2MPPMM+dpz3NGulo9ZZR9d1dWo4fYvm+xkfvRrw1ZmJ7mkWtiJVXSsdmPbcK1p5dZzOCKDN0g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/runtime-core": "3.4.27",
|
||||
"@vue/shared": "3.4.27",
|
||||
"@vue/reactivity": "3.4.29",
|
||||
"@vue/runtime-core": "3.4.29",
|
||||
"@vue/shared": "3.4.29",
|
||||
"csstype": "^3.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom/node_modules/@vue/reactivity": {
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.29.tgz",
|
||||
"integrity": "sha512-w8+KV+mb1a8ornnGQitnMdLfE0kXmteaxLdccm2XwdFxXst4q/Z7SEboCV5SqJNpZbKFeaRBBJBhW24aJyGINg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.4.29"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
|
||||
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
|
||||
"integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/server-renderer": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.27.tgz",
|
||||
"integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.29.tgz",
|
||||
"integrity": "sha512-HMLCmPI2j/k8PVkSBysrA2RxcxC5DgBiCdj7n7H2QtR8bQQPqKAe8qoaxLcInzouBmzwJ+J0x20ygN/B5mYBng==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.4.27",
|
||||
"@vue/shared": "3.4.27"
|
||||
"@vue/compiler-ssr": "3.4.29",
|
||||
"@vue/shared": "3.4.29"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "3.4.27"
|
||||
"vue": "3.4.29"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
|
||||
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
|
||||
"integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
@ -789,11 +799,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@ -1094,9 +1104,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@ -1897,9 +1907,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
|
||||
"integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==",
|
||||
"version": "3.4.4",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz",
|
||||
"integrity": "sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==",
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"arg": "^5.0.2",
|
||||
@ -2082,16 +2092,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz",
|
||||
"integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.29.tgz",
|
||||
"integrity": "sha512-8QUYfRcYzNlYuzKPfge1UWC6nF9ym0lx7mpGVPJYNhddxEf3DD0+kU07NTL0sXuiT2HuJuKr/iEO8WvXvT0RSQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.4.27",
|
||||
"@vue/compiler-sfc": "3.4.27",
|
||||
"@vue/runtime-dom": "3.4.27",
|
||||
"@vue/server-renderer": "3.4.27",
|
||||
"@vue/shared": "3.4.27"
|
||||
"@vue/compiler-dom": "3.4.29",
|
||||
"@vue/compiler-sfc": "3.4.29",
|
||||
"@vue/runtime-dom": "3.4.29",
|
||||
"@vue/server-renderer": "3.4.29",
|
||||
"@vue/shared": "3.4.29"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "*"
|
||||
@ -2103,9 +2113,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vue/node_modules/@vue/shared": {
|
||||
"version": "3.4.27",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
|
||||
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
|
||||
"version": "3.4.29",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz",
|
||||
"integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
|
@ -13,9 +13,9 @@
|
||||
"laravel-vite-plugin": "0.8.1",
|
||||
"postcss": "8.4.38",
|
||||
"pusher-js": "8.4.0-rc2",
|
||||
"tailwindcss": "3.4.3",
|
||||
"tailwindcss": "3.4.4",
|
||||
"vite": "4.5.3",
|
||||
"vue": "3.4.27"
|
||||
"vue": "3.4.29"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/forms": "0.5.7",
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
html,
|
||||
body {
|
||||
zoom: 0.95;
|
||||
@apply h-full bg-neutral-50 text-neutral-800 dark:bg-base dark:text-neutral-400;
|
||||
}
|
||||
|
||||
@ -12,6 +11,18 @@ body {
|
||||
@apply text-sm antialiased scrollbar;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip {
|
||||
@apply dark:text-white dark:border-coolgray-300 dark:bg-coolgray-200 shadow-none !important;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip-title {
|
||||
@apply hidden !important;
|
||||
}
|
||||
|
||||
.apexcharts-xaxistooltip {
|
||||
@apply hidden !important;
|
||||
}
|
||||
|
||||
.input,
|
||||
.select {
|
||||
@apply text-black dark:bg-coolgray-100 dark:text-white ring-neutral-200 dark:ring-coolgray-300;
|
||||
@ -116,15 +127,19 @@ .alert-success {
|
||||
.alert-error {
|
||||
@apply flex items-center gap-2 text-error;
|
||||
}
|
||||
|
||||
.tag {
|
||||
@apply px-2 py-1 cursor-pointer box-description dark:bg-coolgray-100 dark:hover:bg-coolgray-300 bg-neutral-100 hover:bg-neutral-200;
|
||||
}
|
||||
|
||||
.add-tag {
|
||||
@apply flex items-center px-2 text-xs cursor-pointer dark:text-neutral-500/20 text-neutral-500 group-hover:text-neutral-700 group-hover:dark:text-white dark:hover:bg-coolgray-300 hover:bg-neutral-200;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
@apply relative flex cursor-pointer select-none dark:text-white hover:bg-neutral-100 dark:hover:bg-coollabs items-center pr-4 pl-2 py-1 text-xs justify-start outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 gap-2 w-full;
|
||||
}
|
||||
|
||||
.dropdown-item-no-padding {
|
||||
@apply relative flex cursor-pointer select-none dark:text-white hover:bg-neutral-100 dark:hover:bg-coollabs items-center py-1 text-xs justify-start outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 gap-2 w-full;
|
||||
}
|
||||
@ -200,12 +215,15 @@ .kbd-custom {
|
||||
.box {
|
||||
@apply relative flex lg:flex-row flex-col p-2 transition-colors cursor-pointer min-h-[4rem] dark:bg-coolgray-100 bg-white border text-black dark:text-white hover:text-black border-neutral-200 dark:border-black hover:bg-neutral-100 dark:hover:bg-coollabs-100 dark:hover:text-white hover:no-underline;
|
||||
}
|
||||
|
||||
.box-boarding {
|
||||
@apply flex lg:flex-row flex-col p-2 transition-colors cursor-pointer min-h-[4rem] dark:bg-coolgray-100 dark:text-white bg-neutral-50 border border-neutral-200 dark:border-black hover:bg-neutral-100 dark:hover:bg-coollabs-100 dark:hover:text-white hover:text-black hover:no-underline text-black;
|
||||
}
|
||||
|
||||
.box-without-bg {
|
||||
@apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem] border border-neutral-200 dark:border-black;
|
||||
}
|
||||
|
||||
.box-without-bg-without-border {
|
||||
@apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem];
|
||||
}
|
||||
|
437
resources/views/components/forms/monaco-editor.blade.php
Normal file
@ -0,0 +1,437 @@
|
||||
<div wire:key="{{ rand() }}" class="coolify-monaco-editor flex-1">
|
||||
<div x-ref="monacoRef" x-data="{
|
||||
monacoContent: @entangle($id),
|
||||
monacoLanguage: '',
|
||||
monacoPlaceholder: true,
|
||||
monacoPlaceholderText: 'Start typing here',
|
||||
monacoLoader: true,
|
||||
monacoFontSize: '15px',
|
||||
monacoId: $id('monaco-editor'),
|
||||
monacoEditor(editor) {
|
||||
editor.onDidChangeModelContent((e) => {
|
||||
this.monacoContent = editor.getValue();
|
||||
this.updatePlaceholder(editor.getValue());
|
||||
});
|
||||
editor.onDidBlurEditorWidget(() => {
|
||||
this.updatePlaceholder(editor.getValue());
|
||||
});
|
||||
editor.onDidFocusEditorWidget(() => {
|
||||
this.updatePlaceholder(editor.getValue());
|
||||
});
|
||||
},
|
||||
updatePlaceholder(value) {
|
||||
this.monacoPlaceholder = value === '';
|
||||
},
|
||||
monacoEditorFocus() {
|
||||
document.getElementById(this.monacoId).dispatchEvent(new CustomEvent('monaco-editor-focused', { detail: { monacoId: this.monacoId } }));
|
||||
},
|
||||
monacoEditorAddLoaderScriptToHead() {
|
||||
let script = document.createElement('script');
|
||||
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.39.0/min/vs/loader.min.js';
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
}" x-modelable="monacoContent">
|
||||
<div x-cloak x-init="if (typeof _amdLoaderGlobal == 'undefined') {
|
||||
monacoEditorAddLoaderScriptToHead();
|
||||
}
|
||||
checkTheme();
|
||||
let monacoLoaderInterval = setInterval(() => {
|
||||
if (typeof _amdLoaderGlobal !== 'undefined') {
|
||||
require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.39.0/min/vs' } });
|
||||
let proxy = URL.createObjectURL(new Blob([` self.MonacoEnvironment = { baseUrl: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.39.0/min' }; importScripts('https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.39.0/min/vs/base/worker/workerMain.min.js');`], { type: 'text/javascript' }));
|
||||
window.MonacoEnvironment = { getWorkerUrl: () => proxy };
|
||||
require(['vs/editor/editor.main'], () => {
|
||||
monaco.editor.defineTheme('blackboard', {
|
||||
'base': 'vs-dark',
|
||||
'inherit': true,
|
||||
'rules': [{
|
||||
'background': editorBackground,
|
||||
'token': ''
|
||||
},
|
||||
{
|
||||
'foreground': '959da5',
|
||||
'token': 'comment'
|
||||
},
|
||||
{
|
||||
'foreground': '959da5',
|
||||
'token': 'punctuation.definition.comment'
|
||||
},
|
||||
{
|
||||
'foreground': '959da5',
|
||||
'token': 'string.comment'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'constant'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'entity.name.constant'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'variable.other.constant'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'variable.language'
|
||||
},
|
||||
{
|
||||
'foreground': 'b392f0',
|
||||
'token': 'entity'
|
||||
},
|
||||
{
|
||||
'foreground': 'b392f0',
|
||||
'token': 'entity.name'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'variable.parameter.function'
|
||||
},
|
||||
{
|
||||
'foreground': '7bcc72',
|
||||
'token': 'entity.name.tag'
|
||||
},
|
||||
{
|
||||
'foreground': 'ea4a5a',
|
||||
'token': 'keyword'
|
||||
},
|
||||
{
|
||||
'foreground': 'ea4a5a',
|
||||
'token': 'storage'
|
||||
},
|
||||
{
|
||||
'foreground': 'ea4a5a',
|
||||
'token': 'storage.type'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'storage.modifier.package'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'storage.modifier.import'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'storage.type.java'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'punctuation.definition.string'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string punctuation.section.embedded source'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'support'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'meta.property-name'
|
||||
},
|
||||
{
|
||||
'foreground': 'fb8532',
|
||||
'token': 'variable'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'variable.other'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'fontStyle': 'bold italic underline',
|
||||
'token': 'invalid.broken'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'fontStyle': 'bold italic underline',
|
||||
'token': 'invalid.deprecated'
|
||||
},
|
||||
{
|
||||
'foreground': 'fafbfc',
|
||||
'background': 'd73a49',
|
||||
'fontStyle': 'italic underline',
|
||||
'token': 'invalid.illegal'
|
||||
},
|
||||
{
|
||||
'foreground': 'fafbfc',
|
||||
'background': 'd73a49',
|
||||
'fontStyle': 'italic underline',
|
||||
'token': 'carriage-return'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'fontStyle': 'bold italic underline',
|
||||
'token': 'invalid.unimplemented'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'token': 'message.error'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'string source'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'string variable'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'source.regexp'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string.regexp'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string.regexp.character-class'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string.regexp constant.character.escape'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string.regexp source.ruby.embedded'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string.regexp string.regexp.arbitrary-repitition'
|
||||
},
|
||||
{
|
||||
'foreground': '7bcc72',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'string.regexp constant.character.escape'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'support.constant'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'support.variable'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'meta.module-reference'
|
||||
},
|
||||
{
|
||||
'foreground': 'fb8532',
|
||||
'token': 'markup.list'
|
||||
},
|
||||
{
|
||||
'foreground': '0366d6',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'markup.heading'
|
||||
},
|
||||
{
|
||||
'foreground': '0366d6',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'markup.heading entity.name'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'markup.quote'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'fontStyle': 'italic',
|
||||
'token': 'markup.italic'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'markup.bold'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'markup.raw'
|
||||
},
|
||||
{
|
||||
'foreground': 'b31d28',
|
||||
'background': 'ffeef0',
|
||||
'token': 'markup.deleted'
|
||||
},
|
||||
{
|
||||
'foreground': 'b31d28',
|
||||
'background': 'ffeef0',
|
||||
'token': 'meta.diff.header.from-file'
|
||||
},
|
||||
{
|
||||
'foreground': 'b31d28',
|
||||
'background': 'ffeef0',
|
||||
'token': 'punctuation.definition.deleted'
|
||||
},
|
||||
{
|
||||
'foreground': '176f2c',
|
||||
'background': 'f0fff4',
|
||||
'token': 'markup.inserted'
|
||||
},
|
||||
{
|
||||
'foreground': '176f2c',
|
||||
'background': 'f0fff4',
|
||||
'token': 'meta.diff.header.to-file'
|
||||
},
|
||||
{
|
||||
'foreground': '176f2c',
|
||||
'background': 'f0fff4',
|
||||
'token': 'punctuation.definition.inserted'
|
||||
},
|
||||
{
|
||||
'foreground': 'b08800',
|
||||
'background': 'fffdef',
|
||||
'token': 'markup.changed'
|
||||
},
|
||||
{
|
||||
'foreground': 'b08800',
|
||||
'background': 'fffdef',
|
||||
'token': 'punctuation.definition.changed'
|
||||
},
|
||||
{
|
||||
'foreground': '2f363d',
|
||||
'background': '959da5',
|
||||
'token': 'markup.ignored'
|
||||
},
|
||||
{
|
||||
'foreground': '2f363d',
|
||||
'background': '959da5',
|
||||
'token': 'markup.untracked'
|
||||
},
|
||||
{
|
||||
'foreground': 'b392f0',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'meta.diff.range'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'meta.diff.header'
|
||||
},
|
||||
{
|
||||
'foreground': '0366d6',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'meta.separator'
|
||||
},
|
||||
{
|
||||
'foreground': '0366d6',
|
||||
'token': 'meta.output'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.tag'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.curly'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.round'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.square'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.angle'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.quote'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'token': 'brackethighlighter.unmatched'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'token': 'sublimelinter.mark.error'
|
||||
},
|
||||
{
|
||||
'foreground': 'fb8532',
|
||||
'token': 'sublimelinter.mark.warning'
|
||||
},
|
||||
{
|
||||
'foreground': '6a737d',
|
||||
'token': 'sublimelinter.gutter-mark'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'fontStyle': 'underline',
|
||||
'token': 'constant.other.reference.link'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'fontStyle': 'underline',
|
||||
'token': 'string.other.link'
|
||||
}
|
||||
],
|
||||
'colors': {
|
||||
'editor.foreground': '#f6f8fa',
|
||||
'editor.background': editorBackground,
|
||||
'editor.selectionBackground': '#4c2889',
|
||||
'editor.inactiveSelectionBackground': '#444d56',
|
||||
'editor.lineHighlightBackground': '#444d56',
|
||||
'editorCursor.foreground': '#ffffff',
|
||||
'editorWhitespace.foreground': '#6a737d',
|
||||
'editorIndentGuide.background': '#6a737d',
|
||||
'editorIndentGuide.activeBackground': '#f6f8fa',
|
||||
'editor.selectionHighlightBorder': '#444d56'
|
||||
}
|
||||
});
|
||||
|
||||
const editor = monaco.editor.create($refs.monacoEditorElement, {
|
||||
value: monacoContent,
|
||||
theme: editorTheme,
|
||||
wordWrap: 'on',
|
||||
readOnly: '{{ $readonly ?? false }}',
|
||||
minimap: { enabled: false },
|
||||
fontSize: monacoFontSize,
|
||||
lineNumbersMinChars: 3,
|
||||
automaticLayout: true,
|
||||
language: '{{ $language }}'
|
||||
|
||||
});
|
||||
|
||||
monacoEditor(editor);
|
||||
|
||||
document.getElementById(monacoId).editor = editor;
|
||||
document.getElementById(monacoId).addEventListener('monaco-editor-focused', (event) => {
|
||||
editor.focus();
|
||||
});
|
||||
|
||||
updatePlaceholder(editor.getValue());
|
||||
|
||||
// Watch for changes in monacoContent from Livewire
|
||||
$watch('monacoContent', value => {
|
||||
if (editor.getValue() !== value) {
|
||||
editor.setValue(value);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
clearInterval(monacoLoaderInterval);
|
||||
monacoLoader = false;
|
||||
|
||||
}
|
||||
}, 5);" :id="monacoId">
|
||||
</div>
|
||||
<div class="relative z-10 w-full h-full">
|
||||
<div x-ref="monacoEditorElement" class="w-full h-96 text-md"></div>
|
||||
<div x-ref="monacoPlaceholderElement" x-show="monacoPlaceholder" @click="monacoEditorFocus()"
|
||||
:style="'font-size: ' + monacoFontSize"
|
||||
class="w-full text-sm font-mono absolute z-50 text-gray-500 ml-14 -translate-x-0.5 mt-0.5 left-0 top-0"
|
||||
x-text="monacoPlaceholderText"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -24,6 +24,11 @@ function handleKeydown(e) {
|
||||
@endif
|
||||
</label>
|
||||
@endif
|
||||
@if ($useMonacoEditor)
|
||||
<x-forms.monaco-editor id="{{ $id }}" language="{{ $monacoEditorLanguage }}" name="{{ $name }}"
|
||||
name="{{ $id }}" model="{{ $value ?? $id }}" wire:model="{{ $value ?? $id }}"
|
||||
readonly="{{ $readonly }}" label="dockerfile" />
|
||||
@else
|
||||
@if ($type === 'password')
|
||||
<div class="relative" x-data="{ type: 'password' }">
|
||||
@if ($allowToPeak)
|
||||
@ -33,7 +38,8 @@ class="absolute inset-y-0 right-0 flex items-center h-6 pt-2 pr-2 cursor-pointer
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M10 12a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
|
||||
<path d="M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6" />
|
||||
<path
|
||||
d="M21 12c-2.4 4 -5.4 6 -9 6c-3.6 0 -6.6 -2 -9 -6c2.4 -4 5.4 -6 9 -6c3.6 0 6.6 2 9 6" />
|
||||
</svg>
|
||||
</div>
|
||||
@endif
|
||||
@ -56,7 +62,8 @@ class="absolute inset-y-0 right-0 flex items-center h-6 pt-2 pr-2 cursor-pointer
|
||||
|
||||
</div>
|
||||
@else
|
||||
<textarea {{ $allowTab ? '@keydown.tab=handleKeydown' : '' }} placeholder="{{ $placeholder }}" {{ !$spellcheck ? 'spellcheck=false' : '' }} {{ $attributes->merge(['class' => $defaultClass]) }}
|
||||
<textarea {{ $allowTab ? '@keydown.tab=handleKeydown' : '' }} placeholder="{{ $placeholder }}"
|
||||
{{ !$spellcheck ? 'spellcheck=false' : '' }} {{ $attributes->merge(['class' => $defaultClass]) }}
|
||||
@if ($realtimeValidation) wire:model.debounce.200ms="{{ $id }}"
|
||||
@else
|
||||
wire:model={{ $value ?? $id }}
|
||||
@ -64,7 +71,7 @@ class="absolute inset-y-0 right-0 flex items-center h-6 pt-2 pr-2 cursor-pointer
|
||||
@disabled($disabled) @readonly($readonly) @required($required) id="{{ $id }}"
|
||||
name="{{ $name }}" name={{ $id }}></textarea>
|
||||
@endif
|
||||
|
||||
@endif
|
||||
@error($id)
|
||||
<label class="label">
|
||||
<span class="text-red-500 label-text-alt">{{ $message }}</span>
|
||||
|
@ -15,7 +15,21 @@
|
||||
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/800.woff2" as="style" />
|
||||
<link href="https://api.fonts.coollabs.io/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<meta name="robots" content="noindex">
|
||||
<title>{{ $title ?? 'Coolify' }}</title>
|
||||
@use('App\Models\InstanceSettings')
|
||||
@php
|
||||
|
||||
$instanceSettings = InstanceSettings::first();
|
||||
$name = null;
|
||||
|
||||
if($instanceSettings) {
|
||||
$displayName = $instanceSettings->getTitleDisplayName();
|
||||
|
||||
if(strlen($displayName) > 0) {
|
||||
$name = $displayName . ' ';
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
<title>{{ $name }}{{ $title ?? 'Coolify' }}</title>
|
||||
@env('local')
|
||||
<link rel="icon" href="{{ asset('favicon-dev.png') }}" type="image/x-icon" />
|
||||
@else
|
||||
@ -38,6 +52,7 @@
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pusher/8.3.0/pusher.min.js"
|
||||
integrity="sha512-tXL5mrkSoP49uQf2jO0LbvzMyFgki//znmq0wYXGq94gVF6TU0QlrSbwGuPpKTeN1mIjReeqKZ4/NJPjHN1d2Q=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
|
||||
@endauth
|
||||
</head>
|
||||
@section('body')
|
||||
@ -59,6 +74,26 @@
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
}
|
||||
let theme = localStorage.theme
|
||||
let baseColor = '#FCD452'
|
||||
let textColor = '#ffffff'
|
||||
let editorBackground = '#181818'
|
||||
let editorTheme = 'blackboard'
|
||||
|
||||
function checkTheme() {
|
||||
theme = localStorage.theme
|
||||
if (theme == 'dark') {
|
||||
baseColor = '#FCD452'
|
||||
textColor = '#ffffff'
|
||||
editorBackground = '#181818'
|
||||
editorTheme = 'blackboard'
|
||||
} else {
|
||||
baseColor = 'black'
|
||||
textColor = '#000000'
|
||||
editorBackground = '#ffffff'
|
||||
editorTheme = null
|
||||
}
|
||||
}
|
||||
@auth
|
||||
window.Pusher = Pusher;
|
||||
window.Echo = new Echo({
|
||||
|
@ -23,9 +23,7 @@
|
||||
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
|
||||
@foreach ($projects as $project)
|
||||
<div class="gap-2 border border-transparent cursor-pointer box group"
|
||||
@if (data_get($project, 'environments')->count() === 1) onclick="gotoProject('{{ data_get($project, 'uuid') }}', '{{ data_get($project, 'environments.0.name', 'production') }}')"
|
||||
@else
|
||||
onclick="window.location.href = '{{ route('project.show', ['project_uuid' => data_get($project, 'uuid')]) }}'" @endif>
|
||||
onclick="window.location.href = '{{ route('project.resource.index', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => $project->default_environment()->name]) }}'">
|
||||
<div class="flex flex-1 mx-6">
|
||||
<div class="flex flex-col justify-center flex-1">
|
||||
<div class="box-title">{{ $project->name }}</div>
|
||||
@ -161,7 +159,6 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
<script>
|
||||
function gotoProject(uuid, environment = 'production') {
|
||||
window.location.href = '/project/' + uuid + '/' + environment;
|
||||
|
@ -40,7 +40,7 @@
|
||||
<x-forms.button type="submit">Validate 2FA</x-forms.button>
|
||||
</form>
|
||||
<div>
|
||||
<div>{!! request()->user()->twoFactorQrCodeSvg() !!}</div>
|
||||
<div class="flex items-center justify-center w-64 h-64 bg-transparent">{!! request()->user()->twoFactorQrCodeSvg() !!}</div>
|
||||
<div x-data="{ showCode: false }" class="py-2">
|
||||
<template x-if="showCode">
|
||||
<div class="py-2 ">{!! decrypt(request()->user()->two_factor_secret) !!}</div>
|
||||
|
@ -74,6 +74,9 @@
|
||||
@click.prevent="activeTab = 'resource-operations'; window.location.hash = 'resource-operations'"
|
||||
href="#">Resource Operations
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'metrics' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'metrics'; window.location.hash = 'metrics'" href="#">Metrics
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'tags' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'tags'; window.location.hash = 'tags'" href="#">Tags
|
||||
</a>
|
||||
@ -126,6 +129,9 @@
|
||||
<div x-cloak x-show="activeTab === 'resource-operations'">
|
||||
<livewire:project.shared.resource-operations :resource="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'metrics'">
|
||||
<livewire:project.shared.metrics :resource="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'tags'">
|
||||
<livewire:project.shared.tags :resource="$application" />
|
||||
</div>
|
||||
|
@ -46,7 +46,7 @@ class="w-6 h-6" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
{{ $deployment->status }}
|
||||
</div>
|
||||
@if (data_get($deployment, 'is_webhook') || data_get($deployment, 'pull_request_id'))
|
||||
<div class="flex gap-1">
|
||||
<div class="flex items-center gap-1">
|
||||
@if (data_get($deployment, 'is_webhook'))
|
||||
Webhook
|
||||
@endif
|
||||
@ -55,21 +55,32 @@ class="w-6 h-6" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
|
|
||||
@endif
|
||||
Pull Request #{{ data_get($deployment, 'pull_request_id') }}
|
||||
(SHA
|
||||
@if (data_get($deployment, 'commit'))
|
||||
{{ data_get($deployment, 'commit') }})
|
||||
@else
|
||||
HEAD)
|
||||
@endif
|
||||
@if (data_get($deployment, 'commit'))
|
||||
<div class="dark:hover:text-white"
|
||||
x-on:click.stop="goto('{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}')">
|
||||
<div class="text-xs underline">
|
||||
@if ($deployment->commitMessage())
|
||||
({{ data_get_str($deployment, 'commit')->limit(7) }} -
|
||||
{{ $deployment->commitMessage() }})
|
||||
@else
|
||||
{{ data_get_str($deployment, 'commit')->limit(7) }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@else
|
||||
<div class="flex items-center gap-1">
|
||||
@if (data_get($deployment, 'rollback') === true)
|
||||
Rollback
|
||||
@else
|
||||
@if (data_get($deployment, 'is_api'))
|
||||
API
|
||||
@else
|
||||
Manual
|
||||
@endif
|
||||
@endif
|
||||
@if (data_get($deployment, 'commit'))
|
||||
<div class="dark:hover:text-white"
|
||||
x-on:click.stop="goto('{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}')">
|
||||
|
@ -89,7 +89,7 @@ class="fixed top-4 right-16" x-on:click="toggleScroll"><svg class="icon" viewBox
|
||||
@if (decode_remote_command_output($application_deployment_queue)->count() > 0)
|
||||
@foreach (decode_remote_command_output($application_deployment_queue) as $line)
|
||||
<span @class([
|
||||
'dark:text-warning whitespace-pre-line' => $line['hidden'],
|
||||
'text-coollabs dark:text-warning whitespace-pre-line' => $line['hidden'],
|
||||
'text-red-500 font-bold whitespace-pre-line' => $line['type'] == 'stderr',
|
||||
])>[{{ $line['timestamp'] }}] @if ($line['hidden'])
|
||||
<br><br>[COMMAND] {{ $line['command'] }}<br>[OUTPUT]
|
||||
|
@ -73,7 +73,7 @@
|
||||
<option value="www">Redirect to www.</option>
|
||||
<option value="non-www">Redirect to non-www.</option>
|
||||
</x-forms.select>
|
||||
<x-modal-confirmation action="set_redirect" >
|
||||
<x-modal-confirmation action="set_redirect">
|
||||
<x-slot:customButton>
|
||||
<div class="w-[7.2rem]">Set Direction</div>
|
||||
</x-slot:customButton>
|
||||
@ -231,10 +231,12 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
|
||||
@if ($application->settings->is_raw_compose_deployment_enabled)
|
||||
<x-forms.textarea rows="10" readonly id="application.docker_compose_raw"
|
||||
label="Docker Compose Content (applicationId: {{ $application->id }})"
|
||||
helper="You need to modify the docker compose file." />
|
||||
helper="You need to modify the docker compose file." monacoEditorLanguage="yaml"
|
||||
useMonacoEditor />
|
||||
@else
|
||||
<x-forms.textarea rows="10" readonly id="application.docker_compose"
|
||||
label="Docker Compose Content" helper="You need to modify the docker compose file." />
|
||||
label="Docker Compose Content" helper="You need to modify the docker compose file."
|
||||
monacoEditorLanguage="yaml" useMonacoEditor />
|
||||
@endif
|
||||
<div class="w-72">
|
||||
<x-forms.checkbox label="Escape special characters in labels?"
|
||||
@ -246,7 +248,8 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
|
||||
@endif
|
||||
|
||||
@if ($application->dockerfile)
|
||||
<x-forms.textarea label="Dockerfile" id="application.dockerfile" rows="6"> </x-forms.textarea>
|
||||
<x-forms.textarea label="Dockerfile" id="application.dockerfile" monacoEditorLanguage="dockerfile"
|
||||
useMonacoEditor rows="6"> </x-forms.textarea>
|
||||
@endif
|
||||
@if ($application->build_pack !== 'dockercompose')
|
||||
<h3 class="pt-8">Network</h3>
|
||||
@ -263,7 +266,9 @@ class="underline" href="https://coolify.io/docs/knowledge-base/docker/registry"
|
||||
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><br><span class='inline-block font-bold dark:text-warning'>Example:</span><br>3000:3000,3002:3002<br><br>Rolling update is not supported if you have a port mapped to the host." />
|
||||
@endif
|
||||
</div>
|
||||
<x-forms.textarea label="Container Labels" rows="15" id="customLabels"></x-forms.textarea>
|
||||
|
||||
<x-forms.textarea label="Container Labels" rows="15" id="customLabels"
|
||||
monacoEditorLanguage="ini" useMonacoEditor></x-forms.textarea>
|
||||
<div class="w-72">
|
||||
<x-forms.checkbox label="Escape special characters in labels?"
|
||||
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
|
||||
|