Merge branch 'coollabsio:main' into mongodb-restore
No files matched your search
@@ -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 @@ class StartClickhouse
|
||||
}
|
||||
$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 @@ class StartDatabaseProxy
|
||||
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 @@ class StartDragonfly
|
||||
}
|
||||
$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 @@ class StartKeydb
|
||||
}
|
||||
$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 @@ class StartMariadb
|
||||
}
|
||||
$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 @@ class StartMariadb
|
||||
$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 @@ class StartMongodb
|
||||
|
||||
$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 @@ class StartMongodb
|
||||
$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 @@ class StartMysql
|
||||
}
|
||||
$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 @@ class StartMysql
|
||||
$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 @@ class StartPostgresql
|
||||
}
|
||||
$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 @@ class StartPostgresql
|
||||
$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 @@ class StartPostgresql
|
||||
$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 @@ class StartRedis
|
||||
}
|
||||
$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 @@ class CheckConfiguration
|
||||
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 @@ class CheckProxy
|
||||
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 @@ class SaveConfiguration
|
||||
|
||||
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 @@ class StartProxy
|
||||
$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 @@ class ConfigureCloudflared
|
||||
$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 @@ class ConfigureCloudflared
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
throw $e;
|
||||
} finally {
|
||||
$commands = collect([
|
||||
"rm -fr /tmp/cloudflared",
|
||||
]);
|
||||
instant_remote_process($commands, $server);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,20 +48,28 @@ class InstallDocker
|
||||
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 @@ class InstallDocker
|
||||
"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 @@ Files:
|
||||
$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 @@ class Kernel extends ConsoleKernel
|
||||
$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 {
|
||||
|
||||
@@ -56,7 +56,7 @@ class Handler extends ExceptionHandler
|
||||
{
|
||||
$this->reportable(function (Throwable $e) {
|
||||
if (isDev()) {
|
||||
// return;
|
||||
return;
|
||||
}
|
||||
if ($e instanceof RuntimeException) {
|
||||
return;
|
||||
|
||||
@@ -49,6 +49,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private string $deployment_uuid;
|
||||
private int $pull_request_id;
|
||||
private string $commit;
|
||||
private bool $rollback;
|
||||
private bool $force_rebuild;
|
||||
private bool $restart_only;
|
||||
|
||||
@@ -117,6 +118,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->deployment_uuid = $this->application_deployment_queue->deployment_uuid;
|
||||
$this->pull_request_id = $this->application_deployment_queue->pull_request_id;
|
||||
$this->commit = $this->application_deployment_queue->commit;
|
||||
$this->rollback = $this->application_deployment_queue->rollback;
|
||||
$this->force_rebuild = $this->application_deployment_queue->force_rebuild;
|
||||
$this->restart_only = $this->application_deployment_queue->restart_only;
|
||||
$this->only_this_server = $this->application_deployment_queue->only_this_server;
|
||||
@@ -322,7 +324,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$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 +393,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
$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 +567,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
"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 +710,78 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
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");
|
||||
}
|
||||
}
|
||||
$envs = $envs->sort(function ($a, $b) {
|
||||
return strpos($a, '$') === false ? -1 : 1;
|
||||
});
|
||||
} else {
|
||||
$this->env_filename = ".env";
|
||||
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");
|
||||
}
|
||||
}
|
||||
$envs = $envs->sort(function ($a, $b) {
|
||||
return strpos($a, '$') === false ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
if ($envs->isEmpty()) {
|
||||
$this->env_filename = null;
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->original_server;
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"command" => "rm -f $this->configuration_dir/{$this->env_filename}",
|
||||
@@ -728,17 +789,51 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
"ignore_errors" => true
|
||||
]
|
||||
);
|
||||
return;
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->build_server;
|
||||
}
|
||||
} else {
|
||||
$envs_base64 = base64_encode($envs->implode("\n"));
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d | tee $this->workdir/{$this->env_filename} > /dev/null")
|
||||
],
|
||||
|
||||
);
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->original_server;
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null"
|
||||
]
|
||||
);
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->build_server;
|
||||
}
|
||||
}
|
||||
$envs_base64 = base64_encode($envs->implode("\n"));
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d > $this->workdir/.env")
|
||||
],
|
||||
[
|
||||
"echo '$envs_base64' | base64 -d > $this->configuration_dir/{$this->env_filename}"
|
||||
]
|
||||
);
|
||||
// $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()) {
|
||||
// $base64_dotenv = base64_encode($this->saved_outputs->get('dotenv')->value());
|
||||
// $this->execute_remote_command(
|
||||
// [
|
||||
// "echo '{$base64_dotenv}' | base64 -d | tee $this->configuration_dir/.env > /dev/null"
|
||||
// ]
|
||||
// );
|
||||
// } else {
|
||||
// $this->execute_remote_command(
|
||||
// [
|
||||
// "command" => "rm -f $this->configuration_dir/.env",
|
||||
// "hidden" => true,
|
||||
// "ignore_errors" => true
|
||||
// ]
|
||||
// );
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -973,7 +1068,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
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")
|
||||
@@ -993,7 +1088,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
],
|
||||
);
|
||||
}
|
||||
if ($this->saved_outputs->get('git_commit_sha')) {
|
||||
if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) {
|
||||
$this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t");
|
||||
}
|
||||
}
|
||||
@@ -1124,8 +1219,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
$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)));
|
||||
@@ -1159,8 +1254,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||
}
|
||||
$labels = $labels->map(function ($value, $key) {
|
||||
return escapeDollarSign($value);
|
||||
});
|
||||
$labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
|
||||
|
||||
// Check for custom HEALTHCHECK
|
||||
$this->custom_healthcheck_found = false;
|
||||
if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
|
||||
@@ -1179,7 +1276,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
'image' => $this->production_image_name,
|
||||
'container_name' => $this->container_name,
|
||||
'restart' => RESTART_MODE,
|
||||
'environment' => $environment_variables,
|
||||
'expose' => $ports,
|
||||
'networks' => [
|
||||
$this->destination->network,
|
||||
@@ -1200,10 +1296,22 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
]
|
||||
]
|
||||
];
|
||||
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 (!is_null($this->env_filename)) {
|
||||
$docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename];
|
||||
}
|
||||
if (!$this->custom_healthcheck_found) {
|
||||
$docker_compose['services'][$this->container_name]['healthcheck'] = [
|
||||
@@ -1357,8 +1465,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
|
||||
$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 +1522,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} 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 +1533,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} 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 +1545,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} 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 +1556,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} 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 +1658,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
} 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 +1679,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
$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 +1714,13 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
$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 +1733,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
$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 +1742,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
} 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 +1763,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
$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 +1881,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
$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 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
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 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
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 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
// 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 @@ class NewActivityMonitor extends Component
|
||||
}
|
||||
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 @@ class General extends Component
|
||||
}
|
||||
$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 @@ class General extends Component
|
||||
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 @@ class General extends Component
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->initLoadingCompose = false;
|
||||
|
||||
}
|
||||
}
|
||||
public function generateDomain(string $serviceName)
|
||||
@@ -184,7 +212,14 @@ class General extends Component
|
||||
$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()
|
||||
|
||||
@@ -18,7 +18,6 @@ class Rollback extends Component
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
}
|
||||
|
||||
public function rollbackImage($commit)
|
||||
{
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
@@ -27,6 +26,7 @@ class Rollback extends Component
|
||||
application: $this->application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
commit: $commit,
|
||||
rollback: true,
|
||||
force_rebuild: false,
|
||||
);
|
||||
return redirect()->route('project.application.deployment.show', [
|
||||
|
||||
@@ -25,6 +25,7 @@ class Edit extends Component
|
||||
'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 @@ class EnvironmentEdit extends Component
|
||||
'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 @@ class PublicGitRepository extends Component
|
||||
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 @@ class Create extends Component
|
||||
$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 @@ class Create extends Component
|
||||
}
|
||||
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 @@ class EditDomain extends Component
|
||||
} 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 @@ use Illuminate\Support\Str;
|
||||
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 @@ class FileStorage extends Component
|
||||
$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 @@ class StackForm extends Component
|
||||
$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->sortDesc();
|
||||
}
|
||||
public function saveCompose($raw)
|
||||
{
|
||||
|
||||
@@ -7,12 +7,13 @@ use Livewire\Component;
|
||||
|
||||
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 @@ class Storage extends Component
|
||||
$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 @@ use Livewire\Component;
|
||||
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 @@ class Add extends Component
|
||||
'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 @@ class Add extends Component
|
||||
$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 @@ class All extends Component
|
||||
$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 @@ class Show extends Component
|
||||
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 @@ class Logs extends Component
|
||||
} 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 @@ class Logs extends Component
|
||||
$this->servers = $this->servers->push($this->resource->server);
|
||||
}
|
||||
}
|
||||
$this->containers = $this->containers->sort();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,5 @@ use Livewire\Component;
|
||||
class All extends Component
|
||||
{
|
||||
public $resource;
|
||||
protected $listeners = ['refreshStorages'];
|
||||
|
||||
public function refreshStorages()
|
||||
{
|
||||
$this->resource->refresh();
|
||||
}
|
||||
protected $listeners = ['refresh_storages' => '$refresh'];
|
||||
}
|
||||
@@ -39,6 +39,6 @@ class Show extends Component
|
||||
public function delete()
|
||||
{
|
||||
$this->storage->delete();
|
||||
$this->dispatch('refreshStorages');
|
||||
$this->dispatch('refresh_storages');
|
||||
}
|
||||
}
|
||||
@@ -76,14 +76,14 @@ class Form extends Component
|
||||
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 @@ class Deploy extends Component
|
||||
'proxyStatusUpdated',
|
||||
'traefikDashboardAvailable',
|
||||
'serverRefresh' => 'proxyStatusUpdated',
|
||||
"checkProxy", "startProxy"
|
||||
"checkProxy",
|
||||
"startProxy"
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class NewDynamicConfiguration extends Component
|
||||
}
|
||||
$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 @@ class ShowPrivateKey extends Component
|
||||
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 @@ class ValidateAndInstall extends Component
|
||||
$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 @@ class ValidateAndInstall extends Component
|
||||
} 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 @@ class ValidateAndInstall extends Component
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -69,13 +69,14 @@ class Configuration extends Component
|
||||
}
|
||||
$this->validate();
|
||||
|
||||
if ($this->settings->is_dns_validation_enabled) {
|
||||
if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) {
|
||||
ray('asdf');
|
||||
if (!validate_dns_entry($this->settings->fqdn, $this->server)) {
|
||||
$this->dispatch('error', "Validating DNS ({$this->settings->fqdn}) failed.<br><br>Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||
$error_show = true;
|
||||
}
|
||||
}
|
||||
check_domain_usage(domain: $this->settings->fqdn);
|
||||
if ($this->settings->fqdn) check_domain_usage(domain: $this->settings->fqdn);
|
||||
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->replaceEnd(',', '')->trim();
|
||||
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->trim()->explode(',')->map(function ($dns) {
|
||||
return str($dns)->trim()->lower();
|
||||
|
||||
@@ -54,6 +54,7 @@ class PricingPlans extends Component
|
||||
return;
|
||||
}
|
||||
$payload = [
|
||||
'allow_promotion_codes' => true,
|
||||
'billing_address_collection' => 'required',
|
||||
'client_reference_id' => auth()->user()->id . ':' . currentTeam()->id,
|
||||
'line_items' => [[
|
||||
|
||||
@@ -21,6 +21,7 @@ class TeamSharedVariablesIndex extends Component
|
||||
'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 @@ class Application extends BaseModel
|
||||
{
|
||||
$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 @@ class Application extends BaseModel
|
||||
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 @@ class Application extends BaseModel
|
||||
});
|
||||
return $matches->count() > 0;
|
||||
}
|
||||
|
||||
public function getFilesFromServer(bool $isInit = false)
|
||||
{
|
||||
getFilesystemVolumesFromServer($this, $isInit);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
||||
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 @@ class EnvironmentVariable extends Model
|
||||
}
|
||||
);
|
||||
}
|
||||
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 @@ class LocalFileVolume extends BaseModel
|
||||
{
|
||||
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 @@ class LocalFileVolume extends BaseModel
|
||||
$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 @@ class LocalFileVolume extends BaseModel
|
||||
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 @@ respond 404
|
||||
$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 @@ respond 404
|
||||
|
||||
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 @@ respond 404
|
||||
$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 @@ $schema://$host {
|
||||
}";
|
||||
$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 @@ $schema://$host {
|
||||
// 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 @@ $schema://$host {
|
||||
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 @@ $schema://$host {
|
||||
|
||||
$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 @@ $schema://$host {
|
||||
}
|
||||
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,11 @@ $schema://$host {
|
||||
return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false);
|
||||
}
|
||||
}
|
||||
public function isNonRoot()
|
||||
{
|
||||
if ($this->user instanceof Stringable) {
|
||||
return $this->user->value() !== 'root';
|
||||
}
|
||||
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,76 @@ class Service extends BaseModel
|
||||
foreach ($applications as $application) {
|
||||
$image = str($application->image)->before(':')->value();
|
||||
switch ($image) {
|
||||
case str($image)?->contains('tolgee'):
|
||||
$data = collect([]);
|
||||
$admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_TOLGEE')->first();
|
||||
$data = $data->merge([
|
||||
'Admin User' => [
|
||||
'key' => 'TOLGEE_AUTHENTICATION_INITIAL_USERNAME',
|
||||
'value' => 'admin',
|
||||
'readonly' => true,
|
||||
'rules' => 'required',
|
||||
],
|
||||
]);
|
||||
if ($admin_password) {
|
||||
$data = $data->merge([
|
||||
'Admin Password' => [
|
||||
'key' => 'SERVICE_PASSWORD_TOLGEE',
|
||||
'value' => data_get($admin_password, 'value'),
|
||||
'rules' => 'required',
|
||||
'isPassword' => true,
|
||||
],
|
||||
]);
|
||||
}
|
||||
$fields->put('Tolgee', $data);
|
||||
break;
|
||||
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 +688,7 @@ class Service extends BaseModel
|
||||
$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 @@ class StandaloneClickhouse extends BaseModel
|
||||
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 @@ class StandaloneDragonfly extends BaseModel
|
||||
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 @@ class StandaloneDragonfly extends BaseModel
|
||||
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 @@ class StandaloneKeydb extends BaseModel
|
||||
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 @@ class StandaloneMariadb extends BaseModel
|
||||
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 @@ class StandaloneMongodb extends BaseModel
|
||||
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 @@ class StandaloneMysql extends BaseModel
|
||||
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 @@ class StandalonePostgresql extends BaseModel
|
||||
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 @@ class StandaloneRedis extends BaseModel
|
||||
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 @@ trait ExecuteRemoteCommand
|
||||
$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 @@ class Links extends Component
|
||||
$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) {
|
||||
|
||||
@@ -9,7 +9,7 @@ use App\Models\StandaloneDocker;
|
||||
use Illuminate\Support\Collection;
|
||||
use Spatie\Url\Url;
|
||||
|
||||
function queue_application_deployment(Application $application, string $deployment_uuid, int | null $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null, bool $no_questions_asked = false, Server $server = null, StandaloneDocker $destination = null, bool $only_this_server = false)
|
||||
function queue_application_deployment(Application $application, string $deployment_uuid, int | null $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null, bool $no_questions_asked = false, Server $server = null, StandaloneDocker $destination = null, bool $only_this_server = false, bool $rollback = false)
|
||||
{
|
||||
$application_id = $application->id;
|
||||
$deployment_link = Url::fromString($application->link() . "/deployment/{$deployment_uuid}");
|
||||
@@ -38,6 +38,7 @@ function queue_application_deployment(Application $application, string $deployme
|
||||
'is_webhook' => $is_webhook,
|
||||
'restart_only' => $restart_only,
|
||||
'commit' => $commit,
|
||||
'rollback' => $rollback,
|
||||
'git_type' => $git_type,
|
||||
'only_this_server' => $only_this_server
|
||||
]);
|
||||
|
||||
@@ -32,6 +32,7 @@ const DATABASE_DOCKER_IMAGES = [
|
||||
];
|
||||
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,9 @@ function remote_process(
|
||||
if ($command instanceof Collection) {
|
||||
$command = $command->toArray();
|
||||
}
|
||||
if ($server->isNonRoot()) {
|
||||
$command = parseCommandsByLineForSudo(collect($command), $server);
|
||||
}
|
||||
$command_string = implode("\n", $command);
|
||||
if (auth()->user()) {
|
||||
$teams = auth()->user()->teams->pluck('id');
|
||||
@@ -133,17 +136,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 +162,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 DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
|
||||
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 @@ return [
|
||||
*/
|
||||
|
||||
'maintenance' => [
|
||||
'driver' => 'file',
|
||||
// 'store' => 'redis',
|
||||
'driver' => 'cache',
|
||||
'store' => 'redis',
|
||||
],
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,6 +5,7 @@ return [
|
||||
'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 @@ return [
|
||||
'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 @@ return [
|
||||
*/
|
||||
|
||||
'pagination_theme' => 'tailwind',
|
||||
'lazy_placeholder' => 'components.page-loading',
|
||||
];
|
||||
@@ -7,7 +7,7 @@ return [
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => '4.0.0-beta.258',
|
||||
'release' => '4.0.0-beta.266',
|
||||
// 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.266';
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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('application_deployment_queues', function (Blueprint $table) {
|
||||
$table->boolean('rollback')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('application_deployment_queues', function (Blueprint $table) {
|
||||
$table->dropColumn('rollback');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
@@ -93,7 +95,7 @@ services:
|
||||
expose:
|
||||
- "${APP_PORT:-8000}"
|
||||
healthcheck:
|
||||
test: curl --fail http://localhost:80/api/health || exit 1
|
||||
test: curl --fail http://127.0.0.1:80/api/health || exit 1
|
||||
interval: 5s
|
||||
retries: 10
|
||||
timeout: 2s
|
||||
@@ -140,7 +142,7 @@ services:
|
||||
SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY}"
|
||||
SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET}"
|
||||
healthcheck:
|
||||
test: wget -qO- http://localhost:6001/ready || exit 1
|
||||
test: wget -qO- http://127.0.0.1:6001/ready || exit 1
|
||||
interval: 5s
|
||||
retries: 10
|
||||
timeout: 2s
|
||||
|
||||
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -0,0 +1,18 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="512" height="512" rx="9" fill="#FAFAFA" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M183.603 184.722L156.74 220H183.545L197.032 201.54L209.701 220H237.938L211.008 183.874L211.012 183.868L210.859 183.675L205.927 177.444L197.894 167.294L197.882 167.31L197.693 167.041L196.926 166.072L196.982 166.027L185.367 149.472H157.52L183.603 184.722ZM200.948 163.099L214.127 179.749L237.027 149.472H210.872L200.948 163.099Z"
|
||||
fill="#3F3F46" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M69.1087 98.9125V190H94.8735V165.797H108.938L108.938 165.797C108.943 165.797 108.948 165.797 108.953 165.797C118.377 165.381 125.285 164.448 133.094 161.494C133.195 161.455 133.296 161.417 133.397 161.378L133.382 161.356C138.893 158.705 143.19 155.066 146.273 150.442C149.569 145.41 151.218 139.468 151.218 132.615C151.218 125.675 149.569 119.689 146.273 114.658C142.976 109.539 138.292 105.636 132.219 102.946C126.234 100.257 119.077 98.9125 110.749 98.9125H69.1087ZM136.456 165.836L111.867 170.09L125.453 190H153.039L136.456 165.836ZM94.8735 145.888H109.187C114.566 145.888 118.556 144.716 121.159 142.374C123.848 140.032 125.193 136.779 125.193 132.615C125.193 128.364 123.848 125.068 121.159 122.725C118.556 120.383 114.566 119.212 109.187 119.212H94.8735V145.888Z"
|
||||
fill="#3F3F46" />
|
||||
<path
|
||||
d="M69.6 288V232H95.2C100.32 232 104.72 232.827 108.4 234.48C112.133 236.133 115.013 238.533 117.04 241.68C119.067 244.773 120.08 248.453 120.08 252.72C120.08 256.933 119.067 260.587 117.04 263.68C115.013 266.72 112.133 269.067 108.4 270.72C104.72 272.32 100.32 273.12 95.2 273.12H78.4L85.44 266.48V288H69.6ZM104.24 288L90.32 267.6H107.2L121.2 288H104.24ZM85.44 268.16L78.4 260.88H94.24C97.5467 260.88 100 260.16 101.6 258.72C103.253 257.28 104.08 255.28 104.08 252.72C104.08 250.107 103.253 248.08 101.6 246.64C100 245.2 97.5467 244.48 94.24 244.48H78.4L85.44 237.2V268.16ZM150.819 288.72C145.699 288.72 141.219 287.76 137.379 285.84C133.592 283.867 130.632 281.2 128.499 277.84C126.419 274.427 125.379 270.56 125.379 266.24C125.379 261.92 126.392 258.08 128.419 254.72C130.499 251.307 133.352 248.667 136.979 246.8C140.605 244.88 144.685 243.92 149.219 243.92C153.485 243.92 157.379 244.8 160.899 246.56C164.419 248.267 167.219 250.8 169.299 254.16C171.379 257.52 172.419 261.6 172.419 266.4C172.419 266.933 172.392 267.547 172.339 268.24C172.285 268.933 172.232 269.573 172.179 270.16H137.859V262.16H164.099L158.339 264.4C158.392 262.427 158.019 260.72 157.219 259.28C156.472 257.84 155.405 256.72 154.019 255.92C152.685 255.12 151.112 254.72 149.299 254.72C147.485 254.72 145.885 255.12 144.499 255.92C143.165 256.72 142.125 257.867 141.379 259.36C140.632 260.8 140.259 262.507 140.259 264.48V266.8C140.259 268.933 140.685 270.773 141.539 272.32C142.445 273.867 143.725 275.067 145.379 275.92C147.032 276.72 149.005 277.12 151.299 277.12C153.432 277.12 155.245 276.827 156.739 276.24C158.285 275.6 159.805 274.64 161.299 273.36L169.299 281.68C167.219 283.973 164.659 285.733 161.619 286.96C158.579 288.133 154.979 288.72 150.819 288.72ZM195.187 288.72C191.507 288.72 187.907 288.293 184.387 287.44C180.92 286.587 178.12 285.52 175.987 284.24L180.547 273.84C182.574 275.067 184.947 276.053 187.667 276.8C190.387 277.493 193.054 277.84 195.667 277.84C198.227 277.84 199.987 277.573 200.947 277.04C201.96 276.507 202.467 275.787 202.467 274.88C202.467 274.027 201.987 273.413 201.027 273.04C200.12 272.613 198.894 272.293 197.347 272.08C195.854 271.867 194.2 271.627 192.387 271.36C190.574 271.093 188.734 270.747 186.867 270.32C185.054 269.84 183.374 269.147 181.827 268.24C180.334 267.28 179.134 266 178.227 264.4C177.32 262.8 176.867 260.773 176.867 258.32C176.867 255.547 177.667 253.093 179.267 250.96C180.92 248.773 183.32 247.067 186.467 245.84C189.614 244.56 193.454 243.92 197.987 243.92C201.027 243.92 204.094 244.24 207.187 244.88C210.334 245.467 212.974 246.373 215.107 247.6L210.547 257.92C208.414 256.693 206.28 255.867 204.147 255.44C202.014 254.96 199.987 254.72 198.067 254.72C195.507 254.72 193.694 255.013 192.627 255.6C191.614 256.187 191.107 256.907 191.107 257.76C191.107 258.613 191.56 259.28 192.467 259.76C193.374 260.187 194.574 260.533 196.067 260.8C197.614 261.013 199.294 261.253 201.107 261.52C202.92 261.733 204.734 262.08 206.547 262.56C208.414 263.04 210.094 263.76 211.587 264.72C213.134 265.627 214.36 266.88 215.267 268.48C216.174 270.027 216.627 272.027 216.627 274.48C216.627 277.147 215.8 279.547 214.147 281.68C212.547 283.813 210.147 285.52 206.947 286.8C203.8 288.08 199.88 288.72 195.187 288.72ZM241.417 288.72C237.897 288.72 234.724 288.027 231.897 286.64C229.124 285.253 226.937 283.093 225.337 280.16C223.79 277.173 223.017 273.413 223.017 268.88V244.64H238.217V266.4C238.217 269.76 238.83 272.16 240.057 273.6C241.337 275.04 243.124 275.76 245.417 275.76C246.91 275.76 248.27 275.413 249.497 274.72C250.724 274.027 251.71 272.933 252.457 271.44C253.204 269.893 253.577 267.92 253.577 265.52V244.64H268.777V288H254.297V275.76L257.097 279.2C255.604 282.4 253.444 284.8 250.617 286.4C247.79 287.947 244.724 288.72 241.417 288.72ZM335.048 243.92C338.408 243.92 341.394 244.613 344.008 246C346.674 247.333 348.754 249.413 350.248 252.24C351.794 255.067 352.568 258.72 352.568 263.2V288H337.368V265.68C337.368 262.587 336.781 260.347 335.608 258.96C334.434 257.52 332.834 256.8 330.808 256.8C329.368 256.8 328.061 257.147 326.888 257.84C325.714 258.533 324.808 259.6 324.168 261.04C323.528 262.427 323.208 264.24 323.208 266.48V288H308.008V265.68C308.008 262.587 307.421 260.347 306.248 258.96C305.128 257.52 303.528 256.8 301.448 256.8C299.954 256.8 298.621 257.147 297.448 257.84C296.328 258.533 295.421 259.6 294.728 261.04C294.088 262.427 293.768 264.24 293.768 266.48V288H278.568V244.64H293.048V256.72L290.168 253.28C291.768 250.187 293.954 247.867 296.728 246.32C299.501 244.72 302.594 243.92 306.008 243.92C309.901 243.92 313.314 244.933 316.248 246.96C319.234 248.933 321.234 252.027 322.248 256.24L317.288 255.2C318.834 251.68 321.154 248.933 324.248 246.96C327.394 244.933 330.994 243.92 335.048 243.92ZM385.428 288.72C380.308 288.72 375.828 287.76 371.988 285.84C368.201 283.867 365.241 281.2 363.108 277.84C361.028 274.427 359.988 270.56 359.988 266.24C359.988 261.92 361.001 258.08 363.028 254.72C365.108 251.307 367.961 248.667 371.588 246Line truncated
|
||||
fill="#3F3F46" />
|
||||
<line opacity="0.1" x1="60" y1="358" x2="360" y2="358" stroke="#3F3F46" stroke-width="20"
|
||||
stroke-miterlimit="16" />
|
||||
<line opacity="0.2" x1="60" y1="398" x2="300" y2="398" stroke="#3F3F46" stroke-width="20"
|
||||
stroke-miterlimit="16" />
|
||||
<line opacity="0.1" x1="60" y1="438" x2="240" y2="438" stroke="#3F3F46" stroke-width="20"
|
||||
stroke-miterlimit="16" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.6 KiB |
@@ -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"
|
||||
|
||||
@@ -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 @@
|
||||
@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>
|
||||
|
||||