diff --git a/app/Actions/Database/StartKeydb.php b/app/Actions/Database/StartKeydb.php index 58f3cda4e..fe80a7d54 100644 --- a/app/Actions/Database/StartKeydb.php +++ b/app/Actions/Database/StartKeydb.php @@ -207,6 +207,9 @@ public function handle(StandaloneKeydb $database) if ($this->database->enable_ssl) { $this->commands[] = "chown -R 999:999 $this->configuration_dir/ssl/server.key $this->configuration_dir/ssl/server.crt"; } + if (! is_null($this->database->keydb_conf) && ! empty($this->database->keydb_conf)) { + $this->commands[] = "chown 999:999 $this->configuration_dir/keydb.conf"; + } $this->commands[] = "docker stop -t 10 $container_name 2>/dev/null || true"; $this->commands[] = "docker rm -f $container_name 2>/dev/null || true"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d"; diff --git a/app/Actions/Database/StartRedis.php b/app/Actions/Database/StartRedis.php index 4e4f3ce53..70df91054 100644 --- a/app/Actions/Database/StartRedis.php +++ b/app/Actions/Database/StartRedis.php @@ -204,6 +204,9 @@ public function handle(StandaloneRedis $database) if ($this->database->enable_ssl) { $this->commands[] = "chown -R 999:999 $this->configuration_dir/ssl/server.key $this->configuration_dir/ssl/server.crt"; } + if (! is_null($this->database->redis_conf) && ! empty($this->database->redis_conf)) { + $this->commands[] = "chown 999:999 $this->configuration_dir/redis.conf"; + } $this->commands[] = "docker stop -t 10 $container_name 2>/dev/null || true"; $this->commands[] = "docker rm -f $container_name 2>/dev/null || true"; $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d"; diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index 57bcc13f6..256308afd 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -1101,7 +1101,6 @@ private function create_application(Request $request, $type) 'git_branch' => ['string', 'required', new ValidGitBranch], 'build_pack' => ['required', Rule::enum(BuildPackTypes::class)], 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required', - 'docker_compose_location' => 'string', 'docker_compose_domains' => 'array|nullable', 'docker_compose_domains.*' => 'array:name,domain', 'docker_compose_domains.*.name' => 'string|required', @@ -1297,7 +1296,6 @@ private function create_application(Request $request, $type) 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required', 'github_app_uuid' => 'string|required', 'watch_paths' => 'string|nullable', - 'docker_compose_location' => 'string', 'docker_compose_domains' => 'array|nullable', 'docker_compose_domains.*' => 'array:name,domain', 'docker_compose_domains.*.name' => 'string|required', @@ -1525,7 +1523,6 @@ private function create_application(Request $request, $type) 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required', 'private_key_uuid' => 'string|required', 'watch_paths' => 'string|nullable', - 'docker_compose_location' => 'string', 'docker_compose_domains' => 'array|nullable', 'docker_compose_domains.*' => 'array:name,domain', 'docker_compose_domains.*.name' => 'string|required', @@ -2470,7 +2467,6 @@ public function update_by_uuid(Request $request) 'description' => 'string|nullable', 'static_image' => 'string', 'watch_paths' => 'string|nullable', - 'docker_compose_location' => 'string', 'docker_compose_domains' => 'array|nullable', 'docker_compose_domains.*' => 'array:name,domain', 'docker_compose_domains.*.name' => 'string|required', diff --git a/app/Http/Controllers/Api/DeployController.php b/app/Http/Controllers/Api/DeployController.php index baff3ec4f..a21940257 100644 --- a/app/Http/Controllers/Api/DeployController.php +++ b/app/Http/Controllers/Api/DeployController.php @@ -127,6 +127,10 @@ public function deployment_by_uuid(Request $request) if (! $deployment) { return response()->json(['message' => 'Deployment not found.'], 404); } + $application = $deployment->application; + if (! $application || data_get($application->team(), 'id') !== $teamId) { + return response()->json(['message' => 'Deployment not found.'], 404); + } return response()->json($this->removeSensitiveData($deployment)); } diff --git a/app/Http/Middleware/CheckForcePasswordReset.php b/app/Http/Middleware/CheckForcePasswordReset.php index 78b1f896c..c857cb836 100644 --- a/app/Http/Middleware/CheckForcePasswordReset.php +++ b/app/Http/Middleware/CheckForcePasswordReset.php @@ -25,7 +25,7 @@ public function handle(Request $request, Closure $next): Response } $force_password_reset = auth()->user()->force_password_reset; if ($force_password_reset) { - if ($request->routeIs('auth.force-password-reset') || $request->path() === 'force-password-reset' || $request->path() === 'livewire/update' || $request->path() === 'logout') { + if ($request->routeIs('auth.force-password-reset') || $request->path() === 'force-password-reset' || $request->path() === 'two-factor-challenge' || $request->path() === 'livewire/update' || $request->path() === 'logout') { return $next($request); } diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index f1ee64e62..9b83357a7 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -171,6 +171,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue private bool $dockerBuildkitSupported = false; + private bool $dockerSecretsSupported = false; + private bool $skip_build = false; private Collection|string $build_secrets; @@ -251,7 +253,7 @@ public function __construct(public int $application_deployment_queue_id) } if ($this->application->build_pack === 'dockerfile') { if (data_get($this->application, 'dockerfile_location')) { - $this->dockerfile_location = $this->application->dockerfile_location; + $this->dockerfile_location = $this->validatePathField($this->application->dockerfile_location, 'dockerfile_location'); } } } @@ -381,13 +383,6 @@ public function handle(): void private function detectBuildKitCapabilities(): void { - // If build secrets are not enabled, skip detection and use traditional args - if (! $this->application->settings->use_build_secrets) { - $this->dockerBuildkitSupported = false; - - return; - } - $serverToCheck = $this->use_build_server ? $this->build_server : $this->server; $serverName = $this->use_build_server ? "build server ({$serverToCheck->name})" : "deployment server ({$serverToCheck->name})"; @@ -403,53 +398,55 @@ private function detectBuildKitCapabilities(): void if ($majorVersion < 18 || ($majorVersion == 18 && $minorVersion < 9)) { $this->dockerBuildkitSupported = false; - $this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} on {$serverName} does not support BuildKit (requires 18.09+). Build secrets feature disabled."); + $this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} on {$serverName} does not support BuildKit (requires 18.09+)."); return; } - $buildkitEnabled = instant_remote_process( + // Check buildx availability (always installed by Coolify on Docker 24.0+) + $buildxAvailable = instant_remote_process( ["docker buildx version >/dev/null 2>&1 && echo 'available' || echo 'not-available'"], $serverToCheck ); - if (trim($buildkitEnabled) !== 'available') { + if (trim($buildxAvailable) === 'available') { + $this->dockerBuildkitSupported = true; + $this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} with BuildKit and Buildx detected on {$serverName}."); + } else { + // Fallback: test DOCKER_BUILDKIT=1 support via --progress flag $buildkitTest = instant_remote_process( - ["DOCKER_BUILDKIT=1 docker build --help 2>&1 | grep -q 'secret' && echo 'supported' || echo 'not-supported'"], + ["DOCKER_BUILDKIT=1 docker build --help 2>&1 | grep -q '\\-\\-progress' && echo 'supported' || echo 'not-supported'"], $serverToCheck ); if (trim($buildkitTest) === 'supported') { $this->dockerBuildkitSupported = true; - $this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} with BuildKit secrets support detected on {$serverName}."); - $this->application_deployment_queue->addLogEntry('Build secrets are enabled and will be used for enhanced security.'); + $this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} with BuildKit support detected on {$serverName}."); } else { $this->dockerBuildkitSupported = false; - $this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} on {$serverName} does not have BuildKit secrets support."); - $this->application_deployment_queue->addLogEntry('Build secrets feature is enabled but not supported. Using traditional build arguments.'); + $this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} on {$serverName} does not support BuildKit. Build output progress will be limited."); } - } else { - // Buildx is available, which means BuildKit is available - // Now specifically test for secrets support + } + + // If build secrets are enabled and BuildKit is available, verify --secret flag support + if ($this->application->settings->use_build_secrets && $this->dockerBuildkitSupported) { $secretsTest = instant_remote_process( ["docker build --help 2>&1 | grep -q 'secret' && echo 'supported' || echo 'not-supported'"], $serverToCheck ); if (trim($secretsTest) === 'supported') { - $this->dockerBuildkitSupported = true; - $this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} with BuildKit and Buildx detected on {$serverName}."); + $this->dockerSecretsSupported = true; $this->application_deployment_queue->addLogEntry('Build secrets are enabled and will be used for enhanced security.'); } else { - $this->dockerBuildkitSupported = false; - $this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} with Buildx on {$serverName}, but secrets not supported."); - $this->application_deployment_queue->addLogEntry('Build secrets feature is enabled but not supported. Using traditional build arguments.'); + $this->dockerSecretsSupported = false; + $this->application_deployment_queue->addLogEntry("Docker on {$serverName} does not support build secrets. Using traditional build arguments."); } } } catch (\Exception $e) { $this->dockerBuildkitSupported = false; + $this->dockerSecretsSupported = false; $this->application_deployment_queue->addLogEntry("Could not detect BuildKit capabilities on {$serverName}: {$e->getMessage()}"); - $this->application_deployment_queue->addLogEntry('Build secrets feature is enabled but detection failed. Using traditional build arguments.'); } } @@ -571,7 +568,7 @@ private function deploy_dockerimage_buildpack() private function deploy_docker_compose_buildpack() { if (data_get($this->application, 'docker_compose_location')) { - $this->docker_compose_location = $this->application->docker_compose_location; + $this->docker_compose_location = $this->validatePathField($this->application->docker_compose_location, 'docker_compose_location'); } if (data_get($this->application, 'docker_compose_custom_start_command')) { $this->docker_compose_custom_start_command = $this->application->docker_compose_custom_start_command; @@ -632,7 +629,7 @@ private function deploy_docker_compose_buildpack() // For raw compose, we cannot automatically add secrets configuration // User must define it manually in their docker-compose file - if ($this->application->settings->use_build_secrets && $this->dockerBuildkitSupported && ! empty($this->build_secrets)) { + if ($this->dockerSecretsSupported && ! empty($this->build_secrets)) { $this->application_deployment_queue->addLogEntry('Build secrets are configured. Ensure your docker-compose file includes build.secrets configuration for services that need them.'); } } else { @@ -653,7 +650,7 @@ private function deploy_docker_compose_buildpack() } // Add build secrets to compose file if enabled and BuildKit is supported - if ($this->application->settings->use_build_secrets && $this->dockerBuildkitSupported && ! empty($this->build_secrets)) { + if ($this->dockerSecretsSupported && ! empty($this->build_secrets)) { $composeFile = $this->add_build_secrets_to_compose($composeFile); } @@ -831,7 +828,7 @@ private function deploy_dockerfile_buildpack() $this->server = $this->build_server; } if (data_get($this->application, 'dockerfile_location')) { - $this->dockerfile_location = $this->application->dockerfile_location; + $this->dockerfile_location = $this->validatePathField($this->application->dockerfile_location, 'dockerfile_location'); } $this->prepare_builder_image(); $this->check_git_if_build_needed(); @@ -2826,7 +2823,11 @@ private function build_static_image() $nginx_config = base64_encode(defaultNginxConfiguration()); } } - $build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile --progress plain -t {$this->production_image_name} {$this->workdir}"; + if ($this->dockerBuildkitSupported) { + $build_command = "DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile --progress plain -t {$this->production_image_name} {$this->workdir}"; + } else { + $build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile -t {$this->production_image_name} {$this->workdir}"; + } $base64_build_command = base64_encode($build_command); $this->execute_remote_command( [ @@ -2866,21 +2867,19 @@ private function wrap_build_command_with_env_export(string $build_command): stri private function build_image() { // Add Coolify related variables to the build args/secrets - if ($this->dockerBuildkitSupported) { - // Coolify variables are already included in the secrets from generate_build_env_variables - // build_secrets is already a string at this point - } else { + if (! $this->dockerBuildkitSupported) { // Traditional build args approach - generate COOLIFY_ variables locally - // Generate COOLIFY_ variables locally for build args $coolify_envs = $this->generate_coolify_env_variables(forBuildTime: true); $coolify_envs->each(function ($value, $key) { $this->build_args->push("--build-arg '{$key}'"); }); - $this->build_args = $this->build_args instanceof \Illuminate\Support\Collection - ? $this->build_args->implode(' ') - : (string) $this->build_args; } + // Always convert build_args Collection to string for command interpolation + $this->build_args = $this->build_args instanceof \Illuminate\Support\Collection + ? $this->build_args->implode(' ') + : (string) $this->build_args; + $this->application_deployment_queue->addLogEntry('----------------------------------------'); if ($this->disableBuildCache) { $this->application_deployment_queue->addLogEntry('Docker build cache is disabled. It will not be used during the build process.'); @@ -2908,7 +2907,7 @@ private function build_image() executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), 'hidden' => true, ]); - if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { + if ($this->dockerSecretsSupported) { // Modify the nixpacks Dockerfile to use build secrets $this->modify_dockerfile_for_secrets("{$this->workdir}/.nixpacks/Dockerfile"); $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; @@ -2916,9 +2915,8 @@ private function build_image() } elseif ($this->dockerBuildkitSupported) { // BuildKit without secrets $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->build_image_name} {$this->build_args} {$this->workdir}"); - ray($build_command); } else { - $build_command = $this->wrap_build_command_with_env_export("docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->build_image_name} {$this->build_args} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile -t {$this->build_image_name} {$this->build_args} {$this->workdir}"); } } else { $this->execute_remote_command([ @@ -2928,18 +2926,16 @@ private function build_image() executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), 'hidden' => true, ]); - if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { + if ($this->dockerSecretsSupported) { // Modify the nixpacks Dockerfile to use build secrets $this->modify_dockerfile_for_secrets("{$this->workdir}/.nixpacks/Dockerfile"); $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile{$secrets_flags} --progress plain -t {$this->build_image_name} {$this->workdir}"); } elseif ($this->dockerBuildkitSupported) { // BuildKit without secrets - $this->modify_dockerfile_for_secrets("{$this->workdir}/.nixpacks/Dockerfile"); - $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; - $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile{$secrets_flags} --progress plain -t {$this->build_image_name} {$this->build_args} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->build_image_name} {$this->build_args} {$this->workdir}"); } else { - $build_command = $this->wrap_build_command_with_env_export("docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->build_image_name} {$this->build_args} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile -t {$this->build_image_name} {$this->build_args} {$this->workdir}"); } } @@ -2961,7 +2957,7 @@ private function build_image() $this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm '.self::NIXPACKS_PLAN_PATH), 'hidden' => true]); } else { // Dockerfile buildpack - if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { + if ($this->dockerSecretsSupported) { // Modify the Dockerfile to use build secrets $this->modify_dockerfile_for_secrets("{$this->workdir}{$this->dockerfile_location}"); $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; @@ -2972,19 +2968,17 @@ private function build_image() } } elseif ($this->dockerBuildkitSupported) { // BuildKit without secrets - $this->modify_dockerfile_for_secrets("{$this->workdir}{$this->dockerfile_location}"); - $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; if ($this->force_rebuild) { - $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t $this->build_image_name {$this->build_args} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} --progress plain -t $this->build_image_name {$this->build_args} {$this->workdir}"); } else { - $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t $this->build_image_name {$this->build_args} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} --progress plain -t $this->build_image_name {$this->build_args} {$this->workdir}"); } } else { // Traditional build with args if ($this->force_rebuild) { - $build_command = $this->wrap_build_command_with_env_export("docker build --no-cache {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("docker build --no-cache {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} -t $this->build_image_name {$this->workdir}"); } else { - $build_command = $this->wrap_build_command_with_env_export("docker build {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("docker build {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} -t $this->build_image_name {$this->workdir}"); } } $base64_build_command = base64_encode($build_command); @@ -3019,7 +3013,11 @@ private function build_image() $nginx_config = base64_encode(defaultNginxConfiguration()); } } - $build_command = $this->wrap_build_command_with_env_export("docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"); + if ($this->dockerBuildkitSupported) { + $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"); + } else { + $build_command = $this->wrap_build_command_with_env_export("docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile {$this->build_args} -t {$this->production_image_name} {$this->workdir}"); + } $base64_build_command = base64_encode($build_command); $this->execute_remote_command( [ @@ -3044,7 +3042,7 @@ private function build_image() } else { // Pure Dockerfile based deployment if ($this->application->dockerfile) { - if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { + if ($this->dockerSecretsSupported) { // Modify the Dockerfile to use build secrets $this->modify_dockerfile_for_secrets("{$this->workdir}{$this->dockerfile_location}"); $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; @@ -3053,12 +3051,19 @@ private function build_image() } else { $build_command = "DOCKER_BUILDKIT=1 docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->workdir}"; } - } else { - // Traditional build with args + } elseif ($this->dockerBuildkitSupported) { + // BuildKit without secrets if ($this->force_rebuild) { - $build_command = $this->wrap_build_command_with_env_export("docker build --no-cache --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); } else { - $build_command = $this->wrap_build_command_with_env_export("docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); + } + } else { + // Traditional build with args (no --progress for legacy builder compatibility) + if ($this->force_rebuild) { + $build_command = $this->wrap_build_command_with_env_export("docker build --no-cache --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} -t {$this->production_image_name} {$this->workdir}"); + } else { + $build_command = $this->wrap_build_command_with_env_export("docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} -t {$this->production_image_name} {$this->workdir}"); } } $base64_build_command = base64_encode($build_command); @@ -3088,18 +3093,16 @@ private function build_image() executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), 'hidden' => true, ]); - if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { + if ($this->dockerSecretsSupported) { // Modify the nixpacks Dockerfile to use build secrets $this->modify_dockerfile_for_secrets("{$this->workdir}/.nixpacks/Dockerfile"); $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->workdir}"); } elseif ($this->dockerBuildkitSupported) { // BuildKit without secrets - $this->modify_dockerfile_for_secrets("{$this->workdir}/.nixpacks/Dockerfile"); - $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; - $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); } else { - $build_command = $this->wrap_build_command_with_env_export("docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); } } else { $this->execute_remote_command([ @@ -3109,18 +3112,16 @@ private function build_image() executeInDocker($this->deployment_uuid, "cat {$this->workdir}/.nixpacks/Dockerfile"), 'hidden' => true, ]); - if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { + if ($this->dockerSecretsSupported) { // Modify the nixpacks Dockerfile to use build secrets $this->modify_dockerfile_for_secrets("{$this->workdir}/.nixpacks/Dockerfile"); $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->workdir}"); } elseif ($this->dockerBuildkitSupported) { // BuildKit without secrets - $this->modify_dockerfile_for_secrets("{$this->workdir}/.nixpacks/Dockerfile"); - $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; - $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); } else { - $build_command = $this->wrap_build_command_with_env_export("docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); } } $base64_build_command = base64_encode($build_command); @@ -3141,7 +3142,7 @@ private function build_image() $this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm '.self::NIXPACKS_PLAN_PATH), 'hidden' => true]); } else { // Dockerfile buildpack - if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { + if ($this->dockerSecretsSupported) { // Modify the Dockerfile to use build secrets $this->modify_dockerfile_for_secrets("{$this->workdir}{$this->dockerfile_location}"); // Use BuildKit with secrets @@ -3153,19 +3154,17 @@ private function build_image() } } elseif ($this->dockerBuildkitSupported) { // BuildKit without secrets - $this->modify_dockerfile_for_secrets("{$this->workdir}{$this->dockerfile_location}"); - $secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : ''; if ($this->force_rebuild) { - $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); } else { - $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} --progress plain -t {$this->production_image_name} {$this->build_args} {$this->workdir}"); } } else { // Traditional build with args if ($this->force_rebuild) { - $build_command = $this->wrap_build_command_with_env_export("docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} -t {$this->production_image_name} {$this->workdir}"); } else { - $build_command = $this->wrap_build_command_with_env_export("docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"); + $build_command = $this->wrap_build_command_with_env_export("docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} -t {$this->production_image_name} {$this->workdir}"); } } $base64_build_command = base64_encode($build_command); @@ -3341,7 +3340,7 @@ private function generate_build_env_variables() $this->analyzeBuildTimeVariables($variables); } - if ($this->dockerBuildkitSupported && $this->application->settings->use_build_secrets) { + if ($this->dockerSecretsSupported) { $this->generate_build_secrets($variables); $this->build_args = ''; } else { @@ -3828,7 +3827,7 @@ private function modify_dockerfiles_for_compose($composeFile) $this->application_deployment_queue->addLogEntry("Service {$serviceName}: All required ARG declarations already exist."); } - if ($this->application->settings->use_build_secrets && $this->dockerBuildkitSupported && ! empty($this->build_secrets)) { + if ($this->dockerSecretsSupported && ! empty($this->build_secrets)) { $fullDockerfilePath = "{$this->workdir}/{$dockerfilePath}"; $this->modify_dockerfile_for_secrets($fullDockerfilePath); $this->application_deployment_queue->addLogEntry("Modified Dockerfile for service {$serviceName} to use build secrets."); @@ -3888,6 +3887,18 @@ private function add_build_secrets_to_compose($composeFile) return $composeFile; } + private function validatePathField(string $value, string $fieldName): string + { + if (! preg_match('/^\/[a-zA-Z0-9._\-\/]+$/', $value)) { + throw new \RuntimeException("Invalid {$fieldName}: contains forbidden characters."); + } + if (str_contains($value, '..')) { + throw new \RuntimeException("Invalid {$fieldName}: path traversal detected."); + } + + return $value; + } + private function run_pre_deployment_command() { if (empty($this->application->pre_deployment_command)) { diff --git a/app/Jobs/ScheduledJobManager.php b/app/Jobs/ScheduledJobManager.php index c9dc20af1..d69585788 100644 --- a/app/Jobs/ScheduledJobManager.php +++ b/app/Jobs/ScheduledJobManager.php @@ -104,7 +104,7 @@ public function handle(): void Log::channel('scheduled')->info('ScheduledJobManager completed', [ 'execution_time' => $this->executionTime->toIso8601String(), - 'duration_ms' => Carbon::now()->diffInMilliseconds($this->executionTime), + 'duration_ms' => $this->executionTime->diffInMilliseconds(Carbon::now()), 'dispatched' => $this->dispatchedCount, 'skipped' => $this->skippedCount, ]); diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index b7c17fcc3..008bd3905 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -73,7 +73,7 @@ class General extends Component #[Validate(['string', 'nullable'])] public ?string $dockerfile = null; - #[Validate(['string', 'nullable'])] + #[Validate(['string', 'nullable', 'max:255', 'regex:/^\/[a-zA-Z0-9._\-\/]+$/'])] public ?string $dockerfileLocation = null; #[Validate(['string', 'nullable'])] @@ -85,7 +85,7 @@ class General extends Component #[Validate(['string', 'nullable'])] public ?string $dockerRegistryImageTag = null; - #[Validate(['string', 'nullable'])] + #[Validate(['string', 'nullable', 'max:255', 'regex:/^\/[a-zA-Z0-9._\-\/]+$/'])] public ?string $dockerComposeLocation = null; #[Validate(['string', 'nullable'])] diff --git a/app/Livewire/Project/New/GithubPrivateRepository.php b/app/Livewire/Project/New/GithubPrivateRepository.php index 6acb17f82..1bb276b89 100644 --- a/app/Livewire/Project/New/GithubPrivateRepository.php +++ b/app/Livewire/Project/New/GithubPrivateRepository.php @@ -163,10 +163,12 @@ public function submit() 'selected_repository_owner' => $this->selected_repository_owner, 'selected_repository_repo' => $this->selected_repository_repo, 'selected_branch_name' => $this->selected_branch_name, + 'docker_compose_location' => $this->docker_compose_location, ], [ 'selected_repository_owner' => 'required|string|regex:/^[a-zA-Z0-9\-_]+$/', 'selected_repository_repo' => 'required|string|regex:/^[a-zA-Z0-9\-_\.]+$/', 'selected_branch_name' => ['required', 'string', new ValidGitBranch], + 'docker_compose_location' => ['nullable', 'string', 'max:255', 'regex:/^\/[a-zA-Z0-9._\-\/]+$/'], ]); if ($validator->fails()) { diff --git a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php index 77b106200..f52c01e91 100644 --- a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php +++ b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php @@ -64,6 +64,7 @@ class GithubPrivateRepositoryDeployKey extends Component 'is_static' => 'required|boolean', 'publish_directory' => 'nullable|string', 'build_pack' => 'required|string', + 'docker_compose_location' => ['nullable', 'string', 'max:255', 'regex:/^\/[a-zA-Z0-9._\-\/]+$/'], ]; protected function rules() @@ -75,6 +76,7 @@ protected function rules() 'is_static' => 'required|boolean', 'publish_directory' => 'nullable|string', 'build_pack' => 'required|string', + 'docker_compose_location' => ['nullable', 'string', 'max:255', 'regex:/^\/[a-zA-Z0-9._\-\/]+$/'], ]; } diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index 2fffff6b9..a08c448dd 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -70,7 +70,7 @@ class PublicGitRepository extends Component 'publish_directory' => 'nullable|string', 'build_pack' => 'required|string', 'base_directory' => 'nullable|string', - 'docker_compose_location' => 'nullable|string', + 'docker_compose_location' => ['nullable', 'string', 'max:255', 'regex:/^\/[a-zA-Z0-9._\-\/]+$/'], ]; protected function rules() @@ -82,7 +82,7 @@ protected function rules() 'publish_directory' => 'nullable|string', 'build_pack' => 'required|string', 'base_directory' => 'nullable|string', - 'docker_compose_location' => 'nullable|string', + 'docker_compose_location' => ['nullable', 'string', 'max:255', 'regex:/^\/[a-zA-Z0-9._\-\/]+$/'], 'git_branch' => ['required', 'string', new ValidGitBranch], ]; } diff --git a/app/Models/Application.php b/app/Models/Application.php index b4d753ec4..c51ad4e81 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -992,7 +992,7 @@ public function deploymentType() if (isDev() && data_get($this, 'private_key_id') === 0) { return 'deploy_key'; } - if (data_get($this, 'private_key_id')) { + if (! is_null(data_get($this, 'private_key_id'))) { return 'deploy_key'; } elseif (data_get($this, 'source')) { return 'source'; diff --git a/app/Models/ServiceApplication.php b/app/Models/ServiceApplication.php index 7b8b46812..4bf78085e 100644 --- a/app/Models/ServiceApplication.php +++ b/app/Models/ServiceApplication.php @@ -88,7 +88,7 @@ public function type() public function team() { - return data_get($this, 'environment.project.team'); + return data_get($this, 'service.environment.project.team'); } public function workdir() diff --git a/app/Models/ServiceDatabase.php b/app/Models/ServiceDatabase.php index f6a39cfe4..7b0abe59e 100644 --- a/app/Models/ServiceDatabase.php +++ b/app/Models/ServiceDatabase.php @@ -124,7 +124,7 @@ public function getServiceDatabaseUrl() public function team() { - return data_get($this, 'environment.project.team'); + return data_get($this, 'service.environment.project.team'); } public function workdir() diff --git a/app/Models/Team.php b/app/Models/Team.php index 5cb186942..e32526169 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -191,7 +191,8 @@ public function isAnyNotificationEnabled() $this->getNotificationSettings('discord')?->isEnabled() || $this->getNotificationSettings('slack')?->isEnabled() || $this->getNotificationSettings('telegram')?->isEnabled() || - $this->getNotificationSettings('pushover')?->isEnabled(); + $this->getNotificationSettings('pushover')?->isEnabled() || + $this->getNotificationSettings('webhook')?->isEnabled(); } public function subscriptionEnded() diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php index d5c2c996b..5674d37f6 100644 --- a/bootstrap/helpers/api.php +++ b/bootstrap/helpers/api.php @@ -132,8 +132,8 @@ function sharedDataApplications() 'manual_webhook_secret_gitlab' => 'string|nullable', 'manual_webhook_secret_bitbucket' => 'string|nullable', 'manual_webhook_secret_gitea' => 'string|nullable', - 'dockerfile_location' => 'string|nullable', - 'docker_compose_location' => 'string', + 'dockerfile_location' => ['string', 'nullable', 'max:255', 'regex:/^\/[a-zA-Z0-9._\-\/]+$/'], + 'docker_compose_location' => ['string', 'nullable', 'max:255', 'regex:/^\/[a-zA-Z0-9._\-\/]+$/'], 'docker_compose' => 'string|nullable', 'docker_compose_domains' => 'array|nullable', 'docker_compose_custom_start_command' => 'string|nullable', diff --git a/bootstrap/helpers/parsers.php b/bootstrap/helpers/parsers.php index 43ba58e59..53060d28f 100644 --- a/bootstrap/helpers/parsers.php +++ b/bootstrap/helpers/parsers.php @@ -1233,7 +1233,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik( uuid: $uuid, domains: $fqdns, - is_force_https_enabled: true, + is_force_https_enabled: $originalResource->isForceHttpsEnabled(), serviceLabels: $serviceLabels, is_gzip_enabled: $originalResource->isGzipEnabled(), is_stripprefix_enabled: $originalResource->isStripprefixEnabled(), @@ -1246,7 +1246,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int network: $network, uuid: $uuid, domains: $fqdns, - is_force_https_enabled: true, + is_force_https_enabled: $originalResource->isForceHttpsEnabled(), serviceLabels: $serviceLabels, is_gzip_enabled: $originalResource->isGzipEnabled(), is_stripprefix_enabled: $originalResource->isStripprefixEnabled(), @@ -1260,7 +1260,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik( uuid: $uuid, domains: $fqdns, - is_force_https_enabled: true, + is_force_https_enabled: $originalResource->isForceHttpsEnabled(), serviceLabels: $serviceLabels, is_gzip_enabled: $originalResource->isGzipEnabled(), is_stripprefix_enabled: $originalResource->isStripprefixEnabled(), @@ -1271,7 +1271,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int network: $network, uuid: $uuid, domains: $fqdns, - is_force_https_enabled: true, + is_force_https_enabled: $originalResource->isForceHttpsEnabled(), serviceLabels: $serviceLabels, is_gzip_enabled: $originalResource->isGzipEnabled(), is_stripprefix_enabled: $originalResource->isStripprefixEnabled(), diff --git a/bootstrap/helpers/subscriptions.php b/bootstrap/helpers/subscriptions.php index 4b84fb7f6..709af854a 100644 --- a/bootstrap/helpers/subscriptions.php +++ b/bootstrap/helpers/subscriptions.php @@ -77,6 +77,7 @@ function allowedPathsForUnsubscribedAccounts() 'login', 'logout', 'force-password-reset', + 'two-factor-challenge', 'livewire/update', 'admin', ]; @@ -95,6 +96,7 @@ function allowedPathsForInvalidAccounts() 'logout', 'verify', 'force-password-reset', + 'two-factor-challenge', 'livewire/update', ]; } diff --git a/config/constants.php b/config/constants.php index 0b404fe9d..be41c4618 100644 --- a/config/constants.php +++ b/config/constants.php @@ -2,7 +2,7 @@ return [ 'coolify' => [ - 'version' => '4.0.0-beta.463', + 'version' => '4.0.0-beta.464', 'helper_version' => '1.0.12', 'realtime_version' => '1.0.10', 'self_hosted' => env('SELF_HOSTED', true), diff --git a/config/horizon.php b/config/horizon.php index cdabcb1e8..0423f1549 100644 --- a/config/horizon.php +++ b/config/horizon.php @@ -184,13 +184,13 @@ 'connection' => 'redis', 'balance' => env('HORIZON_BALANCE', 'false'), 'queue' => env('HORIZON_QUEUES', 'high,default'), - 'maxTime' => 3600, + 'maxTime' => env('HORIZON_MAX_TIME', 0), 'maxJobs' => 400, 'memory' => 128, 'tries' => 1, 'nice' => 0, 'sleep' => 3, - 'timeout' => 3600, + 'timeout' => env('HORIZON_TIMEOUT', 36000), ], ], diff --git a/database/seeders/ApplicationSeeder.php b/database/seeders/ApplicationSeeder.php index f5a00fe15..18ffbe166 100644 --- a/database/seeders/ApplicationSeeder.php +++ b/database/seeders/ApplicationSeeder.php @@ -21,7 +21,7 @@ public function run(): void 'git_repository' => 'coollabsio/coolify-examples', 'git_branch' => 'v4.x', 'base_directory' => '/docker-compose', - 'docker_compose_location' => 'docker-compose-test.yaml', + 'docker_compose_location' => '/docker-compose-test.yaml', 'build_pack' => 'dockercompose', 'ports_exposes' => '80', 'environment_id' => 1, diff --git a/docker-compose-maxio.dev.yml b/docker-compose-maxio.dev.yml new file mode 100644 index 000000000..e2650fb7b --- /dev/null +++ b/docker-compose-maxio.dev.yml @@ -0,0 +1,209 @@ +services: + coolify: + image: coolify:dev + pull_policy: never + build: + context: . + dockerfile: ./docker/development/Dockerfile + args: + - USER_ID=${USERID:-1000} + - GROUP_ID=${GROUPID:-1000} + ports: + - "${APP_PORT:-8000}:8080" + environment: + AUTORUN_ENABLED: false + PUSHER_HOST: "${PUSHER_HOST}" + PUSHER_PORT: "${PUSHER_PORT}" + PUSHER_SCHEME: "${PUSHER_SCHEME:-http}" + PUSHER_APP_ID: "${PUSHER_APP_ID:-coolify}" + PUSHER_APP_KEY: "${PUSHER_APP_KEY:-coolify}" + PUSHER_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}" + healthcheck: + test: curl -sf http://127.0.0.1:8080/api/health || exit 1 + interval: 5s + retries: 10 + timeout: 2s + volumes: + - .:/var/www/html/:cached + - dev_backups_data:/var/www/html/storage/app/backups + networks: + - coolify + postgres: + pull_policy: always + ports: + - "${FORWARD_DB_PORT:-5432}:5432" + env_file: + - .env + environment: + POSTGRES_USER: "${DB_USERNAME:-coolify}" + POSTGRES_PASSWORD: "${DB_PASSWORD:-password}" + POSTGRES_DB: "${DB_DATABASE:-coolify}" + POSTGRES_HOST_AUTH_METHOD: "trust" + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB" ] + interval: 5s + retries: 10 + timeout: 2s + volumes: + - dev_postgres_data:/var/lib/postgresql/data + redis: + pull_policy: always + ports: + - "${FORWARD_REDIS_PORT:-6379}:6379" + env_file: + - .env + healthcheck: + test: redis-cli ping + interval: 5s + retries: 10 + timeout: 2s + volumes: + - dev_redis_data:/data + soketi: + image: coolify-realtime:dev + pull_policy: never + build: + context: . + dockerfile: ./docker/coolify-realtime/Dockerfile + env_file: + - .env + ports: + - "${FORWARD_SOKETI_PORT:-6001}:6001" + - "6002:6002" + volumes: + - ./storage:/var/www/html/storage + - ./docker/coolify-realtime/terminal-server.js:/terminal/terminal-server.js + environment: + SOKETI_DEBUG: "false" + SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID:-coolify}" + SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY:-coolify}" + SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}" + healthcheck: + test: [ "CMD-SHELL", "curl -fsS http://127.0.0.1:6001/ready && curl -fsS http://127.0.0.1:6002/ready || exit 1" ] + interval: 5s + retries: 10 + timeout: 2s + entrypoint: ["/bin/sh", "/soketi-entrypoint.sh"] + vite: + image: node:24-alpine + pull_policy: always + container_name: coolify-vite + working_dir: /var/www/html + environment: + VITE_HOST: "${VITE_HOST:-localhost}" + VITE_PORT: "${VITE_PORT:-5173}" + ports: + - "${VITE_PORT:-5173}:${VITE_PORT:-5173}" + volumes: + - .:/var/www/html/:cached + command: sh -c "npm install && npm run dev" + networks: + - coolify + testing-host: + image: coolify-testing-host:dev + pull_policy: never + build: + context: . + dockerfile: ./docker/testing-host/Dockerfile + init: true + container_name: coolify-testing-host + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - dev_coolify_data:/data/coolify + - dev_backups_data:/data/coolify/backups + - dev_postgres_data:/data/coolify/_volumes/database + - dev_redis_data:/data/coolify/_volumes/redis + - dev_minio_data:/data/coolify/_volumes/minio + networks: + - coolify + mailpit: + image: axllent/mailpit:latest + pull_policy: always + container_name: coolify-mail + ports: + - "${FORWARD_MAILPIT_PORT:-1025}:1025" + - "${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025" + networks: + - coolify + # maxio: + # image: ghcr.io/coollabsio/maxio + # pull_policy: always + # container_name: coolify-maxio + # ports: + # - "${FORWARD_MAXIO_PORT:-9000}:9000" + # environment: + # MAXIO_ACCESS_KEY: "${MAXIO_ACCESS_KEY:-maxioadmin}" + # MAXIO_SECRET_KEY: "${MAXIO_SECRET_KEY:-maxioadmin}" + # volumes: + # - dev_maxio_data:/data + # networks: + # - coolify + minio: + image: ghcr.io/coollabsio/minio:RELEASE.2025-10-15T17-29-55Z # Released on 15 October 2025 + pull_policy: always + container_name: coolify-minio + command: server /data --console-address ":9001" + ports: + - "${FORWARD_MINIO_PORT:-9000}:9000" + - "${FORWARD_MINIO_PORT_CONSOLE:-9001}:9001" + environment: + MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY:-minioadmin}" + MINIO_SECRET_KEY: "${MINIO_SECRET_KEY:-minioadmin}" + volumes: + - dev_minio_data:/data + - dev_maxio_data:/data + networks: + - coolify + # maxio-init: + # image: minio/mc:latest + # pull_policy: always + # container_name: coolify-maxio-init + # restart: no + # depends_on: + # - maxio + # entrypoint: > + # /bin/sh -c " + # echo 'Waiting for MaxIO to be ready...'; + # until mc alias set local http://coolify-maxio:9000 maxioadmin maxioadmin 2>/dev/null; do + # echo 'MaxIO not ready yet, waiting...'; + # sleep 2; + # done; + # echo 'MaxIO is ready, creating bucket if needed...'; + # mc mb local/local --ignore-existing; + # echo 'MaxIO initialization complete - bucket local is ready'; + # " + # networks: + # - coolify + minio-init: + image: minio/mc:latest + pull_policy: always + container_name: coolify-minio-init + restart: no + depends_on: + - minio + entrypoint: > + /bin/sh -c " + echo 'Waiting for MinIO to be ready...'; + until mc alias set local http://coolify-minio:9000 minioadmin minioadmin 2>/dev/null; do + echo 'MinIO not ready yet, waiting...'; + sleep 2; + done; + echo 'MinIO is ready, creating bucket if needed...'; + mc mb local/local --ignore-existing; + echo 'MinIO initialization complete - bucket local is ready'; + " + networks: + - coolify + +volumes: + dev_backups_data: + dev_postgres_data: + dev_redis_data: + dev_coolify_data: + dev_minio_data: + dev_maxio_data: + +networks: + coolify: + name: coolify + external: false diff --git a/resources/views/components/forms/input.blade.php b/resources/views/components/forms/input.blade.php index a329664a2..cf72dfbe9 100644 --- a/resources/views/components/forms/input.blade.php +++ b/resources/views/components/forms/input.blade.php @@ -25,7 +25,7 @@ class="flex absolute inset-y-0 right-0 items-center pr-2 cursor-pointer dark:hov {{-- Eye-off icon (shown when password is visible) --}} - diff --git a/resources/views/errors/419.blade.php b/resources/views/errors/419.blade.php index e7cd3fc45..8569f4e22 100644 --- a/resources/views/errors/419.blade.php +++ b/resources/views/errors/419.blade.php @@ -3,15 +3,11 @@

419

This page is definitely old, not like you!

-

Sorry, we couldn't find the page you're looking - for. +

Your session has expired. Please log in again to continue.

- - Go back - - - Dashboard + + Back to Login Contact support diff --git a/resources/views/livewire/project/application/heading.blade.php b/resources/views/livewire/project/application/heading.blade.php index 96e5d9770..4af466fc5 100644 --- a/resources/views/livewire/project/application/heading.blade.php +++ b/resources/views/livewire/project/application/heading.blade.php @@ -1,7 +1,7 @@