Merge branch 'coollabsio:main' into vaultwarden-fix
This commit is contained in:
		@@ -3,7 +3,7 @@ tasks:
 | 
				
			|||||||
    # Fix because of https://github.com/gitpod-io/gitpod/issues/16614
 | 
					    # Fix because of https://github.com/gitpod-io/gitpod/issues/16614
 | 
				
			||||||
    before: sudo curl -o /usr/local/bin/docker-compose -fsSL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-$(uname -m)
 | 
					    before: sudo curl -o /usr/local/bin/docker-compose -fsSL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-$(uname -m)
 | 
				
			||||||
    init: |
 | 
					    init: |
 | 
				
			||||||
      cp .env.example .env &&
 | 
					      cp .env.development.example .env &&
 | 
				
			||||||
      sed -i "s#APP_URL=http://localhost#APP_URL=$(gp url 8000)#g" .env
 | 
					      sed -i "s#APP_URL=http://localhost#APP_URL=$(gp url 8000)#g" .env
 | 
				
			||||||
      sed -i "s#USERID=#USERID=33333#g" .env
 | 
					      sed -i "s#USERID=#USERID=33333#g" .env
 | 
				
			||||||
      sed -i "s#GROUPID=#GROUPID=33333#g" .env
 | 
					      sed -i "s#GROUPID=#GROUPID=33333#g" .env
 | 
				
			||||||
@@ -20,7 +20,7 @@ tasks:
 | 
				
			|||||||
      echo "Waiting for Sail environment to boot up."
 | 
					      echo "Waiting for Sail environment to boot up."
 | 
				
			||||||
      gp sync-await spin-is-ready
 | 
					      gp sync-await spin-is-ready
 | 
				
			||||||
      ./vendor/bin/spin exec vite npm install
 | 
					      ./vendor/bin/spin exec vite npm install
 | 
				
			||||||
      ./vendor/bin/spin exec vite npm run dev
 | 
					      ./vendor/bin/spin exec vite npm run dev -- --host
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name: Laravel Queue Worker, listening to code changes
 | 
					  - name: Laravel Queue Worker, listening to code changes
 | 
				
			||||||
    command: |
 | 
					    command: |
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,12 @@ Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Github Sponsors ($40+)
 | 
					## Github Sponsors ($40+)
 | 
				
			||||||
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
 | 
					<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
 | 
				
			||||||
 | 
					<a href="https://serpapi.com/?utm_source=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>
 | 
				
			||||||
<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
 | 
					<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
 | 
				
			||||||
 | 
					<a href="https://www.quantcdn.io/?utm_source=coolify.io"><img src="https://github.com/quantcdn.png" width="60px" alt="QuantCDN"/></a> 
 | 
				
			||||||
 | 
					<a href="https://www.runpod.io/?utm_source=coolify.io">
 | 
				
			||||||
 | 
					<svg style="width:60px;height:60px;background:#fff;" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 200 200"><g><path d="M74.5 51.1c-25.4 14.9-27 16-29.6 20.2-1.8 3-1.9 5.3-1.9 32.3 0 21.7.3 29.4 1.3 30.6 1.9 2.5 46.7 27.9 48.5 27.6 1.5-.3 1.7-3.1 2-27.7.2-21.9 0-27.8-1.1-29.5-.8-1.2-9.9-6.8-20.2-12.6-10.3-5.8-19.4-11.5-20.2-12.7-1.8-2.6-.9-5.9 1.8-7.4 1.6-.8 6.3 0 21.8 4C87.8 78.7 98 81 99.6 81c4.4 0 49.9-25.9 49.9-28.4 0-1.6-3.4-2.8-24-8.2-13.2-3.5-25.1-6.3-26.5-6.3-1.4.1-12.4 5.9-24.5 13z"></path><path d="m137.2 68.1-3.3 2.1 6.3 3.7c3.5 2 6.3 4.3 6.3 5.1 0 .9-8 6.1-19.4 12.6-10.6 6-20 11.9-20.7 12.9-1.2 1.6-1.4 7.2-1.2 29.4.3 24.8.5 27.6 2 27.9 1.8.3 46.6-25.1 48.6-27.6.9-1.2 1.2-8.8 1.2-30.2s-.3-29-1.2-30.2c-1.6-1.9-12.1-7.8-13.9-7.8-.8 0-2.9 1-4.7 2.1z"></path></g></svg></a>
 | 
				
			||||||
 | 
					<a href="https://www.flint.sh/en/home?utm_source=coolify.io"> <img src="https://github.com/Flint-company.png" width="60px" alt="FlintCompany"/></a>
 | 
				
			||||||
<a href="https://americancloud.com/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
 | 
					<a href="https://americancloud.com/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
 | 
				
			||||||
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
 | 
					<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
 | 
				
			||||||
<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
 | 
					<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
 | 
				
			||||||
@@ -62,6 +67,7 @@ Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
 | 
				
			|||||||
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
 | 
					<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
 | 
				
			||||||
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
 | 
					<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Individuals
 | 
					## Individuals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
 | 
					<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,6 @@ class StartClickhouse
 | 
				
			|||||||
        $environment_variables = $this->generate_environment_variables();
 | 
					        $environment_variables = $this->generate_environment_variables();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $docker_compose = [
 | 
					        $docker_compose = [
 | 
				
			||||||
            'version' => '3.8',
 | 
					 | 
				
			||||||
            'services' => [
 | 
					            'services' => [
 | 
				
			||||||
                $container_name => [
 | 
					                $container_name => [
 | 
				
			||||||
                    'image' => $this->database->image,
 | 
					                    'image' => $this->database->image,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -107,7 +107,6 @@ class StartDatabaseProxy
 | 
				
			|||||||
    COPY nginx.conf /etc/nginx/nginx.conf
 | 
					    COPY nginx.conf /etc/nginx/nginx.conf
 | 
				
			||||||
    EOF;
 | 
					    EOF;
 | 
				
			||||||
        $docker_compose = [
 | 
					        $docker_compose = [
 | 
				
			||||||
            'version' => '3.8',
 | 
					 | 
				
			||||||
            'services' => [
 | 
					            'services' => [
 | 
				
			||||||
                $proxyContainerName => [
 | 
					                $proxyContainerName => [
 | 
				
			||||||
                    'build' => [
 | 
					                    'build' => [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,6 @@ class StartDragonfly
 | 
				
			|||||||
        $environment_variables = $this->generate_environment_variables();
 | 
					        $environment_variables = $this->generate_environment_variables();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $docker_compose = [
 | 
					        $docker_compose = [
 | 
				
			||||||
            'version' => '3.8',
 | 
					 | 
				
			||||||
            'services' => [
 | 
					            'services' => [
 | 
				
			||||||
                $container_name => [
 | 
					                $container_name => [
 | 
				
			||||||
                    'image' => $this->database->image,
 | 
					                    'image' => $this->database->image,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,7 +37,6 @@ class StartKeydb
 | 
				
			|||||||
        $this->add_custom_keydb();
 | 
					        $this->add_custom_keydb();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $docker_compose = [
 | 
					        $docker_compose = [
 | 
				
			||||||
            'version' => '3.8',
 | 
					 | 
				
			||||||
            'services' => [
 | 
					            'services' => [
 | 
				
			||||||
                $container_name => [
 | 
					                $container_name => [
 | 
				
			||||||
                    'image' => $this->database->image,
 | 
					                    'image' => $this->database->image,
 | 
				
			||||||
@@ -96,7 +95,7 @@ class StartKeydb
 | 
				
			|||||||
        if (count($volume_names) > 0) {
 | 
					        if (count($volume_names) > 0) {
 | 
				
			||||||
            $docker_compose['volumes'] = $volume_names;
 | 
					            $docker_compose['volumes'] = $volume_names;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!is_null($this->database->keydb_conf)) {
 | 
					        if (!is_null($this->database->keydb_conf) || !empty($this->database->keydb_conf)) {
 | 
				
			||||||
            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
					            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
				
			||||||
                'type' => 'bind',
 | 
					                'type' => 'bind',
 | 
				
			||||||
                'source' => $this->configuration_dir . '/keydb.conf',
 | 
					                'source' => $this->configuration_dir . '/keydb.conf',
 | 
				
			||||||
@@ -162,7 +161,7 @@ class StartKeydb
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    private function add_custom_keydb()
 | 
					    private function add_custom_keydb()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (is_null($this->database->keydb_conf)) {
 | 
					        if (is_null($this->database->keydb_conf) || empty($this->database->keydb_conf)) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $filename = 'keydb.conf';
 | 
					        $filename = 'keydb.conf';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,6 @@ class StartMariadb
 | 
				
			|||||||
        $environment_variables = $this->generate_environment_variables();
 | 
					        $environment_variables = $this->generate_environment_variables();
 | 
				
			||||||
        $this->add_custom_mysql();
 | 
					        $this->add_custom_mysql();
 | 
				
			||||||
        $docker_compose = [
 | 
					        $docker_compose = [
 | 
				
			||||||
            'version' => '3.8',
 | 
					 | 
				
			||||||
            'services' => [
 | 
					            'services' => [
 | 
				
			||||||
                $container_name => [
 | 
					                $container_name => [
 | 
				
			||||||
                    'image' => $this->database->image,
 | 
					                    'image' => $this->database->image,
 | 
				
			||||||
@@ -90,7 +89,7 @@ class StartMariadb
 | 
				
			|||||||
        if (count($volume_names) > 0) {
 | 
					        if (count($volume_names) > 0) {
 | 
				
			||||||
            $docker_compose['volumes'] = $volume_names;
 | 
					            $docker_compose['volumes'] = $volume_names;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!is_null($this->database->mariadb_conf)) {
 | 
					        if (!is_null($this->database->mariadb_conf) || !empty($this->database->mariadb_conf)) {
 | 
				
			||||||
            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
					            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
				
			||||||
                'type' => 'bind',
 | 
					                'type' => 'bind',
 | 
				
			||||||
                'source' => $this->configuration_dir . '/custom-config.cnf',
 | 
					                'source' => $this->configuration_dir . '/custom-config.cnf',
 | 
				
			||||||
@@ -165,7 +164,7 @@ class StartMariadb
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    private function add_custom_mysql()
 | 
					    private function add_custom_mysql()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (is_null($this->database->mariadb_conf)) {
 | 
					        if (is_null($this->database->mariadb_conf) || empty($this->database->mariadb_conf)) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $filename = 'custom-config.cnf';
 | 
					        $filename = 'custom-config.cnf';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,6 @@ class StartMongodb
 | 
				
			|||||||
        $this->add_custom_mongo_conf();
 | 
					        $this->add_custom_mongo_conf();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $docker_compose = [
 | 
					        $docker_compose = [
 | 
				
			||||||
            'version' => '3.8',
 | 
					 | 
				
			||||||
            'services' => [
 | 
					            'services' => [
 | 
				
			||||||
                $container_name => [
 | 
					                $container_name => [
 | 
				
			||||||
                    'image' => $this->database->image,
 | 
					                    'image' => $this->database->image,
 | 
				
			||||||
@@ -51,8 +50,9 @@ class StartMongodb
 | 
				
			|||||||
                    ],
 | 
					                    ],
 | 
				
			||||||
                    'healthcheck' => [
 | 
					                    'healthcheck' => [
 | 
				
			||||||
                        'test' => [
 | 
					                        'test' => [
 | 
				
			||||||
                            'CMD-SHELL',
 | 
					                            "CMD",
 | 
				
			||||||
                            'mongosh --eval "printjson(db.runCommand(\"ping\"))"'
 | 
					                            "echo",
 | 
				
			||||||
 | 
					                            "ok"
 | 
				
			||||||
                        ],
 | 
					                        ],
 | 
				
			||||||
                        'interval' => '5s',
 | 
					                        'interval' => '5s',
 | 
				
			||||||
                        'timeout' => '5s',
 | 
					                        'timeout' => '5s',
 | 
				
			||||||
@@ -97,7 +97,7 @@ class StartMongodb
 | 
				
			|||||||
        if (count($volume_names) > 0) {
 | 
					        if (count($volume_names) > 0) {
 | 
				
			||||||
            $docker_compose['volumes'] = $volume_names;
 | 
					            $docker_compose['volumes'] = $volume_names;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!is_null($this->database->mongo_conf)) {
 | 
					        if (!is_null($this->database->mongo_conf) || !empty($this->database->mongo_conf)) {
 | 
				
			||||||
            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
					            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
				
			||||||
                'type' => 'bind',
 | 
					                'type' => 'bind',
 | 
				
			||||||
                'source' => $this->configuration_dir . '/mongod.conf',
 | 
					                'source' => $this->configuration_dir . '/mongod.conf',
 | 
				
			||||||
@@ -178,7 +178,7 @@ class StartMongodb
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    private function add_custom_mongo_conf()
 | 
					    private function add_custom_mongo_conf()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (is_null($this->database->mongo_conf)) {
 | 
					        if (is_null($this->database->mongo_conf) || empty($this->database->mongo_conf)) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $filename = 'mongod.conf';
 | 
					        $filename = 'mongod.conf';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,6 @@ class StartMysql
 | 
				
			|||||||
        $environment_variables = $this->generate_environment_variables();
 | 
					        $environment_variables = $this->generate_environment_variables();
 | 
				
			||||||
        $this->add_custom_mysql();
 | 
					        $this->add_custom_mysql();
 | 
				
			||||||
        $docker_compose = [
 | 
					        $docker_compose = [
 | 
				
			||||||
            'version' => '3.8',
 | 
					 | 
				
			||||||
            'services' => [
 | 
					            'services' => [
 | 
				
			||||||
                $container_name => [
 | 
					                $container_name => [
 | 
				
			||||||
                    'image' => $this->database->image,
 | 
					                    'image' => $this->database->image,
 | 
				
			||||||
@@ -90,7 +89,7 @@ class StartMysql
 | 
				
			|||||||
        if (count($volume_names) > 0) {
 | 
					        if (count($volume_names) > 0) {
 | 
				
			||||||
            $docker_compose['volumes'] = $volume_names;
 | 
					            $docker_compose['volumes'] = $volume_names;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!is_null($this->database->mysql_conf)) {
 | 
					        if (!is_null($this->database->mysql_conf) || !empty($this->database->mysql_conf)) {
 | 
				
			||||||
            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
					            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
				
			||||||
                'type' => 'bind',
 | 
					                'type' => 'bind',
 | 
				
			||||||
                'source' => $this->configuration_dir . '/custom-config.cnf',
 | 
					                'source' => $this->configuration_dir . '/custom-config.cnf',
 | 
				
			||||||
@@ -165,7 +164,7 @@ class StartMysql
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    private function add_custom_mysql()
 | 
					    private function add_custom_mysql()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (is_null($this->database->mysql_conf)) {
 | 
					        if (is_null($this->database->mysql_conf) || empty($this->database->mysql_conf)) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $filename = 'custom-config.cnf';
 | 
					        $filename = 'custom-config.cnf';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,6 @@ class StartPostgresql
 | 
				
			|||||||
        $this->add_custom_conf();
 | 
					        $this->add_custom_conf();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $docker_compose = [
 | 
					        $docker_compose = [
 | 
				
			||||||
            'version' => '3.8',
 | 
					 | 
				
			||||||
            'services' => [
 | 
					            'services' => [
 | 
				
			||||||
                $container_name => [
 | 
					                $container_name => [
 | 
				
			||||||
                    'image' => $this->database->image,
 | 
					                    'image' => $this->database->image,
 | 
				
			||||||
@@ -78,7 +77,6 @@ class StartPostgresql
 | 
				
			|||||||
            data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
 | 
					            data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
 | 
					        if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
 | 
				
			||||||
            ray('Log Drain Enabled');
 | 
					 | 
				
			||||||
            $docker_compose['services'][$container_name]['logging'] = [
 | 
					            $docker_compose['services'][$container_name]['logging'] = [
 | 
				
			||||||
                'driver' => 'fluentd',
 | 
					                'driver' => 'fluentd',
 | 
				
			||||||
                'options' => [
 | 
					                'options' => [
 | 
				
			||||||
@@ -107,7 +105,7 @@ class StartPostgresql
 | 
				
			|||||||
                ];
 | 
					                ];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!is_null($this->database->postgres_conf)) {
 | 
					        if (!is_null($this->database->postgres_conf) && !empty($this->database->postgres_conf)) {
 | 
				
			||||||
            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
					            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
				
			||||||
                'type' => 'bind',
 | 
					                'type' => 'bind',
 | 
				
			||||||
                'source' => $this->configuration_dir . '/custom-postgres.conf',
 | 
					                'source' => $this->configuration_dir . '/custom-postgres.conf',
 | 
				
			||||||
@@ -165,8 +163,6 @@ class StartPostgresql
 | 
				
			|||||||
    private function generate_environment_variables()
 | 
					    private function generate_environment_variables()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $environment_variables = collect();
 | 
					        $environment_variables = collect();
 | 
				
			||||||
        ray('Generate Environment Variables')->green();
 | 
					 | 
				
			||||||
        ray($this->database->runtime_environment_variables)->green();
 | 
					 | 
				
			||||||
        foreach ($this->database->runtime_environment_variables as $env) {
 | 
					        foreach ($this->database->runtime_environment_variables as $env) {
 | 
				
			||||||
            $environment_variables->push("$env->key=$env->real_value");
 | 
					            $environment_variables->push("$env->key=$env->real_value");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -203,11 +199,16 @@ class StartPostgresql
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    private function add_custom_conf()
 | 
					    private function add_custom_conf()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (is_null($this->database->postgres_conf)) {
 | 
					        if (is_null($this->database->postgres_conf) || empty($this->database->postgres_conf)) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $filename = 'custom-postgres.conf';
 | 
					        $filename = 'custom-postgres.conf';
 | 
				
			||||||
        $content = $this->database->postgres_conf;
 | 
					        $content = $this->database->postgres_conf;
 | 
				
			||||||
 | 
					        if (!str($content)->contains('listen_addresses')) {
 | 
				
			||||||
 | 
					            $content .= "\nlisten_addresses = '*'";
 | 
				
			||||||
 | 
					            $this->database->postgres_conf = $content;
 | 
				
			||||||
 | 
					            $this->database->save();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $content_base64 = base64_encode($content);
 | 
					        $content_base64 = base64_encode($content);
 | 
				
			||||||
        $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
 | 
					        $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,7 +37,6 @@ class StartRedis
 | 
				
			|||||||
        $this->add_custom_redis();
 | 
					        $this->add_custom_redis();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $docker_compose = [
 | 
					        $docker_compose = [
 | 
				
			||||||
            'version' => '3.8',
 | 
					 | 
				
			||||||
            'services' => [
 | 
					            'services' => [
 | 
				
			||||||
                $container_name => [
 | 
					                $container_name => [
 | 
				
			||||||
                    'image' => $this->database->image,
 | 
					                    'image' => $this->database->image,
 | 
				
			||||||
@@ -100,7 +99,7 @@ class StartRedis
 | 
				
			|||||||
        if (count($volume_names) > 0) {
 | 
					        if (count($volume_names) > 0) {
 | 
				
			||||||
            $docker_compose['volumes'] = $volume_names;
 | 
					            $docker_compose['volumes'] = $volume_names;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!is_null($this->database->redis_conf)) {
 | 
					        if (!is_null($this->database->redis_conf) || !empty($this->database->redis_conf)) {
 | 
				
			||||||
            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
					            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
				
			||||||
                'type' => 'bind',
 | 
					                'type' => 'bind',
 | 
				
			||||||
                'source' => $this->configuration_dir . '/redis.conf',
 | 
					                'source' => $this->configuration_dir . '/redis.conf',
 | 
				
			||||||
@@ -166,7 +165,7 @@ class StartRedis
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    private function add_custom_redis()
 | 
					    private function add_custom_redis()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (is_null($this->database->redis_conf)) {
 | 
					        if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $filename = 'redis.conf';
 | 
					        $filename = 'redis.conf';
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										657
									
								
								app/Actions/Docker/GetContainersStatus.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										657
									
								
								app/Actions/Docker/GetContainersStatus.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,657 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Actions\Docker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Actions\Database\StartDatabaseProxy;
 | 
				
			||||||
 | 
					use App\Actions\Proxy\CheckProxy;
 | 
				
			||||||
 | 
					use App\Actions\Proxy\StartProxy;
 | 
				
			||||||
 | 
					use App\Actions\Shared\ComplexStatusCheck;
 | 
				
			||||||
 | 
					use App\Models\ApplicationPreview;
 | 
				
			||||||
 | 
					use App\Models\Server;
 | 
				
			||||||
 | 
					use App\Models\ServiceDatabase;
 | 
				
			||||||
 | 
					use App\Notifications\Container\ContainerRestarted;
 | 
				
			||||||
 | 
					use App\Notifications\Container\ContainerStopped;
 | 
				
			||||||
 | 
					use Illuminate\Support\Arr;
 | 
				
			||||||
 | 
					use Lorisleiva\Actions\Concerns\AsAction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GetContainersStatus
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use AsAction;
 | 
				
			||||||
 | 
					    public $applications;
 | 
				
			||||||
 | 
					    public $server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function handle(Server $server)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // if (isDev()) {
 | 
				
			||||||
 | 
					        //     $server = Server::find(0);
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					        $this->server = $server;
 | 
				
			||||||
 | 
					        if (!$this->server->isFunctional()) {
 | 
				
			||||||
 | 
					            return 'Server is not ready.';
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        $this->applications = $this->server->applications();
 | 
				
			||||||
 | 
					        $skip_these_applications = collect([]);
 | 
				
			||||||
 | 
					        foreach ($this->applications as $application) {
 | 
				
			||||||
 | 
					            if ($application->additional_servers->count() > 0) {
 | 
				
			||||||
 | 
					                $skip_these_applications->push($application);
 | 
				
			||||||
 | 
					                ComplexStatusCheck::run($application);
 | 
				
			||||||
 | 
					                $this->applications = $this->applications->filter(function ($value, $key) use ($application) {
 | 
				
			||||||
 | 
					                    return $value->id !== $application->id;
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $this->applications = $this->applications->filter(function ($value, $key) use ($skip_these_applications) {
 | 
				
			||||||
 | 
					            return !$skip_these_applications->pluck('id')->contains($value->id);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        $this->old_way();
 | 
				
			||||||
 | 
					        // if ($this->server->isSwarm()) {
 | 
				
			||||||
 | 
					        //     $this->old_way();
 | 
				
			||||||
 | 
					        // } else {
 | 
				
			||||||
 | 
					        //     if (!$this->server->is_metrics_enabled) {
 | 
				
			||||||
 | 
					        //         $this->old_way();
 | 
				
			||||||
 | 
					        //         return;
 | 
				
			||||||
 | 
					        //     }
 | 
				
			||||||
 | 
					        //     $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this->server, false);
 | 
				
			||||||
 | 
					        //     $sentinel_found = json_decode($sentinel_found, true);
 | 
				
			||||||
 | 
					        //     $status = data_get($sentinel_found, '0.State.Status', 'exited');
 | 
				
			||||||
 | 
					        //     if ($status === 'running') {
 | 
				
			||||||
 | 
					        //         ray('Checking with Sentinel');
 | 
				
			||||||
 | 
					        //         $this->sentinel();
 | 
				
			||||||
 | 
					        //     } else {
 | 
				
			||||||
 | 
					        //         ray('Checking the Old way');
 | 
				
			||||||
 | 
					        //         $this->old_way();
 | 
				
			||||||
 | 
					        //     }
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function sentinel()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $containers = $this->server->getContainers();
 | 
				
			||||||
 | 
					            if ($containers->count() === 0) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $databases = $this->server->databases();
 | 
				
			||||||
 | 
					            $services = $this->server->services()->get();
 | 
				
			||||||
 | 
					            $previews = $this->server->previews();
 | 
				
			||||||
 | 
					            $foundApplications = [];
 | 
				
			||||||
 | 
					            $foundApplicationPreviews = [];
 | 
				
			||||||
 | 
					            $foundDatabases = [];
 | 
				
			||||||
 | 
					            $foundServices = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach ($containers as $container) {
 | 
				
			||||||
 | 
					                $labels = Arr::undot(data_get($container, 'labels'));
 | 
				
			||||||
 | 
					                $containerStatus = data_get($container, 'state');
 | 
				
			||||||
 | 
					                $containerHealth = data_get($container, 'health_status', 'unhealthy');
 | 
				
			||||||
 | 
					                $containerStatus = "$containerStatus ($containerHealth)";
 | 
				
			||||||
 | 
					                $applicationId = data_get($labels, 'coolify.applicationId');
 | 
				
			||||||
 | 
					                if ($applicationId) {
 | 
				
			||||||
 | 
					                    $pullRequestId = data_get($labels, 'coolify.pullRequestId');
 | 
				
			||||||
 | 
					                    if ($pullRequestId) {
 | 
				
			||||||
 | 
					                        if (str($applicationId)->contains('-')) {
 | 
				
			||||||
 | 
					                            $applicationId = str($applicationId)->before('-');
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
 | 
				
			||||||
 | 
					                        if ($preview) {
 | 
				
			||||||
 | 
					                            $foundApplicationPreviews[] = $preview->id;
 | 
				
			||||||
 | 
					                            $statusFromDb = $preview->status;
 | 
				
			||||||
 | 
					                            if ($statusFromDb !== $containerStatus) {
 | 
				
			||||||
 | 
					                                $preview->update(['status' => $containerStatus]);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            //Notify user that this container should not be there.
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        $application = $this->applications->where('id', $applicationId)->first();
 | 
				
			||||||
 | 
					                        if ($application) {
 | 
				
			||||||
 | 
					                            $foundApplications[] = $application->id;
 | 
				
			||||||
 | 
					                            $statusFromDb = $application->status;
 | 
				
			||||||
 | 
					                            if ($statusFromDb !== $containerStatus) {
 | 
				
			||||||
 | 
					                                $application->update(['status' => $containerStatus]);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            //Notify user that this container should not be there.
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $uuid = data_get($labels, 'com.docker.compose.service');
 | 
				
			||||||
 | 
					                    $type = data_get($labels, 'coolify.type');
 | 
				
			||||||
 | 
					                    if ($uuid) {
 | 
				
			||||||
 | 
					                        if ($type === 'service') {
 | 
				
			||||||
 | 
					                            $database_id = data_get($labels, 'coolify.service.subId');
 | 
				
			||||||
 | 
					                            if ($database_id) {
 | 
				
			||||||
 | 
					                                $service_db = ServiceDatabase::where('id', $database_id)->first();
 | 
				
			||||||
 | 
					                                if ($service_db) {
 | 
				
			||||||
 | 
					                                    $uuid = $service_db->service->uuid;
 | 
				
			||||||
 | 
					                                    $isPublic = data_get($service_db, 'is_public');
 | 
				
			||||||
 | 
					                                    if ($isPublic) {
 | 
				
			||||||
 | 
					                                        $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
 | 
				
			||||||
 | 
					                                            if ($this->server->isSwarm()) {
 | 
				
			||||||
 | 
					                                                // TODO: fix this with sentinel
 | 
				
			||||||
 | 
					                                                return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
 | 
				
			||||||
 | 
					                                            } else {
 | 
				
			||||||
 | 
					                                                return data_get($value, 'name') === "$uuid-proxy";
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                        })->first();
 | 
				
			||||||
 | 
					                                        if (!$foundTcpProxy) {
 | 
				
			||||||
 | 
					                                            StartDatabaseProxy::run($service_db);
 | 
				
			||||||
 | 
					                                            // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            $database = $databases->where('uuid', $uuid)->first();
 | 
				
			||||||
 | 
					                            if ($database) {
 | 
				
			||||||
 | 
					                                $isPublic = data_get($database, 'is_public');
 | 
				
			||||||
 | 
					                                $foundDatabases[] = $database->id;
 | 
				
			||||||
 | 
					                                $statusFromDb = $database->status;
 | 
				
			||||||
 | 
					                                if ($statusFromDb !== $containerStatus) {
 | 
				
			||||||
 | 
					                                    $database->update(['status' => $containerStatus]);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                if ($isPublic) {
 | 
				
			||||||
 | 
					                                    $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
 | 
				
			||||||
 | 
					                                        if ($this->server->isSwarm()) {
 | 
				
			||||||
 | 
					                                            // TODO: fix this with sentinel
 | 
				
			||||||
 | 
					                                            return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
 | 
				
			||||||
 | 
					                                        } else {
 | 
				
			||||||
 | 
					                                            return data_get($value, 'name') === "$uuid-proxy";
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    })->first();
 | 
				
			||||||
 | 
					                                    if (!$foundTcpProxy) {
 | 
				
			||||||
 | 
					                                        StartDatabaseProxy::run($database);
 | 
				
			||||||
 | 
					                                        $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                // Notify user that this container should not be there.
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if (data_get($container, 'name') === 'coolify-db') {
 | 
				
			||||||
 | 
					                        $foundDatabases[] = 0;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $serviceLabelId = data_get($labels, 'coolify.serviceId');
 | 
				
			||||||
 | 
					                if ($serviceLabelId) {
 | 
				
			||||||
 | 
					                    $subType = data_get($labels, 'coolify.service.subType');
 | 
				
			||||||
 | 
					                    $subId = data_get($labels, 'coolify.service.subId');
 | 
				
			||||||
 | 
					                    $service = $services->where('id', $serviceLabelId)->first();
 | 
				
			||||||
 | 
					                    if (!$service) {
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if ($subType === 'application') {
 | 
				
			||||||
 | 
					                        $service =  $service->applications()->where('id', $subId)->first();
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        $service =  $service->databases()->where('id', $subId)->first();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if ($service) {
 | 
				
			||||||
 | 
					                        $foundServices[] = "$service->id-$service->name";
 | 
				
			||||||
 | 
					                        $statusFromDb = $service->status;
 | 
				
			||||||
 | 
					                        if ($statusFromDb !== $containerStatus) {
 | 
				
			||||||
 | 
					                            // ray('Updating status: ' . $containerStatus);
 | 
				
			||||||
 | 
					                            $service->update(['status' => $containerStatus]);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $exitedServices = collect([]);
 | 
				
			||||||
 | 
					            foreach ($services as $service) {
 | 
				
			||||||
 | 
					                $apps = $service->applications()->get();
 | 
				
			||||||
 | 
					                $dbs = $service->databases()->get();
 | 
				
			||||||
 | 
					                foreach ($apps as $app) {
 | 
				
			||||||
 | 
					                    if (in_array("$app->id-$app->name", $foundServices)) {
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        $exitedServices->push($app);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                foreach ($dbs as $db) {
 | 
				
			||||||
 | 
					                    if (in_array("$db->id-$db->name", $foundServices)) {
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        $exitedServices->push($db);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $exitedServices = $exitedServices->unique('id');
 | 
				
			||||||
 | 
					            foreach ($exitedServices as $exitedService) {
 | 
				
			||||||
 | 
					                if (str($exitedService->status)->startsWith('exited')) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $name = data_get($exitedService, 'name');
 | 
				
			||||||
 | 
					                $fqdn = data_get($exitedService, 'fqdn');
 | 
				
			||||||
 | 
					                $containerName = $name ? "$name, available at $fqdn" : $fqdn;
 | 
				
			||||||
 | 
					                $projectUuid = data_get($service, 'environment.project.uuid');
 | 
				
			||||||
 | 
					                $serviceUuid = data_get($service, 'uuid');
 | 
				
			||||||
 | 
					                $environmentName = data_get($service, 'environment.name');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ($projectUuid && $serviceUuid && $environmentName) {
 | 
				
			||||||
 | 
					                    $url =  base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $url = null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
				
			||||||
 | 
					                $exitedService->update(['status' => 'exited']);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
 | 
				
			||||||
 | 
					            foreach ($notRunningApplications as $applicationId) {
 | 
				
			||||||
 | 
					                $application = $this->applications->where('id', $applicationId)->first();
 | 
				
			||||||
 | 
					                if (str($application->status)->startsWith('exited')) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $application->update(['status' => 'exited']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $name = data_get($application, 'name');
 | 
				
			||||||
 | 
					                $fqdn = data_get($application, 'fqdn');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $containerName = $name ? "$name ($fqdn)" : $fqdn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $projectUuid = data_get($application, 'environment.project.uuid');
 | 
				
			||||||
 | 
					                $applicationUuid = data_get($application, 'uuid');
 | 
				
			||||||
 | 
					                $environment = data_get($application, 'environment.name');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ($projectUuid && $applicationUuid && $environment) {
 | 
				
			||||||
 | 
					                    $url =  base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $url = null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
 | 
				
			||||||
 | 
					            foreach ($notRunningApplicationPreviews as $previewId) {
 | 
				
			||||||
 | 
					                $preview = $previews->where('id', $previewId)->first();
 | 
				
			||||||
 | 
					                if (str($preview->status)->startsWith('exited')) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $preview->update(['status' => 'exited']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $name = data_get($preview, 'name');
 | 
				
			||||||
 | 
					                $fqdn = data_get($preview, 'fqdn');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $containerName = $name ? "$name ($fqdn)" : $fqdn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $projectUuid = data_get($preview, 'application.environment.project.uuid');
 | 
				
			||||||
 | 
					                $environmentName = data_get($preview, 'application.environment.name');
 | 
				
			||||||
 | 
					                $applicationUuid = data_get($preview, 'application.uuid');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ($projectUuid && $applicationUuid && $environmentName) {
 | 
				
			||||||
 | 
					                    $url =  base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $url = null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
 | 
				
			||||||
 | 
					            foreach ($notRunningDatabases as $database) {
 | 
				
			||||||
 | 
					                $database = $databases->where('id', $database)->first();
 | 
				
			||||||
 | 
					                if (str($database->status)->startsWith('exited')) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $database->update(['status' => 'exited']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $name = data_get($database, 'name');
 | 
				
			||||||
 | 
					                $fqdn = data_get($database, 'fqdn');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $containerName = $name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $projectUuid = data_get($database, 'environment.project.uuid');
 | 
				
			||||||
 | 
					                $environmentName = data_get($database, 'environment.name');
 | 
				
			||||||
 | 
					                $databaseUuid = data_get($database, 'uuid');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ($projectUuid && $databaseUuid && $environmentName) {
 | 
				
			||||||
 | 
					                    $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $url = null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Check if proxy is running
 | 
				
			||||||
 | 
					            $this->server->proxyType();
 | 
				
			||||||
 | 
					            $foundProxyContainer = $containers->filter(function ($value, $key) {
 | 
				
			||||||
 | 
					                if ($this->server->isSwarm()) {
 | 
				
			||||||
 | 
					                    // TODO: fix this with sentinel
 | 
				
			||||||
 | 
					                    return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    return data_get($value, 'name') === 'coolify-proxy';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })->first();
 | 
				
			||||||
 | 
					            if (!$foundProxyContainer) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    $shouldStart = CheckProxy::run($this->server);
 | 
				
			||||||
 | 
					                    if ($shouldStart) {
 | 
				
			||||||
 | 
					                        StartProxy::run($this->server, false);
 | 
				
			||||||
 | 
					                        $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					                    ray($e);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->server->proxy->status = data_get($foundProxyContainer, 'state');
 | 
				
			||||||
 | 
					                $this->server->save();
 | 
				
			||||||
 | 
					                $connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
 | 
				
			||||||
 | 
					                instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
 | 
					            // send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
 | 
				
			||||||
 | 
					            ray($e->getMessage());
 | 
				
			||||||
 | 
					            return handleError($e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    private function old_way()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->server->isSwarm()) {
 | 
				
			||||||
 | 
					            $containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
 | 
				
			||||||
 | 
					            $containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Precheck for containers
 | 
				
			||||||
 | 
					            $containers = instant_remote_process(["docker container ls -q"], $this->server, false);
 | 
				
			||||||
 | 
					            if (!$containers) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
 | 
				
			||||||
 | 
					            $containerReplicates = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (is_null($containers)) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $containers = format_docker_command_output_to_json($containers);
 | 
				
			||||||
 | 
					        if ($containerReplicates) {
 | 
				
			||||||
 | 
					            $containerReplicates = format_docker_command_output_to_json($containerReplicates);
 | 
				
			||||||
 | 
					            foreach ($containerReplicates as $containerReplica) {
 | 
				
			||||||
 | 
					                $name = data_get($containerReplica, 'Name');
 | 
				
			||||||
 | 
					                $containers = $containers->map(function ($container) use ($name, $containerReplica) {
 | 
				
			||||||
 | 
					                    if (data_get($container, 'Spec.Name') === $name) {
 | 
				
			||||||
 | 
					                        $replicas = data_get($containerReplica, 'Replicas');
 | 
				
			||||||
 | 
					                        $running = str($replicas)->explode('/')[0];
 | 
				
			||||||
 | 
					                        $total = str($replicas)->explode('/')[1];
 | 
				
			||||||
 | 
					                        if ($running === $total) {
 | 
				
			||||||
 | 
					                            data_set($container, 'State.Status', 'running');
 | 
				
			||||||
 | 
					                            data_set($container, 'State.Health.Status', 'healthy');
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            data_set($container, 'State.Status', 'starting');
 | 
				
			||||||
 | 
					                            data_set($container, 'State.Health.Status', 'unhealthy');
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return $container;
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $databases = $this->server->databases();
 | 
				
			||||||
 | 
					        $services = $this->server->services()->get();
 | 
				
			||||||
 | 
					        $previews = $this->server->previews();
 | 
				
			||||||
 | 
					        $foundApplications = [];
 | 
				
			||||||
 | 
					        $foundApplicationPreviews = [];
 | 
				
			||||||
 | 
					        $foundDatabases = [];
 | 
				
			||||||
 | 
					        $foundServices = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($containers as $container) {
 | 
				
			||||||
 | 
					            if ($this->server->isSwarm()) {
 | 
				
			||||||
 | 
					                $labels = data_get($container, 'Spec.Labels');
 | 
				
			||||||
 | 
					                $uuid = data_get($labels, 'coolify.name');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $labels = data_get($container, 'Config.Labels');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $containerStatus = data_get($container, 'State.Status');
 | 
				
			||||||
 | 
					            $containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
 | 
				
			||||||
 | 
					            $containerStatus = "$containerStatus ($containerHealth)";
 | 
				
			||||||
 | 
					            $labels = Arr::undot(format_docker_labels_to_json($labels));
 | 
				
			||||||
 | 
					            $applicationId = data_get($labels, 'coolify.applicationId');
 | 
				
			||||||
 | 
					            if ($applicationId) {
 | 
				
			||||||
 | 
					                $pullRequestId = data_get($labels, 'coolify.pullRequestId');
 | 
				
			||||||
 | 
					                if ($pullRequestId) {
 | 
				
			||||||
 | 
					                    if (str($applicationId)->contains('-')) {
 | 
				
			||||||
 | 
					                        $applicationId = str($applicationId)->before('-');
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
 | 
				
			||||||
 | 
					                    if ($preview) {
 | 
				
			||||||
 | 
					                        $foundApplicationPreviews[] = $preview->id;
 | 
				
			||||||
 | 
					                        $statusFromDb = $preview->status;
 | 
				
			||||||
 | 
					                        if ($statusFromDb !== $containerStatus) {
 | 
				
			||||||
 | 
					                            $preview->update(['status' => $containerStatus]);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        //Notify user that this container should not be there.
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $application = $this->applications->where('id', $applicationId)->first();
 | 
				
			||||||
 | 
					                    if ($application) {
 | 
				
			||||||
 | 
					                        $foundApplications[] = $application->id;
 | 
				
			||||||
 | 
					                        $statusFromDb = $application->status;
 | 
				
			||||||
 | 
					                        if ($statusFromDb !== $containerStatus) {
 | 
				
			||||||
 | 
					                            $application->update(['status' => $containerStatus]);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        //Notify user that this container should not be there.
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $uuid = data_get($labels, 'com.docker.compose.service');
 | 
				
			||||||
 | 
					                $type = data_get($labels, 'coolify.type');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ($uuid) {
 | 
				
			||||||
 | 
					                    if ($type === 'service') {
 | 
				
			||||||
 | 
					                        $database_id = data_get($labels, 'coolify.service.subId');
 | 
				
			||||||
 | 
					                        if ($database_id) {
 | 
				
			||||||
 | 
					                            $service_db = ServiceDatabase::where('id', $database_id)->first();
 | 
				
			||||||
 | 
					                            if ($service_db) {
 | 
				
			||||||
 | 
					                                $uuid = data_get($service_db, 'service.uuid');
 | 
				
			||||||
 | 
					                                if ($uuid) {
 | 
				
			||||||
 | 
					                                    $isPublic = data_get($service_db, 'is_public');
 | 
				
			||||||
 | 
					                                    if ($isPublic) {
 | 
				
			||||||
 | 
					                                        $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
 | 
				
			||||||
 | 
					                                            if ($this->server->isSwarm()) {
 | 
				
			||||||
 | 
					                                                return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
 | 
				
			||||||
 | 
					                                            } else {
 | 
				
			||||||
 | 
					                                                return data_get($value, 'Name') === "/$uuid-proxy";
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                        })->first();
 | 
				
			||||||
 | 
					                                        if (!$foundTcpProxy) {
 | 
				
			||||||
 | 
					                                            StartDatabaseProxy::run($service_db);
 | 
				
			||||||
 | 
					                                            // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        $database = $databases->where('uuid', $uuid)->first();
 | 
				
			||||||
 | 
					                        if ($database) {
 | 
				
			||||||
 | 
					                            $isPublic = data_get($database, 'is_public');
 | 
				
			||||||
 | 
					                            $foundDatabases[] = $database->id;
 | 
				
			||||||
 | 
					                            $statusFromDb = $database->status;
 | 
				
			||||||
 | 
					                            if ($statusFromDb !== $containerStatus) {
 | 
				
			||||||
 | 
					                                $database->update(['status' => $containerStatus]);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            if ($isPublic) {
 | 
				
			||||||
 | 
					                                $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
 | 
				
			||||||
 | 
					                                    if ($this->server->isSwarm()) {
 | 
				
			||||||
 | 
					                                        return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
 | 
				
			||||||
 | 
					                                    } else {
 | 
				
			||||||
 | 
					                                        return data_get($value, 'Name') === "/$uuid-proxy";
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                })->first();
 | 
				
			||||||
 | 
					                                if (!$foundTcpProxy) {
 | 
				
			||||||
 | 
					                                    StartDatabaseProxy::run($database);
 | 
				
			||||||
 | 
					                                    $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            // Notify user that this container should not be there.
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (data_get($container, 'Name') === '/coolify-db') {
 | 
				
			||||||
 | 
					                    $foundDatabases[] = 0;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $serviceLabelId = data_get($labels, 'coolify.serviceId');
 | 
				
			||||||
 | 
					            if ($serviceLabelId) {
 | 
				
			||||||
 | 
					                $subType = data_get($labels, 'coolify.service.subType');
 | 
				
			||||||
 | 
					                $subId = data_get($labels, 'coolify.service.subId');
 | 
				
			||||||
 | 
					                $service = $services->where('id', $serviceLabelId)->first();
 | 
				
			||||||
 | 
					                if (!$service) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ($subType === 'application') {
 | 
				
			||||||
 | 
					                    $service =  $service->applications()->where('id', $subId)->first();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $service =  $service->databases()->where('id', $subId)->first();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ($service) {
 | 
				
			||||||
 | 
					                    $foundServices[] = "$service->id-$service->name";
 | 
				
			||||||
 | 
					                    $statusFromDb = $service->status;
 | 
				
			||||||
 | 
					                    if ($statusFromDb !== $containerStatus) {
 | 
				
			||||||
 | 
					                        // ray('Updating status: ' . $containerStatus);
 | 
				
			||||||
 | 
					                        $service->update(['status' => $containerStatus]);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $exitedServices = collect([]);
 | 
				
			||||||
 | 
					        foreach ($services as $service) {
 | 
				
			||||||
 | 
					            $apps = $service->applications()->get();
 | 
				
			||||||
 | 
					            $dbs = $service->databases()->get();
 | 
				
			||||||
 | 
					            foreach ($apps as $app) {
 | 
				
			||||||
 | 
					                if (in_array("$app->id-$app->name", $foundServices)) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $exitedServices->push($app);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            foreach ($dbs as $db) {
 | 
				
			||||||
 | 
					                if (in_array("$db->id-$db->name", $foundServices)) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $exitedServices->push($db);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $exitedServices = $exitedServices->unique('id');
 | 
				
			||||||
 | 
					        foreach ($exitedServices as $exitedService) {
 | 
				
			||||||
 | 
					            if (str($exitedService->status)->startsWith('exited')) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $name = data_get($exitedService, 'name');
 | 
				
			||||||
 | 
					            $fqdn = data_get($exitedService, 'fqdn');
 | 
				
			||||||
 | 
					            $containerName = $name ? "$name, available at $fqdn" : $fqdn;
 | 
				
			||||||
 | 
					            $projectUuid = data_get($service, 'environment.project.uuid');
 | 
				
			||||||
 | 
					            $serviceUuid = data_get($service, 'uuid');
 | 
				
			||||||
 | 
					            $environmentName = data_get($service, 'environment.name');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($projectUuid && $serviceUuid && $environmentName) {
 | 
				
			||||||
 | 
					                $url =  base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $url = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
				
			||||||
 | 
					            $exitedService->update(['status' => 'exited']);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
 | 
				
			||||||
 | 
					        foreach ($notRunningApplications as $applicationId) {
 | 
				
			||||||
 | 
					            $application = $this->applications->where('id', $applicationId)->first();
 | 
				
			||||||
 | 
					            if (str($application->status)->startsWith('exited')) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $application->update(['status' => 'exited']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $name = data_get($application, 'name');
 | 
				
			||||||
 | 
					            $fqdn = data_get($application, 'fqdn');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $containerName = $name ? "$name ($fqdn)" : $fqdn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $projectUuid = data_get($application, 'environment.project.uuid');
 | 
				
			||||||
 | 
					            $applicationUuid = data_get($application, 'uuid');
 | 
				
			||||||
 | 
					            $environment = data_get($application, 'environment.name');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($projectUuid && $applicationUuid && $environment) {
 | 
				
			||||||
 | 
					                $url =  base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $url = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
 | 
				
			||||||
 | 
					        foreach ($notRunningApplicationPreviews as $previewId) {
 | 
				
			||||||
 | 
					            $preview = $previews->where('id', $previewId)->first();
 | 
				
			||||||
 | 
					            if (str($preview->status)->startsWith('exited')) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $preview->update(['status' => 'exited']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $name = data_get($preview, 'name');
 | 
				
			||||||
 | 
					            $fqdn = data_get($preview, 'fqdn');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $containerName = $name ? "$name ($fqdn)" : $fqdn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $projectUuid = data_get($preview, 'application.environment.project.uuid');
 | 
				
			||||||
 | 
					            $environmentName = data_get($preview, 'application.environment.name');
 | 
				
			||||||
 | 
					            $applicationUuid = data_get($preview, 'application.uuid');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($projectUuid && $applicationUuid && $environmentName) {
 | 
				
			||||||
 | 
					                $url =  base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $url = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
 | 
				
			||||||
 | 
					        foreach ($notRunningDatabases as $database) {
 | 
				
			||||||
 | 
					            $database = $databases->where('id', $database)->first();
 | 
				
			||||||
 | 
					            if (str($database->status)->startsWith('exited')) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $database->update(['status' => 'exited']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $name = data_get($database, 'name');
 | 
				
			||||||
 | 
					            $fqdn = data_get($database, 'fqdn');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $containerName = $name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $projectUuid = data_get($database, 'environment.project.uuid');
 | 
				
			||||||
 | 
					            $environmentName = data_get($database, 'environment.name');
 | 
				
			||||||
 | 
					            $databaseUuid = data_get($database, 'uuid');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($projectUuid && $databaseUuid && $environmentName) {
 | 
				
			||||||
 | 
					                $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $url = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Check if proxy is running
 | 
				
			||||||
 | 
					        $this->server->proxyType();
 | 
				
			||||||
 | 
					        $foundProxyContainer = $containers->filter(function ($value, $key) {
 | 
				
			||||||
 | 
					            if ($this->server->isSwarm()) {
 | 
				
			||||||
 | 
					                return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return data_get($value, 'Name') === '/coolify-proxy';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })->first();
 | 
				
			||||||
 | 
					        if (!$foundProxyContainer) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                $shouldStart = CheckProxy::run($this->server);
 | 
				
			||||||
 | 
					                if ($shouldStart) {
 | 
				
			||||||
 | 
					                    StartProxy::run($this->server, false);
 | 
				
			||||||
 | 
					                    $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					                ray($e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
 | 
				
			||||||
 | 
					            $this->server->save();
 | 
				
			||||||
 | 
					            $connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
 | 
				
			||||||
 | 
					            instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										22
									
								
								app/Actions/Server/StartSentinel.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/Actions/Server/StartSentinel.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Actions\Server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Lorisleiva\Actions\Concerns\AsAction;
 | 
				
			||||||
 | 
					use App\Models\Server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StartSentinel
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use AsAction;
 | 
				
			||||||
 | 
					    public function handle(Server $server, $version = 'latest', bool $restart = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($restart) {
 | 
				
			||||||
 | 
					            instant_remote_process(['docker rm -f coolify-sentinel'], $server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        instant_remote_process([
 | 
				
			||||||
 | 
					            "docker run --rm --pull always -d -e \"SCHEDULER=true\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
 | 
				
			||||||
 | 
					            "chown -R 9999:root /data/coolify/metrics /data/coolify/logs",
 | 
				
			||||||
 | 
					            "chmod -R 700 /data/coolify/metrics /data/coolify/logs"
 | 
				
			||||||
 | 
					        ], $server, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -27,10 +27,10 @@ class UpdateCoolify
 | 
				
			|||||||
            CleanupDocker::run($this->server, false);
 | 
					            CleanupDocker::run($this->server, false);
 | 
				
			||||||
            $this->latestVersion = get_latest_version_of_coolify();
 | 
					            $this->latestVersion = get_latest_version_of_coolify();
 | 
				
			||||||
            $this->currentVersion = config('version');
 | 
					            $this->currentVersion = config('version');
 | 
				
			||||||
            if ($settings->next_channel) {
 | 
					            // if ($settings->next_channel) {
 | 
				
			||||||
                ray('next channel enabled');
 | 
					            //     ray('next channel enabled');
 | 
				
			||||||
                $this->latestVersion = 'next';
 | 
					            //     $this->latestVersion = 'next';
 | 
				
			||||||
            }
 | 
					            // }
 | 
				
			||||||
            if ($force) {
 | 
					            if ($force) {
 | 
				
			||||||
                $this->update();
 | 
					                $this->update();
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										54
									
								
								app/Console/Commands/AdminRemoveUser.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								app/Console/Commands/AdminRemoveUser.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Console\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Server;
 | 
				
			||||||
 | 
					use App\Models\User;
 | 
				
			||||||
 | 
					use Illuminate\Console\Command;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AdminRemoveUser extends Command
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The name and signature of the console command.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @var string
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected $signature = 'admin:remove-user {email}';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The console command description.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @var string
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected $description = 'Remove User from database';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Execute the console command.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $email = $this->argument('email');
 | 
				
			||||||
 | 
					            $confirm = $this->confirm('Are you sure you want to remove user with email: ' . $email . '?');
 | 
				
			||||||
 | 
					            if (!$confirm) {
 | 
				
			||||||
 | 
					                $this->info('User removal cancelled.');
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->info("Removing user with email: $email");
 | 
				
			||||||
 | 
					            $user = User::whereEmail($email)->firstOrFail();
 | 
				
			||||||
 | 
					            $teams = $user->teams;
 | 
				
			||||||
 | 
					            foreach ($teams as $team) {
 | 
				
			||||||
 | 
					                if ($team->members->count() > 1) {
 | 
				
			||||||
 | 
					                    $this->error('User is a member of a team with more than one member. Please remove user from team first.');
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $team->delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $user->delete();
 | 
				
			||||||
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
 | 
					            $this->error('Failed to remove user.');
 | 
				
			||||||
 | 
					            $this->error($e->getMessage());
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,33 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace App\Console\Commands;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use App\Models\Server;
 | 
					 | 
				
			||||||
use Illuminate\Console\Command;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Cloud extends Command
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The name and signature of the console command.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @var string
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected $signature = 'cloud:unused-servers';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The console command description.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @var string
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected $description = 'Get Unused Servers from Cloud';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Execute the console command.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function handle()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended',true)->each(function($server){
 | 
					 | 
				
			||||||
            $this->info($server->name);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -29,7 +29,6 @@ class RootResetPassword extends Command
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handle()
 | 
					    public function handle()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        //
 | 
					 | 
				
			||||||
        $this->info('You are about to reset the root password.');
 | 
					        $this->info('You are about to reset the root password.');
 | 
				
			||||||
        $password = password('Give me a new password for root user: ');
 | 
					        $password = password('Give me a new password for root user: ');
 | 
				
			||||||
        $passwordAgain = password('Again');
 | 
					        $passwordAgain = password('Again');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ use App\Jobs\ScheduledTaskJob;
 | 
				
			|||||||
use App\Jobs\InstanceAutoUpdateJob;
 | 
					use App\Jobs\InstanceAutoUpdateJob;
 | 
				
			||||||
use App\Jobs\ContainerStatusJob;
 | 
					use App\Jobs\ContainerStatusJob;
 | 
				
			||||||
use App\Jobs\PullHelperImageJob;
 | 
					use App\Jobs\PullHelperImageJob;
 | 
				
			||||||
 | 
					use App\Jobs\PullSentinelImageJob;
 | 
				
			||||||
use App\Jobs\ServerStatusJob;
 | 
					use App\Jobs\ServerStatusJob;
 | 
				
			||||||
use App\Models\InstanceSettings;
 | 
					use App\Models\InstanceSettings;
 | 
				
			||||||
use App\Models\ScheduledDatabaseBackup;
 | 
					use App\Models\ScheduledDatabaseBackup;
 | 
				
			||||||
@@ -20,8 +21,10 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Kernel extends ConsoleKernel
 | 
					class Kernel extends ConsoleKernel
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    private $all_servers;
 | 
				
			||||||
    protected function schedule(Schedule $schedule): void
 | 
					    protected function schedule(Schedule $schedule): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        $this->all_servers = Server::all();
 | 
				
			||||||
        if (isDev()) {
 | 
					        if (isDev()) {
 | 
				
			||||||
            // Instance Jobs
 | 
					            // Instance Jobs
 | 
				
			||||||
            $schedule->command('horizon:snapshot')->everyMinute();
 | 
					            $schedule->command('horizon:snapshot')->everyMinute();
 | 
				
			||||||
@@ -55,35 +58,38 @@ class Kernel extends ConsoleKernel
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    private function pull_helper_image($schedule)
 | 
					    private function pull_helper_image($schedule)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
 | 
					        $servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
 | 
				
			||||||
        foreach ($servers as $server) {
 | 
					        foreach ($servers as $server) {
 | 
				
			||||||
            $schedule->job(new PullHelperImageJob($server))->everyTenMinutes()->onOneServer();
 | 
					            if (config('coolify.is_sentinel_enabled')) {
 | 
				
			||||||
 | 
					                $schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private function check_resources($schedule)
 | 
					    private function check_resources($schedule)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (isCloud()) {
 | 
					        if (isCloud()) {
 | 
				
			||||||
            $servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
 | 
					            $servers = $this->all_servers->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
 | 
				
			||||||
            $own = Team::find(0)->servers;
 | 
					            $own = Team::find(0)->servers;
 | 
				
			||||||
            $servers = $servers->merge($own);
 | 
					            $servers = $servers->merge($own);
 | 
				
			||||||
            $containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
 | 
					            $containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $servers = Server::all()->where('ip', '!=', '1.2.3.4');
 | 
					            $servers = $this->all_servers->where('ip', '!=', '1.2.3.4');
 | 
				
			||||||
            $containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
 | 
					            $containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        foreach ($containerServers as $server) {
 | 
					        foreach ($containerServers as $server) {
 | 
				
			||||||
            $schedule->job(new ContainerStatusJob($server))->everyTwoMinutes()->onOneServer();
 | 
					            $schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
 | 
				
			||||||
            if ($server->isLogDrainEnabled()) {
 | 
					            if ($server->isLogDrainEnabled()) {
 | 
				
			||||||
                $schedule->job(new CheckLogDrainContainerJob($server))->everyTwoMinutes()->onOneServer();
 | 
					                $schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        foreach ($servers as $server) {
 | 
					        foreach ($servers as $server) {
 | 
				
			||||||
            $schedule->job(new ServerStatusJob($server))->everyTwoMinutes()->onOneServer();
 | 
					            $schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private function instance_auto_update($schedule)
 | 
					    private function instance_auto_update($schedule)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (isDev()) {
 | 
					        if (isDev() || isCloud()) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $settings = InstanceSettings::get();
 | 
					        $settings = InstanceSettings::get();
 | 
				
			||||||
@@ -134,7 +140,16 @@ class Kernel extends ConsoleKernel
 | 
				
			|||||||
                $scheduled_task->delete();
 | 
					                $scheduled_task->delete();
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if ($application) {
 | 
				
			||||||
 | 
					                if (str($application->status)->contains('running') === false) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ($service) {
 | 
				
			||||||
 | 
					                if (str($service->status())->contains('running') === false) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
 | 
					            if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
 | 
				
			||||||
                $scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
 | 
					                $scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,7 +37,7 @@ class Controller extends BaseController
 | 
				
			|||||||
    public function email_verify(EmailVerificationRequest $request) {
 | 
					    public function email_verify(EmailVerificationRequest $request) {
 | 
				
			||||||
        $request->fulfill();
 | 
					        $request->fulfill();
 | 
				
			||||||
        $name = request()->user()?->name;
 | 
					        $name = request()->user()?->name;
 | 
				
			||||||
        send_internal_notification("User {$name} verified their email address.");
 | 
					        // send_internal_notification("User {$name} verified their email address.");
 | 
				
			||||||
        return redirect(RouteServiceProvider::HOME);
 | 
					        return redirect(RouteServiceProvider::HOME);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function forgot_password(Request $request) {
 | 
					    public function forgot_password(Request $request) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@ class Bitbucket extends Controller
 | 
				
			|||||||
            if ($x_bitbucket_event === 'repo:push') {
 | 
					            if ($x_bitbucket_event === 'repo:push') {
 | 
				
			||||||
                $branch = data_get($payload, 'push.changes.0.new.name');
 | 
					                $branch = data_get($payload, 'push.changes.0.new.name');
 | 
				
			||||||
                $full_name = data_get($payload, 'repository.full_name');
 | 
					                $full_name = data_get($payload, 'repository.full_name');
 | 
				
			||||||
 | 
					                $commit = data_get($payload, 'push.changes.0.new.target.hash');
 | 
				
			||||||
                if (!$branch) {
 | 
					                if (!$branch) {
 | 
				
			||||||
                    return response([
 | 
					                    return response([
 | 
				
			||||||
                        'status' => 'failed',
 | 
					                        'status' => 'failed',
 | 
				
			||||||
@@ -104,6 +104,7 @@ class Bitbucket extends Controller
 | 
				
			|||||||
                        queue_application_deployment(
 | 
					                        queue_application_deployment(
 | 
				
			||||||
                            application: $application,
 | 
					                            application: $application,
 | 
				
			||||||
                            deployment_uuid: $deployment_uuid,
 | 
					                            deployment_uuid: $deployment_uuid,
 | 
				
			||||||
 | 
					                            commit: $commit,
 | 
				
			||||||
                            force_rebuild: false,
 | 
					                            force_rebuild: false,
 | 
				
			||||||
                            is_webhook: true
 | 
					                            is_webhook: true
 | 
				
			||||||
                        );
 | 
					                        );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -129,6 +129,7 @@ class Github extends Controller
 | 
				
			|||||||
                                application: $application,
 | 
					                                application: $application,
 | 
				
			||||||
                                deployment_uuid: $deployment_uuid,
 | 
					                                deployment_uuid: $deployment_uuid,
 | 
				
			||||||
                                force_rebuild: false,
 | 
					                                force_rebuild: false,
 | 
				
			||||||
 | 
					                                commit: data_get($payload, 'after', 'HEAD'),
 | 
				
			||||||
                                is_webhook: true,
 | 
					                                is_webhook: true,
 | 
				
			||||||
                            );
 | 
					                            );
 | 
				
			||||||
                            $return_payloads->push([
 | 
					                            $return_payloads->push([
 | 
				
			||||||
@@ -177,6 +178,7 @@ class Github extends Controller
 | 
				
			|||||||
                                pull_request_id: $pull_request_id,
 | 
					                                pull_request_id: $pull_request_id,
 | 
				
			||||||
                                deployment_uuid: $deployment_uuid,
 | 
					                                deployment_uuid: $deployment_uuid,
 | 
				
			||||||
                                force_rebuild: false,
 | 
					                                force_rebuild: false,
 | 
				
			||||||
 | 
					                                commit: data_get($payload, 'head.sha', 'HEAD'),
 | 
				
			||||||
                                is_webhook: true,
 | 
					                                is_webhook: true,
 | 
				
			||||||
                                git_type: 'github'
 | 
					                                git_type: 'github'
 | 
				
			||||||
                            );
 | 
					                            );
 | 
				
			||||||
@@ -338,6 +340,7 @@ class Github extends Controller
 | 
				
			|||||||
                            queue_application_deployment(
 | 
					                            queue_application_deployment(
 | 
				
			||||||
                                application: $application,
 | 
					                                application: $application,
 | 
				
			||||||
                                deployment_uuid: $deployment_uuid,
 | 
					                                deployment_uuid: $deployment_uuid,
 | 
				
			||||||
 | 
					                                commit: data_get($payload, 'after', 'HEAD'),
 | 
				
			||||||
                                force_rebuild: false,
 | 
					                                force_rebuild: false,
 | 
				
			||||||
                                is_webhook: true,
 | 
					                                is_webhook: true,
 | 
				
			||||||
                            );
 | 
					                            );
 | 
				
			||||||
@@ -387,6 +390,7 @@ class Github extends Controller
 | 
				
			|||||||
                                pull_request_id: $pull_request_id,
 | 
					                                pull_request_id: $pull_request_id,
 | 
				
			||||||
                                deployment_uuid: $deployment_uuid,
 | 
					                                deployment_uuid: $deployment_uuid,
 | 
				
			||||||
                                force_rebuild: false,
 | 
					                                force_rebuild: false,
 | 
				
			||||||
 | 
					                                commit: data_get($payload, 'head.sha', 'HEAD'),
 | 
				
			||||||
                                is_webhook: true,
 | 
					                                is_webhook: true,
 | 
				
			||||||
                                git_type: 'github'
 | 
					                                git_type: 'github'
 | 
				
			||||||
                            );
 | 
					                            );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,6 +38,15 @@ class Gitlab extends Controller
 | 
				
			|||||||
            $headers = $request->headers->all();
 | 
					            $headers = $request->headers->all();
 | 
				
			||||||
            $x_gitlab_token = data_get($headers, 'x-gitlab-token.0');
 | 
					            $x_gitlab_token = data_get($headers, 'x-gitlab-token.0');
 | 
				
			||||||
            $x_gitlab_event = data_get($payload, 'object_kind');
 | 
					            $x_gitlab_event = data_get($payload, 'object_kind');
 | 
				
			||||||
 | 
					            $allowed_events = ['push', 'merge_request'];
 | 
				
			||||||
 | 
					            if (!in_array($x_gitlab_event, $allowed_events)) {
 | 
				
			||||||
 | 
					                $return_payloads->push([
 | 
				
			||||||
 | 
					                    'status' => 'failed',
 | 
				
			||||||
 | 
					                    'message' => 'Event not allowed. Only push and merge_request events are allowed.',
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					                return response($return_payloads);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($x_gitlab_event === 'push') {
 | 
					            if ($x_gitlab_event === 'push') {
 | 
				
			||||||
                $branch = data_get($payload, 'ref');
 | 
					                $branch = data_get($payload, 'ref');
 | 
				
			||||||
                $full_name = data_get($payload, 'project.path_with_namespace');
 | 
					                $full_name = data_get($payload, 'project.path_with_namespace');
 | 
				
			||||||
@@ -124,6 +133,7 @@ class Gitlab extends Controller
 | 
				
			|||||||
                            queue_application_deployment(
 | 
					                            queue_application_deployment(
 | 
				
			||||||
                                application: $application,
 | 
					                                application: $application,
 | 
				
			||||||
                                deployment_uuid: $deployment_uuid,
 | 
					                                deployment_uuid: $deployment_uuid,
 | 
				
			||||||
 | 
					                                commit: data_get($payload, 'after', 'HEAD'),
 | 
				
			||||||
                                force_rebuild: false,
 | 
					                                force_rebuild: false,
 | 
				
			||||||
                                is_webhook: true,
 | 
					                                is_webhook: true,
 | 
				
			||||||
                            );
 | 
					                            );
 | 
				
			||||||
@@ -173,6 +183,7 @@ class Gitlab extends Controller
 | 
				
			|||||||
                                application: $application,
 | 
					                                application: $application,
 | 
				
			||||||
                                pull_request_id: $pull_request_id,
 | 
					                                pull_request_id: $pull_request_id,
 | 
				
			||||||
                                deployment_uuid: $deployment_uuid,
 | 
					                                deployment_uuid: $deployment_uuid,
 | 
				
			||||||
 | 
					                                commit: data_get($payload, 'object_attributes.last_commit.id', 'HEAD'),
 | 
				
			||||||
                                force_rebuild: false,
 | 
					                                force_rebuild: false,
 | 
				
			||||||
                                is_webhook: true,
 | 
					                                is_webhook: true,
 | 
				
			||||||
                                git_type: 'gitlab'
 | 
					                                git_type: 'gitlab'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Jobs;
 | 
					namespace App\Jobs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Actions\Docker\GetContainersStatus;
 | 
				
			||||||
use App\Enums\ApplicationDeploymentStatus;
 | 
					use App\Enums\ApplicationDeploymentStatus;
 | 
				
			||||||
use App\Enums\ProcessStatus;
 | 
					use App\Enums\ProcessStatus;
 | 
				
			||||||
use App\Events\ApplicationStatusChanged;
 | 
					use App\Events\ApplicationStatusChanged;
 | 
				
			||||||
@@ -95,7 +96,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
    private ?string $buildTarget = null;
 | 
					    private ?string $buildTarget = null;
 | 
				
			||||||
    private Collection $saved_outputs;
 | 
					    private Collection $saved_outputs;
 | 
				
			||||||
    private ?string $full_healthcheck_url = null;
 | 
					    private ?string $full_healthcheck_url = null;
 | 
				
			||||||
    private bool $custom_healthcheck_found = false;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private string $serverUser = 'root';
 | 
					    private string $serverUser = 'root';
 | 
				
			||||||
    private string $serverUserHomeDir = '/root';
 | 
					    private string $serverUserHomeDir = '/root';
 | 
				
			||||||
@@ -107,6 +107,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
    private ?string $fullRepoUrl = null;
 | 
					    private ?string $fullRepoUrl = null;
 | 
				
			||||||
    private ?string $branch = null;
 | 
					    private ?string $branch = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ?string $coolify_variables = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public $tries = 1;
 | 
					    public $tries = 1;
 | 
				
			||||||
    public function __construct(int $application_deployment_queue_id)
 | 
					    public function __construct(int $application_deployment_queue_id)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -303,7 +305,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($this->server->isProxyShouldRun()) {
 | 
					        if ($this->server->isProxyShouldRun()) {
 | 
				
			||||||
            dispatch(new ContainerStatusJob($this->server));
 | 
					            GetContainersStatus::dispatch($this->server);
 | 
				
			||||||
 | 
					            // dispatch(new ContainerStatusJob($this->server));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->next(ApplicationDeploymentStatus::FINISHED->value);
 | 
					        $this->next(ApplicationDeploymentStatus::FINISHED->value);
 | 
				
			||||||
        if ($this->pull_request_id !== 0) {
 | 
					        if ($this->pull_request_id !== 0) {
 | 
				
			||||||
@@ -405,7 +408,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
            );
 | 
					            );
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $this->execute_remote_command(
 | 
					            $this->execute_remote_command(
 | 
				
			||||||
                [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true],
 | 
					                [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true],
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -435,9 +438,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                $this->write_deployment_configurations();
 | 
					                $this->write_deployment_configurations();
 | 
				
			||||||
                $server_workdir = $this->application->workdir();
 | 
					                $server_workdir = $this->application->workdir();
 | 
				
			||||||
                ray("SOURCE_COMMIT={$this->commit} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d");
 | 
					                ray("{$this->coolify_variables} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d");
 | 
				
			||||||
                $this->execute_remote_command(
 | 
					                $this->execute_remote_command(
 | 
				
			||||||
                    ["SOURCE_COMMIT={$this->commit} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d", "hidden" => true],
 | 
					                    ["{$this->coolify_variables} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d", "hidden" => true],
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -448,7 +451,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                $this->write_deployment_configurations();
 | 
					                $this->write_deployment_configurations();
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                $this->execute_remote_command(
 | 
					                $this->execute_remote_command(
 | 
				
			||||||
                    [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true],
 | 
					                    [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true],
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
                $this->write_deployment_configurations();
 | 
					                $this->write_deployment_configurations();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -710,10 +713,40 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
    private function save_environment_variables()
 | 
					    private function save_environment_variables()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $envs = collect([]);
 | 
					        $envs = collect([]);
 | 
				
			||||||
 | 
					        $local_branch = $this->branch;
 | 
				
			||||||
 | 
					        if ($this->pull_request_id !== 0) {
 | 
				
			||||||
 | 
					            $local_branch = "pull/{$this->pull_request_id}/head";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $sort = $this->application->settings->is_env_sorting_enabled;
 | 
				
			||||||
 | 
					        if ($sort) {
 | 
				
			||||||
 | 
					            $sorted_environment_variables = $this->application->environment_variables->sortBy('key');
 | 
				
			||||||
 | 
					            $sorted_environment_variables_preview = $this->application->environment_variables_preview->sortBy('key');
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $sorted_environment_variables = $this->application->environment_variables->sortBy('id');
 | 
				
			||||||
 | 
					            $sorted_environment_variables_preview = $this->application->environment_variables_preview->sortBy('id');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $ports = $this->application->main_port();
 | 
					        $ports = $this->application->main_port();
 | 
				
			||||||
        if ($this->pull_request_id !== 0) {
 | 
					        if ($this->pull_request_id !== 0) {
 | 
				
			||||||
            $this->env_filename = ".env-pr-$this->pull_request_id";
 | 
					            $this->env_filename = ".env-pr-$this->pull_request_id";
 | 
				
			||||||
            foreach ($this->application->environment_variables_preview as $env) {
 | 
					            // Add SOURCE_COMMIT if not exists
 | 
				
			||||||
 | 
					            if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) {
 | 
				
			||||||
 | 
					                if (!is_null($this->commit)) {
 | 
				
			||||||
 | 
					                    $envs->push("SOURCE_COMMIT={$this->commit}");
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $envs->push("SOURCE_COMMIT=unknown");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
 | 
				
			||||||
 | 
					                $envs->push("COOLIFY_FQDN={$this->preview->fqdn}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
 | 
				
			||||||
 | 
					                $url = str($this->preview->fqdn)->replace('http://', '')->replace('https://', '');
 | 
				
			||||||
 | 
					                $envs->push("COOLIFY_URL={$url}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
 | 
				
			||||||
 | 
					                $envs->push("COOLIFY_BRANCH={$local_branch}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            foreach ($sorted_environment_variables_preview as $env) {
 | 
				
			||||||
                $real_value = $env->real_value;
 | 
					                $real_value = $env->real_value;
 | 
				
			||||||
                if ($env->version === '4.0.0-beta.239') {
 | 
					                if ($env->version === '4.0.0-beta.239') {
 | 
				
			||||||
                    $real_value = $env->real_value;
 | 
					                    $real_value = $env->real_value;
 | 
				
			||||||
@@ -734,20 +767,27 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
            if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
 | 
					            if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
 | 
				
			||||||
                $envs->push("HOST=0.0.0.0");
 | 
					                $envs->push("HOST=0.0.0.0");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->env_filename = ".env";
 | 
				
			||||||
            // Add SOURCE_COMMIT if not exists
 | 
					            // Add SOURCE_COMMIT if not exists
 | 
				
			||||||
            if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) {
 | 
					            if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) {
 | 
				
			||||||
                if (!is_null($this->commit)) {
 | 
					                if (!is_null($this->commit)) {
 | 
				
			||||||
                    $envs->push("SOURCE_COMMIT={$this->commit}");
 | 
					                    $envs->push("SOURCE_COMMIT={$this->commit}");
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    $envs->push("SOURCE_COMMIT=unknown");
 | 
					                    $envs->push("SOURCE_COMMIT=unknown");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            $envs = $envs->sort(function ($a, $b) {
 | 
					            if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
 | 
				
			||||||
                return strpos($a, '$') === false ? -1 : 1;
 | 
					                $envs->push("COOLIFY_FQDN={$this->application->fqdn}");
 | 
				
			||||||
            });
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					            if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
 | 
				
			||||||
            $this->env_filename = ".env";
 | 
					                $url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
 | 
				
			||||||
            foreach ($this->application->environment_variables as $env) {
 | 
					                $envs->push("COOLIFY_URL={$url}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
 | 
				
			||||||
 | 
					                $envs->push("COOLIFY_BRANCH={$local_branch}");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            foreach ($sorted_environment_variables as $env) {
 | 
				
			||||||
                $real_value = $env->real_value;
 | 
					                $real_value = $env->real_value;
 | 
				
			||||||
                if ($env->version === '4.0.0-beta.239') {
 | 
					                if ($env->version === '4.0.0-beta.239') {
 | 
				
			||||||
                    $real_value = $env->real_value;
 | 
					                    $real_value = $env->real_value;
 | 
				
			||||||
@@ -768,17 +808,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
            if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
 | 
					            if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
 | 
				
			||||||
                $envs->push("HOST=0.0.0.0");
 | 
					                $envs->push("HOST=0.0.0.0");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            // Add SOURCE_COMMIT if not exists
 | 
					 | 
				
			||||||
            if ($this->application->environment_variables->where('key', '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()) {
 | 
					        if ($envs->isEmpty()) {
 | 
				
			||||||
@@ -870,7 +899,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                $this->write_deployment_configurations();
 | 
					                $this->write_deployment_configurations();
 | 
				
			||||||
                $this->server = $this->original_server;
 | 
					                $this->server = $this->original_server;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled || $this->pull_request_id !== 0 || str($this->application->custom_docker_run_options)->contains('--ip') || str($this->application->custom_docker_run_options)->contains('--ip6')) {
 | 
					            if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name) || $this->pull_request_id !== 0 || str($this->application->custom_docker_run_options)->contains('--ip') || str($this->application->custom_docker_run_options)->contains('--ip6')) {
 | 
				
			||||||
                $this->application_deployment_queue->addLogEntry("----------------------------------------");
 | 
					                $this->application_deployment_queue->addLogEntry("----------------------------------------");
 | 
				
			||||||
                if (count($this->application->ports_mappings_array) > 0) {
 | 
					                if (count($this->application->ports_mappings_array) > 0) {
 | 
				
			||||||
                    $this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
 | 
					                    $this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
 | 
				
			||||||
@@ -878,6 +907,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                if ((bool) $this->application->settings->is_consistent_container_name_enabled) {
 | 
					                if ((bool) $this->application->settings->is_consistent_container_name_enabled) {
 | 
				
			||||||
                    $this->application_deployment_queue->addLogEntry("Consistent container name feature enabled, rolling update is not supported.");
 | 
					                    $this->application_deployment_queue->addLogEntry("Consistent container name feature enabled, rolling update is not supported.");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                if (isset($this->application->settings->custom_internal_name)) {
 | 
				
			||||||
 | 
					                    $this->application_deployment_queue->addLogEntry("Custom internal name is set, rolling update is not supported.");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                if ($this->pull_request_id !== 0) {
 | 
					                if ($this->pull_request_id !== 0) {
 | 
				
			||||||
                    $this->application->settings->is_consistent_container_name_enabled = true;
 | 
					                    $this->application->settings->is_consistent_container_name_enabled = true;
 | 
				
			||||||
                    $this->application_deployment_queue->addLogEntry("Pull request deployment, rolling update is not supported.");
 | 
					                    $this->application_deployment_queue->addLogEntry("Pull request deployment, rolling update is not supported.");
 | 
				
			||||||
@@ -903,10 +935,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        if ($this->server->isSwarm()) {
 | 
					        if ($this->server->isSwarm()) {
 | 
				
			||||||
            // Implement healthcheck for swarm
 | 
					            // Implement healthcheck for swarm
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            if ($this->application->isHealthcheckDisabled() && $this->custom_healthcheck_found === false) {
 | 
					            if ($this->application->isHealthcheckDisabled() && $this->application->custom_healthcheck_found === false) {
 | 
				
			||||||
                $this->newVersionIsHealthy = true;
 | 
					                $this->newVersionIsHealthy = true;
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if ($this->application->custom_healthcheck_found) {
 | 
				
			||||||
 | 
					                $this->application_deployment_queue->addLogEntry("Custom healthcheck found, skipping default healthcheck.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            // ray('New container name: ', $this->container_name);
 | 
					            // ray('New container name: ', $this->container_name);
 | 
				
			||||||
            if ($this->container_name) {
 | 
					            if ($this->container_name) {
 | 
				
			||||||
                $counter = 1;
 | 
					                $counter = 1;
 | 
				
			||||||
@@ -914,6 +949,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                if ($this->full_healthcheck_url) {
 | 
					                if ($this->full_healthcheck_url) {
 | 
				
			||||||
                    $this->application_deployment_queue->addLogEntry("Healthcheck URL (inside the container): {$this->full_healthcheck_url}");
 | 
					                    $this->application_deployment_queue->addLogEntry("Healthcheck URL (inside the container): {$this->full_healthcheck_url}");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                $this->application_deployment_queue->addLogEntry("Waiting for the start period ({$this->application->health_check_start_period} seconds) before starting healthcheck.");
 | 
				
			||||||
 | 
					                $sleeptime = 0;
 | 
				
			||||||
 | 
					                while ($sleeptime < $this->application->health_check_start_period) {
 | 
				
			||||||
 | 
					                    Sleep::for(1)->seconds();
 | 
				
			||||||
 | 
					                    $sleeptime++;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                while ($counter <= $this->application->health_check_retries) {
 | 
					                while ($counter <= $this->application->health_check_retries) {
 | 
				
			||||||
                    $this->execute_remote_command(
 | 
					                    $this->execute_remote_command(
 | 
				
			||||||
                        [
 | 
					                        [
 | 
				
			||||||
@@ -922,9 +963,23 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                            "save" => "health_check",
 | 
					                            "save" => "health_check",
 | 
				
			||||||
                            "append" => false
 | 
					                            "append" => false
 | 
				
			||||||
                        ],
 | 
					                        ],
 | 
				
			||||||
 | 
					                        [
 | 
				
			||||||
 | 
					                            "docker inspect --format='{{json .State.Health.Log}}' {$this->container_name}",
 | 
				
			||||||
 | 
					                            "hidden" => true,
 | 
				
			||||||
 | 
					                            "save" => "health_check_logs",
 | 
				
			||||||
 | 
					                            "append" => false
 | 
				
			||||||
 | 
					                        ],
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
                    $this->application_deployment_queue->addLogEntry("Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}");
 | 
					                    $this->application_deployment_queue->addLogEntry("Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}");
 | 
				
			||||||
 | 
					                    $health_check_logs = data_get(collect(json_decode($this->saved_outputs->get('health_check_logs')))->last(), 'Output', '(no logs)');
 | 
				
			||||||
 | 
					                    if (empty($health_check_logs)) {
 | 
				
			||||||
 | 
					                        $health_check_logs = '(no logs)';
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $health_check_return_code = data_get(collect(json_decode($this->saved_outputs->get('health_check_logs')))->last(), 'ExitCode', '(no return code)');
 | 
				
			||||||
 | 
					                    if ($health_check_logs !== '(no logs)' || $health_check_return_code !== '(no return code)') {
 | 
				
			||||||
 | 
					                        $this->application_deployment_queue->addLogEntry("Healthcheck logs: {$health_check_logs} | Return code: {$health_check_return_code}");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'healthy') {
 | 
					                    if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'healthy') {
 | 
				
			||||||
                        $this->newVersionIsHealthy = true;
 | 
					                        $this->newVersionIsHealthy = true;
 | 
				
			||||||
                        $this->application->update(['status' => 'running']);
 | 
					                        $this->application->update(['status' => 'running']);
 | 
				
			||||||
@@ -936,7 +991,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    $counter++;
 | 
					                    $counter++;
 | 
				
			||||||
                    Sleep::for($this->application->health_check_interval)->seconds();
 | 
					                    $sleeptime = 0;
 | 
				
			||||||
 | 
					                    while ($sleeptime < $this->application->health_check_interval) {
 | 
				
			||||||
 | 
					                        Sleep::for(1)->seconds();
 | 
				
			||||||
 | 
					                        $sleeptime++;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -954,6 +1013,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        $this->generate_image_names();
 | 
					        $this->generate_image_names();
 | 
				
			||||||
        $this->application_deployment_queue->addLogEntry("Starting pull request (#{$this->pull_request_id}) deployment of {$this->customRepository}:{$this->application->git_branch}.");
 | 
					        $this->application_deployment_queue->addLogEntry("Starting pull request (#{$this->pull_request_id}) deployment of {$this->customRepository}:{$this->application->git_branch}.");
 | 
				
			||||||
        $this->prepare_builder_image();
 | 
					        $this->prepare_builder_image();
 | 
				
			||||||
 | 
					        $this->check_git_if_build_needed();
 | 
				
			||||||
        $this->clone_repository();
 | 
					        $this->clone_repository();
 | 
				
			||||||
        $this->set_base_dir();
 | 
					        $this->set_base_dir();
 | 
				
			||||||
        $this->cleanup_git();
 | 
					        $this->cleanup_git();
 | 
				
			||||||
@@ -1005,7 +1065,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                "command" => "docker rm -f {$this->deployment_uuid}",
 | 
					                "command" => "docker rm -f {$this->deployment_uuid}",
 | 
				
			||||||
                "ignore_errors" => true,
 | 
					                "ignore_errors" => true,
 | 
				
			||||||
                "hidden" => true
 | 
					                "hidden" => true
 | 
				
			||||||
            ],
 | 
					            ]
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        $this->execute_remote_command(
 | 
				
			||||||
            [
 | 
					            [
 | 
				
			||||||
                $runCommand,
 | 
					                $runCommand,
 | 
				
			||||||
                "hidden" => true,
 | 
					                "hidden" => true,
 | 
				
			||||||
@@ -1061,9 +1123,30 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->application_deployment_queue->addLogEntry("Setting base directory to {$this->workdir}.");
 | 
					        $this->application_deployment_queue->addLogEntry("Setting base directory to {$this->workdir}.");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    private function set_coolify_variables()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->coolify_variables = "SOURCE_COMMIT={$this->commit} ";
 | 
				
			||||||
 | 
					        if ($this->pull_request_id === 0) {
 | 
				
			||||||
 | 
					            $fqdn = $this->application->fqdn;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $fqdn = $this->preview->fqdn;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (isset($fqdn)) {
 | 
				
			||||||
 | 
					            $this->coolify_variables .= "COOLIFY_FQDN={$fqdn} ";
 | 
				
			||||||
 | 
					            $url = str($fqdn)->replace('http://', '')->replace('https://', '');
 | 
				
			||||||
 | 
					            $this->coolify_variables .= "COOLIFY_URL={$url} ";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (isset($this->application->git_branch)) {
 | 
				
			||||||
 | 
					            $this->coolify_variables .= "COOLIFY_BRANCH={$this->application->git_branch} ";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    private function check_git_if_build_needed()
 | 
					    private function check_git_if_build_needed()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->generate_git_import_commands();
 | 
					        $this->generate_git_import_commands();
 | 
				
			||||||
 | 
					        $local_branch = $this->branch;
 | 
				
			||||||
 | 
					        if ($this->pull_request_id !== 0) {
 | 
				
			||||||
 | 
					            $local_branch = "pull/{$this->pull_request_id}/head";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $private_key = data_get($this->application, 'private_key.private_key');
 | 
					        $private_key = data_get($this->application, 'private_key.private_key');
 | 
				
			||||||
        if ($private_key) {
 | 
					        if ($private_key) {
 | 
				
			||||||
            $private_key = base64_encode($private_key);
 | 
					            $private_key = base64_encode($private_key);
 | 
				
			||||||
@@ -1078,7 +1161,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                    executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa")
 | 
					                    executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa")
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
                [
 | 
					                [
 | 
				
			||||||
                    executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->fullRepoUrl} {$this->branch}"),
 | 
					                    executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->fullRepoUrl} {$local_branch}"),
 | 
				
			||||||
                    "hidden" => true,
 | 
					                    "hidden" => true,
 | 
				
			||||||
                    "save" => "git_commit_sha"
 | 
					                    "save" => "git_commit_sha"
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
@@ -1086,15 +1169,19 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $this->execute_remote_command(
 | 
					            $this->execute_remote_command(
 | 
				
			||||||
                [
 | 
					                [
 | 
				
			||||||
                    executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->fullRepoUrl} {$this->branch}"),
 | 
					                    executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->fullRepoUrl} {$local_branch}"),
 | 
				
			||||||
                    "hidden" => true,
 | 
					                    "hidden" => true,
 | 
				
			||||||
                    "save" => "git_commit_sha"
 | 
					                    "save" => "git_commit_sha"
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        ray("GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->fullRepoUrl} {$local_branch}");
 | 
				
			||||||
        if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) {
 | 
					        if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) {
 | 
				
			||||||
            $this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t");
 | 
					            $this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t");
 | 
				
			||||||
 | 
					            $this->application_deployment_queue->commit = $this->commit;
 | 
				
			||||||
 | 
					            $this->application_deployment_queue->save();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        $this->set_coolify_variables();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private function clone_repository()
 | 
					    private function clone_repository()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -1104,12 +1191,29 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        if ($this->pull_request_id !== 0) {
 | 
					        if ($this->pull_request_id !== 0) {
 | 
				
			||||||
            $this->application_deployment_queue->addLogEntry("Checking out tag pull/{$this->pull_request_id}/head.");
 | 
					            $this->application_deployment_queue->addLogEntry("Checking out tag pull/{$this->pull_request_id}/head.");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        ray($importCommands);
 | 
				
			||||||
        $this->execute_remote_command(
 | 
					        $this->execute_remote_command(
 | 
				
			||||||
            [
 | 
					            [
 | 
				
			||||||
                $importCommands, "hidden" => true
 | 
					                $importCommands, "hidden" => true
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        $this->create_workdir();
 | 
					        $this->create_workdir();
 | 
				
			||||||
 | 
					        $this->execute_remote_command(
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                executeInDocker($this->deployment_uuid, "cd {$this->workdir} && git log -1 {$this->commit} --pretty=%B"),
 | 
				
			||||||
 | 
					                "hidden" => true,
 | 
				
			||||||
 | 
					                "save" => "commit_message"
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        ray($this->saved_outputs->get('commit_message'));
 | 
				
			||||||
 | 
					        raY($this->commit);
 | 
				
			||||||
 | 
					        if ($this->saved_outputs->get('commit_message')) {
 | 
				
			||||||
 | 
					            $commit_message = str($this->saved_outputs->get('commit_message'))->limit(47);
 | 
				
			||||||
 | 
					            $this->application_deployment_queue->commit_message = $commit_message->value();
 | 
				
			||||||
 | 
					            ApplicationDeploymentQueue::whereCommit($this->commit)->whereApplicationId($this->application->id)->update(
 | 
				
			||||||
 | 
					                ['commit_message' => $commit_message->value()]
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function generate_git_import_commands()
 | 
					    private function generate_git_import_commands()
 | 
				
			||||||
@@ -1197,6 +1301,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
    private function generate_env_variables()
 | 
					    private function generate_env_variables()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->env_args = collect([]);
 | 
					        $this->env_args = collect([]);
 | 
				
			||||||
 | 
					        $this->env_args->put('SOURCE_COMMIT', $this->commit);
 | 
				
			||||||
        if ($this->pull_request_id === 0) {
 | 
					        if ($this->pull_request_id === 0) {
 | 
				
			||||||
            foreach ($this->application->build_environment_variables as $env) {
 | 
					            foreach ($this->application->build_environment_variables as $env) {
 | 
				
			||||||
                if (!is_null($env->real_value)) {
 | 
					                if (!is_null($env->real_value)) {
 | 
				
			||||||
@@ -1210,7 +1315,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->env_args->put('SOURCE_COMMIT', $this->commit);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function generate_compose_file()
 | 
					    private function generate_compose_file()
 | 
				
			||||||
@@ -1258,23 +1362,22 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        if ($this->pull_request_id !== 0) {
 | 
					        if ($this->pull_request_id !== 0) {
 | 
				
			||||||
            $labels = collect(generateLabelsApplication($this->application, $this->preview));
 | 
					            $labels = collect(generateLabelsApplication($this->application, $this->preview));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $labels = $labels->map(function ($value, $key) {
 | 
					        if ($this->application->settings->is_container_label_escape_enabled) {
 | 
				
			||||||
            return escapeDollarSign($value);
 | 
					            $labels = $labels->map(function ($value, $key) {
 | 
				
			||||||
        });
 | 
					                return escapeDollarSign($value);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
 | 
					        $labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Check for custom HEALTHCHECK
 | 
					        // Check for custom HEALTHCHECK
 | 
				
			||||||
        $this->custom_healthcheck_found = false;
 | 
					 | 
				
			||||||
        if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
 | 
					        if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
 | 
				
			||||||
            $this->execute_remote_command([
 | 
					            $this->execute_remote_command([
 | 
				
			||||||
                executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile_from_repo', "ignore_errors" => true
 | 
					                executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile_from_repo', "ignore_errors" => true
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
            $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n"));
 | 
					            $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n"));
 | 
				
			||||||
            if (str($dockerfile)->contains('HEALTHCHECK')) {
 | 
					            $this->application->parseHealthcheckFromDockerfile($dockerfile);
 | 
				
			||||||
                $this->custom_healthcheck_found = true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $docker_compose = [
 | 
					        $docker_compose = [
 | 
				
			||||||
            'version' => '3.8',
 | 
					 | 
				
			||||||
            'services' => [
 | 
					            'services' => [
 | 
				
			||||||
                $this->container_name => [
 | 
					                $this->container_name => [
 | 
				
			||||||
                    'image' => $this->production_image_name,
 | 
					                    'image' => $this->production_image_name,
 | 
				
			||||||
@@ -1282,7 +1385,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                    'restart' => RESTART_MODE,
 | 
					                    'restart' => RESTART_MODE,
 | 
				
			||||||
                    'expose' => $ports,
 | 
					                    'expose' => $ports,
 | 
				
			||||||
                    'networks' => [
 | 
					                    'networks' => [
 | 
				
			||||||
                        $this->destination->network,
 | 
					                        $this->destination->network => [
 | 
				
			||||||
 | 
					                            'aliases' => [
 | 
				
			||||||
 | 
					                                $this->container_name
 | 
				
			||||||
 | 
					                            ]
 | 
				
			||||||
 | 
					                        ]
 | 
				
			||||||
                    ],
 | 
					                    ],
 | 
				
			||||||
                    'mem_limit' => $this->application->limits_memory,
 | 
					                    'mem_limit' => $this->application->limits_memory,
 | 
				
			||||||
                    'memswap_limit' => $this->application->limits_memory_swap,
 | 
					                    'memswap_limit' => $this->application->limits_memory_swap,
 | 
				
			||||||
@@ -1300,6 +1407,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                ]
 | 
					                ]
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					        if (isset($this->application->settings->custom_internal_name)) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['aliases'][] = $this->application->settings->custom_internal_name;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        // if (str($this->saved_outputs->get('dotenv'))->isNotEmpty()) {
 | 
					        // if (str($this->saved_outputs->get('dotenv'))->isNotEmpty()) {
 | 
				
			||||||
        //     if (data_get($docker_compose, "services.{$this->container_name}.env_file")) {
 | 
					        //     if (data_get($docker_compose, "services.{$this->container_name}.env_file")) {
 | 
				
			||||||
        //         $docker_compose['services'][$this->container_name]['env_file'][] = '.env';
 | 
					        //         $docker_compose['services'][$this->container_name]['env_file'][] = '.env';
 | 
				
			||||||
@@ -1317,18 +1427,17 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        if (!is_null($this->env_filename)) {
 | 
					        if (!is_null($this->env_filename)) {
 | 
				
			||||||
            $docker_compose['services'][$this->container_name]['env_file'] = [$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'] = [
 | 
				
			||||||
            $docker_compose['services'][$this->container_name]['healthcheck'] = [
 | 
					            'test' => [
 | 
				
			||||||
                'test' => [
 | 
					                'CMD-SHELL',
 | 
				
			||||||
                    'CMD-SHELL',
 | 
					                $this->generate_healthcheck_commands()
 | 
				
			||||||
                    $this->generate_healthcheck_commands()
 | 
					            ],
 | 
				
			||||||
                ],
 | 
					            'interval' => $this->application->health_check_interval . 's',
 | 
				
			||||||
                'interval' => $this->application->health_check_interval . 's',
 | 
					            'timeout' => $this->application->health_check_timeout . 's',
 | 
				
			||||||
                'timeout' => $this->application->health_check_timeout . 's',
 | 
					            'retries' => $this->application->health_check_retries,
 | 
				
			||||||
                'retries' => $this->application->health_check_retries,
 | 
					            'start_period' => $this->application->health_check_start_period . 's'
 | 
				
			||||||
                'start_period' => $this->application->health_check_start_period . 's'
 | 
					        ];
 | 
				
			||||||
            ];
 | 
					
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (!is_null($this->application->limits_cpuset)) {
 | 
					        if (!is_null($this->application->limits_cpuset)) {
 | 
				
			||||||
            data_set($docker_compose, 'services.' . $this->container_name . '.cpuset', $this->application->limits_cpuset);
 | 
					            data_set($docker_compose, 'services.' . $this->container_name . '.cpuset', $this->application->limits_cpuset);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -1510,95 +1619,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        return $local_persistent_volumes_names;
 | 
					        return $local_persistent_volumes_names;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*private function generate_environment_variables($ports)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $environment_variables = collect();
 | 
					 | 
				
			||||||
        if ($this->pull_request_id === 0) {
 | 
					 | 
				
			||||||
            foreach ($this->application->runtime_environment_variables as $env) {
 | 
					 | 
				
			||||||
                // This is necessary because we have to escape the value of the environment variable
 | 
					 | 
				
			||||||
                // but only if the environment variable is created after 4.0.0-beta.240
 | 
					 | 
				
			||||||
                // when I implemented the escaping feature.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Old environment variables are not escaped, because it could break the application
 | 
					 | 
				
			||||||
                // as the application could expect the unescaped 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 = escapeDollarSign($real_value);
 | 
					 | 
				
			||||||
                    $environment_variables->push("$env->key='$real_value'");
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    $environment_variables->push("$env->key=$real_value");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            foreach ($this->application->nixpacks_environment_variables as $env) {
 | 
					 | 
				
			||||||
                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 = escapeDollarSign($real_value);
 | 
					 | 
				
			||||||
                    $environment_variables->push("$env->key='$real_value'");
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    $environment_variables->push("$env->key=$real_value");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            foreach ($this->application->runtime_environment_variables_preview as $env) {
 | 
					 | 
				
			||||||
                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 = escapeDollarSign($real_value);
 | 
					 | 
				
			||||||
                    $environment_variables->push("$env->key='$real_value'");
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    $environment_variables->push("$env->key=$real_value");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            foreach ($this->application->nixpacks_environment_variables_preview as $env) {
 | 
					 | 
				
			||||||
                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 = escapeDollarSign($real_value);
 | 
					 | 
				
			||||||
                    $environment_variables->push("$env->key='$real_value'");
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    $environment_variables->push("$env->key=$real_value");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // Add PORT if not exists, use the first port as default
 | 
					 | 
				
			||||||
        if ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('PORT'))->isEmpty()) {
 | 
					 | 
				
			||||||
            $environment_variables->push("PORT={$ports[0]}");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // Add HOST if not exists
 | 
					 | 
				
			||||||
        if ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('HOST'))->isEmpty()) {
 | 
					 | 
				
			||||||
            $environment_variables->push("HOST=0.0.0.0");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('SOURCE_COMMIT'))->isEmpty()) {
 | 
					 | 
				
			||||||
            if (!is_null($this->commit)) {
 | 
					 | 
				
			||||||
                $environment_variables->push("SOURCE_COMMIT={$this->commit}");
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                $environment_variables->push("SOURCE_COMMIT=unknown");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ray($environment_variables->all());
 | 
					 | 
				
			||||||
        return $environment_variables->all();
 | 
					 | 
				
			||||||
    }*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private function generate_healthcheck_commands()
 | 
					    private function generate_healthcheck_commands()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') {
 | 
					 | 
				
			||||||
            // TODO: disabled HC because there are several ways to hc a simple docker image, hard to figure out a good way. Like some docker images (pocketbase) does not have curl.
 | 
					 | 
				
			||||||
            return 'exit 0';
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (!$this->application->health_check_port) {
 | 
					        if (!$this->application->health_check_port) {
 | 
				
			||||||
            $health_check_port = $this->application->ports_exposes_array[0];
 | 
					            $health_check_port = $this->application->ports_exposes_array[0];
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -1610,12 +1632,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        if ($this->application->health_check_path) {
 | 
					        if ($this->application->health_check_path) {
 | 
				
			||||||
            $this->full_healthcheck_url = "{$this->application->health_check_method}: {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path}";
 | 
					            $this->full_healthcheck_url = "{$this->application->health_check_method}: {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path}";
 | 
				
			||||||
            $generated_healthchecks_commands = [
 | 
					            $generated_healthchecks_commands = [
 | 
				
			||||||
                "curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path} > /dev/null"
 | 
					                "curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path} > /dev/null || wget -q -O- {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path} > /dev/null || exit 1"
 | 
				
			||||||
            ];
 | 
					            ];
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $this->full_healthcheck_url = "{$this->application->health_check_method}: {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/";
 | 
					            $this->full_healthcheck_url = "{$this->application->health_check_method}: {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/";
 | 
				
			||||||
            $generated_healthchecks_commands = [
 | 
					            $generated_healthchecks_commands = [
 | 
				
			||||||
                "curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/"
 | 
					                "curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/ > /dev/null || wget -q -O- {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/ > /dev/null || exit 1"
 | 
				
			||||||
            ];
 | 
					            ];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return implode(' ', $generated_healthchecks_commands);
 | 
					        return implode(' ', $generated_healthchecks_commands);
 | 
				
			||||||
@@ -1804,12 +1826,17 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
 | 
				
			|||||||
                    ["docker rm -f $containerName >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true],
 | 
					                    ["docker rm -f $containerName >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true],
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            if ($this->application->settings->is_consistent_container_name_enabled) {
 | 
					            if ($this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name)) {
 | 
				
			||||||
                $this->execute_remote_command(
 | 
					                $this->execute_remote_command(
 | 
				
			||||||
                    ["docker rm -f $this->container_name >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true],
 | 
					                    ["docker rm -f $this->container_name >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true],
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') {
 | 
				
			||||||
 | 
					                $this->application_deployment_queue->addLogEntry("----------------------------------------");
 | 
				
			||||||
 | 
					                $this->application_deployment_queue->addLogEntry("WARNING: Dockerfile or Docker Image based deployment detected. The healthcheck needs a curl or wget command to check the health of the application. Please make sure that it is available in the image or turn off healthcheck on Coolify's UI.");
 | 
				
			||||||
 | 
					                $this->application_deployment_queue->addLogEntry("----------------------------------------");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            $this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container.");
 | 
					            $this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container.");
 | 
				
			||||||
            $this->application_deployment_queue->update([
 | 
					            $this->application_deployment_queue->update([
 | 
				
			||||||
                'status' => ApplicationDeploymentStatus::FAILED->value,
 | 
					                'status' => ApplicationDeploymentStatus::FAILED->value,
 | 
				
			||||||
@@ -1827,11 +1854,11 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
 | 
				
			|||||||
            $this->application_deployment_queue->addLogEntry("Pulling latest images from the registry.");
 | 
					            $this->application_deployment_queue->addLogEntry("Pulling latest images from the registry.");
 | 
				
			||||||
            $this->execute_remote_command(
 | 
					            $this->execute_remote_command(
 | 
				
			||||||
                [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true],
 | 
					                [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true],
 | 
				
			||||||
                [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} build"), "hidden" => true],
 | 
					                [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} build"), "hidden" => true],
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $this->execute_remote_command(
 | 
					            $this->execute_remote_command(
 | 
				
			||||||
                [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true],
 | 
					                [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true],
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->application_deployment_queue->addLogEntry("New images built.");
 | 
					        $this->application_deployment_queue->addLogEntry("New images built.");
 | 
				
			||||||
@@ -1843,16 +1870,16 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
 | 
				
			|||||||
            $this->application_deployment_queue->addLogEntry("Pulling latest images from the registry.");
 | 
					            $this->application_deployment_queue->addLogEntry("Pulling latest images from the registry.");
 | 
				
			||||||
            $this->execute_remote_command(
 | 
					            $this->execute_remote_command(
 | 
				
			||||||
                [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true],
 | 
					                [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true],
 | 
				
			||||||
                [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
 | 
					                [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            if ($this->use_build_server) {
 | 
					            if ($this->use_build_server) {
 | 
				
			||||||
                $this->execute_remote_command(
 | 
					                $this->execute_remote_command(
 | 
				
			||||||
                    ["SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", "hidden" => true],
 | 
					                    ["{$this->coolify_variables} docker compose --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", "hidden" => true],
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                $this->execute_remote_command(
 | 
					                $this->execute_remote_command(
 | 
				
			||||||
                    [executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), "hidden" => true],
 | 
					                    [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), "hidden" => true],
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,15 +2,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Jobs;
 | 
					namespace App\Jobs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Actions\Database\StartDatabaseProxy;
 | 
					use App\Actions\Docker\GetContainersStatus;
 | 
				
			||||||
use App\Actions\Proxy\CheckProxy;
 | 
					 | 
				
			||||||
use App\Actions\Proxy\StartProxy;
 | 
					 | 
				
			||||||
use App\Actions\Shared\ComplexStatusCheck;
 | 
					 | 
				
			||||||
use App\Models\ApplicationPreview;
 | 
					 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
use App\Models\ServiceDatabase;
 | 
					 | 
				
			||||||
use App\Notifications\Container\ContainerRestarted;
 | 
					 | 
				
			||||||
use App\Notifications\Container\ContainerStopped;
 | 
					 | 
				
			||||||
use Illuminate\Bus\Queueable;
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
					use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
@@ -18,7 +11,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			|||||||
use Illuminate\Queue\InteractsWithQueue;
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
 | 
					use Illuminate\Queue\Middleware\WithoutOverlapping;
 | 
				
			||||||
use Illuminate\Queue\SerializesModels;
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
use Illuminate\Support\Arr;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
 | 
					class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -44,335 +36,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function handle()
 | 
					    public function handle()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (!$this->server->isFunctional()) {
 | 
					        GetContainersStatus::run($this->server);
 | 
				
			||||||
            return 'Server is not ready.';
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        $applications = $this->server->applications();
 | 
					 | 
				
			||||||
        $skip_these_applications = collect([]);
 | 
					 | 
				
			||||||
        foreach ($applications as $application) {
 | 
					 | 
				
			||||||
            if ($application->additional_servers->count() > 0) {
 | 
					 | 
				
			||||||
                $skip_these_applications->push($application);
 | 
					 | 
				
			||||||
                ComplexStatusCheck::run($application);
 | 
					 | 
				
			||||||
                $applications = $applications->filter(function ($value, $key) use ($application) {
 | 
					 | 
				
			||||||
                    return $value->id !== $application->id;
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        $applications = $applications->filter(function ($value, $key) use ($skip_these_applications) {
 | 
					 | 
				
			||||||
            return !$skip_these_applications->pluck('id')->contains($value->id);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            if ($this->server->isSwarm()) {
 | 
					 | 
				
			||||||
                $containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
 | 
					 | 
				
			||||||
                $containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                // Precheck for containers
 | 
					 | 
				
			||||||
                $containers = instant_remote_process(["docker container ls -q"], $this->server, false);
 | 
					 | 
				
			||||||
                if (!$containers) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
 | 
					 | 
				
			||||||
                $containerReplicates = null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (is_null($containers)) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $containers = format_docker_command_output_to_json($containers);
 | 
					 | 
				
			||||||
            if ($containerReplicates) {
 | 
					 | 
				
			||||||
                $containerReplicates = format_docker_command_output_to_json($containerReplicates);
 | 
					 | 
				
			||||||
                foreach ($containerReplicates as $containerReplica) {
 | 
					 | 
				
			||||||
                    $name = data_get($containerReplica, 'Name');
 | 
					 | 
				
			||||||
                    $containers = $containers->map(function ($container) use ($name, $containerReplica) {
 | 
					 | 
				
			||||||
                        if (data_get($container, 'Spec.Name') === $name) {
 | 
					 | 
				
			||||||
                            $replicas = data_get($containerReplica, 'Replicas');
 | 
					 | 
				
			||||||
                            $running = str($replicas)->explode('/')[0];
 | 
					 | 
				
			||||||
                            $total = str($replicas)->explode('/')[1];
 | 
					 | 
				
			||||||
                            if ($running === $total) {
 | 
					 | 
				
			||||||
                                data_set($container, 'State.Status', 'running');
 | 
					 | 
				
			||||||
                                data_set($container, 'State.Health.Status', 'healthy');
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                data_set($container, 'State.Status', 'starting');
 | 
					 | 
				
			||||||
                                data_set($container, 'State.Health.Status', 'unhealthy');
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        return $container;
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            $databases = $this->server->databases();
 | 
					 | 
				
			||||||
            $services = $this->server->services()->get();
 | 
					 | 
				
			||||||
            $previews = $this->server->previews();
 | 
					 | 
				
			||||||
            $foundApplications = [];
 | 
					 | 
				
			||||||
            $foundApplicationPreviews = [];
 | 
					 | 
				
			||||||
            $foundDatabases = [];
 | 
					 | 
				
			||||||
            $foundServices = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            foreach ($containers as $container) {
 | 
					 | 
				
			||||||
                if ($this->server->isSwarm()) {
 | 
					 | 
				
			||||||
                    $labels = data_get($container, 'Spec.Labels');
 | 
					 | 
				
			||||||
                    $uuid = data_get($labels, 'coolify.name');
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    $labels = data_get($container, 'Config.Labels');
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $containerStatus = data_get($container, 'State.Status');
 | 
					 | 
				
			||||||
                $containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
 | 
					 | 
				
			||||||
                $containerStatus = "$containerStatus ($containerHealth)";
 | 
					 | 
				
			||||||
                $labels = Arr::undot(format_docker_labels_to_json($labels));
 | 
					 | 
				
			||||||
                $applicationId = data_get($labels, 'coolify.applicationId');
 | 
					 | 
				
			||||||
                if ($applicationId) {
 | 
					 | 
				
			||||||
                    $pullRequestId = data_get($labels, 'coolify.pullRequestId');
 | 
					 | 
				
			||||||
                    if ($pullRequestId) {
 | 
					 | 
				
			||||||
                        if (str($applicationId)->contains('-')) {
 | 
					 | 
				
			||||||
                            $applicationId = str($applicationId)->before('-');
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
 | 
					 | 
				
			||||||
                        if ($preview) {
 | 
					 | 
				
			||||||
                            $foundApplicationPreviews[] = $preview->id;
 | 
					 | 
				
			||||||
                            $statusFromDb = $preview->status;
 | 
					 | 
				
			||||||
                            if ($statusFromDb !== $containerStatus) {
 | 
					 | 
				
			||||||
                                $preview->update(['status' => $containerStatus]);
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            //Notify user that this container should not be there.
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        $application = $applications->where('id', $applicationId)->first();
 | 
					 | 
				
			||||||
                        if ($application) {
 | 
					 | 
				
			||||||
                            $foundApplications[] = $application->id;
 | 
					 | 
				
			||||||
                            $statusFromDb = $application->status;
 | 
					 | 
				
			||||||
                            if ($statusFromDb !== $containerStatus) {
 | 
					 | 
				
			||||||
                                $application->update(['status' => $containerStatus]);
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            //Notify user that this container should not be there.
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    $uuid = data_get($labels, 'com.docker.compose.service');
 | 
					 | 
				
			||||||
                    $type = data_get($labels, 'coolify.type');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if ($uuid) {
 | 
					 | 
				
			||||||
                        if ($type === 'service') {
 | 
					 | 
				
			||||||
                            $database_id = data_get($labels, 'coolify.service.subId');
 | 
					 | 
				
			||||||
                            if ($database_id) {
 | 
					 | 
				
			||||||
                                $service_db = ServiceDatabase::where('id', $database_id)->first();
 | 
					 | 
				
			||||||
                                if ($service_db) {
 | 
					 | 
				
			||||||
                                    $uuid = $service_db->service->uuid;
 | 
					 | 
				
			||||||
                                    $isPublic = data_get($service_db, 'is_public');
 | 
					 | 
				
			||||||
                                    if ($isPublic) {
 | 
					 | 
				
			||||||
                                        $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
 | 
					 | 
				
			||||||
                                            if ($this->server->isSwarm()) {
 | 
					 | 
				
			||||||
                                                return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
 | 
					 | 
				
			||||||
                                            } else {
 | 
					 | 
				
			||||||
                                                return data_get($value, 'Name') === "/$uuid-proxy";
 | 
					 | 
				
			||||||
                                            }
 | 
					 | 
				
			||||||
                                        })->first();
 | 
					 | 
				
			||||||
                                        if (!$foundTcpProxy) {
 | 
					 | 
				
			||||||
                                            StartDatabaseProxy::run($service_db);
 | 
					 | 
				
			||||||
                                            // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
 | 
					 | 
				
			||||||
                                        }
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            $database = $databases->where('uuid', $uuid)->first();
 | 
					 | 
				
			||||||
                            if ($database) {
 | 
					 | 
				
			||||||
                                $isPublic = data_get($database, 'is_public');
 | 
					 | 
				
			||||||
                                $foundDatabases[] = $database->id;
 | 
					 | 
				
			||||||
                                $statusFromDb = $database->status;
 | 
					 | 
				
			||||||
                                if ($statusFromDb !== $containerStatus) {
 | 
					 | 
				
			||||||
                                    $database->update(['status' => $containerStatus]);
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                                if ($isPublic) {
 | 
					 | 
				
			||||||
                                    $foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
 | 
					 | 
				
			||||||
                                        if ($this->server->isSwarm()) {
 | 
					 | 
				
			||||||
                                            return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
 | 
					 | 
				
			||||||
                                        } else {
 | 
					 | 
				
			||||||
                                            return data_get($value, 'Name') === "/$uuid-proxy";
 | 
					 | 
				
			||||||
                                        }
 | 
					 | 
				
			||||||
                                    })->first();
 | 
					 | 
				
			||||||
                                    if (!$foundTcpProxy) {
 | 
					 | 
				
			||||||
                                        StartDatabaseProxy::run($database);
 | 
					 | 
				
			||||||
                                        $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                // Notify user that this container should not be there.
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    if (data_get($container, 'Name') === '/coolify-db') {
 | 
					 | 
				
			||||||
                        $foundDatabases[] = 0;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $serviceLabelId = data_get($labels, 'coolify.serviceId');
 | 
					 | 
				
			||||||
                if ($serviceLabelId) {
 | 
					 | 
				
			||||||
                    $subType = data_get($labels, 'coolify.service.subType');
 | 
					 | 
				
			||||||
                    $subId = data_get($labels, 'coolify.service.subId');
 | 
					 | 
				
			||||||
                    $service = $services->where('id', $serviceLabelId)->first();
 | 
					 | 
				
			||||||
                    if (!$service) {
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    if ($subType === 'application') {
 | 
					 | 
				
			||||||
                        $service =  $service->applications()->where('id', $subId)->first();
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        $service =  $service->databases()->where('id', $subId)->first();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    if ($service) {
 | 
					 | 
				
			||||||
                        $foundServices[] = "$service->id-$service->name";
 | 
					 | 
				
			||||||
                        $statusFromDb = $service->status;
 | 
					 | 
				
			||||||
                        if ($statusFromDb !== $containerStatus) {
 | 
					 | 
				
			||||||
                            // ray('Updating status: ' . $containerStatus);
 | 
					 | 
				
			||||||
                            $service->update(['status' => $containerStatus]);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            $exitedServices = collect([]);
 | 
					 | 
				
			||||||
            foreach ($services as $service) {
 | 
					 | 
				
			||||||
                $apps = $service->applications()->get();
 | 
					 | 
				
			||||||
                $dbs = $service->databases()->get();
 | 
					 | 
				
			||||||
                foreach ($apps as $app) {
 | 
					 | 
				
			||||||
                    if (in_array("$app->id-$app->name", $foundServices)) {
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        $exitedServices->push($app);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                foreach ($dbs as $db) {
 | 
					 | 
				
			||||||
                    if (in_array("$db->id-$db->name", $foundServices)) {
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        $exitedServices->push($db);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            $exitedServices = $exitedServices->unique('id');
 | 
					 | 
				
			||||||
            foreach ($exitedServices as $exitedService) {
 | 
					 | 
				
			||||||
                if (str($exitedService->status)->startsWith('exited')) {
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $name = data_get($exitedService, 'name');
 | 
					 | 
				
			||||||
                $fqdn = data_get($exitedService, 'fqdn');
 | 
					 | 
				
			||||||
                $containerName = $name ? "$name, available at $fqdn" : $fqdn;
 | 
					 | 
				
			||||||
                $projectUuid = data_get($service, 'environment.project.uuid');
 | 
					 | 
				
			||||||
                $serviceUuid = data_get($service, 'uuid');
 | 
					 | 
				
			||||||
                $environmentName = data_get($service, 'environment.name');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if ($projectUuid && $serviceUuid && $environmentName) {
 | 
					 | 
				
			||||||
                    $url =  base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    $url = null;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
					 | 
				
			||||||
                $exitedService->update(['status' => 'exited']);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $notRunningApplications = $applications->pluck('id')->diff($foundApplications);
 | 
					 | 
				
			||||||
            foreach ($notRunningApplications as $applicationId) {
 | 
					 | 
				
			||||||
                $application = $applications->where('id', $applicationId)->first();
 | 
					 | 
				
			||||||
                if (str($application->status)->startsWith('exited')) {
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $application->update(['status' => 'exited']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $name = data_get($application, 'name');
 | 
					 | 
				
			||||||
                $fqdn = data_get($application, 'fqdn');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $containerName = $name ? "$name ($fqdn)" : $fqdn;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $projectUuid = data_get($application, 'environment.project.uuid');
 | 
					 | 
				
			||||||
                $applicationUuid = data_get($application, 'uuid');
 | 
					 | 
				
			||||||
                $environment = data_get($application, 'environment.name');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if ($projectUuid && $applicationUuid && $environment) {
 | 
					 | 
				
			||||||
                    $url =  base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    $url = null;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            $notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
 | 
					 | 
				
			||||||
            foreach ($notRunningApplicationPreviews as $previewId) {
 | 
					 | 
				
			||||||
                $preview = $previews->where('id', $previewId)->first();
 | 
					 | 
				
			||||||
                if (str($preview->status)->startsWith('exited')) {
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $preview->update(['status' => 'exited']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $name = data_get($preview, 'name');
 | 
					 | 
				
			||||||
                $fqdn = data_get($preview, 'fqdn');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $containerName = $name ? "$name ($fqdn)" : $fqdn;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $projectUuid = data_get($preview, 'application.environment.project.uuid');
 | 
					 | 
				
			||||||
                $environmentName = data_get($preview, 'application.environment.name');
 | 
					 | 
				
			||||||
                $applicationUuid = data_get($preview, 'application.uuid');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if ($projectUuid && $applicationUuid && $environmentName) {
 | 
					 | 
				
			||||||
                    $url =  base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    $url = null;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            $notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
 | 
					 | 
				
			||||||
            foreach ($notRunningDatabases as $database) {
 | 
					 | 
				
			||||||
                $database = $databases->where('id', $database)->first();
 | 
					 | 
				
			||||||
                if (str($database->status)->startsWith('exited')) {
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $database->update(['status' => 'exited']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $name = data_get($database, 'name');
 | 
					 | 
				
			||||||
                $fqdn = data_get($database, 'fqdn');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $containerName = $name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $projectUuid = data_get($database, 'environment.project.uuid');
 | 
					 | 
				
			||||||
                $environmentName = data_get($database, 'environment.name');
 | 
					 | 
				
			||||||
                $databaseUuid = data_get($database, 'uuid');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if ($projectUuid && $databaseUuid && $environmentName) {
 | 
					 | 
				
			||||||
                    $url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    $url = null;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Check if proxy is running
 | 
					 | 
				
			||||||
            $this->server->proxyType();
 | 
					 | 
				
			||||||
            $foundProxyContainer = $containers->filter(function ($value, $key) {
 | 
					 | 
				
			||||||
                if ($this->server->isSwarm()) {
 | 
					 | 
				
			||||||
                    return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    return data_get($value, 'Name') === '/coolify-proxy';
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            })->first();
 | 
					 | 
				
			||||||
            if (!$foundProxyContainer) {
 | 
					 | 
				
			||||||
                try {
 | 
					 | 
				
			||||||
                    $shouldStart = CheckProxy::run($this->server);
 | 
					 | 
				
			||||||
                    if ($shouldStart) {
 | 
					 | 
				
			||||||
                        StartProxy::run($this->server, false);
 | 
					 | 
				
			||||||
                        $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } catch (\Throwable $e) {
 | 
					 | 
				
			||||||
                    ray($e);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
 | 
					 | 
				
			||||||
                $this->server->save();
 | 
					 | 
				
			||||||
                $connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
 | 
					 | 
				
			||||||
                instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					 | 
				
			||||||
            send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
 | 
					 | 
				
			||||||
            ray($e->getMessage());
 | 
					 | 
				
			||||||
            return handleError($e);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -289,7 +289,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                    if ($this->backup->save_s3) {
 | 
					                    if ($this->backup->save_s3) {
 | 
				
			||||||
                        $this->upload_to_s3();
 | 
					                        $this->upload_to_s3();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    $this->team?->notify(new BackupSuccess($this->backup, $this->database));
 | 
					                    $this->team?->notify(new BackupSuccess($this->backup, $this->database, $database));
 | 
				
			||||||
                    $this->backup_log->update([
 | 
					                    $this->backup_log->update([
 | 
				
			||||||
                        'status' => 'success',
 | 
					                        'status' => 'success',
 | 
				
			||||||
                        'message' => $this->backup_output,
 | 
					                        'message' => $this->backup_output,
 | 
				
			||||||
@@ -305,8 +305,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                        ]);
 | 
					                        ]);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
 | 
					                    send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
 | 
				
			||||||
                    $this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
 | 
					                    $this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output, $database));
 | 
				
			||||||
                    throw $e;
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
@@ -319,10 +318,15 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
    private function backup_standalone_mongodb(string $databaseWithCollections): void
 | 
					    private function backup_standalone_mongodb(string $databaseWithCollections): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
 | 
					            ray($this->database->toArray());
 | 
				
			||||||
            $url = $this->database->get_db_url(useInternal: true);
 | 
					            $url = $this->database->get_db_url(useInternal: true);
 | 
				
			||||||
            if ($databaseWithCollections === 'all') {
 | 
					            if ($databaseWithCollections === 'all') {
 | 
				
			||||||
                $commands[] = "mkdir -p " . $this->backup_dir;
 | 
					                $commands[] = "mkdir -p " . $this->backup_dir;
 | 
				
			||||||
                $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location";
 | 
					                if (str($this->database->image)->startsWith('mongo:4.0')) {
 | 
				
			||||||
 | 
					                    $commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --archive > $this->backup_location";
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                if (str($databaseWithCollections)->contains(':')) {
 | 
					                if (str($databaseWithCollections)->contains(':')) {
 | 
				
			||||||
                    $databaseName = str($databaseWithCollections)->before(':');
 | 
					                    $databaseName = str($databaseWithCollections)->before(':');
 | 
				
			||||||
@@ -333,9 +337,17 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                $commands[] = "mkdir -p " . $this->backup_dir;
 | 
					                $commands[] = "mkdir -p " . $this->backup_dir;
 | 
				
			||||||
                if ($collectionsToExclude->count() === 0) {
 | 
					                if ($collectionsToExclude->count() === 0) {
 | 
				
			||||||
                    $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --archive > $this->backup_location";
 | 
					                    if (str($this->database->image)->startsWith('mongo:4.0')) {
 | 
				
			||||||
 | 
					                        $commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --archive > $this->backup_location";
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --archive > $this->backup_location";
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location";
 | 
					                    if (str($this->database->image)->startsWith('mongo:4.0')) {
 | 
				
			||||||
 | 
					                        $commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location";
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        $commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location";
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            $this->backup_output = instant_remote_process($commands, $this->server);
 | 
					            $this->backup_output = instant_remote_process($commands, $this->server);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,8 @@ class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncr
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public $timeout = 120;
 | 
					    public $timeout = 600;
 | 
				
			||||||
 | 
					    public $tries = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(private bool $force = false)
 | 
					    public function __construct(private bool $force = false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										56
									
								
								app/Jobs/PullSentinelImageJob.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								app/Jobs/PullSentinelImageJob.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Jobs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Actions\Server\StartSentinel;
 | 
				
			||||||
 | 
					use App\Models\Server;
 | 
				
			||||||
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			||||||
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
 | 
					use Illuminate\Queue\Middleware\WithoutOverlapping;
 | 
				
			||||||
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PullSentinelImageJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $timeout = 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function middleware(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return [(new WithoutOverlapping($this->server->uuid))];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function uniqueId(): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->server->uuid;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function __construct(public Server $server)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function handle(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $version = get_latest_sentinel_version();
 | 
				
			||||||
 | 
					            if (!$version) {
 | 
				
			||||||
 | 
					                ray('Failed to get latest Sentinel version');
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $local_version = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/version"'], $this->server, false);
 | 
				
			||||||
 | 
					            if (empty($local_version)) {
 | 
				
			||||||
 | 
					                $local_version = '0.0.0';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (version_compare($local_version, $version, '<')) {
 | 
				
			||||||
 | 
					                StartSentinel::run($this->server, $version, true);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ray('Sentinel image is up to date');
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            send_internal_notification('PullSentinelImageJob failed with: ' . $e->getMessage());
 | 
				
			||||||
 | 
					            ray($e->getMessage());
 | 
				
			||||||
 | 
					            throw $e;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -8,14 +8,13 @@ use App\Models\Server;
 | 
				
			|||||||
use App\Models\Application;
 | 
					use App\Models\Application;
 | 
				
			||||||
use App\Models\Service;
 | 
					use App\Models\Service;
 | 
				
			||||||
use App\Models\Team;
 | 
					use App\Models\Team;
 | 
				
			||||||
 | 
					use App\Notifications\ScheduledTask\TaskFailed;
 | 
				
			||||||
use Illuminate\Bus\Queueable;
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
					use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			||||||
use Illuminate\Queue\InteractsWithQueue;
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
 | 
					use Illuminate\Queue\Middleware\WithoutOverlapping;
 | 
				
			||||||
use Illuminate\Queue\SerializesModels;
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
use Illuminate\Support\Collection;
 | 
					 | 
				
			||||||
use Throwable;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ScheduledTaskJob implements ShouldQueue
 | 
					class ScheduledTaskJob implements ShouldQueue
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -77,8 +76,12 @@ class ScheduledTaskJob implements ShouldQueue
 | 
				
			|||||||
                        $this->containers[] = data_get($application, 'name') . '-' . data_get($this->resource, 'uuid');
 | 
					                        $this->containers[] = data_get($application, 'name') . '-' . data_get($this->resource, 'uuid');
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					                $this->resource->databases()->get()->each(function ($database) {
 | 
				
			||||||
 | 
					                    if (str(data_get($database, 'status'))->contains('running')) {
 | 
				
			||||||
 | 
					                        $this->containers[] = data_get($database, 'name') . '-' . data_get($this->resource, 'uuid');
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (count($this->containers) == 0) {
 | 
					            if (count($this->containers) == 0) {
 | 
				
			||||||
                throw new \Exception('ScheduledTaskJob failed: No containers running.');
 | 
					                throw new \Exception('ScheduledTaskJob failed: No containers running.');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -89,7 +92,7 @@ class ScheduledTaskJob implements ShouldQueue
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            foreach ($this->containers as $containerName) {
 | 
					            foreach ($this->containers as $containerName) {
 | 
				
			||||||
                if (count($this->containers) == 1 || str_starts_with($containerName, $this->task->container . '-' . $this->resource->uuid)) {
 | 
					                if (count($this->containers) == 1 || str_starts_with($containerName, $this->task->container . '-' . $this->resource->uuid)) {
 | 
				
			||||||
                    $cmd = 'sh -c "' . str_replace('"', '\"', $this->task->command)  . '"';
 | 
					                    $cmd = "sh -c '" . str_replace("'", "'\''", $this->task->command)   . "'";
 | 
				
			||||||
                    $exec = "docker exec {$containerName} {$cmd}";
 | 
					                    $exec = "docker exec {$containerName} {$cmd}";
 | 
				
			||||||
                    $this->task_output = instant_remote_process([$exec], $this->server, true);
 | 
					                    $this->task_output = instant_remote_process([$exec], $this->server, true);
 | 
				
			||||||
                    $this->task_log->update([
 | 
					                    $this->task_log->update([
 | 
				
			||||||
@@ -110,6 +113,7 @@ class ScheduledTaskJob implements ShouldQueue
 | 
				
			|||||||
                    'message' => $this->task_output ?? $e->getMessage(),
 | 
					                    'message' => $this->task_output ?? $e->getMessage(),
 | 
				
			||||||
                ]);
 | 
					                ]);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            $this->team?->notify(new TaskFailed($this->task, $e->getMessage()));
 | 
				
			||||||
            // send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage());
 | 
					            // send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage());
 | 
				
			||||||
            throw $e;
 | 
					            throw $e;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,11 +20,12 @@ class SendMessageToDiscordJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
     * @var int
 | 
					     * @var int
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public $tries = 5;
 | 
					    public $tries = 5;
 | 
				
			||||||
 | 
					    public $backoff = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * The maximum number of unhandled exceptions to allow before failing.
 | 
					     * The maximum number of unhandled exceptions to allow before failing.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public int $maxExceptions = 3;
 | 
					    public int $maxExceptions = 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(
 | 
					    public function __construct(
 | 
				
			||||||
        public string $text,
 | 
					        public string $text,
 | 
				
			||||||
@@ -40,7 +41,6 @@ class SendMessageToDiscordJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        $payload = [
 | 
					        $payload = [
 | 
				
			||||||
            'content' => $this->text,
 | 
					            'content' => $this->text,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
        ray($payload);
 | 
					 | 
				
			||||||
        Http::post($this->webhookUrl, $payload);
 | 
					        Http::post($this->webhookUrl, $payload);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,7 +57,7 @@ class SendMessageToTelegramJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $payload = [
 | 
					        $payload = [
 | 
				
			||||||
            'parse_mode' => 'markdown',
 | 
					            // 'parse_mode' => 'markdown',
 | 
				
			||||||
            'reply_markup' => json_encode([
 | 
					            'reply_markup' => json_encode([
 | 
				
			||||||
                'inline_keyboard' => [
 | 
					                'inline_keyboard' => [
 | 
				
			||||||
                    [...$inlineButtons],
 | 
					                    [...$inlineButtons],
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@ class ServerLimitCheckJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $servers = $this->team->servers;
 | 
					            $servers = $this->team->servers;
 | 
				
			||||||
            $servers_count = $servers->count();
 | 
					            $servers_count = $servers->count();
 | 
				
			||||||
            $limit = $this->team->limits['serverLimit'];
 | 
					            $limit = data_get($this->team->limits, 'serverLimit', 2);
 | 
				
			||||||
            $number_of_servers_to_disable = $servers_count - $limit;
 | 
					            $number_of_servers_to_disable = $servers_count - $limit;
 | 
				
			||||||
            ray('ServerLimitCheckJob', $this->team->uuid, $servers_count, $limit, $number_of_servers_to_disable);
 | 
					            ray('ServerLimitCheckJob', $this->team->uuid, $servers_count, $limit, $number_of_servers_to_disable);
 | 
				
			||||||
            if ($number_of_servers_to_disable > 0) {
 | 
					            if ($number_of_servers_to_disable > 0) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public int|string|null $disk_usage = null;
 | 
					    public int|string|null $disk_usage = null;
 | 
				
			||||||
    public $tries = 4;
 | 
					    public $tries = 3;
 | 
				
			||||||
    public function backoff(): int
 | 
					    public function backoff(): int
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return isDev() ? 1 : 3;
 | 
					        return isDev() ? 1 : 3;
 | 
				
			||||||
@@ -43,6 +43,10 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
            if ($this->server->isFunctional()) {
 | 
					            if ($this->server->isFunctional()) {
 | 
				
			||||||
                $this->cleanup(notify: false);
 | 
					                $this->cleanup(notify: false);
 | 
				
			||||||
 | 
					                $this->removeCoolifyYaml();
 | 
				
			||||||
 | 
					                if (config('coolify.is_sentinel_enabled')) {
 | 
				
			||||||
 | 
					                    $this->server->checkSentinel();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
 | 
					            send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
 | 
				
			||||||
@@ -50,6 +54,16 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
            return handleError($e);
 | 
					            return handleError($e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    private function removeCoolifyYaml()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // This will remote the coolify.yaml file from the server as it is not needed on cloud servers
 | 
				
			||||||
 | 
					        if (isCloud() && $this->server->id !== 0) {
 | 
				
			||||||
 | 
					            $file = $this->server->proxyPath() . "/dynamic/coolify.yaml";
 | 
				
			||||||
 | 
					            return instant_remote_process([
 | 
				
			||||||
 | 
					                "rm -f $file",
 | 
				
			||||||
 | 
					            ], $this->server, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function cleanup(bool $notify = false): void
 | 
					    public function cleanup(bool $notify = false): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->disk_usage = $this->server->getDiskUsage();
 | 
					        $this->disk_usage = $this->server->getDiskUsage();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,6 +52,9 @@ class Index extends Component
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        if (auth()->user()?->isMember() && auth()->user()->currentTeam()->show_boarding === true) {
 | 
				
			||||||
 | 
					            return redirect()->route('dashboard');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $this->privateKeyName = generate_random_name();
 | 
					        $this->privateKeyName = generate_random_name();
 | 
				
			||||||
        $this->remoteServerName = generate_random_name();
 | 
					        $this->remoteServerName = generate_random_name();
 | 
				
			||||||
        if (isDev()) {
 | 
					        if (isDev()) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@ class Discord extends Component
 | 
				
			|||||||
        'team.discord_notifications_deployments' => 'nullable|boolean',
 | 
					        'team.discord_notifications_deployments' => 'nullable|boolean',
 | 
				
			||||||
        'team.discord_notifications_status_changes' => 'nullable|boolean',
 | 
					        'team.discord_notifications_status_changes' => 'nullable|boolean',
 | 
				
			||||||
        'team.discord_notifications_database_backups' => 'nullable|boolean',
 | 
					        'team.discord_notifications_database_backups' => 'nullable|boolean',
 | 
				
			||||||
 | 
					        'team.discord_notifications_scheduled_tasks' => 'nullable|boolean',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    protected $validationAttributes = [
 | 
					    protected $validationAttributes = [
 | 
				
			||||||
        'team.discord_webhook_url' => 'Discord Webhook',
 | 
					        'team.discord_webhook_url' => 'Discord Webhook',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,7 @@ class Email extends Component
 | 
				
			|||||||
        'team.smtp_notifications_deployments' => 'nullable|boolean',
 | 
					        'team.smtp_notifications_deployments' => 'nullable|boolean',
 | 
				
			||||||
        'team.smtp_notifications_status_changes' => 'nullable|boolean',
 | 
					        'team.smtp_notifications_status_changes' => 'nullable|boolean',
 | 
				
			||||||
        'team.smtp_notifications_database_backups' => 'nullable|boolean',
 | 
					        'team.smtp_notifications_database_backups' => 'nullable|boolean',
 | 
				
			||||||
 | 
					        'team.smtp_notifications_scheduled_tasks' => 'nullable|boolean',
 | 
				
			||||||
        'team.use_instance_email_settings' => 'boolean',
 | 
					        'team.use_instance_email_settings' => 'boolean',
 | 
				
			||||||
        'team.resend_enabled' => 'nullable|boolean',
 | 
					        'team.resend_enabled' => 'nullable|boolean',
 | 
				
			||||||
        'team.resend_api_key' => 'nullable',
 | 
					        'team.resend_api_key' => 'nullable',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,10 +18,12 @@ class Telegram extends Component
 | 
				
			|||||||
        'team.telegram_notifications_deployments' => 'nullable|boolean',
 | 
					        'team.telegram_notifications_deployments' => 'nullable|boolean',
 | 
				
			||||||
        'team.telegram_notifications_status_changes' => 'nullable|boolean',
 | 
					        'team.telegram_notifications_status_changes' => 'nullable|boolean',
 | 
				
			||||||
        'team.telegram_notifications_database_backups' => 'nullable|boolean',
 | 
					        'team.telegram_notifications_database_backups' => 'nullable|boolean',
 | 
				
			||||||
 | 
					        'team.telegram_notifications_scheduled_tasks' => 'nullable|boolean',
 | 
				
			||||||
        'team.telegram_notifications_test_message_thread_id' => 'nullable|string',
 | 
					        'team.telegram_notifications_test_message_thread_id' => 'nullable|string',
 | 
				
			||||||
        'team.telegram_notifications_deployments_message_thread_id' => 'nullable|string',
 | 
					        'team.telegram_notifications_deployments_message_thread_id' => 'nullable|string',
 | 
				
			||||||
        'team.telegram_notifications_status_changes_message_thread_id' => 'nullable|string',
 | 
					        'team.telegram_notifications_status_changes_message_thread_id' => 'nullable|string',
 | 
				
			||||||
        'team.telegram_notifications_database_backups_message_thread_id' => 'nullable|string',
 | 
					        'team.telegram_notifications_database_backups_message_thread_id' => 'nullable|string',
 | 
				
			||||||
 | 
					        'team.telegram_notifications_scheduled_tasks_thread_id' => 'nullable|string',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    protected $validationAttributes = [
 | 
					    protected $validationAttributes = [
 | 
				
			||||||
        'team.telegram_token' => 'Token',
 | 
					        'team.telegram_token' => 'Token',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ class Advanced extends Component
 | 
				
			|||||||
        'application.settings.is_gpu_enabled' => 'boolean|required',
 | 
					        'application.settings.is_gpu_enabled' => 'boolean|required',
 | 
				
			||||||
        'application.settings.is_build_server_enabled' => 'boolean|required',
 | 
					        'application.settings.is_build_server_enabled' => 'boolean|required',
 | 
				
			||||||
        'application.settings.is_consistent_container_name_enabled' => 'boolean|required',
 | 
					        'application.settings.is_consistent_container_name_enabled' => 'boolean|required',
 | 
				
			||||||
 | 
					        'application.settings.custom_internal_name' => 'string|nullable',
 | 
				
			||||||
        'application.settings.is_gzip_enabled' => 'boolean|required',
 | 
					        'application.settings.is_gzip_enabled' => 'boolean|required',
 | 
				
			||||||
        'application.settings.is_stripprefix_enabled' => 'boolean|required',
 | 
					        'application.settings.is_stripprefix_enabled' => 'boolean|required',
 | 
				
			||||||
        'application.settings.gpu_driver' => 'string|required',
 | 
					        'application.settings.gpu_driver' => 'string|required',
 | 
				
			||||||
@@ -30,7 +31,8 @@ class Advanced extends Component
 | 
				
			|||||||
        'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
 | 
					        'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
 | 
				
			||||||
        'application.settings.connect_to_docker_network' => 'boolean|required',
 | 
					        'application.settings.connect_to_docker_network' => 'boolean|required',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    public function mount() {
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        $this->is_force_https_enabled = $this->application->isForceHttpsEnabled();
 | 
					        $this->is_force_https_enabled = $this->application->isForceHttpsEnabled();
 | 
				
			||||||
        $this->is_gzip_enabled = $this->application->isGzipEnabled();
 | 
					        $this->is_gzip_enabled = $this->application->isGzipEnabled();
 | 
				
			||||||
        $this->is_stripprefix_enabled = $this->application->isStripprefixEnabled();
 | 
					        $this->is_stripprefix_enabled = $this->application->isStripprefixEnabled();
 | 
				
			||||||
@@ -65,7 +67,8 @@ class Advanced extends Component
 | 
				
			|||||||
        $this->dispatch('success', 'Settings saved.');
 | 
					        $this->dispatch('success', 'Settings saved.');
 | 
				
			||||||
        $this->dispatch('configurationChanged');
 | 
					        $this->dispatch('configurationChanged');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function submit() {
 | 
					    public function submit()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        if ($this->application->settings->gpu_count && $this->application->settings->gpu_device_ids) {
 | 
					        if ($this->application->settings->gpu_count && $this->application->settings->gpu_device_ids) {
 | 
				
			||||||
            $this->dispatch('error', 'You cannot set both GPU count and GPU device IDs.');
 | 
					            $this->dispatch('error', 'You cannot set both GPU count and GPU device IDs.');
 | 
				
			||||||
            $this->application->settings->gpu_count = null;
 | 
					            $this->application->settings->gpu_count = null;
 | 
				
			||||||
@@ -76,6 +79,16 @@ class Advanced extends Component
 | 
				
			|||||||
        $this->application->settings->save();
 | 
					        $this->application->settings->save();
 | 
				
			||||||
        $this->dispatch('success', 'Settings saved.');
 | 
					        $this->dispatch('success', 'Settings saved.');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function saveCustomName()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (isset($this->application->settings->custom_internal_name)) {
 | 
				
			||||||
 | 
					            $this->application->settings->custom_internal_name = str($this->application->settings->custom_internal_name)->slug()->value();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->application->settings->custom_internal_name = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $this->application->settings->save();
 | 
				
			||||||
 | 
					        $this->dispatch('success', 'Custom name saved.');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function render()
 | 
					    public function render()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return view('livewire.project.application.advanced');
 | 
					        return view('livewire.project.application.advanced');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ class General extends Component
 | 
				
			|||||||
    public ?string $git_commit_sha = null;
 | 
					    public ?string $git_commit_sha = null;
 | 
				
			||||||
    public string $build_pack;
 | 
					    public string $build_pack;
 | 
				
			||||||
    public ?string $ports_exposes = null;
 | 
					    public ?string $ports_exposes = null;
 | 
				
			||||||
 | 
					    public bool $is_container_label_escape_enabled = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public $customLabels;
 | 
					    public $customLabels;
 | 
				
			||||||
    public bool $labelsChanged = false;
 | 
					    public bool $labelsChanged = false;
 | 
				
			||||||
@@ -30,7 +31,7 @@ class General extends Component
 | 
				
			|||||||
    public ?string $initialDockerComposeLocation = null;
 | 
					    public ?string $initialDockerComposeLocation = null;
 | 
				
			||||||
    public ?string $initialDockerComposePrLocation = null;
 | 
					    public ?string $initialDockerComposePrLocation = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public $parsedServices = [];
 | 
					    public null|Collection $parsedServices;
 | 
				
			||||||
    public $parsedServiceDomains = [];
 | 
					    public $parsedServiceDomains = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $listeners = [
 | 
					    protected $listeners = [
 | 
				
			||||||
@@ -74,6 +75,7 @@ class General extends Component
 | 
				
			|||||||
        'application.post_deployment_command_container' => 'nullable',
 | 
					        'application.post_deployment_command_container' => 'nullable',
 | 
				
			||||||
        'application.settings.is_static' => 'boolean|required',
 | 
					        'application.settings.is_static' => 'boolean|required',
 | 
				
			||||||
        'application.settings.is_build_server_enabled' => 'boolean|required',
 | 
					        'application.settings.is_build_server_enabled' => 'boolean|required',
 | 
				
			||||||
 | 
					        'application.settings.is_container_label_escape_enabled' => 'boolean|required',
 | 
				
			||||||
        'application.watch_paths' => 'nullable',
 | 
					        'application.watch_paths' => 'nullable',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    protected $validationAttributes = [
 | 
					    protected $validationAttributes = [
 | 
				
			||||||
@@ -109,12 +111,17 @@ class General extends Component
 | 
				
			|||||||
        'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
 | 
					        'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
 | 
				
			||||||
        'application.settings.is_static' => 'Is static',
 | 
					        'application.settings.is_static' => 'Is static',
 | 
				
			||||||
        'application.settings.is_build_server_enabled' => 'Is build server enabled',
 | 
					        'application.settings.is_build_server_enabled' => 'Is build server enabled',
 | 
				
			||||||
 | 
					        'application.settings.is_container_label_escape_enabled' => 'Is container label escape enabled',
 | 
				
			||||||
        'application.watch_paths' => 'Watch paths',
 | 
					        'application.watch_paths' => 'Watch paths',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $this->parsedServices = $this->application->parseCompose();
 | 
					            $this->parsedServices = $this->application->parseCompose();
 | 
				
			||||||
 | 
					            if (is_null($this->parsedServices) || empty($this->parsedServices)) {
 | 
				
			||||||
 | 
					                $this->dispatch('error', "Failed to parse your docker-compose file. Please check the syntax and try again.");
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            $this->dispatch('error', $e->getMessage());
 | 
					            $this->dispatch('error', $e->getMessage());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -124,6 +131,7 @@ class General extends Component
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
 | 
					        $this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
 | 
				
			||||||
        $this->ports_exposes = $this->application->ports_exposes;
 | 
					        $this->ports_exposes = $this->application->ports_exposes;
 | 
				
			||||||
 | 
					        $this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
 | 
				
			||||||
        $this->customLabels = $this->application->parseContainerLabels();
 | 
					        $this->customLabels = $this->application->parseContainerLabels();
 | 
				
			||||||
        if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
 | 
					        if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
 | 
				
			||||||
            $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
 | 
					            $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
 | 
				
			||||||
@@ -145,7 +153,7 @@ class General extends Component
 | 
				
			|||||||
        $this->application->settings->save();
 | 
					        $this->application->settings->save();
 | 
				
			||||||
        $this->dispatch('success', 'Settings saved.');
 | 
					        $this->dispatch('success', 'Settings saved.');
 | 
				
			||||||
        $this->application->refresh();
 | 
					        $this->application->refresh();
 | 
				
			||||||
        if ($this->ports_exposes !== $this->application->ports_exposes) {
 | 
					        if ($this->ports_exposes !== $this->application->ports_exposes || $this->is_container_label_escape_enabled !== $this->application->settings->is_container_label_escape_enabled) {
 | 
				
			||||||
            $this->resetDefaultLabels(false);
 | 
					            $this->resetDefaultLabels(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -156,6 +164,10 @@ class General extends Component
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
 | 
					            ['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
 | 
				
			||||||
 | 
					            if (is_null($this->parsedServices)) {
 | 
				
			||||||
 | 
					                $this->dispatch('error', "Failed to parse your docker-compose file. Please check the syntax and try again.");
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            $compose = $this->application->parseCompose();
 | 
					            $compose = $this->application->parseCompose();
 | 
				
			||||||
            $services = data_get($compose, 'services');
 | 
					            $services = data_get($compose, 'services');
 | 
				
			||||||
            if ($services) {
 | 
					            if ($services) {
 | 
				
			||||||
@@ -186,6 +198,7 @@ class General extends Component
 | 
				
			|||||||
            $this->dispatch('success', 'Docker compose file loaded.');
 | 
					            $this->dispatch('success', 'Docker compose file loaded.');
 | 
				
			||||||
            $this->dispatch('compose_loaded');
 | 
					            $this->dispatch('compose_loaded');
 | 
				
			||||||
            $this->dispatch('refresh_storages');
 | 
					            $this->dispatch('refresh_storages');
 | 
				
			||||||
 | 
					            $this->dispatch('refreshEnvs');
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            $this->application->docker_compose_location = $this->initialDockerComposeLocation;
 | 
					            $this->application->docker_compose_location = $this->initialDockerComposeLocation;
 | 
				
			||||||
            $this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
 | 
					            $this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
 | 
				
			||||||
@@ -203,6 +216,9 @@ class General extends Component
 | 
				
			|||||||
        $this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
 | 
					        $this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
 | 
				
			||||||
        $this->application->save();
 | 
					        $this->application->save();
 | 
				
			||||||
        $this->dispatch('success', 'Domain generated.');
 | 
					        $this->dispatch('success', 'Domain generated.');
 | 
				
			||||||
 | 
					        if ($this->application->build_pack === 'dockercompose') {
 | 
				
			||||||
 | 
					            $this->loadComposeFile();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        return $domain;
 | 
					        return $domain;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function updatedApplicationBaseDirectory()
 | 
					    public function updatedApplicationBaseDirectory()
 | 
				
			||||||
@@ -254,12 +270,14 @@ class General extends Component
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    public function resetDefaultLabels()
 | 
					    public function resetDefaultLabels()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        ray('resetDefaultLabels');
 | 
					 | 
				
			||||||
        $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
 | 
					        $this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
 | 
				
			||||||
        $this->ports_exposes = $this->application->ports_exposes;
 | 
					        $this->ports_exposes = $this->application->ports_exposes;
 | 
				
			||||||
 | 
					        $this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
 | 
				
			||||||
        $this->application->custom_labels = base64_encode($this->customLabels);
 | 
					        $this->application->custom_labels = base64_encode($this->customLabels);
 | 
				
			||||||
        $this->application->save();
 | 
					        $this->application->save();
 | 
				
			||||||
 | 
					        if ($this->application->build_pack === 'dockercompose') {
 | 
				
			||||||
 | 
					            $this->loadComposeFile();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function checkFqdns($showToaster = true)
 | 
					    public function checkFqdns($showToaster = true)
 | 
				
			||||||
@@ -298,10 +316,13 @@ class General extends Component
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) {
 | 
					            if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) {
 | 
				
			||||||
                $this->loadComposeFile();
 | 
					                $compose_return = $this->loadComposeFile();
 | 
				
			||||||
 | 
					                if ($compose_return instanceof \Livewire\Features\SupportEvents\Event) {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            $this->validate();
 | 
					            $this->validate();
 | 
				
			||||||
            if ($this->ports_exposes !== $this->application->ports_exposes) {
 | 
					            if ($this->ports_exposes !== $this->application->ports_exposes || $this->is_container_label_escape_enabled !== $this->application->settings->is_container_label_escape_enabled) {
 | 
				
			||||||
                $this->resetDefaultLabels();
 | 
					                $this->resetDefaultLabels();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (data_get($this->application, 'build_pack') === 'dockerimage') {
 | 
					            if (data_get($this->application, 'build_pack') === 'dockerimage') {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,8 +3,8 @@
 | 
				
			|||||||
namespace App\Livewire\Project\Application;
 | 
					namespace App\Livewire\Project\Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Actions\Application\StopApplication;
 | 
					use App\Actions\Application\StopApplication;
 | 
				
			||||||
 | 
					use App\Actions\Docker\GetContainersStatus;
 | 
				
			||||||
use App\Events\ApplicationStatusChanged;
 | 
					use App\Events\ApplicationStatusChanged;
 | 
				
			||||||
use App\Jobs\ComplexContainerStatusJob;
 | 
					 | 
				
			||||||
use App\Jobs\ContainerStatusJob;
 | 
					use App\Jobs\ContainerStatusJob;
 | 
				
			||||||
use App\Jobs\ServerStatusJob;
 | 
					use App\Jobs\ServerStatusJob;
 | 
				
			||||||
use App\Models\Application;
 | 
					use App\Models\Application;
 | 
				
			||||||
@@ -14,6 +14,8 @@ use Visus\Cuid2\Cuid2;
 | 
				
			|||||||
class Heading extends Component
 | 
					class Heading extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public Application $application;
 | 
					    public Application $application;
 | 
				
			||||||
 | 
					    public ?string $lastDeploymentInfo = null;
 | 
				
			||||||
 | 
					    public ?string $lastDeploymentLink = null;
 | 
				
			||||||
    public array $parameters;
 | 
					    public array $parameters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected string $deploymentUuid;
 | 
					    protected string $deploymentUuid;
 | 
				
			||||||
@@ -28,18 +30,23 @@ class Heading extends Component
 | 
				
			|||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->parameters = get_route_parameters();
 | 
					        $this->parameters = get_route_parameters();
 | 
				
			||||||
 | 
					        $lastDeployment = $this->application->get_last_successful_deployment();
 | 
				
			||||||
 | 
					        $this->lastDeploymentInfo = data_get_str($lastDeployment, 'commit')->limit(7) . ' ' . data_get($lastDeployment, 'commit_message');
 | 
				
			||||||
 | 
					        $this->lastDeploymentLink = $this->application->gitCommitLink(data_get($lastDeployment, 'commit'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function check_status($showNotification = false)
 | 
					    public function check_status($showNotification = false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->application->destination->server->isFunctional()) {
 | 
					        if ($this->application->destination->server->isFunctional()) {
 | 
				
			||||||
            dispatch(new ContainerStatusJob($this->application->destination->server));
 | 
					            GetContainersStatus::dispatch($this->application->destination->server);
 | 
				
			||||||
 | 
					            // dispatch(new ContainerStatusJob($this->application->destination->server));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            dispatch(new ServerStatusJob($this->application->destination->server));
 | 
					            dispatch(new ServerStatusJob($this->application->destination->server));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($showNotification) $this->dispatch('success', "Success", "Application status updated.");
 | 
					        if ($showNotification) $this->dispatch('success', "Success", "Application status updated.");
 | 
				
			||||||
        $this->dispatch('configurationChanged');
 | 
					        // Removed because it caused flickering
 | 
				
			||||||
 | 
					        // $this->dispatch('configurationChanged');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function force_deploy_without_cache()
 | 
					    public function force_deploy_without_cache()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,11 +35,6 @@ class Execution extends Component
 | 
				
			|||||||
        $this->executions = $executions;
 | 
					        $this->executions = $executions;
 | 
				
			||||||
        $this->s3s = currentTeam()->s3s;
 | 
					        $this->s3s = currentTeam()->s3s;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function cleanupFailed()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $this->backup->executions()->where('status', 'failed')->delete();
 | 
					 | 
				
			||||||
        $this->dispatch('refreshBackupExecutions');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    public function render()
 | 
					    public function render()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return view('livewire.project.database.backup.execution');
 | 
					        return view('livewire.project.database.backup.execution');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@ class BackupEdit extends Component
 | 
				
			|||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->parameters = get_route_parameters();
 | 
					        $this->parameters = get_route_parameters();
 | 
				
			||||||
        if (is_null($this->backup->s3_storage_id)) {
 | 
					        if (is_null(data_get($this->backup, 's3_storage_id'))) {
 | 
				
			||||||
            $this->backup->s3_storage_id = 'default';
 | 
					            $this->backup->s3_storage_id = 'default';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,9 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Livewire\Project\Database;
 | 
					namespace App\Livewire\Project\Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use Illuminate\Support\Facades\Storage;
 | 
					 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
use Symfony\Component\HttpFoundation\StreamedResponse;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BackupExecutions extends Component
 | 
					class BackupExecutions extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -16,11 +14,15 @@ class BackupExecutions extends Component
 | 
				
			|||||||
        $userId = auth()->user()->id;
 | 
					        $userId = auth()->user()->id;
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            "echo-private:team.{$userId},BackupCreated" => 'refreshBackupExecutions',
 | 
					            "echo-private:team.{$userId},BackupCreated" => 'refreshBackupExecutions',
 | 
				
			||||||
            "refreshBackupExecutions",
 | 
					 | 
				
			||||||
            "deleteBackup"
 | 
					            "deleteBackup"
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function cleanupFailed()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->backup?->executions()->where('status', 'failed')->delete();
 | 
				
			||||||
 | 
					        $this->refreshBackupExecutions();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function deleteBackup($exeuctionId)
 | 
					    public function deleteBackup($exeuctionId)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $execution = $this->backup->executions()->where('id', $exeuctionId)->first();
 | 
					        $execution = $this->backup->executions()->where('id', $exeuctionId)->first();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ use App\Actions\Database\StartMysql;
 | 
				
			|||||||
use App\Actions\Database\StartPostgresql;
 | 
					use App\Actions\Database\StartPostgresql;
 | 
				
			||||||
use App\Actions\Database\StartRedis;
 | 
					use App\Actions\Database\StartRedis;
 | 
				
			||||||
use App\Actions\Database\StopDatabase;
 | 
					use App\Actions\Database\StopDatabase;
 | 
				
			||||||
 | 
					use App\Actions\Docker\GetContainersStatus;
 | 
				
			||||||
use App\Jobs\ContainerStatusJob;
 | 
					use App\Jobs\ContainerStatusJob;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -44,7 +45,8 @@ class Heading extends Component
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function check_status($showNotification = false)
 | 
					    public function check_status($showNotification = false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        dispatch_sync(new ContainerStatusJob($this->database->destination->server));
 | 
					        GetContainersStatus::run($this->database->destination->server);
 | 
				
			||||||
 | 
					        // dispatch_sync(new ContainerStatusJob($this->database->destination->server));
 | 
				
			||||||
        $this->database->refresh();
 | 
					        $this->database->refresh();
 | 
				
			||||||
        if ($showNotification) $this->dispatch('success', 'Database status updated.');
 | 
					        if ($showNotification) $this->dispatch('success', 'Database status updated.');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ class Import extends Component
 | 
				
			|||||||
    public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB';
 | 
					    public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB';
 | 
				
			||||||
    public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE';
 | 
					    public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE';
 | 
				
			||||||
    public string $mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE';
 | 
					    public string $mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE';
 | 
				
			||||||
 | 
					    public string $mongodbRestoreCommand = 'mongorestore --authenticationDatabase=admin --username $MONGO_INITDB_ROOT_USERNAME --password $MONGO_INITDB_ROOT_PASSWORD --uri mongodb://localhost:27017 --gzip --archive=';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function getListeners()
 | 
					    public function getListeners()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -62,8 +63,7 @@ class Import extends Component
 | 
				
			|||||||
            $this->resource->getMorphClass() == 'App\Models\StandaloneRedis' ||
 | 
					            $this->resource->getMorphClass() == 'App\Models\StandaloneRedis' ||
 | 
				
			||||||
            $this->resource->getMorphClass() == 'App\Models\StandaloneKeydb' ||
 | 
					            $this->resource->getMorphClass() == 'App\Models\StandaloneKeydb' ||
 | 
				
			||||||
            $this->resource->getMorphClass() == 'App\Models\StandaloneDragonfly' ||
 | 
					            $this->resource->getMorphClass() == 'App\Models\StandaloneDragonfly' ||
 | 
				
			||||||
            $this->resource->getMorphClass() == 'App\Models\StandaloneClickhouse' ||
 | 
					            $this->resource->getMorphClass() == 'App\Models\StandaloneClickhouse'
 | 
				
			||||||
            $this->resource->getMorphClass() == 'App\Models\StandaloneMongodb'
 | 
					 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            $this->unsupported = true;
 | 
					            $this->unsupported = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -101,6 +101,10 @@ class Import extends Component
 | 
				
			|||||||
                    $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->postgresqlRestoreCommand} {$tmpPath}'";
 | 
					                    $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->postgresqlRestoreCommand} {$tmpPath}'";
 | 
				
			||||||
                    $this->importCommands[] = "rm {$tmpPath}";
 | 
					                    $this->importCommands[] = "rm {$tmpPath}";
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
					                case 'App\Models\StandaloneMongodb':
 | 
				
			||||||
 | 
					                    $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->mongodbRestoreCommand}{$tmpPath}'";
 | 
				
			||||||
 | 
					                    $this->importCommands[] = "rm {$tmpPath}";
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $this->importCommands[] = "docker exec {$this->container} sh -c 'rm {$tmpPath}'";
 | 
					            $this->importCommands[] = "docker exec {$this->container} sh -c 'rm {$tmpPath}'";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -150,7 +150,7 @@ class GithubPrivateRepository extends Component
 | 
				
			|||||||
                'repository_project_id' => $this->selected_repository_id,
 | 
					                'repository_project_id' => $this->selected_repository_id,
 | 
				
			||||||
                'git_repository' => "{$this->selected_repository_owner}/{$this->selected_repository_repo}",
 | 
					                'git_repository' => "{$this->selected_repository_owner}/{$this->selected_repository_repo}",
 | 
				
			||||||
                'git_branch' => $this->selected_branch_name,
 | 
					                'git_branch' => $this->selected_branch_name,
 | 
				
			||||||
                'build_pack' => 'nixpacks',
 | 
					                'build_pack' => $this->build_pack,
 | 
				
			||||||
                'ports_exposes' => $this->port,
 | 
					                'ports_exposes' => $this->port,
 | 
				
			||||||
                'publish_directory' => $this->publish_directory,
 | 
					                'publish_directory' => $this->publish_directory,
 | 
				
			||||||
                'environment_id' => $environment->id,
 | 
					                'environment_id' => $environment->id,
 | 
				
			||||||
@@ -162,6 +162,9 @@ class GithubPrivateRepository extends Component
 | 
				
			|||||||
            $application->settings->is_static = $this->is_static;
 | 
					            $application->settings->is_static = $this->is_static;
 | 
				
			||||||
            $application->settings->save();
 | 
					            $application->settings->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
 | 
				
			||||||
 | 
					                $application->health_check_enabled = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            $fqdn = generateFqdn($destination->server, $application->uuid);
 | 
					            $fqdn = generateFqdn($destination->server, $application->uuid);
 | 
				
			||||||
            $application->fqdn = $fqdn;
 | 
					            $application->fqdn = $fqdn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ class GithubPrivateRepositoryDeployKey extends Component
 | 
				
			|||||||
    public $current_step = 'private_keys';
 | 
					    public $current_step = 'private_keys';
 | 
				
			||||||
    public $parameters;
 | 
					    public $parameters;
 | 
				
			||||||
    public $query;
 | 
					    public $query;
 | 
				
			||||||
    public $private_keys =[];
 | 
					    public $private_keys = [];
 | 
				
			||||||
    public int $private_key_id;
 | 
					    public int $private_key_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public int $port = 3000;
 | 
					    public int $port = 3000;
 | 
				
			||||||
@@ -125,7 +125,7 @@ class GithubPrivateRepositoryDeployKey extends Component
 | 
				
			|||||||
                    'name' => generate_random_name(),
 | 
					                    'name' => generate_random_name(),
 | 
				
			||||||
                    'git_repository' => $this->git_repository,
 | 
					                    'git_repository' => $this->git_repository,
 | 
				
			||||||
                    'git_branch' => $this->branch,
 | 
					                    'git_branch' => $this->branch,
 | 
				
			||||||
                    'build_pack' => 'nixpacks',
 | 
					                    'build_pack' => $this->build_pack,
 | 
				
			||||||
                    'ports_exposes' => $this->port,
 | 
					                    'ports_exposes' => $this->port,
 | 
				
			||||||
                    'publish_directory' => $this->publish_directory,
 | 
					                    'publish_directory' => $this->publish_directory,
 | 
				
			||||||
                    'environment_id' => $environment->id,
 | 
					                    'environment_id' => $environment->id,
 | 
				
			||||||
@@ -138,7 +138,7 @@ class GithubPrivateRepositoryDeployKey extends Component
 | 
				
			|||||||
                    'name' => generate_random_name(),
 | 
					                    'name' => generate_random_name(),
 | 
				
			||||||
                    'git_repository' => $this->git_repository,
 | 
					                    'git_repository' => $this->git_repository,
 | 
				
			||||||
                    'git_branch' => $this->branch,
 | 
					                    'git_branch' => $this->branch,
 | 
				
			||||||
                    'build_pack' => 'nixpacks',
 | 
					                    'build_pack' => $this->build_pack,
 | 
				
			||||||
                    'ports_exposes' => $this->port,
 | 
					                    'ports_exposes' => $this->port,
 | 
				
			||||||
                    'publish_directory' => $this->publish_directory,
 | 
					                    'publish_directory' => $this->publish_directory,
 | 
				
			||||||
                    'environment_id' => $environment->id,
 | 
					                    'environment_id' => $environment->id,
 | 
				
			||||||
@@ -149,7 +149,9 @@ class GithubPrivateRepositoryDeployKey extends Component
 | 
				
			|||||||
                    'source_type' => $this->git_source->getMorphClass()
 | 
					                    'source_type' => $this->git_source->getMorphClass()
 | 
				
			||||||
                ];
 | 
					                ];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
 | 
				
			||||||
 | 
					                $application_init['health_check_enabled'] = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            $application = Application::create($application_init);
 | 
					            $application = Application::create($application_init);
 | 
				
			||||||
            $application->settings->is_static = $this->is_static;
 | 
					            $application->settings->is_static = $this->is_static;
 | 
				
			||||||
            $application->settings->save();
 | 
					            $application->settings->save();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,6 +94,18 @@ class PublicGitRepository extends Component
 | 
				
			|||||||
                $repository = str($this->repository_url)->after(':')->before('.git');
 | 
					                $repository = str($this->repository_url)->after(':')->before('.git');
 | 
				
			||||||
                $this->repository_url = 'https://' . str($github_instance) . '/' . $repository;
 | 
					                $this->repository_url = 'https://' . str($github_instance) . '/' . $repository;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if (
 | 
				
			||||||
 | 
					                (str($this->repository_url)->startsWith('https://') ||
 | 
				
			||||||
 | 
					                    str($this->repository_url)->startsWith('http://')) &&
 | 
				
			||||||
 | 
					                !str($this->repository_url)->endsWith('.git') &&
 | 
				
			||||||
 | 
					                (!str($this->repository_url)->contains('github.com') ||
 | 
				
			||||||
 | 
					                    !str($this->repository_url)->contains('git.sr.ht'))
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
 | 
					                $this->repository_url = $this->repository_url . '.git';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (str($this->repository_url)->contains('github.com')) {
 | 
				
			||||||
 | 
					                $this->repository_url = str($this->repository_url)->before('.git')->value();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -170,7 +182,6 @@ class PublicGitRepository extends Component
 | 
				
			|||||||
                    'name' => generate_random_name(),
 | 
					                    'name' => generate_random_name(),
 | 
				
			||||||
                    'git_repository' => $this->git_repository,
 | 
					                    'git_repository' => $this->git_repository,
 | 
				
			||||||
                    'git_branch' => $this->git_branch,
 | 
					                    'git_branch' => $this->git_branch,
 | 
				
			||||||
                    'build_pack' => 'nixpacks',
 | 
					 | 
				
			||||||
                    'ports_exposes' => $this->port,
 | 
					                    'ports_exposes' => $this->port,
 | 
				
			||||||
                    'publish_directory' => $this->publish_directory,
 | 
					                    'publish_directory' => $this->publish_directory,
 | 
				
			||||||
                    'environment_id' => $environment->id,
 | 
					                    'environment_id' => $environment->id,
 | 
				
			||||||
@@ -183,7 +194,6 @@ class PublicGitRepository extends Component
 | 
				
			|||||||
                    'name' => generate_application_name($this->git_repository, $this->git_branch),
 | 
					                    'name' => generate_application_name($this->git_repository, $this->git_branch),
 | 
				
			||||||
                    'git_repository' => $this->git_repository,
 | 
					                    'git_repository' => $this->git_repository,
 | 
				
			||||||
                    'git_branch' => $this->git_branch,
 | 
					                    'git_branch' => $this->git_branch,
 | 
				
			||||||
                    'build_pack' => 'nixpacks',
 | 
					 | 
				
			||||||
                    'ports_exposes' => $this->port,
 | 
					                    'ports_exposes' => $this->port,
 | 
				
			||||||
                    'publish_directory' => $this->publish_directory,
 | 
					                    'publish_directory' => $this->publish_directory,
 | 
				
			||||||
                    'environment_id' => $environment->id,
 | 
					                    'environment_id' => $environment->id,
 | 
				
			||||||
@@ -195,7 +205,9 @@ class PublicGitRepository extends Component
 | 
				
			|||||||
                ];
 | 
					                ];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
 | 
				
			||||||
 | 
					                $application_init['health_check_enabled'] = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            $application = Application::create($application_init);
 | 
					            $application = Application::create($application_init);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $application->settings->is_static = $this->is_static;
 | 
					            $application->settings->is_static = $this->is_static;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -70,6 +70,8 @@ CMD ["nginx", "-g", "daemon off;"]
 | 
				
			|||||||
            'fqdn' => $fqdn
 | 
					            'fqdn' => $fqdn
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $application->parseHealthcheckFromDockerfile(dockerfile: collect(str($this->dockerfile)->trim()->explode("\n")), isInit: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return redirect()->route('project.application.configuration', [
 | 
					        return redirect()->route('project.application.configuration', [
 | 
				
			||||||
            'application_uuid' => $application->uuid,
 | 
					            'application_uuid' => $application->uuid,
 | 
				
			||||||
            'environment_name' => $environment->name,
 | 
					            'environment_name' => $environment->name,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,6 @@ class Create extends Component
 | 
				
			|||||||
    public $type;
 | 
					    public $type;
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $services = getServiceTemplates();
 | 
					 | 
				
			||||||
        $type = str(request()->query('type'));
 | 
					        $type = str(request()->query('type'));
 | 
				
			||||||
        $destination_uuid = request()->query('destination');
 | 
					        $destination_uuid = request()->query('destination');
 | 
				
			||||||
        $server_id = request()->query('server_id');
 | 
					        $server_id = request()->query('server_id');
 | 
				
			||||||
@@ -25,83 +24,87 @@ class Create extends Component
 | 
				
			|||||||
        if (!$environment) {
 | 
					        if (!$environment) {
 | 
				
			||||||
            return redirect()->route('dashboard');
 | 
					            return redirect()->route('dashboard');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (in_array($type, DATABASE_TYPES)) {
 | 
					        if (isset($type) && isset($destination_uuid) && isset($server_id)) {
 | 
				
			||||||
            if ($type->value() === "postgresql") {
 | 
					            $services = getServiceTemplates();
 | 
				
			||||||
                $database = create_standalone_postgresql($environment->id, $destination_uuid);
 | 
					
 | 
				
			||||||
            } else if ($type->value() === 'redis') {
 | 
					            if (in_array($type, DATABASE_TYPES)) {
 | 
				
			||||||
                $database = create_standalone_redis($environment->id, $destination_uuid);
 | 
					                if ($type->value() === "postgresql") {
 | 
				
			||||||
            } else if ($type->value() === 'mongodb') {
 | 
					                    $database = create_standalone_postgresql($environment->id, $destination_uuid);
 | 
				
			||||||
                $database = create_standalone_mongodb($environment->id, $destination_uuid);
 | 
					                } else if ($type->value() === 'redis') {
 | 
				
			||||||
            } else if ($type->value() === 'mysql') {
 | 
					                    $database = create_standalone_redis($environment->id, $destination_uuid);
 | 
				
			||||||
                $database = create_standalone_mysql($environment->id, $destination_uuid);
 | 
					                } else if ($type->value() === 'mongodb') {
 | 
				
			||||||
            } else if ($type->value() === 'mariadb') {
 | 
					                    $database = create_standalone_mongodb($environment->id, $destination_uuid);
 | 
				
			||||||
                $database = create_standalone_mariadb($environment->id, $destination_uuid);
 | 
					                } else if ($type->value() === 'mysql') {
 | 
				
			||||||
            } else if ($type->value() === 'keydb') {
 | 
					                    $database = create_standalone_mysql($environment->id, $destination_uuid);
 | 
				
			||||||
                $database = create_standalone_keydb($environment->id, $destination_uuid);
 | 
					                } else if ($type->value() === 'mariadb') {
 | 
				
			||||||
            } else if ($type->value() === 'dragonfly') {
 | 
					                    $database = create_standalone_mariadb($environment->id, $destination_uuid);
 | 
				
			||||||
                $database = create_standalone_dragonfly($environment->id, $destination_uuid);
 | 
					                } else if ($type->value() === 'keydb') {
 | 
				
			||||||
            } else if ($type->value() === 'clickhouse') {
 | 
					                    $database = create_standalone_keydb($environment->id, $destination_uuid);
 | 
				
			||||||
                $database = create_standalone_clickhouse($environment->id, $destination_uuid);
 | 
					                } else if ($type->value() === 'dragonfly') {
 | 
				
			||||||
            }
 | 
					                    $database = create_standalone_dragonfly($environment->id, $destination_uuid);
 | 
				
			||||||
            return redirect()->route('project.database.configuration', [
 | 
					                } else if ($type->value() === 'clickhouse') {
 | 
				
			||||||
                'project_uuid' => $project->uuid,
 | 
					                    $database = create_standalone_clickhouse($environment->id, $destination_uuid);
 | 
				
			||||||
                'environment_name' => $environment->name,
 | 
					 | 
				
			||||||
                'database_uuid' => $database->uuid,
 | 
					 | 
				
			||||||
            ]);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if ($type->startsWith('one-click-service-') && !is_null((int)$server_id)) {
 | 
					 | 
				
			||||||
            $oneClickServiceName = $type->after('one-click-service-')->value();
 | 
					 | 
				
			||||||
            $oneClickService = data_get($services, "$oneClickServiceName.compose");
 | 
					 | 
				
			||||||
            $oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
 | 
					 | 
				
			||||||
            if ($oneClickDotEnvs) {
 | 
					 | 
				
			||||||
                $oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
 | 
					 | 
				
			||||||
                    return !empty($value);
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if ($oneClickService) {
 | 
					 | 
				
			||||||
                $destination = StandaloneDocker::whereUuid($destination_uuid)->first();
 | 
					 | 
				
			||||||
                $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);
 | 
					                return redirect()->route('project.database.configuration', [
 | 
				
			||||||
                $service->name = "$oneClickServiceName-" . $service->uuid;
 | 
					 | 
				
			||||||
                $service->save();
 | 
					 | 
				
			||||||
                if ($oneClickDotEnvs?->count() > 0) {
 | 
					 | 
				
			||||||
                    $oneClickDotEnvs->each(function ($value) use ($service) {
 | 
					 | 
				
			||||||
                        $key = str()->before($value, '=');
 | 
					 | 
				
			||||||
                        $value = str(str()->after($value, '='));
 | 
					 | 
				
			||||||
                        $generatedValue = $value;
 | 
					 | 
				
			||||||
                        if ($value->contains('SERVICE_')) {
 | 
					 | 
				
			||||||
                            $command = $value->after('SERVICE_')->beforeLast('_');
 | 
					 | 
				
			||||||
                            $generatedValue = generateEnvValue($command->value(), $service);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        EnvironmentVariable::create([
 | 
					 | 
				
			||||||
                            'key' => $key,
 | 
					 | 
				
			||||||
                            'value' => $generatedValue,
 | 
					 | 
				
			||||||
                            'service_id' => $service->id,
 | 
					 | 
				
			||||||
                            'is_build_time' => false,
 | 
					 | 
				
			||||||
                            'is_preview' => false,
 | 
					 | 
				
			||||||
                        ]);
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $service->parse(isNew: true);
 | 
					 | 
				
			||||||
                return redirect()->route('project.service.configuration', [
 | 
					 | 
				
			||||||
                    'service_uuid' => $service->uuid,
 | 
					 | 
				
			||||||
                    'environment_name' => $environment->name,
 | 
					 | 
				
			||||||
                    'project_uuid' => $project->uuid,
 | 
					                    'project_uuid' => $project->uuid,
 | 
				
			||||||
 | 
					                    'environment_name' => $environment->name,
 | 
				
			||||||
 | 
					                    'database_uuid' => $database->uuid,
 | 
				
			||||||
                ]);
 | 
					                ]);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if ($type->startsWith('one-click-service-') && !is_null((int)$server_id)) {
 | 
				
			||||||
 | 
					                $oneClickServiceName = $type->after('one-click-service-')->value();
 | 
				
			||||||
 | 
					                $oneClickService = data_get($services, "$oneClickServiceName.compose");
 | 
				
			||||||
 | 
					                $oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
 | 
				
			||||||
 | 
					                if ($oneClickDotEnvs) {
 | 
				
			||||||
 | 
					                    $oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
 | 
				
			||||||
 | 
					                        return !empty($value);
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ($oneClickService) {
 | 
				
			||||||
 | 
					                    $destination = StandaloneDocker::whereUuid($destination_uuid)->first();
 | 
				
			||||||
 | 
					                    $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) {
 | 
				
			||||||
 | 
					                        $oneClickDotEnvs->each(function ($value) use ($service) {
 | 
				
			||||||
 | 
					                            $key = str()->before($value, '=');
 | 
				
			||||||
 | 
					                            $value = str(str()->after($value, '='));
 | 
				
			||||||
 | 
					                            $generatedValue = $value;
 | 
				
			||||||
 | 
					                            if ($value->contains('SERVICE_')) {
 | 
				
			||||||
 | 
					                                $command = $value->after('SERVICE_')->beforeLast('_');
 | 
				
			||||||
 | 
					                                $generatedValue = generateEnvValue($command->value(), $service);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            EnvironmentVariable::create([
 | 
				
			||||||
 | 
					                                'key' => $key,
 | 
				
			||||||
 | 
					                                'value' => $generatedValue,
 | 
				
			||||||
 | 
					                                'service_id' => $service->id,
 | 
				
			||||||
 | 
					                                'is_build_time' => false,
 | 
				
			||||||
 | 
					                                'is_preview' => false,
 | 
				
			||||||
 | 
					                            ]);
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $service->parse(isNew: true);
 | 
				
			||||||
 | 
					                    return redirect()->route('project.service.configuration', [
 | 
				
			||||||
 | 
					                        'service_uuid' => $service->uuid,
 | 
				
			||||||
 | 
					                        'environment_name' => $environment->name,
 | 
				
			||||||
 | 
					                        'project_uuid' => $project->uuid,
 | 
				
			||||||
 | 
					                    ]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $this->type = $type->value();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->type = $type->value();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function render()
 | 
					    public function render()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Livewire\Project\Service;
 | 
					namespace App\Livewire\Project\Service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Jobs\ContainerStatusJob;
 | 
					use App\Actions\Docker\GetContainersStatus;
 | 
				
			||||||
use App\Models\Service;
 | 
					use App\Models\Service;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -64,7 +64,8 @@ class Configuration extends Component
 | 
				
			|||||||
    public function check_status()
 | 
					    public function check_status()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            dispatch_sync(new ContainerStatusJob($this->service->server));
 | 
					            GetContainersStatus::run($this->service->server);
 | 
				
			||||||
 | 
					            // dispatch_sync(new ContainerStatusJob($this->service->server));
 | 
				
			||||||
            $this->dispatch('refresh')->self();
 | 
					            $this->dispatch('refresh')->self();
 | 
				
			||||||
            $this->dispatch('updateStatus');
 | 
					            $this->dispatch('updateStatus');
 | 
				
			||||||
        } catch (\Exception $e) {
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ class EditCompose extends Component
 | 
				
			|||||||
    protected $rules = [
 | 
					    protected $rules = [
 | 
				
			||||||
        'service.docker_compose_raw' => 'required',
 | 
					        'service.docker_compose_raw' => 'required',
 | 
				
			||||||
        'service.docker_compose' => 'required',
 | 
					        'service.docker_compose' => 'required',
 | 
				
			||||||
 | 
					        'service.is_container_label_escape_enabled' => 'required',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -23,6 +24,14 @@ class EditCompose extends Component
 | 
				
			|||||||
        $this->dispatch('info', "Saving new docker compose...");
 | 
					        $this->dispatch('info', "Saving new docker compose...");
 | 
				
			||||||
        $this->dispatch('saveCompose', $this->service->docker_compose_raw);
 | 
					        $this->dispatch('saveCompose', $this->service->docker_compose_raw);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function instantSave()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->validate([
 | 
				
			||||||
 | 
					            'service.is_container_label_escape_enabled' => 'required',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        $this->service->save(['is_container_label_escape_enabled' => $this->service->is_container_label_escape_enabled]);
 | 
				
			||||||
 | 
					        $this->dispatch('success', "Service updated successfully");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function render()
 | 
					    public function render()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return view('livewire.project.service.edit-compose');
 | 
					        return view('livewire.project.service.edit-compose');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
namespace App\Livewire\Project\Shared;
 | 
					namespace App\Livewire\Project\Shared;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Actions\Application\StopApplicationOneServer;
 | 
					use App\Actions\Application\StopApplicationOneServer;
 | 
				
			||||||
 | 
					use App\Actions\Docker\GetContainersStatus;
 | 
				
			||||||
use App\Events\ApplicationStatusChanged;
 | 
					use App\Events\ApplicationStatusChanged;
 | 
				
			||||||
use App\Jobs\ContainerStatusJob;
 | 
					use App\Jobs\ContainerStatusJob;
 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
@@ -90,7 +91,8 @@ class Destination extends Component
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    public function refreshServers()
 | 
					    public function refreshServers()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        ContainerStatusJob::dispatchSync($this->resource->destination->server);
 | 
					        GetContainersStatus::run($this->resource->destination->server);
 | 
				
			||||||
 | 
					        // ContainerStatusJob::dispatchSync($this->resource->destination->server);
 | 
				
			||||||
        $this->loadData();
 | 
					        $this->loadData();
 | 
				
			||||||
        $this->dispatch('refresh');
 | 
					        $this->dispatch('refresh');
 | 
				
			||||||
        ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
 | 
					        ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,11 +5,11 @@ namespace App\Livewire\Project\Shared\EnvironmentVariable;
 | 
				
			|||||||
use App\Models\EnvironmentVariable;
 | 
					use App\Models\EnvironmentVariable;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
use Visus\Cuid2\Cuid2;
 | 
					use Visus\Cuid2\Cuid2;
 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class All extends Component
 | 
					class All extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public $resource;
 | 
					    public $resource;
 | 
				
			||||||
 | 
					    public string $resourceClass;
 | 
				
			||||||
    public bool $showPreview = false;
 | 
					    public bool $showPreview = false;
 | 
				
			||||||
    public ?string $modalId = null;
 | 
					    public ?string $modalId = null;
 | 
				
			||||||
    public ?string $variables = null;
 | 
					    public ?string $variables = null;
 | 
				
			||||||
@@ -19,17 +19,44 @@ class All extends Component
 | 
				
			|||||||
        'refreshEnvs',
 | 
					        'refreshEnvs',
 | 
				
			||||||
        'saveKey' => 'submit',
 | 
					        'saveKey' => 'submit',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					    protected $rules = [
 | 
				
			||||||
 | 
					        'resource.settings.is_env_sorting_enabled' => 'required|boolean',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $resourceClass = get_class($this->resource);
 | 
					        $this->resourceClass = get_class($this->resource);
 | 
				
			||||||
        $resourceWithPreviews = ['App\Models\Application'];
 | 
					        $resourceWithPreviews = ['App\Models\Application'];
 | 
				
			||||||
        $simpleDockerfile = !is_null(data_get($this->resource, 'dockerfile'));
 | 
					        $simpleDockerfile = !is_null(data_get($this->resource, 'dockerfile'));
 | 
				
			||||||
        if (Str::of($resourceClass)->contains($resourceWithPreviews) && !$simpleDockerfile) {
 | 
					        if (str($this->resourceClass)->contains($resourceWithPreviews) && !$simpleDockerfile) {
 | 
				
			||||||
            $this->showPreview = true;
 | 
					            $this->showPreview = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $this->modalId = new Cuid2(7);
 | 
					        $this->modalId = new Cuid2(7);
 | 
				
			||||||
 | 
					        $this->sortMe();
 | 
				
			||||||
        $this->getDevView();
 | 
					        $this->getDevView();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function sortMe()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->resourceClass === 'App\Models\Application' && data_get($this->resource, 'build_pack') !== 'dockercompose') {
 | 
				
			||||||
 | 
					            if ($this->resource->settings->is_env_sorting_enabled) {
 | 
				
			||||||
 | 
					                $this->resource->environment_variables = $this->resource->environment_variables->sortBy('key');
 | 
				
			||||||
 | 
					                $this->resource->environment_variables_preview = $this->resource->environment_variables_preview->sortBy('key');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->resource->environment_variables = $this->resource->environment_variables->sortBy('id');
 | 
				
			||||||
 | 
					                $this->resource->environment_variables_preview = $this->resource->environment_variables_preview->sortBy('id');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $this->getDevView();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function instantSave()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->resourceClass === 'App\Models\Application' && data_get($this->resource, 'build_pack') !== 'dockercompose') {
 | 
				
			||||||
 | 
					            $this->resource->settings->save();
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'Environment variable settings updated.');
 | 
				
			||||||
 | 
					            $this->sortMe();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function getDevView()
 | 
					    public function getDevView()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->variables = $this->resource->environment_variables->map(function ($item) {
 | 
					        $this->variables = $this->resource->environment_variables->map(function ($item) {
 | 
				
			||||||
@@ -40,7 +67,7 @@ class All extends Component
 | 
				
			|||||||
                return "$item->key=(multiline, edit in normal view)";
 | 
					                return "$item->key=(multiline, edit in normal view)";
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return "$item->key=$item->value";
 | 
					            return "$item->key=$item->value";
 | 
				
			||||||
        })->sort()->join('
 | 
					        })->join('
 | 
				
			||||||
');
 | 
					');
 | 
				
			||||||
        if ($this->showPreview) {
 | 
					        if ($this->showPreview) {
 | 
				
			||||||
            $this->variablesPreview = $this->resource->environment_variables_preview->map(function ($item) {
 | 
					            $this->variablesPreview = $this->resource->environment_variables_preview->map(function ($item) {
 | 
				
			||||||
@@ -51,13 +78,18 @@ class All extends Component
 | 
				
			|||||||
                    return "$item->key=(multiline, edit in normal view)";
 | 
					                    return "$item->key=(multiline, edit in normal view)";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return "$item->key=$item->value";
 | 
					                return "$item->key=$item->value";
 | 
				
			||||||
            })->sort()->join('
 | 
					            })->join('
 | 
				
			||||||
');
 | 
					');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function switch()
 | 
					    public function switch()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->view = $this->view === 'normal' ? 'dev' : 'normal';
 | 
					        if ($this->view === 'normal') {
 | 
				
			||||||
 | 
					            $this->view = 'dev';
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->view = 'normal';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $this->sortMe();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function saveVariables($isPreview)
 | 
					    public function saveVariables($isPreview)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -66,6 +98,7 @@ class All extends Component
 | 
				
			|||||||
            $this->resource->environment_variables_preview()->whereNotIn('key', array_keys($variables))->delete();
 | 
					            $this->resource->environment_variables_preview()->whereNotIn('key', array_keys($variables))->delete();
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $variables = parseEnvFormatToArray($this->variables);
 | 
					            $variables = parseEnvFormatToArray($this->variables);
 | 
				
			||||||
 | 
					            ray($variables, $this->variables);
 | 
				
			||||||
            $this->resource->environment_variables()->whereNotIn('key', array_keys($variables))->delete();
 | 
					            $this->resource->environment_variables()->whereNotIn('key', array_keys($variables))->delete();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        foreach ($variables as $key => $variable) {
 | 
					        foreach ($variables as $key => $variable) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -122,7 +122,7 @@ class ExecuteContainerCommand extends Component
 | 
				
			|||||||
            if ($server->isForceDisabled()) {
 | 
					            if ($server->isForceDisabled()) {
 | 
				
			||||||
                throw new \RuntimeException('Server is disabled.');
 | 
					                throw new \RuntimeException('Server is disabled.');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            $cmd = 'sh -c "if [ -f ~/.profile ]; then . ~/.profile; fi; ' . str_replace('"', '\"', $this->command)  . '"';
 | 
					            $cmd = "sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; " . str_replace("'", "'\''", $this->command)  . "'";
 | 
				
			||||||
            if (!empty($this->workDir)) {
 | 
					            if (!empty($this->workDir)) {
 | 
				
			||||||
                $exec = "docker exec -w {$this->workDir} {$container_name} {$cmd}";
 | 
					                $exec = "docker exec -w {$this->workDir} {$container_name} {$cmd}";
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -91,15 +91,35 @@ class GetLogs extends Component
 | 
				
			|||||||
        if ($this->container) {
 | 
					        if ($this->container) {
 | 
				
			||||||
            if ($this->showTimeStamps) {
 | 
					            if ($this->showTimeStamps) {
 | 
				
			||||||
                if ($this->server->isSwarm()) {
 | 
					                if ($this->server->isSwarm()) {
 | 
				
			||||||
                    $sshCommand = generateSshCommand($this->server, "docker service logs -n {$this->numberOfLines} -t {$this->container}");
 | 
					                    $command = "docker service logs -n {$this->numberOfLines} -t {$this->container}";
 | 
				
			||||||
 | 
					                    if ($this->server->isNonRoot()) {
 | 
				
			||||||
 | 
					                        $command = parseCommandsByLineForSudo(collect($command), $this->server);
 | 
				
			||||||
 | 
					                        $command = $command[0];
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $sshCommand = generateSshCommand($this->server, $command);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    $sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} -t {$this->container}");
 | 
					                    $command = "docker logs -n {$this->numberOfLines} -t {$this->container}";
 | 
				
			||||||
 | 
					                    if ($this->server->isNonRoot()) {
 | 
				
			||||||
 | 
					                        $command = parseCommandsByLineForSudo(collect($command), $this->server);
 | 
				
			||||||
 | 
					                        $command = $command[0];
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $sshCommand = generateSshCommand($this->server, $command);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                if ($this->server->isSwarm()) {
 | 
					                if ($this->server->isSwarm()) {
 | 
				
			||||||
                    $sshCommand = generateSshCommand($this->server, "docker service logs -n {$this->numberOfLines} {$this->container}");
 | 
					                    $command = "docker service logs -n {$this->numberOfLines} {$this->container}";
 | 
				
			||||||
 | 
					                    if ($this->server->isNonRoot()) {
 | 
				
			||||||
 | 
					                        $command = parseCommandsByLineForSudo(collect($command), $this->server);
 | 
				
			||||||
 | 
					                        $command = $command[0];
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $sshCommand = generateSshCommand($this->server, $command);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    $sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} {$this->container}");
 | 
					                    $command = "docker logs -n {$this->numberOfLines} {$this->container}";
 | 
				
			||||||
 | 
					                    if ($this->server->isNonRoot()) {
 | 
				
			||||||
 | 
					                        $command = parseCommandsByLineForSudo(collect($command), $this->server);
 | 
				
			||||||
 | 
					                        $command = $command[0];
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $sshCommand = generateSshCommand($this->server, $command);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if ($refresh) {
 | 
					            if ($refresh) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,18 +17,17 @@ class HealthChecks extends Component
 | 
				
			|||||||
        'resource.health_check_return_code' => 'integer',
 | 
					        'resource.health_check_return_code' => 'integer',
 | 
				
			||||||
        'resource.health_check_scheme' => 'string',
 | 
					        'resource.health_check_scheme' => 'string',
 | 
				
			||||||
        'resource.health_check_response_text' => 'nullable|string',
 | 
					        'resource.health_check_response_text' => 'nullable|string',
 | 
				
			||||||
        'resource.health_check_interval' => 'integer',
 | 
					        'resource.health_check_interval' => 'integer|min:1',
 | 
				
			||||||
        'resource.health_check_timeout' => 'integer',
 | 
					        'resource.health_check_timeout' => 'integer|min:1',
 | 
				
			||||||
        'resource.health_check_retries' => 'integer',
 | 
					        'resource.health_check_retries' => 'integer|min:1',
 | 
				
			||||||
        'resource.health_check_start_period' => 'integer',
 | 
					        'resource.health_check_start_period' => 'integer',
 | 
				
			||||||
 | 
					        'resource.custom_healthcheck_found' => 'boolean',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    public function instantSave()
 | 
					    public function instantSave()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->resource->save();
 | 
					        $this->resource->save();
 | 
				
			||||||
        $this->dispatch('success', 'Health check updated.');
 | 
					        $this->dispatch('success', 'Health check updated.');
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function submit()
 | 
					    public function submit()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,7 @@ class Logs extends Component
 | 
				
			|||||||
    public $query;
 | 
					    public $query;
 | 
				
			||||||
    public $status;
 | 
					    public $status;
 | 
				
			||||||
    public $serviceSubType;
 | 
					    public $serviceSubType;
 | 
				
			||||||
 | 
					    public $cpu;
 | 
				
			||||||
    public function loadContainers($server_id)
 | 
					    public function loadContainers($server_id)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
@@ -49,6 +49,14 @@ class Logs extends Component
 | 
				
			|||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function loadMetrics()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					        $server = data_get($this->resource, 'destination.server');
 | 
				
			||||||
 | 
					        if ($server->isFunctional()) {
 | 
				
			||||||
 | 
					            $this->cpu = $server->getMetrics();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
@@ -95,6 +103,7 @@ class Logs extends Component
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            $this->containers = $this->containers->sort();
 | 
					            $this->containers = $this->containers->sort();
 | 
				
			||||||
 | 
					            $this->loadMetrics();
 | 
				
			||||||
        } catch (\Exception $e) {
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,11 +2,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Livewire\Project\Shared\ScheduledTask;
 | 
					namespace App\Livewire\Project\Shared\ScheduledTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Support\Collection;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Add extends Component
 | 
					class Add extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public $parameters;
 | 
					    public $parameters;
 | 
				
			||||||
 | 
					    public string $type;
 | 
				
			||||||
 | 
					    public Collection $containerNames;
 | 
				
			||||||
    public string $name;
 | 
					    public string $name;
 | 
				
			||||||
    public string $command;
 | 
					    public string $command;
 | 
				
			||||||
    public string $frequency;
 | 
					    public string $frequency;
 | 
				
			||||||
@@ -29,6 +32,9 @@ class Add extends Component
 | 
				
			|||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->parameters = get_route_parameters();
 | 
					        $this->parameters = get_route_parameters();
 | 
				
			||||||
 | 
					        if ($this->containerNames->count() > 0) {
 | 
				
			||||||
 | 
					            $this->container = $this->containerNames->first();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function submit()
 | 
					    public function submit()
 | 
				
			||||||
@@ -40,6 +46,11 @@ class Add extends Component
 | 
				
			|||||||
                $this->dispatch('error', 'Invalid Cron / Human expression.');
 | 
					                $this->dispatch('error', 'Invalid Cron / Human expression.');
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if (empty($this->container) || $this->container == 'null') {
 | 
				
			||||||
 | 
					                if ($this->type == 'service') {
 | 
				
			||||||
 | 
					                    $this->container = $this->subServiceName;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            $this->dispatch('saveScheduledTask', [
 | 
					            $this->dispatch('saveScheduledTask', [
 | 
				
			||||||
                'name' => $this->name,
 | 
					                'name' => $this->name,
 | 
				
			||||||
                'command' => $this->command,
 | 
					                'command' => $this->command,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,14 +3,13 @@
 | 
				
			|||||||
namespace App\Livewire\Project\Shared\ScheduledTask;
 | 
					namespace App\Livewire\Project\Shared\ScheduledTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Models\ScheduledTask;
 | 
					use App\Models\ScheduledTask;
 | 
				
			||||||
 | 
					use Illuminate\Support\Collection;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
use Visus\Cuid2\Cuid2;
 | 
					 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class All extends Component
 | 
					class All extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public $resource;
 | 
					    public $resource;
 | 
				
			||||||
    public string|null $modalId = null;
 | 
					    public Collection $containerNames;
 | 
				
			||||||
    public ?string $variables = null;
 | 
					    public ?string $variables = null;
 | 
				
			||||||
    public array $parameters;
 | 
					    public array $parameters;
 | 
				
			||||||
    protected $listeners = ['refreshTasks', 'saveScheduledTask' => 'submit'];
 | 
					    protected $listeners = ['refreshTasks', 'saveScheduledTask' => 'submit'];
 | 
				
			||||||
@@ -18,7 +17,18 @@ class All extends Component
 | 
				
			|||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->parameters = get_route_parameters();
 | 
					        $this->parameters = get_route_parameters();
 | 
				
			||||||
        $this->modalId = new Cuid2(7);
 | 
					        if ($this->resource->type() == 'service') {
 | 
				
			||||||
 | 
					            $this->containerNames = $this->resource->applications()->pluck('name');
 | 
				
			||||||
 | 
					            $this->containerNames = $this->containerNames->merge($this->resource->databases()->pluck('name'));
 | 
				
			||||||
 | 
					        } elseif ($this->resource->type() == 'application') {
 | 
				
			||||||
 | 
					            if ($this->resource->build_pack === 'dockercompose') {
 | 
				
			||||||
 | 
					                $parsed = $this->resource->parseCompose();
 | 
				
			||||||
 | 
					                $containers = collect(data_get($parsed,'services'))->keys();
 | 
				
			||||||
 | 
					                $this->containerNames = $containers;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $this->containerNames = collect([]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function refreshTasks()
 | 
					    public function refreshTasks()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ class Show extends Component
 | 
				
			|||||||
    public string $type;
 | 
					    public string $type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $rules = [
 | 
					    protected $rules = [
 | 
				
			||||||
 | 
					        'task.enabled' => 'required|boolean',
 | 
				
			||||||
        'task.name' => 'required|string',
 | 
					        'task.name' => 'required|string',
 | 
				
			||||||
        'task.command' => 'required|string',
 | 
					        'task.command' => 'required|string',
 | 
				
			||||||
        'task.frequency' => 'required|string',
 | 
					        'task.frequency' => 'required|string',
 | 
				
			||||||
@@ -45,9 +46,18 @@ class Show extends Component
 | 
				
			|||||||
        $this->task = ModelsScheduledTask::where('uuid', request()->route('task_uuid'))->first();
 | 
					        $this->task = ModelsScheduledTask::where('uuid', request()->route('task_uuid'))->first();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function instantSave()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->validateOnly('task.enabled');
 | 
				
			||||||
 | 
					        $this->task->save(['enabled' => $this->task->enabled]);
 | 
				
			||||||
 | 
					        $this->dispatch('success', 'Scheduled task updated.');
 | 
				
			||||||
 | 
					        $this->dispatch('refreshTasks');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function submit()
 | 
					    public function submit()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->validate();
 | 
					        $this->validate();
 | 
				
			||||||
 | 
					        $this->task->name = str($this->task->name)->trim()->value();
 | 
				
			||||||
 | 
					        $this->task->container = str($this->task->container)->trim()->value();
 | 
				
			||||||
        $this->task->save();
 | 
					        $this->task->save();
 | 
				
			||||||
        $this->dispatch('success', 'Scheduled task updated.');
 | 
					        $this->dispatch('success', 'Scheduled task updated.');
 | 
				
			||||||
        $this->dispatch('refreshTasks');
 | 
					        $this->dispatch('refreshTasks');
 | 
				
			||||||
@@ -60,11 +70,9 @@ class Show extends Component
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if ($this->type == 'application') {
 | 
					            if ($this->type == 'application') {
 | 
				
			||||||
                return redirect()->route('project.application.configuration', $this->parameters);
 | 
					                return redirect()->route('project.application.configuration', $this->parameters);
 | 
				
			||||||
            }
 | 
					            } else {
 | 
				
			||||||
            else {
 | 
					 | 
				
			||||||
                return redirect()->route('project.service.configuration', $this->parameters);
 | 
					                return redirect()->route('project.service.configuration', $this->parameters);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        } catch (\Exception $e) {
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
            return handleError($e);
 | 
					            return handleError($e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -82,6 +82,7 @@ class Form extends Component
 | 
				
			|||||||
            $this->server->settings->is_reachable = true;
 | 
					            $this->server->settings->is_reachable = true;
 | 
				
			||||||
            $this->server->settings->is_usable = true;
 | 
					            $this->server->settings->is_usable = true;
 | 
				
			||||||
            $this->server->settings->save();
 | 
					            $this->server->settings->save();
 | 
				
			||||||
 | 
					            $this->dispatch('proxyStatusUpdated');
 | 
				
			||||||
        } else {
 | 
					        } 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. <br><br>Error: ' . $error);
 | 
					            $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;
 | 
					            return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Livewire\Server\Proxy;
 | 
					namespace App\Livewire\Server\Proxy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Actions\Docker\GetContainersStatus;
 | 
				
			||||||
use App\Actions\Proxy\CheckProxy;
 | 
					use App\Actions\Proxy\CheckProxy;
 | 
				
			||||||
use App\Jobs\ContainerStatusJob;
 | 
					use App\Jobs\ContainerStatusJob;
 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
@@ -49,7 +50,8 @@ class Status extends Component
 | 
				
			|||||||
    public function getProxyStatus()
 | 
					    public function getProxyStatus()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            dispatch_sync(new ContainerStatusJob($this->server));
 | 
					            GetContainersStatus::run($this->server);
 | 
				
			||||||
 | 
					            // dispatch_sync(new ContainerStatusJob($this->server));
 | 
				
			||||||
            $this->dispatch('proxyStatusUpdated');
 | 
					            $this->dispatch('proxyStatusUpdated');
 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ class Configuration extends Component
 | 
				
			|||||||
    public bool $is_auto_update_enabled;
 | 
					    public bool $is_auto_update_enabled;
 | 
				
			||||||
    public bool $is_registration_enabled;
 | 
					    public bool $is_registration_enabled;
 | 
				
			||||||
    public bool $is_dns_validation_enabled;
 | 
					    public bool $is_dns_validation_enabled;
 | 
				
			||||||
    public bool $next_channel;
 | 
					    // public bool $next_channel;
 | 
				
			||||||
    protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
 | 
					    protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
 | 
				
			||||||
    protected Server $server;
 | 
					    protected Server $server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -37,7 +37,7 @@ class Configuration extends Component
 | 
				
			|||||||
        $this->do_not_track = $this->settings->do_not_track;
 | 
					        $this->do_not_track = $this->settings->do_not_track;
 | 
				
			||||||
        $this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
 | 
					        $this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
 | 
				
			||||||
        $this->is_registration_enabled = $this->settings->is_registration_enabled;
 | 
					        $this->is_registration_enabled = $this->settings->is_registration_enabled;
 | 
				
			||||||
        $this->next_channel = $this->settings->next_channel;
 | 
					        // $this->next_channel = $this->settings->next_channel;
 | 
				
			||||||
        $this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
 | 
					        $this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,12 +47,12 @@ class Configuration extends Component
 | 
				
			|||||||
        $this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
 | 
					        $this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
 | 
				
			||||||
        $this->settings->is_registration_enabled = $this->is_registration_enabled;
 | 
					        $this->settings->is_registration_enabled = $this->is_registration_enabled;
 | 
				
			||||||
        $this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
 | 
					        $this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
 | 
				
			||||||
        if ($this->next_channel) {
 | 
					        // if ($this->next_channel) {
 | 
				
			||||||
            $this->settings->next_channel = false;
 | 
					        //     $this->settings->next_channel = false;
 | 
				
			||||||
            $this->next_channel = false;
 | 
					        //     $this->next_channel = false;
 | 
				
			||||||
        } else {
 | 
					        // } else {
 | 
				
			||||||
            $this->settings->next_channel = $this->next_channel;
 | 
					        //     $this->settings->next_channel = $this->next_channel;
 | 
				
			||||||
        }
 | 
					        // }
 | 
				
			||||||
        $this->settings->save();
 | 
					        $this->settings->save();
 | 
				
			||||||
        $this->dispatch('success', 'Settings updated!');
 | 
					        $this->dispatch('success', 'Settings updated!');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,13 +16,13 @@ class Create extends Component
 | 
				
			|||||||
    public string $endpoint;
 | 
					    public string $endpoint;
 | 
				
			||||||
    public S3Storage $storage;
 | 
					    public S3Storage $storage;
 | 
				
			||||||
    protected $rules = [
 | 
					    protected $rules = [
 | 
				
			||||||
        'name' => 'nullable|min:3|max:255',
 | 
					        'name' => 'required|min:3|max:255',
 | 
				
			||||||
        'description' => 'nullable|min:3|max:255',
 | 
					        'description' => 'nullable|min:3|max:255',
 | 
				
			||||||
        'region' => 'required|max:255',
 | 
					        'region' => 'required|max:255',
 | 
				
			||||||
        'key' => 'required|max:255',
 | 
					        'key' => 'required|max:255',
 | 
				
			||||||
        'secret' => 'required|max:255',
 | 
					        'secret' => 'required|max:255',
 | 
				
			||||||
        'bucket' => 'required|max:255',
 | 
					        'bucket' => 'required|max:255',
 | 
				
			||||||
        'endpoint' => 'nullable|url|max:255',
 | 
					        'endpoint' => 'required|url|max:255',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    protected $validationAttributes = [
 | 
					    protected $validationAttributes = [
 | 
				
			||||||
        'name' => 'Name',
 | 
					        'name' => 'Name',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,7 @@ class Deployments extends Component
 | 
				
			|||||||
                "server_id",
 | 
					                "server_id",
 | 
				
			||||||
                "status"
 | 
					                "status"
 | 
				
			||||||
            ])->sortBy('id')->groupBy('server_name')->toArray();
 | 
					            ])->sortBy('id')->groupBy('server_name')->toArray();
 | 
				
			||||||
 | 
					            $this->dispatch('deployments', $this->deployments_per_tag_per_server);
 | 
				
			||||||
        } catch (\Exception $e) {
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,12 @@ class Index extends Component
 | 
				
			|||||||
    public $webhook = null;
 | 
					    public $webhook = null;
 | 
				
			||||||
    public $deployments_per_tag_per_server = [];
 | 
					    public $deployments_per_tag_per_server = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $listeners = ['deployments' => 'update_deployments'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function update_deployments($deployments)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->deployments_per_tag_per_server = $deployments;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function tag_updated()
 | 
					    public function tag_updated()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->tag == "") {
 | 
					        if ($this->tag == "") {
 | 
				
			||||||
@@ -39,14 +45,13 @@ class Index extends Component
 | 
				
			|||||||
    public function redeploy_all()
 | 
					    public function redeploy_all()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $message = collect([]);
 | 
					            $this->applications->each(function ($resource){
 | 
				
			||||||
            $this->applications->each(function ($resource) use ($message) {
 | 
					 | 
				
			||||||
                $deploy = new Deploy();
 | 
					                $deploy = new Deploy();
 | 
				
			||||||
                $message->push($deploy->deploy_resource($resource));
 | 
					                $deploy->deploy_resource($resource);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            $this->services->each(function ($resource) use ($message) {
 | 
					            $this->services->each(function ($resource) {
 | 
				
			||||||
                $deploy = new Deploy();
 | 
					                $deploy = new Deploy();
 | 
				
			||||||
                $message->push($deploy->deploy_resource($resource));
 | 
					                $deploy->deploy_resource($resource);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            $this->dispatch('success', 'Mass deployment started.');
 | 
					            $this->dispatch('success', 'Mass deployment started.');
 | 
				
			||||||
        } catch (\Exception $e) {
 | 
					        } catch (\Exception $e) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										117
									
								
								app/Livewire/Team/AdminView.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								app/Livewire/Team/AdminView.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Livewire\Team;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Team;
 | 
				
			||||||
 | 
					use App\Models\User;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AdminView extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public $users;
 | 
				
			||||||
 | 
					    public ?string $search = "";
 | 
				
			||||||
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (!isInstanceAdmin()) {
 | 
				
			||||||
 | 
					            return redirect()->route('dashboard');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $this->getUsers();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function submitSearch()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->search !== "") {
 | 
				
			||||||
 | 
					            $this->users = User::where(function ($query) {
 | 
				
			||||||
 | 
					                $query->where('name', 'like', "%{$this->search}%")
 | 
				
			||||||
 | 
					                    ->orWhere('email', 'like', "%{$this->search}%");
 | 
				
			||||||
 | 
					            })->get()->filter(function ($user) {
 | 
				
			||||||
 | 
					                return $user->id !== auth()->id();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->getUsers();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function getUsers()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->users = User::where('id', '!=', auth()->id())->get();
 | 
				
			||||||
 | 
					        // $this->users = User::all();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    private function finalizeDeletion(User $user, Team $team)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $servers = $team->servers;
 | 
				
			||||||
 | 
					        foreach ($servers as $server) {
 | 
				
			||||||
 | 
					            $resources = $server->definedResources();
 | 
				
			||||||
 | 
					            foreach ($resources as $resource) {
 | 
				
			||||||
 | 
					                ray("Deleting resource: " . $resource->name);
 | 
				
			||||||
 | 
					                $resource->forceDelete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ray("Deleting server: " . $server->name);
 | 
				
			||||||
 | 
					            $server->forceDelete();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $projects = $team->projects;
 | 
				
			||||||
 | 
					        foreach ($projects as $project) {
 | 
				
			||||||
 | 
					            ray("Deleting project: " . $project->name);
 | 
				
			||||||
 | 
					            $project->forceDelete();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $team->members()->detach($user->id);
 | 
				
			||||||
 | 
					        ray('Deleting team: ' . $team->name);
 | 
				
			||||||
 | 
					        $team->delete();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function delete($id)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $user = User::find($id);
 | 
				
			||||||
 | 
					        $teams = $user->teams;
 | 
				
			||||||
 | 
					        foreach ($teams as $team) {
 | 
				
			||||||
 | 
					            ray($team->name);
 | 
				
			||||||
 | 
					            $user_alone_in_team = $team->members->count() === 1;
 | 
				
			||||||
 | 
					            if ($team->id === 0) {
 | 
				
			||||||
 | 
					                if ($user_alone_in_team) {
 | 
				
			||||||
 | 
					                    ray('user is alone in the root team, do nothing');
 | 
				
			||||||
 | 
					                    return $this->dispatch('error', 'User is alone in the root team, cannot delete');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if ($user_alone_in_team) {
 | 
				
			||||||
 | 
					                ray('user is alone in the team');
 | 
				
			||||||
 | 
					                $this->finalizeDeletion($user, $team);
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ray('user is not alone in the team');
 | 
				
			||||||
 | 
					            if ($user->isOwner()) {
 | 
				
			||||||
 | 
					                $found_other_owner_or_admin = $team->members->filter(function ($member) {
 | 
				
			||||||
 | 
					                    return $member->pivot->role === 'owner' || $member->pivot->role === 'admin';
 | 
				
			||||||
 | 
					                })->where('id', '!=', $user->id)->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if ($found_other_owner_or_admin) {
 | 
				
			||||||
 | 
					                    ray('found other owner or admin');
 | 
				
			||||||
 | 
					                    $team->members()->detach($user->id);
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    $found_other_member_who_is_not_owner = $team->members->filter(function ($member) {
 | 
				
			||||||
 | 
					                        return $member->pivot->role === 'member';
 | 
				
			||||||
 | 
					                    })->first();
 | 
				
			||||||
 | 
					                    if ($found_other_member_who_is_not_owner) {
 | 
				
			||||||
 | 
					                        ray('found other member who is not owner');
 | 
				
			||||||
 | 
					                        $found_other_member_who_is_not_owner->pivot->role = 'owner';
 | 
				
			||||||
 | 
					                        $found_other_member_who_is_not_owner->pivot->save();
 | 
				
			||||||
 | 
					                        $team->members()->detach($user->id);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        // This should never happen as if the user is the only member in the team, the team should be deleted already.
 | 
				
			||||||
 | 
					                        ray('found no other member who is not owner');
 | 
				
			||||||
 | 
					                        $this->finalizeDeletion($user, $team);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                ray('user is not owner');
 | 
				
			||||||
 | 
					                $team->members()->detach($user->id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ray("Deleting user: " . $user->name);
 | 
				
			||||||
 | 
					        $user->delete();
 | 
				
			||||||
 | 
					        $this->getUsers();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return view('livewire.team.admin-view');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,7 +3,7 @@
 | 
				
			|||||||
namespace App\Livewire;
 | 
					namespace App\Livewire;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Actions\Server\UpdateCoolify;
 | 
					use App\Actions\Server\UpdateCoolify;
 | 
				
			||||||
use App\Models\InstanceSettings;
 | 
					
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
 | 
					use DanHarrin\LivewireRateLimiting\WithRateLimiting;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -11,6 +11,7 @@ class Upgrade extends Component
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    use WithRateLimiting;
 | 
					    use WithRateLimiting;
 | 
				
			||||||
    public bool $showProgress = false;
 | 
					    public bool $showProgress = false;
 | 
				
			||||||
 | 
					    public bool $updateInProgress = false;
 | 
				
			||||||
    public bool $isUpgradeAvailable = false;
 | 
					    public bool $isUpgradeAvailable = false;
 | 
				
			||||||
    public string $latestVersion = '';
 | 
					    public string $latestVersion = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -22,23 +23,17 @@ class Upgrade extends Component
 | 
				
			|||||||
        if (isDev()) {
 | 
					        if (isDev()) {
 | 
				
			||||||
            $this->isUpgradeAvailable = true;
 | 
					            $this->isUpgradeAvailable = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $settings = InstanceSettings::get();
 | 
					 | 
				
			||||||
        if ($settings->next_channel) {
 | 
					 | 
				
			||||||
            $this->isUpgradeAvailable = true;
 | 
					 | 
				
			||||||
            $this->latestVersion = 'next';
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function upgrade()
 | 
					    public function upgrade()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            if ($this->showProgress) {
 | 
					            if ($this->updateInProgress) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            $this->rateLimit(1, 30);
 | 
					            $this->rateLimit(1, 60);
 | 
				
			||||||
            $this->showProgress = true;
 | 
					            $this->updateInProgress = true;
 | 
				
			||||||
            UpdateCoolify::run(force: true, async: true);
 | 
					            UpdateCoolify::run(force: true, async: true);
 | 
				
			||||||
            $this->dispatch('success', "Updating Coolify to {$this->latestVersion} version...");
 | 
					 | 
				
			||||||
        } catch (\Throwable $e) {
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
            return handleError($e, $this);
 | 
					            return handleError($e, $this);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,6 +113,18 @@ class Application extends BaseModel
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function failedTaskLink($task_uuid)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (data_get($this, 'environment.project.uuid')) {
 | 
				
			||||||
 | 
					            return route('project.application.scheduled-tasks', [
 | 
				
			||||||
 | 
					                'project_uuid' => data_get($this, 'environment.project.uuid'),
 | 
				
			||||||
 | 
					                'environment_name' => data_get($this, 'environment.name'),
 | 
				
			||||||
 | 
					                'application_uuid' => data_get($this, 'uuid'),
 | 
				
			||||||
 | 
					                'task_uuid' => $task_uuid
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function settings()
 | 
					    public function settings()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->hasOne(ApplicationSetting::class);
 | 
					        return $this->hasOne(ApplicationSetting::class);
 | 
				
			||||||
@@ -146,9 +158,13 @@ class Application extends BaseModel
 | 
				
			|||||||
                if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
 | 
					                if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
 | 
				
			||||||
                    return "{$this->source->html_url}/{$this->git_repository}/tree/{$this->git_branch}";
 | 
					                    return "{$this->source->html_url}/{$this->git_repository}/tree/{$this->git_branch}";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                // Convert the SSH URL to HTTPS URL
 | 
				
			||||||
 | 
					                if (strpos($this->git_repository, 'git@') === 0) {
 | 
				
			||||||
 | 
					                    $git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
 | 
				
			||||||
 | 
					                    return "https://{$git_repository}/tree/{$this->git_branch}";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                return $this->git_repository;
 | 
					                return $this->git_repository;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -159,6 +175,11 @@ class Application extends BaseModel
 | 
				
			|||||||
                if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
 | 
					                if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
 | 
				
			||||||
                    return "{$this->source->html_url}/{$this->git_repository}/settings/hooks";
 | 
					                    return "{$this->source->html_url}/{$this->git_repository}/settings/hooks";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                // Convert the SSH URL to HTTPS URL
 | 
				
			||||||
 | 
					                if (strpos($this->git_repository, 'git@') === 0) {
 | 
				
			||||||
 | 
					                    $git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
 | 
				
			||||||
 | 
					                    return "https://{$git_repository}/settings/hooks";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                return $this->git_repository;
 | 
					                return $this->git_repository;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@@ -171,10 +192,29 @@ class Application extends BaseModel
 | 
				
			|||||||
                if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
 | 
					                if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
 | 
				
			||||||
                    return "{$this->source->html_url}/{$this->git_repository}/commits/{$this->git_branch}";
 | 
					                    return "{$this->source->html_url}/{$this->git_repository}/commits/{$this->git_branch}";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                // Convert the SSH URL to HTTPS URL
 | 
				
			||||||
 | 
					                if (strpos($this->git_repository, 'git@') === 0) {
 | 
				
			||||||
 | 
					                    $git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
 | 
				
			||||||
 | 
					                    return "https://{$git_repository}/commits/{$this->git_branch}";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                return $this->git_repository;
 | 
					                return $this->git_repository;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function gitCommitLink($link): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
 | 
				
			||||||
 | 
					            if (str($this->source->html_url)->contains('bitbucket')) {
 | 
				
			||||||
 | 
					                return "{$this->source->html_url}/{$this->git_repository}/commits/{$link}";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return "{$this->source->html_url}/{$this->git_repository}/commit/{$link}";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (strpos($this->git_repository, 'git@') === 0) {
 | 
				
			||||||
 | 
					            $git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
 | 
				
			||||||
 | 
					            return "https://{$git_repository}/commit/{$link}";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return $this->git_repository;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function dockerfileLocation(): Attribute
 | 
					    public function dockerfileLocation(): Attribute
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return Attribute::make(
 | 
					        return Attribute::make(
 | 
				
			||||||
@@ -429,6 +469,10 @@ class Application extends BaseModel
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function get_last_successful_deployment()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', 'finished')->where('pull_request_id', 0)->orderBy('created_at', 'desc')->first();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function get_last_days_deployments()
 | 
					    public function get_last_days_deployments()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return ApplicationDeploymentQueue::where('application_id', $this->id)->where('created_at', '>=', now()->subDays(7))->orderBy('created_at', 'desc')->get();
 | 
					        return ApplicationDeploymentQueue::where('application_id', $this->id)->where('created_at', '>=', now()->subDays(7))->orderBy('created_at', 'desc')->get();
 | 
				
			||||||
@@ -847,7 +891,7 @@ class Application extends BaseModel
 | 
				
			|||||||
        if (!$composeFileContent) {
 | 
					        if (!$composeFileContent) {
 | 
				
			||||||
            $this->docker_compose_location = $initialDockerComposeLocation;
 | 
					            $this->docker_compose_location = $initialDockerComposeLocation;
 | 
				
			||||||
            $this->save();
 | 
					            $this->save();
 | 
				
			||||||
            throw new \RuntimeException("Could not load base compose file from $workdir$composeFile");
 | 
					            throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $this->docker_compose_raw = $composeFileContent;
 | 
					            $this->docker_compose_raw = $composeFileContent;
 | 
				
			||||||
            $this->save();
 | 
					            $this->save();
 | 
				
			||||||
@@ -963,4 +1007,52 @@ class Application extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        getFilesystemVolumesFromServer($this, $isInit);
 | 
					        getFilesystemVolumesFromServer($this, $isInit);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function parseHealthcheckFromDockerfile($dockerfile, bool $isInit = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (str($dockerfile)->contains('HEALTHCHECK') && ($this->isHealthcheckDisabled() || $isInit)) {
 | 
				
			||||||
 | 
					            $healthcheckCommand = null;
 | 
				
			||||||
 | 
					            $lines = $dockerfile->toArray();
 | 
				
			||||||
 | 
					            foreach ($lines as $line) {
 | 
				
			||||||
 | 
					                $trimmedLine = trim($line);
 | 
				
			||||||
 | 
					                if (str_starts_with($trimmedLine, 'HEALTHCHECK')) {
 | 
				
			||||||
 | 
					                    $healthcheckCommand .= trim($trimmedLine, '\\ ');
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (isset($healthcheckCommand) && str_contains($trimmedLine, '\\')) {
 | 
				
			||||||
 | 
					                    $healthcheckCommand .= ' ' . trim($trimmedLine, '\\ ');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (isset($healthcheckCommand) && !str_contains($trimmedLine, '\\') && !empty($healthcheckCommand)) {
 | 
				
			||||||
 | 
					                    $healthcheckCommand .= ' ' . $trimmedLine;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (str($healthcheckCommand)->isNotEmpty()) {
 | 
				
			||||||
 | 
					                $interval = str($healthcheckCommand)->match('/--interval=(\d+)/');
 | 
				
			||||||
 | 
					                $timeout = str($healthcheckCommand)->match('/--timeout=(\d+)/');
 | 
				
			||||||
 | 
					                $start_period = str($healthcheckCommand)->match('/--start-period=(\d+)/');
 | 
				
			||||||
 | 
					                $start_interval = str($healthcheckCommand)->match('/--start-interval=(\d+)/');
 | 
				
			||||||
 | 
					                $retries = str($healthcheckCommand)->match('/--retries=(\d+)/');
 | 
				
			||||||
 | 
					                if ($interval->isNotEmpty()) {
 | 
				
			||||||
 | 
					                    $this->health_check_interval = $interval->toInteger();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ($timeout->isNotEmpty()) {
 | 
				
			||||||
 | 
					                    $this->health_check_timeout = $timeout->toInteger();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ($start_period->isNotEmpty()) {
 | 
				
			||||||
 | 
					                    $this->health_check_start_period = $start_period->toInteger();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // if ($start_interval) {
 | 
				
			||||||
 | 
					                //     $this->health_check_start_interval = $start_interval->value();
 | 
				
			||||||
 | 
					                // }
 | 
				
			||||||
 | 
					                if ($retries->isNotEmpty()) {
 | 
				
			||||||
 | 
					                    $this->health_check_retries = $retries->toInteger();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ($interval || $timeout || $start_period || $start_interval || $retries) {
 | 
				
			||||||
 | 
					                    $this->custom_healthcheck_found = true;
 | 
				
			||||||
 | 
					                    $this->save();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,8 @@ class ApplicationDeploymentQueue extends Model
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    protected $guarded = [];
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function setStatus(string $status) {
 | 
					    public function setStatus(string $status)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        $this->update([
 | 
					        $this->update([
 | 
				
			||||||
            'status' => $status,
 | 
					            'status' => $status,
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
@@ -21,7 +22,13 @@ class ApplicationDeploymentQueue extends Model
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return collect(json_decode($this->logs))->where('name', $name)->first()?->output ?? null;
 | 
					        return collect(json_decode($this->logs))->where('name', $name)->first()?->output ?? null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function commitMessage()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (empty($this->commit_message) || is_null($this->commit_message)) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return str($this->commit_message)->trim()->limit(50)->value();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function addLogEntry(string $message, string $type = 'stdout', bool $hidden = false)
 | 
					    public function addLogEntry(string $message, string $type = 'stdout', bool $hidden = false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($type === 'error') {
 | 
					        if ($type === 'error') {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,18 @@ use Illuminate\Database\Eloquent\Model;
 | 
				
			|||||||
class Environment extends Model
 | 
					class Environment extends Model
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected $guarded = [];
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected static function booted()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        static::deleting(function ($environment) {
 | 
				
			||||||
 | 
					            $shared_variables = $environment->environment_variables();
 | 
				
			||||||
 | 
					            foreach ($shared_variables as $shared_variable) {
 | 
				
			||||||
 | 
					                ray('Deleting environment shared variable: ' . $shared_variable->name);
 | 
				
			||||||
 | 
					                $shared_variable->delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function isEmpty()
 | 
					    public function isEmpty()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->applications()->count() == 0 &&
 | 
					        return $this->applications()->count() == 0 &&
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,11 @@ class Project extends BaseModel
 | 
				
			|||||||
        static::deleting(function ($project) {
 | 
					        static::deleting(function ($project) {
 | 
				
			||||||
            $project->environments()->delete();
 | 
					            $project->environments()->delete();
 | 
				
			||||||
            $project->settings()->delete();
 | 
					            $project->settings()->delete();
 | 
				
			||||||
 | 
					            $shared_variables = $project->environment_variables();
 | 
				
			||||||
 | 
					            foreach ($shared_variables as $shared_variable) {
 | 
				
			||||||
 | 
					                ray('Deleting project shared variable: ' . $shared_variable->name);
 | 
				
			||||||
 | 
					                $shared_variable->delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function environment_variables()
 | 
					    public function environment_variables()
 | 
				
			||||||
@@ -55,6 +60,7 @@ class Project extends BaseModel
 | 
				
			|||||||
        return $this->hasManyThrough(Application::class, Environment::class);
 | 
					        return $this->hasManyThrough(Application::class, Environment::class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function postgresqls()
 | 
					    public function postgresqls()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->hasManyThrough(StandalonePostgresql::class, Environment::class);
 | 
					        return $this->hasManyThrough(StandalonePostgresql::class, Environment::class);
 | 
				
			||||||
@@ -91,4 +97,7 @@ class Project extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count() + $this->keydbs()->count() + $this->dragonflies()->count()  + $this->services()->count() + $this->clickhouses()->count();
 | 
					        return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count() + $this->keydbs()->count() + $this->dragonflies()->count()  + $this->services()->count() + $this->clickhouses()->count();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function databases() {
 | 
				
			||||||
 | 
					        return $this->postgresqls()->get()->merge($this->redis()->get())->merge($this->mongodbs()->get())->merge($this->mysqls()->get())->merge($this->mariadbs()->get())->merge($this->keydbs()->get())->merge($this->dragonflies()->get())->merge($this->clickhouses()->get());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,11 +3,14 @@
 | 
				
			|||||||
namespace App\Models;
 | 
					namespace App\Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Actions\Server\InstallDocker;
 | 
					use App\Actions\Server\InstallDocker;
 | 
				
			||||||
 | 
					use App\Actions\Server\StartSentinel;
 | 
				
			||||||
use App\Enums\ProxyTypes;
 | 
					use App\Enums\ProxyTypes;
 | 
				
			||||||
 | 
					use App\Jobs\PullSentinelImageJob;
 | 
				
			||||||
use App\Notifications\Server\Revived;
 | 
					use App\Notifications\Server\Revived;
 | 
				
			||||||
use App\Notifications\Server\Unreachable;
 | 
					use App\Notifications\Server\Unreachable;
 | 
				
			||||||
use Illuminate\Database\Eloquent\Builder;
 | 
					use Illuminate\Database\Eloquent\Builder;
 | 
				
			||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
					use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
				
			||||||
 | 
					use Illuminate\Support\Collection;
 | 
				
			||||||
use Illuminate\Support\Facades\DB;
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
use Illuminate\Support\Facades\Storage;
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
 | 
					use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
 | 
				
			||||||
@@ -239,7 +242,7 @@ respond 404
 | 
				
			|||||||
        $dynamic_config_path = $this->proxyPath() . "/dynamic";
 | 
					        $dynamic_config_path = $this->proxyPath() . "/dynamic";
 | 
				
			||||||
        if ($this->proxyType() === 'TRAEFIK_V2') {
 | 
					        if ($this->proxyType() === 'TRAEFIK_V2') {
 | 
				
			||||||
            $file = "$dynamic_config_path/coolify.yaml";
 | 
					            $file = "$dynamic_config_path/coolify.yaml";
 | 
				
			||||||
            if (empty($settings->fqdn)) {
 | 
					            if (empty($settings->fqdn) || (isCloud() && $this->id !== 0)) {
 | 
				
			||||||
                instant_remote_process([
 | 
					                instant_remote_process([
 | 
				
			||||||
                    "rm -f $file",
 | 
					                    "rm -f $file",
 | 
				
			||||||
                ], $this);
 | 
					                ], $this);
 | 
				
			||||||
@@ -358,7 +361,7 @@ respond 404
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        } else if ($this->proxyType() === 'CADDY') {
 | 
					        } else if ($this->proxyType() === 'CADDY') {
 | 
				
			||||||
            $file = "$dynamic_config_path/coolify.caddy";
 | 
					            $file = "$dynamic_config_path/coolify.caddy";
 | 
				
			||||||
            if (empty($settings->fqdn)) {
 | 
					            if (empty($settings->fqdn) || (isCloud() && $this->id !== 0)) {
 | 
				
			||||||
                instant_remote_process([
 | 
					                instant_remote_process([
 | 
				
			||||||
                    "rm -f $file",
 | 
					                    "rm -f $file",
 | 
				
			||||||
                ], $this);
 | 
					                ], $this);
 | 
				
			||||||
@@ -462,6 +465,36 @@ $schema://$host {
 | 
				
			|||||||
        Storage::disk('ssh-keys')->delete($sshKeyFileLocation);
 | 
					        Storage::disk('ssh-keys')->delete($sshKeyFileLocation);
 | 
				
			||||||
        Storage::disk('ssh-mux')->delete($this->muxFilename());
 | 
					        Storage::disk('ssh-mux')->delete($this->muxFilename());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function checkSentinel()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ray("Checking sentinel on server: {$this->name}");
 | 
				
			||||||
 | 
					        if ($this->is_metrics_enabled) {
 | 
				
			||||||
 | 
					            $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this, false);
 | 
				
			||||||
 | 
					            $sentinel_found = json_decode($sentinel_found, true);
 | 
				
			||||||
 | 
					            $status = data_get($sentinel_found, '0.State.Status', 'exited');
 | 
				
			||||||
 | 
					            if ($status !== 'running') {
 | 
				
			||||||
 | 
					                ray('Sentinel is not running, starting it...');
 | 
				
			||||||
 | 
					                PullSentinelImageJob::dispatch($this);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                ray('Sentinel is running');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function getMetrics()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->is_metrics_enabled) {
 | 
				
			||||||
 | 
					            $from = now()->subMinutes(5)->toIso8601ZuluString();
 | 
				
			||||||
 | 
					            $cpu = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl http://localhost:8888/api/cpu/history?from=$from'"], $this, false);
 | 
				
			||||||
 | 
					            $cpu = str($cpu)->explode("\n")->skip(1)->all();
 | 
				
			||||||
 | 
					            $parsedCollection = collect($cpu)->flatMap(function ($item) {
 | 
				
			||||||
 | 
					                return collect(explode("\n", trim($item)))->map(function ($line) {
 | 
				
			||||||
 | 
					                    list($time, $value) = explode(',', trim($line));
 | 
				
			||||||
 | 
					                    return [(int) $time, (float) $value];
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            })->toArray();
 | 
				
			||||||
 | 
					            return $parsedCollection;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function isServerReady(int $tries = 3)
 | 
					    public function isServerReady(int $tries = 3)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->skipServer()) {
 | 
					        if ($this->skipServer()) {
 | 
				
			||||||
@@ -548,7 +581,36 @@ $schema://$host {
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return instant_remote_process(["docker start $id"], $this);
 | 
					        return instant_remote_process(["docker start $id"], $this);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function loadUnmanagedContainers()
 | 
					    public function getContainers(): Collection
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this, false);
 | 
				
			||||||
 | 
					        $sentinel_found = json_decode($sentinel_found, true);
 | 
				
			||||||
 | 
					        $status = data_get($sentinel_found, '0.State.Status', 'exited');
 | 
				
			||||||
 | 
					        if ($status === 'running') {
 | 
				
			||||||
 | 
					            $containers = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/containers"'], $this, false);
 | 
				
			||||||
 | 
					            if (is_null($containers)) {
 | 
				
			||||||
 | 
					                return collect([]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $containers = data_get(json_decode($containers, true), 'containers', []);
 | 
				
			||||||
 | 
					            return collect($containers);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if ($this->isSwarm()) {
 | 
				
			||||||
 | 
					                $containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this, false);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $containers = instant_remote_process(["docker container ls -q"], $this, false);
 | 
				
			||||||
 | 
					                if (!$containers) {
 | 
				
			||||||
 | 
					                    return collect([]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this, false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (is_null($containers)) {
 | 
				
			||||||
 | 
					                return collect([]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return format_docker_command_output_to_json($containers);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function loadUnmanagedContainers(): Collection
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->isFunctional()) {
 | 
					        if ($this->isFunctional()) {
 | 
				
			||||||
            $containers = instant_remote_process(["docker ps -a --format '{{json .}}'"], $this);
 | 
					            $containers = instant_remote_process(["docker ps -a --format '{{json .}}'"], $this);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -171,7 +171,7 @@ class Service extends BaseModel
 | 
				
			|||||||
                            ],
 | 
					                            ],
 | 
				
			||||||
                        ]);
 | 
					                        ]);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    $fields->put('Tolgee', $data);
 | 
					                    $fields->put('Tolgee', $data->toArray());
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case str($image)?->contains('logto'):
 | 
					                case str($image)?->contains('logto'):
 | 
				
			||||||
                    $data = collect([]);
 | 
					                    $data = collect([]);
 | 
				
			||||||
@@ -195,7 +195,7 @@ class Service extends BaseModel
 | 
				
			|||||||
                            ],
 | 
					                            ],
 | 
				
			||||||
                        ]);
 | 
					                        ]);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    $fields->put('Logto', $data);
 | 
					                    $fields->put('Logto', $data->toArray());
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case str($image)?->contains('unleash-server'):
 | 
					                case str($image)?->contains('unleash-server'):
 | 
				
			||||||
                    $data = collect([]);
 | 
					                    $data = collect([]);
 | 
				
			||||||
@@ -218,7 +218,7 @@ class Service extends BaseModel
 | 
				
			|||||||
                            ],
 | 
					                            ],
 | 
				
			||||||
                        ]);
 | 
					                        ]);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    $fields->put('Unleash', $data);
 | 
					                    $fields->put('Unleash', $data->toArray());
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case str($image)?->contains('grafana'):
 | 
					                case str($image)?->contains('grafana'):
 | 
				
			||||||
                    $data = collect([]);
 | 
					                    $data = collect([]);
 | 
				
			||||||
@@ -241,7 +241,7 @@ class Service extends BaseModel
 | 
				
			|||||||
                            ],
 | 
					                            ],
 | 
				
			||||||
                        ]);
 | 
					                        ]);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    $fields->put('Grafana', $data);
 | 
					                    $fields->put('Grafana', $data->toArray());
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case str($image)?->contains('directus'):
 | 
					                case str($image)?->contains('directus'):
 | 
				
			||||||
                    $data = collect([]);
 | 
					                    $data = collect([]);
 | 
				
			||||||
@@ -267,7 +267,7 @@ class Service extends BaseModel
 | 
				
			|||||||
                            ],
 | 
					                            ],
 | 
				
			||||||
                        ]);
 | 
					                        ]);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    $fields->put('Directus', $data);
 | 
					                    $fields->put('Directus', $data->toArray());
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case str($image)?->contains('kong'):
 | 
					                case str($image)?->contains('kong'):
 | 
				
			||||||
                    $data = collect([]);
 | 
					                    $data = collect([]);
 | 
				
			||||||
@@ -370,7 +370,7 @@ class Service extends BaseModel
 | 
				
			|||||||
                            ],
 | 
					                            ],
 | 
				
			||||||
                        ]);
 | 
					                        ]);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    $fields->put('Weblate', $data);
 | 
					                    $fields->put('Weblate', $data->toArray());
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case str($image)?->contains('meilisearch'):
 | 
					                case str($image)?->contains('meilisearch'):
 | 
				
			||||||
                    $data = collect([]);
 | 
					                    $data = collect([]);
 | 
				
			||||||
@@ -384,7 +384,7 @@ class Service extends BaseModel
 | 
				
			|||||||
                            ],
 | 
					                            ],
 | 
				
			||||||
                        ]);
 | 
					                        ]);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    $fields->put('Meilisearch', $data);
 | 
					                    $fields->put('Meilisearch', $data->toArray());
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case str($image)?->contains('ghost'):
 | 
					                case str($image)?->contains('ghost'):
 | 
				
			||||||
                    $data = collect([]);
 | 
					                    $data = collect([]);
 | 
				
			||||||
@@ -444,7 +444,33 @@ class Service extends BaseModel
 | 
				
			|||||||
                        ]);
 | 
					                        ]);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    $fields->put('Ghost', $data);
 | 
					                    $fields->put('Ghost', $data->toArray());
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    $data = collect([]);
 | 
				
			||||||
 | 
					                    $admin_user = $this->environment_variables()->where('key', 'SERVICE_USER_ADMIN')->first();
 | 
				
			||||||
 | 
					                    $admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_ADMIN')->first();
 | 
				
			||||||
 | 
					                    if ($admin_user) {
 | 
				
			||||||
 | 
					                        $data = $data->merge([
 | 
				
			||||||
 | 
					                            'User' => [
 | 
				
			||||||
 | 
					                                'key' => 'SERVICE_USER_ADMIN',
 | 
				
			||||||
 | 
					                                'value' => data_get($admin_user, 'value', 'admin'),
 | 
				
			||||||
 | 
					                                'readonly' => true,
 | 
				
			||||||
 | 
					                                'rules' => 'required',
 | 
				
			||||||
 | 
					                            ],
 | 
				
			||||||
 | 
					                        ]);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if ($admin_password) {
 | 
				
			||||||
 | 
					                        $data = $data->merge([
 | 
				
			||||||
 | 
					                            'Password' => [
 | 
				
			||||||
 | 
					                                'key' => 'SERVICE_PASSWORD_ADMIN',
 | 
				
			||||||
 | 
					                                'value' => data_get($admin_password, 'value'),
 | 
				
			||||||
 | 
					                                'rules' => 'required',
 | 
				
			||||||
 | 
					                                'isPassword' => true,
 | 
				
			||||||
 | 
					                            ],
 | 
				
			||||||
 | 
					                        ]);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $fields->put('Admin', $data->toArray());
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case str($image)?->contains('vaultwarden'):
 | 
					                case str($image)?->contains('vaultwarden'):
 | 
				
			||||||
                    $data = collect([]);
 | 
					                    $data = collect([]);
 | 
				
			||||||
@@ -723,6 +749,18 @@ class Service extends BaseModel
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function failedTaskLink($task_uuid)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (data_get($this, 'environment.project.uuid')) {
 | 
				
			||||||
 | 
					            return route('project.service.scheduled-tasks', [
 | 
				
			||||||
 | 
					                'project_uuid' => data_get($this, 'environment.project.uuid'),
 | 
				
			||||||
 | 
					                'environment_name' => data_get($this, 'environment.name'),
 | 
				
			||||||
 | 
					                'application_uuid' => data_get($this, 'uuid'),
 | 
				
			||||||
 | 
					                'task_uuid' => $task_uuid
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function documentation()
 | 
					    public function documentation()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $services = getServiceTemplates();
 | 
					        $services = getServiceTemplates();
 | 
				
			||||||
@@ -749,6 +787,17 @@ class Service extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->belongsTo(Server::class);
 | 
					        return $this->belongsTo(Server::class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function byUuid(string $uuid) {
 | 
				
			||||||
 | 
					        $app = $this->applications()->whereUuid($uuid)->first();
 | 
				
			||||||
 | 
					        if ($app) {
 | 
				
			||||||
 | 
					            return $app;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $db = $this->databases()->whereUuid($uuid)->first();
 | 
				
			||||||
 | 
					        if ($db) {
 | 
				
			||||||
 | 
					            return $db;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function byName(string $name)
 | 
					    public function byName(string $name)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $app = $this->applications()->whereName($name)->first();
 | 
					        $app = $this->applications()->whereName($name)->first();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -207,7 +207,4 @@ class StandaloneClickhouse extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
					        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function database_name() {
 | 
					 | 
				
			||||||
        return $this->clickhouse_db;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -207,7 +207,4 @@ class StandaloneDragonfly extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
					        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function database_name() {
 | 
					 | 
				
			||||||
        return '0';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -208,7 +208,4 @@ class StandaloneKeydb extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
					        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function database_name() {
 | 
					 | 
				
			||||||
        return '0';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -208,7 +208,4 @@ class StandaloneMariadb extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
					        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function database_name() {
 | 
					 | 
				
			||||||
        return $this->mariadb_database;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -223,7 +223,4 @@ class StandaloneMongodb extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
					        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function database_name() {
 | 
					 | 
				
			||||||
        return $this->mongo_db;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -209,7 +209,4 @@ class StandaloneMysql extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
					        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function database_name() {
 | 
					 | 
				
			||||||
        return $this->mysql_database;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -208,7 +208,4 @@ class StandalonePostgresql extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
					        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function database_name() {
 | 
					 | 
				
			||||||
        return $this->postgres_db;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -204,7 +204,4 @@ class StandaloneRedis extends BaseModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
					        return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function database_name() {
 | 
					 | 
				
			||||||
        return '0';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,34 @@ class Team extends Model implements SendsDiscord, SendsEmail
 | 
				
			|||||||
                throw new \Exception('You are not allowed to update this team.');
 | 
					                throw new \Exception('You are not allowed to update this team.');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static::deleting(function ($team) {
 | 
				
			||||||
 | 
					            $keys = $team->privateKeys;
 | 
				
			||||||
 | 
					            foreach ($keys as $key) {
 | 
				
			||||||
 | 
					                ray('Deleting key: ' . $key->name);
 | 
				
			||||||
 | 
					                $key->delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $sources = $team->sources();
 | 
				
			||||||
 | 
					            foreach ($sources as $source) {
 | 
				
			||||||
 | 
					                ray('Deleting source: ' . $source->name);
 | 
				
			||||||
 | 
					                $source->delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $tags = Tag::whereTeamId($team->id)->get();
 | 
				
			||||||
 | 
					            foreach ($tags as $tag) {
 | 
				
			||||||
 | 
					                ray('Deleting tag: ' . $tag->name);
 | 
				
			||||||
 | 
					                $tag->delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $shared_variables = $team->environment_variables();
 | 
				
			||||||
 | 
					            foreach ($shared_variables as $shared_variable) {
 | 
				
			||||||
 | 
					                ray('Deleting team shared variable: ' . $shared_variable->name);
 | 
				
			||||||
 | 
					                $shared_variable->delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $s3s = $team->s3s;
 | 
				
			||||||
 | 
					            foreach ($s3s as $s3) {
 | 
				
			||||||
 | 
					                ray('Deleting s3: ' . $s3->name);
 | 
				
			||||||
 | 
					                $s3->delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function routeNotificationForDiscord()
 | 
					    public function routeNotificationForDiscord()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -183,6 +183,7 @@ class User extends Authenticatable implements SendsEmail
 | 
				
			|||||||
        if (data_get($this, 'pivot')) {
 | 
					        if (data_get($this, 'pivot')) {
 | 
				
			||||||
            return $this->pivot->role;
 | 
					            return $this->pivot->role;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return auth()->user()->teams->where('id', currentTeam()->id)->first()->pivot->role;
 | 
					        $user = auth()->user()->teams->where('id', currentTeam()->id)->first();
 | 
				
			||||||
 | 
					        return data_get($user, 'pivot.role');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,10 +69,10 @@ class DeploymentFailed extends Notification implements ShouldQueue
 | 
				
			|||||||
    public function toDiscord(): string
 | 
					    public function toDiscord(): string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->preview) {
 | 
					        if ($this->preview) {
 | 
				
			||||||
            $message = 'Coolify:  Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: ';
 | 
					            $message = 'Coolify:  Pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . ' (' . $this->preview->fqdn . ') deployment failed: ';
 | 
				
			||||||
            $message .= '[View Deployment Logs](' . $this->deployment_url . ')';
 | 
					            $message .= '[View Deployment Logs](' . $this->deployment_url . ')';
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $message = 'Coolify: Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): ';
 | 
					            $message = 'Coolify: Deployment failed of ' . $this->application_name . ' (' . $this->fqdn . '): ';
 | 
				
			||||||
            $message .= '[View Deployment Logs](' . $this->deployment_url . ')';
 | 
					            $message .= '[View Deployment Logs](' . $this->deployment_url . ')';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return $message;
 | 
					        return $message;
 | 
				
			||||||
@@ -80,9 +80,9 @@ class DeploymentFailed extends Notification implements ShouldQueue
 | 
				
			|||||||
    public function toTelegram(): array
 | 
					    public function toTelegram(): array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($this->preview) {
 | 
					        if ($this->preview) {
 | 
				
			||||||
            $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: ';
 | 
					            $message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . ' (' . $this->preview->fqdn . ') deployment failed: ';
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $message = 'Coolify: Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): ';
 | 
					            $message = 'Coolify: Deployment failed of ' . $this->application_name . ' (' . $this->fqdn . '): ';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $buttons[] = [
 | 
					        $buttons[] = [
 | 
				
			||||||
            "text" => "Deployment logs",
 | 
					            "text" => "Deployment logs",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,13 +45,11 @@ class DeploymentSuccess extends Notification implements ShouldQueue
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $channels = setNotificationChannels($notifiable, 'deployments');
 | 
					        $channels = setNotificationChannels($notifiable, 'deployments');
 | 
				
			||||||
        if (isCloud()) {
 | 
					        if (isCloud()) {
 | 
				
			||||||
            $channels = array_filter($channels, function ($channel) {
 | 
					            // TODO: Make batch notifications work with email
 | 
				
			||||||
                return $channel !== 'App\Notifications\Channels\EmailChannel';
 | 
					            $channels = array_diff($channels, ['App\Notifications\Channels\EmailChannel']);
 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return $channels;
 | 
					        return $channels;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function toMail(): MailMessage
 | 
					    public function toMail(): MailMessage
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $mail = new MailMessage();
 | 
					        $mail = new MailMessage();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,22 +14,27 @@ class TelegramChannel
 | 
				
			|||||||
        $buttons = data_get($data, 'buttons', []);
 | 
					        $buttons = data_get($data, 'buttons', []);
 | 
				
			||||||
        $telegramToken = data_get($telegramData, 'token');
 | 
					        $telegramToken = data_get($telegramData, 'token');
 | 
				
			||||||
        $chatId = data_get($telegramData, 'chat_id');
 | 
					        $chatId = data_get($telegramData, 'chat_id');
 | 
				
			||||||
        $topicId  = null;
 | 
					        $topicId = null;
 | 
				
			||||||
        $topicsInstance = get_class($notification);
 | 
					        $topicsInstance = get_class($notification);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        switch ($topicsInstance) {
 | 
					        switch ($topicsInstance) {
 | 
				
			||||||
            case 'App\Notifications\StatusChange':
 | 
					 | 
				
			||||||
                $topicId = data_get($notifiable, 'telegram_notifications_status_changes_message_thread_id');
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case 'App\Notifications\Test':
 | 
					            case 'App\Notifications\Test':
 | 
				
			||||||
                $topicId = data_get($notifiable, 'telegram_notifications_test_message_thread_id');
 | 
					                $topicId = data_get($notifiable, 'telegram_notifications_test_message_thread_id');
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case 'App\Notifications\Deployment':
 | 
					            case 'App\Notifications\Application\StatusChanged':
 | 
				
			||||||
 | 
					                $topicId = data_get($notifiable, 'telegram_notifications_status_changes_message_thread_id');
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'App\Notifications\Application\DeploymentSuccess':
 | 
				
			||||||
 | 
					            case 'App\Notifications\Application\DeploymentFailed':
 | 
				
			||||||
                $topicId = data_get($notifiable, 'telegram_notifications_deployments_message_thread_id');
 | 
					                $topicId = data_get($notifiable, 'telegram_notifications_deployments_message_thread_id');
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case 'App\Notifications\DatabaseBackup':
 | 
					            case 'App\Notifications\Database\BackupSuccess':
 | 
				
			||||||
 | 
					            case 'App\Notifications\Database\BackupFailed':
 | 
				
			||||||
                $topicId = data_get($notifiable, 'telegram_notifications_database_backups_message_thread_id');
 | 
					                $topicId = data_get($notifiable, 'telegram_notifications_database_backups_message_thread_id');
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'App\Notifications\ScheduledTask\TaskFailed':
 | 
				
			||||||
 | 
					                $topicId = data_get($notifiable, 'telegram_notifications_scheduled_tasks_thread_id');
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!$telegramToken || !$chatId || !$message) {
 | 
					        if (!$telegramToken || !$chatId || !$message) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,7 +31,7 @@ class ContainerRestarted extends Notification implements ShouldQueue
 | 
				
			|||||||
        $mail->view('emails.container-restarted', [
 | 
					        $mail->view('emails.container-restarted', [
 | 
				
			||||||
            'containerName' => $this->name,
 | 
					            'containerName' => $this->name,
 | 
				
			||||||
            'serverName' => $this->server->name,
 | 
					            'serverName' => $this->server->name,
 | 
				
			||||||
            'url' => $this->url ,
 | 
					            'url' => $this->url,
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
        return $mail;
 | 
					        return $mail;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,21 +15,20 @@ class BackupFailed extends Notification implements ShouldQueue
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    use Queueable;
 | 
					    use Queueable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public $tries = 1;
 | 
					    public $backoff = 10;
 | 
				
			||||||
 | 
					    public $tries = 2;
 | 
				
			||||||
    public string $name;
 | 
					    public string $name;
 | 
				
			||||||
    public string $database_name;
 | 
					 | 
				
			||||||
    public string $frequency;
 | 
					    public string $frequency;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(ScheduledDatabaseBackup $backup, public $database, public $output)
 | 
					    public function __construct(ScheduledDatabaseBackup $backup, public $database, public $output, public $database_name)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->name = $database->name;
 | 
					        $this->name = $database->name;
 | 
				
			||||||
        $this->database_name = $database->database_name();
 | 
					 | 
				
			||||||
        $this->frequency = $backup->frequency;
 | 
					        $this->frequency = $backup->frequency;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function via(object $notifiable): array
 | 
					    public function via(object $notifiable): array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return [DiscordChannel::class, TelegramChannel::class, MailChannel::class];
 | 
					        return setNotificationChannels($notifiable, 'database_backups');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function toMail(): MailMessage
 | 
					    public function toMail(): MailMessage
 | 
				
			||||||
@@ -47,11 +46,11 @@ class BackupFailed extends Notification implements ShouldQueue
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function toDiscord(): string
 | 
					    public function toDiscord(): string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}";
 | 
					        return "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason:\n{$this->output}";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    public function toTelegram(): array
 | 
					    public function toTelegram(): array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $message = "Coolify:  Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}";
 | 
					        $message = "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason:\n{$this->output}";
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            "message" => $message,
 | 
					            "message" => $message,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,15 +12,14 @@ class BackupSuccess extends Notification implements ShouldQueue
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    use Queueable;
 | 
					    use Queueable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public $tries = 1;
 | 
					    public $backoff = 10;
 | 
				
			||||||
 | 
					    public $tries = 3;
 | 
				
			||||||
    public string $name;
 | 
					    public string $name;
 | 
				
			||||||
    public string $database_name;
 | 
					 | 
				
			||||||
    public string $frequency;
 | 
					    public string $frequency;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(ScheduledDatabaseBackup $backup, public $database)
 | 
					    public function __construct(ScheduledDatabaseBackup $backup, public $database, public $database_name)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->name = $database->name;
 | 
					        $this->name = $database->name;
 | 
				
			||||||
        $this->database_name = $database->database_name();
 | 
					 | 
				
			||||||
        $this->frequency = $backup->frequency;
 | 
					        $this->frequency = $backup->frequency;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -48,6 +47,7 @@ class BackupSuccess extends Notification implements ShouldQueue
 | 
				
			|||||||
    public function toTelegram(): array
 | 
					    public function toTelegram(): array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $message = "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was successful.";
 | 
					        $message = "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was successful.";
 | 
				
			||||||
 | 
					        ray($message);
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            "message" => $message,
 | 
					            "message" => $message,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										64
									
								
								app/Notifications/ScheduledTask/TaskFailed.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								app/Notifications/ScheduledTask/TaskFailed.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Notifications\ScheduledTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\ScheduledTask;
 | 
				
			||||||
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
 | 
					use Illuminate\Notifications\Messages\MailMessage;
 | 
				
			||||||
 | 
					use Illuminate\Notifications\Notification;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TaskFailed extends Notification implements ShouldQueue
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use Queueable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $backoff = 10;
 | 
				
			||||||
 | 
					    public $tries = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ?string $url = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(public ScheduledTask $task, public string $output)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($task->application) {
 | 
				
			||||||
 | 
					            $this->url = $task->application->failedTaskLink($task->uuid);
 | 
				
			||||||
 | 
					        } else if ($task->service) {
 | 
				
			||||||
 | 
					            $this->url = $task->service->failedTaskLink($task->uuid);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function via(object $notifiable): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return setNotificationChannels($notifiable, 'scheduled_tasks');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function toMail(): MailMessage
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $mail = new MailMessage();
 | 
				
			||||||
 | 
					        $mail->subject("Coolify: [ACTION REQUIRED] Scheduled task ({$this->task->name}) failed.");
 | 
				
			||||||
 | 
					        $mail->view('emails.scheduled-task-failed', [
 | 
				
			||||||
 | 
					            'task' => $this->task,
 | 
				
			||||||
 | 
					            'url' => $this->url,
 | 
				
			||||||
 | 
					            'output' => $this->output,
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return $mail;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function toDiscord(): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return "Coolify: Scheduled task ({$this->task->name}, [link]({$this->url})) failed with output: {$this->output}";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function toTelegram(): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $message = "Coolify: Scheduled task ({$this->task->name}) failed with output: {$this->output}";
 | 
				
			||||||
 | 
					        if ($this->url) {
 | 
				
			||||||
 | 
					            $buttons[] = [
 | 
				
			||||||
 | 
					                "text" => "Open task in Coolify",
 | 
				
			||||||
 | 
					                "url" => (string) $this->url
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            "message" => $message,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Notifications\Server;
 | 
					namespace App\Notifications\Server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Actions\Docker\GetContainersStatus;
 | 
				
			||||||
use App\Jobs\ContainerStatusJob;
 | 
					use App\Jobs\ContainerStatusJob;
 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
use Illuminate\Bus\Queueable;
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
@@ -22,7 +23,8 @@ class Revived extends Notification implements ShouldQueue
 | 
				
			|||||||
        if ($this->server->unreachable_notification_sent === false) {
 | 
					        if ($this->server->unreachable_notification_sent === false) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        dispatch(new ContainerStatusJob($server));
 | 
					        GetContainersStatus::dispatch($server);
 | 
				
			||||||
 | 
					        // dispatch(new ContainerStatusJob($server));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function via(object $notifiable): array
 | 
					    public function via(object $notifiable): array
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ use App\Models\Application;
 | 
				
			|||||||
use App\Models\ApplicationDeploymentQueue;
 | 
					use App\Models\ApplicationDeploymentQueue;
 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
use App\Models\StandaloneDocker;
 | 
					use App\Models\StandaloneDocker;
 | 
				
			||||||
use Illuminate\Support\Collection;
 | 
					 | 
				
			||||||
use Spatie\Url\Url;
 | 
					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, bool $rollback = 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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ use App\Models\ServiceApplication;
 | 
				
			|||||||
use Illuminate\Support\Collection;
 | 
					use Illuminate\Support\Collection;
 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
use Spatie\Url\Url;
 | 
					use Spatie\Url\Url;
 | 
				
			||||||
 | 
					use Visus\Cuid2\Cuid2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null, ?bool $includePullrequests = false): Collection
 | 
					function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null, ?bool $includePullrequests = false): Collection
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -272,7 +273,7 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    return $labels->sort();
 | 
					    return $labels->sort();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null)
 | 
					function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    $labels = collect([]);
 | 
					    $labels = collect([]);
 | 
				
			||||||
    $labels->push('traefik.enable=true');
 | 
					    $labels->push('traefik.enable=true');
 | 
				
			||||||
@@ -313,7 +314,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    foreach ($domains as $loop => $domain) {
 | 
					    foreach ($domains as $loop => $domain) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            // $uuid = new Cuid2(7);
 | 
					            if ($generate_unique_uuid) {
 | 
				
			||||||
 | 
					                $uuid = new Cuid2(7);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            $url = Url::fromString($domain);
 | 
					            $url = Url::fromString($domain);
 | 
				
			||||||
            $host = $url->getHost();
 | 
					            $host = $url->getHost();
 | 
				
			||||||
            $path = $url->getPath();
 | 
					            $path = $url->getPath();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ function collectRegex(string $name)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
function replaceVariables($variable)
 | 
					function replaceVariables($variable)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return $variable->replaceFirst('$', '')->replaceFirst('{', '')->replaceLast('}', '');
 | 
					    return $variable->before('}')->replaceFirst('$', '')->replaceFirst('{', '');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Application $oneService, bool $isInit = false)
 | 
					function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Application $oneService, bool $isInit = false)
 | 
				
			||||||
@@ -27,7 +27,7 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli
 | 
				
			|||||||
        if ($oneService->getMorphClass() === 'App\Models\Application') {
 | 
					        if ($oneService->getMorphClass() === 'App\Models\Application') {
 | 
				
			||||||
            $workdir = $oneService->workdir();
 | 
					            $workdir = $oneService->workdir();
 | 
				
			||||||
            $server = $oneService->destination->server;
 | 
					            $server = $oneService->destination->server;
 | 
				
			||||||
        } else{
 | 
					        } else {
 | 
				
			||||||
            $workdir = $oneService->service->workdir();
 | 
					            $workdir = $oneService->service->workdir();
 | 
				
			||||||
            $server = $oneService->service->server;
 | 
					            $server = $oneService->service->server;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -95,6 +95,9 @@ function currentTeam()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
function showBoarding(): bool
 | 
					function showBoarding(): bool
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    if (auth()->user()?->isMember()) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return currentTeam()->show_boarding ?? false;
 | 
					    return currentTeam()->show_boarding ?? false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function refreshSession(?Team $team = null): void
 | 
					function refreshSession(?Team $team = null): void
 | 
				
			||||||
@@ -147,6 +150,18 @@ function get_route_parameters(): array
 | 
				
			|||||||
    return Route::current()->parameters();
 | 
					    return Route::current()->parameters();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_latest_sentinel_version(): string
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        $response = Http::get('https://cdn.coollabs.io/coolify/versions.json');
 | 
				
			||||||
 | 
					        $versions = $response->json();
 | 
				
			||||||
 | 
					        return data_get($versions, 'coolify.sentinel.version');
 | 
				
			||||||
 | 
					    } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					        //throw $e;
 | 
				
			||||||
 | 
					        ray($e->getMessage());
 | 
				
			||||||
 | 
					        return '0.0.0';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
function get_latest_version_of_coolify(): string
 | 
					function get_latest_version_of_coolify(): string
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
@@ -637,7 +652,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
            $allServices = getServiceTemplates();
 | 
					            $allServices = getServiceTemplates();
 | 
				
			||||||
            $topLevelVolumes = collect(data_get($yaml, 'volumes', []));
 | 
					            $topLevelVolumes = collect(data_get($yaml, 'volumes', []));
 | 
				
			||||||
            $topLevelNetworks = collect(data_get($yaml, 'networks', []));
 | 
					            $topLevelNetworks = collect(data_get($yaml, 'networks', []));
 | 
				
			||||||
            $dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
 | 
					 | 
				
			||||||
            $services = data_get($yaml, 'services');
 | 
					            $services = data_get($yaml, 'services');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $generatedServiceFQDNS = collect([]);
 | 
					            $generatedServiceFQDNS = collect([]);
 | 
				
			||||||
@@ -988,20 +1002,18 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
                            if ($fqdns_exploded->count() > 1) {
 | 
					                            if ($fqdns_exploded->count() > 1) {
 | 
				
			||||||
                                continue;
 | 
					                                continue;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            if ($resource->server->proxyType() === 'CADDY') {
 | 
					                            $env = EnvironmentVariable::where([
 | 
				
			||||||
                                $env = EnvironmentVariable::where([
 | 
					                                'key' => $key,
 | 
				
			||||||
                                    'key' => $key,
 | 
					                                'service_id' => $resource->id,
 | 
				
			||||||
                                    'service_id' => $resource->id,
 | 
					                            ])->first();
 | 
				
			||||||
                                ])->first();
 | 
					                            if ($env) {
 | 
				
			||||||
                                if ($env) {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                    $env_url = Url::fromString($savedService->fqdn);
 | 
					                                $env_url = Url::fromString($savedService->fqdn);
 | 
				
			||||||
                                    $env_port = $env_url->getPort();
 | 
					                                $env_port = $env_url->getPort();
 | 
				
			||||||
                                    if ($env_port !== $predefinedPort) {
 | 
					                                if ($env_port !== $predefinedPort) {
 | 
				
			||||||
                                        $env_url = $env_url->withPort($predefinedPort);
 | 
					                                    $env_url = $env_url->withPort($predefinedPort);
 | 
				
			||||||
                                        $savedService->fqdn = $env_url->__toString();
 | 
					                                    $savedService->fqdn = $env_url->__toString();
 | 
				
			||||||
                                        $savedService->save();
 | 
					                                    $savedService->save();
 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
@@ -1165,6 +1177,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
                        ]
 | 
					                        ]
 | 
				
			||||||
                    ]);
 | 
					                    ]);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                if ($serviceLabels->count() > 0) {
 | 
				
			||||||
 | 
					                    if ($resource->is_container_label_escape_enabled) {
 | 
				
			||||||
 | 
					                        $serviceLabels = $serviceLabels->map(function ($value, $key) {
 | 
				
			||||||
 | 
					                            return escapeDollarSign($value);
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                data_set($service, 'labels', $serviceLabels->toArray());
 | 
					                data_set($service, 'labels', $serviceLabels->toArray());
 | 
				
			||||||
                data_forget($service, 'is_database');
 | 
					                data_forget($service, 'is_database');
 | 
				
			||||||
                if (!data_get($service, 'restart')) {
 | 
					                if (!data_get($service, 'restart')) {
 | 
				
			||||||
@@ -1194,7 +1213,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
                return $service;
 | 
					                return $service;
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            $finalServices = [
 | 
					            $finalServices = [
 | 
				
			||||||
                'version' => $dockerComposeVersion,
 | 
					 | 
				
			||||||
                'services' => $services->toArray(),
 | 
					                'services' => $services->toArray(),
 | 
				
			||||||
                'volumes' => $topLevelVolumes->toArray(),
 | 
					                'volumes' => $topLevelVolumes->toArray(),
 | 
				
			||||||
                'networks' => $topLevelNetworks->toArray(),
 | 
					                'networks' => $topLevelNetworks->toArray(),
 | 
				
			||||||
@@ -1217,13 +1235,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
            try {
 | 
					            try {
 | 
				
			||||||
                $yaml = Yaml::parse($resource->docker_compose_pr_raw);
 | 
					                $yaml = Yaml::parse($resource->docker_compose_pr_raw);
 | 
				
			||||||
            } catch (\Exception $e) {
 | 
					            } catch (\Exception $e) {
 | 
				
			||||||
                throw new \Exception($e->getMessage());
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                $yaml = Yaml::parse($resource->docker_compose_raw);
 | 
					                $yaml = Yaml::parse($resource->docker_compose_raw);
 | 
				
			||||||
            } catch (\Exception $e) {
 | 
					            } catch (\Exception $e) {
 | 
				
			||||||
                throw new \Exception($e->getMessage());
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $server = $resource->destination->server;
 | 
					        $server = $resource->destination->server;
 | 
				
			||||||
@@ -1232,7 +1250,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
            $topLevelVolumes = collect([]);
 | 
					            $topLevelVolumes = collect([]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $topLevelNetworks = collect(data_get($yaml, 'networks', []));
 | 
					        $topLevelNetworks = collect(data_get($yaml, 'networks', []));
 | 
				
			||||||
        $dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
 | 
					 | 
				
			||||||
        $services = data_get($yaml, 'services');
 | 
					        $services = data_get($yaml, 'services');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $generatedServiceFQDNS = collect([]);
 | 
					        $generatedServiceFQDNS = collect([]);
 | 
				
			||||||
@@ -1252,6 +1269,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
            $servicePorts = collect(data_get($service, 'ports', []));
 | 
					            $servicePorts = collect(data_get($service, 'ports', []));
 | 
				
			||||||
            $serviceNetworks = collect(data_get($service, 'networks', []));
 | 
					            $serviceNetworks = collect(data_get($service, 'networks', []));
 | 
				
			||||||
            $serviceVariables = collect(data_get($service, 'environment', []));
 | 
					            $serviceVariables = collect(data_get($service, 'environment', []));
 | 
				
			||||||
 | 
					            $serviceDependencies = collect(data_get($service, 'depends_on', []));
 | 
				
			||||||
            $serviceLabels = collect(data_get($service, 'labels', []));
 | 
					            $serviceLabels = collect(data_get($service, 'labels', []));
 | 
				
			||||||
            $serviceBuildVariables = collect(data_get($service, 'build.args', []));
 | 
					            $serviceBuildVariables = collect(data_get($service, 'build.args', []));
 | 
				
			||||||
            $serviceVariables = $serviceVariables->merge($serviceBuildVariables);
 | 
					            $serviceVariables = $serviceVariables->merge($serviceBuildVariables);
 | 
				
			||||||
@@ -1268,11 +1286,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
                    $serviceLabels->push("$removedLabelName=$removedLabel");
 | 
					                    $serviceLabels->push("$removedLabelName=$removedLabel");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if ($serviceLabels->count() > 0) {
 | 
					
 | 
				
			||||||
                $serviceLabels = $serviceLabels->map(function ($value, $key) {
 | 
					 | 
				
			||||||
                    return escapeDollarSign($value);
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            $baseName = generateApplicationContainerName($resource, $pull_request_id);
 | 
					            $baseName = generateApplicationContainerName($resource, $pull_request_id);
 | 
				
			||||||
            $containerName = "$serviceName-$baseName";
 | 
					            $containerName = "$serviceName-$baseName";
 | 
				
			||||||
            if (count($serviceVolumes) > 0) {
 | 
					            if (count($serviceVolumes) > 0) {
 | 
				
			||||||
@@ -1363,6 +1377,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
                data_set($service, 'volumes', $serviceVolumes->toArray());
 | 
					                data_set($service, 'volumes', $serviceVolumes->toArray());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($pull_request_id !== 0 && count($serviceDependencies) > 0) {
 | 
				
			||||||
 | 
					                $serviceDependencies = $serviceDependencies->map(function ($dependency) use ($pull_request_id) {
 | 
				
			||||||
 | 
					                    return $dependency . "-pr-$pull_request_id";
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                data_set($service, 'depends_on', $serviceDependencies->toArray());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Decide if the service is a database
 | 
					            // Decide if the service is a database
 | 
				
			||||||
            $isDatabase = isDatabaseImage(data_get_str($service, 'image'));
 | 
					            $isDatabase = isDatabaseImage(data_get_str($service, 'image'));
 | 
				
			||||||
            data_set($service, 'is_database', $isDatabase);
 | 
					            data_set($service, 'is_database', $isDatabase);
 | 
				
			||||||
@@ -1620,7 +1641,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
                        $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
 | 
					                        $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
 | 
				
			||||||
                            uuid: $resource->uuid,
 | 
					                            uuid: $resource->uuid,
 | 
				
			||||||
                            domains: $fqdns,
 | 
					                            domains: $fqdns,
 | 
				
			||||||
                            serviceLabels: $serviceLabels
 | 
					                            serviceLabels: $serviceLabels,
 | 
				
			||||||
 | 
					                            generate_unique_uuid: $resource->build_pack === 'dockercompose'
 | 
				
			||||||
                        ));
 | 
					                        ));
 | 
				
			||||||
                        $serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
 | 
					                        $serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
 | 
				
			||||||
                            network: $resource->destination->network,
 | 
					                            network: $resource->destination->network,
 | 
				
			||||||
@@ -1644,6 +1666,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
                    ]
 | 
					                    ]
 | 
				
			||||||
                ]);
 | 
					                ]);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if ($serviceLabels->count() > 0) {
 | 
				
			||||||
 | 
					                if ($resource->settings->is_container_label_escape_enabled) {
 | 
				
			||||||
 | 
					                    $serviceLabels = $serviceLabels->map(function ($value, $key) {
 | 
				
			||||||
 | 
					                        return escapeDollarSign($value);
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            data_set($service, 'labels', $serviceLabels->toArray());
 | 
					            data_set($service, 'labels', $serviceLabels->toArray());
 | 
				
			||||||
            data_forget($service, 'is_database');
 | 
					            data_forget($service, 'is_database');
 | 
				
			||||||
            if (!data_get($service, 'restart')) {
 | 
					            if (!data_get($service, 'restart')) {
 | 
				
			||||||
@@ -1662,7 +1691,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $finalServices = [
 | 
					        $finalServices = [
 | 
				
			||||||
            'version' => $dockerComposeVersion,
 | 
					 | 
				
			||||||
            'services' => $services->toArray(),
 | 
					            'services' => $services->toArray(),
 | 
				
			||||||
            'volumes' => $topLevelVolumes->toArray(),
 | 
					            'volumes' => $topLevelVolumes->toArray(),
 | 
				
			||||||
            'networks' => $topLevelNetworks->toArray(),
 | 
					            'networks' => $topLevelNetworks->toArray(),
 | 
				
			||||||
@@ -1842,7 +1870,7 @@ function validate_dns_entry(string $fqdn, Server $server)
 | 
				
			|||||||
    $dns_servers = data_get($settings, 'custom_dns_servers');
 | 
					    $dns_servers = data_get($settings, 'custom_dns_servers');
 | 
				
			||||||
    $dns_servers = str($dns_servers)->explode(',');
 | 
					    $dns_servers = str($dns_servers)->explode(',');
 | 
				
			||||||
    if ($server->id === 0) {
 | 
					    if ($server->id === 0) {
 | 
				
			||||||
        $ip = data_get($settings, 'public_ipv4') || data_get($settings, 'public_ipv6') || $server->ip;
 | 
					        $ip = data_get($settings, 'public_ipv4', data_get($settings, 'public_ipv6', $server->ip));
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        $ip = $server->ip;
 | 
					        $ip = $server->ip;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -1921,7 +1949,6 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
 | 
				
			|||||||
            $naked_domain = str($domain)->value();
 | 
					            $naked_domain = str($domain)->value();
 | 
				
			||||||
            if ($domains->contains($naked_domain)) {
 | 
					            if ($domains->contains($naked_domain)) {
 | 
				
			||||||
                if (data_get($resource, 'uuid')) {
 | 
					                if (data_get($resource, 'uuid')) {
 | 
				
			||||||
                    ray($resource->uuid, $app->uuid);
 | 
					 | 
				
			||||||
                    if ($resource->uuid !== $app->uuid) {
 | 
					                    if ($resource->uuid !== $app->uuid) {
 | 
				
			||||||
                        throw new \RuntimeException("Domain $naked_domain is already in use by another resource called: <br><br>{$app->name}.");
 | 
					                        throw new \RuntimeException("Domain $naked_domain is already in use by another resource called: <br><br>{$app->name}.");
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,7 @@ return [
 | 
				
			|||||||
            'basic' => env('LIMIT_SERVER_BASIC', 2),
 | 
					            'basic' => env('LIMIT_SERVER_BASIC', 2),
 | 
				
			||||||
            'pro' => env('LIMIT_SERVER_PRO', 10),
 | 
					            'pro' => env('LIMIT_SERVER_PRO', 10),
 | 
				
			||||||
            'ultimate' => env('LIMIT_SERVER_ULTIMATE', 25),
 | 
					            'ultimate' => env('LIMIT_SERVER_ULTIMATE', 25),
 | 
				
			||||||
 | 
					            'dynamic' => env('LIMIT_SERVER_DYNAMIC', 2),
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        'email' => [
 | 
					        'email' => [
 | 
				
			||||||
            'zero' => true,
 | 
					            'zero' => true,
 | 
				
			||||||
@@ -39,6 +40,7 @@ return [
 | 
				
			|||||||
            'basic' => true,
 | 
					            'basic' => true,
 | 
				
			||||||
            'pro' => true,
 | 
					            'pro' => true,
 | 
				
			||||||
            'ultimate' => true,
 | 
					            'ultimate' => true,
 | 
				
			||||||
 | 
					            'dynamic' => true,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,4 +14,5 @@ return [
 | 
				
			|||||||
    'helper_image' => env('HELPER_IMAGE', 'ghcr.io/coollabsio/coolify-helper:latest'),
 | 
					    'helper_image' => env('HELPER_IMAGE', 'ghcr.io/coollabsio/coolify-helper:latest'),
 | 
				
			||||||
    'is_horizon_enabled' => env('HORIZON_ENABLED', true),
 | 
					    'is_horizon_enabled' => env('HORIZON_ENABLED', true),
 | 
				
			||||||
    'is_scheduler_enabled' => env('SCHEDULER_ENABLED', true),
 | 
					    'is_scheduler_enabled' => env('SCHEDULER_ENABLED', true),
 | 
				
			||||||
 | 
					    'is_sentinel_enabled' => env('SENTINEL_ENABLED', false),
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ return [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // The release version of your application
 | 
					    // The release version of your application
 | 
				
			||||||
    // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
 | 
					    // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
 | 
				
			||||||
    'release' => '4.0.0-beta.269',
 | 
					    'release' => '4.0.0-beta.285',
 | 
				
			||||||
    // When left empty or `null` the Laravel environment will be used
 | 
					    // When left empty or `null` the Laravel environment will be used
 | 
				
			||||||
    'environment' => config('app.env'),
 | 
					    'environment' => config('app.env'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,3 @@
 | 
				
			|||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return '4.0.0-beta.269';
 | 
					return '4.0.0-beta.285';
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user