diff --git a/.github/workflows/coolify-realtime-next.yml b/.github/workflows/coolify-realtime-next.yml
new file mode 100644
index 000000000..33e048627
--- /dev/null
+++ b/.github/workflows/coolify-realtime-next.yml
@@ -0,0 +1,103 @@
+name: Coolify Realtime Development (v4)
+
+on:
+ push:
+ branches: [ "next" ]
+ paths:
+ - .github/workflows/coolify-realtime.yml
+ - docker/coolify-realtime/Dockerfile
+ - docker/coolify-realtime/terminal-server.js
+ - docker/coolify-realtime/package.json
+ - docker/coolify-realtime/soketi-entrypoint.sh
+
+env:
+ REGISTRY: ghcr.io
+ IMAGE_NAME: "coollabsio/coolify-realtime"
+
+jobs:
+ amd64:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+ steps:
+ - uses: actions/checkout@v4
+ - name: Login to ghcr.io
+ uses: docker/login-action@v3
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Get Version
+ id: version
+ run: |
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ - name: Build image and push to registry
+ uses: docker/build-push-action@v5
+ with:
+ no-cache: true
+ context: .
+ file: docker/coolify-realtime/Dockerfile
+ platforms: linux/amd64
+ push: true
+ tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
+ labels: |
+ coolify.managed=true
+ aarch64:
+ runs-on: [ self-hosted, arm64 ]
+ permissions:
+ contents: read
+ packages: write
+ steps:
+ - uses: actions/checkout@v4
+ - name: Login to ghcr.io
+ uses: docker/login-action@v3
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Get Version
+ id: version
+ run: |
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ - name: Build image and push to registry
+ uses: docker/build-push-action@v5
+ with:
+ no-cache: true
+ context: .
+ file: docker/coolify-realtime/Dockerfile
+ platforms: linux/aarch64
+ push: true
+ tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
+ labels: |
+ coolify.managed=true
+ merge-manifest:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+ needs: [ amd64, aarch64 ]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+ - name: Login to ghcr.io
+ uses: docker/login-action@v3
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Get Version
+ id: version
+ run: |
+ echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
+ - name: Create & publish manifest
+ run: |
+ docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
+ - uses: sarisia/actions-status-discord@v1
+ if: always()
+ with:
+ webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL }}
diff --git a/.github/workflows/coolify-realtime.yml b/.github/workflows/coolify-realtime.yml
index 75e3f1681..30910ae0b 100644
--- a/.github/workflows/coolify-realtime.yml
+++ b/.github/workflows/coolify-realtime.yml
@@ -2,7 +2,7 @@ name: Coolify Realtime (v4)
on:
push:
- branches: [ "main", "next" ]
+ branches: [ "main" ]
paths:
- .github/workflows/coolify-realtime.yml
- docker/coolify-realtime/Dockerfile
diff --git a/app/Actions/Database/StartDragonfly.php b/app/Actions/Database/StartDragonfly.php
index 352c6a59f..3ee46a2e1 100644
--- a/app/Actions/Database/StartDragonfly.php
+++ b/app/Actions/Database/StartDragonfly.php
@@ -46,9 +46,6 @@ class StartDragonfly
'networks' => [
$this->database->destination->network,
],
- 'ulimits' => [
- 'memlock' => '-1',
- ],
'labels' => [
'coolify.managed' => 'true',
],
diff --git a/app/Actions/Fortify/CreateNewUser.php b/app/Actions/Fortify/CreateNewUser.php
index f8882d12a..481757162 100644
--- a/app/Actions/Fortify/CreateNewUser.php
+++ b/app/Actions/Fortify/CreateNewUser.php
@@ -2,7 +2,6 @@
namespace App\Actions\Fortify;
-use App\Models\InstanceSettings;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
@@ -20,7 +19,7 @@ class CreateNewUser implements CreatesNewUsers
*/
public function create(array $input): User
{
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
if (! $settings->is_registration_enabled) {
abort(403);
}
@@ -48,7 +47,7 @@ class CreateNewUser implements CreatesNewUsers
$team = $user->teams()->first();
// Disable registration after first user is created
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$settings->is_registration_enabled = false;
$settings->save();
} else {
diff --git a/app/Actions/License/CheckResaleLicense.php b/app/Actions/License/CheckResaleLicense.php
index dcb4058c0..55af1a8c0 100644
--- a/app/Actions/License/CheckResaleLicense.php
+++ b/app/Actions/License/CheckResaleLicense.php
@@ -2,7 +2,6 @@
namespace App\Actions\License;
-use App\Models\InstanceSettings;
use Illuminate\Support\Facades\Http;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -13,7 +12,7 @@ class CheckResaleLicense
public function handle()
{
try {
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
if (isDev()) {
$settings->update([
'is_resale_license_active' => true,
diff --git a/app/Actions/Proxy/CheckProxy.php b/app/Actions/Proxy/CheckProxy.php
index cf0f6015c..03a0beddf 100644
--- a/app/Actions/Proxy/CheckProxy.php
+++ b/app/Actions/Proxy/CheckProxy.php
@@ -2,14 +2,17 @@
namespace App\Actions\Proxy;
+use App\Enums\ProxyTypes;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
+use Symfony\Component\Yaml\Yaml;
class CheckProxy
{
use AsAction;
- public function handle(Server $server, $fromUI = false)
+ // It should return if the proxy should be started (true) or not (false)
+ public function handle(Server $server, $fromUI = false): bool
{
if (! $server->isFunctional()) {
return false;
@@ -62,22 +65,42 @@ class CheckProxy
$ip = 'host.docker.internal';
}
- $connection80 = @fsockopen($ip, '80');
- $connection443 = @fsockopen($ip, '443');
- $port80 = is_resource($connection80) && fclose($connection80);
- $port443 = is_resource($connection443) && fclose($connection443);
- if ($port80) {
- if ($fromUI) {
- throw new \Exception("Port 80 is in use.
You must stop the process using this port.
Docs: https://coolify.io/docs
Discord: https://coollabs.io/discord");
+ $portsToCheck = ['80', '443'];
+
+ try {
+ if ($server->proxyType() !== ProxyTypes::NONE->value) {
+ $proxyCompose = CheckConfiguration::run($server);
+ if (isset($proxyCompose)) {
+ $yaml = Yaml::parse($proxyCompose);
+ $portsToCheck = [];
+ if ($server->proxyType() === ProxyTypes::TRAEFIK->value) {
+ $ports = data_get($yaml, 'services.traefik.ports');
+ } elseif ($server->proxyType() === ProxyTypes::CADDY->value) {
+ $ports = data_get($yaml, 'services.caddy.ports');
+ }
+ if (isset($ports)) {
+ foreach ($ports as $port) {
+ $portsToCheck[] = str($port)->before(':')->value();
+ }
+ }
+ }
} else {
- return false;
+ $portsToCheck = [];
}
+ } catch (\Exception $e) {
+ ray($e->getMessage());
}
- if ($port443) {
- if ($fromUI) {
- throw new \Exception("Port 443 is in use.
You must stop the process using this port.
Docs: https://coolify.io/docs
Discord: https://coollabs.io/discord");
- } else {
- return false;
+ if (count($portsToCheck) === 0) {
+ return false;
+ }
+ foreach ($portsToCheck as $port) {
+ $connection = @fsockopen($ip, $port);
+ if (is_resource($connection) && fclose($connection)) {
+ if ($fromUI) {
+ throw new \Exception("Port $port is in use.
You must stop the process using this port.
Docs: https://coolify.io/docs
Discord: https://coollabs.io/discord");
+ } else {
+ return false;
+ }
}
}
diff --git a/app/Actions/Server/CleanupDocker.php b/app/Actions/Server/CleanupDocker.php
index 19bb03383..dc6ac12bf 100644
--- a/app/Actions/Server/CleanupDocker.php
+++ b/app/Actions/Server/CleanupDocker.php
@@ -2,7 +2,6 @@
namespace App\Actions\Server;
-use App\Models\InstanceSettings;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -12,7 +11,7 @@ class CleanupDocker
public function handle(Server $server)
{
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$helperImageVersion = data_get($settings, 'helper_version');
$helperImage = config('coolify.helper_image');
$helperImageWithVersion = "$helperImage:$helperImageVersion";
diff --git a/app/Actions/Server/UpdateCoolify.php b/app/Actions/Server/UpdateCoolify.php
index 901f2cf77..30664df26 100644
--- a/app/Actions/Server/UpdateCoolify.php
+++ b/app/Actions/Server/UpdateCoolify.php
@@ -3,7 +3,6 @@
namespace App\Actions\Server;
use App\Jobs\PullHelperImageJob;
-use App\Models\InstanceSettings;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -20,7 +19,7 @@ class UpdateCoolify
public function handle($manual_update = false)
{
try {
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$this->server = Server::find(0);
if (! $this->server) {
return;
diff --git a/app/Console/Commands/CheckApplicationDeploymentQueue.php b/app/Console/Commands/CheckApplicationDeploymentQueue.php
new file mode 100644
index 000000000..e89d26f2c
--- /dev/null
+++ b/app/Console/Commands/CheckApplicationDeploymentQueue.php
@@ -0,0 +1,50 @@
+option('seconds');
+ $deployments = ApplicationDeploymentQueue::whereIn('status', [
+ ApplicationDeploymentStatus::IN_PROGRESS,
+ ApplicationDeploymentStatus::QUEUED,
+ ])->where('created_at', '<=', now()->subSeconds($seconds))->get();
+ if ($deployments->isEmpty()) {
+ $this->info('No deployments found in the last '.$seconds.' seconds.');
+
+ return;
+ }
+
+ $this->info('Found '.$deployments->count().' deployments created in the last '.$seconds.' seconds.');
+
+ foreach ($deployments as $deployment) {
+ if ($this->option('force')) {
+ $this->info('Deployment '.$deployment->id.' created at '.$deployment->created_at.' is older than '.$seconds.' seconds. Setting status to failed.');
+ $this->cancelDeployment($deployment);
+ } else {
+ $this->info('Deployment '.$deployment->id.' created at '.$deployment->created_at.' is older than '.$seconds.' seconds. Setting status to failed.');
+ if ($this->confirm('Do you want to cancel this deployment?', true)) {
+ $this->cancelDeployment($deployment);
+ }
+ }
+ }
+ }
+
+ private function cancelDeployment(ApplicationDeploymentQueue $deployment)
+ {
+ $deployment->update(['status' => ApplicationDeploymentStatus::FAILED]);
+ if ($deployment->server?->isFunctional()) {
+ remote_process(['docker rm -f '.$deployment->deployment_uuid], $deployment->server, false);
+ }
+ }
+}
diff --git a/app/Console/Commands/CleanupApplicationDeploymentQueue.php b/app/Console/Commands/CleanupApplicationDeploymentQueue.php
index f068e3eb2..3aae28ae6 100644
--- a/app/Console/Commands/CleanupApplicationDeploymentQueue.php
+++ b/app/Console/Commands/CleanupApplicationDeploymentQueue.php
@@ -7,9 +7,9 @@ use Illuminate\Console\Command;
class CleanupApplicationDeploymentQueue extends Command
{
- protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
+ protected $signature = 'cleanup:deployment-queue {--team-id=}';
- protected $description = 'CleanupApplicationDeploymentQueue';
+ protected $description = 'Cleanup application deployment queue.';
public function handle()
{
diff --git a/app/Console/Commands/CleanupStuckedResources.php b/app/Console/Commands/CleanupStuckedResources.php
index dfd09d4b7..66c25ec27 100644
--- a/app/Console/Commands/CleanupStuckedResources.php
+++ b/app/Console/Commands/CleanupStuckedResources.php
@@ -4,6 +4,7 @@ namespace App\Console\Commands;
use App\Jobs\CleanupHelperContainersJob;
use App\Models\Application;
+use App\Models\ApplicationDeploymentQueue;
use App\Models\ApplicationPreview;
use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledTask;
@@ -47,6 +48,17 @@ class CleanupStuckedResources extends Command
} catch (\Throwable $e) {
echo "Error in cleaning stucked resources: {$e->getMessage()}\n";
}
+ try {
+ $applicationsDeploymentQueue = ApplicationDeploymentQueue::get();
+ foreach ($applicationsDeploymentQueue as $applicationDeploymentQueue) {
+ if (is_null($applicationDeploymentQueue->application)) {
+ echo "Deleting stuck application deployment queue: {$applicationDeploymentQueue->id}\n";
+ $applicationDeploymentQueue->delete();
+ }
+ }
+ } catch (\Throwable $e) {
+ echo "Error in cleaning stuck application deployment queue: {$e->getMessage()}\n";
+ }
try {
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($applications as $application) {
diff --git a/app/Console/Commands/Dev.php b/app/Console/Commands/Dev.php
index 964b8e46e..20a2667c3 100644
--- a/app/Console/Commands/Dev.php
+++ b/app/Console/Commands/Dev.php
@@ -48,6 +48,13 @@ class Dev extends Command
echo "Generating APP_KEY.\n";
Artisan::call('key:generate');
}
+
+ // Generate STORAGE link if not exists
+ if (! file_exists(public_path('storage'))) {
+ echo "Generating STORAGE link.\n";
+ Artisan::call('storage:link');
+ }
+
// Seed database if it's empty
$settings = InstanceSettings::find(0);
if (! $settings) {
diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php
index 2f5d36140..ad7bff86d 100644
--- a/app/Console/Commands/Init.php
+++ b/app/Console/Commands/Init.php
@@ -7,7 +7,6 @@ use App\Enums\ActivityTypes;
use App\Enums\ApplicationDeploymentStatus;
use App\Models\ApplicationDeploymentQueue;
use App\Models\Environment;
-use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
use App\Models\Server;
use App\Models\StandalonePostgresql;
@@ -69,7 +68,7 @@ class Init extends Command
} catch (\Throwable $e) {
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
}
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
if (! is_null(env('AUTOUPDATE', null))) {
if (env('AUTOUPDATE') == true) {
$settings->update(['is_auto_update_enabled' => true]);
@@ -196,7 +195,7 @@ class Init extends Command
{
$id = config('app.id');
$version = config('version');
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$do_not_track = data_get($settings, 'do_not_track');
if ($do_not_track == true) {
echo "Skipping alive as do_not_track is enabled\n";
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index 03d479400..1430fcdd1 100644
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -12,8 +12,8 @@ use App\Jobs\PullSentinelImageJob;
use App\Jobs\PullTemplatesFromCDN;
use App\Jobs\ScheduledTaskJob;
use App\Jobs\ServerCheckJob;
+use App\Jobs\ServerStorageCheckJob;
use App\Jobs\UpdateCoolifyJob;
-use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledTask;
use App\Models\Server;
@@ -28,7 +28,7 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule): void
{
$this->all_servers = Server::all();
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$schedule->job(new CleanupStaleMultiplexedConnections)->hourly();
@@ -66,7 +66,7 @@ class Kernel extends ConsoleKernel
private function pull_images($schedule)
{
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$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 ($server->isSentinelEnabled()) {
@@ -88,7 +88,7 @@ class Kernel extends ConsoleKernel
private function schedule_updates($schedule)
{
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$updateCheckFrequency = $settings->update_check_frequency;
$schedule->job(new CheckForUpdatesJob)
@@ -116,6 +116,7 @@ class Kernel extends ConsoleKernel
}
foreach ($servers as $server) {
$schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
+ // $schedule->job(new ServerStorageCheckJob($server))->everyMinute()->onOneServer();
$serverTimezone = $server->settings->server_timezone;
if ($server->settings->force_docker_cleanup) {
$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer();
diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php
index 6b69350fe..63fbfc862 100644
--- a/app/Exceptions/Handler.php
+++ b/app/Exceptions/Handler.php
@@ -65,7 +65,7 @@ class Handler extends ExceptionHandler
if ($e instanceof RuntimeException) {
return;
}
- $this->settings = \App\Models\InstanceSettings::get();
+ $this->settings = instanceSettings();
if ($this->settings->do_not_track) {
return;
}
diff --git a/app/Helpers/SshMultiplexingHelper.php b/app/Helpers/SshMultiplexingHelper.php
index b0a832605..1a2146799 100644
--- a/app/Helpers/SshMultiplexingHelper.php
+++ b/app/Helpers/SshMultiplexingHelper.php
@@ -94,7 +94,9 @@ class SshMultiplexingHelper
$muxPersistTime = config('constants.ssh.mux_persist_time');
$scp_command = "timeout $timeout scp ";
-
+ if ($server->isIpv6()) {
+ $scp_command .= '-6 ';
+ }
if (self::isMultiplexingEnabled()) {
$scp_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
self::ensureMultiplexedConnection($server);
@@ -136,8 +138,8 @@ class SshMultiplexingHelper
$ssh_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'));
- $command = "PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/host/usr/local/sbin:/host/usr/local/bin:/host/usr/sbin:/host/usr/bin:/host/sbin:/host/bin && $command";
$delimiter = Hash::make($command);
+ $delimiter = base64_encode($delimiter);
$command = str_replace($delimiter, '', $command);
$ssh_command .= "{$server->user}@{$server->ip} 'bash -se' << \\$delimiter".PHP_EOL
diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php
index 48e126f27..eef179048 100644
--- a/app/Http/Controllers/Api/ApplicationsController.php
+++ b/app/Http/Controllers/Api/ApplicationsController.php
@@ -177,6 +177,7 @@ class ApplicationsController extends Controller
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
+ 'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -279,6 +280,7 @@ class ApplicationsController extends Controller
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
+ 'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -381,6 +383,7 @@ class ApplicationsController extends Controller
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
+ 'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -468,6 +471,7 @@ class ApplicationsController extends Controller
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
+ 'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -552,6 +556,7 @@ class ApplicationsController extends Controller
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
+ 'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -602,6 +607,7 @@ class ApplicationsController extends Controller
'name' => ['type' => 'string', 'description' => 'The application name.'],
'description' => ['type' => 'string', 'description' => 'The application description.'],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
+ 'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -627,7 +633,7 @@ class ApplicationsController extends Controller
private function create_application(Request $request, $type)
{
- $allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths'];
+ $allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths', 'use_build_server'];
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
return invalidTokenResponse();
@@ -665,6 +671,7 @@ class ApplicationsController extends Controller
$fqdn = $request->domains;
$instantDeploy = $request->instant_deploy;
$githubAppUuid = $request->github_app_uuid;
+ $useBuildServer = $request->use_build_server;
$project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first();
if (! $project) {
@@ -737,6 +744,10 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id;
+ if (isset($useBuildServer)) {
+ $application->settings->is_build_server_enabled = $useBuildServer;
+ $application->settings->save();
+ }
$application->save();
$application->refresh();
if (! $application->settings->is_container_label_readonly_enabled) {
@@ -833,6 +844,10 @@ class ApplicationsController extends Controller
$application->environment_id = $environment->id;
$application->source_type = $githubApp->getMorphClass();
$application->source_id = $githubApp->id;
+ if (isset($useBuildServer)) {
+ $application->settings->is_build_server_enabled = $useBuildServer;
+ $application->settings->save();
+ }
$application->save();
$application->refresh();
if (! $application->settings->is_container_label_readonly_enabled) {
@@ -925,6 +940,10 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id;
+ if (isset($useBuildServer)) {
+ $application->settings->is_build_server_enabled = $useBuildServer;
+ $application->settings->save();
+ }
$application->save();
$application->refresh();
if (! $application->settings->is_container_label_readonly_enabled) {
@@ -1004,6 +1023,10 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id;
+ if (isset($useBuildServer)) {
+ $application->settings->is_build_server_enabled = $useBuildServer;
+ $application->settings->save();
+ }
$application->git_repository = 'coollabsio/coolify';
$application->git_branch = 'main';
@@ -1062,6 +1085,10 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id;
+ if (isset($useBuildServer)) {
+ $application->settings->is_build_server_enabled = $useBuildServer;
+ $application->settings->save();
+ }
$application->git_repository = 'coollabsio/coolify';
$application->git_branch = 'main';
@@ -1259,16 +1286,10 @@ class ApplicationsController extends Controller
format: 'uuid',
)
),
- new OA\Parameter(
- name: 'cleanup',
- in: 'query',
- description: 'Delete configurations and volumes.',
- required: false,
- schema: new OA\Schema(
- type: 'boolean',
- default: true,
- )
- ),
+ new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
+ new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
+ new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
+ new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
],
responses: [
new OA\Response(
@@ -1316,10 +1337,14 @@ class ApplicationsController extends Controller
'message' => 'Application not found',
], 404);
}
+
DeleteResourceJob::dispatch(
resource: $application,
- deleteConfigurations: $cleanup,
- deleteVolumes: $cleanup);
+ deleteConfigurations: $request->query->get('delete_configurations', true),
+ deleteVolumes: $request->query->get('delete_volumes', true),
+ dockerCleanup: $request->query->get('docker_cleanup', true),
+ deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
+ );
return response()->json([
'message' => 'Application deletion request queued.',
@@ -1404,6 +1429,7 @@ class ApplicationsController extends Controller
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
+ 'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -1460,7 +1486,7 @@ class ApplicationsController extends Controller
], 404);
}
$server = $application->destination->server;
- $allowedFields = ['name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy'];
+ $allowedFields = ['name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy', 'use_build_server'];
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
@@ -1538,6 +1564,13 @@ class ApplicationsController extends Controller
}
$instantDeploy = $request->instant_deploy;
+ $use_build_server = $request->use_build_server;
+
+ if (isset($use_build_server)) {
+ $application->settings->is_build_server_enabled = $use_build_server;
+ $application->settings->save();
+ }
+
removeUnnecessaryFieldsFromRequest($request);
$data = $request->all();
diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php
index a205704cc..65873f818 100644
--- a/app/Http/Controllers/Api/DatabasesController.php
+++ b/app/Http/Controllers/Api/DatabasesController.php
@@ -1541,16 +1541,10 @@ class DatabasesController extends Controller
format: 'uuid',
)
),
- new OA\Parameter(
- name: 'cleanup',
- in: 'query',
- description: 'Delete configurations and volumes.',
- required: false,
- schema: new OA\Schema(
- type: 'boolean',
- default: true,
- )
- ),
+ new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
+ new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
+ new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
+ new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
],
responses: [
new OA\Response(
@@ -1595,10 +1589,14 @@ class DatabasesController extends Controller
if (! $database) {
return response()->json(['message' => 'Database not found.'], 404);
}
+
DeleteResourceJob::dispatch(
resource: $database,
- deleteConfigurations: $cleanup,
- deleteVolumes: $cleanup);
+ deleteConfigurations: $request->query->get('delete_configurations', true),
+ deleteVolumes: $request->query->get('delete_volumes', true),
+ dockerCleanup: $request->query->get('docker_cleanup', true),
+ deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
+ );
return response()->json([
'message' => 'Database deletion request queued.',
diff --git a/app/Http/Controllers/Api/OtherController.php b/app/Http/Controllers/Api/OtherController.php
index c085b88a5..2414b7a42 100644
--- a/app/Http/Controllers/Api/OtherController.php
+++ b/app/Http/Controllers/Api/OtherController.php
@@ -86,7 +86,7 @@ class OtherController extends Controller
if ($teamId !== '0') {
return response()->json(['message' => 'You are not allowed to enable the API.'], 403);
}
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
$settings->update(['is_api_enabled' => true]);
return response()->json(['message' => 'API enabled.'], 200);
@@ -138,7 +138,7 @@ class OtherController extends Controller
if ($teamId !== '0') {
return response()->json(['message' => 'You are not allowed to disable the API.'], 403);
}
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
$settings->update(['is_api_enabled' => false]);
return response()->json(['message' => 'API disabled.'], 200);
diff --git a/app/Http/Controllers/Api/ServersController.php b/app/Http/Controllers/Api/ServersController.php
index 5f0d6bb12..a49515579 100644
--- a/app/Http/Controllers/Api/ServersController.php
+++ b/app/Http/Controllers/Api/ServersController.php
@@ -308,7 +308,7 @@ class ServersController extends Controller
$projects = Project::where('team_id', $teamId)->get();
$domains = collect();
$applications = $projects->pluck('applications')->flatten();
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
if ($applications->count() > 0) {
foreach ($applications as $application) {
$ip = $application->destination->server->ip;
diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php
index 0a6154410..89418517b 100644
--- a/app/Http/Controllers/Api/ServicesController.php
+++ b/app/Http/Controllers/Api/ServicesController.php
@@ -432,6 +432,10 @@ class ServicesController extends Controller
tags: ['Services'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Service UUID', schema: new OA\Schema(type: 'string')),
+ new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
+ new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
+ new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
+ new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
],
responses: [
new OA\Response(
@@ -476,7 +480,14 @@ class ServicesController extends Controller
if (! $service) {
return response()->json(['message' => 'Service not found.'], 404);
}
- DeleteResourceJob::dispatch($service);
+
+ DeleteResourceJob::dispatch(
+ resource: $service,
+ deleteConfigurations: $request->query->get('delete_configurations', true),
+ deleteVolumes: $request->query->get('delete_volumes', true),
+ dockerCleanup: $request->query->get('docker_cleanup', true),
+ deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
+ );
return response()->json([
'message' => 'Service deletion request queued.',
@@ -516,7 +527,8 @@ class ServicesController extends Controller
items: new OA\Items(ref: '#/components/schemas/EnvironmentVariable')
)
),
- ]),
+ ]
+ ),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -619,7 +631,8 @@ class ServicesController extends Controller
]
)
),
- ]),
+ ]
+ ),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -738,7 +751,8 @@ class ServicesController extends Controller
]
)
),
- ]),
+ ]
+ ),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -853,7 +867,8 @@ class ServicesController extends Controller
]
)
),
- ]),
+ ]
+ ),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -953,7 +968,8 @@ class ServicesController extends Controller
]
)
),
- ]),
+ ]
+ ),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1025,9 +1041,11 @@ class ServicesController extends Controller
type: 'object',
properties: [
'message' => ['type' => 'string', 'example' => 'Service starting request queued.'],
- ])
+ ]
+ )
),
- ]),
+ ]
+ ),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1101,9 +1119,11 @@ class ServicesController extends Controller
type: 'object',
properties: [
'message' => ['type' => 'string', 'example' => 'Service stopping request queued.'],
- ])
+ ]
+ )
),
- ]),
+ ]
+ ),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1177,9 +1197,11 @@ class ServicesController extends Controller
type: 'object',
properties: [
'message' => ['type' => 'string', 'example' => 'Service restaring request queued.'],
- ])
+ ]
+ )
),
- ]),
+ ]
+ ),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
diff --git a/app/Http/Controllers/OauthController.php b/app/Http/Controllers/OauthController.php
index 9569e8cfa..630d01045 100644
--- a/app/Http/Controllers/OauthController.php
+++ b/app/Http/Controllers/OauthController.php
@@ -2,7 +2,6 @@
namespace App\Http\Controllers;
-use App\Models\InstanceSettings;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -22,7 +21,7 @@ class OauthController extends Controller
$oauthUser = get_socialite_provider($provider)->user();
$user = User::whereEmail($oauthUser->email)->first();
if (! $user) {
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
if (! $settings->is_registration_enabled) {
abort(403, 'Registration is disabled');
}
diff --git a/app/Http/Middleware/ApiAllowed.php b/app/Http/Middleware/ApiAllowed.php
index 648720ba4..471e6d602 100644
--- a/app/Http/Middleware/ApiAllowed.php
+++ b/app/Http/Middleware/ApiAllowed.php
@@ -14,7 +14,7 @@ class ApiAllowed
if (isCloud()) {
return $next($request);
}
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
if ($settings->is_api_enabled === false) {
return response()->json(['success' => true, 'message' => 'API is disabled.'], 403);
}
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 24565b389..9ae383a9f 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -12,7 +12,6 @@ use App\Models\ApplicationPreview;
use App\Models\EnvironmentVariable;
use App\Models\GithubApp;
use App\Models\GitlabApp;
-use App\Models\InstanceSettings;
use App\Models\Server;
use App\Models\StandaloneDocker;
use App\Models\SwarmDocker;
@@ -962,7 +961,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}
}
if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
- if ($this->application->compose_parsing_version === '3') {
+ if ((int) $this->application->compose_parsing_version >= 3) {
$envs->push("COOLIFY_URL={$this->application->fqdn}");
} else {
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
@@ -970,7 +969,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
- if ($this->application->compose_parsing_version === '3') {
+ if ((int) $this->application->compose_parsing_version >= 3) {
$envs->push("COOLIFY_FQDN={$url}");
} else {
$envs->push("COOLIFY_URL={$url}");
@@ -1334,7 +1333,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private function prepare_builder_image()
{
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$helperImage = config('coolify.helper_image');
$helperImage = "{$helperImage}:{$settings->helper_version}";
// Get user home directory
diff --git a/app/Jobs/CheckForUpdatesJob.php b/app/Jobs/CheckForUpdatesJob.php
index 747a9a98a..f2348118a 100644
--- a/app/Jobs/CheckForUpdatesJob.php
+++ b/app/Jobs/CheckForUpdatesJob.php
@@ -2,7 +2,6 @@
namespace App\Jobs;
-use App\Models\InstanceSettings;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -22,7 +21,7 @@ class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
if (isDev() || isCloud()) {
return;
}
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
if ($response->successful()) {
$versions = $response->json();
diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php
index 947dc4317..769739d5e 100644
--- a/app/Jobs/DatabaseBackupJob.php
+++ b/app/Jobs/DatabaseBackupJob.php
@@ -2,9 +2,7 @@
namespace App\Jobs;
-use App\Actions\Database\StopDatabase;
use App\Events\BackupCreated;
-use App\Models\InstanceSettings;
use App\Models\S3Storage;
use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledDatabaseBackupExecution;
@@ -25,7 +23,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str;
-use Visus\Cuid2\Cuid2;
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{
@@ -64,32 +61,32 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
public function __construct($backup)
{
$this->backup = $backup;
- $this->team = Team::find($backup->team_id);
- if (is_null($this->team)) {
- return;
- }
- if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
- $this->database = data_get($this->backup, 'database');
- $this->server = $this->database->service->server;
- $this->s3 = $this->backup->s3;
- } else {
- $this->database = data_get($this->backup, 'database');
- $this->server = $this->database->destination->server;
- $this->s3 = $this->backup->s3;
- }
}
public function handle(): void
{
try {
- // Check if team is exists
- if (is_null($this->team)) {
- $this->backup->update(['status' => 'failed']);
- StopDatabase::run($this->database);
- $this->database->delete();
+ $this->team = Team::find($this->backup->team_id);
+ if (! $this->team) {
+ $this->backup->delete();
return;
}
+ if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
+ $this->database = data_get($this->backup, 'database');
+ $this->server = $this->database->service->server;
+ $this->s3 = $this->backup->s3;
+ } else {
+ $this->database = data_get($this->backup, 'database');
+ $this->server = $this->database->destination->server;
+ $this->s3 = $this->backup->s3;
+ }
+ if (is_null($this->server)) {
+ throw new \Exception('Server not found?!');
+ }
+ if (is_null($this->database)) {
+ throw new \Exception('Database not found?!');
+ }
BackupCreated::dispatch($this->team->id);
@@ -239,7 +236,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
}
}
$this->backup_dir = backup_dir().'/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name;
-
if ($this->database->name === 'coolify-db') {
$databasesToBackup = ['coolify'];
$this->directory_name = $this->container_name = 'coolify-db';
@@ -252,6 +248,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
try {
if (str($databaseType)->contains('postgres')) {
$this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp';
+ if ($this->backup->dump_all) {
+ $this->backup_file = '/pg-dump-all-'.Carbon::now()->timestamp.'.gz';
+ }
$this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database,
@@ -280,6 +279,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$this->backup_standalone_mongodb($database);
} elseif (str($databaseType)->contains('mysql')) {
$this->backup_file = "/mysql-dump-$database-".Carbon::now()->timestamp.'.dmp';
+ if ($this->backup->dump_all) {
+ $this->backup_file = '/mysql-dump-all-'.Carbon::now()->timestamp.'.gz';
+ }
$this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database,
@@ -289,6 +291,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$this->backup_standalone_mysql($database);
} elseif (str($databaseType)->contains('mariadb')) {
$this->backup_file = "/mariadb-dump-$database-".Carbon::now()->timestamp.'.dmp';
+ if ($this->backup->dump_all) {
+ $this->backup_file = '/mariadb-dump-all-'.Carbon::now()->timestamp.'.gz';
+ }
$this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database,
@@ -327,7 +332,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
send_internal_notification('DatabaseBackupJob failed with: '.$e->getMessage());
throw $e;
} finally {
- BackupCreated::dispatch($this->team->id);
+ if ($this->team) {
+ BackupCreated::dispatch($this->team->id);
+ }
}
}
@@ -386,7 +393,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
if ($this->postgres_password) {
$backupCommand .= " -e PGPASSWORD=$this->postgres_password";
}
- $backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location";
+ if ($this->backup->dump_all) {
+ $backupCommand .= " $this->container_name pg_dumpall --username {$this->database->postgres_user} | gzip > $this->backup_location";
+ } else {
+ $backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location";
+ }
$commands[] = $backupCommand;
ray($commands);
@@ -407,8 +418,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{
try {
$commands[] = 'mkdir -p '.$this->backup_dir;
- $commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
- ray($commands);
+ if ($this->backup->dump_all) {
+ $commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} --all-databases --single-transaction --quick --lock-tables=false --compress | gzip > $this->backup_location";
+ } else {
+ $commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
+ }
$this->backup_output = instant_remote_process($commands, $this->server);
$this->backup_output = trim($this->backup_output);
if ($this->backup_output === '') {
@@ -426,7 +440,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{
try {
$commands[] = 'mkdir -p '.$this->backup_dir;
- $commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
+ if ($this->backup->dump_all) {
+ $commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} --all-databases --single-transaction --quick --lock-tables=false --compress > $this->backup_location";
+ } else {
+ $commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
+ }
ray($commands);
$this->backup_output = instant_remote_process($commands, $this->server);
$this->backup_output = trim($this->backup_output);
@@ -468,34 +486,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
}
}
- // private function upload_to_s3(): void
- // {
- // try {
- // if (is_null($this->s3)) {
- // return;
- // }
- // $key = $this->s3->key;
- // $secret = $this->s3->secret;
- // // $region = $this->s3->region;
- // $bucket = $this->s3->bucket;
- // $endpoint = $this->s3->endpoint;
- // $this->s3->testConnection(shouldSave: true);
- // $configName = new Cuid2;
-
- // $s3_copy_dir = str($this->backup_location)->replace(backup_dir(), '/var/www/html/storage/app/backups/');
- // $commands[] = "docker exec coolify bash -c 'mc config host add {$configName} {$endpoint} $key $secret'";
- // $commands[] = "docker exec coolify bash -c 'mc cp $s3_copy_dir {$configName}/{$bucket}{$this->backup_dir}/'";
- // instant_remote_process($commands, $this->server);
- // $this->add_to_backup_output('Uploaded to S3.');
- // } catch (\Throwable $e) {
- // $this->add_to_backup_output($e->getMessage());
- // throw $e;
- // } finally {
- // $removeConfigCommands[] = "docker exec coolify bash -c 'mc config remove {$configName}'";
- // $removeConfigCommands[] = "docker exec coolify bash -c 'mc alias rm {$configName}'";
- // instant_remote_process($removeConfigCommands, $this->server, false);
- // }
- // }
private function upload_to_s3(): void
{
try {
@@ -517,10 +507,27 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$this->ensureHelperImageAvailable();
$fullImageName = $this->getFullImageName();
- $commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
- $commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
+
+ if (isDev()) {
+ if ($this->database->name === 'coolify-db') {
+ $backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/coolify/coolify-db-'.$this->server->ip.$this->backup_file;
+ $commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
+ } else {
+ $backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name.$this->backup_file;
+ $commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
+ }
+ } else {
+ $commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
+ }
+ if ($this->s3->isHetzner()) {
+ $endpointWithoutBucket = 'https://'.str($endpoint)->after('https://')->after('.')->value();
+ $commands[] = "docker exec backup-of-{$this->backup->uuid} mc alias set --path=off --api=S3v4 temporary {$endpointWithoutBucket} $key $secret";
+ } else {
+ $commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
+ }
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
instant_remote_process($commands, $this->server);
+
$this->add_to_backup_output('Uploaded to S3.');
} catch (\Throwable $e) {
$this->add_to_backup_output($e->getMessage());
@@ -562,7 +569,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
private function getFullImageName(): string
{
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$helperImage = config('coolify.helper_image');
$latestVersion = $settings->helper_version;
diff --git a/app/Jobs/DeleteResourceJob.php b/app/Jobs/DeleteResourceJob.php
index ac34d064e..2442d5b06 100644
--- a/app/Jobs/DeleteResourceJob.php
+++ b/app/Jobs/DeleteResourceJob.php
@@ -31,10 +31,10 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
public function __construct(
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource,
- public bool $deleteConfigurations,
- public bool $deleteVolumes,
- public bool $dockerCleanup,
- public bool $deleteConnectedNetworks
+ public bool $deleteConfigurations = true,
+ public bool $deleteVolumes = true,
+ public bool $dockerCleanup = true,
+ public bool $deleteConnectedNetworks = true
) {}
public function handle()
diff --git a/app/Jobs/PullHelperImageJob.php b/app/Jobs/PullHelperImageJob.php
index ef1659680..4b208fc31 100644
--- a/app/Jobs/PullHelperImageJob.php
+++ b/app/Jobs/PullHelperImageJob.php
@@ -2,7 +2,6 @@
namespace App\Jobs;
-use App\Models\InstanceSettings;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
@@ -26,7 +25,7 @@ class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
if ($response->successful()) {
$versions = $response->json();
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$latest_version = data_get($versions, 'coolify.helper.version');
$current_version = $settings->helper_version;
if (version_compare($latest_version, $current_version, '>')) {
diff --git a/app/Jobs/ServerStorageCheckJob.php b/app/Jobs/ServerStorageCheckJob.php
new file mode 100644
index 000000000..376cb8532
--- /dev/null
+++ b/app/Jobs/ServerStorageCheckJob.php
@@ -0,0 +1,59 @@
+server->isFunctional()) {
+ ray('Server is not ready.');
+
+ return 'Server is not ready.';
+ }
+ $team = $this->server->team;
+ $percentage = $this->server->storageCheck();
+ if ($percentage > 1) {
+ ray('Server storage is at '.$percentage.'%');
+ }
+
+ } catch (\Throwable $e) {
+ ray($e->getMessage());
+
+ return handleError($e);
+ }
+
+ }
+}
diff --git a/app/Jobs/UpdateCoolifyJob.php b/app/Jobs/UpdateCoolifyJob.php
index 4c65a711f..2cc705e4a 100644
--- a/app/Jobs/UpdateCoolifyJob.php
+++ b/app/Jobs/UpdateCoolifyJob.php
@@ -3,7 +3,6 @@
namespace App\Jobs;
use App\Actions\Server\UpdateCoolify;
-use App\Models\InstanceSettings;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
@@ -23,7 +22,7 @@ class UpdateCoolifyJob implements ShouldBeEncrypted, ShouldQueue
{
try {
CheckForUpdatesJob::dispatchSync();
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
if (! $settings->new_version_available) {
Log::info('No new version available. Skipping update.');
diff --git a/app/Livewire/Dashboard.php b/app/Livewire/Dashboard.php
index 1f0b68dd3..d18a7689e 100644
--- a/app/Livewire/Dashboard.php
+++ b/app/Livewire/Dashboard.php
@@ -30,7 +30,7 @@ class Dashboard extends Component
public function cleanup_queue()
{
- Artisan::queue('cleanup:application-deployment-queue', [
+ Artisan::queue('cleanup:deployment-queue', [
'--team-id' => currentTeam()->id,
]);
}
diff --git a/app/Livewire/Help.php b/app/Livewire/Help.php
index d6dc0d521..68691c1cd 100644
--- a/app/Livewire/Help.php
+++ b/app/Livewire/Help.php
@@ -47,7 +47,7 @@ class Help extends Component
]
);
$mail->subject("[HELP]: {$this->subject}");
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
$type = set_transanctional_email_settings($settings);
if (! $type) {
$url = 'https://app.coolify.io/api/feedback';
diff --git a/app/Livewire/Notifications/Email.php b/app/Livewire/Notifications/Email.php
index 2960ed226..53673292e 100644
--- a/app/Livewire/Notifications/Email.php
+++ b/app/Livewire/Notifications/Email.php
@@ -172,7 +172,7 @@ class Email extends Component
public function copyFromInstanceSettings()
{
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
if ($settings->smtp_enabled) {
$team = currentTeam();
$team->update([
diff --git a/app/Livewire/Project/Database/BackupEdit.php b/app/Livewire/Project/Database/BackupEdit.php
index 98b2b4263..7e2e4a12b 100644
--- a/app/Livewire/Project/Database/BackupEdit.php
+++ b/app/Livewire/Project/Database/BackupEdit.php
@@ -31,6 +31,7 @@ class BackupEdit extends Component
'backup.save_s3' => 'required|boolean',
'backup.s3_storage_id' => 'nullable|integer',
'backup.databases_to_backup' => 'nullable',
+ 'backup.dump_all' => 'required|boolean',
];
protected $validationAttributes = [
@@ -40,6 +41,7 @@ class BackupEdit extends Component
'backup.save_s3' => 'Save to S3',
'backup.s3_storage_id' => 'S3 Storage',
'backup.databases_to_backup' => 'Databases to Backup',
+ 'backup.dump_all' => 'Backup All Databases',
];
protected $messages = [
diff --git a/app/Livewire/Project/Database/ScheduledBackups.php b/app/Livewire/Project/Database/ScheduledBackups.php
index beb5a9c39..8021e25d3 100644
--- a/app/Livewire/Project/Database/ScheduledBackups.php
+++ b/app/Livewire/Project/Database/ScheduledBackups.php
@@ -26,7 +26,7 @@ class ScheduledBackups extends Component
public function mount(): void
{
if ($this->selectedBackupId) {
- $this->setSelectedBackup($this->selectedBackupId);
+ $this->setSelectedBackup($this->selectedBackupId, true);
}
$this->parameters = get_route_parameters();
if ($this->database->getMorphClass() === 'App\Models\ServiceDatabase') {
@@ -37,10 +37,13 @@ class ScheduledBackups extends Component
$this->s3s = currentTeam()->s3s;
}
- public function setSelectedBackup($backupId)
+ public function setSelectedBackup($backupId, $force = false)
{
+ if ($this->selectedBackupId === $backupId && ! $force) {
+ return;
+ }
$this->selectedBackupId = $backupId;
- $this->selectedBackup = $this->database->scheduledBackups->find($this->selectedBackupId);
+ $this->selectedBackup = $this->database->scheduledBackups->find($backupId);
if (is_null($this->selectedBackup)) {
$this->selectedBackupId = null;
}
diff --git a/app/Livewire/Project/Service/Navbar.php b/app/Livewire/Project/Service/Navbar.php
index 42c9357fd..70b3b5db6 100644
--- a/app/Livewire/Project/Service/Navbar.php
+++ b/app/Livewire/Project/Service/Navbar.php
@@ -108,8 +108,23 @@ class Navbar extends Component
return;
}
+ StopService::run(service: $this->service, dockerCleanup: false);
+ $this->service->parse();
+ $this->dispatch('imagePulled');
+ $activity = StartService::run($this->service);
+ $this->dispatch('activityMonitor', $activity->id);
+ }
+
+ public function pullAndRestartEvent()
+ {
+ $this->checkDeployments();
+ if ($this->isDeploymentProgress) {
+ $this->dispatch('error', 'There is a deployment in progress.');
+
+ return;
+ }
PullImage::run($this->service);
- StopService::run($this->service);
+ StopService::run(service: $this->service, dockerCleanup: false);
$this->service->parse();
$this->dispatch('imagePulled');
$activity = StartService::run($this->service);
diff --git a/app/Livewire/Project/Shared/Danger.php b/app/Livewire/Project/Shared/Danger.php
index 543e64539..c05260899 100644
--- a/app/Livewire/Project/Shared/Danger.php
+++ b/app/Livewire/Project/Shared/Danger.php
@@ -91,10 +91,12 @@ class Danger extends Component
public function delete($password)
{
- if (! Hash::check($password, Auth::user()->password)) {
- $this->addError('password', 'The provided password is incorrect.');
+ if (isProduction()) {
+ if (! Hash::check($password, Auth::user()->password)) {
+ $this->addError('password', 'The provided password is incorrect.');
- return;
+ return;
+ }
}
if (! $this->resource) {
diff --git a/app/Livewire/Project/Shared/ExecuteContainerCommand.php b/app/Livewire/Project/Shared/ExecuteContainerCommand.php
index d95443621..90419caed 100644
--- a/app/Livewire/Project/Shared/ExecuteContainerCommand.php
+++ b/app/Livewire/Project/Shared/ExecuteContainerCommand.php
@@ -11,6 +11,8 @@ use Livewire\Component;
class ExecuteContainerCommand extends Component
{
+ public $selected_container = 'default';
+
public $container;
public Collection $containers;
@@ -83,11 +85,14 @@ class ExecuteContainerCommand extends Component
$containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true);
}
foreach ($containers as $container) {
- $payload = [
- 'server' => $server,
- 'container' => $container,
- ];
- $this->containers = $this->containers->push($payload);
+ // if container state is running
+ if (data_get($container, 'State') === 'running') {
+ $payload = [
+ 'server' => $server,
+ 'container' => $container,
+ ];
+ $this->containers = $this->containers->push($payload);
+ }
}
} elseif (data_get($this->parameters, 'database_uuid')) {
if ($this->resource->isRunning()) {
@@ -100,7 +105,6 @@ class ExecuteContainerCommand extends Component
}
} elseif (data_get($this->parameters, 'service_uuid')) {
$this->resource->applications()->get()->each(function ($application) {
- ray($application);
if ($application->isRunning()) {
$this->containers->push([
'server' => $this->resource->server,
@@ -131,9 +135,14 @@ class ExecuteContainerCommand extends Component
#[On('connectToContainer')]
public function connectToContainer()
{
+ if ($this->selected_container === 'default') {
+ $this->dispatch('error', 'Please select a container.');
+
+ return;
+ }
try {
- $container_name = data_get($this->container, 'container.Names');
- if (is_null($container_name)) {
+ $container = collect($this->containers)->firstWhere('container.Names', $this->selected_container);
+ if (is_null($container)) {
throw new \RuntimeException('Container not found.');
}
$server = data_get($this->container, 'server');
@@ -141,11 +150,11 @@ class ExecuteContainerCommand extends Component
if ($server->isForceDisabled()) {
throw new \RuntimeException('Server is disabled.');
}
-
- $this->dispatch('send-terminal-command',
- true,
- $container_name,
- $server->uuid,
+ $this->dispatch(
+ 'send-terminal-command',
+ isset($container),
+ data_get($container, 'container.Names'),
+ data_get($container, 'server.uuid')
);
} catch (\Throwable $e) {
diff --git a/app/Livewire/Project/Shared/Terminal.php b/app/Livewire/Project/Shared/Terminal.php
index 27be46227..916db650f 100644
--- a/app/Livewire/Project/Shared/Terminal.php
+++ b/app/Livewire/Project/Shared/Terminal.php
@@ -34,9 +34,9 @@ class Terminal extends Component
if ($status !== 'running') {
return;
}
- $command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$identifier} sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
+ $command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$identifier} sh -c 'PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
} else {
- $command = SshMultiplexingHelper::generateSshCommand($server, "sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
+ $command = SshMultiplexingHelper::generateSshCommand($server, 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n "$SHELL" ]; then exec $SHELL; else sh; fi');
}
// ssh command is sent back to frontend then to websocket
diff --git a/app/Livewire/Security/ApiTokens.php b/app/Livewire/Security/ApiTokens.php
index 40752630e..fe68a8ba5 100644
--- a/app/Livewire/Security/ApiTokens.php
+++ b/app/Livewire/Security/ApiTokens.php
@@ -15,6 +15,8 @@ class ApiTokens extends Component
public bool $readOnly = true;
+ public bool $rootAccess = false;
+
public array $permissions = ['read-only'];
public $isApiEnabled;
@@ -35,12 +37,11 @@ class ApiTokens extends Component
if ($this->viewSensitiveData) {
$this->permissions[] = 'view:sensitive';
$this->permissions = array_diff($this->permissions, ['*']);
+ $this->rootAccess = false;
} else {
$this->permissions = array_diff($this->permissions, ['view:sensitive']);
}
- if (count($this->permissions) == 0) {
- $this->permissions = ['*'];
- }
+ $this->makeSureOneIsSelected();
}
public function updatedReadOnly()
@@ -48,11 +49,30 @@ class ApiTokens extends Component
if ($this->readOnly) {
$this->permissions[] = 'read-only';
$this->permissions = array_diff($this->permissions, ['*']);
+ $this->rootAccess = false;
} else {
$this->permissions = array_diff($this->permissions, ['read-only']);
}
- if (count($this->permissions) == 0) {
+ $this->makeSureOneIsSelected();
+ }
+
+ public function updatedRootAccess()
+ {
+ if ($this->rootAccess) {
$this->permissions = ['*'];
+ $this->readOnly = false;
+ $this->viewSensitiveData = false;
+ } else {
+ $this->readOnly = true;
+ $this->permissions = ['read-only'];
+ }
+ }
+
+ public function makeSureOneIsSelected()
+ {
+ if (count($this->permissions) == 0) {
+ $this->permissions = ['read-only'];
+ $this->readOnly = true;
}
}
@@ -62,12 +82,6 @@ class ApiTokens extends Component
$this->validate([
'description' => 'required|min:3|max:255',
]);
- // if ($this->viewSensitiveData) {
- // $this->permissions[] = 'view:sensitive';
- // }
- // if ($this->readOnly) {
- // $this->permissions[] = 'read-only';
- // }
$token = auth()->user()->createToken($this->description, $this->permissions);
$this->tokens = auth()->user()->tokens;
session()->flash('token', $token->plainTextToken);
diff --git a/app/Livewire/Server/ConfigureCloudflareTunnels.php b/app/Livewire/Server/ConfigureCloudflareTunnels.php
index a69a5e15d..f58d7b6be 100644
--- a/app/Livewire/Server/ConfigureCloudflareTunnels.php
+++ b/app/Livewire/Server/ConfigureCloudflareTunnels.php
@@ -30,6 +30,11 @@ class ConfigureCloudflareTunnels extends Component
public function submit()
{
try {
+ if (str($this->ssh_domain)->contains('https://')) {
+ $this->ssh_domain = str($this->ssh_domain)->replace('https://', '')->replace('http://', '')->trim();
+ // remove / from the end
+ $this->ssh_domain = str($this->ssh_domain)->replace('/', '');
+ }
$server = Server::ownedByCurrentTeam()->where('id', $this->server_id)->firstOrFail();
ConfigureCloudflared::dispatch($server, $this->cloudflare_token);
$server->settings->is_cloudflare_tunnel = true;
diff --git a/app/Livewire/Server/Proxy/Status.php b/app/Livewire/Server/Proxy/Status.php
index 20db4dad4..f4f18381f 100644
--- a/app/Livewire/Server/Proxy/Status.php
+++ b/app/Livewire/Server/Proxy/Status.php
@@ -4,7 +4,7 @@ namespace App\Livewire\Server\Proxy;
use App\Actions\Docker\GetContainersStatus;
use App\Actions\Proxy\CheckProxy;
-use App\Jobs\ContainerStatusJob;
+use App\Actions\Proxy\StartProxy;
use App\Models\Server;
use Livewire\Component;
@@ -44,7 +44,10 @@ class Status extends Component
}
$this->numberOfPolls++;
}
- CheckProxy::run($this->server, true);
+ $shouldStart = CheckProxy::run($this->server, true);
+ if ($shouldStart) {
+ StartProxy::run($this->server, false);
+ }
$this->dispatch('proxyStatusUpdated');
if ($this->server->proxy->status === 'running') {
$this->polling = false;
diff --git a/app/Livewire/Settings/Index.php b/app/Livewire/Settings/Index.php
index c52970258..754f0929b 100644
--- a/app/Livewire/Settings/Index.php
+++ b/app/Livewire/Settings/Index.php
@@ -60,7 +60,7 @@ class Index extends Component
public function mount()
{
if (isInstanceAdmin()) {
- $this->settings = InstanceSettings::get();
+ $this->settings = instanceSettings();
$this->do_not_track = $this->settings->do_not_track;
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
$this->is_registration_enabled = $this->settings->is_registration_enabled;
@@ -162,7 +162,7 @@ class Index extends Component
{
CheckForUpdatesJob::dispatchSync();
$this->dispatch('updateAvailable');
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
if ($settings->new_version_available) {
$this->dispatch('success', 'New version available!');
} else {
diff --git a/app/Livewire/Settings/License.php b/app/Livewire/Settings/License.php
index f9402fd7b..ca0c9c1ae 100644
--- a/app/Livewire/Settings/License.php
+++ b/app/Livewire/Settings/License.php
@@ -29,7 +29,7 @@ class License extends Component
abort(404);
}
$this->instance_id = config('app.id');
- $this->settings = \App\Models\InstanceSettings::get();
+ $this->settings = instanceSettings();
}
public function render()
diff --git a/app/Livewire/SettingsBackup.php b/app/Livewire/SettingsBackup.php
index 99b8f8d49..9240aa96d 100644
--- a/app/Livewire/SettingsBackup.php
+++ b/app/Livewire/SettingsBackup.php
@@ -42,7 +42,7 @@ class SettingsBackup extends Component
public function mount()
{
if (isInstanceAdmin()) {
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
$this->database = StandalonePostgresql::whereName('coolify-db')->first();
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
if ($this->database) {
diff --git a/app/Livewire/SettingsEmail.php b/app/Livewire/SettingsEmail.php
index 3eb8ea646..4515df9a7 100644
--- a/app/Livewire/SettingsEmail.php
+++ b/app/Livewire/SettingsEmail.php
@@ -43,7 +43,7 @@ class SettingsEmail extends Component
public function mount()
{
if (isInstanceAdmin()) {
- $this->settings = InstanceSettings::get();
+ $this->settings = instanceSettings();
$this->emails = auth()->user()->email;
} else {
return redirect()->route('dashboard');
diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php
index 75d7fd04a..193b650ff 100644
--- a/app/Livewire/Source/Github/Change.php
+++ b/app/Livewire/Source/Github/Change.php
@@ -99,7 +99,7 @@ class Change extends Component
return redirect()->route('source.all');
}
$this->applications = $this->github_app->applications;
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
$this->name = str($this->github_app->name)->kebab();
diff --git a/app/Livewire/Storage/Create.php b/app/Livewire/Storage/Create.php
index a05834ecc..c5250e1e3 100644
--- a/app/Livewire/Storage/Create.php
+++ b/app/Livewire/Storage/Create.php
@@ -43,15 +43,17 @@ class Create extends Component
'endpoint' => 'Endpoint',
];
- public function mount()
+ public function updatedEndpoint($value)
{
- if (isDev()) {
- $this->name = 'Local MinIO';
- $this->description = 'Local MinIO';
- $this->key = 'minioadmin';
- $this->secret = 'minioadmin';
- $this->bucket = 'local';
- $this->endpoint = 'http://coolify-minio:9000';
+ if (! str($value)->startsWith('https://') && ! str($value)->startsWith('http://')) {
+ $this->endpoint = 'https://'.$value;
+ $value = $this->endpoint;
+ }
+
+ if (str($value)->contains('your-objectstorage.com') && ! isset($this->bucket)) {
+ $this->bucket = str($value)->after('//')->before('.');
+ } elseif (str($value)->contains('your-objectstorage.com')) {
+ $this->bucket = $this->bucket ?: str($value)->after('//')->before('.');
}
}
diff --git a/app/Livewire/Subscription/Index.php b/app/Livewire/Subscription/Index.php
index c278bf58e..df450cf7e 100644
--- a/app/Livewire/Subscription/Index.php
+++ b/app/Livewire/Subscription/Index.php
@@ -23,7 +23,7 @@ class Index extends Component
if (data_get(currentTeam(), 'subscription') && isSubscriptionActive()) {
return redirect()->route('subscription.show');
}
- $this->settings = \App\Models\InstanceSettings::get();
+ $this->settings = instanceSettings();
$this->alreadySubscribed = currentTeam()->subscription()->exists();
}
diff --git a/app/Models/Application.php b/app/Models/Application.php
index 55006745a..e4ab3918a 100644
--- a/app/Models/Application.php
+++ b/app/Models/Application.php
@@ -104,7 +104,7 @@ class Application extends BaseModel
{
use SoftDeletes;
- private static $parserVersion = '3';
+ private static $parserVersion = '4';
protected $guarded = [];
@@ -143,6 +143,9 @@ class Application extends BaseModel
}
$application->tags()->detach();
$application->previews()->delete();
+ foreach ($application->deployment_queue as $deployment) {
+ $deployment->delete();
+ }
});
}
@@ -304,7 +307,7 @@ class Application extends BaseModel
'application_uuid' => data_get($this, 'uuid'),
'task_uuid' => $task_uuid,
]);
- $settings = InstanceSettings::get();
+ $settings = instanceSettings();
if (data_get($settings, 'fqdn')) {
$url = Url::fromString($route);
$url = $url->withPort(null);
@@ -710,6 +713,11 @@ class Application extends BaseModel
return $this->hasMany(ApplicationPreview::class);
}
+ public function deployment_queue()
+ {
+ return $this->hasMany(ApplicationDeploymentQueue::class);
+ }
+
public function destination()
{
return $this->morphTo();
@@ -1150,7 +1158,7 @@ class Application extends BaseModel
public function parse(int $pull_request_id = 0, ?int $preview_id = null)
{
- if ($this->compose_parsing_version === '3') {
+ if ((int) $this->compose_parsing_version >= 3) {
return newParser($this, $pull_request_id, $preview_id);
} elseif ($this->docker_compose_raw) {
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
diff --git a/app/Models/ApplicationDeploymentQueue.php b/app/Models/ApplicationDeploymentQueue.php
index 90d7608cc..c261c30c6 100644
--- a/app/Models/ApplicationDeploymentQueue.php
+++ b/app/Models/ApplicationDeploymentQueue.php
@@ -2,6 +2,7 @@
namespace App\Models;
+use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use OpenApi\Attributes as OA;
@@ -39,6 +40,20 @@ class ApplicationDeploymentQueue extends Model
{
protected $guarded = [];
+ public function application(): Attribute
+ {
+ return Attribute::make(
+ get: fn () => Application::find($this->application_id),
+ );
+ }
+
+ public function server(): Attribute
+ {
+ return Attribute::make(
+ get: fn () => Server::find($this->server_id),
+ );
+ }
+
public function setStatus(string $status)
{
$this->update([
diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php
index 138775aba..9f8e4b342 100644
--- a/app/Models/EnvironmentVariable.php
+++ b/app/Models/EnvironmentVariable.php
@@ -126,11 +126,6 @@ class EnvironmentVariable extends Model
$env = $this->get_real_environment_variables($this->value, $resource);
return data_get($env, 'value', $env);
- if (is_string($env)) {
- return $env;
- }
-
- return $env->value;
}
);
}
diff --git a/app/Models/InstanceSettings.php b/app/Models/InstanceSettings.php
index 27a181ee4..bb3d1478b 100644
--- a/app/Models/InstanceSettings.php
+++ b/app/Models/InstanceSettings.php
@@ -85,4 +85,17 @@ class InstanceSettings extends Model implements SendsEmail
return "[{$instanceName}]";
}
+
+ public function helperVersion(): Attribute
+ {
+ return Attribute::make(
+ get: function ($value) {
+ if (isDev()) {
+ return 'latest';
+ }
+
+ return $value;
+ }
+ );
+ }
}
diff --git a/app/Models/S3Storage.php b/app/Models/S3Storage.php
index 4c7faaa6f..a432a6e9c 100644
--- a/app/Models/S3Storage.php
+++ b/app/Models/S3Storage.php
@@ -40,6 +40,16 @@ class S3Storage extends BaseModel
return "{$this->endpoint}/{$this->bucket}";
}
+ public function isHetzner()
+ {
+ return str($this->endpoint)->contains('your-objectstorage.com');
+ }
+
+ public function isDigitalOcean()
+ {
+ return str($this->endpoint)->contains('digitaloceanspaces.com');
+ }
+
public function testConnection(bool $shouldSave = false)
{
try {
diff --git a/app/Models/Server.php b/app/Models/Server.php
index a1b4c0828..3329c9761 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -268,7 +268,7 @@ respond 404
public function setupDynamicProxyConfiguration()
{
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
$dynamic_config_path = $this->proxyPath().'/dynamic';
if ($this->proxyType() === ProxyTypes::TRAEFIK->value) {
$file = "$dynamic_config_path/coolify.yaml";
@@ -460,15 +460,6 @@ $schema://$host {
public function proxyType()
{
- // $proxyType = $this->proxy->get('type');
- // if ($proxyType === ProxyTypes::NONE->value) {
- // return $proxyType;
- // }
- // if (is_null($proxyType)) {
- // $this->proxy->type = ProxyTypes::TRAEFIK->value;
- // $this->proxy->status = ProxyStatus::EXITED->value;
- // $this->save();
- // }
return data_get($this->proxy, 'type');
}
@@ -1212,4 +1203,18 @@ $schema://$host {
return $this;
}
+
+ public function storageCheck(): ?string
+ {
+ $commands = [
+ 'df / --output=pcent | tr -cd 0-9',
+ ];
+
+ return instant_remote_process($commands, $this, false);
+ }
+
+ public function isIpv6(): bool
+ {
+ return str($this->ip)->contains(':');
+ }
}
diff --git a/app/Models/Service.php b/app/Models/Service.php
index d236869ba..632934524 100644
--- a/app/Models/Service.php
+++ b/app/Models/Service.php
@@ -42,7 +42,7 @@ class Service extends BaseModel
{
use HasFactory, SoftDeletes;
- private static $parserVersion = '3';
+ private static $parserVersion = '4';
protected $guarded = [];
@@ -770,6 +770,30 @@ class Service extends BaseModel
}
$fields->put('Code Server', $data->toArray());
break;
+ case str($image)->contains('elestio/strapi'):
+ $data = collect([]);
+ $license = $this->environment_variables()->where('key', 'STRAPI_LICENSE')->first();
+ if ($license) {
+ $data = $data->merge([
+ 'License' => [
+ 'key' => data_get($license, 'key'),
+ 'value' => data_get($license, 'value'),
+ ],
+ ]);
+ }
+ $nodeEnv = $this->environment_variables()->where('key', 'NODE_ENV')->first();
+ if ($nodeEnv) {
+ $data = $data->merge([
+ 'Node Environment' => [
+ 'key' => data_get($nodeEnv, 'key'),
+ 'value' => data_get($nodeEnv, 'value'),
+ ],
+ ]);
+ }
+
+ $fields->put('Strapi', $data->toArray());
+ break;
+
}
}
$databases = $this->databases()->get();
@@ -1095,7 +1119,21 @@ class Service extends BaseModel
return 3;
});
foreach ($sorted as $env) {
- $commands[] = "echo '{$env->key}={$env->real_value}' >> .env";
+ if (version_compare($env->version, '4.0.0-beta.347', '<=')) {
+ $commands[] = "echo '{$env->key}={$env->real_value}' >> .env";
+ } else {
+ $real_value = $env->real_value;
+ if ($env->version === '4.0.0-beta.239') {
+ $real_value = $env->real_value;
+ } else {
+ if ($env->is_literal || $env->is_multiline) {
+ $real_value = '\''.$real_value.'\'';
+ } else {
+ $real_value = escapeEnvVariables($env->real_value);
+ }
+ }
+ $commands[] = "echo \"{$env->key}={$real_value}\" >> .env";
+ }
}
if ($sorted->count() === 0) {
$commands[] = 'touch .env';
@@ -1105,7 +1143,7 @@ class Service extends BaseModel
public function parse(bool $isNew = false): Collection
{
- if ($this->compose_parsing_version === '3') {
+ if ((int) $this->compose_parsing_version >= 3) {
return newParser($this);
} elseif ($this->docker_compose_raw) {
return parseDockerComposeFile($this, $isNew);
diff --git a/app/Models/ServiceApplication.php b/app/Models/ServiceApplication.php
index d312fab96..0e79e1e2e 100644
--- a/app/Models/ServiceApplication.php
+++ b/app/Models/ServiceApplication.php
@@ -112,4 +112,9 @@ class ServiceApplication extends BaseModel
{
getFilesystemVolumesFromServer($this, $isInit);
}
+
+ public function isBackupSolutionAvailable()
+ {
+ return false;
+ }
}
diff --git a/app/Models/ServiceDatabase.php b/app/Models/ServiceDatabase.php
index 6b96738e8..927527118 100644
--- a/app/Models/ServiceDatabase.php
+++ b/app/Models/ServiceDatabase.php
@@ -115,4 +115,13 @@ class ServiceDatabase extends BaseModel
{
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
}
+
+ public function isBackupSolutionAvailable()
+ {
+ return str($this->databaseType())->contains('mysql') ||
+ str($this->databaseType())->contains('postgres') ||
+ str($this->databaseType())->contains('postgis') ||
+ str($this->databaseType())->contains('mariadb') ||
+ str($this->databaseType())->contains('mongodb');
+ }
}
diff --git a/app/Models/StandaloneClickhouse.php b/app/Models/StandaloneClickhouse.php
index ee5c3becc..e4341b1b9 100644
--- a/app/Models/StandaloneClickhouse.php
+++ b/app/Models/StandaloneClickhouse.php
@@ -294,4 +294,9 @@ class StandaloneClickhouse extends BaseModel
return $parsedCollection->toArray();
}
}
+
+ public function isBackupSolutionAvailable()
+ {
+ return false;
+ }
}
diff --git a/app/Models/StandaloneDragonfly.php b/app/Models/StandaloneDragonfly.php
index 361abf110..94ab2d745 100644
--- a/app/Models/StandaloneDragonfly.php
+++ b/app/Models/StandaloneDragonfly.php
@@ -294,4 +294,9 @@ class StandaloneDragonfly extends BaseModel
return $parsedCollection->toArray();
}
}
+
+ public function isBackupSolutionAvailable()
+ {
+ return false;
+ }
}
diff --git a/app/Models/StandaloneKeydb.php b/app/Models/StandaloneKeydb.php
index e05879371..335c8931c 100644
--- a/app/Models/StandaloneKeydb.php
+++ b/app/Models/StandaloneKeydb.php
@@ -294,4 +294,9 @@ class StandaloneKeydb extends BaseModel
return $parsedCollection->toArray();
}
}
+
+ public function isBackupSolutionAvailable()
+ {
+ return false;
+ }
}
diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php
index c1e6c85d7..c6c08dee5 100644
--- a/app/Models/StandaloneMariadb.php
+++ b/app/Models/StandaloneMariadb.php
@@ -294,4 +294,9 @@ class StandaloneMariadb extends BaseModel
return $parsedCollection->toArray();
}
}
+
+ public function isBackupSolutionAvailable()
+ {
+ return true;
+ }
}
diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php
index e5ed0a5f4..99893b1d1 100644
--- a/app/Models/StandaloneMongodb.php
+++ b/app/Models/StandaloneMongodb.php
@@ -314,4 +314,9 @@ class StandaloneMongodb extends BaseModel
return $parsedCollection->toArray();
}
}
+
+ public function isBackupSolutionAvailable()
+ {
+ return true;
+ }
}
diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php
index bd4a7abb7..f2a5b5c14 100644
--- a/app/Models/StandaloneMysql.php
+++ b/app/Models/StandaloneMysql.php
@@ -295,4 +295,9 @@ class StandaloneMysql extends BaseModel
return $parsedCollection->toArray();
}
}
+
+ public function isBackupSolutionAvailable()
+ {
+ return true;
+ }
}
diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php
index db771c7cd..1b18a5ca7 100644
--- a/app/Models/StandalonePostgresql.php
+++ b/app/Models/StandalonePostgresql.php
@@ -296,4 +296,9 @@ class StandalonePostgresql extends BaseModel
return $parsedCollection->toArray();
}
}
+
+ public function isBackupSolutionAvailable()
+ {
+ return true;
+ }
}
diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php
index c524d4d03..a5868e243 100644
--- a/app/Models/StandaloneRedis.php
+++ b/app/Models/StandaloneRedis.php
@@ -290,4 +290,9 @@ class StandaloneRedis extends BaseModel
return $parsedCollection->toArray();
}
}
+
+ public function isBackupSolutionAvailable()
+ {
+ return false;
+ }
}
diff --git a/app/Notifications/Channels/TransactionalEmailChannel.php b/app/Notifications/Channels/TransactionalEmailChannel.php
index 549fc6cd3..cc7d76ebf 100644
--- a/app/Notifications/Channels/TransactionalEmailChannel.php
+++ b/app/Notifications/Channels/TransactionalEmailChannel.php
@@ -13,7 +13,7 @@ class TransactionalEmailChannel
{
public function send(User $notifiable, Notification $notification): void
{
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
if (! data_get($settings, 'smtp_enabled') && ! data_get($settings, 'resend_enabled')) {
Log::info('SMTP/Resend not enabled');
diff --git a/app/Notifications/TransactionalEmails/ResetPassword.php b/app/Notifications/TransactionalEmails/ResetPassword.php
index 8b1c02d39..3938a8da7 100644
--- a/app/Notifications/TransactionalEmails/ResetPassword.php
+++ b/app/Notifications/TransactionalEmails/ResetPassword.php
@@ -18,7 +18,7 @@ class ResetPassword extends Notification
public function __construct($token)
{
- $this->settings = \App\Models\InstanceSettings::get();
+ $this->settings = instanceSettings();
$this->token = $token;
}
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index cd90918ad..8b4c2eef2 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -2,10 +2,8 @@
namespace App\Providers;
-use App\Models\InstanceSettings;
use App\Models\PersonalAccessToken;
use Illuminate\Support\Facades\Http;
-use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Laravel\Sanctum\Sanctum;
@@ -30,9 +28,5 @@ class AppServiceProvider extends ServiceProvider
])->baseUrl($api_url);
}
});
- // if (! env('CI')) {
- // View::share('instanceSettings', InstanceSettings::get());
- // }
-
}
}
diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php
index 53a2e9281..b916b6234 100644
--- a/app/Providers/FortifyServiceProvider.php
+++ b/app/Providers/FortifyServiceProvider.php
@@ -46,7 +46,7 @@ class FortifyServiceProvider extends ServiceProvider
Fortify::registerView(function () {
$isFirstUser = User::count() === 0;
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
if (! $settings->is_registration_enabled) {
return redirect()->route('login');
}
@@ -60,7 +60,7 @@ class FortifyServiceProvider extends ServiceProvider
});
Fortify::loginView(function () {
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
$enabled_oauth_providers = OauthSetting::where('enabled', true)->get();
$users = User::count();
if ($users == 0) {
diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php
index 8e14ef9ee..006b095cf 100644
--- a/bootstrap/helpers/api.php
+++ b/bootstrap/helpers/api.php
@@ -175,4 +175,5 @@ function removeUnnecessaryFieldsFromRequest(Request $request)
$request->offsetUnset('instant_deploy');
$request->offsetUnset('github_app_uuid');
$request->offsetUnset('private_key_uuid');
+ $request->offsetUnset('use_build_server');
}
diff --git a/bootstrap/helpers/constants.php b/bootstrap/helpers/constants.php
index 1eeec8f94..d8dc26a48 100644
--- a/bootstrap/helpers/constants.php
+++ b/bootstrap/helpers/constants.php
@@ -20,12 +20,16 @@ const RESTART_MODE = 'unless-stopped';
const DATABASE_DOCKER_IMAGES = [
'bitnami/mariadb',
'bitnami/mongodb',
- 'bitnami/mysql',
- 'bitnami/postgresql',
'bitnami/redis',
'mysql',
+ 'bitnami/mysql',
+ 'mysql/mysql-server',
'mariadb',
+ 'postgis/postgis',
'postgres',
+ 'bitnami/postgresql',
+ 'supabase/postgres',
+ 'elestio/postgres',
'mongo',
'redis',
'memcached',
@@ -33,7 +37,6 @@ const DATABASE_DOCKER_IMAGES = [
'neo4j',
'influxdb',
'clickhouse/clickhouse-server',
- 'supabase/postgres',
];
const SPECIFIC_SERVICES = [
'quay.io/minio/minio',
diff --git a/bootstrap/helpers/s3.php b/bootstrap/helpers/s3.php
index 4a2252016..2ee7bf44a 100644
--- a/bootstrap/helpers/s3.php
+++ b/bootstrap/helpers/s3.php
@@ -1,14 +1,11 @@
endpoint) {
- $is_digital_ocean = Str::contains($s3->endpoint, 'digitaloceanspaces.com');
- }
+
config()->set('filesystems.disks.custom-s3', [
'driver' => 's3',
'region' => $s3['region'],
@@ -17,7 +14,7 @@ function set_s3_target(S3Storage $s3)
'bucket' => $s3['bucket'],
'endpoint' => $s3['endpoint'],
'use_path_style_endpoint' => true,
- 'bucket_endpoint' => $is_digital_ocean,
+ 'bucket_endpoint' => $s3->isHetzner() || $s3->isDigitalOcean(),
'aws_url' => $s3->awsUrl(),
]);
}
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 350f16837..e13f019e3 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -247,7 +247,7 @@ function is_transactional_emails_active(): bool
function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string
{
if (! $settings) {
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
}
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
@@ -281,7 +281,7 @@ function base_ip(): string
if (isDev()) {
return 'localhost';
}
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
if ($settings->public_ipv4) {
return "$settings->public_ipv4";
}
@@ -309,7 +309,7 @@ function getFqdnWithoutPort(string $fqdn)
*/
function base_url(bool $withPort = true): string
{
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
if ($settings->fqdn) {
return $settings->fqdn;
}
@@ -343,6 +343,11 @@ function isSubscribed()
{
return isSubscriptionActive() || auth()->user()->isInstanceAdmin();
}
+
+function isProduction(): bool
+{
+ return ! isDev();
+}
function isDev(): bool
{
return config('app.env') === 'local';
@@ -384,7 +389,7 @@ function send_internal_notification(string $message): void
}
function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null): void
{
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
$type = set_transanctional_email_settings($settings);
if (! $type) {
throw new Exception('No email settings found.');
@@ -703,7 +708,9 @@ function getTopLevelNetworks(Service|Application $resource)
return $value == $networkName || $key == $networkName;
});
if (! $networkExists) {
- $topLevelNetworks->put($networkDetails, null);
+ if (is_string($networkDetails) || is_int($networkDetails)) {
+ $topLevelNetworks->put($networkDetails, null);
+ }
}
}
}
@@ -753,7 +760,9 @@ function getTopLevelNetworks(Service|Application $resource)
return $value == $networkName || $key == $networkName;
});
if (! $networkExists) {
- $topLevelNetworks->put($networkDetails, null);
+ if (is_string($networkDetails) || is_int($networkDetails)) {
+ $topLevelNetworks->put($networkDetails, null);
+ }
}
}
}
@@ -819,6 +828,31 @@ function convertToArray($collection)
return $collection;
}
+function parseCommandFromMagicEnvVariable(Str|string $key): Stringable
+{
+ $value = str($key);
+ $count = substr_count($value->value(), '_');
+ if ($count === 2) {
+ if ($value->startsWith('SERVICE_FQDN') || $value->startsWith('SERVICE_URL')) {
+ // SERVICE_FQDN_UMAMI
+ $command = $value->after('SERVICE_')->beforeLast('_');
+ } else {
+ // SERVICE_BASE64_UMAMI
+ $command = $value->after('SERVICE_')->beforeLast('_');
+ }
+ }
+ if ($count === 3) {
+ if ($value->startsWith('SERVICE_FQDN') || $value->startsWith('SERVICE_URL')) {
+ // SERVICE_FQDN_UMAMI_1000
+ $command = $value->after('SERVICE_')->before('_');
+ } else {
+ // SERVICE_BASE64_64_UMAMI
+ $command = $value->after('SERVICE_')->beforeLast('_');
+ }
+ }
+
+ return str($command);
+}
function parseEnvVariable(Str|string $value)
{
$value = str($value);
@@ -850,6 +884,7 @@ function parseEnvVariable(Str|string $value)
} else {
// SERVICE_BASE64_64_UMAMI
$command = $value->after('SERVICE_')->beforeLast('_');
+ ray($command);
}
}
}
@@ -970,7 +1005,7 @@ function validate_dns_entry(string $fqdn, Server $server)
if (str($host)->contains('sslip.io')) {
return true;
}
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
$is_dns_validation_enabled = data_get($settings, 'is_dns_validation_enabled');
if (! $is_dns_validation_enabled) {
return true;
@@ -1090,7 +1125,7 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId =
if ($domainFound) {
return true;
}
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
if (data_get($settings, 'fqdn')) {
$domain = data_get($settings, 'fqdn');
if (str($domain)->endsWith('/')) {
@@ -1162,7 +1197,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
}
}
if ($resource) {
- $settings = \App\Models\InstanceSettings::get();
+ $settings = instanceSettings();
if (data_get($settings, 'fqdn')) {
$domain = data_get($settings, 'fqdn');
if (str($domain)->endsWith('/')) {
@@ -1179,12 +1214,26 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
function parseCommandsByLineForSudo(Collection $commands, Server $server): array
{
$commands = $commands->map(function ($line) {
- if (! str($line)->startsWith('cd') && ! str($line)->startsWith('command') && ! str($line)->startsWith('echo') && ! str($line)->startsWith('true')) {
+ if (
+ ! str(trim($line))->startsWith([
+ 'cd',
+ 'command',
+ 'echo',
+ 'true',
+ 'if',
+ 'fi',
+ ])
+ ) {
return "sudo $line";
}
+ if (str(trim($line))->startsWith('if')) {
+ return str_replace('if', 'if sudo', $line);
+ }
+
return $line;
});
+
$commands = $commands->map(function ($line) use ($server) {
if (Str::startsWith($line, 'sudo mkdir -p')) {
return "$line && sudo chown -R $server->user:$server->user ".Str::after($line, 'sudo mkdir -p').' && sudo chmod -R o-rwx '.Str::after($line, 'sudo mkdir -p');
@@ -1192,6 +1241,7 @@ function parseCommandsByLineForSudo(Collection $commands, Server $server): array
return $line;
});
+
$commands = $commands->map(function ($line) {
$line = str($line);
if (str($line)->contains('$(')) {
@@ -1236,8 +1286,6 @@ function parseLineForSudo(string $command, Server $server): string
function get_public_ips()
{
try {
- echo "Refreshing public ips!\n";
- $settings = \App\Models\InstanceSettings::get();
[$first, $second] = Process::concurrently(function (Pool $pool) {
$pool->path(__DIR__)->command('curl -4s https://ifconfig.io');
$pool->path(__DIR__)->command('curl -6s https://ifconfig.io');
@@ -1251,7 +1299,7 @@ function get_public_ips()
return;
}
- $settings->update(['public_ipv4' => $ipv4]);
+ InstanceSettings::get()->update(['public_ipv4' => $ipv4]);
}
} catch (\Exception $e) {
echo "Error: {$e->getMessage()}\n";
@@ -1266,7 +1314,7 @@ function get_public_ips()
return;
}
- $settings->update(['public_ipv6' => $ipv6]);
+ InstanceSettings::get()->update(['public_ipv6' => $ipv6]);
}
} catch (\Throwable $e) {
echo "Error: {$e->getMessage()}\n";
@@ -1590,7 +1638,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
return $value == $networkName || $key == $networkName;
});
if (! $networkExists) {
- $topLevelNetworks->put($networkDetails, null);
+ if (is_string($networkDetails) || is_int($networkDetails)) {
+ $topLevelNetworks->put($networkDetails, null);
+ }
}
}
}
@@ -2505,7 +2555,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
return $value == $networkName || $key == $networkName;
});
if (! $networkExists) {
- $topLevelNetworks->put($networkDetails, null);
+ if (is_string($networkDetails) || is_int($networkDetails)) {
+ $topLevelNetworks->put($networkDetails, null);
+ }
}
}
}
@@ -2966,11 +3018,22 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$predefinedPort = '8000';
}
if ($isDatabase) {
- $savedService = ServiceDatabase::firstOrCreate([
- 'name' => $serviceName,
- 'image' => $image,
- 'service_id' => $resource->id,
- ]);
+ $applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first();
+ if ($applicationFound) {
+ $savedService = $applicationFound;
+ $savedService = ServiceDatabase::firstOrCreate([
+ 'name' => $applicationFound->name,
+ 'image' => $applicationFound->image,
+ 'service_id' => $applicationFound->service_id,
+ ]);
+ $applicationFound->delete();
+ } else {
+ $savedService = ServiceDatabase::firstOrCreate([
+ 'name' => $serviceName,
+ 'image' => $image,
+ 'service_id' => $resource->id,
+ ]);
+ }
} else {
$savedService = ServiceApplication::firstOrCreate([
'name' => $serviceName,
@@ -3080,7 +3143,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
foreach ($magicEnvironments as $key => $value) {
$key = str($key);
$value = replaceVariables($value);
- $command = $key->after('SERVICE_')->before('_');
+ $command = parseCommandFromMagicEnvVariable($key);
$found = $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->first();
if ($found) {
continue;
@@ -3191,12 +3254,24 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
if ($serviceName === 'plausible') {
$predefinedPort = '8000';
}
+
if ($isDatabase) {
- $savedService = ServiceDatabase::firstOrCreate([
- 'name' => $serviceName,
- 'image' => $image,
- 'service_id' => $resource->id,
- ]);
+ $applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first();
+ if ($applicationFound) {
+ $savedService = $applicationFound;
+ $savedService = ServiceDatabase::firstOrCreate([
+ 'name' => $applicationFound->name,
+ 'image' => $applicationFound->image,
+ 'service_id' => $applicationFound->service_id,
+ ]);
+ $applicationFound->delete();
+ } else {
+ $savedService = ServiceDatabase::firstOrCreate([
+ 'name' => $serviceName,
+ 'image' => $image,
+ 'service_id' => $resource->id,
+ ]);
+ }
} else {
$savedService = ServiceApplication::firstOrCreate([
'name' => $serviceName,
@@ -3266,7 +3341,15 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
} elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') {
$volume = $source->value().':'.$target->value();
} else {
- $mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
+ if ((int) $resource->compose_parsing_version >= 4) {
+ if ($isApplication) {
+ $mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
+ } elseif ($isService) {
+ $mainDirectory = str(base_configuration_dir().'/services/'.$uuid);
+ }
+ } else {
+ $mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
+ }
$source = replaceLocalSource($source, $mainDirectory);
if ($isApplication && $isPullRequest) {
$source = $source."-pr-$pullRequestId";
@@ -3286,6 +3369,17 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
'resource_type' => get_class($originalResource),
]
);
+ if (isDev()) {
+ if ((int) $resource->compose_parsing_version >= 4) {
+ if ($isApplication) {
+ $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid);
+ } elseif ($isService) {
+ $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/services/'.$uuid);
+ }
+ } else {
+ $source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid);
+ }
+ }
$volume = "$source:$target";
}
} elseif ($type->value() === 'volume') {
@@ -3828,14 +3922,37 @@ function convertComposeEnvironmentToArray($environment)
{
$convertedServiceVariables = collect([]);
if (isAssociativeArray($environment)) {
+ // Example: $environment = ['FOO' => 'bar', 'BAZ' => 'qux'];
+ if ($environment instanceof Collection) {
+ $changedEnvironment = collect([]);
+ $environment->each(function ($value, $key) use ($changedEnvironment) {
+ if (is_numeric($key)) {
+ $parts = explode('=', $value, 2);
+ if (count($parts) === 2) {
+ $key = $parts[0];
+ $realValue = $parts[1] ?? '';
+ $changedEnvironment->put($key, $realValue);
+ } else {
+ $changedEnvironment->put($key, $value);
+ }
+ } else {
+ $changedEnvironment->put($key, $value);
+ }
+ });
+
+ return $changedEnvironment;
+ }
$convertedServiceVariables = $environment;
} else {
+ // Example: $environment = ['FOO=bar', 'BAZ=qux'];
foreach ($environment as $value) {
- $parts = explode('=', $value, 2);
- $key = $parts[0];
- $realValue = $parts[1] ?? '';
- if ($key) {
- $convertedServiceVariables->put($key, $realValue);
+ if (is_string($value)) {
+ $parts = explode('=', $value, 2);
+ $key = $parts[0];
+ $realValue = $parts[1] ?? '';
+ if ($key) {
+ $convertedServiceVariables->put($key, $realValue);
+ }
}
}
}
@@ -3843,3 +3960,7 @@ function convertComposeEnvironmentToArray($environment)
return $convertedServiceVariables;
}
+function instanceSettings()
+{
+ return InstanceSettings::get();
+}
diff --git a/composer.json b/composer.json
index 17432c532..03adf9823 100644
--- a/composer.json
+++ b/composer.json
@@ -48,6 +48,7 @@
"zircote/swagger-php": "^4.10"
},
"require-dev": {
+ "barryvdh/laravel-debugbar": "^3.13",
"fakerphp/faker": "^v1.21.0",
"laravel/dusk": "^v8.0",
"laravel/pint": "^1.16",
diff --git a/composer.lock b/composer.lock
index fffb320d3..420d87ec0 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "96f8146407d0e6e897ff097c5eccd3a4",
+ "content-hash": "42c28ab141b70fcabf75b51afa96c670",
"packages": [
{
"name": "amphp/amp",
@@ -11823,6 +11823,90 @@
}
],
"packages-dev": [
+ {
+ "name": "barryvdh/laravel-debugbar",
+ "version": "v3.13.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/barryvdh/laravel-debugbar.git",
+ "reference": "92d86be45ee54edff735e46856f64f14b6a8bb07"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/92d86be45ee54edff735e46856f64f14b6a8bb07",
+ "reference": "92d86be45ee54edff735e46856f64f14b6a8bb07",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/routing": "^9|^10|^11",
+ "illuminate/session": "^9|^10|^11",
+ "illuminate/support": "^9|^10|^11",
+ "maximebf/debugbar": "~1.22.0",
+ "php": "^8.0",
+ "symfony/finder": "^6|^7"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.3.3",
+ "orchestra/testbench-dusk": "^5|^6|^7|^8|^9",
+ "phpunit/phpunit": "^9.6|^10.5",
+ "squizlabs/php_codesniffer": "^3.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.13-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Barryvdh\\Debugbar\\ServiceProvider"
+ ],
+ "aliases": {
+ "Debugbar": "Barryvdh\\Debugbar\\Facades\\Debugbar"
+ }
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/helpers.php"
+ ],
+ "psr-4": {
+ "Barryvdh\\Debugbar\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Barry vd. Heuvel",
+ "email": "barryvdh@gmail.com"
+ }
+ ],
+ "description": "PHP Debugbar integration for Laravel",
+ "keywords": [
+ "debug",
+ "debugbar",
+ "laravel",
+ "profiler",
+ "webprofiler"
+ ],
+ "support": {
+ "issues": "https://github.com/barryvdh/laravel-debugbar/issues",
+ "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.13.5"
+ },
+ "funding": [
+ {
+ "url": "https://fruitcake.nl",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/barryvdh",
+ "type": "github"
+ }
+ ],
+ "time": "2024-04-12T11:20:37+00:00"
+ },
{
"name": "brianium/paratest",
"version": "v7.4.3",
@@ -12301,6 +12385,74 @@
},
"time": "2024-09-03T15:00:28+00:00"
},
+ {
+ "name": "maximebf/debugbar",
+ "version": "v1.22.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/maximebf/php-debugbar.git",
+ "reference": "1b5cabe0ce013134cf595bfa427bbf2f6abcd989"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/1b5cabe0ce013134cf595bfa427bbf2f6abcd989",
+ "reference": "1b5cabe0ce013134cf595bfa427bbf2f6abcd989",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2|^8",
+ "psr/log": "^1|^2|^3",
+ "symfony/var-dumper": "^4|^5|^6|^7"
+ },
+ "require-dev": {
+ "dbrekelmans/bdi": "^1",
+ "phpunit/phpunit": "^8|^9",
+ "symfony/panther": "^1|^2.1",
+ "twig/twig": "^1.38|^2.7|^3.0"
+ },
+ "suggest": {
+ "kriswallsmith/assetic": "The best way to manage assets",
+ "monolog/monolog": "Log using Monolog",
+ "predis/predis": "Redis storage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.22-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "DebugBar\\": "src/DebugBar/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Maxime Bouroumeau-Fuseau",
+ "email": "maxime.bouroumeau@gmail.com",
+ "homepage": "http://maximebf.com"
+ },
+ {
+ "name": "Barry vd. Heuvel",
+ "email": "barryvdh@gmail.com"
+ }
+ ],
+ "description": "Debug bar in the browser for php application",
+ "homepage": "https://github.com/maximebf/php-debugbar",
+ "keywords": [
+ "debug",
+ "debugbar"
+ ],
+ "support": {
+ "issues": "https://github.com/maximebf/php-debugbar/issues",
+ "source": "https://github.com/maximebf/php-debugbar/tree/v1.22.5"
+ },
+ "time": "2024-09-09T08:05:55+00:00"
+ },
{
"name": "mockery/mockery",
"version": "1.6.12",
diff --git a/config/debugbar.php b/config/debugbar.php
new file mode 100644
index 000000000..eae406ba7
--- /dev/null
+++ b/config/debugbar.php
@@ -0,0 +1,325 @@
+ env('DEBUGBAR_ENABLED', null),
+ 'except' => [
+ 'telescope*',
+ 'horizon*',
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Storage settings
+ |--------------------------------------------------------------------------
+ |
+ | DebugBar stores data for session/ajax requests.
+ | You can disable this, so the debugbar stores data in headers/session,
+ | but this can cause problems with large data collectors.
+ | By default, file storage (in the storage folder) is used. Redis and PDO
+ | can also be used. For PDO, run the package migrations first.
+ |
+ | Warning: Enabling storage.open will allow everyone to access previous
+ | request, do not enable open storage in publicly available environments!
+ | Specify a callback if you want to limit based on IP or authentication.
+ | Leaving it to null will allow localhost only.
+ */
+ 'storage' => [
+ 'enabled' => true,
+ 'open' => env('DEBUGBAR_OPEN_STORAGE'), // bool/callback.
+ 'driver' => 'file', // redis, file, pdo, socket, custom
+ 'path' => storage_path('debugbar'), // For file driver
+ 'connection' => null, // Leave null for default connection (Redis/PDO)
+ 'provider' => '', // Instance of StorageInterface for custom driver
+ 'hostname' => '127.0.0.1', // Hostname to use with the "socket" driver
+ 'port' => 2304, // Port to use with the "socket" driver
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Editor
+ |--------------------------------------------------------------------------
+ |
+ | Choose your preferred editor to use when clicking file name.
+ |
+ | Supported: "phpstorm", "vscode", "vscode-insiders", "vscode-remote",
+ | "vscode-insiders-remote", "vscodium", "textmate", "emacs",
+ | "sublime", "atom", "nova", "macvim", "idea", "netbeans",
+ | "xdebug", "espresso"
+ |
+ */
+
+ 'editor' => env('DEBUGBAR_EDITOR') ?: env('IGNITION_EDITOR', 'phpstorm'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Remote Path Mapping
+ |--------------------------------------------------------------------------
+ |
+ | If you are using a remote dev server, like Laravel Homestead, Docker, or
+ | even a remote VPS, it will be necessary to specify your path mapping.
+ |
+ | Leaving one, or both of these, empty or null will not trigger the remote
+ | URL changes and Debugbar will treat your editor links as local files.
+ |
+ | "remote_sites_path" is an absolute base path for your sites or projects
+ | in Homestead, Vagrant, Docker, or another remote development server.
+ |
+ | Example value: "/home/vagrant/Code"
+ |
+ | "local_sites_path" is an absolute base path for your sites or projects
+ | on your local computer where your IDE or code editor is running on.
+ |
+ | Example values: "/Users//Code", "C:\Users\\Documents\Code"
+ |
+ */
+
+ 'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH'),
+ 'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', env('IGNITION_LOCAL_SITES_PATH')),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Vendors
+ |--------------------------------------------------------------------------
+ |
+ | Vendor files are included by default, but can be set to false.
+ | This can also be set to 'js' or 'css', to only include javascript or css vendor files.
+ | Vendor files are for css: font-awesome (including fonts) and highlight.js (css files)
+ | and for js: jquery and highlight.js
+ | So if you want syntax highlighting, set it to true.
+ | jQuery is set to not conflict with existing jQuery scripts.
+ |
+ */
+
+ 'include_vendors' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Capture Ajax Requests
+ |--------------------------------------------------------------------------
+ |
+ | The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors),
+ | you can use this option to disable sending the data through the headers.
+ |
+ | Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools.
+ |
+ | Note for your request to be identified as ajax requests they must either send the header
+ | X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header.
+ |
+ | By default `ajax_handler_auto_show` is set to true allowing ajax requests to be shown automatically in the Debugbar.
+ | Changing `ajax_handler_auto_show` to false will prevent the Debugbar from reloading.
+ */
+
+ 'capture_ajax' => true,
+ 'add_ajax_timing' => false,
+ 'ajax_handler_auto_show' => true,
+ 'ajax_handler_enable_tab' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Error Handler for Deprecated warnings
+ |--------------------------------------------------------------------------
+ |
+ | When enabled, the Debugbar shows deprecated warnings for Symfony components
+ | in the Messages tab.
+ |
+ */
+ 'error_handler' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Clockwork integration
+ |--------------------------------------------------------------------------
+ |
+ | The Debugbar can emulate the Clockwork headers, so you can use the Chrome
+ | Extension, without the server-side code. It uses Debugbar collectors instead.
+ |
+ */
+ 'clockwork' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | DataCollectors
+ |--------------------------------------------------------------------------
+ |
+ | Enable/disable DataCollectors
+ |
+ */
+
+ 'collectors' => [
+ 'phpinfo' => true, // Php version
+ 'messages' => true, // Messages
+ 'time' => true, // Time Datalogger
+ 'memory' => true, // Memory usage
+ 'exceptions' => true, // Exception displayer
+ 'log' => true, // Logs from Monolog (merged in messages if enabled)
+ 'db' => true, // Show database (PDO) queries and bindings
+ 'views' => true, // Views with their data
+ 'route' => true, // Current route information
+ 'auth' => false, // Display Laravel authentication status
+ 'gate' => true, // Display Laravel Gate checks
+ 'session' => true, // Display session data
+ 'symfony_request' => true, // Only one can be enabled..
+ 'mail' => true, // Catch mail messages
+ 'laravel' => false, // Laravel version and environment
+ 'events' => false, // All events fired
+ 'default_request' => false, // Regular or special Symfony request logger
+ 'logs' => false, // Add the latest log messages
+ 'files' => false, // Show the included files
+ 'config' => false, // Display config settings
+ 'cache' => false, // Display cache events
+ 'models' => true, // Display models
+ 'livewire' => true, // Display Livewire (when available)
+ 'jobs' => false, // Display dispatched jobs
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Extra options
+ |--------------------------------------------------------------------------
+ |
+ | Configure some DataCollectors
+ |
+ */
+
+ 'options' => [
+ 'time' => [
+ 'memory_usage' => false, // Calculated by subtracting memory start and end, it may be inaccurate
+ ],
+ 'messages' => [
+ 'trace' => true, // Trace the origin of the debug message
+ ],
+ 'memory' => [
+ 'reset_peak' => false, // run memory_reset_peak_usage before collecting
+ 'with_baseline' => false, // Set boot memory usage as memory peak baseline
+ 'precision' => 0, // Memory rounding precision
+ ],
+ 'auth' => [
+ 'show_name' => true, // Also show the users name/email in the debugbar
+ 'show_guards' => true, // Show the guards that are used
+ ],
+ 'db' => [
+ 'with_params' => true, // Render SQL with the parameters substituted
+ 'backtrace' => true, // Use a backtrace to find the origin of the query in your files.
+ 'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults)
+ 'timeline' => false, // Add the queries to the timeline
+ 'duration_background' => true, // Show shaded background on each query relative to how long it took to execute.
+ 'explain' => [ // Show EXPLAIN output on queries
+ 'enabled' => false,
+ 'types' => ['SELECT'], // Deprecated setting, is always only SELECT
+ ],
+ 'hints' => false, // Show hints for common mistakes
+ 'show_copy' => false, // Show copy button next to the query,
+ 'slow_threshold' => false, // Only track queries that last longer than this time in ms
+ 'memory_usage' => false, // Show queries memory usage
+ 'soft_limit' => 100, // After the soft limit, no parameters/backtrace are captured
+ 'hard_limit' => 500, // After the hard limit, queries are ignored
+ ],
+ 'mail' => [
+ 'timeline' => false, // Add mails to the timeline
+ 'show_body' => true,
+ ],
+ 'views' => [
+ 'timeline' => false, // Add the views to the timeline (Experimental)
+ 'data' => false, //true for all data, 'keys' for only names, false for no parameters.
+ 'group' => 50, // Group duplicate views. Pass value to auto-group, or true/false to force
+ 'exclude_paths' => [ // Add the paths which you don't want to appear in the views
+ 'vendor/filament', // Exclude Filament components by default
+ ],
+ ],
+ 'route' => [
+ 'label' => true, // show complete route on bar
+ ],
+ 'session' => [
+ 'hiddens' => [], // hides sensitive values using array paths
+ ],
+ 'symfony_request' => [
+ 'hiddens' => [], // hides sensitive values using array paths, example: request_request.password
+ ],
+ 'events' => [
+ 'data' => false, // collect events data, listeners
+ ],
+ 'logs' => [
+ 'file' => null,
+ ],
+ 'cache' => [
+ 'values' => true, // collect cache values
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Inject Debugbar in Response
+ |--------------------------------------------------------------------------
+ |
+ | Usually, the debugbar is added just before