diff --git a/app/Actions/Database/StartClickhouse.php b/app/Actions/Database/StartClickhouse.php
index d043da410..5f567802f 100644
--- a/app/Actions/Database/StartClickhouse.php
+++ b/app/Actions/Database/StartClickhouse.php
@@ -33,7 +33,6 @@ class StartClickhouse
$environment_variables = $this->generate_environment_variables();
$docker_compose = [
- 'version' => '3.8',
'services' => [
$container_name => [
'image' => $this->database->image,
diff --git a/app/Actions/Database/StartDatabaseProxy.php b/app/Actions/Database/StartDatabaseProxy.php
index a1e47710c..547884b7a 100644
--- a/app/Actions/Database/StartDatabaseProxy.php
+++ b/app/Actions/Database/StartDatabaseProxy.php
@@ -107,7 +107,6 @@ class StartDatabaseProxy
COPY nginx.conf /etc/nginx/nginx.conf
EOF;
$docker_compose = [
- 'version' => '3.8',
'services' => [
$proxyContainerName => [
'build' => [
diff --git a/app/Actions/Database/StartDragonfly.php b/app/Actions/Database/StartDragonfly.php
index bb71d8c48..92daf195d 100644
--- a/app/Actions/Database/StartDragonfly.php
+++ b/app/Actions/Database/StartDragonfly.php
@@ -36,7 +36,6 @@ class StartDragonfly
$environment_variables = $this->generate_environment_variables();
$docker_compose = [
- 'version' => '3.8',
'services' => [
$container_name => [
'image' => $this->database->image,
diff --git a/app/Actions/Database/StartKeydb.php b/app/Actions/Database/StartKeydb.php
index 489c74053..8c833efd5 100644
--- a/app/Actions/Database/StartKeydb.php
+++ b/app/Actions/Database/StartKeydb.php
@@ -37,7 +37,6 @@ class StartKeydb
$this->add_custom_keydb();
$docker_compose = [
- 'version' => '3.8',
'services' => [
$container_name => [
'image' => $this->database->image,
@@ -96,7 +95,7 @@ class StartKeydb
if (count($volume_names) > 0) {
$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'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/keydb.conf',
@@ -162,7 +161,7 @@ class StartKeydb
}
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;
}
$filename = 'keydb.conf';
diff --git a/app/Actions/Database/StartMariadb.php b/app/Actions/Database/StartMariadb.php
index e02b28b2e..c79df0dc5 100644
--- a/app/Actions/Database/StartMariadb.php
+++ b/app/Actions/Database/StartMariadb.php
@@ -32,7 +32,6 @@ class StartMariadb
$environment_variables = $this->generate_environment_variables();
$this->add_custom_mysql();
$docker_compose = [
- 'version' => '3.8',
'services' => [
$container_name => [
'image' => $this->database->image,
@@ -90,7 +89,7 @@ class StartMariadb
if (count($volume_names) > 0) {
$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'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/custom-config.cnf',
@@ -165,7 +164,7 @@ class StartMariadb
}
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;
}
$filename = 'custom-config.cnf';
diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php
index 7bb6cbcd0..99403c2c1 100644
--- a/app/Actions/Database/StartMongodb.php
+++ b/app/Actions/Database/StartMongodb.php
@@ -35,7 +35,6 @@ class StartMongodb
$this->add_custom_mongo_conf();
$docker_compose = [
- 'version' => '3.8',
'services' => [
$container_name => [
'image' => $this->database->image,
@@ -97,7 +96,7 @@ class StartMongodb
if (count($volume_names) > 0) {
$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'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/mongod.conf',
@@ -178,7 +177,7 @@ class StartMongodb
}
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;
}
$filename = 'mongod.conf';
diff --git a/app/Actions/Database/StartMysql.php b/app/Actions/Database/StartMysql.php
index b3f695d72..6fdc8cdad 100644
--- a/app/Actions/Database/StartMysql.php
+++ b/app/Actions/Database/StartMysql.php
@@ -32,7 +32,6 @@ class StartMysql
$environment_variables = $this->generate_environment_variables();
$this->add_custom_mysql();
$docker_compose = [
- 'version' => '3.8',
'services' => [
$container_name => [
'image' => $this->database->image,
@@ -90,7 +89,7 @@ class StartMysql
if (count($volume_names) > 0) {
$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'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/custom-config.cnf',
@@ -165,7 +164,7 @@ class StartMysql
}
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;
}
$filename = 'custom-config.cnf';
diff --git a/app/Actions/Database/StartPostgresql.php b/app/Actions/Database/StartPostgresql.php
index f19a8b036..8db874ea6 100644
--- a/app/Actions/Database/StartPostgresql.php
+++ b/app/Actions/Database/StartPostgresql.php
@@ -35,7 +35,6 @@ class StartPostgresql
$this->add_custom_conf();
$docker_compose = [
- 'version' => '3.8',
'services' => [
$container_name => [
'image' => $this->database->image,
@@ -78,7 +77,6 @@ class StartPostgresql
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
}
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
- ray('Log Drain Enabled');
$docker_compose['services'][$container_name]['logging'] = [
'driver' => 'fluentd',
'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'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/custom-postgres.conf',
@@ -165,8 +163,6 @@ class StartPostgresql
private function generate_environment_variables()
{
$environment_variables = collect();
- ray('Generate Environment Variables')->green();
- ray($this->database->runtime_environment_variables)->green();
foreach ($this->database->runtime_environment_variables as $env) {
$environment_variables->push("$env->key=$env->real_value");
}
@@ -203,11 +199,16 @@ class StartPostgresql
}
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;
}
$filename = 'custom-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);
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
}
diff --git a/app/Actions/Database/StartRedis.php b/app/Actions/Database/StartRedis.php
index 01e9a9bef..5b6ab2999 100644
--- a/app/Actions/Database/StartRedis.php
+++ b/app/Actions/Database/StartRedis.php
@@ -37,7 +37,6 @@ class StartRedis
$this->add_custom_redis();
$docker_compose = [
- 'version' => '3.8',
'services' => [
$container_name => [
'image' => $this->database->image,
@@ -100,7 +99,7 @@ class StartRedis
if (count($volume_names) > 0) {
$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'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/redis.conf',
@@ -166,7 +165,7 @@ class StartRedis
}
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;
}
$filename = 'redis.conf';
diff --git a/app/Actions/Docker/GetContainersStatus.php b/app/Actions/Docker/GetContainersStatus.php
new file mode 100644
index 000000000..667a8c92e
--- /dev/null
+++ b/app/Actions/Docker/GetContainersStatus.php
@@ -0,0 +1,657 @@
+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 = $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 = $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);
+ }
+ }
+}
diff --git a/app/Actions/Server/StartSentinel.php b/app/Actions/Server/StartSentinel.php
new file mode 100644
index 000000000..6f3c81d77
--- /dev/null
+++ b/app/Actions/Server/StartSentinel.php
@@ -0,0 +1,22 @@
+where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
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)
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index e3221e091..69bfa29d0 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -2,6 +2,7 @@
namespace App\Jobs;
+use App\Actions\Docker\GetContainersStatus;
use App\Enums\ApplicationDeploymentStatus;
use App\Enums\ProcessStatus;
use App\Events\ApplicationStatusChanged;
@@ -302,7 +303,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
{
if ($this->server->isProxyShouldRun()) {
- dispatch(new ContainerStatusJob($this->server));
+ GetContainersStatus::dispatch($this->server);
+ // dispatch(new ContainerStatusJob($this->server));
}
$this->next(ApplicationDeploymentStatus::FINISHED->value);
if ($this->pull_request_id !== 0) {
@@ -1020,7 +1022,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
"command" => "docker rm -f {$this->deployment_uuid}",
"ignore_errors" => true,
"hidden" => true
- ],
+ ]
+ );
+ $this->execute_remote_command(
[
$runCommand,
"hidden" => true,
@@ -1287,7 +1291,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->application->parseHealthcheckFromDockerfile($dockerfile);
}
$docker_compose = [
- 'version' => '3.8',
'services' => [
$this->container_name => [
'image' => $this->production_image_name,
diff --git a/app/Jobs/ContainerStatusJob.php b/app/Jobs/ContainerStatusJob.php
index a9cec009b..9ffd68f11 100644
--- a/app/Jobs/ContainerStatusJob.php
+++ b/app/Jobs/ContainerStatusJob.php
@@ -2,15 +2,8 @@
namespace App\Jobs;
-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\Actions\Docker\GetContainersStatus;
use App\Models\Server;
-use App\Models\ServiceDatabase;
-use App\Notifications\Container\ContainerRestarted;
-use App\Notifications\Container\ContainerStopped;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -18,7 +11,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
-use Illuminate\Support\Arr;
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
{
@@ -44,335 +36,337 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
public function handle()
{
- if (!$this->server->isFunctional()) {
- 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;
- }
+ GetContainersStatus::run($this->server);
+ return;
+ // if (!$this->server->isFunctional()) {
+ // 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 = [];
+ // $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');
+ // 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 ($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']);
- }
+ // 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']);
+ // $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');
+ // $name = data_get($application, 'name');
+ // $fqdn = data_get($application, 'fqdn');
- $containerName = $name ? "$name ($fqdn)" : $fqdn;
+ // $containerName = $name ? "$name ($fqdn)" : $fqdn;
- $projectUuid = data_get($application, 'environment.project.uuid');
- $applicationUuid = data_get($application, 'uuid');
- $environment = data_get($application, 'environment.name');
+ // $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;
- }
+ // 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']);
+ // $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');
+ // $name = data_get($preview, 'name');
+ // $fqdn = data_get($preview, 'fqdn');
- $containerName = $name ? "$name ($fqdn)" : $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');
+ // $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;
- }
+ // 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']);
+ // $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');
+ // $name = data_get($database, 'name');
+ // $fqdn = data_get($database, 'fqdn');
- $containerName = $name;
+ // $containerName = $name;
- $projectUuid = data_get($database, 'environment.project.uuid');
- $environmentName = data_get($database, 'environment.name');
- $databaseUuid = data_get($database, 'uuid');
+ // $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));
- }
+ // 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);
- }
+ // // 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);
+ // }
}
}
diff --git a/app/Jobs/PullSentinelImageJob.php b/app/Jobs/PullSentinelImageJob.php
new file mode 100644
index 000000000..1c51928f6
--- /dev/null
+++ b/app/Jobs/PullSentinelImageJob.php
@@ -0,0 +1,56 @@
+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;
+ }
+ }
+}
diff --git a/app/Jobs/ServerStatusJob.php b/app/Jobs/ServerStatusJob.php
index 6402b81cf..449ab85a0 100644
--- a/app/Jobs/ServerStatusJob.php
+++ b/app/Jobs/ServerStatusJob.php
@@ -44,6 +44,9 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
if ($this->server->isFunctional()) {
$this->cleanup(notify: false);
$this->removeCoolifyYaml();
+ if (config('coolify.is_sentinel_enabled')) {
+ $this->server->checkSentinel();
+ }
}
} catch (\Throwable $e) {
send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
diff --git a/app/Livewire/Project/Application/Heading.php b/app/Livewire/Project/Application/Heading.php
index 9adee7003..06b9a3c3c 100644
--- a/app/Livewire/Project/Application/Heading.php
+++ b/app/Livewire/Project/Application/Heading.php
@@ -3,6 +3,7 @@
namespace App\Livewire\Project\Application;
use App\Actions\Application\StopApplication;
+use App\Actions\Docker\GetContainersStatus;
use App\Events\ApplicationStatusChanged;
use App\Jobs\ContainerStatusJob;
use App\Jobs\ServerStatusJob;
@@ -32,7 +33,8 @@ class Heading extends Component
public function check_status($showNotification = false)
{
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 {
dispatch(new ServerStatusJob($this->application->destination->server));
}
diff --git a/app/Livewire/Project/Database/Heading.php b/app/Livewire/Project/Database/Heading.php
index 960ff2689..d6a0fe087 100644
--- a/app/Livewire/Project/Database/Heading.php
+++ b/app/Livewire/Project/Database/Heading.php
@@ -11,6 +11,7 @@ use App\Actions\Database\StartMysql;
use App\Actions\Database\StartPostgresql;
use App\Actions\Database\StartRedis;
use App\Actions\Database\StopDatabase;
+use App\Actions\Docker\GetContainersStatus;
use App\Jobs\ContainerStatusJob;
use Livewire\Component;
@@ -44,7 +45,8 @@ class Heading extends Component
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();
if ($showNotification) $this->dispatch('success', 'Database status updated.');
}
diff --git a/app/Livewire/Project/New/GithubPrivateRepository.php b/app/Livewire/Project/New/GithubPrivateRepository.php
index 322fd4a4e..58e3fe586 100644
--- a/app/Livewire/Project/New/GithubPrivateRepository.php
+++ b/app/Livewire/Project/New/GithubPrivateRepository.php
@@ -150,7 +150,7 @@ class GithubPrivateRepository extends Component
'repository_project_id' => $this->selected_repository_id,
'git_repository' => "{$this->selected_repository_owner}/{$this->selected_repository_repo}",
'git_branch' => $this->selected_branch_name,
- 'build_pack' => 'nixpacks',
+ 'build_pack' => $this->build_pack,
'ports_exposes' => $this->port,
'publish_directory' => $this->publish_directory,
'environment_id' => $environment->id,
@@ -162,6 +162,9 @@ class GithubPrivateRepository extends Component
$application->settings->is_static = $this->is_static;
$application->settings->save();
+ if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
+ $application->health_check_enabled = false;
+ }
$fqdn = generateFqdn($destination->server, $application->uuid);
$application->fqdn = $fqdn;
diff --git a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php
index ad52b9070..691b246fd 100644
--- a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php
+++ b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php
@@ -19,7 +19,7 @@ class GithubPrivateRepositoryDeployKey extends Component
public $current_step = 'private_keys';
public $parameters;
public $query;
- public $private_keys =[];
+ public $private_keys = [];
public int $private_key_id;
public int $port = 3000;
@@ -125,7 +125,7 @@ class GithubPrivateRepositoryDeployKey extends Component
'name' => generate_random_name(),
'git_repository' => $this->git_repository,
'git_branch' => $this->branch,
- 'build_pack' => 'nixpacks',
+ 'build_pack' => $this->build_pack,
'ports_exposes' => $this->port,
'publish_directory' => $this->publish_directory,
'environment_id' => $environment->id,
@@ -138,7 +138,7 @@ class GithubPrivateRepositoryDeployKey extends Component
'name' => generate_random_name(),
'git_repository' => $this->git_repository,
'git_branch' => $this->branch,
- 'build_pack' => 'nixpacks',
+ 'build_pack' => $this->build_pack,
'ports_exposes' => $this->port,
'publish_directory' => $this->publish_directory,
'environment_id' => $environment->id,
@@ -149,7 +149,9 @@ class GithubPrivateRepositoryDeployKey extends Component
'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->settings->is_static = $this->is_static;
$application->settings->save();
diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php
index b71a0b670..f4f3008d4 100644
--- a/app/Livewire/Project/New/PublicGitRepository.php
+++ b/app/Livewire/Project/New/PublicGitRepository.php
@@ -205,6 +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->settings->is_static = $this->is_static;
diff --git a/app/Livewire/Project/Service/Configuration.php b/app/Livewire/Project/Service/Configuration.php
index 2cbda4e02..0b26af22f 100644
--- a/app/Livewire/Project/Service/Configuration.php
+++ b/app/Livewire/Project/Service/Configuration.php
@@ -2,6 +2,7 @@
namespace App\Livewire\Project\Service;
+use App\Actions\Docker\GetContainersStatus;
use App\Jobs\ContainerStatusJob;
use App\Models\Service;
use Livewire\Component;
@@ -64,7 +65,8 @@ class Configuration extends Component
public function check_status()
{
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('updateStatus');
} catch (\Exception $e) {
diff --git a/app/Livewire/Project/Shared/Destination.php b/app/Livewire/Project/Shared/Destination.php
index fa19e8c42..2ccae47fd 100644
--- a/app/Livewire/Project/Shared/Destination.php
+++ b/app/Livewire/Project/Shared/Destination.php
@@ -3,6 +3,7 @@
namespace App\Livewire\Project\Shared;
use App\Actions\Application\StopApplicationOneServer;
+use App\Actions\Docker\GetContainersStatus;
use App\Events\ApplicationStatusChanged;
use App\Jobs\ContainerStatusJob;
use App\Models\Server;
@@ -90,7 +91,8 @@ class Destination extends Component
}
public function refreshServers()
{
- ContainerStatusJob::dispatchSync($this->resource->destination->server);
+ GetContainersStatus::run($this->resource->destination->server);
+ // ContainerStatusJob::dispatchSync($this->resource->destination->server);
$this->loadData();
$this->dispatch('refresh');
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
diff --git a/app/Livewire/Project/Shared/ExecuteContainerCommand.php b/app/Livewire/Project/Shared/ExecuteContainerCommand.php
index 52d628dc1..4fc8bb8c6 100644
--- a/app/Livewire/Project/Shared/ExecuteContainerCommand.php
+++ b/app/Livewire/Project/Shared/ExecuteContainerCommand.php
@@ -122,7 +122,7 @@ class ExecuteContainerCommand extends Component
if ($server->isForceDisabled()) {
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)) {
$exec = "docker exec -w {$this->workDir} {$container_name} {$cmd}";
} else {
diff --git a/app/Livewire/Project/Shared/Logs.php b/app/Livewire/Project/Shared/Logs.php
index 68e4e193e..f1d70bf28 100644
--- a/app/Livewire/Project/Shared/Logs.php
+++ b/app/Livewire/Project/Shared/Logs.php
@@ -27,7 +27,7 @@ class Logs extends Component
public $query;
public $status;
public $serviceSubType;
-
+ public $cpu;
public function loadContainers($server_id)
{
try {
@@ -49,6 +49,14 @@ class Logs extends Component
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()
{
try {
@@ -95,6 +103,7 @@ class Logs extends Component
}
}
$this->containers = $this->containers->sort();
+ $this->loadMetrics();
} catch (\Exception $e) {
return handleError($e, $this);
}
diff --git a/app/Livewire/Server/Proxy/Status.php b/app/Livewire/Server/Proxy/Status.php
index bd0ffe431..fbc16fde4 100644
--- a/app/Livewire/Server/Proxy/Status.php
+++ b/app/Livewire/Server/Proxy/Status.php
@@ -2,6 +2,7 @@
namespace App\Livewire\Server\Proxy;
+use App\Actions\Docker\GetContainersStatus;
use App\Actions\Proxy\CheckProxy;
use App\Jobs\ContainerStatusJob;
use App\Models\Server;
@@ -49,7 +50,8 @@ class Status extends Component
public function getProxyStatus()
{
try {
- dispatch_sync(new ContainerStatusJob($this->server));
+ GetContainersStatus::run($this->server);
+ // dispatch_sync(new ContainerStatusJob($this->server));
$this->dispatch('proxyStatusUpdated');
} catch (\Throwable $e) {
return handleError($e, $this);
diff --git a/app/Livewire/Tags/Deployments.php b/app/Livewire/Tags/Deployments.php
index 5c43edfb1..07034ed5d 100644
--- a/app/Livewire/Tags/Deployments.php
+++ b/app/Livewire/Tags/Deployments.php
@@ -26,6 +26,7 @@ class Deployments extends Component
"server_id",
"status"
])->sortBy('id')->groupBy('server_name')->toArray();
+ $this->dispatch('deployments', $this->deployments_per_tag_per_server);
} catch (\Exception $e) {
return handleError($e, $this);
}
diff --git a/app/Livewire/Tags/Index.php b/app/Livewire/Tags/Index.php
index d04bb53f9..c2b2a5928 100644
--- a/app/Livewire/Tags/Index.php
+++ b/app/Livewire/Tags/Index.php
@@ -20,6 +20,12 @@ class Index extends Component
public $webhook = null;
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()
{
if ($this->tag == "") {
@@ -39,14 +45,13 @@ class Index extends Component
public function redeploy_all()
{
try {
- $message = collect([]);
- $this->applications->each(function ($resource) use ($message) {
+ $this->applications->each(function ($resource){
$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();
- $message->push($deploy->deploy_resource($resource));
+ $deploy->deploy_resource($resource);
});
$this->dispatch('success', 'Mass deployment started.');
} catch (\Exception $e) {
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 19da8d784..2f4c29080 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -3,11 +3,14 @@
namespace App\Models;
use App\Actions\Server\InstallDocker;
+use App\Actions\Server\StartSentinel;
use App\Enums\ProxyTypes;
+use App\Jobs\PullSentinelImageJob;
use App\Notifications\Server\Revived;
use App\Notifications\Server\Unreachable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
+use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
@@ -462,6 +465,36 @@ $schema://$host {
Storage::disk('ssh-keys')->delete($sshKeyFileLocation);
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)
{
if ($this->skipServer()) {
@@ -548,7 +581,36 @@ $schema://$host {
{
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()) {
$containers = instant_remote_process(["docker ps -a --format '{{json .}}'"], $this);
diff --git a/app/Models/Service.php b/app/Models/Service.php
index 5de1a9745..cd8e578d6 100644
--- a/app/Models/Service.php
+++ b/app/Models/Service.php
@@ -171,7 +171,7 @@ class Service extends BaseModel
],
]);
}
- $fields->put('Tolgee', $data);
+ $fields->put('Tolgee', $data->toArray());
break;
case str($image)?->contains('logto'):
$data = collect([]);
@@ -195,7 +195,7 @@ class Service extends BaseModel
],
]);
}
- $fields->put('Logto', $data);
+ $fields->put('Logto', $data->toArray());
break;
case str($image)?->contains('unleash-server'):
$data = collect([]);
@@ -218,7 +218,7 @@ class Service extends BaseModel
],
]);
}
- $fields->put('Unleash', $data);
+ $fields->put('Unleash', $data->toArray());
break;
case str($image)?->contains('grafana'):
$data = collect([]);
@@ -241,7 +241,7 @@ class Service extends BaseModel
],
]);
}
- $fields->put('Grafana', $data);
+ $fields->put('Grafana', $data->toArray());
break;
case str($image)?->contains('directus'):
$data = collect([]);
@@ -267,7 +267,7 @@ class Service extends BaseModel
],
]);
}
- $fields->put('Directus', $data);
+ $fields->put('Directus', $data->toArray());
break;
case str($image)?->contains('kong'):
$data = collect([]);
@@ -370,7 +370,7 @@ class Service extends BaseModel
],
]);
}
- $fields->put('Weblate', $data);
+ $fields->put('Weblate', $data->toArray());
break;
case str($image)?->contains('meilisearch'):
$data = collect([]);
@@ -384,7 +384,7 @@ class Service extends BaseModel
],
]);
}
- $fields->put('Meilisearch', $data);
+ $fields->put('Meilisearch', $data->toArray());
break;
case str($image)?->contains('ghost'):
$data = collect([]);
@@ -444,7 +444,31 @@ 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();
+ $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;
}
}
diff --git a/app/Notifications/Server/Revived.php b/app/Notifications/Server/Revived.php
index c670ded9a..36775976b 100644
--- a/app/Notifications/Server/Revived.php
+++ b/app/Notifications/Server/Revived.php
@@ -2,6 +2,7 @@
namespace App\Notifications\Server;
+use App\Actions\Docker\GetContainersStatus;
use App\Jobs\ContainerStatusJob;
use App\Models\Server;
use Illuminate\Bus\Queueable;
@@ -22,7 +23,8 @@ class Revived extends Notification implements ShouldQueue
if ($this->server->unreachable_notification_sent === false) {
return;
}
- dispatch(new ContainerStatusJob($server));
+ GetContainersStatus::dispatch($server);
+ // dispatch(new ContainerStatusJob($server));
}
public function via(object $notifiable): array
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index d5cacb06c..b93b0ec3d 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -147,6 +147,18 @@ function get_route_parameters(): array
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
{
try {
@@ -637,7 +649,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$allServices = getServiceTemplates();
$topLevelVolumes = collect(data_get($yaml, 'volumes', []));
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
- $dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
$services = data_get($yaml, 'services');
$generatedServiceFQDNS = collect([]);
@@ -1192,7 +1203,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
return $service;
});
$finalServices = [
- 'version' => $dockerComposeVersion,
'services' => $services->toArray(),
'volumes' => $topLevelVolumes->toArray(),
'networks' => $topLevelNetworks->toArray(),
@@ -1230,7 +1240,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$topLevelVolumes = collect([]);
}
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
- $dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
$services = data_get($yaml, 'services');
$generatedServiceFQDNS = collect([]);
@@ -1661,7 +1670,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
});
}
$finalServices = [
- 'version' => $dockerComposeVersion,
'services' => $services->toArray(),
'volumes' => $topLevelVolumes->toArray(),
'networks' => $topLevelNetworks->toArray(),
diff --git a/config/coolify.php b/config/coolify.php
index a6d6d8581..c7cfe6101 100644
--- a/config/coolify.php
+++ b/config/coolify.php
@@ -14,4 +14,5 @@ return [
'helper_image' => env('HELPER_IMAGE', 'ghcr.io/coollabsio/coolify-helper:latest'),
'is_horizon_enabled' => env('HORIZON_ENABLED', true),
'is_scheduler_enabled' => env('SCHEDULER_ENABLED', true),
+ 'is_sentinel_enabled' => env('SENTINEL_ENABLED', false),
];
diff --git a/config/sentry.php b/config/sentry.php
index f36eb7818..e65072dcc 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -7,7 +7,7 @@ return [
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
- 'release' => '4.0.0-beta.276',
+ 'release' => '4.0.0-beta.277',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),
diff --git a/config/version.php b/config/version.php
index ae9fc8db5..fea91e1f0 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
boolean('is_metrics_enabled')->default(true);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('servers', function (Blueprint $table) {
+ $table->dropColumn('is_metrics_enabled');
+ });
+ }
+};
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index 6d76a9abd..91e90b989 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -1,5 +1,3 @@
-version: "3.8"
-
services:
coolify:
build:
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index f68b2c41c..f3dda9748 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -1,4 +1,3 @@
-version: '3.8'
services:
coolify:
image: "ghcr.io/coollabsio/coolify:${LATEST_IMAGE:-latest}"
diff --git a/docker-compose.windows.yml b/docker-compose.windows.yml
index e35ece624..af5ecc0f7 100644
--- a/docker-compose.windows.yml
+++ b/docker-compose.windows.yml
@@ -1,4 +1,3 @@
-version: '3.8'
services:
coolify-testing-host:
init: true
diff --git a/docker-compose.yml b/docker-compose.yml
index 6adfaf98a..8eed44f8c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,4 +1,3 @@
-version: '3.8'
services:
coolify:
container_name: coolify
@@ -11,7 +10,6 @@ services:
depends_on:
- postgres
- redis
-
postgres:
image: postgres:15-alpine
container_name: coolify-db
diff --git a/other/scripts/get-subs.php b/other/scripts/get-subs.php
new file mode 100644
index 000000000..3a23fc073
--- /dev/null
+++ b/other/scripts/get-subs.php
@@ -0,0 +1,11 @@
+$handle = fopen("/tmp/export.csv", "w");
+App\Models\Team::chunk(100, function ($teams) use ($handle) {
+ foreach ($teams as $team) {
+ if ($team->subscription->stripe_invoice_paid == true) {
+ foreach ($team->members as $member) {
+ fputcsv($handle, [$member->email, $member->name], ",");
+ }
+ }
+ }
+});
+fclose($handle);
diff --git a/public/svgs/listmonk.svg b/public/svgs/listmonk.svg
new file mode 100644
index 000000000..a4e5efd5f
--- /dev/null
+++ b/public/svgs/listmonk.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/resources/css/app.css b/resources/css/app.css
index 5858f5cc9..bb05b783b 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -203,7 +203,7 @@ tr td:first-child {
@apply flex lg:flex-row flex-col p-2 transition-colors cursor-pointer min-h-[4rem] dark:bg-coolgray-100 dark:text-white bg-neutral-50 border border-neutral-200 dark:border-black hover:bg-neutral-100 dark:hover:bg-coollabs-100 dark:hover:text-white hover:text-black hover:no-underline text-black;
}
.box-without-bg {
- @apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem] border border-neutral-200 dark:border-black;
+ @apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem] border border-neutral-200 dark:border-black;
}
.box-without-bg-without-border {
@apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem];
diff --git a/resources/views/livewire/dashboard.blade.php b/resources/views/livewire/dashboard.blade.php
index 28850d5e9..0670f5aaa 100644
--- a/resources/views/livewire/dashboard.blade.php
+++ b/resources/views/livewire/dashboard.blade.php
@@ -9,10 +9,10 @@
Your subscription has been activated! Welcome onboard!
It could take a few seconds before your
- subscription is activated.
Please be patient.
+ subscription is activated.
Please be patient.
@endif
No environments found.
diff --git a/resources/views/livewire/server/resources.blade.php b/resources/views/livewire/server/resources.blade.php index a5ff69ea1..29648cafe 100644 --- a/resources/views/livewire/server/resources.blade.php +++ b/resources/views/livewire/server/resources.blade.php @@ -1,7 +1,7 @@