diff --git a/app/Actions/Proxy/CheckConfiguration.php b/app/Actions/Proxy/CheckConfiguration.php
index 208a6863a..5ce7e1e87 100644
--- a/app/Actions/Proxy/CheckConfiguration.php
+++ b/app/Actions/Proxy/CheckConfiguration.php
@@ -16,11 +16,19 @@ class CheckConfiguration
return 'OK';
}
$proxy_path = $server->proxyPath();
-
- $proxy_configuration = instant_remote_process([
- "mkdir -p $proxy_path",
- "cat $proxy_path/docker-compose.yml",
- ], $server, false);
+ if ($server->isNonRoot()) {
+ $payload = [
+ "mkdir -p $proxy_path",
+ "chown -R $server->user:$server->user $proxy_path",
+ "cat $proxy_path/docker-compose.yml",
+ ];
+ } else {
+ $payload = [
+ "mkdir -p $proxy_path",
+ "cat $proxy_path/docker-compose.yml",
+ ];
+ }
+ $proxy_configuration = instant_remote_process($payload, $server, false);
if ($reset || !$proxy_configuration || is_null($proxy_configuration)) {
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
diff --git a/app/Actions/Proxy/CheckProxy.php b/app/Actions/Proxy/CheckProxy.php
index d39b33a2a..9323ca1d1 100644
--- a/app/Actions/Proxy/CheckProxy.php
+++ b/app/Actions/Proxy/CheckProxy.php
@@ -13,8 +13,9 @@ class CheckProxy
if ($server->proxyType() === 'NONE') {
return false;
}
- if (!$server->validateConnection()) {
- throw new \Exception("Server Connection Error");
+ ['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
+ if (!$uptime) {
+ throw new \Exception($error);
}
if (!$server->isProxyShouldRun()) {
if ($fromUI) {
diff --git a/app/Jobs/ContainerStatusJob.php b/app/Jobs/ContainerStatusJob.php
index 333d46027..a9cec009b 100644
--- a/app/Jobs/ContainerStatusJob.php
+++ b/app/Jobs/ContainerStatusJob.php
@@ -47,7 +47,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
if (!$this->server->isFunctional()) {
return 'Server is not ready.';
};
-
$applications = $this->server->applications();
$skip_these_applications = collect([]);
foreach ($applications as $application) {
@@ -78,6 +77,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
if (is_null($containers)) {
return;
}
+
$containers = format_docker_command_output_to_json($containers);
if ($containerReplicates) {
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
@@ -201,7 +201,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
// Notify user that this container should not be there.
}
}
-
}
if (data_get($container, 'Name') === '/coolify-db') {
$foundDatabases[] = 0;
diff --git a/app/Livewire/NewActivityMonitor.php b/app/Livewire/NewActivityMonitor.php
index 8f2dba0da..853115888 100644
--- a/app/Livewire/NewActivityMonitor.php
+++ b/app/Livewire/NewActivityMonitor.php
@@ -11,15 +11,17 @@ class NewActivityMonitor extends Component
public ?string $header = null;
public $activityId;
public $eventToDispatch = 'activityFinished';
+ public $eventData = null;
public $isPollingActive = false;
protected $activity;
protected $listeners = ['newActivityMonitor' => 'newMonitorActivity'];
- public function newMonitorActivity($activityId, $eventToDispatch = 'activityFinished')
+ public function newMonitorActivity($activityId, $eventToDispatch = 'activityFinished', $eventData = null)
{
$this->activityId = $activityId;
$this->eventToDispatch = $eventToDispatch;
+ $this->eventData = $eventData;
$this->hydrateActivity();
@@ -55,8 +57,12 @@ class NewActivityMonitor extends Component
}
return;
}
- $this->dispatch($this->eventToDispatch);
- ray('Dispatched event: ' . $this->eventToDispatch);
+ if (!is_null($this->eventData)) {
+ $this->dispatch($this->eventToDispatch, $this->eventData);
+ } else {
+ $this->dispatch($this->eventToDispatch);
+ }
+ ray('Dispatched event: ' . $this->eventToDispatch . ' with data: ' . $this->eventData);
}
}
}
diff --git a/app/Livewire/Server/Form.php b/app/Livewire/Server/Form.php
index e51aff8a3..14a2809c7 100644
--- a/app/Livewire/Server/Form.php
+++ b/app/Livewire/Server/Form.php
@@ -76,14 +76,14 @@ class Form extends Component
public function checkLocalhostConnection()
{
$this->submit();
- $uptime = $this->server->validateConnection();
+ ['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
if ($uptime) {
$this->dispatch('success', 'Server is reachable.');
$this->server->settings->is_reachable = true;
$this->server->settings->is_usable = true;
$this->server->settings->save();
} else {
- $this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.
Check this documentation for further help.');
+ $this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.
Check this documentation for further help.
Error: ' . $error);
return;
}
}
diff --git a/app/Livewire/Server/ShowPrivateKey.php b/app/Livewire/Server/ShowPrivateKey.php
index d44d2cb5f..e0474f2c4 100644
--- a/app/Livewire/Server/ShowPrivateKey.php
+++ b/app/Livewire/Server/ShowPrivateKey.php
@@ -35,10 +35,11 @@ class ShowPrivateKey extends Component
public function checkConnection()
{
try {
- $uptime = $this->server->validateConnection();
+ ['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
if ($uptime) {
$this->dispatch('success', 'Server is reachable.');
} else {
+ ray($error);
$this->dispatch('error', 'Server is not reachable.
Please validate your configuration and connection.
Check this documentation for further help.');
return;
}
diff --git a/app/Livewire/Server/ValidateAndInstall.php b/app/Livewire/Server/ValidateAndInstall.php
index 6d4173956..b148fe2c4 100644
--- a/app/Livewire/Server/ValidateAndInstall.php
+++ b/app/Livewire/Server/ValidateAndInstall.php
@@ -11,7 +11,7 @@ class ValidateAndInstall extends Component
{
public Server $server;
public int $number_of_tries = 0;
- public int $max_tries = 1;
+ public int $max_tries = 3;
public bool $install = true;
public $uptime = null;
public $supported_os_type = null;
@@ -32,9 +32,8 @@ class ValidateAndInstall extends Component
'refresh' => '$refresh',
];
- public function init(bool $install = true)
+ public function init(int $data = 0)
{
- $this->install = $install;
$this->uptime = null;
$this->supported_os_type = null;
$this->docker_installed = null;
@@ -42,7 +41,7 @@ class ValidateAndInstall extends Component
$this->docker_compose_installed = null;
$this->proxy_started = null;
$this->error = null;
- $this->number_of_tries = 0;
+ $this->number_of_tries = $data;
if (!$this->ask) {
$this->dispatch('validateConnection');
}
@@ -66,16 +65,15 @@ class ValidateAndInstall extends Component
} else {
$this->proxy_started = true;
}
-
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function validateConnection()
{
- $this->uptime = $this->server->validateConnection();
+ ['uptime' => $this->uptime, 'error' => $error] = $this->server->validateConnection();
if (!$this->uptime) {
- $this->error = 'Server is not reachable. Please validate your configuration and connection.
Check this documentation for further help.';
+ $this->error = 'Server is not reachable. Please validate your configuration and connection.
Check this documentation for further help.
Error: ' . $error;
return;
}
$this->dispatch('validateOS');
@@ -99,10 +97,10 @@ class ValidateAndInstall extends Component
$this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: documentation.';
return;
} else {
- if ($this->number_of_tries == 0) {
+ if ($this->number_of_tries <= $this->max_tries) {
$activity = $this->server->installDocker();
$this->number_of_tries++;
- $this->dispatch('newActivityMonitor', $activity->id, 'init');
+ $this->dispatch('newActivityMonitor', $activity->id, 'init', $this->number_of_tries);
}
return;
}
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 79c98ccf6..7afadecfc 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -481,8 +481,8 @@ $schema://$host {
// ray('serverUptimeCheckNumber: ' . $serverUptimeCheckNumber);
// ray('serverUptimeCheckNumberMax: ' . $serverUptimeCheckNumberMax);
- $result = $this->validateConnection();
- if ($result) {
+ ['uptime' => $uptime] = $this->validateConnection();
+ if ($uptime) {
if ($this->unreachable_notification_sent === true) {
$this->update(['unreachable_notification_sent' => false]);
}
@@ -551,7 +551,7 @@ $schema://$host {
public function loadUnmanagedContainers()
{
if ($this->isFunctional()) {
- $containers = instant_remote_process(["docker ps -a --format '{{json .}}' "], $this);
+ $containers = instant_remote_process(["docker ps -a --format '{{json .}}'"], $this);
$containers = format_docker_command_output_to_json($containers);
$containers = $containers->map(function ($container) {
$labels = data_get($container, 'Labels');
@@ -748,34 +748,31 @@ $schema://$host {
$server = Server::find($this->id);
if (!$server) {
- return false;
+ return ['uptime' => false, 'error' => 'Server not found.'];
}
if ($server->skipServer()) {
- return false;
+ return ['uptime' => false, 'error' => 'Server skipped.'];
}
- // EC2 does not have `uptime` command, lol
-
- $uptime = instant_remote_process(['ls /'], $server, false);
- if (!$uptime) {
- $server->settings()->update([
- 'is_reachable' => false,
- ]);
- return false;
- } else {
+ try {
+ // EC2 does not have `uptime` command, lol
+ instant_remote_process(['ls /'], $server);
$server->settings()->update([
'is_reachable' => true,
]);
$server->update([
'unreachable_count' => 0,
]);
+ if (data_get($server, 'unreachable_notification_sent') === true) {
+ $server->team?->notify(new Revived($server));
+ $server->update(['unreachable_notification_sent' => false]);
+ }
+ return ['uptime' => true, 'error' => null];
+ } catch (\Throwable $e) {
+ $server->settings()->update([
+ 'is_reachable' => false,
+ ]);
+ return ['uptime' => false, 'error' => $e->getMessage()];
}
-
- if (data_get($server, 'unreachable_notification_sent') === true) {
- $server->team?->notify(new Revived($server));
- $server->update(['unreachable_notification_sent' => false]);
- }
-
- return true;
}
public function installDocker()
{
@@ -784,7 +781,7 @@ $schema://$host {
}
public function validateDockerEngine($throwError = false)
{
- $dockerBinary = instant_remote_process(["command -v docker"], $this, false);
+ $dockerBinary = instant_remote_process(["command -v docker"], $this, false, no_sudo: true);
if (is_null($dockerBinary)) {
$this->settings->is_usable = false;
$this->settings->save();
@@ -861,4 +858,8 @@ $schema://$host {
return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false);
}
}
+ public function isNonRoot()
+ {
+ return $this->user !== 'root';
+ }
}
diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php
index 3edc92ecc..fac4b84f0 100644
--- a/bootstrap/helpers/remoteProcess.php
+++ b/bootstrap/helpers/remoteProcess.php
@@ -32,6 +32,33 @@ function remote_process(
if ($command instanceof Collection) {
$command = $command->toArray();
}
+ if ($server->isNonRoot()) {
+ $command = collect($command)->map(function ($line) {
+ if (!str($line)->startSwith('cd')) {
+ return "sudo $line";
+ }
+ return $line;
+ })->toArray();
+ $command = collect($command)->map(function ($line) use ($server) {
+ if (Str::startsWith($line, 'sudo mkdir -p')) {
+ return "$line && sudo chown -R $server->user:$server->user " . Str::after($line, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($line, 'sudo mkdir -p');
+ }
+ return $line;
+ })->toArray();
+ $command = collect($command)->map(function ($line) {
+ if (str($line)->contains('$(') || str($line)->contains('`')) {
+ return str($line)->replace('$(', '$(sudo ')->replace('`', '`sudo ')->value();
+ }
+ if (str($line)->contains('||')) {
+ return str($line)->replace('||', '|| sudo ')->value();
+ }
+ if (str($line)->contains('&&')) {
+ return str($line)->replace('&&', '&& sudo ')->value();
+ }
+ return $line;
+ })->toArray();
+ }
+ ray($command);
$command_string = implode("\n", $command);
if (auth()->user()) {
$teams = auth()->user()->teams->pluck('id');
@@ -144,7 +171,6 @@ function generateSshCommand(Server $server, string $command)
$ssh_command .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" ';
}
$command = "PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/host/usr/local/sbin:/host/usr/local/bin:/host/usr/sbin:/host/usr/bin:/host/sbin:/host/bin && $command";
-
$delimiter = Hash::make($command);
$command = str_replace($delimiter, '', $command);
$ssh_command .= "-i {$privateKeyLocation} "
@@ -160,17 +186,42 @@ function generateSshCommand(Server $server, string $command)
. $command . PHP_EOL
. $delimiter;
// ray($ssh_command);
- // ray($delimiter);
return $ssh_command;
}
-function instant_remote_process(Collection|array $command, Server $server, $throwError = true)
+function instant_remote_process(Collection|array $command, Server $server, bool $throwError = true, bool $no_sudo = false)
{
$timeout = config('constants.ssh.command_timeout');
if ($command instanceof Collection) {
$command = $command->toArray();
}
+ if ($server->isNonRoot() && !$no_sudo) {
+ $command = collect($command)->map(function ($line) {
+ if (!str($line)->startSwith('cd')) {
+ return "sudo $line";
+ }
+ return $line;
+ })->toArray();
+ $command = collect($command)->map(function ($line) use ($server) {
+ if (Str::startsWith($line, 'sudo mkdir -p')) {
+ return "$line && sudo chown -R $server->user:$server->user " . Str::after($line, 'sudo mkdir -p') . ' && sudo chmod -R o-rwx ' . Str::after($line, 'sudo mkdir -p');
+ }
+ return $line;
+ })->toArray();
+ $command = collect($command)->map(function ($line) {
+ if (str($line)->contains('$(') || str($line)->contains('`')) {
+ return str($line)->replace('$(', '$(sudo ')->replace('`', '`sudo ')->value();
+ }
+ if (str($line)->contains('||')) {
+ return str($line)->replace('||', '|| sudo ')->value();
+ }
+ if (str($line)->contains('&&')) {
+ return str($line)->replace('&&', '&& sudo ')->value();
+ }
+ return $line;
+ })->toArray();
+ }
$command_string = implode("\n", $command);
- $ssh_command = generateSshCommand($server, $command_string);
+ $ssh_command = generateSshCommand($server, $command_string, $no_sudo);
$process = Process::timeout($timeout)->run($ssh_command);
$output = trim($process->output());
$exitCode = $process->exitCode();
diff --git a/resources/views/livewire/server/new/by-ip.blade.php b/resources/views/livewire/server/new/by-ip.blade.php
index e3b5b6396..989283a3e 100644
--- a/resources/views/livewire/server/new/by-ip.blade.php
+++ b/resources/views/livewire/server/new/by-ip.blade.php
@@ -10,7 +10,7 @@