feat(storage): add read-only volume handling and UI notifications

- Introduced `isReadOnlyVolume` method in `LocalFileVolume` and `LocalPersistentVolume` models to determine if a volume is read-only based on Docker Compose configuration.
- Updated `FileStorage` and `Show` components to set `isReadOnly` state during mounting.
- Enhanced UI to display notifications for read-only volumes, preventing modification actions in the interface.
- Refactored file storage and directory management forms to conditionally enable or disable actions based on read-only status.
This commit is contained in:
Andras Bacsai
2025-10-03 16:39:57 +02:00
parent 0bc7283bd6
commit b4cfb78f86
7 changed files with 358 additions and 88 deletions

View File

@@ -5,6 +5,7 @@ namespace App\Models;
use App\Events\FileStorageChanged;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Symfony\Component\Yaml\Yaml;
class LocalFileVolume extends BaseModel
{
@@ -192,4 +193,61 @@ class LocalFileVolume extends BaseModel
{
return $query->get()->where('plain_mount_path', $path);
}
// Check if this volume is read-only by parsing the docker-compose content
public function isReadOnlyVolume(): bool
{
try {
// Only check for services
$service = $this->service;
if (! $service || ! method_exists($service, 'service')) {
return false;
}
$actualService = $service->service;
if (! $actualService || ! $actualService->docker_compose_raw) {
return false;
}
// Parse the docker-compose content
$compose = Yaml::parse($actualService->docker_compose_raw);
if (! isset($compose['services'])) {
return false;
}
// Find the service that this volume belongs to
$serviceName = $service->name;
if (! isset($compose['services'][$serviceName]['volumes'])) {
return false;
}
$volumes = $compose['services'][$serviceName]['volumes'];
// Check each volume to find a match
foreach ($volumes as $volume) {
// Volume can be string like "host:container:ro" or "host:container"
if (is_string($volume)) {
$parts = explode(':', $volume);
// Check if this volume matches our fs_path and mount_path
if (count($parts) >= 2) {
$hostPath = $parts[0];
$containerPath = $parts[1];
$options = $parts[2] ?? null;
// Match based on fs_path and mount_path
if ($hostPath === $this->fs_path && $containerPath === $this->mount_path) {
return $options === 'ro';
}
}
}
}
return false;
} catch (\Throwable $e) {
ray($e->getMessage(), 'Error checking read-only volume');
return false;
}
}
}