2
.github/workflows/production-build.yml
vendored
@ -3,6 +3,8 @@ name: Production Build (v4)
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths-ignore:
|
||||
- templates/service-templates.json
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
|
@ -99,7 +99,7 @@ public function handle(StandaloneClickhouse $database)
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
|
@ -149,9 +149,9 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
|
||||
instant_remote_process(["docker rm -f $proxyContainerName"], $server, false);
|
||||
instant_remote_process([
|
||||
"mkdir -p $configuration_dir",
|
||||
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
||||
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
||||
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
||||
"echo '{$dockerfile_base64}' | base64 -d | tee $configuration_dir/Dockerfile > /dev/null",
|
||||
"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",
|
||||
"docker compose --project-directory {$configuration_dir} pull",
|
||||
"docker compose --project-directory {$configuration_dir} up --build -d",
|
||||
], $server);
|
||||
|
@ -100,7 +100,7 @@ public function handle(StandaloneDragonfly $database)
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
|
@ -107,7 +107,7 @@ public function handle(StandaloneKeydb $database)
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
|
@ -100,7 +100,7 @@ public function handle(StandaloneMariadb $database)
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
@ -171,6 +171,6 @@ private function add_custom_mysql()
|
||||
$filename = 'custom-config.cnf';
|
||||
$content = $this->database->mariadb_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ public function handle(StandaloneMongodb $database)
|
||||
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
@ -184,13 +184,13 @@ private function add_custom_mongo_conf()
|
||||
$filename = 'mongod.conf';
|
||||
$content = $this->database->mongo_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||
}
|
||||
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_base64 = base64_encode($content);
|
||||
$this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js > /dev/null";
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ public function handle(StandaloneMysql $database)
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
@ -171,6 +171,6 @@ private function add_custom_mysql()
|
||||
$filename = 'custom-config.cnf';
|
||||
$content = $this->database->mysql_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ public function handle(StandalonePostgresql $database)
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
@ -197,7 +197,7 @@ private function generate_init_scripts()
|
||||
$filename = data_get($init_script, 'filename');
|
||||
$content = data_get($init_script, 'content');
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/{$filename} > /dev/null";
|
||||
$this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
||||
}
|
||||
}
|
||||
@ -209,6 +209,6 @@ private function add_custom_conf()
|
||||
$filename = 'custom-postgres.conf';
|
||||
$content = $this->database->postgres_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ public function handle(StandaloneRedis $database)
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||
|
@ -16,11 +16,11 @@ public function handle(Server $server, bool $reset = false)
|
||||
return 'OK';
|
||||
}
|
||||
$proxy_path = $server->proxyPath();
|
||||
|
||||
$proxy_configuration = instant_remote_process([
|
||||
$payload = [
|
||||
"mkdir -p $proxy_path",
|
||||
"cat $proxy_path/docker-compose.yml",
|
||||
], $server, false);
|
||||
];
|
||||
$proxy_configuration = instant_remote_process($payload, $server, false);
|
||||
|
||||
if ($reset || !$proxy_configuration || is_null($proxy_configuration)) {
|
||||
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
|
||||
|
@ -13,8 +13,9 @@ public function handle(Server $server, $fromUI = false)
|
||||
if ($server->proxyType() === 'NONE') {
|
||||
return false;
|
||||
}
|
||||
if (!$server->validateConnection()) {
|
||||
throw new \Exception("Server Connection Error");
|
||||
['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
|
||||
if (!$uptime) {
|
||||
throw new \Exception($error);
|
||||
}
|
||||
if (!$server->isProxyShouldRun()) {
|
||||
if ($fromUI) {
|
||||
|
@ -23,7 +23,7 @@ public function handle(Server $server, ?string $proxy_settings = null)
|
||||
|
||||
return instant_remote_process([
|
||||
"mkdir -p $proxy_path",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d | tee $proxy_path/docker-compose.yml > /dev/null",
|
||||
], $server);
|
||||
}
|
||||
}
|
||||
|
@ -30,16 +30,18 @@ public function handle(Server $server, bool $async = true): string|Activity
|
||||
$server->save();
|
||||
if ($server->isSwarm()) {
|
||||
$commands = $commands->merge([
|
||||
"mkdir -p $proxy_path/dynamic && cd $proxy_path",
|
||||
"mkdir -p $proxy_path/dynamic",
|
||||
"cd $proxy_path",
|
||||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Starting coolify-proxy.'",
|
||||
"cd $proxy_path && docker stack deploy -c docker-compose.yml coolify-proxy",
|
||||
"docker stack deploy -c docker-compose.yml coolify-proxy",
|
||||
"echo 'Proxy started successfully.'"
|
||||
]);
|
||||
} else {
|
||||
$caddfile = "import /dynamic/*.caddy";
|
||||
$commands = $commands->merge([
|
||||
"mkdir -p $proxy_path/dynamic && cd $proxy_path",
|
||||
"mkdir -p $proxy_path/dynamic",
|
||||
"cd $proxy_path",
|
||||
"echo '$caddfile' > $proxy_path/dynamic/Caddyfile",
|
||||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Pulling docker image.'",
|
||||
|
@ -29,8 +29,9 @@ public function handle(Server $server, string $cloudflare_token)
|
||||
$config = Yaml::dump($config, 12, 2);
|
||||
$docker_compose_yml_base64 = base64_encode($config);
|
||||
$commands = collect([
|
||||
"mkdir -p /tmp/cloudflared && cd /tmp/cloudflared",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d > docker-compose.yml",
|
||||
"mkdir -p /tmp/cloudflared",
|
||||
"cd /tmp/cloudflared",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d | tee docker-compose.yml > /dev/null",
|
||||
"docker compose pull",
|
||||
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||
"docker compose up -d --remove-orphans",
|
||||
@ -39,6 +40,11 @@ public function handle(Server $server, string $cloudflare_token)
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
throw $e;
|
||||
} finally {
|
||||
$commands = collect([
|
||||
"rm -fr /tmp/cloudflared",
|
||||
]);
|
||||
instant_remote_process($commands, $server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,20 +48,28 @@ public function handle(Server $server)
|
||||
if ($supported_os_type->contains('debian')) {
|
||||
$command = $command->merge([
|
||||
"echo 'Installing Prerequisites...'",
|
||||
"command -v jq >/dev/null || apt-get update -y",
|
||||
"command -v jq >/dev/null || apt install -y curl wget git jq",
|
||||
|
||||
"apt-get update -y",
|
||||
"command -v curl >/dev/null || apt install -y curl",
|
||||
"command -v wget >/dev/null || apt install -y wget",
|
||||
"command -v git >/dev/null || apt install -y git",
|
||||
"command -v jq >/dev/null || apt install -y jq",
|
||||
]);
|
||||
} else if ($supported_os_type->contains('rhel')) {
|
||||
$command = $command->merge([
|
||||
"echo 'Installing Prerequisites...'",
|
||||
"command -v jq >/dev/null || dnf install -y curl wget git jq",
|
||||
"command -v curl >/dev/null || dnf install -y curl",
|
||||
"command -v wget >/dev/null || dnf install -y wget",
|
||||
"command -v git >/dev/null || dnf install -y git",
|
||||
"command -v jq >/dev/null || dnf install -y jq",
|
||||
]);
|
||||
} else if ($supported_os_type->contains('sles')) {
|
||||
$command = $command->merge([
|
||||
"echo 'Installing Prerequisites...'",
|
||||
"command -v jq >/dev/null || zypper update -y",
|
||||
"command -v jq >/dev/null || zypper install -y curl wget git jq",
|
||||
"zypper update -y",
|
||||
"command -v curl >/dev/null || zypper install -y curl",
|
||||
"command -v wget >/dev/null || zypper install -y wget",
|
||||
"command -v git >/dev/null || zypper install -y git",
|
||||
"command -v jq >/dev/null || zypper install -y jq",
|
||||
]);
|
||||
} else {
|
||||
throw new \Exception('Unsupported OS');
|
||||
@ -70,10 +78,13 @@ public function handle(Server $server)
|
||||
"echo 'Installing Docker Engine...'",
|
||||
"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)...'",
|
||||
"test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json",
|
||||
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
||||
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify",
|
||||
"cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json",
|
||||
"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",
|
||||
"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",
|
||||
"jq -s '.[0] * .[1]' /etc/docker/daemon.json.coolify /etc/docker/daemon.json | tee /etc/docker/daemon.json.appended > /dev/null",
|
||||
"mv /etc/docker/daemon.json.appended /etc/docker/daemon.json",
|
||||
"echo 'Restarting Docker Engine...'",
|
||||
"systemctl enable docker >/dev/null 2>&1 || true",
|
||||
"systemctl restart docker",
|
||||
|
@ -168,10 +168,10 @@ public function handle(Server $server)
|
||||
$command = [
|
||||
"echo 'Saving configuration'",
|
||||
"mkdir -p $config_path",
|
||||
"echo '{$parsers}' | base64 -d > $parsers_config",
|
||||
"echo '{$config}' | base64 -d > $fluent_bit_config",
|
||||
"echo '{$compose}' | base64 -d > $compose_path",
|
||||
"echo '{$readme}' | base64 -d > $readme_path",
|
||||
"echo '{$parsers}' | base64 -d | tee $parsers_config > /dev/null",
|
||||
"echo '{$config}' | base64 -d | tee $fluent_bit_config > /dev/null",
|
||||
"echo '{$compose}' | base64 -d | tee $compose_path > /dev/null",
|
||||
"echo '{$readme}' | base64 -d | tee $readme_path > /dev/null",
|
||||
"test -f $config_path/.env && rm $config_path/.env",
|
||||
|
||||
];
|
||||
|
@ -31,7 +31,7 @@ protected function schedule(Schedule $schedule): void
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->check_resources($schedule);
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->pull_helper_image($schedule);
|
||||
// $this->pull_helper_image($schedule);
|
||||
$this->check_scheduled_tasks($schedule);
|
||||
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||
} else {
|
||||
@ -46,7 +46,7 @@ protected function schedule(Schedule $schedule): void
|
||||
$this->instance_auto_update($schedule);
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->check_resources($schedule);
|
||||
$this->pull_helper_image($schedule);
|
||||
// $this->pull_helper_image($schedule);
|
||||
$this->check_scheduled_tasks($schedule);
|
||||
|
||||
$schedule->command('cleanup:database --yes')->daily();
|
||||
|
@ -322,7 +322,7 @@ private function deploy_simple_dockerfile()
|
||||
$this->prepare_builder_image();
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d > {$this->workdir}{$this->dockerfile_location}")
|
||||
executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null")
|
||||
],
|
||||
);
|
||||
$this->generate_image_names();
|
||||
@ -391,7 +391,7 @@ private function deploy_docker_compose_buildpack()
|
||||
}
|
||||
$this->docker_compose_base64 = base64_encode($yaml);
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"), "hidden" => true
|
||||
]);
|
||||
$this->save_environment_variables();
|
||||
// Build new container to limit downtime.
|
||||
@ -565,7 +565,7 @@ private function write_deployment_configurations()
|
||||
"mkdir -p $this->configuration_dir"
|
||||
],
|
||||
[
|
||||
"echo '{$this->docker_compose_base64}' | base64 -d > $composeFileName",
|
||||
"echo '{$this->docker_compose_base64}' | base64 -d | tee $composeFileName > /dev/null",
|
||||
],
|
||||
[
|
||||
"echo '{$readme}' > $this->configuration_dir/README.md",
|
||||
@ -708,19 +708,68 @@ private function check_image_locally_or_remotely()
|
||||
private function save_environment_variables()
|
||||
{
|
||||
$envs = collect([]);
|
||||
$ports = $this->application->settings->is_static ? [80] : $this->application->ports_exposes_array;
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$this->env_filename = ".env-pr-$this->pull_request_id";
|
||||
foreach ($this->application->environment_variables_preview as $env) {
|
||||
$envs->push($env->key . '=' . $env->real_value);
|
||||
$real_value = $env->real_value;
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
if ($env->is_literal) {
|
||||
$real_value = '\'' . $real_value . '\'';
|
||||
}
|
||||
$envs->push($env->key . '=' . $real_value);
|
||||
}
|
||||
// Add PORT if not exists, use the first port as default
|
||||
if ($this->application->environment_variables_preview->filter(fn ($env) => Str::of($env)->startsWith('PORT'))->isEmpty()) {
|
||||
$envs->push("PORT={$ports[0]}");
|
||||
}
|
||||
// Add HOST if not exists
|
||||
if ($this->application->environment_variables_preview->filter(fn ($env) => Str::of($env)->startsWith('HOST'))->isEmpty()) {
|
||||
$envs->push("HOST=0.0.0.0");
|
||||
}
|
||||
if ($this->application->environment_variables_preview->filter(fn ($env) => Str::of($env)->startsWith('SOURCE_COMMIT'))->isEmpty()) {
|
||||
if (!is_null($this->commit)) {
|
||||
$envs->push("SOURCE_COMMIT={$this->commit}");
|
||||
} else {
|
||||
$envs->push("SOURCE_COMMIT=unknown");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->env_filename = ".env";
|
||||
$this->env_filename = ".env-coolify";
|
||||
foreach ($this->application->environment_variables as $env) {
|
||||
$envs->push($env->key . '=' . $env->real_value);
|
||||
$real_value = $env->real_value;
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
if ($env->is_literal) {
|
||||
$real_value = '\'' . $real_value . '\'';
|
||||
}
|
||||
$envs->push($env->key . '=' . $real_value);
|
||||
}
|
||||
// Add PORT if not exists, use the first port as default
|
||||
if ($this->application->environment_variables->filter(fn ($env) => Str::of($env)->startsWith('PORT'))->isEmpty()) {
|
||||
$envs->push("PORT={$ports[0]}");
|
||||
}
|
||||
// Add HOST if not exists
|
||||
if ($this->application->environment_variables->filter(fn ($env) => Str::of($env)->startsWith('HOST'))->isEmpty()) {
|
||||
$envs->push("HOST=0.0.0.0");
|
||||
}
|
||||
if ($this->application->environment_variables->filter(fn ($env) => Str::of($env)->startsWith('SOURCE_COMMIT'))->isEmpty()) {
|
||||
if (!is_null($this->commit)) {
|
||||
$envs->push("SOURCE_COMMIT={$this->commit}");
|
||||
} else {
|
||||
$envs->push("SOURCE_COMMIT=unknown");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($envs->isEmpty()) {
|
||||
$this->env_filename = null;
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"command" => "rm -f $this->configuration_dir/{$this->env_filename}",
|
||||
@ -728,15 +777,28 @@ private function save_environment_variables()
|
||||
"ignore_errors" => true
|
||||
]
|
||||
);
|
||||
$this->env_filename = null;
|
||||
return;
|
||||
}
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "cat $this->workdir/.env 2>/dev/null || true"),
|
||||
"hidden" => true,
|
||||
"save" => "dotenv"
|
||||
]);
|
||||
if (str($this->saved_outputs->get('dotenv'))->isNotEmpty()) {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo '{$this->saved_outputs->get('dotenv')->value()}' | tee $this->configuration_dir/.env > /dev/null"
|
||||
]
|
||||
);
|
||||
}
|
||||
$envs_base64 = base64_encode($envs->implode("\n"));
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d > $this->workdir/.env")
|
||||
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d | tee $this->workdir/{$this->env_filename} > /dev/null")
|
||||
],
|
||||
[
|
||||
"echo '$envs_base64' | base64 -d > $this->configuration_dir/{$this->env_filename}"
|
||||
"echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null"
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -902,6 +964,10 @@ private function prepare_builder_image()
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage.");
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"command" => "docker pull -q {$helperImage}",
|
||||
"hidden" => true
|
||||
],
|
||||
[
|
||||
"command" => "docker rm -f {$this->deployment_uuid}",
|
||||
"ignore_errors" => true,
|
||||
@ -973,7 +1039,7 @@ private function check_git_if_build_needed()
|
||||
executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh")
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa")
|
||||
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")
|
||||
@ -1124,8 +1190,8 @@ private function generate_compose_file()
|
||||
}
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables($ports);
|
||||
|
||||
// $environment_variables = $this->generate_environment_variables($ports);
|
||||
$this->save_environment_variables();
|
||||
if (data_get($this->application, 'custom_labels')) {
|
||||
$this->application->parseContainerLabels();
|
||||
$labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels)));
|
||||
@ -1179,7 +1245,6 @@ private function generate_compose_file()
|
||||
'image' => $this->production_image_name,
|
||||
'container_name' => $this->container_name,
|
||||
'restart' => RESTART_MODE,
|
||||
'environment' => $environment_variables,
|
||||
'expose' => $ports,
|
||||
'networks' => [
|
||||
$this->destination->network,
|
||||
@ -1200,11 +1265,21 @@ private function generate_compose_file()
|
||||
]
|
||||
]
|
||||
];
|
||||
if ($this->env_filename) {
|
||||
$docker_compose['services'][$this->container_name]['env_file'] = [
|
||||
$this->env_filename
|
||||
];
|
||||
if (str($this->saved_outputs->get('dotenv'))->isNotEmpty()) {
|
||||
if (data_get($docker_compose, "services.{$this->container_name}.env_file")) {
|
||||
$docker_compose['services'][$this->container_name]['env_file'][] = '.env';
|
||||
} else {
|
||||
$docker_compose['services'][$this->container_name]['env_file'] = ['.env'];
|
||||
}
|
||||
}
|
||||
if ($this->env_filename) {
|
||||
if (data_get($docker_compose, "services.{$this->container_name}.env_file")) {
|
||||
$docker_compose['services'][$this->container_name]['env_file'][] = $this->env_filename;
|
||||
} else {
|
||||
$docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->custom_healthcheck_found) {
|
||||
$docker_compose['services'][$this->container_name]['healthcheck'] = [
|
||||
'test' => [
|
||||
@ -1357,8 +1432,7 @@ private function generate_compose_file()
|
||||
|
||||
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$this->docker_compose_base64 = base64_encode($this->docker_compose);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
|
||||
$this->save_environment_variables();
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}/docker-compose.yml > /dev/null"), "hidden" => true]);
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes()
|
||||
@ -1415,6 +1489,9 @@ private function generate_environment_variables($ports)
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
if ($env->is_literal) {
|
||||
$real_value = escapeDollarSign($real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
foreach ($this->application->nixpacks_environment_variables as $env) {
|
||||
@ -1423,6 +1500,9 @@ private function generate_environment_variables($ports)
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
if ($env->is_literal) {
|
||||
$real_value = escapeDollarSign($real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
} else {
|
||||
@ -1432,6 +1512,9 @@ private function generate_environment_variables($ports)
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
if ($env->is_literal) {
|
||||
$real_value = escapeDollarSign($real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
|
||||
@ -1440,6 +1523,9 @@ private function generate_environment_variables($ports)
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
if ($env->is_literal) {
|
||||
$real_value = escapeDollarSign($real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
}
|
||||
@ -1539,7 +1625,7 @@ private function build_image()
|
||||
} else {
|
||||
if ($this->application->build_pack === 'nixpacks') {
|
||||
$this->nixpacks_plan = base64_encode($this->nixpacks_plan);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d > /artifacts/thegameplan.json"), "hidden" => true]);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), "hidden" => true]);
|
||||
if ($this->force_rebuild) {
|
||||
$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}"), "hidden" => true
|
||||
@ -1560,7 +1646,7 @@ private function build_image()
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d > /artifacts/build.sh"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
|
||||
@ -1595,13 +1681,13 @@ private function build_image()
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d > {$this->workdir}/Dockerfile")
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d | tee {$this->workdir}/Dockerfile > /dev/null")
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
|
||||
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null")
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d > /artifacts/build.sh"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
|
||||
@ -1614,7 +1700,7 @@ private function build_image()
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d > /artifacts/build.sh"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
|
||||
@ -1623,7 +1709,7 @@ private function build_image()
|
||||
} else {
|
||||
if ($this->application->build_pack === 'nixpacks') {
|
||||
$this->nixpacks_plan = base64_encode($this->nixpacks_plan);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d > /artifacts/thegameplan.json"), "hidden" => true]);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), "hidden" => true]);
|
||||
if ($this->force_rebuild) {
|
||||
$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}"), "hidden" => true
|
||||
@ -1644,7 +1730,7 @@ private function build_image()
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d > /artifacts/build.sh"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
|
||||
@ -1762,7 +1848,7 @@ private function add_build_env_variables_to_dockerfile()
|
||||
}
|
||||
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d > {$this->workdir}{$this->dockerfile_location}"),
|
||||
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null"),
|
||||
"hidden" => true
|
||||
]);
|
||||
}
|
||||
|
@ -47,7 +47,6 @@ public function handle()
|
||||
if (!$this->server->isFunctional()) {
|
||||
return 'Server is not ready.';
|
||||
};
|
||||
|
||||
$applications = $this->server->applications();
|
||||
$skip_these_applications = collect([]);
|
||||
foreach ($applications as $application) {
|
||||
@ -78,6 +77,7 @@ public function handle()
|
||||
if (is_null($containers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$containers = format_docker_command_output_to_json($containers);
|
||||
if ($containerReplicates) {
|
||||
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
|
||||
@ -201,7 +201,6 @@ public function handle()
|
||||
// Notify user that this container should not be there.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (data_get($container, 'Name') === '/coolify-db') {
|
||||
$foundDatabases[] = 0;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@ -16,11 +17,11 @@ class ServerFilesFromServerJob implements ShouldQueue, ShouldBeEncrypted
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
|
||||
public function __construct(public ServiceApplication|ServiceDatabase $service)
|
||||
public function __construct(public ServiceApplication|ServiceDatabase|Application $resource)
|
||||
{
|
||||
}
|
||||
public function handle()
|
||||
{
|
||||
$this->service->getFilesFromServer(isInit: true);
|
||||
$this->resource->getFilesFromServer(isInit: true);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ class ActivityMonitor extends Component
|
||||
public $activityId;
|
||||
public $eventToDispatch = 'activityFinished';
|
||||
public $isPollingActive = false;
|
||||
public bool $fullHeight = false;
|
||||
public bool $showWaiting = false;
|
||||
|
||||
protected $activity;
|
||||
|
@ -11,15 +11,17 @@ class NewActivityMonitor extends Component
|
||||
public ?string $header = null;
|
||||
public $activityId;
|
||||
public $eventToDispatch = 'activityFinished';
|
||||
public $eventData = null;
|
||||
public $isPollingActive = false;
|
||||
|
||||
protected $activity;
|
||||
protected $listeners = ['newActivityMonitor' => 'newMonitorActivity'];
|
||||
|
||||
public function newMonitorActivity($activityId, $eventToDispatch = 'activityFinished')
|
||||
public function newMonitorActivity($activityId, $eventToDispatch = 'activityFinished', $eventData = null)
|
||||
{
|
||||
$this->activityId = $activityId;
|
||||
$this->eventToDispatch = $eventToDispatch;
|
||||
$this->eventData = $eventData;
|
||||
|
||||
$this->hydrateActivity();
|
||||
|
||||
@ -55,8 +57,12 @@ public function polling()
|
||||
}
|
||||
return;
|
||||
}
|
||||
$this->dispatch($this->eventToDispatch);
|
||||
ray('Dispatched event: ' . $this->eventToDispatch);
|
||||
if (!is_null($this->eventData)) {
|
||||
$this->dispatch($this->eventToDispatch, $this->eventData);
|
||||
} else {
|
||||
$this->dispatch($this->eventToDispatch);
|
||||
}
|
||||
ray('Dispatched event: ' . $this->eventToDispatch . ' with data: ' . $this->eventData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Livewire\Project\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\LocalFileVolume;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Component;
|
||||
@ -124,7 +125,7 @@ public function mount()
|
||||
}
|
||||
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
$this->customLabels = $this->application->parseContainerLabels();
|
||||
$this->customLabels = $this->application->parseContainerLabels();
|
||||
if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
|
||||
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||
@ -156,8 +157,36 @@ public function loadComposeFile($isInit = false)
|
||||
return;
|
||||
}
|
||||
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
|
||||
$compose = $this->application->parseCompose();
|
||||
$services = data_get($compose, 'services');
|
||||
if ($services) {
|
||||
$volumes = collect($services)->map(function ($service) {
|
||||
return data_get($service, 'volumes');
|
||||
})->flatten()->filter(function ($volume) {
|
||||
return str($volume)->startsWith('/data/coolify');
|
||||
})->unique()->values();
|
||||
foreach ($volumes as $volume) {
|
||||
$source = Str::of($volume)->before(':');
|
||||
$target = Str::of($volume)->after(':')->beforeLast(':');
|
||||
|
||||
LocalFileVolume::updateOrCreate(
|
||||
[
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $this->application->id,
|
||||
'resource_type' => get_class($this->application)
|
||||
],
|
||||
[
|
||||
'fs_path' => $source,
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $this->application->id,
|
||||
'resource_type' => get_class($this->application)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->dispatch('success', 'Docker compose file loaded.');
|
||||
$this->dispatch('compose_loaded');
|
||||
$this->dispatch('refresh_storages');
|
||||
} catch (\Throwable $e) {
|
||||
$this->application->docker_compose_location = $this->initialDockerComposeLocation;
|
||||
$this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
|
||||
@ -165,7 +194,6 @@ public function loadComposeFile($isInit = false)
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->initLoadingCompose = false;
|
||||
|
||||
}
|
||||
}
|
||||
public function generateDomain(string $serviceName)
|
||||
@ -184,7 +212,14 @@ public function updatedApplicationBaseDirectory()
|
||||
$this->loadComposeFile();
|
||||
}
|
||||
}
|
||||
public function updatedApplicationFqdn() {
|
||||
public function updatedApplicationFqdn()
|
||||
{
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
$this->resetDefaultLabels();
|
||||
}
|
||||
public function updatedApplicationBuildPack()
|
||||
|
@ -25,6 +25,7 @@ public function saveKey($data)
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'is_literal' => $data['is_literal'],
|
||||
'type' => 'project',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
|
@ -29,6 +29,7 @@ public function saveKey($data)
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'is_literal' => $data['is_literal'],
|
||||
'type' => 'environment',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
|
@ -89,9 +89,11 @@ public function load_any_git()
|
||||
public function load_branch()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'repository_url' => 'required|url'
|
||||
]);
|
||||
if (str($this->repository_url)->startsWith('git@')) {
|
||||
$github_instance = str($this->repository_url)->after('git@')->before(':');
|
||||
$repository = str($this->repository_url)->after(':')->before('.git');
|
||||
$this->repository_url = 'https://' . str($github_instance) . '/' . $repository;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public function mount()
|
||||
$database = create_standalone_keydb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'dragonfly') {
|
||||
$database = create_standalone_dragonfly($environment->id, $destination_uuid);
|
||||
}else if ($type->value() === 'clickhouse') {
|
||||
} else if ($type->value() === 'clickhouse') {
|
||||
$database = create_standalone_clickhouse($environment->id, $destination_uuid);
|
||||
}
|
||||
return redirect()->route('project.database.configuration', [
|
||||
@ -60,14 +60,19 @@ public function mount()
|
||||
}
|
||||
if ($oneClickService) {
|
||||
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||
$service = Service::create([
|
||||
$service_payload = [
|
||||
'name' => "$oneClickServiceName-" . str()->random(10),
|
||||
'docker_compose_raw' => base64_decode($oneClickService),
|
||||
'environment_id' => $environment->id,
|
||||
'service_type' => $oneClickServiceName,
|
||||
'server_id' => (int) $server_id,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_type' => $destination->getMorphClass(),
|
||||
]);
|
||||
];
|
||||
if ($oneClickServiceName === 'cloudflared') {
|
||||
data_set($service_payload, 'connect_to_docker_network', true);
|
||||
}
|
||||
$service = Service::create($service_payload);
|
||||
$service->name = "$oneClickServiceName-" . $service->uuid;
|
||||
$service->save();
|
||||
if ($oneClickDotEnvs?->count() > 0) {
|
||||
|
@ -13,11 +13,13 @@ class EditCompose extends Component
|
||||
'service.docker_compose_raw' => 'required',
|
||||
'service.docker_compose' => 'required',
|
||||
];
|
||||
public function mount() {
|
||||
public function mount()
|
||||
{
|
||||
$this->service = Service::find($this->serviceId);
|
||||
}
|
||||
|
||||
public function saveEditedCompose() {
|
||||
public function saveEditedCompose()
|
||||
{
|
||||
$this->dispatch('info', "Saving new docker compose...");
|
||||
$this->dispatch('saveCompose', $this->service->docker_compose_raw);
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public function submit()
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->dispatch('generateDockerCompose');
|
||||
$this->application->service->parse();
|
||||
$this->dispatch('refresh');
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\LocalFileVolume;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
@ -12,7 +13,7 @@
|
||||
class FileStorage extends Component
|
||||
{
|
||||
public LocalFileVolume $fileStorage;
|
||||
public ServiceApplication|ServiceDatabase|StandaloneClickhouse $resource;
|
||||
public ServiceApplication|ServiceDatabase|StandaloneClickhouse|Application $resource;
|
||||
public string $fs_path;
|
||||
public ?string $workdir = null;
|
||||
|
||||
@ -33,6 +34,43 @@ public function mount()
|
||||
$this->fs_path = $this->fileStorage->fs_path;
|
||||
}
|
||||
}
|
||||
public function convertToDirectory() {
|
||||
try {
|
||||
$this->fileStorage->deleteStorageOnServer();
|
||||
$this->fileStorage->is_directory = true;
|
||||
$this->fileStorage->content = null;
|
||||
$this->fileStorage->save();
|
||||
$this->fileStorage->saveStorageOnServer();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->dispatch('refresh_storages');
|
||||
}
|
||||
}
|
||||
public function convertToFile() {
|
||||
try {
|
||||
$this->fileStorage->deleteStorageOnServer();
|
||||
$this->fileStorage->is_directory = false;
|
||||
$this->fileStorage->content = null;
|
||||
$this->fileStorage->save();
|
||||
$this->fileStorage->saveStorageOnServer();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->dispatch('refresh_storages');
|
||||
}
|
||||
}
|
||||
public function delete() {
|
||||
try {
|
||||
$this->fileStorage->deleteStorageOnServer();
|
||||
$this->fileStorage->delete();
|
||||
$this->dispatch('success', 'File deleted.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->dispatch('refresh_storages');
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
$original = $this->fileStorage->getOriginal();
|
||||
|
@ -3,12 +3,13 @@
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\Service;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class StackForm extends Component
|
||||
{
|
||||
public Service $service;
|
||||
public $fields = [];
|
||||
public Collection $fields;
|
||||
protected $listeners = ["saveCompose"];
|
||||
public $rules = [
|
||||
'service.docker_compose_raw' => 'required',
|
||||
@ -20,6 +21,7 @@ class StackForm extends Component
|
||||
public $validationAttributes = [];
|
||||
public function mount()
|
||||
{
|
||||
$this->fields = collect([]);
|
||||
$extraFields = $this->service->extraFields();
|
||||
foreach ($extraFields as $serviceName => $fields) {
|
||||
foreach ($fields as $fieldKey => $field) {
|
||||
@ -27,18 +29,20 @@ public function mount()
|
||||
$value = data_get($field, 'value');
|
||||
$rules = data_get($field, 'rules', 'nullable');
|
||||
$isPassword = data_get($field, 'isPassword');
|
||||
$this->fields[$key] = [
|
||||
$this->fields->put($key, [
|
||||
"serviceName" => $serviceName,
|
||||
"key" => $key,
|
||||
"name" => $fieldKey,
|
||||
"value" => $value,
|
||||
"isPassword" => $isPassword,
|
||||
"rules" => $rules
|
||||
];
|
||||
]);
|
||||
|
||||
$this->rules["fields.$key.value"] = $rules;
|
||||
$this->validationAttributes["fields.$key.value"] = $fieldKey;
|
||||
}
|
||||
}
|
||||
$this->fields = $this->fields->sort();
|
||||
}
|
||||
public function saveCompose($raw)
|
||||
{
|
||||
|
@ -7,12 +7,13 @@
|
||||
|
||||
class Storage extends Component
|
||||
{
|
||||
protected $listeners = ['addNewVolume'];
|
||||
public $resource;
|
||||
|
||||
public function render()
|
||||
public function getListeners()
|
||||
{
|
||||
return view('livewire.project.service.storage');
|
||||
return [
|
||||
'addNewVolume',
|
||||
'refresh_storages' => '$refresh',
|
||||
];
|
||||
}
|
||||
public function addNewVolume($data)
|
||||
{
|
||||
@ -27,9 +28,13 @@ public function addNewVolume($data)
|
||||
$this->resource->refresh();
|
||||
$this->dispatch('success', 'Storage added successfully');
|
||||
$this->dispatch('clearAddStorage');
|
||||
$this->dispatch('refreshStorages');
|
||||
$this->dispatch('refresh_storages');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.storage');
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,13 @@
|
||||
class Add extends Component
|
||||
{
|
||||
public $parameters;
|
||||
public bool $shared = false;
|
||||
public bool $is_preview = false;
|
||||
public string $key;
|
||||
public ?string $value = null;
|
||||
public bool $is_build_time = false;
|
||||
public bool $is_multiline = false;
|
||||
public bool $is_literal = false;
|
||||
|
||||
protected $listeners = ['clearAddEnv' => 'clear'];
|
||||
protected $rules = [
|
||||
@ -19,12 +21,14 @@ class Add extends Component
|
||||
'value' => 'nullable',
|
||||
'is_build_time' => 'required|boolean',
|
||||
'is_multiline' => 'required|boolean',
|
||||
'is_literal' => 'required|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'key' => 'key',
|
||||
'value' => 'value',
|
||||
'is_build_time' => 'build',
|
||||
'is_multiline' => 'multiline',
|
||||
'is_literal' => 'literal',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
@ -47,6 +51,7 @@ public function submit()
|
||||
'value' => $this->value,
|
||||
'is_build_time' => $this->is_build_time,
|
||||
'is_multiline' => $this->is_multiline,
|
||||
'is_literal' => $this->is_literal,
|
||||
'is_preview' => $this->is_preview,
|
||||
]);
|
||||
$this->clear();
|
||||
@ -58,5 +63,6 @@ public function clear()
|
||||
$this->value = '';
|
||||
$this->is_build_time = false;
|
||||
$this->is_multiline = false;
|
||||
$this->is_literal = false;
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,10 @@ class All extends Component
|
||||
public ?string $variables = null;
|
||||
public ?string $variablesPreview = null;
|
||||
public string $view = 'normal';
|
||||
protected $listeners = ['refreshEnvs', 'saveKey' => 'submit'];
|
||||
|
||||
protected $listeners = [
|
||||
'refreshEnvs',
|
||||
'saveKey' => 'submit',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
$resourceClass = get_class($this->resource);
|
||||
@ -161,6 +163,7 @@ public function submit($data)
|
||||
$environment->value = $data['value'];
|
||||
$environment->is_build_time = $data['is_build_time'];
|
||||
$environment->is_multiline = $data['is_multiline'];
|
||||
$environment->is_literal = $data['is_literal'];
|
||||
$environment->is_preview = $data['is_preview'];
|
||||
|
||||
switch ($this->resource->type()) {
|
||||
|
@ -16,12 +16,16 @@ class Show extends Component
|
||||
public bool $isLocked = false;
|
||||
public bool $isSharedVariable = false;
|
||||
public string $type;
|
||||
protected $listeners = [
|
||||
"compose_loaded" => '$refresh',
|
||||
];
|
||||
|
||||
protected $rules = [
|
||||
'env.key' => 'required|string',
|
||||
'env.value' => 'nullable',
|
||||
'env.is_build_time' => 'required|boolean',
|
||||
'env.is_multiline' => 'required|boolean',
|
||||
'env.is_literal' => 'required|boolean',
|
||||
'env.is_shown_once' => 'required|boolean',
|
||||
'env.real_value' => 'nullable',
|
||||
];
|
||||
@ -30,6 +34,7 @@ class Show extends Component
|
||||
'env.value' => 'Value',
|
||||
'env.is_build_time' => 'Build Time',
|
||||
'env.is_multiline' => 'Multiline',
|
||||
'env.is_literal' => 'Literal',
|
||||
'env.is_shown_once' => 'Shown Once',
|
||||
];
|
||||
|
||||
@ -45,9 +50,9 @@ public function mount()
|
||||
public function checkEnvs()
|
||||
{
|
||||
$this->isDisabled = false;
|
||||
if (str($this->env->key)->startsWith('SERVICE_FQDN') || str($this->env->key)->startsWith('SERVICE_URL')) {
|
||||
$this->isDisabled = true;
|
||||
}
|
||||
// if (str($this->env->key)->startsWith('SERVICE_FQDN') || str($this->env->key)->startsWith('SERVICE_URL')) {
|
||||
// $this->isDisabled = true;
|
||||
// }
|
||||
if ($this->env->is_shown_once) {
|
||||
$this->isLocked = true;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public function loadContainers($server_id)
|
||||
} else {
|
||||
$containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true);
|
||||
}
|
||||
$server->containers = $containers;
|
||||
$server->containers = $containers->sort();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@ -94,6 +94,7 @@ public function mount()
|
||||
$this->servers = $this->servers->push($this->resource->server);
|
||||
}
|
||||
}
|
||||
$this->containers = $this->containers->sort();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
@ -7,10 +7,5 @@
|
||||
class All extends Component
|
||||
{
|
||||
public $resource;
|
||||
protected $listeners = ['refreshStorages'];
|
||||
|
||||
public function refreshStorages()
|
||||
{
|
||||
$this->resource->refresh();
|
||||
}
|
||||
protected $listeners = ['refresh_storages' => '$refresh'];
|
||||
}
|
||||
|
@ -39,6 +39,6 @@ public function submit()
|
||||
public function delete()
|
||||
{
|
||||
$this->storage->delete();
|
||||
$this->dispatch('refreshStorages');
|
||||
$this->dispatch('refresh_storages');
|
||||
}
|
||||
}
|
||||
|
@ -76,14 +76,14 @@ public function revalidate()
|
||||
public function checkLocalhostConnection()
|
||||
{
|
||||
$this->submit();
|
||||
$uptime = $this->server->validateConnection();
|
||||
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
|
||||
if ($uptime) {
|
||||
$this->dispatch('success', 'Server is reachable.');
|
||||
$this->server->settings->is_reachable = true;
|
||||
$this->server->settings->is_usable = true;
|
||||
$this->server->settings->save();
|
||||
} else {
|
||||
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.');
|
||||
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: ' . $error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,8 @@ public function getListeners()
|
||||
'proxyStatusUpdated',
|
||||
'traefikDashboardAvailable',
|
||||
'serverRefresh' => 'proxyStatusUpdated',
|
||||
"checkProxy", "startProxy"
|
||||
"checkProxy",
|
||||
"startProxy"
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ public function addDynamicConfiguration()
|
||||
}
|
||||
$base64_value = base64_encode($this->value);
|
||||
instant_remote_process([
|
||||
"echo '{$base64_value}' | base64 -d > {$file}",
|
||||
"echo '{$base64_value}' | base64 -d | tee {$file} > /dev/null",
|
||||
], $this->server);
|
||||
if ($proxy_type === 'CADDY') {
|
||||
$this->server->reloadCaddy();
|
||||
|
@ -35,10 +35,11 @@ public function setPrivateKey($newPrivateKeyId)
|
||||
public function checkConnection()
|
||||
{
|
||||
try {
|
||||
$uptime = $this->server->validateConnection();
|
||||
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
|
||||
if ($uptime) {
|
||||
$this->dispatch('success', 'Server is reachable.');
|
||||
} else {
|
||||
ray($error);
|
||||
$this->dispatch('error', 'Server is not reachable.<br>Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.');
|
||||
return;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ class ValidateAndInstall extends Component
|
||||
{
|
||||
public Server $server;
|
||||
public int $number_of_tries = 0;
|
||||
public int $max_tries = 1;
|
||||
public int $max_tries = 3;
|
||||
public bool $install = true;
|
||||
public $uptime = null;
|
||||
public $supported_os_type = null;
|
||||
@ -32,9 +32,8 @@ class ValidateAndInstall extends Component
|
||||
'refresh' => '$refresh',
|
||||
];
|
||||
|
||||
public function init(bool $install = true)
|
||||
public function init(int $data = 0)
|
||||
{
|
||||
$this->install = $install;
|
||||
$this->uptime = null;
|
||||
$this->supported_os_type = null;
|
||||
$this->docker_installed = null;
|
||||
@ -42,7 +41,7 @@ public function init(bool $install = true)
|
||||
$this->docker_compose_installed = null;
|
||||
$this->proxy_started = null;
|
||||
$this->error = null;
|
||||
$this->number_of_tries = 0;
|
||||
$this->number_of_tries = $data;
|
||||
if (!$this->ask) {
|
||||
$this->dispatch('validateConnection');
|
||||
}
|
||||
@ -66,16 +65,15 @@ public function startProxy()
|
||||
} else {
|
||||
$this->proxy_started = true;
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function validateConnection()
|
||||
{
|
||||
$this->uptime = $this->server->validateConnection();
|
||||
['uptime' => $this->uptime, 'error' => $error] = $this->server->validateConnection();
|
||||
if (!$this->uptime) {
|
||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.';
|
||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: ' . $error;
|
||||
return;
|
||||
}
|
||||
$this->dispatch('validateOS');
|
||||
@ -99,10 +97,10 @@ public function validateDockerEngine()
|
||||
$this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
return;
|
||||
} else {
|
||||
if ($this->number_of_tries == 0) {
|
||||
if ($this->number_of_tries <= $this->max_tries) {
|
||||
$activity = $this->server->installDocker();
|
||||
$this->number_of_tries++;
|
||||
$this->dispatch('newActivityMonitor', $activity->id, 'init');
|
||||
$this->dispatch('newActivityMonitor', $activity->id, 'init', $this->number_of_tries);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ public function subscribeStripe($type)
|
||||
return;
|
||||
}
|
||||
$payload = [
|
||||
'allow_promotion_codes' => true,
|
||||
'billing_address_collection' => 'required',
|
||||
'client_reference_id' => auth()->user()->id . ':' . currentTeam()->id,
|
||||
'line_items' => [[
|
||||
|
@ -21,6 +21,7 @@ public function saveKey($data)
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'is_literal' => $data['is_literal'],
|
||||
'type' => 'team',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
|
@ -511,9 +511,9 @@ public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->ports_exposes . $this->ports_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels . $this->custom_docker_run_options . $this->dockerfile_target_build;
|
||||
if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
|
||||
} else {
|
||||
$newConfigHash .= json_encode($this->environment_variables_preview->get('updated_at'));
|
||||
$newConfigHash .= json_encode($this->environment_variables_preview->get('value')->sort());
|
||||
}
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
@ -653,13 +653,13 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id
|
||||
if ($exec_in_docker) {
|
||||
$commands = collect([
|
||||
executeInDocker($deployment_uuid, "mkdir -p /root/.ssh"),
|
||||
executeInDocker($deployment_uuid, "echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa"),
|
||||
executeInDocker($deployment_uuid, "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null"),
|
||||
executeInDocker($deployment_uuid, "chmod 600 /root/.ssh/id_rsa"),
|
||||
]);
|
||||
} else {
|
||||
$commands = collect([
|
||||
"mkdir -p /root/.ssh",
|
||||
"echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa",
|
||||
"echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null",
|
||||
"chmod 600 /root/.ssh/id_rsa",
|
||||
]);
|
||||
}
|
||||
@ -954,4 +954,9 @@ public function isWatchPathsTriggered(Collection $modified_files): bool
|
||||
});
|
||||
return $matches->count() > 0;
|
||||
}
|
||||
|
||||
public function getFilesFromServer(bool $isInit = false)
|
||||
{
|
||||
getFilesystemVolumesFromServer($this, $isInit);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class EnvironmentVariable extends Model
|
||||
{
|
||||
@ -81,6 +82,44 @@ public function realValue(): Attribute
|
||||
}
|
||||
);
|
||||
}
|
||||
protected function isFoundInCompose(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function () {
|
||||
if (!$this->application_id) {
|
||||
return true;
|
||||
}
|
||||
$found_in_compose = false;
|
||||
$resource = $this->resource();
|
||||
$compose = data_get($resource, 'docker_compose_raw');
|
||||
if (!$compose) {
|
||||
return true;
|
||||
}
|
||||
$yaml = Yaml::parse($compose);
|
||||
$services = collect(data_get($yaml, 'services'));
|
||||
if ($services->isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
foreach ($services as $service) {
|
||||
$environments = collect(data_get($service, 'environment'));
|
||||
if ($environments->isEmpty()) {
|
||||
$found_in_compose = false;
|
||||
break;
|
||||
}
|
||||
$found_in_compose = $environments->contains(function ($item) {
|
||||
if (str($item)->contains('=')) {
|
||||
$item = str($item)->before('=');
|
||||
}
|
||||
return strpos($item, $this->key) !== false;
|
||||
});
|
||||
if ($found_in_compose) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $found_in_compose;
|
||||
}
|
||||
);
|
||||
}
|
||||
protected function isShared(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
|
@ -20,6 +20,26 @@ public function service()
|
||||
{
|
||||
return $this->morphTo('resource');
|
||||
}
|
||||
public function deleteStorageOnServer()
|
||||
{
|
||||
$isService = data_get($this->resource, 'service');
|
||||
if ($isService) {
|
||||
$workdir = $this->resource->service->workdir();
|
||||
$server = $this->resource->service->server;
|
||||
} else {
|
||||
$workdir = $this->resource->workdir();
|
||||
$server = $this->resource->destination->server;
|
||||
}
|
||||
$commands = collect([
|
||||
"cd $workdir"
|
||||
]);
|
||||
$fs_path = data_get($this, 'fs_path');
|
||||
if ($fs_path && $fs_path != '/' && $fs_path != '.' && $fs_path != '..') {
|
||||
$commands->push("rm -rf $fs_path");
|
||||
}
|
||||
ray($commands);
|
||||
return instant_remote_process($commands, $server);
|
||||
}
|
||||
public function saveStorageOnServer()
|
||||
{
|
||||
$isService = data_get($this->resource, 'service');
|
||||
@ -63,8 +83,7 @@ public function saveStorageOnServer()
|
||||
$content = base64_encode($content);
|
||||
$chmod = $fileVolume->chmod;
|
||||
$chown = $fileVolume->chown;
|
||||
ray($content, $path, $chmod, $chown);
|
||||
$commands->push("echo '$content' | base64 -d > $path");
|
||||
$commands->push("echo '$content' | base64 -d | tee $path > /dev/null");
|
||||
$commands->push("chmod +x $path");
|
||||
if ($chown) {
|
||||
$commands->push("chown $chown $path");
|
||||
@ -72,7 +91,6 @@ public function saveStorageOnServer()
|
||||
if ($chmod) {
|
||||
$commands->push("chmod $chmod $path");
|
||||
}
|
||||
|
||||
}
|
||||
} else if ($isDir == 'NOK' && $fileVolume->is_directory) {
|
||||
$commands->push("mkdir -p $path > /dev/null 2>&1 || true");
|
||||
|
@ -141,7 +141,7 @@ public function setupDefault404Redirect()
|
||||
$base64 = base64_encode($conf);
|
||||
instant_remote_process([
|
||||
"mkdir -p $dynamic_conf_path",
|
||||
"echo '$base64' | base64 -d > $default_redirect_file",
|
||||
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
|
||||
], $this);
|
||||
$this->reloadCaddy();
|
||||
return;
|
||||
@ -223,7 +223,7 @@ public function setupDefault404Redirect()
|
||||
|
||||
instant_remote_process([
|
||||
"mkdir -p $dynamic_conf_path",
|
||||
"echo '$base64' | base64 -d > $default_redirect_file",
|
||||
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
|
||||
], $this);
|
||||
|
||||
if (config('app.env') == 'local') {
|
||||
@ -349,7 +349,7 @@ public function setupDynamicProxyConfiguration()
|
||||
$base64 = base64_encode($yaml);
|
||||
instant_remote_process([
|
||||
"mkdir -p $dynamic_config_path",
|
||||
"echo '$base64' | base64 -d > $file",
|
||||
"echo '$base64' | base64 -d | tee $file > /dev/null",
|
||||
], $this);
|
||||
|
||||
if (config('app.env') == 'local') {
|
||||
@ -376,7 +376,7 @@ public function setupDynamicProxyConfiguration()
|
||||
}";
|
||||
$base64 = base64_encode($caddy_file);
|
||||
instant_remote_process([
|
||||
"echo '$base64' | base64 -d > $file",
|
||||
"echo '$base64' | base64 -d | tee $file > /dev/null",
|
||||
], $this);
|
||||
$this->reloadCaddy();
|
||||
}
|
||||
@ -481,8 +481,8 @@ public function isServerReady(int $tries = 3)
|
||||
// ray('serverUptimeCheckNumber: ' . $serverUptimeCheckNumber);
|
||||
// ray('serverUptimeCheckNumberMax: ' . $serverUptimeCheckNumberMax);
|
||||
|
||||
$result = $this->validateConnection();
|
||||
if ($result) {
|
||||
['uptime' => $uptime] = $this->validateConnection();
|
||||
if ($uptime) {
|
||||
if ($this->unreachable_notification_sent === true) {
|
||||
$this->update(['unreachable_notification_sent' => false]);
|
||||
}
|
||||
@ -551,7 +551,7 @@ public function startUnmanaged($id)
|
||||
public function loadUnmanagedContainers()
|
||||
{
|
||||
if ($this->isFunctional()) {
|
||||
$containers = instant_remote_process(["docker ps -a --format '{{json .}}' "], $this);
|
||||
$containers = instant_remote_process(["docker ps -a --format '{{json .}}'"], $this);
|
||||
$containers = format_docker_command_output_to_json($containers);
|
||||
$containers = $containers->map(function ($container) {
|
||||
$labels = data_get($container, 'Labels');
|
||||
@ -748,34 +748,31 @@ public function validateConnection()
|
||||
|
||||
$server = Server::find($this->id);
|
||||
if (!$server) {
|
||||
return false;
|
||||
return ['uptime' => false, 'error' => 'Server not found.'];
|
||||
}
|
||||
if ($server->skipServer()) {
|
||||
return false;
|
||||
return ['uptime' => false, 'error' => 'Server skipped.'];
|
||||
}
|
||||
// EC2 does not have `uptime` command, lol
|
||||
|
||||
$uptime = instant_remote_process(['ls /'], $server, false);
|
||||
if (!$uptime) {
|
||||
$server->settings()->update([
|
||||
'is_reachable' => false,
|
||||
]);
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
// EC2 does not have `uptime` command, lol
|
||||
instant_remote_process(['ls /'], $server);
|
||||
$server->settings()->update([
|
||||
'is_reachable' => true,
|
||||
]);
|
||||
$server->update([
|
||||
'unreachable_count' => 0,
|
||||
]);
|
||||
if (data_get($server, 'unreachable_notification_sent') === true) {
|
||||
$server->team?->notify(new Revived($server));
|
||||
$server->update(['unreachable_notification_sent' => false]);
|
||||
}
|
||||
return ['uptime' => true, 'error' => null];
|
||||
} catch (\Throwable $e) {
|
||||
$server->settings()->update([
|
||||
'is_reachable' => false,
|
||||
]);
|
||||
return ['uptime' => false, 'error' => $e->getMessage()];
|
||||
}
|
||||
|
||||
if (data_get($server, 'unreachable_notification_sent') === true) {
|
||||
$server->team?->notify(new Revived($server));
|
||||
$server->update(['unreachable_notification_sent' => false]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public function installDocker()
|
||||
{
|
||||
@ -784,7 +781,7 @@ public function installDocker()
|
||||
}
|
||||
public function validateDockerEngine($throwError = false)
|
||||
{
|
||||
$dockerBinary = instant_remote_process(["command -v docker"], $this, false);
|
||||
$dockerBinary = instant_remote_process(["command -v docker"], $this, false, no_sudo: true);
|
||||
if (is_null($dockerBinary)) {
|
||||
$this->settings->is_usable = false;
|
||||
$this->settings->save();
|
||||
@ -861,4 +858,8 @@ public function validateCoolifyNetwork($isSwarm = false, $isBuildServer = false)
|
||||
return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false);
|
||||
}
|
||||
}
|
||||
public function isNonRoot()
|
||||
{
|
||||
return $this->user !== 'root';
|
||||
}
|
||||
}
|
||||
|
@ -14,20 +14,20 @@ class Service extends BaseModel
|
||||
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$domains = $this->applications()->get()->pluck('fqdn')->toArray();
|
||||
$domains = $this->applications()->get()->pluck('fqdn')->sort()->toArray();
|
||||
$domains = implode(',', $domains);
|
||||
|
||||
$applicationImages = $this->applications()->get()->pluck('image');
|
||||
$databaseImages = $this->databases()->get()->pluck('image');
|
||||
$applicationImages = $this->applications()->get()->pluck('image')->sort();
|
||||
$databaseImages = $this->databases()->get()->pluck('image')->sort();
|
||||
$images = $applicationImages->merge($databaseImages);
|
||||
$images = implode(',', $images->toArray());
|
||||
|
||||
$applicationStorages = $this->applications()->get()->pluck('persistentStorages')->flatten();
|
||||
$databaseStorages = $this->databases()->get()->pluck('persistentStorages')->flatten();
|
||||
$applicationStorages = $this->applications()->get()->pluck('persistentStorages')->flatten()->sortBy('id');
|
||||
$databaseStorages = $this->databases()->get()->pluck('persistentStorages')->flatten()->sortBy('id');
|
||||
$storages = $applicationStorages->merge($databaseStorages)->implode('updated_at');
|
||||
|
||||
$newConfigHash = $images . $domains . $images . $storages;
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value'));
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
@ -150,6 +150,53 @@ public function extraFields()
|
||||
foreach ($applications as $application) {
|
||||
$image = str($application->image)->before(':')->value();
|
||||
switch ($image) {
|
||||
case str($image)?->contains('logto'):
|
||||
$data = collect([]);
|
||||
$logto_endpoint = $this->environment_variables()->where('key', 'LOGTO_ENDPOINT')->first();
|
||||
$logto_admin_endpoint = $this->environment_variables()->where('key', 'LOGTO_ADMIN_ENDPOINT')->first();
|
||||
if ($logto_endpoint) {
|
||||
$data = $data->merge([
|
||||
'Endpoint' => [
|
||||
'key' => data_get($logto_endpoint, 'key'),
|
||||
'value' => data_get($logto_endpoint, 'value'),
|
||||
'rules' => 'required|url',
|
||||
],
|
||||
]);
|
||||
}
|
||||
if ($logto_admin_endpoint) {
|
||||
$data = $data->merge([
|
||||
'Admin Endpoint' => [
|
||||
'key' => data_get($logto_admin_endpoint, 'key'),
|
||||
'value' => data_get($logto_admin_endpoint, 'value'),
|
||||
'rules' => 'required|url',
|
||||
],
|
||||
]);
|
||||
}
|
||||
$fields->put('Logto', $data);
|
||||
break;
|
||||
case str($image)?->contains('unleash-server'):
|
||||
$data = collect([]);
|
||||
$admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_UNLEASH')->first();
|
||||
$data = $data->merge([
|
||||
'Admin User' => [
|
||||
'key' => 'SERVICE_USER_UNLEASH',
|
||||
'value' => 'admin',
|
||||
'readonly' => true,
|
||||
'rules' => 'required',
|
||||
],
|
||||
]);
|
||||
if ($admin_password) {
|
||||
$data = $data->merge([
|
||||
'Admin Password' => [
|
||||
'key' => 'SERVICE_PASSWORD_UNLEASH',
|
||||
'value' => data_get($admin_password, 'value'),
|
||||
'rules' => 'required',
|
||||
'isPassword' => true,
|
||||
],
|
||||
]);
|
||||
}
|
||||
$fields->put('Unleash', $data);
|
||||
break;
|
||||
case str($image)?->contains('grafana'):
|
||||
$data = collect([]);
|
||||
$admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_GRAFANA')->first();
|
||||
@ -618,7 +665,7 @@ public function saveComposeConfigs()
|
||||
$commands[] = "cd $workdir";
|
||||
|
||||
$docker_compose_base64 = base64_encode($this->docker_compose);
|
||||
$commands[] = "echo $docker_compose_base64 | base64 -d > docker-compose.yml";
|
||||
$commands[] = "echo $docker_compose_base64 | base64 -d | tee docker-compose.yml > /dev/null";
|
||||
$envs = $this->environment_variables()->get();
|
||||
$commands[] = "rm -f .env || true";
|
||||
foreach ($envs as $env) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SharedEnvironmentVariable extends Model
|
||||
|
@ -45,7 +45,7 @@ protected static function booted()
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->image . $this->ports_mappings;
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
|
@ -44,7 +44,7 @@ protected static function booted()
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->image . $this->ports_mappings;
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
@ -167,9 +167,9 @@ public function type(): string
|
||||
public function get_db_url(bool $useInternal = false): string
|
||||
{
|
||||
if ($this->is_public && !$useInternal) {
|
||||
return "redis://{$this->dragonfly_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
|
||||
return "redis://:{$this->dragonfly_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
|
||||
} else {
|
||||
return "redis://{$this->dragonfly_password}@{$this->uuid}:6379/0";
|
||||
return "redis://:{$this->dragonfly_password}@{$this->uuid}:6379/0";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ protected static function booted()
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->image . $this->ports_mappings . $this->keydb_conf;
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
|
@ -46,7 +46,7 @@ protected static function booted()
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->image . $this->ports_mappings . $this->mariadb_conf;
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
|
@ -49,7 +49,7 @@ protected static function booted()
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->image . $this->ports_mappings . $this->mongo_conf;
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
|
@ -46,7 +46,7 @@ protected static function booted()
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->image . $this->ports_mappings . $this->mysql_conf;
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
|
@ -58,7 +58,7 @@ public function delete_configurations()
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->image . $this->ports_mappings . $this->postgres_initdb_args . $this->postgres_host_auth_method;
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
|
@ -41,7 +41,7 @@ protected static function booted()
|
||||
public function isConfigurationChanged(bool $save = false)
|
||||
{
|
||||
$newConfigHash = $this->image . $this->ports_mappings . $this->redis_conf;
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
|
@ -20,9 +20,9 @@ class EventServiceProvider extends ServiceProvider
|
||||
MaintenanceModeDisabledNotification::class,
|
||||
],
|
||||
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
|
||||
\SocialiteProviders\Azure\AzureExtendSocialite::class.'@handle',
|
||||
\SocialiteProviders\Azure\AzureExtendSocialite::class . '@handle',
|
||||
],
|
||||
ProxyStarted::class => [
|
||||
ProxyStarted::class => [
|
||||
ProxyStartedNotification::class,
|
||||
],
|
||||
];
|
||||
|
@ -34,7 +34,13 @@ public function execute_remote_command(...$commands)
|
||||
$ignore_errors = data_get($single_command, 'ignore_errors', false);
|
||||
$append = data_get($single_command, 'append', true);
|
||||
$this->save = data_get($single_command, 'save');
|
||||
|
||||
if ($this->server->isNonRoot()) {
|
||||
if (str($command)->startsWith('docker exec')) {
|
||||
$command = str($command)->replace('docker exec', 'sudo docker exec');
|
||||
} else {
|
||||
$command = parseLineForSudo($command, $this->server);
|
||||
}
|
||||
}
|
||||
$remote_command = generateSshCommand($this->server, $command);
|
||||
$process = Process::timeout(3600)->idleTimeout(3600)->start($remote_command, function (string $type, string $output) use ($command, $hidden, $customType, $append) {
|
||||
$output = Str::of($output)->trim();
|
||||
|
@ -18,7 +18,10 @@ public function __construct(public Service $service)
|
||||
$service->applications()->get()->map(function ($application) {
|
||||
$type = $application->serviceType();
|
||||
if ($type) {
|
||||
$links = generateServiceSpecificFqdns($application, false);
|
||||
$links = generateServiceSpecificFqdns($application);
|
||||
$links = $links->map(function ($link) {
|
||||
return getFqdnWithoutPort($link);
|
||||
});
|
||||
$this->links = $this->links->merge($links);
|
||||
} else {
|
||||
if ($application->fqdn) {
|
||||
|
@ -32,6 +32,7 @@
|
||||
];
|
||||
const SPECIFIC_SERVICES = [
|
||||
'quay.io/minio/minio',
|
||||
'svhd/logto'
|
||||
];
|
||||
|
||||
// Based on /etc/os-release
|
||||
|
@ -169,7 +169,7 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
|
||||
}
|
||||
return $labels;
|
||||
}
|
||||
function generateServiceSpecificFqdns(ServiceApplication|Application $resource, $forTraefik = false)
|
||||
function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
|
||||
{
|
||||
if ($resource->getMorphClass() === 'App\Models\ServiceApplication') {
|
||||
$uuid = $resource->uuid;
|
||||
@ -182,6 +182,9 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource,
|
||||
$environment_variables = $resource->environment_variables;
|
||||
$type = $resource->serviceType();
|
||||
}
|
||||
if (is_null($server) || is_null($type)) {
|
||||
return collect([]);
|
||||
}
|
||||
$variables = collect($environment_variables);
|
||||
$payload = collect([]);
|
||||
switch ($type) {
|
||||
@ -201,17 +204,31 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource,
|
||||
"value" => generateFqdn($server, 'minio-' . $uuid)
|
||||
]);
|
||||
}
|
||||
if ($forTraefik) {
|
||||
$payload = collect([
|
||||
$MINIO_BROWSER_REDIRECT_URL->value . ':9001',
|
||||
$MINIO_SERVER_URL->value . ':9000',
|
||||
]);
|
||||
} else {
|
||||
$payload = collect([
|
||||
$MINIO_BROWSER_REDIRECT_URL->value,
|
||||
$MINIO_SERVER_URL->value,
|
||||
$payload = collect([
|
||||
$MINIO_BROWSER_REDIRECT_URL->value . ':9001',
|
||||
$MINIO_SERVER_URL->value . ':9000',
|
||||
]);
|
||||
break;
|
||||
case $type?->contains('logto'):
|
||||
$LOGTO_ENDPOINT = $variables->where('key', 'LOGTO_ENDPOINT')->first();
|
||||
$LOGTO_ADMIN_ENDPOINT = $variables->where('key', 'LOGTO_ADMIN_ENDPOINT')->first();
|
||||
if (is_null($LOGTO_ENDPOINT) || is_null($LOGTO_ADMIN_ENDPOINT)) {
|
||||
return $payload;
|
||||
}
|
||||
if (is_null($LOGTO_ENDPOINT?->value)) {
|
||||
$LOGTO_ENDPOINT?->update([
|
||||
"value" => generateFqdn($server, 'logto-' . $uuid)
|
||||
]);
|
||||
}
|
||||
if (is_null($LOGTO_ADMIN_ENDPOINT?->value)) {
|
||||
$LOGTO_ADMIN_ENDPOINT?->update([
|
||||
"value" => generateFqdn($server, 'logto-admin-' . $uuid)
|
||||
]);
|
||||
}
|
||||
$payload = collect([
|
||||
$LOGTO_ENDPOINT->value . ':3001',
|
||||
$LOGTO_ADMIN_ENDPOINT->value . ':3002',
|
||||
]);
|
||||
break;
|
||||
}
|
||||
return $payload;
|
||||
@ -565,7 +582,7 @@ function validateComposeFile(string $compose, int $server_id): string|Throwable
|
||||
$server = Server::findOrFail($server_id);
|
||||
$base64_compose = base64_encode($compose);
|
||||
$output = instant_remote_process([
|
||||
"echo {$base64_compose} | base64 -d > /tmp/{$uuid}.yml",
|
||||
"echo {$base64_compose} | base64 -d | tee /tmp/{$uuid}.yml > /dev/null",
|
||||
"docker compose -f /tmp/{$uuid}.yml config",
|
||||
], $server);
|
||||
ray($output);
|
||||
@ -586,3 +603,9 @@ function escapeEnvVariables($value)
|
||||
$replace = array("\\\\", "\\r", "\\t", "\\0", '\"', "\'");
|
||||
return str_replace($search, $replace, $value);
|
||||
}
|
||||
function escapeDollarSign($value)
|
||||
{
|
||||
$search = array('$');
|
||||
$replace = array('$$');
|
||||
return str_replace($search, $replace, $value);
|
||||
}
|
||||
|
@ -32,6 +32,10 @@ function remote_process(
|
||||
if ($command instanceof Collection) {
|
||||
$command = $command->toArray();
|
||||
}
|
||||
if ($server->isNonRoot()) {
|
||||
$command = parseCommandsByLineForSudo(collect($command), $server);
|
||||
}
|
||||
ray($command);
|
||||
$command_string = implode("\n", $command);
|
||||
if (auth()->user()) {
|
||||
$teams = auth()->user()->teams->pluck('id');
|
||||
@ -133,17 +137,17 @@ function generateSshCommand(Server $server, string $command)
|
||||
$timeout = config('constants.ssh.command_timeout');
|
||||
$connectionTimeout = config('constants.ssh.connection_timeout');
|
||||
$serverInterval = config('constants.ssh.server_interval');
|
||||
$muxPersistTime = config('constants.ssh.mux_persist_time');
|
||||
|
||||
$ssh_command = "timeout $timeout ssh ";
|
||||
|
||||
if (config('coolify.mux_enabled') && config('coolify.is_windows_docker_desktop') == false) {
|
||||
$ssh_command .= '-o ControlMaster=auto -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/ssh/mux/%h_%p_%r ';
|
||||
$ssh_command .= "-o ControlMaster=auto -o ControlPersist={$muxPersistTime} -o ControlPath=/var/www/html/storage/app/ssh/mux/%h_%p_%r ";
|
||||
}
|
||||
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
|
||||
$ssh_command .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" ';
|
||||
}
|
||||
$command = "PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/host/usr/local/sbin:/host/usr/local/bin:/host/usr/sbin:/host/usr/bin:/host/sbin:/host/bin && $command";
|
||||
|
||||
$delimiter = Hash::make($command);
|
||||
$command = str_replace($delimiter, '', $command);
|
||||
$ssh_command .= "-i {$privateKeyLocation} "
|
||||
@ -159,17 +163,19 @@ function generateSshCommand(Server $server, string $command)
|
||||
. $command . PHP_EOL
|
||||
. $delimiter;
|
||||
// ray($ssh_command);
|
||||
// ray($delimiter);
|
||||
return $ssh_command;
|
||||
}
|
||||
function instant_remote_process(Collection|array $command, Server $server, $throwError = true)
|
||||
function instant_remote_process(Collection|array $command, Server $server, bool $throwError = true, bool $no_sudo = false)
|
||||
{
|
||||
$timeout = config('constants.ssh.command_timeout');
|
||||
if ($command instanceof Collection) {
|
||||
$command = $command->toArray();
|
||||
}
|
||||
if ($server->isNonRoot() && !$no_sudo) {
|
||||
$command = parseCommandsByLineForSudo(collect($command), $server);
|
||||
}
|
||||
$command_string = implode("\n", $command);
|
||||
$ssh_command = generateSshCommand($server, $command_string);
|
||||
$ssh_command = generateSshCommand($server, $command_string, $no_sudo);
|
||||
$process = Process::timeout($timeout)->run($ssh_command);
|
||||
$output = trim($process->output());
|
||||
$exitCode = $process->exitCode();
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use Illuminate\Support\Str;
|
||||
@ -21,16 +21,20 @@ function replaceVariables($variable)
|
||||
return $variable->replaceFirst('$', '')->replaceFirst('{', '')->replaceLast('}', '');
|
||||
}
|
||||
|
||||
function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase $oneService, bool $isInit = false)
|
||||
function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Application $oneService, bool $isInit = false)
|
||||
{
|
||||
// TODO: make this async
|
||||
try {
|
||||
$workdir = $oneService->service->workdir();
|
||||
$server = $oneService->service->server;
|
||||
if ($oneService->getMorphClass() === 'App\Models\Application') {
|
||||
$workdir = $oneService->workdir();
|
||||
$server = $oneService->destination->server;
|
||||
} else{
|
||||
$workdir = $oneService->service->workdir();
|
||||
$server = $oneService->service->server;
|
||||
}
|
||||
$fileVolumes = $oneService->fileStorages()->get();
|
||||
$commands = collect([
|
||||
"mkdir -p $workdir > /dev/null 2>&1 || true",
|
||||
"cd "
|
||||
"cd $workdir"
|
||||
]);
|
||||
instant_remote_process($commands, $server);
|
||||
foreach ($fileVolumes as $fileVolume) {
|
||||
@ -67,7 +71,7 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase $oneS
|
||||
$dir = Str::of($fileLocation)->dirname();
|
||||
instant_remote_process([
|
||||
"mkdir -p $dir",
|
||||
"echo '$content' | base64 -d > $fileLocation"
|
||||
"echo '$content' | base64 -d | tee $fileLocation"
|
||||
], $server);
|
||||
} else if ($isFile == 'NOK' && $isDir == 'NOK' && $fileVolume->is_directory && $isInit) {
|
||||
$fileVolume->content = null;
|
||||
|
@ -29,6 +29,7 @@
|
||||
use Illuminate\Database\UniqueConstraintViolationException;
|
||||
use Illuminate\Mail\Message;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
@ -656,6 +657,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
if (str(data_get($service, 'image'))->contains('glitchtip')) {
|
||||
$tempServiceName = 'glitchtip';
|
||||
}
|
||||
if ($serviceName === 'supabase-kong') {
|
||||
$tempServiceName = 'supabase';
|
||||
}
|
||||
$serviceDefinition = data_get($allServices, $tempServiceName);
|
||||
$predefinedPort = data_get($serviceDefinition, 'port');
|
||||
if ($serviceName === 'plausible') {
|
||||
@ -978,12 +982,17 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
// Caddy needs exact port in some cases.
|
||||
if ($predefinedPort && !$key->endsWith("_{$predefinedPort}")) {
|
||||
$fqdns_exploded = str($savedService->fqdn)->explode(',');
|
||||
if ($fqdns_exploded->count() > 1) {
|
||||
continue;
|
||||
}
|
||||
if ($resource->server->proxyType() === 'CADDY') {
|
||||
$env = EnvironmentVariable::where([
|
||||
'key' => $key,
|
||||
'service_id' => $resource->id,
|
||||
])->first();
|
||||
if ($env) {
|
||||
|
||||
$env_url = Url::fromString($savedService->fqdn);
|
||||
$env_port = $env_url->getPort();
|
||||
if ($env_port !== $predefinedPort) {
|
||||
@ -1045,6 +1054,10 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
// Caddy needs exact port in some cases.
|
||||
if ($predefinedPort && !$key->endsWith("_{$predefinedPort}") && $command?->value() === 'FQDN' && $resource->server->proxyType() === 'CADDY') {
|
||||
$fqdns_exploded = str($savedService->fqdn)->explode(',');
|
||||
if ($fqdns_exploded->count() > 1) {
|
||||
continue;
|
||||
}
|
||||
$env = EnvironmentVariable::where([
|
||||
'key' => $key,
|
||||
'service_id' => $resource->id,
|
||||
@ -1109,10 +1122,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add labels to the service
|
||||
if ($savedService->serviceType()) {
|
||||
$fqdns = generateServiceSpecificFqdns($savedService, forTraefik: true);
|
||||
$fqdns = generateServiceSpecificFqdns($savedService);
|
||||
} else {
|
||||
$fqdns = collect(data_get($savedService, 'fqdns'))->filter();
|
||||
}
|
||||
@ -1482,7 +1494,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
'application_id' => $resource->id,
|
||||
])->first();
|
||||
['command' => $command, 'forService' => $forService, 'generatedValue' => $generatedValue, 'port' => $port] = parseEnvVariable($value);
|
||||
ray($command, $generatedValue);
|
||||
if (!is_null($command)) {
|
||||
if ($command?->value() === 'FQDN' || $command?->value() === 'URL') {
|
||||
if (Str::lower($forService) === $serviceName) {
|
||||
@ -1567,7 +1578,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
// Add labels to the service
|
||||
if ($resource->serviceType()) {
|
||||
$fqdns = generateServiceSpecificFqdns($resource, forTraefik: true);
|
||||
$fqdns = generateServiceSpecificFqdns($resource);
|
||||
} else {
|
||||
$domains = collect(json_decode($resource->docker_compose_domains)) ?? [];
|
||||
if ($domains) {
|
||||
@ -1939,3 +1950,57 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseCommandsByLineForSudo(Collection $commands, Server $server): array
|
||||
{
|
||||
$commands = $commands->map(function ($line) {
|
||||
if (!str($line)->startsWith('cd') && !str($line)->startsWith('command') && !str($line)->startsWith('echo') && !str($line)->startsWith('true')) {
|
||||
return "sudo $line";
|
||||
}
|
||||
return $line;
|
||||
});
|
||||
$commands = $commands->map(function ($line) use ($server) {
|
||||
if (Str::startsWith($line, 'sudo mkdir -p')) {
|
||||
return "$line && sudo chown -R $server->user:$server->user " . Str::after($line, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($line, 'sudo mkdir -p');
|
||||
}
|
||||
return $line;
|
||||
});
|
||||
$commands = $commands->map(function ($line) {
|
||||
$line = str($line);
|
||||
if (str($line)->contains('$(')) {
|
||||
$line = $line->replace('$(', '$(sudo ');
|
||||
}
|
||||
if (str($line)->contains('||')) {
|
||||
$line = $line->replace('||', '|| sudo');
|
||||
}
|
||||
if (str($line)->contains('&&')) {
|
||||
$line = $line->replace('&&', '&& sudo');
|
||||
}
|
||||
if (str($line)->contains(' | ')) {
|
||||
$line = $line->replace(' | ', ' | sudo ');
|
||||
}
|
||||
return $line->value();
|
||||
});
|
||||
|
||||
return $commands->toArray();
|
||||
}
|
||||
function parseLineForSudo(string $command, Server $server): string
|
||||
{
|
||||
if (!str($command)->startSwith('cd') && !str($command)->startSwith('command')) {
|
||||
$command = "sudo $command";
|
||||
}
|
||||
if (Str::startsWith($command, 'sudo mkdir -p')) {
|
||||
$command = "$command && sudo chown -R $server->user:$server->user " . Str::after($command, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($command, 'sudo mkdir -p');
|
||||
}
|
||||
if (str($command)->contains('$(') || str($command)->contains('`')) {
|
||||
$command = str($command)->replace('$(', '$(sudo ')->replace('`', '`sudo ')->value();
|
||||
}
|
||||
if (str($command)->contains('||')) {
|
||||
$command = str($command)->replace('||', '|| sudo ')->value();
|
||||
}
|
||||
if (str($command)->contains('&&')) {
|
||||
$command = str($command)->replace('&&', '&& sudo ')->value();
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
@ -141,8 +141,8 @@
|
||||
*/
|
||||
|
||||
'maintenance' => [
|
||||
'driver' => 'file',
|
||||
// 'store' => 'redis',
|
||||
'driver' => 'cache',
|
||||
'store' => 'redis',
|
||||
],
|
||||
|
||||
/*
|
||||
|
@ -5,6 +5,7 @@
|
||||
'contact' => 'https://coolify.io/docs/contact',
|
||||
],
|
||||
'ssh' => [
|
||||
'mux_persist_time' => env('SSH_MUX_PERSIST_TIME', "1m"),
|
||||
'connection_timeout' => 10,
|
||||
'server_interval' => 20,
|
||||
'command_timeout' => 7200,
|
||||
|
@ -183,9 +183,7 @@
|
||||
's6' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => ['default'],
|
||||
'balance' => 'auto',
|
||||
// 'autoScalingStrategy' => 'time',
|
||||
// 'maxProcesses' => 1,
|
||||
'balance' => env('HORIZON_BALANCE', 'auto'),
|
||||
'maxTime' => 0,
|
||||
'maxJobs' => 0,
|
||||
'memory' => 128,
|
||||
|
@ -146,4 +146,5 @@
|
||||
*/
|
||||
|
||||
'pagination_theme' => 'tailwind',
|
||||
'lazy_placeholder' => 'components.page-loading',
|
||||
];
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => '4.0.0-beta.258',
|
||||
'release' => '4.0.0-beta.259',
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.258';
|
||||
return '4.0.0-beta.259';
|
||||
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('environment_variables', function (Blueprint $table) {
|
||||
$table->boolean('is_literal')->default(false);
|
||||
});
|
||||
Schema::table('shared_environment_variables', function (Blueprint $table) {
|
||||
$table->boolean('is_literal')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('environment_variables', function (Blueprint $table) {
|
||||
$table->dropColumn('is_literal');
|
||||
});
|
||||
Schema::table('shared_environment_variables', function (Blueprint $table) {
|
||||
$table->dropColumn('is_literal');
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('services', function (Blueprint $table) {
|
||||
$table->string('service_type')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('services', function (Blueprint $table) {
|
||||
$table->dropColumn('service_type');
|
||||
});
|
||||
}
|
||||
};
|
@ -29,6 +29,7 @@ services:
|
||||
- QUEUE_CONNECTION
|
||||
- REDIS_HOST
|
||||
- REDIS_PASSWORD
|
||||
- HORIZON_BALANCE
|
||||
- HORIZON_MAX_PROCESSES
|
||||
- HORIZON_BALANCE_MAX_SHIFT
|
||||
- HORIZON_BALANCE_COOLDOWN
|
||||
@ -47,6 +48,7 @@ services:
|
||||
- PUSHER_APP_SECRET
|
||||
- AUTOUPDATE
|
||||
- SELF_HOSTED
|
||||
- SSH_MUX_PERSIST_TIME
|
||||
- FEEDBACK_DISCORD_WEBHOOK
|
||||
- WAITLIST
|
||||
- SUBSCRIPTION_PROVIDER
|
||||
|
BIN
public/svgs/activepieces.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
public/svgs/authentik.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
1
public/svgs/classicpress.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 120 120" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path d="M111.108,9.531l-0.006,-0.011c-0.435,-0.618 -1.151,-0.981 -1.906,-0.966c-13.039,0.734 -34.043,8.376 -34.929,8.701c-0.512,0.188 -0.932,0.566 -1.174,1.054l-2.96,5.911l-2.181,-2.183c-0.658,-0.664 -1.681,-0.819 -2.508,-0.381c-2.497,1.305 -15.121,7.998 -17.807,11.256c-1.559,1.885 -2.764,6.031 -3.581,9.685l-1.427,-2.846c-0.295,-0.595 -0.851,-1.018 -1.503,-1.144c-0.652,-0.135 -1.33,0.048 -1.826,0.492c-0.755,0.663 -16.512,14.842 -13.232,32.52c12.607,-17.973 27.758,-33.089 52.542,-45.254c0.295,-0.146 0.619,-0.221 0.948,-0.221c1.175,-0 2.143,0.967 2.143,2.144c-0,0.814 -0.465,1.561 -1.195,1.922l-0.007,-0c-1.442,0.713 -2.842,1.433 -4.216,2.162c-0.35,0.184 -0.693,0.372 -1.038,0.558c-1.055,0.571 -2.093,1.148 -3.113,1.73c-0.333,0.191 -0.662,0.378 -0.99,0.569c-2.629,1.532 -5.141,3.108 -7.534,4.727c-0.197,0.132 -0.39,0.268 -0.59,0.406c-1.042,0.713 -2.064,1.436 -3.067,2.166c-0.179,0.129 -0.35,0.257 -0.526,0.387c-23.388,17.253 -35.764,39.115 -50.59,65.336c-0.182,0.322 -0.279,0.685 -0.279,1.055c0,1.174 0.967,2.14 2.141,2.14l0.005,0c0.772,0.003 1.487,-0.413 1.865,-1.085c5.905,-10.442 11.416,-20.177 17.392,-29.187c2.434,2.967 6.13,4.62 9.964,4.46c16.21,-0 42.981,-23.712 45.677,-31.349c0.081,-0.23 0.123,-0.472 0.123,-0.715c0,-0.98 -0.672,-1.841 -1.623,-2.077l-7.145,-1.789l15.555,-2.592c0.677,-0.111 1.261,-0.543 1.564,-1.16l17.152,-34.3c0.328,-0.679 0.283,-1.482 -0.118,-2.121Z" style="fill:#fff;fill-rule:nonzero;"/></svg>
|
After Width: | Height: | Size: 1.8 KiB |
26
public/svgs/cloudflared.svg
Normal file
@ -0,0 +1,26 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 101.4 33.5">
|
||||
<defs>
|
||||
<style>
|
||||
.a {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.b {
|
||||
fill: #f48120;
|
||||
}
|
||||
|
||||
.c {
|
||||
fill: #faad3f;
|
||||
}
|
||||
|
||||
.d {
|
||||
fill: #404041;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<title>Cloudflare logo</title>
|
||||
<path class="a" d="M94.7,10.6,89.1,9.3l-1-.4-25.7.2V21.5l32.3.1Z"/>
|
||||
<path class="b" d="M84.2,20.4a2.85546,2.85546,0,0,0-.3-2.6,3.09428,3.09428,0,0,0-2.1-1.1l-17.4-.2c-.1,0-.2-.1-.3-.1a.1875.1875,0,0,1,0-.3c.1-.2.2-.3.4-.3L82,15.6a6.29223,6.29223,0,0,0,5.1-3.8l1-2.6c0-.1.1-.2,0-.3A11.39646,11.39646,0,0,0,66.2,7.7a5.45941,5.45941,0,0,0-3.6-1A5.20936,5.20936,0,0,0,58,11.3a5.46262,5.46262,0,0,0,.1,1.8A7.30177,7.30177,0,0,0,51,20.4a4.102,4.102,0,0,0,.1,1.1.3193.3193,0,0,0,.3.3H83.5c.2,0,.4-.1.4-.3Z"/>
|
||||
<path class="c" d="M89.7,9.2h-.5c-.1,0-.2.1-.3.2l-.7,2.4a2.85546,2.85546,0,0,0,.3,2.6,3.09428,3.09428,0,0,0,2.1,1.1l3.7.2c.1,0,.2.1.3.1a.1875.1875,0,0,1,0,.3c-.1.2-.2.3-.4.3l-3.8.2a6.29223,6.29223,0,0,0-5.1,3.8l-.2.9c-.1.1,0,.3.2.3H98.5a.26517.26517,0,0,0,.3-.3,10.87184,10.87184,0,0,0,.4-2.6,9.56045,9.56045,0,0,0-9.5-9.5"/>
|
||||
<path class="d" d="M100.5,27.2a.9.9,0,1,1,.9-.9.89626.89626,0,0,1-.9.9m0-1.6a.7.7,0,1,0,.7.7.68354.68354,0,0,0-.7-.7m.4,1.2h-.2l-.2-.3h-.2v.3h-.2v-.9h.5a.26517.26517,0,0,1,.3.3c0,.1-.1.2-.2.3l.2.3Zm-.3-.5c.1,0,.1,0,.1-.1a.09794.09794,0,0,0-.1-.1h-.3v.3h.3Zm-89.7-.9h2.2v6h3.8v1.9h-6Zm8.3,3.9a4.10491,4.10491,0,0,1,4.3-4.1,4.02,4.02,0,0,1,4.2,4.1,4.10491,4.10491,0,0,1-4.3,4.1,4.07888,4.07888,0,0,1-4.2-4.1m6.3,0a2.05565,2.05565,0,0,0-2-2.2,2.1025,2.1025,0,0,0,0,4.2c1.2.2,2-.8,2-2m4.9.5V25.4h2.2v4.4c0,1.1.6,1.7,1.5,1.7a1.39926,1.39926,0,0,0,1.5-1.6V25.4h2.2v4.4c0,2.6-1.5,3.7-3.7,3.7-2.3-.1-3.7-1.2-3.7-3.7m10.7-4.4h3.1c2.8,0,4.5,1.6,4.5,3.9s-1.7,4-4.5,4h-3V25.4Zm3.1,5.9a2.00909,2.00909,0,1,0,0-4h-.9v4Zm7.6-5.9h6.3v1.9H54v1.3h3.7v1.8H54v2.9H51.8Zm9.4,0h2.2v6h3.8v1.9h-6Zm11.7-.1h2.2l3.4,8H76.1l-.6-1.4H72.4l-.6,1.4H69.5Zm2,4.9L74,28l-.9,2.2Zm6.4-4.8H85a3.41818,3.41818,0,0,1,2.6.9,2.62373,2.62373,0,0,1-.9,4.2l1.9,2.8H86.1l-1.6-2.4h-1v2.4H81.3Zm3.6,3.8c.7,0,1.2-.4,1.2-.9,0-.6-.5-.9-1.2-.9H83.5v1.9h1.4Zm6.5-3.8h6.4v1.8H93.6v1.2h3.8v1.8H93.6v1.2h4.3v1.9H91.4ZM6.1,30.3a1.97548,1.97548,0,0,1-1.8,1.2,2.1025,2.1025,0,0,1,0-4.2,2.0977,2.0977,0,0,1,1.9,1.3H8.5a4.13459,4.13459,0,0,0-4.2-3.3A4.1651,4.1651,0,0,0,0,29.4a4.07888,4.07888,0,0,0,4.2,4.1,4.31812,4.31812,0,0,0,4.2-3.2Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
14
public/svgs/logto_dark.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg viewBox="0 0 154 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 7.49503C2 6.1159 2 5.42633 2.26742 4.87131C2.50309 4.38219 2.8821 3.96763 3.35931 3.67702C3.90082 3.34726 4.62076 3.24937 6.06063 3.05361L26.5958 0.261598C28.4598 0.00816784 29.3918 -0.118547 30.1161 0.149002C30.7518 0.383867 31.2824 0.81992 31.6179 1.38326C32 2.025 32 2.91768 32 4.70302V43.297C32 45.0823 32 45.975 31.6179 46.6167C31.2824 47.1801 30.7518 47.6161 30.1161 47.851C29.3918 48.1185 28.4598 47.9918 26.5959 47.7384H26.5959H26.5958L6.06063 44.9464L6.06057 44.9464C4.62074 44.7506 3.90081 44.6527 3.35931 44.323C2.8821 44.0324 2.50309 43.6178 2.26742 43.1287C2 42.5737 2 41.8841 2 40.505V7.49503ZM24 20.5078C24 20.0879 24 19.8779 24.0705 19.7119C24.1326 19.5657 24.2323 19.4436 24.3567 19.3611C24.4979 19.2674 24.6847 19.2484 25.0583 19.2104L26.731 19.0402C27.1712 18.9954 27.3912 18.973 27.5611 19.0552C27.7104 19.1273 27.8338 19.2535 27.9115 19.4134C28 19.5954 28 19.8428 28 20.3376V27.6624C28 28.1572 28 28.4046 27.9115 28.5866C27.8338 28.7465 27.7104 28.8727 27.5611 28.9448C27.3912 29.027 27.1712 29.0046 26.731 28.9598L25.0583 28.7896C24.6847 28.7516 24.4979 28.7326 24.3567 28.6389C24.2323 28.5564 24.1326 28.4343 24.0705 28.2881C24 28.1221 24 27.9121 24 27.4922V20.5078ZM37.6444 5H35V44H37.6444C39.169 44 39.9313 44 40.5136 43.7024C41.0259 43.4407 41.4423 43.0231 41.7033 42.5094C42 41.9254 42 41.1609 42 39.632V9.368C42 7.83906 42 7.07458 41.7033 6.49061C41.4423 5.97692 41.0259 5.55929 40.5136 5.29755C39.9313 5 39.169 5 37.6444 5Z" fill="url(#paint0_linear_865_5913)"/>
|
||||
<path d="M85.5299 36.8007C83.4751 36.8007 81.7004 36.3737 80.206 35.5198C78.7116 34.6658 77.5641 33.4783 76.7635 31.9572C75.963 30.4094 75.5627 28.6215 75.5627 26.5933C75.5627 24.5118 75.9763 22.7105 76.8036 21.1894C77.6308 19.6683 78.7917 18.4808 80.2861 17.6268C81.8072 16.7729 83.5818 16.3459 85.6099 16.3459C87.6648 16.3459 89.426 16.7729 90.8938 17.6268C92.3882 18.4808 93.549 19.6683 94.3763 21.1894C95.2036 22.7105 95.6172 24.4985 95.6172 26.5533C95.6172 28.5814 95.1902 30.3694 94.3363 31.9172C93.509 33.465 92.3482 34.6658 90.8537 35.5198C89.3593 36.3471 87.5847 36.774 85.5299 36.8007ZM85.5699 32.9179C86.6907 32.9179 87.6514 32.6377 88.452 32.0773C89.2526 31.5169 89.8664 30.7563 90.2933 29.7956C90.747 28.8349 90.9738 27.7542 90.9738 26.5533C90.9738 25.3257 90.7603 24.2316 90.3334 23.2709C89.9064 22.3102 89.2926 21.563 88.492 21.0293C87.6914 20.4689 86.7307 20.1887 85.6099 20.1887C84.5425 20.1887 83.5951 20.4689 82.7679 21.0293C81.9406 21.5897 81.3001 22.3502 80.8465 23.3109C80.4195 24.2716 80.1927 25.3524 80.166 26.5533C80.166 27.7808 80.3795 28.875 80.8065 29.8357C81.2334 30.7964 81.8606 31.5569 82.6878 32.1173C83.5151 32.651 84.4758 32.9179 85.5699 32.9179Z" fill="white"/>
|
||||
<path d="M116.582 17.0751C116.582 16.9598 116.488 16.8663 116.373 16.8663H112.268C112.152 16.8663 112.059 16.9598 112.059 17.0751V37.5613C112.059 38.9222 111.552 39.7675 110.538 40.5147C109.55 41.2619 108.44 41.5062 106.865 41.5329C105.771 41.5062 104.744 41.4088 103.837 41.1152C102.986 40.8649 102.055 40.4971 101.084 40.012C100.978 39.9592 100.849 40.0031 100.799 40.1101L99.3229 43.2556C99.2803 43.3465 99.3081 43.4551 99.3911 43.5116C99.8414 43.8181 100.368 44.0919 100.971 44.333C101.638 44.5998 102.318 44.8133 103.012 44.9734C103.733 45.1602 104.477 45.3161 105.118 45.3961C105.785 45.4762 106.415 45.487 106.815 45.487C108.843 45.487 110.578 45.1668 112.019 44.5263C113.486 43.8858 114.607 42.9785 115.381 41.8043C116.182 40.6302 116.582 39.2291 116.582 37.6013V17.0751ZM113.314 30.3339C113.253 30.1892 113.059 30.1611 112.956 30.2796C112.171 31.183 111.373 31.823 110.618 32.2374C109.817 32.6911 108.896 32.9179 107.856 32.9179C106.788 32.9179 105.854 32.651 105.054 32.1173C104.28 31.5569 103.693 30.7964 103.292 29.8357C102.892 28.875 102.692 27.7675 102.692 26.5133C102.692 25.3124 102.892 24.2449 103.292 23.3109C103.693 22.3502 104.28 21.5897 105.054 21.0293C105.854 20.4689 106.802 20.1887 107.896 20.1887C108.91 20.1887 109.71 20.4022 110.538 20.8291C111.31 21.2153 112.132 22.0971 112.839 23.0181C112.938 23.1464 113.139 23.1225 113.202 22.9736L114.338 20.2934C114.366 20.2281 114.359 20.1533 114.319 20.0952C112.885 18.0383 110.461 16.3459 107.7 16.3459C105.699 16.3459 104.12 16.7729 102.652 17.6268C101.184 18.4808 100.05 19.6683 99.2494 21.1894C98.4755 22.7105 98.0886 24.4851 98.0886 26.5133C98.0886 28.5948 98.4755 30.4094 99.2494 31.9572C100.023 33.4783 101.131 34.6658 102.572 35.5198C104.04 36.3737 105.774 36.8007 107.776 36.8007C108.523 36.8007 109.297 36.6539 110.097 36.3604C110.925 36.0935 111.725 35.6932 112.499 35.1595C113.239 34.6489 113.894 34.0405 114.464 33.3345C114.511 33.2753 114.522 33.1947 114.493 33.1247L113.314 30.3339Z" fill="white"/>
|
||||
<path d="M132.061 17.2352C132.061 17.1199 131.968 17.0264 131.852 17.0264H127.386C127.271 17.0264 127.178 16.9329 127.178 16.8176V11.3577C127.178 11.2424 127.084 11.1489 126.969 11.1489H122.903C122.788 11.1489 122.694 11.2424 122.694 11.3577V16.8176C122.694 16.9329 122.601 17.0264 122.486 17.0264H119.766C119.65 17.0264 119.556 17.1212 119.557 17.2374L119.593 20.6225C119.594 20.737 119.687 20.8291 119.801 20.8291H122.486C122.601 20.8291 122.694 20.9226 122.694 21.038V30.4761C122.694 31.8905 122.975 33.0914 123.535 34.0787C124.122 35.0394 124.923 35.7733 125.937 36.2803C126.951 36.7874 128.178 37.0409 129.619 37.0409C130.02 37.0409 130.447 36.9875 130.9 36.8808C131.381 36.774 131.845 36.6617 132.245 36.5015C132.602 36.3675 133.299 36.0182 133.654 35.8124C133.748 35.7576 133.777 35.6383 133.724 35.5431L132.072 32.631C132.02 32.5386 131.907 32.5 131.808 32.5378C131.541 32.6396 131.265 32.7234 130.98 32.7891C130.633 32.8692 130.286 32.9092 129.94 32.9092C129.006 32.9092 128.312 32.8112 127.858 32.1974C127.404 31.5569 127.178 30.743 127.178 29.7556V21.038C127.178 20.9226 127.271 20.8291 127.386 20.8291H131.852C131.968 20.8291 132.061 20.7356 132.061 20.6203V17.2352Z" fill="white"/>
|
||||
<path d="M143.913 36.7907C141.858 36.7907 140.083 36.3638 138.589 35.5098C137.094 34.6559 135.947 33.4683 135.146 31.9472C134.346 30.3994 133.945 28.6115 133.945 26.5833C133.945 24.5018 134.359 22.7005 135.186 21.1794C136.014 19.6583 137.174 18.4708 138.669 17.6168C140.19 16.7629 141.965 16.3359 143.993 16.3359C146.048 16.3359 147.809 16.7629 149.277 17.6168C150.771 18.4708 151.932 19.6583 152.759 21.1794C153.586 22.7005 154 24.4885 154 26.5433C154 28.5715 153.573 30.3594 152.719 31.9072C151.892 33.455 150.731 34.6559 149.237 35.5098C147.742 36.3371 145.968 36.7641 143.913 36.7907ZM143.953 32.9079C145.074 32.9079 146.034 32.6277 146.835 32.0673C147.635 31.5069 148.249 30.7464 148.676 29.7857C149.13 28.825 149.357 27.7442 149.357 26.5433C149.357 25.3158 149.143 24.2216 148.716 23.2609C148.289 22.3002 147.675 21.553 146.875 21.0193C146.074 20.4589 145.114 20.1787 143.993 20.1787C142.925 20.1787 141.978 20.4589 141.151 21.0193C140.323 21.5797 139.683 22.3403 139.229 23.301C138.802 24.2617 138.575 25.3424 138.549 26.5433C138.549 27.7709 138.762 28.865 139.189 29.8257C139.616 30.7864 140.243 31.5469 141.071 32.1074C141.898 32.6411 142.859 32.9079 143.953 32.9079Z" fill="white"/>
|
||||
<path d="M73.6634 32.7578C73.6634 32.6425 73.5699 32.549 73.4546 32.549H62.652C62.5367 32.549 62.4432 32.4555 62.4432 32.3401V11.3136C62.4432 11.1982 62.3497 11.1047 62.2344 11.1047H58.2088C58.0935 11.1047 58 11.1982 58 11.3136V36.5919C58 36.7072 58.0935 36.8007 58.2088 36.8007H73.4546C73.5699 36.8007 73.6634 36.7072 73.6634 36.5919V32.7578Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_865_5913" x1="-10.381" y1="33.1953" x2="38.8896" y2="13.0537" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4B2EFB"/>
|
||||
<stop offset="1" stop-color="#E65FFC"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 7.6 KiB |
18
public/svgs/rxresume.svg
Normal file
After Width: | Height: | Size: 7.6 KiB |
1
public/svgs/unleash.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="bg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 251.43 251.03"><defs><style>.cls-1{fill:#1a4049;}.cls-2{fill:#fff;}.cls-3{fill:#817afe;}</style></defs><circle class="cls-1" cx="125.71" cy="125.31" r="80"/><polygon class="cls-2" points="137.14 91.03 137.14 113.88 137.14 136.74 160 136.74 160 113.88 160 91.03 137.14 91.03"/><polygon class="cls-2" points="114.29 113.88 114.29 91.03 91.43 91.03 91.43 113.88 91.43 136.74 91.43 159.6 114.29 159.6 137.14 159.6 137.14 136.74 114.29 136.74 114.29 113.88"/><rect class="cls-3" x="137.14" y="136.74" width="22.86" height="22.86"/></svg>
|
After Width: | Height: | Size: 593 B |
@ -41,7 +41,7 @@ option {
|
||||
}
|
||||
|
||||
.button {
|
||||
@apply flex items-center justify-center gap-2 px-2 py-1 text-sm text-black normal-case border rounded cursor-pointer bg-neutral-200/50 border-neutral-300 hover:bg-neutral-300 dark:bg-coolgray-200 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-500 dark:border-black hover:text-black disabled:bg-coolgray-100/10 disabled:cursor-not-allowed min-w-fit focus:outline-1 dark:disabled:text-neutral-600;
|
||||
@apply flex items-center justify-center gap-2 px-2 py-1 text-sm text-black normal-case border rounded cursor-pointer bg-neutral-200/50 border-neutral-300 hover:bg-neutral-300 dark:bg-coolgray-200 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-500 dark:border-black hover:text-black disabled:cursor-not-allowed min-w-fit focus:outline-1 dark:disabled:text-neutral-600 disabled:border-none disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300;
|
||||
}
|
||||
|
||||
button[isError]:not(:disabled) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
'disabled' => false,
|
||||
'action' => 'delete',
|
||||
'content' => null,
|
||||
'closeOutside' => true
|
||||
])
|
||||
<div x-data="{ modalOpen: false }" :class="{ 'z-40': modalOpen }" @keydown.window.escape="modalOpen=false" class="relative w-auto h-auto">
|
||||
@if ($content)
|
||||
@ -27,7 +28,7 @@
|
||||
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"
|
||||
class="absolute inset-0 w-full h-full bg-black bg-opacity-20 backdrop-blur-sm"></div>
|
||||
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" @click.outside="modalOpen=false" x-transition:enter="ease-out duration-100"
|
||||
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" @if ($closeOutside) @click.outside="modalOpen=false" @endif x-transition:enter="ease-out duration-100"
|
||||
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
|
||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave="ease-in duration-100"
|
||||
|
13
resources/views/components/page-loading.blade.php
Normal file
@ -0,0 +1,13 @@
|
||||
@props(['text' => "Loading..."])
|
||||
<div class="inline-flex items-center justify-center" {{ $attributes }}>
|
||||
@if (isset($text))
|
||||
<div>{{ $text }}</div>
|
||||
@endif
|
||||
<svg class="w-4 h-4 mx-1 ml-3 text-coollabs dark:text-warning animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
@ -12,7 +12,7 @@
|
||||
@if ($upgrade)
|
||||
<div>{{ $upgrade }}</div>
|
||||
@else
|
||||
<div class="text-xs dark:text-neutral-500 group-hover:dark:text-neutral-300">
|
||||
<div class="text-xs font-bold dark:text-neutral-500 group-hover:dark:text-neutral-300">
|
||||
{{ $description }}
|
||||
</div>
|
||||
@endif
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div>
|
||||
<p class="font-mono font-semibold text-7xl dark:text-warning">503</p>
|
||||
<h1 class="mt-4 font-bold tracking-tight dark:text-white">We are working on serious things.</h1>
|
||||
<p class="text-base leading-7 text-neutral-300">Service Unavailable. Be right back. Thanks for your
|
||||
<p class="text-base leading-7 text-black dark:text-neutral-300">Service Unavailable. Be right back. Thanks for your
|
||||
patience.
|
||||
</p>
|
||||
<div class="flex items-center mt-10 gap-x-6">
|
||||
|
@ -6,9 +6,13 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://api.fonts.coollabs.io" crossorigin>
|
||||
<link rel="dns-prefetch" href="https://api.fonts.coollabs.io" />
|
||||
<link rel="preload" href="https://api.fonts.coollabs.io/css2?family=Inter&display=swap" as="style" />
|
||||
<link rel="preload" href="https://api.fonts.coollabs.io/css2?family=Inter:wght@400;500;600;700;800&display=swap" as="style" />
|
||||
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/400.woff2" as="style" />
|
||||
<link href="https://api.fonts.coollabs.io/css2?family=Inter&display=swap" rel="stylesheet">
|
||||
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/500.woff2" as="style" />
|
||||
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/600.woff2" as="style" />
|
||||
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/700.woff2" as="style" />
|
||||
<link rel="preload" href="https://cdn.fonts.coollabs.io/inter/normal/800.woff2" as="style" />
|
||||
<link href="https://api.fonts.coollabs.io/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
<meta name="robots" content="noindex">
|
||||
<title>Coolify</title>
|
||||
@env('local')
|
||||
|
@ -9,8 +9,11 @@
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
<div
|
||||
class="flex flex-col-reverse w-full px-4 py-2 overflow-y-auto bg-white border border-solid rounded dark:text-white dark:bg-coolgray-100 scrollbar border-neutral-300 dark:border-coolgray-300 max-h-96">
|
||||
<div @class([
|
||||
'flex flex-col-reverse w-full px-4 py-2 overflow-y-auto bg-white border border-solid rounded dark:text-white dark:bg-coolgray-100 scrollbar border-neutral-300 dark:border-coolgray-300',
|
||||
'max-h-[48rem]' => $fullHeight,
|
||||
'max-h-96' => !$fullHeight,
|
||||
])>
|
||||
<pre class="font-mono whitespace-pre-wrap" @if ($isPollingActive) wire:poll.1000ms="polling" @endif>{{ RunRemoteProcess::decodeOutput($this->activity) }}</pre>
|
||||
</div>
|
||||
@else
|
||||
|
@ -103,7 +103,8 @@ class="flex items-center justify-center gap-2 pt-4 pb-2 mr-4 text-xs lg:py-0 lg:
|
||||
@else
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class='font-bold dark:text-warning'>No servers found.</div>
|
||||
<div class="flex items-center gap-1"><x-modal-input buttonTitle="Add" title="New Server">
|
||||
<div class="flex items-center gap-1">
|
||||
<x-modal-input buttonTitle="Add" title="New Server" :closeOutside="false">
|
||||
<livewire:server.create />
|
||||
</x-modal-input> your first server
|
||||
or
|
||||
|
@ -36,7 +36,10 @@
|
||||
<x-slot:description>
|
||||
<span>Coolify could not connect to its real-time service.<br>This will cause unusual problems on the UI
|
||||
if
|
||||
not fixed! <br><br>Please check the
|
||||
not fixed! <br><br>
|
||||
Please ensure that you have opened the
|
||||
<a class="underline" href='https://coolify.io/docs/knowledge-base/server/firewall' target='_blank'>required ports</a>,
|
||||
check the
|
||||
related <a class="underline" href='https://coolify.io/docs/knowledge-base/cloudflare/tunnels'
|
||||
target='_blank'>documentation</a> or get
|
||||
help on <a class="underline" href='https://coollabs.io/discord' target='_blank'>Discord</a>. </span>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<h2>Advanced</h2>
|
||||
</div>
|
||||
<div>Advanced configuration for your application.</div>
|
||||
<div class="pt-4 w-96">
|
||||
<div class="flex flex-col gap-1 pt-4 md:w-96">
|
||||
<h3>General</h3>
|
||||
@if ($application->git_based())
|
||||
<x-forms.checkbox helper="Automatically deploy new commits based on Git webhooks." instantSave
|
||||
@ -49,7 +49,7 @@
|
||||
@if ($application->build_pack !== 'dockercompose')
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox
|
||||
helper="Enable GPU usage for this application. More info <a href='https://docs.docker.com/compose/gpu-support/' class='dark:text-white underline' target='_blank'>here</a>."
|
||||
helper="Enable GPU usage for this application. More info <a href='https://docs.docker.com/compose/gpu-support/' class='underline dark:text-white' target='_blank'>here</a>."
|
||||
instantSave id="application.settings.is_gpu_enabled" label="Attach GPU" />
|
||||
@if ($application->settings->is_gpu_enabled)
|
||||
<h5>GPU Settings</h5>
|
||||
@ -64,7 +64,7 @@
|
||||
<x-forms.input label="GPU Count" placeholder="empty means use all GPUs"
|
||||
id="application.settings.gpu_count"> </x-forms.input>
|
||||
<x-forms.input label="GPU Device Ids" placeholder="0,2"
|
||||
helper="Comma separated list of device ids. More info <a href='https://docs.docker.com/compose/gpu-support/#access-specific-devices' class='dark:text-white underline' target='_blank'>here</a>."
|
||||
helper="Comma separated list of device ids. More info <a href='https://docs.docker.com/compose/gpu-support/#access-specific-devices' class='underline dark:text-white' target='_blank'>here</a>."
|
||||
id="application.settings.gpu_device_ids"> </x-forms.input>
|
||||
|
||||
</div>
|
||||
|
@ -19,7 +19,7 @@
|
||||
href="#">Environment
|
||||
Variables</a>
|
||||
@endif
|
||||
@if ($application->build_pack !== 'static' && $application->build_pack !== 'dockercompose')
|
||||
@if ($application->build_pack !== 'static')
|
||||
<a class="menu-item" :class="activeTab === 'storages' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'storages'; window.location.hash = 'storages'" href="#">Storages
|
||||
</a>
|
||||
@ -83,48 +83,48 @@
|
||||
<livewire:project.application.general :application="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'swarm'" class="h-full">
|
||||
<livewire:project.application.swarm :application="$application" />
|
||||
<livewire:project.application.swarm :application="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'advanced'" class="h-full">
|
||||
<livewire:project.application.advanced :application="$application" />
|
||||
<livewire:project.application.advanced :application="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'environment-variables'">
|
||||
<livewire:project.shared.environment-variable.all :resource="$application" />
|
||||
<livewire:project.shared.environment-variable.all :resource="$application" lazy />
|
||||
</div>
|
||||
@if ($application->git_based())
|
||||
<div x-cloak x-show="activeTab === 'source'">
|
||||
<livewire:project.application.source :application="$application" />
|
||||
<livewire:project.application.source :application="$application" lazy />
|
||||
</div>
|
||||
@endif
|
||||
<div x-cloak x-show="activeTab === 'servers'">
|
||||
<livewire:project.shared.destination :resource="$application" :servers="$servers" />
|
||||
<livewire:project.shared.destination :resource="$application" :servers="$servers" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'storages'">
|
||||
<livewire:project.service.storage :resource="$application" />
|
||||
<livewire:project.service.storage :resource="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'webhooks'">
|
||||
<livewire:project.shared.webhooks :resource="$application" />
|
||||
<livewire:project.shared.webhooks :resource="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'previews'">
|
||||
<livewire:project.application.previews :application="$application" />
|
||||
<livewire:project.application.previews :application="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'health'">
|
||||
<livewire:project.shared.health-checks :resource="$application" />
|
||||
<livewire:project.shared.health-checks :resource="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'rollback'">
|
||||
<livewire:project.application.rollback :application="$application" />
|
||||
<livewire:project.application.rollback :application="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'resource-limits'">
|
||||
<livewire:project.shared.resource-limits :resource="$application" />
|
||||
<livewire:project.shared.resource-limits :resource="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'scheduled-tasks'">
|
||||
<livewire:project.shared.scheduled-task.all :resource="$application" />
|
||||
<livewire:project.shared.scheduled-task.all :resource="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'resource-operations'">
|
||||
<livewire:project.shared.resource-operations :resource="$application" />
|
||||
<livewire:project.shared.resource-operations :resource="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'tags'">
|
||||
<livewire:project.shared.tags :resource="$application" />
|
||||
<livewire:project.shared.tags :resource="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'danger'">
|
||||
<livewire:project.shared.danger :resource="$application" />
|
||||
|
@ -87,9 +87,9 @@ class="fixed top-4 right-16" x-on:click="toggleScroll"><svg class="icon" viewBox
|
||||
@foreach (decode_remote_command_output($application_deployment_queue) as $line)
|
||||
<span @class([
|
||||
'dark:text-warning whitespace-pre-line' => $line['hidden'],
|
||||
'text-red-500 font-bold whitespace-pre-line' => $line['type'] == 'stderr',
|
||||
'text-red-500 font-bold' => $line['type'] == 'stderr',
|
||||
])>[{{ $line['timestamp'] }}] @if ($line['hidden'])
|
||||
<br><br>COMMAND: {{ $line['command'] }}<br><br>OUTPUT :
|
||||
<br><br>[COMMAND] {{ $line['command'] }}<br>[OUTPUT]
|
||||
@endif @if (str($line['output'])->contains('http://') || str($line['output'])->contains('https://'))
|
||||
@php
|
||||
$line['output'] = preg_replace(
|
||||
@ -100,8 +100,8 @@ class="fixed top-4 right-16" x-on:click="toggleScroll"><svg class="icon" viewBox
|
||||
@endphp {!! $line['output'] !!}
|
||||
@else
|
||||
{{ $line['output'] }}
|
||||
|
||||
@endif
|
||||
<br>
|
||||
</span>
|
||||
@endforeach
|
||||
@else
|
||||
|
@ -17,81 +17,96 @@
|
||||
@endif
|
||||
<x-applications.links :application="$application" />
|
||||
<div class="flex-1"></div>
|
||||
@if ($application->build_pack === 'dockercompose' && is_null($application->docker_compose_raw))
|
||||
<div>Please load a Compose file.</div>
|
||||
@else
|
||||
@if (!$application->destination->server->isSwarm())
|
||||
<x-applications.advanced :application="$application" />
|
||||
@endif
|
||||
<div class="flex gap-2">
|
||||
@if (!str($application->status)->startsWith('exited'))
|
||||
@if (!$application->destination->server->isSwarm())
|
||||
<x-forms.button title="With rolling update if possible" wire:click='deploy'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-orange-400"
|
||||
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path
|
||||
d="M10.09 4.01l.496 -.495a2 2 0 0 1 2.828 0l7.071 7.07a2 2 0 0 1 0 2.83l-7.07 7.07a2 2 0 0 1 -2.83 0l-7.07 -7.07a2 2 0 0 1 0 -2.83l3.535 -3.535h-3.988">
|
||||
</path>
|
||||
<path d="M7.05 11.038v-3.988"></path>
|
||||
</svg>
|
||||
Redeploy
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@if ($application->build_pack !== 'dockercompose')
|
||||
@if ($application->destination->server->isSwarm())
|
||||
<x-forms.button title="Redeploy Swarm Service (rolling update)" wire:click='deploy'>
|
||||
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2">
|
||||
<path
|
||||
d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
|
||||
<path d="M20 4v5h-5" />
|
||||
</g>
|
||||
<div class="flex items-center gap-2">
|
||||
@if ($application->build_pack === 'dockercompose' && is_null($application->docker_compose_raw))
|
||||
<div>Please load a Compose file.</div>
|
||||
@else
|
||||
@if (!$application->destination->server->isSwarm())
|
||||
<x-applications.advanced :application="$application" />
|
||||
@endif
|
||||
<div class="flex gap-2">
|
||||
@if (!str($application->status)->startsWith('exited'))
|
||||
@if (!$application->destination->server->isSwarm())
|
||||
<x-forms.button title="With rolling update if possible" wire:click='deploy'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-orange-400"
|
||||
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path
|
||||
d="M10.09 4.01l.496 -.495a2 2 0 0 1 2.828 0l7.071 7.07a2 2 0 0 1 0 2.83l-7.07 7.07a2 2 0 0 1 -2.83 0l-7.07 -7.07a2 2 0 0 1 0 -2.83l3.535 -3.535h-3.988">
|
||||
</path>
|
||||
<path d="M7.05 11.038v-3.988"></path>
|
||||
</svg>
|
||||
Update Service
|
||||
</x-forms.button>
|
||||
@else
|
||||
<x-forms.button title="Restart without rebuilding" wire:click='restart'>
|
||||
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2">
|
||||
<path
|
||||
d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
|
||||
<path d="M20 4v5h-5" />
|
||||
</g>
|
||||
</svg>
|
||||
Restart
|
||||
Redeploy
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@if ($application->build_pack !== 'dockercompose')
|
||||
@if ($application->destination->server->isSwarm())
|
||||
<x-forms.button title="Redeploy Swarm Service (rolling update)" wire:click='deploy'>
|
||||
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2">
|
||||
<path
|
||||
d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
|
||||
<path d="M20 4v5h-5" />
|
||||
</g>
|
||||
</svg>
|
||||
Update Service
|
||||
</x-forms.button>
|
||||
@else
|
||||
<x-forms.button title="Restart without rebuilding" wire:click='restart'>
|
||||
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2">
|
||||
<path
|
||||
d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
|
||||
<path d="M20 4v5h-5" />
|
||||
</g>
|
||||
</svg>
|
||||
Restart
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@endif
|
||||
<x-modal-confirmation @click="$wire.dispatch('stopEvent')">
|
||||
<x-slot:button-title>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
|
||||
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path
|
||||
d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
|
||||
</path>
|
||||
<path
|
||||
d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
|
||||
</path>
|
||||
</svg>
|
||||
Stop
|
||||
</x-slot:button-title>
|
||||
This application will be stopped. <br>Please think again.
|
||||
</x-modal-confirmation>
|
||||
@else
|
||||
<x-forms.button wire:click='deploy'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning"
|
||||
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M7 4v16l13 -8z" />
|
||||
</svg>
|
||||
Deploy
|
||||
</x-forms.button>
|
||||
@endif
|
||||
<x-forms.button wire:click='stop'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
|
||||
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
|
||||
</path>
|
||||
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
|
||||
</path>
|
||||
</svg>
|
||||
Stop
|
||||
</x-forms.button>
|
||||
@else
|
||||
<x-forms.button wire:click='deploy'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M7 4v16l13 -8z" />
|
||||
</svg>
|
||||
Deploy
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@script
|
||||
<script>
|
||||
$wire.$on('stopEvent', () => {
|
||||
$wire.$dispatch('info', 'Stopping application.');
|
||||
$wire.$call('stop');
|
||||
});
|
||||
</script>
|
||||
@endscript
|
||||
</nav>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<div class="flex gap-2">
|
||||
<h2>Shared Variables</h2>
|
||||
<x-modal-input buttonTitle="+ Add" title="New Shared Variable">
|
||||
<livewire:project.shared.environment-variable.add />
|
||||
<livewire:project.shared.environment-variable.add :shared="true" />
|
||||
</x-modal-input>
|
||||
</div>
|
||||
<div class="pb-4 lg:flex lg:gap-1">
|
||||
|
@ -45,7 +45,7 @@
|
||||
<div class="flex gap-2 pt-10">
|
||||
<h2>Shared Variables</h2>
|
||||
<x-modal-input buttonTitle="+ Add" title="New Shared Variable">
|
||||
<livewire:project.shared.environment-variable.add />
|
||||
<livewire:project.shared.environment-variable.add :shared="true" />
|
||||
</x-modal-input>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 pb-4">You can use these variables anywhere with <span class="dark:text-warning text-coollabs">@{{environment.VARIABLENAME}}</span><x-helper
|
||||
|
@ -2,7 +2,7 @@
|
||||
<h1>Create a new Application</h1>
|
||||
<div class="pb-4">You can deploy an existing Docker Image from any Registry.</div>
|
||||
<form wire:submit="submit">
|
||||
<div class="flex gap-2 pb-1">
|
||||
<div class="flex gap-2 pt-4 pb-1">
|
||||
<h2>Docker Image</h2>
|
||||
<x-forms.button type="submit">Save</x-forms.button>
|
||||
</div>
|
||||
|