Error: '.$error;
+ $this->error = 'Server is not reachable. Please validate your configuration and connection. Check this documentation for further help.
Error: '.$error.'
';
+ $this->server->update([
+ 'validation_logs' => $this->error,
+ ]);
return;
}
@@ -99,6 +102,9 @@ public function validateOS()
$this->supported_os_type = $this->server->validateOS();
if (! $this->supported_os_type) {
$this->error = 'Server OS type is not supported. Please install Docker manually before continuing: documentation.';
+ $this->server->update([
+ 'validation_logs' => $this->error,
+ ]);
return;
}
@@ -113,6 +119,9 @@ public function validateDockerEngine()
if ($this->install) {
if ($this->number_of_tries == $this->max_tries) {
$this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: documentation.';
+ $this->server->update([
+ 'validation_logs' => $this->error,
+ ]);
return;
} else {
@@ -126,6 +135,9 @@ public function validateDockerEngine()
}
} else {
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: documentation.';
+ $this->server->update([
+ 'validation_logs' => $this->error,
+ ]);
return;
}
@@ -148,6 +160,9 @@ public function validateDockerVersion()
$this->dispatch('success', 'Server validated.');
} else {
$this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: documentation.';
+ $this->server->update([
+ 'validation_logs' => $this->error,
+ ]);
return;
}
diff --git a/app/Livewire/Settings/Configuration.php b/app/Livewire/Settings/Configuration.php
deleted file mode 100644
index 7439e112f..000000000
--- a/app/Livewire/Settings/Configuration.php
+++ /dev/null
@@ -1,111 +0,0 @@
- 'nullable',
- 'settings.resale_license' => 'nullable',
- 'settings.public_port_min' => 'required',
- 'settings.public_port_max' => 'required',
- 'settings.custom_dns_servers' => 'nullable',
- 'settings.instance_name' => 'nullable',
- 'settings.allowed_ips' => 'nullable',
- ];
-
- protected $validationAttributes = [
- 'settings.fqdn' => 'FQDN',
- 'settings.resale_license' => 'Resale License',
- 'settings.public_port_min' => 'Public port min',
- 'settings.public_port_max' => 'Public port max',
- 'settings.custom_dns_servers' => 'Custom DNS servers',
- 'settings.allowed_ips' => 'Allowed IPs',
- ];
-
- public function mount()
- {
- $this->do_not_track = $this->settings->do_not_track;
- $this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
- $this->is_registration_enabled = $this->settings->is_registration_enabled;
- $this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
- $this->is_api_enabled = $this->settings->is_api_enabled;
- }
-
- public function instantSave()
- {
- $this->settings->do_not_track = $this->do_not_track;
- $this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
- $this->settings->is_registration_enabled = $this->is_registration_enabled;
- $this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
- $this->settings->is_api_enabled = $this->is_api_enabled;
- $this->settings->save();
- $this->dispatch('success', 'Settings updated!');
- }
-
- public function submit()
- {
- try {
- $error_show = false;
- $this->server = Server::findOrFail(0);
- $this->resetErrorBag();
- if ($this->settings->public_port_min > $this->settings->public_port_max) {
- $this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.');
-
- return;
- }
- $this->validate();
-
- if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) {
- if (! validate_dns_entry($this->settings->fqdn, $this->server)) {
- $this->dispatch('error', "Validating DNS failed.
Make sure you have added the DNS records correctly.
Make sure you have added the DNS records correctly.
{$this->settings->fqdn}->{$this->server->ip}
Check this documentation for further help.");
+ $error_show = true;
+ }
+ }
+ if ($this->settings->fqdn) {
+ check_domain_usage(domain: $this->settings->fqdn);
+ }
+ $this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->replaceEnd(',', '')->trim();
+ $this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->trim()->explode(',')->map(function ($dns) {
+ return str($dns)->trim()->lower();
+ });
+ $this->settings->custom_dns_servers = $this->settings->custom_dns_servers->unique();
+ $this->settings->custom_dns_servers = $this->settings->custom_dns_servers->implode(',');
+
+ $this->settings->allowed_ips = str($this->settings->allowed_ips)->replaceEnd(',', '')->trim();
+ $this->settings->allowed_ips = str($this->settings->allowed_ips)->trim()->explode(',')->map(function ($ip) {
+ return str($ip)->trim();
+ });
+ $this->settings->allowed_ips = $this->settings->allowed_ips->unique();
+ $this->settings->allowed_ips = $this->settings->allowed_ips->implode(',');
+
+ $this->settings->do_not_track = $this->do_not_track;
+ $this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
+ $this->settings->is_registration_enabled = $this->is_registration_enabled;
+ $this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
+ $this->settings->is_api_enabled = $this->is_api_enabled;
+ $this->settings->auto_update_frequency = $this->auto_update_frequency;
+ $this->settings->update_check_frequency = $this->update_check_frequency;
+ $this->settings->save();
+ $this->server->setupDynamicProxyConfiguration();
+ if (! $error_show) {
+ $this->dispatch('success', 'Instance settings updated successfully!');
+ }
+ } catch (\Exception $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function checkManually()
+ {
+ CheckForUpdatesJob::dispatchSync();
+ $this->dispatch('updateAvailable');
+ $settings = InstanceSettings::get();
+ if ($settings->new_version_available) {
+ $this->dispatch('success', 'New version available!');
+ } else {
+ $this->dispatch('success', 'No new version available.');
+ }
+ }
+
+ public function updatedSettingsInstanceTimezone($value)
+ {
+ $this->settings->instance_timezone = $value;
+ $this->settings->save();
+ $this->dispatch('success', 'Instance timezone updated.');
+ }
+
public function render()
{
return view('livewire.settings.index');
diff --git a/app/Livewire/Settings/Backup.php b/app/Livewire/SettingsBackup.php
similarity index 76%
rename from app/Livewire/Settings/Backup.php
rename to app/Livewire/SettingsBackup.php
index 08ad04b2d..99b8f8d49 100644
--- a/app/Livewire/Settings/Backup.php
+++ b/app/Livewire/SettingsBackup.php
@@ -1,6 +1,6 @@
backup = $this->database?->scheduledBackups->first() ?? null;
- $this->executions = $this->backup?->executions ?? [];
+ if (isInstanceAdmin()) {
+ $settings = InstanceSettings::get();
+ $this->database = StandalonePostgresql::whereName('coolify-db')->first();
+ $s3s = S3Storage::whereTeamId(0)->get() ?? [];
+ if ($this->database) {
+ if ($this->database->status !== 'running') {
+ $this->database->status = 'running';
+ $this->database->save();
+ }
+ $this->backup = $this->database->scheduledBackups->first();
+ $this->executions = $this->backup->executions;
+ }
+ $this->settings = $settings;
+ $this->s3s = $s3s;
+
+ } else {
+ return redirect()->route('dashboard');
+ }
}
public function add_coolify_database()
diff --git a/app/Livewire/Settings/Email.php b/app/Livewire/SettingsEmail.php
similarity index 93%
rename from app/Livewire/Settings/Email.php
rename to app/Livewire/SettingsEmail.php
index bd7f8201e..3eb8ea646 100644
--- a/app/Livewire/Settings/Email.php
+++ b/app/Livewire/SettingsEmail.php
@@ -1,12 +1,12 @@
emails = auth()->user()->email;
+ if (isInstanceAdmin()) {
+ $this->settings = InstanceSettings::get();
+ $this->emails = auth()->user()->email;
+ } else {
+ return redirect()->route('dashboard');
+ }
+
}
public function submitFromFields()
diff --git a/app/Livewire/Settings/Auth.php b/app/Livewire/SettingsOauth.php
similarity index 95%
rename from app/Livewire/Settings/Auth.php
rename to app/Livewire/SettingsOauth.php
index 783b163e0..c3884589f 100644
--- a/app/Livewire/Settings/Auth.php
+++ b/app/Livewire/SettingsOauth.php
@@ -1,11 +1,11 @@
'$refresh', 'saveKey' => 'saveKey'];
+ protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey'];
public function saveKey($data)
{
diff --git a/app/Livewire/Storage/Create.php b/app/Livewire/Storage/Create.php
index 1ccc3997c..a05834ecc 100644
--- a/app/Livewire/Storage/Create.php
+++ b/app/Livewire/Storage/Create.php
@@ -59,7 +59,7 @@ public function submit()
{
try {
$this->validate();
- $this->storage = new S3Storage();
+ $this->storage = new S3Storage;
$this->storage->name = $this->name;
$this->storage->description = $this->description ?? null;
$this->storage->region = $this->region;
diff --git a/app/Livewire/Tags/Index.php b/app/Livewire/Tags/Index.php
index 2e3fbd8e0..a01d00a70 100644
--- a/app/Livewire/Tags/Index.php
+++ b/app/Livewire/Tags/Index.php
@@ -51,11 +51,11 @@ public function redeploy_all()
{
try {
$this->applications->each(function ($resource) {
- $deploy = new DeployController();
+ $deploy = new DeployController;
$deploy->deploy_resource($resource);
});
$this->services->each(function ($resource) {
- $deploy = new DeployController();
+ $deploy = new DeployController;
$deploy->deploy_resource($resource);
});
$this->dispatch('success', 'Mass deployment started.');
diff --git a/app/Livewire/Tags/Show.php b/app/Livewire/Tags/Show.php
index ccd190c16..668101edb 100644
--- a/app/Livewire/Tags/Show.php
+++ b/app/Livewire/Tags/Show.php
@@ -59,11 +59,11 @@ public function redeploy_all()
try {
$message = collect([]);
$this->applications->each(function ($resource) use ($message) {
- $deploy = new DeployController();
+ $deploy = new DeployController;
$message->push($deploy->deploy_resource($resource));
});
$this->services->each(function ($resource) use ($message) {
- $deploy = new DeployController();
+ $deploy = new DeployController;
$message->push($deploy->deploy_resource($resource));
});
$this->dispatch('success', 'Mass deployment started.');
diff --git a/app/Livewire/Team/AdminView.php b/app/Livewire/Team/AdminView.php
index 97d4fcdbf..ee5e673a5 100644
--- a/app/Livewire/Team/AdminView.php
+++ b/app/Livewire/Team/AdminView.php
@@ -5,6 +5,8 @@
use App\Models\Team;
use App\Models\User;
use Livewire\Component;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Hash;
class AdminView extends Component
{
@@ -73,8 +75,12 @@ private function finalizeDeletion(User $user, Team $team)
$team->delete();
}
- public function delete($id)
+ public function delete($id, $password)
{
+ if (!Hash::check($password, Auth::user()->password)) {
+ $this->addError('password', 'The provided password is incorrect.');
+ return;
+ }
if (! auth()->user()->isInstanceAdmin()) {
return $this->dispatch('error', 'You are not authorized to delete users');
}
diff --git a/app/Livewire/Team/InviteLink.php b/app/Livewire/Team/InviteLink.php
index 0fa9e980c..6c9e405fc 100644
--- a/app/Livewire/Team/InviteLink.php
+++ b/app/Livewire/Team/InviteLink.php
@@ -79,7 +79,7 @@ private function generate_invite_link(bool $sendEmail = false)
'via' => $sendEmail ? 'email' : 'link',
]);
if ($sendEmail) {
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->view('emails.invitation-link', [
'team' => currentTeam()->name,
'invitation_link' => $link,
diff --git a/app/Livewire/Terminal/Index.php b/app/Livewire/Terminal/Index.php
new file mode 100644
index 000000000..945b25714
--- /dev/null
+++ b/app/Livewire/Terminal/Index.php
@@ -0,0 +1,76 @@
+user()->isAdmin()) {
+ abort(403);
+ }
+ $this->servers = Server::isReachable()->get();
+ $this->containers = $this->getAllActiveContainers();
+ }
+
+ private function getAllActiveContainers()
+ {
+ return collect($this->servers)->flatMap(function ($server) {
+ if (! $server->isFunctional()) {
+ return [];
+ }
+
+ return $server->loadAllContainers()->map(function ($container) use ($server) {
+ $state = data_get_str($container, 'State')->lower();
+ if ($state->contains('running')) {
+ return [
+ 'name' => data_get($container, 'Names'),
+ 'connection_name' => data_get($container, 'Names'),
+ 'uuid' => data_get($container, 'Names'),
+ 'status' => data_get_str($container, 'State')->lower(),
+ 'server' => $server,
+ 'server_uuid' => $server->uuid,
+ ];
+ }
+
+ return null;
+ })->filter();
+ });
+ }
+
+ public function updatedSelectedUuid()
+ {
+ $this->connectToContainer();
+ }
+
+ #[On('connectToContainer')]
+ public function connectToContainer()
+ {
+ if ($this->selected_uuid === 'default') {
+ $this->dispatch('error', 'Please select a server or a container.');
+
+ return;
+ }
+ $container = collect($this->containers)->firstWhere('uuid', $this->selected_uuid);
+ $this->dispatch('send-terminal-command',
+ isset($container),
+ $container['connection_name'] ?? $this->selected_uuid,
+ $container['server_uuid'] ?? $this->selected_uuid
+ );
+ }
+
+ public function render()
+ {
+ return view('livewire.terminal.index');
+ }
+}
diff --git a/app/Livewire/Upgrade.php b/app/Livewire/Upgrade.php
index 7ad7e9523..dfbd945f5 100644
--- a/app/Livewire/Upgrade.php
+++ b/app/Livewire/Upgrade.php
@@ -3,6 +3,7 @@
namespace App\Livewire;
use App\Actions\Server\UpdateCoolify;
+use App\Models\InstanceSettings;
use Livewire\Component;
class Upgrade extends Component
@@ -15,14 +16,18 @@ class Upgrade extends Component
public string $latestVersion = '';
+ protected $listeners = ['updateAvailable' => 'checkUpdate'];
+
public function checkUpdate()
{
- $this->latestVersion = get_latest_version_of_coolify();
- $currentVersion = config('version');
- version_compare($currentVersion, $this->latestVersion, '<') ? $this->isUpgradeAvailable = true : $this->isUpgradeAvailable = false;
- if (isDev()) {
- $this->isUpgradeAvailable = true;
+ try {
+ $this->latestVersion = get_latest_version_of_coolify();
+ $this->isUpgradeAvailable = data_get(InstanceSettings::get(), 'new_version_available', false);
+
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
}
+
}
public function upgrade()
diff --git a/app/Models/Application.php b/app/Models/Application.php
index 81fdb5a1e..8c7520eaf 100644
--- a/app/Models/Application.php
+++ b/app/Models/Application.php
@@ -8,6 +8,8 @@
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
+use Illuminate\Support\Facades\Process;
+use Illuminate\Process\InvokedProcess;
use OpenApi\Attributes as OA;
use RuntimeException;
use Spatie\Activitylog\Models\Activity;
@@ -102,8 +104,12 @@ class Application extends BaseModel
{
use SoftDeletes;
+ private static $parserVersion = '3';
+
protected $guarded = [];
+ protected $appends = ['server_status'];
+
protected static function booted()
{
static::saving(function ($application) {
@@ -123,7 +129,7 @@ protected static function booted()
ApplicationSetting::create([
'application_id' => $application->id,
]);
- $application->compose_parsing_version = '2';
+ $application->compose_parsing_version = self::$parserVersion;
$application->save();
});
static::forceDeleting(function ($application) {
@@ -136,6 +142,7 @@ protected static function booted()
$task->delete();
}
$application->tags()->detach();
+ $application->previews()->delete();
});
}
@@ -144,13 +151,65 @@ public static function ownedByCurrentTeamAPI(int $teamId)
return Application::whereRelation('environment.project.team', 'id', $teamId)->orderBy('name');
}
+ public function getContainersToStop(bool $previewDeployments = false): array
+ {
+ $containers = $previewDeployments
+ ? getCurrentApplicationContainerStatus($this->destination->server, $this->id, includePullrequests: true)
+ : getCurrentApplicationContainerStatus($this->destination->server, $this->id, 0);
+
+ return $containers->pluck('Names')->toArray();
+ }
+
+ public function stopContainers(array $containerNames, $server, int $timeout = 600)
+ {
+ $processes = [];
+ foreach ($containerNames as $containerName) {
+ $processes[$containerName] = $this->stopContainer($containerName, $server, $timeout);
+ }
+
+ $startTime = time();
+ while (count($processes) > 0) {
+ $finishedProcesses = array_filter($processes, function ($process) {
+ return !$process->running();
+ });
+ foreach ($finishedProcesses as $containerName => $process) {
+ unset($processes[$containerName]);
+ $this->removeContainer($containerName, $server);
+ }
+
+ if (time() - $startTime >= $timeout) {
+ $this->forceStopRemainingContainers(array_keys($processes), $server);
+ break;
+ }
+
+ usleep(100000);
+ }
+ }
+
+ public function stopContainer(string $containerName, $server, int $timeout): InvokedProcess
+ {
+ return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
+ }
+
+ public function removeContainer(string $containerName, $server)
+ {
+ instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false);
+ }
+
+ public function forceStopRemainingContainers(array $containerNames, $server)
+ {
+ foreach ($containerNames as $containerName) {
+ instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false);
+ $this->removeContainer($containerName, $server);
+ }
+ }
+
public function delete_configurations()
{
$server = data_get($this, 'destination.server');
$workdir = $this->workdir();
if (str($workdir)->endsWith($this->uuid)) {
- ray('Deleting workdir');
- instant_remote_process(['rm -rf '.$this->workdir()], $server, false);
+ instant_remote_process(['rm -rf ' . $this->workdir()], $server, false);
}
}
@@ -171,6 +230,14 @@ public function delete_volumes(?Collection $persistentStorages)
}
}
+ public function delete_connected_networks($uuid)
+ {
+ $server = data_get($this, 'destination.server');
+ instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
+ instant_remote_process(["docker network rm {$uuid}"], $server, false);
+ }
+
+
public function additional_servers()
{
return $this->belongsToMany(Server::class, 'additional_destinations')
@@ -232,12 +299,24 @@ public function link()
public function failedTaskLink($task_uuid)
{
if (data_get($this, 'environment.project.uuid')) {
- return route('project.application.scheduled-tasks', [
+ $route = route('project.application.scheduled-tasks', [
'project_uuid' => data_get($this, 'environment.project.uuid'),
'environment_name' => data_get($this, 'environment.name'),
'application_uuid' => data_get($this, 'uuid'),
'task_uuid' => $task_uuid,
]);
+ $settings = InstanceSettings::get();
+ if (data_get($settings, 'fqdn')) {
+ $url = Url::fromString($route);
+ $url = $url->withPort(null);
+ $fqdn = data_get($settings, 'fqdn');
+ $fqdn = str_replace(['http://', 'https://'], '', $fqdn);
+ $url = $url->withHost($fqdn);
+
+ return $url->__toString();
+ }
+
+ return $route;
}
return null;
@@ -266,7 +345,7 @@ public function type()
public function publishDirectory(): Attribute
{
return Attribute::make(
- set: fn ($value) => $value ? '/'.ltrim($value, '/') : null,
+ set: fn ($value) => $value ? '/' . ltrim($value, '/') : null,
);
}
@@ -274,13 +353,21 @@ public function gitBranchLocation(): Attribute
{
return Attribute::make(
get: function () {
- if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) {
+ if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
+ if (str($this->git_repository)->contains('bitbucket')) {
+ return "{$this->source->html_url}/{$this->git_repository}/src/{$this->git_branch}";
+ }
+
return "{$this->source->html_url}/{$this->git_repository}/tree/{$this->git_branch}";
}
// Convert the SSH URL to HTTPS URL
if (strpos($this->git_repository, 'git@') === 0) {
$git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
+ if (str($this->git_repository)->contains('bitbucket')) {
+ return "https://{$git_repository}/src/{$this->git_branch}";
+ }
+
return "https://{$git_repository}/tree/{$this->git_branch}";
}
@@ -293,7 +380,7 @@ public function gitWebhook(): Attribute
{
return Attribute::make(
get: function () {
- if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) {
+ if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
return "{$this->source->html_url}/{$this->git_repository}/settings/hooks";
}
// Convert the SSH URL to HTTPS URL
@@ -312,7 +399,7 @@ public function gitCommits(): Attribute
{
return Attribute::make(
get: function () {
- if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) {
+ if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
return "{$this->source->html_url}/{$this->git_repository}/commits/{$this->git_branch}";
}
// Convert the SSH URL to HTTPS URL
@@ -329,7 +416,7 @@ public function gitCommits(): Attribute
public function gitCommitLink($link): string
{
- if (! is_null(data_get($this, 'source.html_url')) && ! is_null(data_get($this, 'git_repository')) && ! is_null(data_get($this, 'git_branch'))) {
+ if (!is_null(data_get($this, 'source.html_url')) && !is_null(data_get($this, 'git_repository')) && !is_null(data_get($this, 'git_branch'))) {
if (str($this->source->html_url)->contains('bitbucket')) {
return "{$this->source->html_url}/{$this->git_repository}/commits/{$link}";
}
@@ -340,7 +427,7 @@ public function gitCommitLink($link): string
$git_repository = str_replace('.git', '', $this->git_repository);
$url = Url::fromString($git_repository);
$url = $url->withUserInfo('');
- $url = $url->withPath($url->getPath().'/commits/'.$link);
+ $url = $url->withPath($url->getPath() . '/commits/' . $link);
return $url->__toString();
}
@@ -390,27 +477,10 @@ public function dockerComposeLocation(): Attribute
);
}
- public function dockerComposePrLocation(): Attribute
- {
- return Attribute::make(
- set: function ($value) {
- if (is_null($value) || $value === '') {
- return '/docker-compose.yaml';
- } else {
- if ($value !== '/') {
- return Str::start(Str::replaceEnd('/', '', $value), '/');
- }
-
- return Str::start($value, '/');
- }
- }
- );
- }
-
public function baseDirectory(): Attribute
{
return Attribute::make(
- set: fn ($value) => '/'.ltrim($value, '/'),
+ set: fn ($value) => '/' . ltrim($value, '/'),
);
}
@@ -431,6 +501,11 @@ public function portsMappingsArray(): Attribute
);
}
+ public function isRunning()
+ {
+ return (bool) str($this->status)->startsWith('running');
+ }
+
public function isExited()
{
return (bool) str($this->status)->startsWith('exited');
@@ -441,6 +516,28 @@ public function realStatus()
return $this->getRawOriginal('status');
}
+ protected function serverStatus(): Attribute
+ {
+ return Attribute::make(
+ get: function () {
+ if ($this->additional_servers->count() === 0) {
+ return $this->destination->server->isFunctional();
+ } else {
+ $additional_servers_status = $this->additional_servers->pluck('pivot.status');
+ $main_server_status = $this->destination->server->isFunctional();
+ foreach ($additional_servers_status as $status) {
+ $server_status = str($status)->before(':')->value();
+ if ($server_status !== 'running') {
+ return false;
+ }
+ }
+
+ return $main_server_status;
+ }
+ }
+ );
+ }
+
public function status(): Attribute
{
return Attribute::make(
@@ -726,7 +823,7 @@ public function isHealthcheckDisabled(): bool
public function workdir()
{
- return application_configuration_dir()."/{$this->uuid}";
+ return application_configuration_dir() . "/{$this->uuid}";
}
public function isLogDrainEnabled()
@@ -736,7 +833,7 @@ public function isLogDrainEnabled()
public function isConfigurationChanged(bool $save = false)
{
- $newConfigHash = $this->fqdn.$this->git_repository.$this->git_branch.$this->git_commit_sha.$this->build_pack.$this->static_image.$this->install_command.$this->build_command.$this->start_command.$this->ports_exposes.$this->ports_mappings.$this->base_directory.$this->publish_directory.$this->dockerfile.$this->dockerfile_location.$this->custom_labels.$this->custom_docker_run_options.$this->dockerfile_target_build.$this->redirect;
+ $newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->ports_exposes . $this->ports_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels . $this->custom_docker_run_options . $this->dockerfile_target_build . $this->redirect;
if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
} else {
@@ -790,7 +887,7 @@ public function generateBaseDir(string $uuid)
public function dirOnServer()
{
- return application_configuration_dir()."/{$this->uuid}";
+ return application_configuration_dir() . "/{$this->uuid}";
}
public function setGitImportSettings(string $deployment_uuid, string $git_clone_command, bool $public = false)
@@ -836,7 +933,7 @@ public function generateGitImportCommands(string $deployment_uuid, int $pull_req
if ($this->source->is_public) {
$fullRepoUrl = "{$this->source->html_url}/{$customRepository}";
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$customRepository} {$baseDir}";
- if (! $only_checkout) {
+ if (!$only_checkout) {
$git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command, public: true);
}
if ($exec_in_docker) {
@@ -853,7 +950,7 @@ public function generateGitImportCommands(string $deployment_uuid, int $pull_req
$git_clone_command = "{$git_clone_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository} {$baseDir}";
$fullRepoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}";
}
- if (! $only_checkout) {
+ if (!$only_checkout) {
$git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command, public: false);
}
if ($exec_in_docker) {
@@ -914,7 +1011,7 @@ public function generateGitImportCommands(string $deployment_uuid, int $pull_req
} else {
$commands->push("echo 'Checking out $branch'");
}
- $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name);
+ $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name);
} elseif ($git_type === 'github' || $git_type === 'gitea') {
$branch = "pull/{$pull_request_id}/head:$pr_branch_name";
if ($exec_in_docker) {
@@ -922,14 +1019,14 @@ public function generateGitImportCommands(string $deployment_uuid, int $pull_req
} else {
$commands->push("echo 'Checking out $branch'");
}
- $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name);
+ $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name);
} elseif ($git_type === 'bitbucket') {
if ($exec_in_docker) {
$commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'"));
} else {
$commands->push("echo 'Checking out $branch'");
}
- $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" ".$this->buildGitCheckoutCommand($commit);
+ $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" " . $this->buildGitCheckoutCommand($commit);
}
}
@@ -958,7 +1055,7 @@ public function generateGitImportCommands(string $deployment_uuid, int $pull_req
} else {
$commands->push("echo 'Checking out $branch'");
}
- $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name);
+ $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name);
} elseif ($git_type === 'github' || $git_type === 'gitea') {
$branch = "pull/{$pull_request_id}/head:$pr_branch_name";
if ($exec_in_docker) {
@@ -966,14 +1063,14 @@ public function generateGitImportCommands(string $deployment_uuid, int $pull_req
} else {
$commands->push("echo 'Checking out $branch'");
}
- $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && ".$this->buildGitCheckoutCommand($pr_branch_name);
+ $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git fetch origin $branch && " . $this->buildGitCheckoutCommand($pr_branch_name);
} elseif ($git_type === 'bitbucket') {
if ($exec_in_docker) {
$commands->push(executeInDocker($deployment_uuid, "echo 'Checking out $branch'"));
} else {
$commands->push("echo 'Checking out $branch'");
}
- $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" ".$this->buildGitCheckoutCommand($commit);
+ $git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" " . $this->buildGitCheckoutCommand($commit);
}
}
@@ -991,7 +1088,7 @@ public function generateGitImportCommands(string $deployment_uuid, int $pull_req
}
}
- public function parseRawCompose()
+ public function oldRawParser()
{
try {
$yaml = Yaml::parse($this->docker_compose_raw);
@@ -1025,20 +1122,20 @@ public function parseRawCompose()
}
if ($source->startsWith('.')) {
$source = $source->after('.');
- $source = $workdir.$source;
+ $source = $workdir . $source;
}
$commands->push("mkdir -p $source > /dev/null 2>&1 || true");
}
}
}
$labels = collect(data_get($service, 'labels', []));
- if (! $labels->contains('coolify.managed')) {
+ if (!$labels->contains('coolify.managed')) {
$labels->push('coolify.managed=true');
}
- if (! $labels->contains('coolify.applicationId')) {
- $labels->push('coolify.applicationId='.$this->id);
+ if (!$labels->contains('coolify.applicationId')) {
+ $labels->push('coolify.applicationId=' . $this->id);
}
- if (! $labels->contains('coolify.type')) {
+ if (!$labels->contains('coolify.type')) {
$labels->push('coolify.type=application');
}
data_set($service, 'labels', $labels->toArray());
@@ -1051,9 +1148,11 @@ public function parseRawCompose()
instant_remote_process($commands, $this->destination->server, false);
}
- public function parseCompose(int $pull_request_id = 0, ?int $preview_id = null)
+ public function parse(int $pull_request_id = 0, ?int $preview_id = null)
{
- if ($this->docker_compose_raw) {
+ if ($this->compose_parsing_version === '3') {
+ return newParser($this, $pull_request_id, $preview_id);
+ } elseif ($this->docker_compose_raw) {
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
} else {
return collect([]);
@@ -1066,7 +1165,7 @@ public function loadComposeFile($isInit = false)
if ($isInit && $this->docker_compose_raw) {
return;
}
- $uuid = new Cuid2();
+ $uuid = new Cuid2;
['commands' => $cloneCommand] = $this->generateGitImportCommands(deployment_uuid: $uuid, only_checkout: true, exec_in_docker: false, custom_base_dir: '.');
$workdir = rtrim($this->base_directory, '/');
$composeFile = $this->docker_compose_location;
@@ -1105,14 +1204,14 @@ public function loadComposeFile($isInit = false)
if ($composeFileContent) {
$this->docker_compose_raw = $composeFileContent;
$this->save();
- $parsedServices = $this->parseCompose();
+ $parsedServices = $this->parse();
if ($this->docker_compose_domains) {
$json = collect(json_decode($this->docker_compose_domains));
$names = collect(data_get($parsedServices, 'services'))->keys()->toArray();
$jsonNames = $json->keys()->toArray();
$diff = array_diff($jsonNames, $names);
$json = $json->filter(function ($value, $key) use ($diff) {
- return ! in_array($key, $diff);
+ return !in_array($key, $diff);
});
if ($json) {
$this->docker_compose_domains = json_encode($json);
@@ -1129,13 +1228,12 @@ public function loadComposeFile($isInit = false)
} else {
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile
Check if you used the right extension (.yaml or .yml) in the compose file name.");
}
-
}
public function parseContainerLabels(?ApplicationPreview $preview = null)
{
$customLabels = data_get($this, 'custom_labels');
- if (! $customLabels) {
+ if (!$customLabels) {
return;
}
if (base64_encode(base64_decode($customLabels, true)) !== $customLabels) {
@@ -1218,10 +1316,10 @@ public function parseHealthcheckFromDockerfile($dockerfile, bool $isInit = false
continue;
}
if (isset($healthcheckCommand) && str_contains($trimmedLine, '\\')) {
- $healthcheckCommand .= ' '.trim($trimmedLine, '\\ ');
+ $healthcheckCommand .= ' ' . trim($trimmedLine, '\\ ');
}
- if (isset($healthcheckCommand) && ! str_contains($trimmedLine, '\\') && ! empty($healthcheckCommand)) {
- $healthcheckCommand .= ' '.$trimmedLine;
+ if (isset($healthcheckCommand) && !str_contains($trimmedLine, '\\') && !empty($healthcheckCommand)) {
+ $healthcheckCommand .= ' ' . $trimmedLine;
break;
}
}
@@ -1270,7 +1368,7 @@ public function generate_preview_fqdn(int $pull_request_id)
$template = $this->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
- $random = new Cuid2(7);
+ $random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $pull_request_id, $preview_fqdn);
diff --git a/app/Models/ApplicationPreview.php b/app/Models/ApplicationPreview.php
index 3bdd24014..04a0ab27e 100644
--- a/app/Models/ApplicationPreview.php
+++ b/app/Models/ApplicationPreview.php
@@ -12,9 +12,9 @@ class ApplicationPreview extends BaseModel
protected static function booted()
{
static::deleting(function ($preview) {
- if ($preview->application->build_pack === 'dockercompose') {
+ if (data_get($preview, 'application.build_pack') === 'dockercompose') {
$server = $preview->application->destination->server;
- $composeFile = $preview->application->parseCompose(pull_request_id: $preview->pull_request_id);
+ $composeFile = $preview->application->parse(pull_request_id: $preview->pull_request_id);
$volumes = data_get($composeFile, 'volumes');
$networks = data_get($composeFile, 'networks');
$networkKeys = collect($networks)->keys();
@@ -35,6 +35,11 @@ public static function findPreviewByApplicationAndPullId(int $application_id, in
return self::where('application_id', $application_id)->where('pull_request_id', $pull_request_id)->firstOrFail();
}
+ public function isRunning()
+ {
+ return (bool) str($this->status)->startsWith('running');
+ }
+
public function application()
{
return $this->belongsTo(Application::class);
@@ -49,7 +54,7 @@ public function generate_preview_fqdn_compose()
$template = $this->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
- $random = new Cuid2(7);
+ $random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn);
diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php
index 7e028a6b5..17201ea6e 100644
--- a/app/Models/BaseModel.php
+++ b/app/Models/BaseModel.php
@@ -14,7 +14,7 @@ protected static function boot()
static::creating(function (Model $model) {
// Generate a UUID if one isn't set
if (! $model->uuid) {
- $model->uuid = (string) new Cuid2(7);
+ $model->uuid = (string) new Cuid2;
}
});
}
diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php
index 1d2a9dc66..138775aba 100644
--- a/app/Models/EnvironmentVariable.php
+++ b/app/Models/EnvironmentVariable.php
@@ -5,9 +5,7 @@
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
-use Illuminate\Support\Str;
use OpenApi\Attributes as OA;
-use Symfony\Component\Yaml\Yaml;
use Visus\Cuid2\Cuid2;
#[OA\Schema(
@@ -52,7 +50,7 @@ protected static function booted()
{
static::creating(function (Model $model) {
if (! $model->uuid) {
- $model->uuid = (string) new Cuid2();
+ $model->uuid = (string) new Cuid2;
}
});
static::created(function (EnvironmentVariable $environment_variable) {
@@ -98,8 +96,22 @@ public function resource()
$resource = Application::find($this->application_id);
} elseif ($this->service_id) {
$resource = Service::find($this->service_id);
- } elseif ($this->database_id) {
- $resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id'));
+ } elseif ($this->standalone_postgresql_id) {
+ $resource = StandalonePostgresql::find($this->standalone_postgresql_id);
+ } elseif ($this->standalone_redis_id) {
+ $resource = StandaloneRedis::find($this->standalone_redis_id);
+ } elseif ($this->standalone_mongodb_id) {
+ $resource = StandaloneMongodb::find($this->standalone_mongodb_id);
+ } elseif ($this->standalone_mysql_id) {
+ $resource = StandaloneMysql::find($this->standalone_mysql_id);
+ } elseif ($this->standalone_mariadb_id) {
+ $resource = StandaloneMariadb::find($this->standalone_mariadb_id);
+ } elseif ($this->standalone_keydb_id) {
+ $resource = StandaloneKeydb::find($this->standalone_keydb_id);
+ } elseif ($this->standalone_dragonfly_id) {
+ $resource = StandaloneDragonfly::find($this->standalone_dragonfly_id);
+ } elseif ($this->standalone_clickhouse_id) {
+ $resource = StandaloneClickhouse::find($this->standalone_clickhouse_id);
}
return $resource;
@@ -123,63 +135,6 @@ public function realValue(): Attribute
);
}
- protected function isFoundInCompose(): Attribute
- {
- return Attribute::make(
- get: function () {
- if (! $this->application_id) {
- return true;
- }
- $found_in_compose = false;
- $found_in_args = false;
- $resource = $this->resource();
- $compose = data_get($resource, 'docker_compose_raw');
- if (! $compose) {
- return true;
- }
- $yaml = Yaml::parse($compose);
- $services = collect(data_get($yaml, 'services'));
- if ($services->isEmpty()) {
- return false;
- }
- foreach ($services as $service) {
- $environments = collect(data_get($service, 'environment'));
- $args = collect(data_get($service, 'build.args'));
- if ($environments->isEmpty() && $args->isEmpty()) {
- $found_in_compose = false;
- break;
- }
-
- $found_in_compose = $environments->contains(function ($item) {
- if (str($item)->contains('=')) {
- $item = str($item)->before('=');
- }
-
- return strpos($item, $this->key) !== false;
- });
-
- if ($found_in_compose) {
- break;
- }
-
- $found_in_args = $args->contains(function ($item) {
- if (str($item)->contains('=')) {
- $item = str($item)->before('=');
- }
-
- return strpos($item, $this->key) !== false;
- });
-
- if ($found_in_args) {
- break;
- }
- }
-
- return $found_in_compose || $found_in_args;
- }
- );
- }
-
protected function isShared(): Attribute
{
return Attribute::make(
@@ -200,28 +155,35 @@ private function get_real_environment_variables(?string $environment_variable =
return null;
}
$environment_variable = trim($environment_variable);
- $type = str($environment_variable)->after('{{')->before('.')->value;
- if (str($environment_variable)->startsWith('{{'.$type) && str($environment_variable)->endsWith('}}')) {
- $variable = Str::after($environment_variable, "{$type}.");
- $variable = Str::before($variable, '}}');
- $variable = str($variable)->trim()->value;
+ $sharedEnvsFound = str($environment_variable)->matchAll('/{{(.*?)}}/');
+ if ($sharedEnvsFound->isEmpty()) {
+
+ return $environment_variable;
+ }
+
+ foreach ($sharedEnvsFound as $sharedEnv) {
+ $type = str($sharedEnv)->match('/(.*?)\./');
if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
- return $variable;
+ continue;
}
- if ($type === 'environment') {
+ $variable = str($sharedEnv)->match('/\.(.*)/');
+ if ($type->value() === 'environment') {
$id = $resource->environment->id;
- } elseif ($type === 'project') {
+ } elseif ($type->value() === 'project') {
$id = $resource->environment->project->id;
- } else {
+ } elseif ($type->value() === 'team') {
$id = $resource->team()->id;
}
+ if (is_null($id)) {
+ continue;
+ }
$environment_variable_found = SharedEnvironmentVariable::where('type', $type)->where('key', $variable)->where('team_id', $resource->team()->id)->where("{$type}_id", $id)->first();
if ($environment_variable_found) {
- return $environment_variable_found;
+ $environment_variable = str($environment_variable)->replace("{{{$sharedEnv}}}", $environment_variable_found->value);
}
}
- return $environment_variable;
+ return str($environment_variable)->value();
}
private function get_environment_variables(?string $environment_variable = null): ?string
diff --git a/app/Models/InstanceSettings.php b/app/Models/InstanceSettings.php
index bd3c41a1f..27a181ee4 100644
--- a/app/Models/InstanceSettings.php
+++ b/app/Models/InstanceSettings.php
@@ -18,6 +18,9 @@ class InstanceSettings extends Model implements SendsEmail
'resale_license' => 'encrypted',
'smtp_password' => 'encrypted',
'allowed_ip_ranges' => 'array',
+ 'is_auto_update_enabled' => 'boolean',
+ 'auto_update_frequency' => 'string',
+ 'update_check_frequency' => 'string',
];
public function fqdn(): Attribute
@@ -34,6 +37,30 @@ public function fqdn(): Attribute
);
}
+ public function updateCheckFrequency(): Attribute
+ {
+ return Attribute::make(
+ set: function ($value) {
+ return translate_cron_expression($value);
+ },
+ get: function ($value) {
+ return translate_cron_expression($value);
+ }
+ );
+ }
+
+ public function autoUpdateFrequency(): Attribute
+ {
+ return Attribute::make(
+ set: function ($value) {
+ return translate_cron_expression($value);
+ },
+ get: function ($value) {
+ return translate_cron_expression($value);
+ }
+ );
+ }
+
public static function get()
{
return InstanceSettings::findOrFail(0);
diff --git a/app/Models/LocalFileVolume.php b/app/Models/LocalFileVolume.php
index 62ee4c45c..d528099ff 100644
--- a/app/Models/LocalFileVolume.php
+++ b/app/Models/LocalFileVolume.php
@@ -2,6 +2,7 @@
namespace App\Models;
+use App\Events\FileStorageChanged;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class LocalFileVolume extends BaseModel
@@ -23,8 +24,9 @@ public function service()
return $this->morphTo('resource');
}
- public function deleteStorageOnServer()
+ public function loadStorageOnServer()
{
+ $this->load(['service']);
$isService = data_get($this->resource, 'service');
if ($isService) {
$workdir = $this->resource->service->workdir();
@@ -33,20 +35,57 @@ public function deleteStorageOnServer()
$workdir = $this->resource->workdir();
$server = $this->resource->destination->server;
}
- $commands = collect([
- "cd $workdir",
- ]);
- $fs_path = data_get($this, 'fs_path');
- if ($fs_path && $fs_path != '/' && $fs_path != '.' && $fs_path != '..') {
- $commands->push("rm -rf $fs_path");
+ $commands = collect([]);
+ $path = data_get_str($this, 'fs_path');
+ if ($path->startsWith('.')) {
+ $path = $path->after('.');
+ $path = $workdir.$path;
}
- ray($commands);
+ $isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
+ if ($isFile === 'OK') {
+ $content = instant_remote_process(["cat $path"], $server, false);
+ $this->content = $content;
+ $this->is_directory = false;
+ $this->save();
+ }
+ }
- return instant_remote_process($commands, $server);
+ public function deleteStorageOnServer()
+ {
+ $this->load(['service']);
+ $isService = data_get($this->resource, 'service');
+ if ($isService) {
+ $workdir = $this->resource->service->workdir();
+ $server = $this->resource->service->server;
+ } else {
+ $workdir = $this->resource->workdir();
+ $server = $this->resource->destination->server;
+ }
+ $commands = collect([]);
+ $path = data_get_str($this, 'fs_path');
+ if ($path->startsWith('.')) {
+ $path = $path->after('.');
+ $path = $workdir.$path;
+ }
+ $isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
+ $isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
+ if ($path && $path != '/' && $path != '.' && $path != '..') {
+ if ($isFile === 'OK') {
+ $commands->push("rm -rf $path > /dev/null 2>&1 || true");
+
+ } elseif ($isDir === 'OK') {
+ $commands->push("rm -rf $path > /dev/null 2>&1 || true");
+ $commands->push("rmdir $path > /dev/null 2>&1 || true");
+ }
+ }
+ if ($commands->count() > 0) {
+ return instant_remote_process($commands, $server);
+ }
}
public function saveStorageOnServer()
{
+ $this->load(['service']);
$isService = data_get($this->resource, 'service');
if ($isService) {
$workdir = $this->resource->service->workdir();
@@ -55,13 +94,10 @@ public function saveStorageOnServer()
$workdir = $this->resource->workdir();
$server = $this->resource->destination->server;
}
- $commands = collect([
- "mkdir -p $workdir > /dev/null 2>&1 || true",
- "cd $workdir",
- ]);
- $is_directory = $this->is_directory;
- if ($is_directory) {
+ $commands = collect([]);
+ if ($this->is_directory) {
$commands->push("mkdir -p $this->fs_path > /dev/null 2>&1 || true");
+ $commands->push("cd $workdir");
}
if (str($this->fs_path)->startsWith('.') || str($this->fs_path)->startsWith('/') || str($this->fs_path)->startsWith('~')) {
$parent_dir = str($this->fs_path)->beforeLast('/');
@@ -69,35 +105,50 @@ public function saveStorageOnServer()
$commands->push("mkdir -p $parent_dir > /dev/null 2>&1 || true");
}
}
- $fileVolume = $this;
- $path = str(data_get($fileVolume, 'fs_path'));
- $content = data_get($fileVolume, 'content');
+ $path = data_get_str($this, 'fs_path');
+ $content = data_get($this, 'content');
if ($path->startsWith('.')) {
$path = $path->after('.');
$path = $workdir.$path;
}
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
- if ($isFile == 'OK' && $fileVolume->is_directory) {
+ if ($isFile == 'OK' && $this->is_directory) {
+ $content = instant_remote_process(["cat $path"], $server, false);
+ $this->is_directory = false;
+ $this->content = $content;
+ $this->save();
+ FileStorageChanged::dispatch(data_get($server, 'team_id'));
throw new \Exception('The following file is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.');
- } elseif ($isDir == 'OK' && ! $fileVolume->is_directory) {
- throw new \Exception('The following file is a directory on the server, but you are trying to mark it as a file.
Please delete the directory on the server or mark it as directory.');
+ } elseif ($isDir == 'OK' && ! $this->is_directory) {
+ if ($path == '/' || $path == '.' || $path == '..' || $path == '' || str($path)->isEmpty() || is_null($path)) {
+ $this->is_directory = true;
+ $this->save();
+ throw new \Exception('The following file is a directory on the server, but you are trying to mark it as a file.
https://github.com/coollabsio/coolify-examples main branch will be selected https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify nodejs-fastify branch will be selected. https://gitea.com/sedlav/expressjs.git main branch will be selected. https://gitlab.com/andrasbacsai/nodejs-example.git main branch will be selected."
+ "repository.url": "Examples For Public repositories, use https://.... For Private repositories, use git@....
-
- Are you sure you want to reset the labels to Coolify generated labels? It could break the proxy
- configuration after you restart the container.
-
-
+
@endif