refactor(file-transfer): replace base64 encoding with direct file transfer method across multiple database actions for improved clarity and efficiency

This commit is contained in:
Andras Bacsai
2025-09-08 14:04:24 +02:00
parent 852b2688d9
commit 18068857b1
17 changed files with 298 additions and 107 deletions

View File

@@ -99,8 +99,12 @@ class StartClickhouse
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$docker_compose_base64 = base64_encode($docker_compose); $this->commands[] = [
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; 'transfer_file' => [
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";

View File

@@ -52,8 +52,9 @@ class StartDatabaseProxy
} }
$configuration_dir = database_proxy_dir($database->uuid); $configuration_dir = database_proxy_dir($database->uuid);
$volume_configuration_dir = $configuration_dir;
if (isDev()) { if (isDev()) {
$configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$database->uuid.'/proxy'; $volume_configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$database->uuid.'/proxy';
} }
$nginxconf = <<<EOF $nginxconf = <<<EOF
user nginx; user nginx;
@@ -86,7 +87,7 @@ class StartDatabaseProxy
'volumes' => [ 'volumes' => [
[ [
'type' => 'bind', 'type' => 'bind',
'source' => "$configuration_dir/nginx.conf", 'source' => "$volume_configuration_dir/nginx.conf",
'target' => '/etc/nginx/nginx.conf', 'target' => '/etc/nginx/nginx.conf',
], ],
], ],
@@ -115,8 +116,18 @@ class StartDatabaseProxy
instant_remote_process(["docker rm -f $proxyContainerName"], $server, false); instant_remote_process(["docker rm -f $proxyContainerName"], $server, false);
instant_remote_process([ instant_remote_process([
"mkdir -p $configuration_dir", "mkdir -p $configuration_dir",
"echo '{$nginxconf_base64}' | base64 -d | tee $configuration_dir/nginx.conf > /dev/null", [
"echo '{$dockercompose_base64}' | base64 -d | tee $configuration_dir/docker-compose.yaml > /dev/null", 'transfer_file' => [
'content' => base64_decode($nginxconf_base64),
'destination' => "$configuration_dir/nginx.conf",
],
],
[
'transfer_file' => [
'content' => base64_decode($dockercompose_base64),
'destination' => "$configuration_dir/docker-compose.yaml",
],
],
"docker compose --project-directory {$configuration_dir} pull", "docker compose --project-directory {$configuration_dir} pull",
"docker compose --project-directory {$configuration_dir} up -d", "docker compose --project-directory {$configuration_dir} up -d",
], $server); ], $server);

View File

@@ -183,8 +183,12 @@ class StartDragonfly
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$docker_compose_base64 = base64_encode($docker_compose); $this->commands[] = [
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; 'transfer_file' => [
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";

View File

@@ -199,8 +199,12 @@ class StartKeydb
$docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options); $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$docker_compose_base64 = base64_encode($docker_compose); $this->commands[] = [
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; 'transfer_file' => [
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";

View File

@@ -203,8 +203,12 @@ class StartMariadb
} }
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$docker_compose_base64 = base64_encode($docker_compose); $this->commands[] = [
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; 'transfer_file' => [
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
@@ -284,7 +288,11 @@ class StartMariadb
} }
$filename = 'custom-config.cnf'; $filename = 'custom-config.cnf';
$content = $this->database->mariadb_conf; $content = $this->database->mariadb_conf;
$content_base64 = base64_encode($content); $this->commands[] = [
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null"; 'transfer_file' => [
'content' => $content,
'destination' => "$this->configuration_dir/{$filename}",
],
];
} }
} }

View File

@@ -18,6 +18,8 @@ class StartMongodb
public string $configuration_dir; public string $configuration_dir;
public string $volume_configuration_dir;
private ?SslCertificate $ssl_certificate = null; private ?SslCertificate $ssl_certificate = null;
public function handle(StandaloneMongodb $database) public function handle(StandaloneMongodb $database)
@@ -27,9 +29,9 @@ class StartMongodb
$startCommand = 'mongod'; $startCommand = 'mongod';
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->volume_configuration_dir = $this->configuration_dir = database_configuration_dir().'/'.$container_name;
if (isDev()) { if (isDev()) {
$this->configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$container_name; $this->volume_configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$container_name;
} }
$this->commands = [ $this->commands = [
@@ -176,7 +178,7 @@ class StartMongodb
$docker_compose['services'][$container_name]['volumes'] ?? [], $docker_compose['services'][$container_name]['volumes'] ?? [],
[[ [[
'type' => 'bind', 'type' => 'bind',
'source' => $this->configuration_dir.'/mongod.conf', 'source' => $this->volume_configuration_dir.'/mongod.conf',
'target' => '/etc/mongo/mongod.conf', 'target' => '/etc/mongo/mongod.conf',
'read_only' => true, 'read_only' => true,
]] ]]
@@ -190,7 +192,7 @@ class StartMongodb
$docker_compose['services'][$container_name]['volumes'] ?? [], $docker_compose['services'][$container_name]['volumes'] ?? [],
[[ [[
'type' => 'bind', 'type' => 'bind',
'source' => $this->configuration_dir.'/docker-entrypoint-initdb.d', 'source' => $this->volume_configuration_dir.'/docker-entrypoint-initdb.d',
'target' => '/docker-entrypoint-initdb.d', 'target' => '/docker-entrypoint-initdb.d',
'read_only' => true, 'read_only' => true,
]] ]]
@@ -254,8 +256,12 @@ class StartMongodb
} }
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$docker_compose_base64 = base64_encode($docker_compose); $this->commands[] = [
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; 'transfer_file' => [
'content' => $docker_compose,
'destination' => "$this->volume_configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
@@ -332,15 +338,22 @@ class StartMongodb
} }
$filename = 'mongod.conf'; $filename = 'mongod.conf';
$content = $this->database->mongo_conf; $content = $this->database->mongo_conf;
$content_base64 = base64_encode($content); $this->commands[] = [
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null"; 'transfer_file' => [
'content' => $content,
'destination' => "$this->configuration_dir/{$filename}",
],
];
} }
private function add_default_database() private function add_default_database()
{ {
$content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});"; $content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});";
$content_base64 = base64_encode($content); $this->commands[] = [
$this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d"; 'transfer_file' => [
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js > /dev/null"; 'content' => $content,
'destination' => "$this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js",
],
];
} }
} }

View File

@@ -204,8 +204,12 @@ class StartMysql
} }
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$docker_compose_base64 = base64_encode($docker_compose); $this->commands[] = [
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; 'transfer_file' => [
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
@@ -287,7 +291,11 @@ class StartMysql
} }
$filename = 'custom-config.cnf'; $filename = 'custom-config.cnf';
$content = $this->database->mysql_conf; $content = $this->database->mysql_conf;
$content_base64 = base64_encode($content); $this->commands[] = [
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null"; 'transfer_file' => [
'content' => $content,
'destination' => "$this->configuration_dir/{$filename}",
],
];
} }
} }

View File

@@ -20,6 +20,8 @@ class StartPostgresql
public string $configuration_dir; public string $configuration_dir;
public string $volume_configuration_dir;
private ?SslCertificate $ssl_certificate = null; private ?SslCertificate $ssl_certificate = null;
public function handle(StandalonePostgresql $database) public function handle(StandalonePostgresql $database)
@@ -27,8 +29,9 @@ class StartPostgresql
$this->database = $database; $this->database = $database;
$container_name = $this->database->uuid; $container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir().'/'.$container_name; $this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->volume_configuration_dir = $this->configuration_dir;
if (isDev()) { if (isDev()) {
$this->configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$container_name; $this->volume_configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$container_name;
} }
$this->commands = [ $this->commands = [
@@ -192,7 +195,7 @@ class StartPostgresql
$docker_compose['services'][$container_name]['volumes'], $docker_compose['services'][$container_name]['volumes'],
[[ [[
'type' => 'bind', 'type' => 'bind',
'source' => $this->configuration_dir.'/custom-postgres.conf', 'source' => $this->volume_configuration_dir.'/custom-postgres.conf',
'target' => '/etc/postgresql/postgresql.conf', 'target' => '/etc/postgresql/postgresql.conf',
'read_only' => true, 'read_only' => true,
]] ]]
@@ -217,8 +220,12 @@ class StartPostgresql
} }
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$docker_compose_base64 = base64_encode($docker_compose); $this->commands[] = [
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; 'transfer_file' => [
'content' => $docker_compose,
'destination' => "$this->volume_configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";
@@ -302,8 +309,12 @@ class StartPostgresql
foreach ($this->database->init_scripts as $init_script) { foreach ($this->database->init_scripts as $init_script) {
$filename = data_get($init_script, 'filename'); $filename = data_get($init_script, 'filename');
$content = data_get($init_script, 'content'); $content = data_get($init_script, 'content');
$content_base64 = base64_encode($content); $this->commands[] = [
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/{$filename} > /dev/null"; 'transfer_file' => [
'content' => $content,
'destination' => "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}",
],
];
$this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}"; $this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
} }
} }
@@ -325,7 +336,11 @@ class StartPostgresql
$this->database->postgres_conf = $content; $this->database->postgres_conf = $content;
$this->database->save(); $this->database->save();
} }
$content_base64 = base64_encode($content); $this->commands[] = [
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $config_file_path > /dev/null"; 'transfer_file' => [
'content' => $content,
'destination' => $config_file_path,
],
];
} }
} }

View File

@@ -196,8 +196,12 @@ class StartRedis
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
$docker_compose = Yaml::dump($docker_compose, 10); $docker_compose = Yaml::dump($docker_compose, 10);
$docker_compose_base64 = base64_encode($docker_compose); $this->commands[] = [
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; 'transfer_file' => [
'content' => $docker_compose,
'destination' => "$this->configuration_dir/docker-compose.yml",
],
];
$readme = generate_readme_file($this->database->name, now()); $readme = generate_readme_file($this->database->name, now());
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md"; $this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
$this->commands[] = "echo 'Pulling {$database->image} image.'"; $this->commands[] = "echo 'Pulling {$database->image} image.'";

View File

@@ -22,7 +22,12 @@ class SaveConfiguration
return instant_remote_process([ return instant_remote_process([
"mkdir -p $proxy_path", "mkdir -p $proxy_path",
"echo '$docker_compose_yml_base64' | base64 -d | tee $proxy_path/docker-compose.yml > /dev/null", [
'transfer_file' => [
'content' => base64_decode($docker_compose_yml_base64),
'destination' => "$proxy_path/docker-compose.yml",
],
],
], $server); ], $server);
} }
} }

View File

@@ -40,7 +40,12 @@ class ConfigureCloudflared
$commands = collect([ $commands = collect([
'mkdir -p /tmp/cloudflared', 'mkdir -p /tmp/cloudflared',
'cd /tmp/cloudflared', 'cd /tmp/cloudflared',
"echo '$docker_compose_yml_base64' | base64 -d | tee docker-compose.yml > /dev/null", [
'transfer_file' => [
'content' => base64_decode($docker_compose_yml_base64),
'destination' => '/tmp/cloudflared/docker-compose.yml',
],
],
'echo Pulling latest Cloudflare Tunnel image.', 'echo Pulling latest Cloudflare Tunnel image.',
'docker compose pull', 'docker compose pull',
'echo Stopping existing Cloudflare Tunnel container.', 'echo Stopping existing Cloudflare Tunnel container.',

View File

@@ -14,6 +14,7 @@ class InstallDocker
public function handle(Server $server) public function handle(Server $server)
{ {
ray('install docker');
$dockerVersion = config('constants.docker.minimum_required_version'); $dockerVersion = config('constants.docker.minimum_required_version');
$supported_os_type = $server->validateOS(); $supported_os_type = $server->validateOS();
if (! $supported_os_type) { if (! $supported_os_type) {
@@ -103,8 +104,15 @@ class InstallDocker
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}", "curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}",
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'", "echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
'test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json "/etc/docker/daemon.json.original-$(date +"%Y%m%d-%H%M%S")"', 'test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json "/etc/docker/daemon.json.original-$(date +"%Y%m%d-%H%M%S")"',
"test ! -s /etc/docker/daemon.json && echo '{$config}' | base64 -d | tee /etc/docker/daemon.json > /dev/null", [
"echo '{$config}' | base64 -d | tee /etc/docker/daemon.json.coolify > /dev/null", 'transfer_file' => [
'content' => base64_decode($config),
'destination' => '/tmp/daemon.json.new',
],
],
'test ! -s /etc/docker/daemon.json && cp /tmp/daemon.json.new /etc/docker/daemon.json',
'cp /tmp/daemon.json.new /etc/docker/daemon.json.coolify',
'rm -f /tmp/daemon.json.new',
'jq . /etc/docker/daemon.json.coolify | tee /etc/docker/daemon.json.coolify.pretty > /dev/null', 'jq . /etc/docker/daemon.json.coolify | tee /etc/docker/daemon.json.coolify.pretty > /dev/null',
'mv /etc/docker/daemon.json.coolify.pretty /etc/docker/daemon.json.coolify', 'mv /etc/docker/daemon.json.coolify.pretty /etc/docker/daemon.json.coolify',
"jq -s '.[0] * .[1]' /etc/docker/daemon.json.coolify /etc/docker/daemon.json | tee /etc/docker/daemon.json.appended > /dev/null", "jq -s '.[0] * .[1]' /etc/docker/daemon.json.coolify /etc/docker/daemon.json | tee /etc/docker/daemon.json.appended > /dev/null",

View File

@@ -180,10 +180,30 @@ Files:
$command = [ $command = [
"echo 'Saving configuration'", "echo 'Saving configuration'",
"mkdir -p $config_path", "mkdir -p $config_path",
"echo '{$parsers}' | base64 -d | tee $parsers_config > /dev/null", [
"echo '{$config}' | base64 -d | tee $fluent_bit_config > /dev/null", 'transfer_file' => [
"echo '{$compose}' | base64 -d | tee $compose_path > /dev/null", 'content' => base64_decode($parsers),
"echo '{$readme}' | base64 -d | tee $readme_path > /dev/null", 'destination' => $parsers_config,
],
],
[
'transfer_file' => [
'content' => base64_decode($config),
'destination' => $fluent_bit_config,
],
],
[
'transfer_file' => [
'content' => base64_decode($compose),
'destination' => $compose_path,
],
],
[
'transfer_file' => [
'content' => base64_decode($readme),
'destination' => $readme_path,
],
],
"test -f $config_path/.env && rm $config_path/.env", "test -f $config_path/.env && rm $config_path/.env",
]; ];
if ($type === 'newrelic') { if ($type === 'newrelic') {

View File

@@ -388,11 +388,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$dockerfile_base64 = base64_encode($this->application->dockerfile); $dockerfile_base64 = base64_encode($this->application->dockerfile);
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->application->name} to {$this->server->name}."); $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->application->name} to {$this->server->name}.");
$this->prepare_builder_image(); $this->prepare_builder_image();
$this->execute_remote_command( $dockerfile_content = base64_decode($dockerfile_base64);
[ transfer_file_to_container($dockerfile_content, "{$this->workdir}{$this->dockerfile_location}", $this->deployment_uuid, $this->server);
executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null"),
],
);
$this->generate_image_names(); $this->generate_image_names();
$this->generate_compose_file(); $this->generate_compose_file();
$this->generate_build_env_variables(); $this->generate_build_env_variables();
@@ -497,10 +494,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$yaml = Yaml::dump(convertToArray($composeFile), 10); $yaml = Yaml::dump(convertToArray($composeFile), 10);
} }
$this->docker_compose_base64 = base64_encode($yaml); $this->docker_compose_base64 = base64_encode($yaml);
$this->execute_remote_command([ transfer_file_to_container($yaml, "{$this->workdir}{$this->docker_compose_location}", $this->deployment_uuid, $this->server);
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"),
'hidden' => true,
]);
// Build new container to limit downtime. // Build new container to limit downtime.
$this->application_deployment_queue->addLogEntry('Pulling & building required images.'); $this->application_deployment_queue->addLogEntry('Pulling & building required images.');
@@ -715,13 +709,12 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$composeFileName = "$mainDir/docker-compose-pr-{$this->pull_request_id}.yaml"; $composeFileName = "$mainDir/docker-compose-pr-{$this->pull_request_id}.yaml";
$this->docker_compose_location = "/docker-compose-pr-{$this->pull_request_id}.yaml"; $this->docker_compose_location = "/docker-compose-pr-{$this->pull_request_id}.yaml";
} }
$this->execute_remote_command([
"mkdir -p $mainDir",
]);
$docker_compose_content = base64_decode($this->docker_compose_base64);
transfer_file_to_server($docker_compose_content, $composeFileName, $this->server);
$this->execute_remote_command( $this->execute_remote_command(
[
"mkdir -p $mainDir",
],
[
"echo '{$this->docker_compose_base64}' | base64 -d | tee $composeFileName > /dev/null",
],
[ [
"echo '{$readme}' > $mainDir/README.md", "echo '{$readme}' > $mainDir/README.md",
] ]
@@ -1013,27 +1006,15 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
); );
} }
} else { } else {
$envs_base64 = base64_encode($envs->implode("\n")); $envs_content = $envs->implode("\n");
$this->execute_remote_command( transfer_file_to_container($envs_content, "$this->workdir/{$this->env_filename}", $this->deployment_uuid, $this->server);
[
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d | tee $this->workdir/{$this->env_filename} > /dev/null"),
],
);
if ($this->use_build_server) { if ($this->use_build_server) {
$this->server = $this->original_server; $this->server = $this->original_server;
$this->execute_remote_command( transfer_file_to_server($envs_content, "$this->configuration_dir/{$this->env_filename}", $this->server);
[
"echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null",
]
);
$this->server = $this->build_server; $this->server = $this->build_server;
} else { } else {
$this->execute_remote_command( transfer_file_to_server($envs_content, "$this->configuration_dir/{$this->env_filename}", $this->server);
[
"echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null",
]
);
} }
} }
$this->environment_variables = $envs; $this->environment_variables = $envs;
@@ -1444,13 +1425,12 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$private_key = data_get($this->application, 'private_key.private_key'); $private_key = data_get($this->application, 'private_key.private_key');
if ($private_key) { if ($private_key) {
$private_key = base64_encode($private_key); $private_key = base64_encode($private_key);
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, 'mkdir -p /root/.ssh'),
]);
$key_content = base64_decode($private_key);
transfer_file_to_container($key_content, '/root/.ssh/id_rsa', $this->deployment_uuid, $this->server);
$this->execute_remote_command( $this->execute_remote_command(
[
executeInDocker($this->deployment_uuid, 'mkdir -p /root/.ssh'),
],
[
executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null"),
],
[ [
executeInDocker($this->deployment_uuid, 'chmod 600 /root/.ssh/id_rsa'), executeInDocker($this->deployment_uuid, 'chmod 600 /root/.ssh/id_rsa'),
], ],
@@ -1993,7 +1973,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->docker_compose = Yaml::dump($docker_compose, 10); $this->docker_compose = Yaml::dump($docker_compose, 10);
$this->docker_compose_base64 = base64_encode($this->docker_compose); $this->docker_compose_base64 = base64_encode($this->docker_compose);
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}/docker-compose.yaml > /dev/null"), 'hidden' => true]); transfer_file_to_container(base64_decode($this->docker_compose_base64), "{$this->workdir}/docker-compose.yaml", $this->deployment_uuid, $this->server);
} }
private function generate_local_persistent_volumes() private function generate_local_persistent_volumes()
@@ -2121,7 +2101,8 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
} else { } else {
if ($this->application->build_pack === 'nixpacks') { if ($this->application->build_pack === 'nixpacks') {
$this->nixpacks_plan = base64_encode($this->nixpacks_plan); $this->nixpacks_plan = base64_encode($this->nixpacks_plan);
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]); $nixpacks_content = base64_decode($this->nixpacks_plan);
transfer_file_to_container($nixpacks_content, '/artifacts/thegameplan.json', $this->deployment_uuid, $this->server);
if ($this->force_rebuild) { if ($this->force_rebuild) {
$this->execute_remote_command([ $this->execute_remote_command([
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"),
@@ -2139,7 +2120,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$base64_build_command = base64_encode($build_command); $base64_build_command = base64_encode($build_command);
$this->execute_remote_command( $this->execute_remote_command(
[ [
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server),
'hidden' => true, 'hidden' => true,
], ],
[ [
@@ -2162,7 +2143,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
} }
$this->execute_remote_command( $this->execute_remote_command(
[ [
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server),
'hidden' => true, 'hidden' => true,
], ],
[ [
@@ -2194,13 +2175,13 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$base64_build_command = base64_encode($build_command); $base64_build_command = base64_encode($build_command);
$this->execute_remote_command( $this->execute_remote_command(
[ [
executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d | tee {$this->workdir}/Dockerfile > /dev/null"), transfer_file_to_container(base64_decode($dockerfile), "{$this->workdir}/Dockerfile", $this->deployment_uuid, $this->server),
], ],
[ [
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null"), transfer_file_to_container(base64_decode($nginx_config), "{$this->workdir}/nginx.conf", $this->deployment_uuid, $this->server),
], ],
[ [
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server),
'hidden' => true, 'hidden' => true,
], ],
[ [
@@ -2223,7 +2204,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$base64_build_command = base64_encode($build_command); $base64_build_command = base64_encode($build_command);
$this->execute_remote_command( $this->execute_remote_command(
[ [
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server),
'hidden' => true, 'hidden' => true,
], ],
[ [
@@ -2238,7 +2219,8 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
} else { } else {
if ($this->application->build_pack === 'nixpacks') { if ($this->application->build_pack === 'nixpacks') {
$this->nixpacks_plan = base64_encode($this->nixpacks_plan); $this->nixpacks_plan = base64_encode($this->nixpacks_plan);
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]); $nixpacks_content = base64_decode($this->nixpacks_plan);
transfer_file_to_container($nixpacks_content, '/artifacts/thegameplan.json', $this->deployment_uuid, $this->server);
if ($this->force_rebuild) { if ($this->force_rebuild) {
$this->execute_remote_command([ $this->execute_remote_command([
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"),
@@ -2255,7 +2237,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$base64_build_command = base64_encode($build_command); $base64_build_command = base64_encode($build_command);
$this->execute_remote_command( $this->execute_remote_command(
[ [
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server),
'hidden' => true, 'hidden' => true,
], ],
[ [
@@ -2278,7 +2260,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
} }
$this->execute_remote_command( $this->execute_remote_command(
[ [
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), transfer_file_to_container(base64_decode($base64_build_command), '/artifacts/build.sh', $this->deployment_uuid, $this->server),
'hidden' => true, 'hidden' => true,
], ],
[ [
@@ -2405,7 +2387,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
} }
$dockerfile_base64 = base64_encode($dockerfile->implode("\n")); $dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
$this->execute_remote_command([ $this->execute_remote_command([
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null"), transfer_file_to_container(base64_decode($dockerfile_base64), "{$this->workdir}{$this->dockerfile_location}", $this->deployment_uuid, $this->server),
'hidden' => true, 'hidden' => true,
]); ]);
} }

View File

@@ -1082,7 +1082,6 @@ $schema://$host {
public function validateConnection(bool $justCheckingNewKey = false) public function validateConnection(bool $justCheckingNewKey = false)
{ {
ray('validateConnection', $this->id);
$this->disableSshMux(); $this->disableSshMux();
if ($this->skipServer()) { if ($this->skipServer()) {
@@ -1320,7 +1319,6 @@ $schema://$host {
public function generateCaCertificate() public function generateCaCertificate()
{ {
try { try {
ray('Generating CA certificate for server', $this->id);
SslHelper::generateSslCertificate( SslHelper::generateSslCertificate(
commonName: 'Coolify CA Certificate', commonName: 'Coolify CA Certificate',
serverId: $this->id, serverId: $this->id,
@@ -1328,7 +1326,6 @@ $schema://$host {
validityDays: 10 * 365 validityDays: 10 * 365
); );
$caCertificate = SslCertificate::where('server_id', $this->id)->where('is_ca_certificate', true)->first(); $caCertificate = SslCertificate::where('server_id', $this->id)->where('is_ca_certificate', true)->first();
ray('CA certificate generated', $caCertificate);
if ($caCertificate) { if ($caCertificate) {
$certificateContent = $caCertificate->ssl_certificate; $certificateContent = $caCertificate->ssl_certificate;
$caCertPath = config('constants.coolify.base_config_path').'/ssl/'; $caCertPath = config('constants.coolify.base_config_path').'/ssl/';

View File

@@ -1281,8 +1281,10 @@ class Service extends BaseModel
if ($envs->count() === 0) { if ($envs->count() === 0) {
$commands[] = 'touch .env'; $commands[] = 'touch .env';
} else { } else {
$envs_base64 = base64_encode($envs->implode("\n")); $envs_content = $envs->implode("\n");
$commands[] = "echo '$envs_base64' | base64 -d | tee .env > /dev/null"; transfer_file_to_server($envs_content, $this->workdir().'/.env', $this->server);
return;
} }
instant_remote_process($commands, $this->server); instant_remote_process($commands, $this->server);

View File

@@ -29,11 +29,31 @@ function remote_process(
$type = $type ?? ActivityTypes::INLINE->value; $type = $type ?? ActivityTypes::INLINE->value;
$command = $command instanceof Collection ? $command->toArray() : $command; $command = $command instanceof Collection ? $command->toArray() : $command;
if ($server->isNonRoot()) { // Process commands and handle file transfers
$command = parseCommandsByLineForSudo(collect($command), $server); $processed_commands = [];
foreach ($command as $cmd) {
if (is_array($cmd) && isset($cmd['transfer_file'])) {
// Handle file transfer command
$transfer_data = $cmd['transfer_file'];
$content = $transfer_data['content'];
$destination = $transfer_data['destination'];
// Execute file transfer immediately
transfer_file_to_server($content, $destination, $server, ! $ignore_errors);
// Add a comment to the command log for visibility
$processed_commands[] = "# File transferred via SCP: $destination";
} else {
// Regular string command
$processed_commands[] = $cmd;
}
} }
$command_string = implode("\n", $command); if ($server->isNonRoot()) {
$processed_commands = parseCommandsByLineForSudo(collect($processed_commands), $server);
}
$command_string = implode("\n", $processed_commands);
if (Auth::check()) { if (Auth::check()) {
$teams = Auth::user()->teams->pluck('id'); $teams = Auth::user()->teams->pluck('id');
@@ -84,6 +104,66 @@ function instant_scp(string $source, string $dest, Server $server, $throwError =
); );
} }
function transfer_file_to_container(string $content, string $container_path, string $deployment_uuid, Server $server, bool $throwError = true): ?string
{
$temp_file = tempnam(sys_get_temp_dir(), 'coolify_env_');
try {
// Write content to temporary file
file_put_contents($temp_file, $content);
// Generate unique filename for server transfer
$server_temp_file = '/tmp/coolify_env_'.uniqid().'_'.$deployment_uuid;
// Transfer file to server
instant_scp($temp_file, $server_temp_file, $server, $throwError);
// Ensure parent directory exists in container, then copy file
$parent_dir = dirname($container_path);
$commands = [];
if ($parent_dir !== '.' && $parent_dir !== '/') {
$commands[] = executeInDocker($deployment_uuid, "mkdir -p \"$parent_dir\"");
}
$commands[] = "docker cp $server_temp_file $deployment_uuid:$container_path";
$commands[] = "rm -f $server_temp_file"; // Cleanup server temp file
return instant_remote_process_with_timeout($commands, $server, $throwError);
} finally {
ray($temp_file);
// Always cleanup local temp file
if (file_exists($temp_file)) {
unlink($temp_file);
}
}
}
function transfer_file_to_server(string $content, string $server_path, Server $server, bool $throwError = true): ?string
{
$temp_file = tempnam(sys_get_temp_dir(), 'coolify_env_');
try {
// Write content to temporary file
file_put_contents($temp_file, $content);
// Ensure parent directory exists on server
$parent_dir = dirname($server_path);
if ($parent_dir !== '.' && $parent_dir !== '/') {
instant_remote_process_with_timeout(["mkdir -p \"$parent_dir\""], $server, $throwError);
}
// Transfer file directly to server destination
return instant_scp($temp_file, $server_path, $server, $throwError);
} finally {
ray($temp_file);
// Always cleanup local temp file
if (file_exists($temp_file)) {
unlink($temp_file);
}
}
}
function instant_remote_process_with_timeout(Collection|array $command, Server $server, bool $throwError = true, bool $no_sudo = false): ?string function instant_remote_process_with_timeout(Collection|array $command, Server $server, bool $throwError = true, bool $no_sudo = false): ?string
{ {
$command = $command instanceof Collection ? $command->toArray() : $command; $command = $command instanceof Collection ? $command->toArray() : $command;
@@ -121,10 +201,31 @@ function instant_remote_process_with_timeout(Collection|array $command, Server $
function instant_remote_process(Collection|array $command, Server $server, bool $throwError = true, bool $no_sudo = false): ?string function instant_remote_process(Collection|array $command, Server $server, bool $throwError = true, bool $no_sudo = false): ?string
{ {
$command = $command instanceof Collection ? $command->toArray() : $command; $command = $command instanceof Collection ? $command->toArray() : $command;
if ($server->isNonRoot() && ! $no_sudo) {
$command = parseCommandsByLineForSudo(collect($command), $server); // Process commands and handle file transfers
$processed_commands = [];
foreach ($command as $cmd) {
if (is_array($cmd) && isset($cmd['transfer_file'])) {
// Handle file transfer command
$transfer_data = $cmd['transfer_file'];
$content = $transfer_data['content'];
$destination = $transfer_data['destination'];
// Execute file transfer immediately
transfer_file_to_server($content, $destination, $server, $throwError);
// Add a comment to the command log for visibility
$processed_commands[] = "# File transferred via SCP: $destination";
} else {
// Regular string command
$processed_commands[] = $cmd;
}
} }
$command_string = implode("\n", $command);
if ($server->isNonRoot() && ! $no_sudo) {
$processed_commands = parseCommandsByLineForSudo(collect($processed_commands), $server);
}
$command_string = implode("\n", $processed_commands);
return \App\Helpers\SshRetryHandler::retry( return \App\Helpers\SshRetryHandler::retry(
function () use ($server, $command_string) { function () use ($server, $command_string) {