diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index 99206606d..e106c1801 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -15,6 +15,9 @@ class StartProxy { try { $proxyType = $server->proxyType(); + if ($proxyType === 'NONE') { + return 'OK'; + } $commands = collect([]); $proxy_path = get_proxy_path(); $configuration = CheckConfiguration::run($server); diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index f010c33b6..2584df48b 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -77,6 +77,9 @@ class Handler extends ExceptionHandler ); } ); + if (str($e->getMessage())->contains('No space left on device')) { + return; + } ray('reporting to sentry'); Integration::captureUnhandledException($e); }); diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 8ecaacc98..0dedfd596 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -374,6 +374,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted $this->cleanup_git(); $this->application->loadComposeFile(isInit: false); if ($this->application->settings->is_raw_compose_deployment_enabled) { + $this->application->parseRawCompose(); $yaml = $composeFile = $this->application->docker_compose_raw; } else { $composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id); @@ -413,16 +414,33 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted ]); } $this->write_deployment_configurations(); + // Start compose file - if ($this->docker_compose_custom_start_command) { - $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true], - ); + if ($this->application->settings->is_raw_compose_deployment_enabled) { + if ($this->docker_compose_custom_start_command) { + $this->execute_remote_command( + ["cd {$this->basedir} && {$this->docker_compose_custom_start_command}", "hidden" => true], + ); + } else { + $server_workdir = $this->application->workdir(); + ray("SOURCE_COMMIT={$this->commit} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d"); + $this->execute_remote_command( + ["SOURCE_COMMIT={$this->commit} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d", "hidden" => true], + ); + } } else { - $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true], - ); + if ($this->docker_compose_custom_start_command) { + $this->execute_remote_command( + [executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true], + ); + } else { + $this->execute_remote_command( + [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true], + ); + } } + + $this->application_deployment_queue->addLogEntry("New container started."); } private function deploy_dockerfile_buildpack() diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index 8a854128a..aa4b2fa7f 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -51,6 +51,9 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted { $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; @@ -316,7 +319,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted private function backup_standalone_mongodb(string $databaseWithCollections): void { try { - $url = $this->database->getDbUrl(useInternal: true); + $url = $this->database->get_db_url(useInternal: true); if ($databaseWithCollections === 'all') { $commands[] = "mkdir -p " . $this->backup_dir; $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location"; diff --git a/app/Livewire/Project/Application/Advanced.php b/app/Livewire/Project/Application/Advanced.php index 08b4f9523..278cab5a1 100644 --- a/app/Livewire/Project/Application/Advanced.php +++ b/app/Livewire/Project/Application/Advanced.php @@ -9,6 +9,8 @@ class Advanced extends Component { public Application $application; public bool $is_force_https_enabled; + public bool $is_gzip_enabled; + public bool $is_stripprefix_enabled; protected $rules = [ 'application.settings.is_git_submodules_enabled' => 'boolean|required', 'application.settings.is_git_lfs_enabled' => 'boolean|required', @@ -19,13 +21,17 @@ class Advanced extends Component 'application.settings.is_gpu_enabled' => 'boolean|required', 'application.settings.is_build_server_enabled' => 'boolean|required', 'application.settings.is_consistent_container_name_enabled' => 'boolean|required', + 'application.settings.is_gzip_enabled' => 'boolean|required', + 'application.settings.is_stripprefix_enabled' => 'boolean|required', 'application.settings.gpu_driver' => 'string|required', 'application.settings.gpu_count' => 'string|required', 'application.settings.gpu_device_ids' => 'string|required', 'application.settings.gpu_options' => 'string|required', ]; public function mount() { - $this->is_force_https_enabled = $this->application->settings->is_force_https_enabled; + $this->is_force_https_enabled = $this->application->isForceHttpsEnabled(); + $this->is_gzip_enabled = $this->application->isGzipEnabled(); + $this->is_stripprefix_enabled = $this->application->isStripprefixEnabled(); } public function instantSave() { @@ -40,6 +46,14 @@ class Advanced extends Component $this->application->settings->is_force_https_enabled = $this->is_force_https_enabled; $this->dispatch('resetDefaultLabels', false); } + if ($this->application->settings->is_gzip_enabled !== $this->is_gzip_enabled) { + $this->application->settings->is_gzip_enabled = $this->is_gzip_enabled; + $this->dispatch('resetDefaultLabels', false); + } + if ($this->application->settings->is_stripprefix_enabled !== $this->is_stripprefix_enabled) { + $this->application->settings->is_stripprefix_enabled = $this->is_stripprefix_enabled; + $this->dispatch('resetDefaultLabels', false); + } $this->application->settings->save(); $this->dispatch('success', 'Settings saved.'); } diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index b3c565fdd..3279ff2d5 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -263,7 +263,11 @@ class General extends Component } if ($this->application->build_pack === 'dockercompose') { $this->application->docker_compose_domains = json_encode($this->parsedServiceDomains); - $this->parsedServices = $this->application->parseCompose(); + if ($this->application->settings->is_raw_compose_deployment_enabled) { + $this->application->parseRawCompose(); + } else { + $this->parsedServices = $this->application->parseCompose(); + } } $this->application->custom_labels = base64_encode($this->customLabels); $this->application->save(); diff --git a/app/Livewire/Project/Database/Mariadb/General.php b/app/Livewire/Project/Database/Mariadb/General.php index 9b15c6c3c..a4b6e1883 100644 --- a/app/Livewire/Project/Database/Mariadb/General.php +++ b/app/Livewire/Project/Database/Mariadb/General.php @@ -46,9 +46,9 @@ class General extends Component public function mount() { - $this->db_url = $this->database->getDbUrl(true); + $this->db_url = $this->database->get_db_url(true); if ($this->database->is_public) { - $this->db_url_public = $this->database->getDbUrl(); + $this->db_url_public = $this->database->get_db_url(); } } public function instantSaveAdvanced() { @@ -93,7 +93,7 @@ class General extends Component return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->getDbUrl(); + $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); diff --git a/app/Livewire/Project/Database/Mongodb/General.php b/app/Livewire/Project/Database/Mongodb/General.php index cd3ea0630..c1b55b1e7 100644 --- a/app/Livewire/Project/Database/Mongodb/General.php +++ b/app/Livewire/Project/Database/Mongodb/General.php @@ -44,9 +44,9 @@ class General extends Component public function mount() { - $this->db_url = $this->database->getDbUrl(true); + $this->db_url = $this->database->get_db_url(true); if ($this->database->is_public) { - $this->db_url_public = $this->database->getDbUrl(); + $this->db_url_public = $this->database->get_db_url(); } } public function instantSaveAdvanced() @@ -95,7 +95,7 @@ class General extends Component return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->getDbUrl(); + $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); diff --git a/app/Livewire/Project/Database/Mysql/General.php b/app/Livewire/Project/Database/Mysql/General.php index 2470a934b..cab11e8ce 100644 --- a/app/Livewire/Project/Database/Mysql/General.php +++ b/app/Livewire/Project/Database/Mysql/General.php @@ -46,9 +46,9 @@ class General extends Component public function mount() { - $this->db_url = $this->database->getDbUrl(true); + $this->db_url = $this->database->get_db_url(true); if ($this->database->is_public) { - $this->db_url_public = $this->database->getDbUrl(); + $this->db_url_public = $this->database->get_db_url(); } } public function instantSaveAdvanced() @@ -94,7 +94,7 @@ class General extends Component return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->getDbUrl(); + $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); diff --git a/app/Livewire/Project/Database/Postgresql/General.php b/app/Livewire/Project/Database/Postgresql/General.php index 43f559140..3237c1be3 100644 --- a/app/Livewire/Project/Database/Postgresql/General.php +++ b/app/Livewire/Project/Database/Postgresql/General.php @@ -53,9 +53,9 @@ class General extends Component ]; public function mount() { - $this->db_url = $this->database->getDbUrl(true); + $this->db_url = $this->database->get_db_url(true); if ($this->database->is_public) { - $this->db_url_public = $this->database->getDbUrl(); + $this->db_url_public = $this->database->get_db_url(); } } public function instantSaveAdvanced() { @@ -87,7 +87,7 @@ class General extends Component return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->getDbUrl(); + $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); diff --git a/app/Livewire/Project/Database/Redis/General.php b/app/Livewire/Project/Database/Redis/General.php index 07c161de5..a894626b0 100644 --- a/app/Livewire/Project/Database/Redis/General.php +++ b/app/Livewire/Project/Database/Redis/General.php @@ -39,9 +39,9 @@ class General extends Component ]; public function mount() { - $this->db_url = $this->database->getDbUrl(true); + $this->db_url = $this->database->get_db_url(true); if ($this->database->is_public) { - $this->db_url_public = $this->database->getDbUrl(); + $this->db_url_public = $this->database->get_db_url(); } } public function instantSaveAdvanced() { @@ -86,7 +86,7 @@ class General extends Component return; } StartDatabaseProxy::run($this->database); - $this->db_url_public = $this->database->getDbUrl(); + $this->db_url_public = $this->database->get_db_url(); $this->dispatch('success', 'Database is now publicly accessible.'); } else { StopDatabaseProxy::run($this->database); diff --git a/app/Livewire/Project/Service/Configuration.php b/app/Livewire/Project/Service/Configuration.php index 33aac21d0..69d158c04 100644 --- a/app/Livewire/Project/Service/Configuration.php +++ b/app/Livewire/Project/Service/Configuration.php @@ -18,6 +18,7 @@ class Configuration extends Component $userId = auth()->user()->id; return [ "echo-private:user.{$userId},ServiceStatusChanged" => 'check_status', + "check_status" ]; } public function render() diff --git a/app/Livewire/Project/Service/Navbar.php b/app/Livewire/Project/Service/Navbar.php index 70c304de0..30e96b838 100644 --- a/app/Livewire/Project/Service/Navbar.php +++ b/app/Livewire/Project/Service/Navbar.php @@ -6,7 +6,6 @@ use App\Actions\Shared\PullImage; use App\Actions\Service\StartService; use App\Actions\Service\StopService; use App\Events\ServiceStatusChanged; -use App\Jobs\ContainerStatusJob; use App\Models\Service; use Livewire\Component; use Spatie\Activitylog\Models\Activity; @@ -27,6 +26,10 @@ class Navbar extends Component { $this->dispatch('refresh')->self(); } + public function check_status() { + $this->dispatch('check_status'); + $this->dispatch('success', 'Service status updated.'); + } public function render() { return view('livewire.project.service.navbar'); diff --git a/app/Livewire/Project/Service/ServiceApplicationView.php b/app/Livewire/Project/Service/ServiceApplicationView.php index 2bae41a80..29bd796fd 100644 --- a/app/Livewire/Project/Service/ServiceApplicationView.php +++ b/app/Livewire/Project/Service/ServiceApplicationView.php @@ -18,6 +18,7 @@ class ServiceApplicationView extends Component 'application.required_fqdn' => 'required|boolean', 'application.is_log_drain_enabled' => 'nullable|boolean', 'application.is_gzip_enabled' => 'nullable|boolean', + 'application.is_stripprefix_enabled' => 'nullable|boolean', ]; public function render() { diff --git a/app/Livewire/Server/Proxy.php b/app/Livewire/Server/Proxy.php index 5825cf3fb..1e23605ff 100644 --- a/app/Livewire/Server/Proxy.php +++ b/app/Livewire/Server/Proxy.php @@ -4,6 +4,7 @@ namespace App\Livewire\Server; use App\Actions\Proxy\CheckConfiguration; use App\Actions\Proxy\SaveConfiguration; +use App\Actions\Proxy\StartProxy; use App\Models\Server; use Livewire\Component; use Illuminate\Support\Str; @@ -26,7 +27,7 @@ class Proxy extends Component public function proxyStatusUpdated() { - $this->server->refresh(); + $this->dispatch('refresh')->self(); } public function change_proxy() @@ -41,6 +42,9 @@ class Proxy extends Component $this->server->proxy->set('type', $proxy_type); $this->server->save(); $this->selectedProxy = $this->server->proxy->type; + if ($this->selectedProxy !== 'NONE') { + StartProxy::run($this->server, false); + } $this->dispatch('proxyStatusUpdated'); } diff --git a/app/Models/Application.php b/app/Models/Application.php index 4cb941215..8fa4979d7 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Spatie\Activitylog\Models\Activity; use Illuminate\Support\Str; use RuntimeException; +use Symfony\Component\Yaml\Yaml; use Visus\Cuid2\Cuid2; class Application extends BaseModel @@ -79,6 +80,18 @@ class Application extends BaseModel } return false; } + public function isForceHttpsEnabled() + { + return data_get($this, 'settings.is_force_https_enabled', false); + } + public function isStripprefixEnabled() + { + return data_get($this, 'settings.is_stripprefix_enabled', true); + } + public function isGzipEnabled() + { + return data_get($this, 'settings.is_gzip_enabled', true); + } public function link() { if (data_get($this, 'environment.project.uuid')) { @@ -476,6 +489,10 @@ class Application extends BaseModel } return false; } + public function workdir() + { + return application_configuration_dir() . "/{$this->uuid}"; + } public function isLogDrainEnabled() { return data_get($this, 'settings.is_log_drain_enabled', false); @@ -710,6 +727,64 @@ class Application extends BaseModel ]; } } + function parseRawCompose() + { + try { + $yaml = Yaml::parse($this->docker_compose_raw); + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); + } + $services = data_get($yaml, 'services'); + $commands = collect([]); + $services = collect($services)->map(function ($service) use ($commands) { + $serviceVolumes = collect(data_get($service, 'volumes', [])); + if ($serviceVolumes->count() > 0) { + foreach ($serviceVolumes as $volume) { + $workdir = $this->workdir(); + $type = null; + $source = null; + if (is_string($volume)) { + $source = Str::of($volume)->before(':'); + if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) { + $type = Str::of('bind'); + } + } else if (is_array($volume)) { + $type = data_get_str($volume, 'type'); + $source = data_get_str($volume, 'source'); + } + if ($type->value() === 'bind') { + if ($source->value() === "/var/run/docker.sock") { + continue; + } + if ($source->value() === '/tmp' || $source->value() === '/tmp/') { + continue; + } + if ($source->startsWith('.')) { + $source = $source->after('.'); + $source = $workdir . $source; + } + $commands->push("mkdir -p $source > /dev/null 2>&1 || true"); + } + } + } + $labels = collect(data_get($service, 'labels', [])); + if (!$labels->contains('coolify.managed')) { + $labels->push('coolify.managed=true'); + } + if (!$labels->contains('coolify.applicationId')) { + $labels->push('coolify.applicationId=' . $this->id); + } + if (!$labels->contains('coolify.type')) { + $labels->push('coolify.type=application'); + } + data_set($service, 'labels', $labels->toArray()); + return $service; + }); + data_set($yaml, 'services', $services->toArray()); + $this->docker_compose_raw = Yaml::dump($yaml, 10, 2); + + instant_remote_process($commands, $this->destination->server, false); + } function parseCompose(int $pull_request_id = 0) { if ($this->docker_compose_raw) { diff --git a/app/Models/LocalFileVolume.php b/app/Models/LocalFileVolume.php index 3e032db0f..a0447b581 100644 --- a/app/Models/LocalFileVolume.php +++ b/app/Models/LocalFileVolume.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Support\Str; class LocalFileVolume extends BaseModel { diff --git a/app/Models/ServiceApplication.php b/app/Models/ServiceApplication.php index 314a01ba3..37f7d9c9c 100644 --- a/app/Models/ServiceApplication.php +++ b/app/Models/ServiceApplication.php @@ -23,6 +23,10 @@ class ServiceApplication extends BaseModel { return data_get($this, 'is_log_drain_enabled', false); } + public function isStripprefixEnabled() + { + return data_get($this, 'is_stripprefix_enabled', true); + } public function isGzipEnabled() { return data_get($this, 'is_gzip_enabled', true); diff --git a/app/Models/ServiceDatabase.php b/app/Models/ServiceDatabase.php index 31bd2786d..9375fe807 100644 --- a/app/Models/ServiceDatabase.php +++ b/app/Models/ServiceDatabase.php @@ -21,9 +21,13 @@ class ServiceDatabase extends BaseModel { return data_get($this, 'is_log_drain_enabled', false); } + public function isStripprefixEnabled() + { + return data_get($this, 'is_stripprefix_enabled', true); + } public function isGzipEnabled() { - return true; + return data_get($this, 'is_gzip_enabled', true); } public function type() { diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php index 174397baa..eb465e989 100644 --- a/app/Models/StandaloneMariadb.php +++ b/app/Models/StandaloneMariadb.php @@ -126,7 +126,7 @@ class StandaloneMariadb extends BaseModel ); } - public function getDbUrl(bool $useInternal = false): string + public function get_db_url(bool $useInternal = false): string { if ($this->is_public && !$useInternal) { return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}"; diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php index d6efaaa1e..a8aab4e42 100644 --- a/app/Models/StandaloneMongodb.php +++ b/app/Models/StandaloneMongodb.php @@ -142,7 +142,7 @@ class StandaloneMongodb extends BaseModel { return 'standalone-mongodb'; } - public function getDbUrl(bool $useInternal = false) + public function get_db_url(bool $useInternal = false) { if ($this->is_public && !$useInternal) { return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true"; diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php index f317196aa..89155d3ec 100644 --- a/app/Models/StandaloneMysql.php +++ b/app/Models/StandaloneMysql.php @@ -127,7 +127,7 @@ class StandaloneMysql extends BaseModel ); } - public function getDbUrl(bool $useInternal = false): string + public function get_db_url(bool $useInternal = false): string { if ($this->is_public && !$useInternal) { return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mysql_database}"; diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index 3d2317159..0b7589e81 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -126,7 +126,7 @@ class StandalonePostgresql extends BaseModel { return 'standalone-postgresql'; } - public function getDbUrl(bool $useInternal = false): string + public function get_db_url(bool $useInternal = false): string { if ($this->is_public && !$useInternal) { return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}"; diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php index 6b6b6c415..2b623f28d 100644 --- a/app/Models/StandaloneRedis.php +++ b/app/Models/StandaloneRedis.php @@ -122,7 +122,7 @@ class StandaloneRedis extends BaseModel { return 'standalone-redis'; } - public function getDbUrl(bool $useInternal = false): string + public function get_db_url(bool $useInternal = false): string { if ($this->is_public && !$useInternal) { return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0"; diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 008bd4d7f..5fd43daa9 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -215,7 +215,7 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource, } return $payload; } -function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?string $service_name = null) +function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null) { $labels = collect([]); $labels->push('traefik.enable=true'); @@ -281,8 +281,10 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ $labels->push("traefik.http.services.{$https_label}.loadbalancer.server.port=$port"); } if ($path !== '/') { - $labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}"); - $middlewares = collect(["{$https_label}-stripprefix"]); + if ($is_stripprefix_enabled) { + $labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}"); + $middlewares = collect(["{$https_label}-stripprefix"]); + } if ($is_gzip_enabled) { $middlewares->push('gzip'); } @@ -334,8 +336,10 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_ $labels->push("traefik.http.routers.{$http_label}.service={$http_label}"); } if ($path !== '/') { - $labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}"); - $middlewares = collect(["{$http_label}-stripprefix"]); + if ($is_stripprefix_enabled) { + $labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}"); + $middlewares = collect(["{$http_label}-stripprefix"]); + } if ($is_gzip_enabled) { $middlewares->push('gzip'); } @@ -392,7 +396,14 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview $domains = Str::of(data_get($application, 'fqdn'))->explode(','); } // Add Traefik labels no matter which proxy is selected - $labels = $labels->merge(fqdnLabelsForTraefik($appUuid, $domains, $application->settings->is_force_https_enabled, $onlyPort)); + $labels = $labels->merge(fqdnLabelsForTraefik( + uuid: $appUuid, + domains: $domains, + onlyPort: $onlyPort, + is_force_https_enabled: $application->isForceHttpsEnabled(), + is_gzip_enabled: $application->isGzipEnabled(), + is_stripprefix_enabled: $application->isStripprefixEnabled() + )); } return $labels->all(); } diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 86a874985..9bb2c164a 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1047,7 +1047,15 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal $serviceLabels = $serviceLabels->merge($defaultLabels); if (!$isDatabase && $fqdns->count() > 0) { if ($fqdns) { - $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($resource->uuid, $fqdns, true, serviceLabels: $serviceLabels, is_gzip_enabled: $savedService->isGzipEnabled(), service_name: $serviceName)); + $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik( + uuid: $resource->uuid, + domains: $fqdns, + is_force_https_enabled: true, + serviceLabels: $serviceLabels, + is_gzip_enabled: $savedService->isGzipEnabled(), + is_stripprefix_enabled: $savedService->isStripprefixEnabled(), + service_name: $serviceName)); + } } if ($resource->server->isLogDrainEnabled() && $savedService->isLogDrainEnabled()) { @@ -1249,7 +1257,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal // Collect/create/update networks if ($serviceNetworks->count() > 0) { foreach ($serviceNetworks as $networkName => $networkDetails) { - ray($networkDetails); $networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) { return $value == $networkName || $key == $networkName; }); @@ -1405,7 +1412,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal ]); } } else { - $generatedValue = generateEnvValue($command, $service); + $generatedValue = generateEnvValue($command); if (!$foundEnv) { EnvironmentVariable::create([ 'key' => $key, @@ -1581,7 +1588,7 @@ function parseEnvVariable(Str|string $value) 'port' => $port, ]; } -function generateEnvValue(string $command, Service $service) +function generateEnvValue(string $command, ?Service $service = null) { switch ($command) { case 'PASSWORD': diff --git a/config/sentry.php b/config/sentry.php index 438c45246..13331bc77 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.232', + 'release' => '4.0.0-beta.233', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index 2ff41e455..7aab683f5 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ boolean('is_gzip_enabled')->default(true); + $table->boolean('is_stripprefix_enabled')->default(true); + }); + Schema::table('service_applications', function (Blueprint $table) { + $table->boolean('is_stripprefix_enabled')->default(true); + }); + Schema::table('service_databases', function (Blueprint $table) { + $table->boolean('is_gzip_enabled')->default(true); + $table->boolean('is_stripprefix_enabled')->default(true); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('application_settings', function (Blueprint $table) { + $table->dropColumn('is_gzip_enabled'); + $table->dropColumn('is_stripprefix_enabled'); + }); + Schema::table('service_applications', function (Blueprint $table) { + $table->dropColumn('is_stripprefix_enabled'); + }); + Schema::table('service_databases', function (Blueprint $table) { + $table->dropColumn('is_gzip_enabled'); + $table->dropColumn('is_stripprefix_enabled'); + }); + } +}; diff --git a/public/svgs/changedetection.png b/public/svgs/changedetection.png new file mode 100644 index 000000000..b2e86f1d0 Binary files /dev/null and b/public/svgs/changedetection.png differ diff --git a/resources/views/components/applications/links.blade.php b/resources/views/components/applications/links.blade.php index 92acb01a2..7f3bed2da 100644 --- a/resources/views/components/applications/links.blade.php +++ b/resources/views/components/applications/links.blade.php @@ -1,8 +1,10 @@