feat(project): add icons and improve persistent volume management

Add project icon storage and serving with local/S3 support, prevent deletion of compose-managed volumes, and optimize volume backups. Refine project and backup UI components with consistent styling and coverage.
This commit is contained in:
Andras Bacsai
2026-08-13 22:56:52 +02:00
parent a02d02d19b
commit fd5eb3e0cd
43 changed files with 728 additions and 60 deletions
+44
View File
@@ -4,6 +4,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Support\Str;
use Symfony\Component\Yaml\Yaml;
class LocalPersistentVolume extends BaseModel
@@ -136,6 +137,49 @@ class LocalPersistentVolume extends BaseModel
return $this->isReadOnlyVolume();
}
public function isDeclaredInCompose(): bool
{
try {
$resource = $this->resource;
if (! $resource) {
return true;
}
$composeContent = $resource instanceof Application
? $resource->docker_compose_raw
: data_get($resource, 'service.docker_compose_raw');
if (blank($composeContent)) {
return true;
}
$compose = Yaml::parse($composeContent);
$services = data_get($compose, 'services', []);
if ($this->isServiceResource()) {
$services = array_intersect_key($services, [$resource->name => true]);
}
foreach ($services as $service) {
foreach (data_get($service, 'volumes', []) as $volume) {
$parsedVolume = is_array($volume) ? $volume : parseDockerVolumeString($volume);
$source = data_get($parsedVolume, 'source');
$target = data_get($parsedVolume, 'target');
$resourceUuid = $resource instanceof Application ? $resource->uuid : data_get($resource, 'service.uuid');
$generatedName = $source ? $resourceUuid.'_'.Str::slug($source, '-') : null;
if ($generatedName === $this->name && $target && str($target)->start('/')->value() === $this->mount_path) {
return true;
}
}
}
return false;
} catch (\Throwable) {
return true;
}
}
// Check if this volume is read-only by parsing the docker-compose content
public function isReadOnlyVolume(): bool
{