fix: service volume read from filesystem

fix: edit compose moved to dialog
This commit is contained in:
Andras Bacsai 2023-09-30 15:08:40 +02:00
parent 64ce41df0f
commit 79fde593a9
14 changed files with 118 additions and 72 deletions

View File

@ -0,0 +1,19 @@
<?php
namespace App\Http\Livewire\Project\Service;
use Livewire\Component;
class ComposeModal extends Component
{
public string $raw;
public string $actual;
public function render()
{
return view('livewire.project.service.compose-modal');
}
public function submit() {
$this->emit('warning', "Saving new docker compose...");
$this->emit('saveCompose', $this->raw);
}
}

View File

@ -13,6 +13,7 @@ class FileStorage extends Component
public LocalFileVolume $fileStorage;
public ServiceApplication|ServiceDatabase $service;
public string $fs_path;
public ?string $workdir = null;
protected $rules = [
'fileStorage.is_directory' => 'required',
@ -23,13 +24,13 @@ class FileStorage extends Component
public function mount()
{
$this->service = $this->fileStorage->service;
$this->fs_path = Str::of($this->fileStorage->fs_path)->beforeLast('/');
$file = Str::of($this->fileStorage->fs_path)->afterLast('/');
if (Str::of($this->fs_path)->startsWith('.')) {
$this->fs_path = Str::of($this->fs_path)->after('.');
$this->fs_path = $this->service->service->workdir() . $this->fs_path . "/" . $file;
}
if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
$this->workdir = $this->service->service->workdir();
$this->fs_path = Str::of($this->fileStorage->fs_path)->after('.');
} else {
$this->workdir = null;
$this->fs_path = $this->fileStorage->fs_path;
}
}
public function submit()
{
@ -41,8 +42,6 @@ public function submit()
}
$this->fileStorage->save();
$this->fileStorage->saveStorageOnServer($this->service);
// ray($this->fileStorage);
// $this->service->saveFileVolumes();
$this->emit('success', 'File updated successfully.');
} catch (\Throwable $e) {
$this->fileStorage->setRawAttributes($original);

View File

@ -4,7 +4,6 @@
use App\Jobs\ContainerStatusJob;
use App\Models\Service;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
use Livewire\Component;
class Index extends Component
@ -20,7 +19,25 @@ class Index extends Component
'service.name' => 'required',
'service.description' => 'nullable',
];
public function checkStatus() {
protected $listeners = ["saveCompose"];
public function render()
{
return view('livewire.project.service.index');
}
public function mount()
{
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$this->refreshStack();
}
public function saveCompose($raw)
{
$this->service->docker_compose_raw = $raw;
$this->submit();
}
public function checkStatus()
{
dispatch_sync(new ContainerStatusJob($this->service->server));
$this->refreshStack();
}
@ -35,17 +52,8 @@ public function refreshStack()
$database->refresh();
});
}
public function mount()
{
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$this->refreshStack();
}
public function render()
{
return view('livewire.project.service.index');
}
public function submit()
{
try {

View File

@ -20,18 +20,26 @@ class Show extends Component
public function mount()
{
$this->services = collect([]);
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
if ($service) {
$this->serviceApplication = $service;
$this->serviceApplication->getFilesFromServer();
} else {
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
$this->serviceDatabase->getFilesFromServer();
try {
$this->services = collect([]);
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
if ($service) {
$this->serviceApplication = $service;
$this->serviceApplication->getFilesFromServer();
} else {
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
$this->serviceDatabase->getFilesFromServer();
}
if (is_null($service)) {
throw new \Exception("Service not found.");
}
} catch(\Throwable $e) {
return handleError($e, $this);
}
}
public function generateDockerCompose()
{

View File

@ -31,7 +31,7 @@ public function saveStorageOnServer(ServiceApplication|ServiceDatabase $service)
}
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
ray($path);
ray($isFile);
if ($isFile == 'OK' && $fileVolume->is_directory) {
throw new \Exception("File $path 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.");
} else if ($isDir == 'OK' && !$fileVolume->is_directory) {

View File

@ -196,6 +196,12 @@ public function parse(bool $isNew = false): Collection
}
}
// Check if image changed
if ($savedService->image !== $image) {
$savedService->image = $image;
$savedService->save();
}
// Collect/create/update networks
if ($serviceNetworks->count() > 0) {
foreach ($serviceNetworks as $networkName => $networkDetails) {

View File

@ -73,17 +73,22 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase $oneS
$fileVolumes = $oneService->fileStorages()->get();
$commands = collect([
"mkdir -p $workdir > /dev/null 2>&1 || true",
"cd $workdir"
"cd "
]);
instant_remote_process($commands, $server);
foreach ($fileVolumes as $fileVolume) {
$path = Str::of(data_get($fileVolume, 'fs_path'));
$content = data_get($fileVolume, 'content');
$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') {
$filesystemContent = instant_remote_process(["cat $path"], $server);
if ($path->startsWith('.')) {
$path = $path->after('.');
$fileLocation = $workdir . $path;
} else {
$fileLocation = $path;
}
$isFile = instant_remote_process(["test -f $fileLocation && echo OK || echo NOK"], $server);
$isDir = instant_remote_process(["test -d $fileLocation && echo OK || echo NOK"], $server);
if ($isFile == 'OK' && !$fileVolume->is_directory) {
$filesystemContent = instant_remote_process(["cat $fileLocation"], $server);
if (base64_encode($filesystemContent) != base64_encode($content)) {
$fileVolume->content = $filesystemContent;
$fileVolume->save();

View File

@ -30,7 +30,7 @@
*
* Minimum: 3000 (in milliseconds)
*/
'duration' => 3000,
'duration' => 5000,
/**
* The horizontal position of each toast.

View File

@ -3,7 +3,7 @@
<x-chevron-down />
</label>
<div class="absolute hidden group-hover:block">
<div class="absolute z-50 hidden group-hover:block">
<ul tabindex="0" class="relative -ml-24 text-xs text-white normal-case rounded min-w-max menu bg-coolgray-200">
@if (data_get($application, 'gitBrancLocation'))
<li>

View File

@ -2,7 +2,7 @@
<div tabindex="0" x-data="{ open: false }"
class="transition border rounded cursor-pointer collapse collapse-arrow border-coolgray-200"
:class="open ? 'collapse-open' : 'collapse-close'">
<div class="flex flex-col justify-center text-sm collapse-title" x-on:click="open = !open">
<div class="flex flex-col justify-center text-sm select-text collapse-title" x-on:click="open = !open">
{{ $title }}
</div>
<div class="collapse-content">

View File

@ -4,7 +4,7 @@
<x-chevron-down />
</label>
<div class="absolute hidden group-hover:block">
<div class="absolute z-50 hidden group-hover:block">
<ul tabindex="0"
class="relative -ml-24 text-xs text-white normal-case rounded min-w-max menu bg-coolgray-200">
@if ($links->count() > 0)

View File

@ -0,0 +1,26 @@
<dialog id="composeModal" class="modal" x-data="{ raw: true }">
<form method="dialog" class="flex flex-col gap-2 rounded max-w-7xl modal-box" wire:submit.prevent='submit'>
<h1>Docker Compose</h1>
<div x-cloak x-show="raw">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Check Deployable Compose</x-forms.button>
</div>
<div x-cloak x-show="raw === false">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
Compose</x-forms.button>
</div>
<div x-cloak x-show="raw">
<x-forms.textarea rows="20" id="raw">
</x-forms.textarea>
</div>
<div x-cloak x-show="raw === false">
<x-forms.textarea rows="20" readonly id="actual">
</x-forms.textarea>
</div>
<x-forms.button onclick="composeModal.close()" type="submit">
Save
</x-forms.button>
</form>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>

View File

@ -1,6 +1,6 @@
<x-collapsible>
<x-slot:title>
<div>{{ $fileStorage->fs_path }} -> {{ $fileStorage->mount_path }}</div>
<div>{{$workdir}}{{ $fs_path }} -> {{ $fileStorage->mount_path }}</div>
</x-slot:title>
<x-slot:action>
<form wire:submit.prevent='submit' class="flex flex-col gap-2">

View File

@ -1,5 +1,6 @@
<div x-data="{ raw: true, activeTab: window.location.hash ? window.location.hash.substring(1) : 'service-stack' }" wire:poll.10000ms="checkStatus">
<livewire:project.service.navbar :service="$service" :parameters="$parameters" :query="$query" />
<livewire:project.service.compose-modal :raw="$service->docker_compose_raw" :actual="$service->docker_compose" />
<div class="flex h-full pt-6">
<div class="flex flex-col items-start gap-4 min-w-fit">
<a target="_blank" href="{{ $service->documentation() }}">Documentation <x-external-link /></a>
@ -23,39 +24,13 @@
<div>Configuration</div>
</div>
<x-forms.button type="submit">Save</x-forms.button>
<div x-cloak x-show="raw">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable
Compose</x-forms.button>
</div>
<div x-cloak x-show="raw === false">
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
Compose</x-forms.button>
</div>
<x-forms.button class="w-64" onclick="composeModal.showModal()">Edit Compose File</x-forms.button>
</div>
<div class="flex gap-2">
<x-forms.input id="service.name" required label="Service Name"
placeholder="My super wordpress site" />
<x-forms.input id="service.description" label="Description" />
</div>
<div x-cloak x-show="raw">
<x-forms.textarea label="Docker Compose file"
helper="
You can use these variables in your Docker Compose file and Coolify will generate default values or replace them with the values you set on the UI forms.<br>
<br>
- SERVICE_FQDN_*: FQDN - could be changable from the UI. (example: SERVICE_FQDN_GHOST)<br>
- SERVICE_URL_*: URL parsed from FQDN - could be changable from the UI. (example: SERVICE_URL_GHOST)<br>
- SERVICE_BASE64_64_*: Generated 'base64' string with length of '64' (example: SERVICE_BASE64_64_GHOST, to generate 32 bit: SERVICE_BASE64_32_GHOST)<br>
- SERVICE_USER_*: Generated user (example: SERVICE_USER_MYSQL)<br>
- SERVICE_PASSWORD_*: Generated password (example: SERVICE_PASSWORD_MYSQL)<br>"
rows="6" id="service.docker_compose_raw">
</x-forms.textarea>
</div>
<div x-cloak x-show="raw === false">
<x-forms.textarea label="Actual Docker Compose file that will be deployed" readonly
rows="6" id="service.docker_compose">
</x-forms.textarea>
</div>
</form>
<div class="grid grid-cols-1 gap-2 pt-4 xl:grid-cols-3">
@foreach ($service->applications as $application)