diff --git a/app/Console/Commands/Generate/Services.php b/app/Console/Commands/Generate/Services.php index 42f9360bb..e316fc391 100644 --- a/app/Console/Commands/Generate/Services.php +++ b/app/Console/Commands/Generate/Services.php @@ -88,6 +88,14 @@ private function processFile(string $file): false|array $payload['envs'] = base64_encode($envFileContent); } + if (str($data->get('amd_only'))->toBoolean()) { + $payload['amd_only'] = true; + } + + if (str($data->get('arm_only'))->toBoolean()) { + $payload['arm_only'] = true; + } + return $payload; } @@ -160,6 +168,14 @@ private function processFileWithFqdn(string $file): false|array $payload['envs'] = base64_encode($modifiedEnvContent); } + if (str($data->get('amd_only'))->toBoolean()) { + $payload['amd_only'] = true; + } + + if (str($data->get('arm_only'))->toBoolean()) { + $payload['arm_only'] = true; + } + return $payload; } @@ -229,6 +245,14 @@ private function processFileWithFqdnRaw(string $file): false|array $payload['envs'] = $modifiedEnvContent; } + if (str($data->get('amd_only'))->toBoolean()) { + $payload['amd_only'] = true; + } + + if (str($data->get('arm_only'))->toBoolean()) { + $payload['arm_only'] = true; + } + return $payload; } } diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index 77f4e626f..3d92300f1 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -217,7 +217,7 @@ public function applications(Request $request) type: 'object', properties: [ 'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'], - 'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")'], + 'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")'], ], ), ], @@ -383,7 +383,7 @@ public function create_public_application(Request $request) type: 'object', properties: [ 'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'], - 'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")'], + 'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")'], ], ), ], @@ -549,7 +549,7 @@ public function create_private_gh_app_application(Request $request) type: 'object', properties: [ 'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'], - 'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")'], + 'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")'], ], ), ], @@ -2397,7 +2397,7 @@ public function delete_by_uuid(Request $request) type: 'object', properties: [ 'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'], - 'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")'], + 'domain' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")'], ], ), ], diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index fbf4b9e56..23ba30998 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -221,7 +221,7 @@ public function services(Request $request) type: 'object', properties: [ 'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'], - 'url' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io").'], + 'url' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io").'], ], ), ], @@ -843,7 +843,7 @@ public function delete_by_uuid(Request $request) type: 'object', properties: [ 'name' => ['type' => 'string', 'description' => 'The service name as defined in docker-compose.'], - 'url' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io").'], + 'url' => ['type' => 'string', 'description' => 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io").'], ], ), ], diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index d070cefc6..7e5025c8a 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -2877,7 +2877,7 @@ private function generate_healthcheck_commands() $scheme = $this->sanitizeHealthCheckValue($this->application->health_check_scheme, '/^https?$/', 'http'); $host = $this->sanitizeHealthCheckValue($this->application->health_check_host, '/^[a-zA-Z0-9.\-_]+$/', 'localhost'); $path = $this->application->health_check_path - ? $this->sanitizeHealthCheckValue($this->application->health_check_path, '#^[a-zA-Z0-9/\-_.~%]+$#', '/') + ? $this->sanitizeHealthCheckValue($this->application->health_check_path, '#^[a-zA-Z0-9/\-_.~%,;]+$#', '/') : null; $url = escapeshellarg("{$scheme}://{$host}:{$health_check_port}".($path ?? '/')); diff --git a/app/Livewire/GlobalSearch.php b/app/Livewire/GlobalSearch.php index 154748b47..df2adf22b 100644 --- a/app/Livewire/GlobalSearch.php +++ b/app/Livewire/GlobalSearch.php @@ -1496,7 +1496,10 @@ public function getServicesProperty() 'category' => 'Services', 'resourceType' => 'service', 'logo' => data_get($service, 'logo'), - ]); + ] + array_filter([ + 'amd_only' => data_get($service, 'amd_only') ? true : null, + 'arm_only' => data_get($service, 'arm_only') ? true : null, + ])); } $cachedServices = $items->toArray(); diff --git a/app/Livewire/Project/Shared/HealthChecks.php b/app/Livewire/Project/Shared/HealthChecks.php index 0d5d71b45..195e7fd92 100644 --- a/app/Livewire/Project/Shared/HealthChecks.php +++ b/app/Livewire/Project/Shared/HealthChecks.php @@ -34,7 +34,7 @@ class HealthChecks extends Component #[Validate(['nullable', 'integer', 'min:1', 'max:65535'])] public ?string $healthCheckPort = null; - #[Validate(['required', 'string', 'regex:#^[a-zA-Z0-9/\-_.~%]+$#'])] + #[Validate(['required', 'string', 'regex:#^[a-zA-Z0-9/\-_.~%,;]+$#'])] public string $healthCheckPath; #[Validate(['integer'])] @@ -62,7 +62,7 @@ class HealthChecks extends Component 'healthCheckEnabled' => 'boolean', 'healthCheckType' => 'string|in:http,cmd', 'healthCheckCommand' => ['nullable', 'string', 'max:1000', 'regex:/^[a-zA-Z0-9 \-_.\/:=@,+]+$/'], - 'healthCheckPath' => ['required', 'string', 'regex:#^[a-zA-Z0-9/\-_.~%]+$#'], + 'healthCheckPath' => ['required', 'string', 'regex:#^[a-zA-Z0-9/\-_.~%,;]+$#'], 'healthCheckPort' => 'nullable|integer|min:1|max:65535', 'healthCheckHost' => ['required', 'string', 'regex:/^[a-zA-Z0-9.\-_]+$/'], 'healthCheckMethod' => 'required|string|in:GET,HEAD,POST,OPTIONS', diff --git a/app/Livewire/Upgrade.php b/app/Livewire/Upgrade.php index 7948ad6a9..1b8701d94 100644 --- a/app/Livewire/Upgrade.php +++ b/app/Livewire/Upgrade.php @@ -23,24 +23,42 @@ class Upgrade extends Component public function mount() { - $this->currentVersion = config('constants.coolify.version'); - $this->devMode = isDev(); + $this->refreshUpgradeState(); } public function checkUpdate() { try { - $this->latestVersion = get_latest_version_of_coolify(); - $this->currentVersion = config('constants.coolify.version'); - $this->isUpgradeAvailable = data_get(InstanceSettings::get(), 'new_version_available', false); - if (isDev()) { - $this->isUpgradeAvailable = true; - } + $this->refreshUpgradeState(); } catch (\Throwable $e) { return handleError($e, $this); } } + protected function refreshUpgradeState(): void + { + $this->currentVersion = config('constants.coolify.version'); + $this->latestVersion = get_latest_version_of_coolify(); + $this->devMode = isDev(); + + if ($this->devMode) { + $this->isUpgradeAvailable = true; + + return; + } + + $settings = InstanceSettings::find(0); + $hasNewerVersion = version_compare($this->latestVersion, $this->currentVersion, '>'); + $newVersionAvailable = (bool) data_get($settings, 'new_version_available', false); + + if ($settings && $newVersionAvailable && ! $hasNewerVersion) { + $settings->update(['new_version_available' => false]); + $newVersionAvailable = false; + } + + $this->isUpgradeAvailable = $hasNewerVersion && $newVersionAvailable; + } + public function upgrade() { try { diff --git a/app/Models/Team.php b/app/Models/Team.php index 8a54a9dee..d5d564444 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -71,25 +71,31 @@ protected static function booted() } }); - static::deleting(function ($team) { - $keys = $team->privateKeys; - foreach ($keys as $key) { + static::deleting(function (Team $team) { + foreach ($team->privateKeys as $key) { $key->delete(); } - $sources = $team->sources(); - foreach ($sources as $source) { + + // Transfer instance-wide sources to root team so they remain available + GithubApp::where('team_id', $team->id)->where('is_system_wide', true)->update(['team_id' => 0]); + GitlabApp::where('team_id', $team->id)->where('is_system_wide', true)->update(['team_id' => 0]); + + // Delete non-instance-wide sources owned by this team + $teamSources = GithubApp::where('team_id', $team->id)->get() + ->merge(GitlabApp::where('team_id', $team->id)->get()); + foreach ($teamSources as $source) { $source->delete(); } - $tags = Tag::whereTeamId($team->id)->get(); - foreach ($tags as $tag) { + + foreach (Tag::whereTeamId($team->id)->get() as $tag) { $tag->delete(); } - $shared_variables = $team->environment_variables(); - foreach ($shared_variables as $shared_variable) { - $shared_variable->delete(); + + foreach ($team->environment_variables()->get() as $sharedVariable) { + $sharedVariable->delete(); } - $s3s = $team->s3s; - foreach ($s3s as $s3) { + + foreach ($team->s3s as $s3) { $s3->delete(); } }); @@ -338,4 +344,5 @@ public function webhookNotificationSettings() { return $this->hasOne(WebhookNotificationSettings::class); } + } diff --git a/app/Support/ValidationPatterns.php b/app/Support/ValidationPatterns.php index 15d0f19e0..88121384f 100644 --- a/app/Support/ValidationPatterns.php +++ b/app/Support/ValidationPatterns.php @@ -203,10 +203,24 @@ public static function volumeNameMessages(string $field = 'name'): array } /** - * Pattern for port mappings (e.g. 3000:3000, 8080:80, 8000-8010:8000-8010) - * Each entry requires host:container format, where each side can be a number or a range (number-number) + * Pattern for port mappings with optional IP binding and protocol suffix on either side. + * Format: [ip:]port[:ip:port] where IP is IPv4 or [IPv6], port can be a range, protocol suffix optional. + * Examples: 8080:80, 127.0.0.1:8080:80, [::1]::80/udp, 127.0.0.1:8080:80/tcp */ - public const PORT_MAPPINGS_PATTERN = '/^(\d+(-\d+)?:\d+(-\d+)?)(,\d+(-\d+)?:\d+(-\d+)?)*$/'; + public const PORT_MAPPINGS_PATTERN = '/^ + (?:(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[\da-fA-F:]+\]):)? # optional IP + (?:\d+(?:-\d+)?(?:\/(?:tcp|udp|sctp))?)? # optional host port + : + (?:(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[\da-fA-F:]+\]):)? # optional IP + \d+(?:-\d+)?(?:\/(?:tcp|udp|sctp))? # container port + (?:, + (?:(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[\da-fA-F:]+\]):)? + (?:\d+(?:-\d+)?(?:\/(?:tcp|udp|sctp))?)? + : + (?:(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[\da-fA-F:]+\]):)? + \d+(?:-\d+)?(?:\/(?:tcp|udp|sctp))? + )* + $/x'; /** * Get validation rules for container name fields @@ -230,7 +244,7 @@ public static function portMappingRules(): array public static function portMappingMessages(string $field = 'portsMappings'): array { return [ - "{$field}.regex" => 'Port mappings must be a comma-separated list of port pairs or ranges (e.g. 3000:3000,8080:80,8000-8010:8000-8010).', + "{$field}.regex" => 'Port mappings must be a comma-separated list of port pairs or ranges with optional IP and protocol (e.g. 3000:3000, 8080:80/udp, 127.0.0.1:8080:80, [::1]::80).', ]; } diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php index c10ed6158..8088e6b99 100644 --- a/bootstrap/helpers/api.php +++ b/bootstrap/helpers/api.php @@ -106,7 +106,7 @@ function sharedDataApplications() 'health_check_enabled' => 'boolean', 'health_check_type' => 'string|in:http,cmd', 'health_check_command' => ['nullable', 'string', 'max:1000', 'regex:/^[a-zA-Z0-9 \-_.\/:=@,+]+$/'], - 'health_check_path' => ['string', 'regex:#^[a-zA-Z0-9/\-_.~%]+$#'], + 'health_check_path' => ['string', 'regex:#^[a-zA-Z0-9/\-_.~%,;]+$#'], 'health_check_port' => 'integer|nullable|min:1|max:65535', 'health_check_host' => ['string', 'regex:/^[a-zA-Z0-9.\-_]+$/'], 'health_check_method' => 'string|in:GET,HEAD,POST,OPTIONS', diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index cd773f6a9..011c149c9 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -3660,13 +3660,21 @@ function convertGitUrl(string $gitRepository, string $deploymentType, GithubApp| } } - preg_match('/(?<=:)\d+(?=\/)/', $gitRepository, $matches); + $normalizedRepository = $repository; - if (count($matches) === 1) { - $providerInfo['port'] = $matches[0]; - $gitHost = str($gitRepository)->before(':'); - $gitRepo = str($gitRepository)->after('/'); - $repository = "$gitHost:$gitRepo"; + if (str($normalizedRepository)->contains('://')) { + $parsedRepository = parse_url($normalizedRepository); + + if ($parsedRepository !== false && array_key_exists('port', $parsedRepository)) { + $providerInfo['port'] = (string) $parsedRepository['port']; + } + } else { + preg_match('/^(?[^:]+):(?\d+)\/(?.+)$/', $normalizedRepository, $matches); + + if (! empty($matches['port'])) { + $providerInfo['port'] = $matches['port']; + $repository = "{$matches['host']}:{$matches['path']}"; + } } return [ diff --git a/composer.lock b/composer.lock index 3884eac06..2f27235f5 100644 --- a/composer.lock +++ b/composer.lock @@ -72,7 +72,6 @@ "type": "zip", "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/67b6b6210af47319c74c5666388d71bc1bc58276", "reference": "67b6b6210af47319c74c5666388d71bc1bc58276", - "shasum": "" }, "require": { @@ -157,7 +156,6 @@ "source": "https://github.com/aws/aws-sdk-php/tree/3.374.2" }, "time": "2026-03-27T18:05:55+00:00" - }, { "name": "bacon/bacon-qr-code", @@ -5158,16 +5156,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.50", + "version": "3.0.51", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "aa6ad8321ed103dc3624fb600a25b66ebf78ec7b" + "reference": "d59c94077f9c9915abb51ddb52ce85188ece1748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/aa6ad8321ed103dc3624fb600a25b66ebf78ec7b", - "reference": "aa6ad8321ed103dc3624fb600a25b66ebf78ec7b", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d59c94077f9c9915abb51ddb52ce85188ece1748", + "reference": "d59c94077f9c9915abb51ddb52ce85188ece1748", "shasum": "" }, "require": { @@ -5248,7 +5246,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.50" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.51" }, "funding": [ { @@ -5264,7 +5262,7 @@ "type": "tidelift" } ], - "time": "2026-03-19T02:57:58+00:00" + "time": "2026-04-10T01:33:53+00:00" }, { "name": "phpstan/phpdoc-parser", diff --git a/config/constants.php b/config/constants.php index d0ae9be65..743b5e38c 100644 --- a/config/constants.php +++ b/config/constants.php @@ -2,9 +2,9 @@ return [ 'coolify' => [ - 'version' => '4.0.0-beta.472', + 'version' => '4.0.0-beta.474', 'helper_version' => '1.0.13', - 'realtime_version' => '1.0.12', + 'realtime_version' => '1.0.13', 'self_hosted' => env('SELF_HOSTED', true), 'autoupdate' => env('AUTOUPDATE'), 'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'), diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 3af443c83..f608fe3cb 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -112,6 +112,7 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock - dev_coolify_data:/data/coolify + - dev_coolify_data:/var/lib/docker/volumes/coolify_dev_coolify_data/_data - dev_backups_data:/data/coolify/backups - dev_postgres_data:/data/coolify/_volumes/database - dev_redis_data:/data/coolify/_volumes/redis diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index e6d2bce54..901aeb833 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -60,7 +60,7 @@ services: retries: 10 timeout: 2s soketi: - image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.12' + image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.13' ports: - "${SOKETI_PORT:-6001}:6001" - "6002:6002" diff --git a/docker-compose.windows.yml b/docker-compose.windows.yml index 00734fb0e..998d35974 100644 --- a/docker-compose.windows.yml +++ b/docker-compose.windows.yml @@ -96,7 +96,7 @@ services: retries: 10 timeout: 2s soketi: - image: 'ghcr.io/coollabsio/coolify-realtime:1.0.12' + image: 'ghcr.io/coollabsio/coolify-realtime:1.0.13' pull_policy: always container_name: coolify-realtime restart: always diff --git a/docker/coolify-realtime/package-lock.json b/docker/coolify-realtime/package-lock.json index 1c49ff930..174077562 100644 --- a/docker/coolify-realtime/package-lock.json +++ b/docker/coolify-realtime/package-lock.json @@ -7,7 +7,7 @@ "dependencies": { "@xterm/addon-fit": "0.11.0", "@xterm/xterm": "6.0.0", - "axios": "1.13.6", + "axios": "1.15.0", "cookie": "1.1.1", "dotenv": "17.3.1", "node-pty": "1.1.0", @@ -36,14 +36,14 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", - "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", + "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", - "proxy-from-env": "^1.1.0" + "proxy-from-env": "^2.1.0" } }, "node_modules/call-bind-apply-helpers": { @@ -344,10 +344,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/ws": { "version": "8.19.0", diff --git a/docker/coolify-realtime/package.json b/docker/coolify-realtime/package.json index ebb7122c8..30bfbcef7 100644 --- a/docker/coolify-realtime/package.json +++ b/docker/coolify-realtime/package.json @@ -5,7 +5,7 @@ "@xterm/addon-fit": "0.11.0", "@xterm/xterm": "6.0.0", "cookie": "1.1.1", - "axios": "1.13.6", + "axios": "1.15.0", "dotenv": "17.3.1", "node-pty": "1.1.0", "ws": "8.19.0" diff --git a/openapi.json b/openapi.json index 239068300..e4e03c99d 100644 --- a/openapi.json +++ b/openapi.json @@ -361,7 +361,7 @@ }, "domain": { "type": "string", - "description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")" + "description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")" } }, "type": "object" @@ -811,7 +811,7 @@ }, "domain": { "type": "string", - "description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")" + "description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")" } }, "type": "object" @@ -1261,7 +1261,7 @@ }, "domain": { "type": "string", - "description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")" + "description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")" } }, "type": "object" @@ -2692,7 +2692,7 @@ }, "domain": { "type": "string", - "description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")" + "description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")" } }, "type": "object" @@ -10811,7 +10811,7 @@ }, "url": { "type": "string", - "description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")." + "description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")." } }, "type": "object" @@ -11142,7 +11142,7 @@ }, "url": { "type": "string", - "description": "Comma-separated list of URLs (e.g. \"http:\/\/app.coolify.io,https:\/\/app2.coolify.io\")." + "description": "Comma-separated list of URLs (e.g. \"https:\/\/app.coolify.io,https:\/\/app2.coolify.io\")." } }, "type": "object" diff --git a/openapi.yaml b/openapi.yaml index 5bf6059af..f2761de59 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -258,7 +258,7 @@ paths: docker_compose_domains: type: array description: 'Array of URLs to be applied to containers of a dockercompose application.' - items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")' } }, type: object } + items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")' } }, type: object } watch_paths: type: string description: 'The watch paths.' @@ -546,7 +546,7 @@ paths: docker_compose_domains: type: array description: 'Array of URLs to be applied to containers of a dockercompose application.' - items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")' } }, type: object } + items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")' } }, type: object } watch_paths: type: string description: 'The watch paths.' @@ -834,7 +834,7 @@ paths: docker_compose_domains: type: array description: 'Array of URLs to be applied to containers of a dockercompose application.' - items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")' } }, type: object } + items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")' } }, type: object } watch_paths: type: string description: 'The watch paths.' @@ -1735,7 +1735,7 @@ paths: docker_compose_domains: type: array description: 'Array of URLs to be applied to containers of a dockercompose application.' - items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io")' } }, type: object } + items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, domain: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io")' } }, type: object } watch_paths: type: string description: 'The watch paths.' @@ -6886,7 +6886,7 @@ paths: urls: type: array description: 'Array of URLs to be applied to containers of a service.' - items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, url: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io").' } }, type: object } + items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, url: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io").' } }, type: object } force_domain_override: type: boolean default: false @@ -7075,7 +7075,7 @@ paths: urls: type: array description: 'Array of URLs to be applied to containers of a service.' - items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, url: { type: string, description: 'Comma-separated list of URLs (e.g. "http://app.coolify.io,https://app2.coolify.io").' } }, type: object } + items: { properties: { name: { type: string, description: 'The service name as defined in docker-compose.' }, url: { type: string, description: 'Comma-separated list of URLs (e.g. "https://app.coolify.io,https://app2.coolify.io").' } }, type: object } force_domain_override: type: boolean default: false diff --git a/other/nightly/docker-compose.prod.yml b/other/nightly/docker-compose.prod.yml index e6d2bce54..901aeb833 100644 --- a/other/nightly/docker-compose.prod.yml +++ b/other/nightly/docker-compose.prod.yml @@ -60,7 +60,7 @@ services: retries: 10 timeout: 2s soketi: - image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.12' + image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.13' ports: - "${SOKETI_PORT:-6001}:6001" - "6002:6002" diff --git a/other/nightly/docker-compose.windows.yml b/other/nightly/docker-compose.windows.yml index 00734fb0e..998d35974 100644 --- a/other/nightly/docker-compose.windows.yml +++ b/other/nightly/docker-compose.windows.yml @@ -96,7 +96,7 @@ services: retries: 10 timeout: 2s soketi: - image: 'ghcr.io/coollabsio/coolify-realtime:1.0.12' + image: 'ghcr.io/coollabsio/coolify-realtime:1.0.13' pull_policy: always container_name: coolify-realtime restart: always diff --git a/other/nightly/install.sh b/other/nightly/install.sh index 09406118c..4a91f7b1b 100755 --- a/other/nightly/install.sh +++ b/other/nightly/install.sh @@ -539,6 +539,15 @@ install_docker_manually() { echo "Docker installed successfully." fi } + +install_docker_from_rhel_repo() { + echo " - Installing Docker from the RHEL repository for Rocky Linux..." + rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo + dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo + dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + systemctl --now enable docker +} + log_section "Step 3/9: Checking Docker installation" echo "3/9 Checking Docker installation..." if ! [ -x "$(command -v docker)" ]; then @@ -579,6 +588,13 @@ if ! [ -x "$(command -v docker)" ]; then exit 1 fi ;; + "rocky") + install_docker_from_rhel_repo + if ! [ -x "$(command -v docker)" ]; then + echo " - Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue." + exit 1 + fi + ;; "almalinux" | "tencentos") dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo >/dev/null 2>&1 dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin >/dev/null 2>&1 diff --git a/other/nightly/versions.json b/other/nightly/versions.json index 26d755967..27d911c67 100644 --- a/other/nightly/versions.json +++ b/other/nightly/versions.json @@ -1,7 +1,7 @@ { "coolify": { "v4": { - "version": "4.0.0-beta.472" + "version": "4.0.0-beta.474" }, "nightly": { "version": "4.0.0" @@ -10,7 +10,7 @@ "version": "1.0.13" }, "realtime": { - "version": "1.0.12" + "version": "1.0.13" }, "sentinel": { "version": "0.0.21" diff --git a/package-lock.json b/package-lock.json index 0af80f950..1fcd7cc1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "devDependencies": { "@tailwindcss/postcss": "4.1.18", "@vitejs/plugin-vue": "6.0.3", - "axios": "1.13.2", + "axios": "1.15.0", "laravel-echo": "2.2.7", "laravel-vite-plugin": "2.0.1", "postcss": "8.5.6", @@ -1474,15 +1474,15 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", + "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", "dev": true, "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" } }, "node_modules/call-bind-apply-helpers": { @@ -2501,11 +2501,14 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/pusher-js": { "version": "8.4.0", diff --git a/package.json b/package.json index 661b13e4c..3afefa833 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "devDependencies": { "@tailwindcss/postcss": "4.1.18", "@vitejs/plugin-vue": "6.0.3", - "axios": "1.13.2", + "axios": "1.15.0", "laravel-echo": "2.2.7", "laravel-vite-plugin": "2.0.1", "postcss": "8.5.6", diff --git a/resources/css/utilities.css b/resources/css/utilities.css index 02be0c0c4..a8e807041 100644 --- a/resources/css/utilities.css +++ b/resources/css/utilities.css @@ -145,6 +145,10 @@ @utility dropdown-item { @apply flex relative gap-2 justify-start items-center py-1 pr-4 pl-2 w-full text-xs transition-colors cursor-pointer select-none dark:text-white hover:bg-neutral-100 dark:hover:bg-coollabs outline-none data-disabled:pointer-events-none data-disabled:opacity-50 focus-visible:bg-neutral-100 dark:focus-visible:bg-coollabs; } +@utility dropdown-item-touch { + @apply min-h-10 px-3 py-2 text-sm; +} + @utility dropdown-item-no-padding { @apply flex relative gap-2 justify-start items-center py-1 w-full text-xs transition-colors cursor-pointer select-none dark:text-white hover:bg-neutral-100 dark:hover:bg-coollabs outline-none data-disabled:pointer-events-none data-disabled:opacity-50 focus-visible:bg-neutral-100 dark:focus-visible:bg-coollabs; } diff --git a/resources/views/components/dropdown.blade.php b/resources/views/components/dropdown.blade.php index 666b93dbc..2bb917f79 100644 --- a/resources/views/components/dropdown.blade.php +++ b/resources/views/components/dropdown.blade.php @@ -1,7 +1,44 @@
- -
+ :style="panelStyles" class="absolute top-full z-50 mt-1 min-w-max max-w-[calc(100vw-1rem)] md:top-0 md:mt-6" x-cloak>
+ class="border border-neutral-300 bg-white p-1 shadow-sm dark:border-coolgray-300 dark:bg-coolgray-200"> {{ $slot }}
diff --git a/resources/views/components/forms/checkbox.blade.php b/resources/views/components/forms/checkbox.blade.php index b291759a8..29717b9b8 100644 --- a/resources/views/components/forms/checkbox.blade.php +++ b/resources/views/components/forms/checkbox.blade.php @@ -11,12 +11,12 @@ ])
$fullWidth, 'dark:hover:bg-coolgray-100 cursor-pointer' => !$disabled, ])> -
@script