fix: Improve read-only volume detection and UI messaging
- Add isServiceResource() and shouldBeReadOnlyInUI() to LocalFileVolume - Update path matching to handle leading slashes in volume comparisons - Update FileStorage and Show components to use shouldBeReadOnlyInUI() - Show consolidated warning message for service/compose resources in all.blade.php - Remove redundant per-volume warnings for service resources - Clean up configuration.blade.php formatting 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
475cfd78cd
commit
9bc33d65ab
7 changed files with 49 additions and 26 deletions
|
|
@ -62,7 +62,7 @@ public function mount()
|
|||
$this->fs_path = $this->fileStorage->fs_path;
|
||||
}
|
||||
|
||||
$this->isReadOnly = $this->fileStorage->isReadOnlyVolume();
|
||||
$this->isReadOnly = $this->fileStorage->shouldBeReadOnlyInUI();
|
||||
$this->syncData();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ private function syncData(bool $toModel = false): void
|
|||
public function mount()
|
||||
{
|
||||
$this->syncData(false);
|
||||
$this->isReadOnly = $this->storage->isReadOnlyVolume();
|
||||
$this->isReadOnly = $this->storage->shouldBeReadOnlyInUI();
|
||||
}
|
||||
|
||||
public function submit()
|
||||
|
|
|
|||
|
|
@ -209,6 +209,23 @@ public function scopeWherePlainMountPath($query, $path)
|
|||
return $query->get()->where('plain_mount_path', $path);
|
||||
}
|
||||
|
||||
// Check if this volume belongs to a service resource
|
||||
public function isServiceResource(): bool
|
||||
{
|
||||
return in_array($this->resource_type, [
|
||||
'App\Models\ServiceApplication',
|
||||
'App\Models\ServiceDatabase',
|
||||
]);
|
||||
}
|
||||
|
||||
// Determine if this volume should be read-only in the UI
|
||||
// File/directory mounts can be edited even for services
|
||||
public function shouldBeReadOnlyInUI(): bool
|
||||
{
|
||||
// Check for explicit :ro flag in compose (existing logic)
|
||||
return $this->isReadOnlyVolume();
|
||||
}
|
||||
|
||||
// Check if this volume is read-only by parsing the docker-compose content
|
||||
public function isReadOnlyVolume(): bool
|
||||
{
|
||||
|
|
@ -251,8 +268,12 @@ public function isReadOnlyVolume(): bool
|
|||
$containerPath = $parts[1];
|
||||
$options = $parts[2] ?? null;
|
||||
|
||||
// Match based on mount_path (container path)
|
||||
if ($containerPath === $this->mount_path) {
|
||||
// Match based on mount_path
|
||||
// Remove leading slash from mount_path if present for comparison
|
||||
$mountPath = str($this->mount_path)->ltrim('/')->toString();
|
||||
$containerPathClean = str($containerPath)->ltrim('/')->toString();
|
||||
|
||||
if ($mountPath === $containerPathClean || $this->mount_path === $containerPath) {
|
||||
return $options === 'ro';
|
||||
}
|
||||
}
|
||||
|
|
@ -261,8 +282,12 @@ public function isReadOnlyVolume(): bool
|
|||
$containerPath = data_get($volume, 'target');
|
||||
$readOnly = data_get($volume, 'read_only', false);
|
||||
|
||||
// Match based on mount_path (container path)
|
||||
if ($containerPath === $this->mount_path) {
|
||||
// Match based on mount_path
|
||||
// Remove leading slash from mount_path if present for comparison
|
||||
$mountPath = str($this->mount_path)->ltrim('/')->toString();
|
||||
$containerPathClean = str($containerPath)->ltrim('/')->toString();
|
||||
|
||||
if ($mountPath === $containerPathClean || $this->mount_path === $containerPath) {
|
||||
return $readOnly === true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
<livewire:project.service.stack-form :service="$service" />
|
||||
<h3>Services</h3>
|
||||
<div class="grid grid-cols-1 gap-2 pt-4 xl:grid-cols-1">
|
||||
@if($applications->isEmpty() && $databases->isEmpty())
|
||||
@if ($applications->isEmpty() && $databases->isEmpty())
|
||||
<div class="p-4 text-sm text-neutral-500">
|
||||
No services defined in this Docker Compose file.
|
||||
</div>
|
||||
|
|
@ -76,7 +76,8 @@
|
|||
@if ($application->fqdn)
|
||||
<span class="flex gap-1 text-xs">{{ Str::limit($application->fqdn, 60) }}
|
||||
@can('update', $service)
|
||||
<x-modal-input title="Edit Domains" :closeOutside="false" minWidth="32rem" maxWidth="40rem">
|
||||
<x-modal-input title="Edit Domains" :closeOutside="false" minWidth="32rem"
|
||||
maxWidth="40rem">
|
||||
<x-slot:content>
|
||||
<span class="cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -100,7 +101,7 @@ class="w-4 h-4 dark:text-warning text-coollabs"
|
|||
@endcan
|
||||
</span>
|
||||
@endif
|
||||
<div class="pt-2 text-xs">{{ formatContainerStatus($application->status) }}</div>
|
||||
<div class="pt-2 text-xs">{{ formatContainerStatus($application->status) }}</div>
|
||||
</div>
|
||||
<div class="flex items-center px-4">
|
||||
<a class="mx-4 text-xs font-bold hover:underline"
|
||||
|
|
@ -149,7 +150,7 @@ class="w-4 h-4 dark:text-warning text-coollabs"
|
|||
@if ($database->description)
|
||||
<span class="text-xs">{{ Str::limit($database->description, 60) }}</span>
|
||||
@endif
|
||||
<div class="text-xs">{{ formatContainerStatus($database->status) }}</div>
|
||||
<div class="text-xs">{{ formatContainerStatus($database->status) }}</div>
|
||||
</div>
|
||||
<div class="flex items-center px-4">
|
||||
@if ($database->isBackupSolutionAvailable() || $database->is_migrated)
|
||||
|
|
@ -185,10 +186,6 @@ class="w-4 h-4 dark:text-warning text-coollabs"
|
|||
<h2>Storages</h2>
|
||||
</div>
|
||||
<div class="pb-4">Persistent storage to preserve data between deployments.</div>
|
||||
<div class="pb-4 dark:text-warning text-coollabs">If you would like to add a volume, you must add it to
|
||||
your compose file (<a class="underline"
|
||||
href="{{ route('project.service.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}">General
|
||||
tab</a>).</div>
|
||||
@foreach ($applications as $application)
|
||||
<livewire:project.service.storage wire:key="application-{{ $application->id }}"
|
||||
:resource="$application" />
|
||||
|
|
|
|||
|
|
@ -275,15 +275,9 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5
|
|||
</div>
|
||||
<div>Persistent storage to preserve data between deployments.</div>
|
||||
</div>
|
||||
@if ($resource?->build_pack === 'dockercompose')
|
||||
<div 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.</div>
|
||||
@else
|
||||
@if ($resource->persistentStorages()->get()->count() === 0 && $fileStorage->count() == 0)
|
||||
<div>No storage found.</div>
|
||||
@endif
|
||||
@if ($resource->persistentStorages()->get()->count() === 0 && $fileStorage->count() == 0)
|
||||
<div>No storage found.</div>
|
||||
@endif
|
||||
|
||||
@php
|
||||
$hasVolumes = $this->volumeCount > 0;
|
||||
$hasFiles = $this->fileCount > 0;
|
||||
|
|
@ -370,7 +364,6 @@ class="px-4 py-2 -mb-px font-medium transition-colors {{ $hasDirectories ? 'dark
|
|||
<h2>{{ Str::headline($resource->name) }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($resource->persistentStorages()->get()->count() === 0 && $fileStorage->count() == 0)
|
||||
<div>No storage found.</div>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
<div>
|
||||
<div class="flex flex-col gap-4">
|
||||
@if ($resource->type() === 'service' || data_get($resource, 'build_pack') === 'dockercompose')
|
||||
<div class="w-full p-2 text-sm rounded bg-warning/10 text-warning">
|
||||
Volume mounts are read-only. If you would like to add or modify a volume, you must edit your Docker
|
||||
Compose file and reload the compose file.
|
||||
</div>
|
||||
@endif
|
||||
@foreach ($resource->persistentStorages as $storage)
|
||||
@if ($resource->type() === 'service')
|
||||
<livewire:project.shared.storages.show wire:key="storage-{{ $storage->id }}" :storage="$storage"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
<div>
|
||||
<form wire:submit='submit' class="flex flex-col items-center gap-4 p-4 bg-white border lg:items-start dark:bg-base dark:border-coolgray-300 border-neutral-200">
|
||||
@if ($isReadOnly)
|
||||
<div class="w-full p-2 text-sm rounded bg-warning/10 text-warning">
|
||||
This volume is mounted as read-only and cannot be modified from the UI.
|
||||
</div>
|
||||
@if (!$storage->isServiceResource() && !$storage->isDockerComposeResource())
|
||||
<div class="w-full p-2 text-sm rounded bg-warning/10 text-warning">
|
||||
This volume is mounted as read-only and cannot be modified from the UI.
|
||||
</div>
|
||||
@endif
|
||||
@if ($isFirst)
|
||||
<div class="flex gap-2 items-end w-full md:flex-row flex-col">
|
||||
@if (
|
||||
|
|
|
|||
Loading…
Reference in a new issue