fix: file storages (dir/file mount) handled properly

This commit is contained in:
Andras Bacsai 2024-08-05 20:00:57 +02:00
parent 8133a8b770
commit ea5101c814
10 changed files with 118 additions and 34 deletions

View File

@ -0,0 +1,32 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class FileStorageChanged implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $teamId;
public function __construct($teamId = null)
{
ray($teamId);
if (is_null($teamId)) {
throw new \Exception('Team id is null');
}
$this->teamId = $teamId;
}
public function broadcastOn(): array
{
return [
new PrivateChannel("team.{$this->teamId}"),
];
}
}

View File

@ -214,7 +214,7 @@ public function loadComposeFile($isInit = false)
}
$this->dispatch('success', 'Docker compose file loaded.');
$this->dispatch('compose_loaded');
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
$this->dispatch('refreshEnvs');
} catch (\Throwable $e) {
$this->application->docker_compose_location = $this->initialDockerComposeLocation;

View File

@ -26,6 +26,8 @@ class FileStorage extends Component
public ?string $workdir = null;
public bool $permanently_delete = true;
protected $rules = [
'fileStorage.is_directory' => 'required',
'fileStorage.fs_path' => 'required',
@ -56,7 +58,7 @@ public function convertToDirectory()
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
}
}
@ -71,20 +73,27 @@ public function convertToFile()
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
}
}
public function delete()
{
try {
$this->fileStorage->deleteStorageOnServer();
$message = 'File deleted.';
if ($this->fileStorage->is_directory) {
$message = 'Directory deleted.';
}
if ($this->permanently_delete) {
$message = 'Directory deleted from the server.';
$this->fileStorage->deleteStorageOnServer();
}
$this->fileStorage->delete();
$this->dispatch('success', 'File deleted.');
$this->dispatch('success', $message);
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
}
}

View File

@ -9,14 +9,35 @@ class Storage extends Component
{
public $resource;
public $fileStorage;
public function getListeners()
{
$teamId = auth()->user()->currentTeam()->id;
return [
"echo-private:team.{$teamId},FileStorageChanged" => 'refreshStoragesFromEvent',
'refreshStorages' => '$refresh',
'addNewVolume',
'refresh_storages' => '$refresh',
];
}
public function mount()
{
$this->refreshStorages();
}
public function refreshStoragesFromEvent()
{
$this->refreshStorages();
$this->dispatch('warning', 'File storage changed. Usually it means that the file / directory is already defined on the server, so Coolify set it up for you properly on the UI.');
}
public function refreshStorages()
{
$this->fileStorage = $this->resource->fileStorages()->get();
}
public function addNewVolume($data)
{
try {
@ -30,7 +51,7 @@ public function addNewVolume($data)
$this->resource->refresh();
$this->dispatch('success', 'Storage added successfully');
$this->dispatch('clearAddStorage');
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@ -96,7 +96,7 @@ public function submitFileStorage()
'resource_type' => get_class($this->resource),
],
);
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
} catch (\Throwable $e) {
return handleError($e, $this);
}
@ -123,7 +123,7 @@ public function submitFileStorageDirectory()
'resource_type' => get_class($this->resource),
],
);
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@ -8,5 +8,5 @@ class All extends Component
{
public $resource;
protected $listeners = ['refresh_storages' => '$refresh'];
protected $listeners = ['refreshStorages' => '$refresh'];
}

View File

@ -39,6 +39,6 @@ public function submit()
public function delete()
{
$this->storage->delete();
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
}
}

View File

@ -2,6 +2,7 @@
namespace App\Models;
use App\Events\FileStorageChanged;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class LocalFileVolume extends BaseModel
@ -33,16 +34,23 @@ public function deleteStorageOnServer()
$workdir = $this->resource->workdir();
$server = $this->resource->destination->server;
}
$commands = collect([
"cd $workdir",
]);
$commands = collect([]);
$fs_path = data_get($this, 'fs_path');
$isFile = instant_remote_process(["test -f $fs_path && echo OK || echo NOK"], $server);
$isDir = instant_remote_process(["test -d $fs_path && echo OK || echo NOK"], $server);
if ($fs_path && $fs_path != '/' && $fs_path != '.' && $fs_path != '..') {
$commands->push("rm -rf $fs_path");
}
ray($commands);
ray($isFile, $isDir);
if ($isFile === 'OK') {
$commands->push("rm -rf $fs_path > /dev/null 2>&1 || true");
return instant_remote_process($commands, $server);
} elseif ($isDir === 'OK') {
$commands->push("rm -rf $fs_path > /dev/null 2>&1 || true");
$commands->push("rmdir $fs_path > /dev/null 2>&1 || true");
}
}
if ($commands->count() > 0) {
return instant_remote_process($commands, $server);
}
}
public function saveStorageOnServer()
@ -55,13 +63,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('/');
@ -79,8 +84,11 @@ public function saveStorageOnServer()
$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) {
$content = instant_remote_process(["cat $path"], $server, false);
$fileVolume->is_directory = false;
$fileVolume->content = $content;
$fileVolume->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) {
$fileVolume->is_directory = true;

View File

@ -14,16 +14,30 @@
<div class="flex gap-2">
@if ($fileStorage->is_directory)
<x-modal-confirmation action="convertToFile" buttonTitle="Convert to file">
This will delete all files in this directory. It is not reversible. <br>Please think again.
<div>This will delete all files in this directory. It is not reversible. <strong
class="text-error">Please think
again.</strong><br><br></div>
</x-modal-confirmation>
@else
<x-modal-confirmation action="convertToDirectory" buttonTitle="Convert to directory">
This will convert this to a directory. If it was a file, it will be deleted. It is not reversible.
<br>Please think again.
<div>This will delete the file and make a directory instead. It is not reversible.
<strong class="text-error">Please think
again.</strong><br><br>
</div>
</x-modal-confirmation>
@endif
<x-modal-confirmation isErrorButton buttonTitle="Delete">
This file / directory will be deleted. It is not reversible. <br>Please think again.
<div class="px-2">This resource will be deleted. It is not reversible. <strong
class="text-error">Please think
again.</strong><br><br></div>
<h4>Actions</h4>
@if ($fileStorage->is_directory)
<x-forms.checkbox id="permanently_delete"
label="Permanently delete directory from the server?"></x-forms.checkbox>
@else
<x-forms.checkbox id="permanently_delete"
label="Permanently delete file from the server?"></x-forms.checkbox>
@endif
</x-modal-confirmation>
</div>
@if (!$fileStorage->is_directory)

View File

@ -25,7 +25,7 @@
<span class="dark:text-warning text-coollabs">Please modify storage layout in your Docker Compose
file or reload the compose file to reread the storage layout.</span>
@else
@if ($resource->persistentStorages()->get()->count() === 0 && $resource->fileStorages()->get()->count() == 0)
@if ($resource->persistentStorages()->get()->count() === 0 && $fileStorage->count() == 0)
<div class="pt-4">No storage found.</div>
@endif
@endif
@ -33,9 +33,9 @@
@if ($resource->persistentStorages()->get()->count() > 0)
<livewire:project.shared.storages.all :resource="$resource" />
@endif
@if ($resource->fileStorages()->get()->count() > 0)
@if ($fileStorage->count() > 0)
<div class="flex flex-col gap-4 pt-4">
@foreach ($resource->fileStorages()->get()->sort() as $fileStorage)
@foreach ($fileStorage->sort() as $fileStorage)
<livewire:project.service.file-storage :fileStorage="$fileStorage"
wire:key="resource-{{ $fileStorage->uuid }}" />
@endforeach
@ -48,9 +48,9 @@
@if ($resource->persistentStorages()->get()->count() > 0)
<livewire:project.shared.storages.all :resource="$resource" />
@endif
@if ($resource->fileStorages()->get()->count() > 0)
@if ($fileStorage->count() > 0)
<div class="flex flex-col gap-4 pt-4">
@foreach ($resource->fileStorages()->get()->sort() as $fileStorage)
@foreach ($fileStorage->sort() as $fileStorage)
<livewire:project.service.file-storage :fileStorage="$fileStorage"
wire:key="resource-{{ $fileStorage->uuid }}" />
@endforeach