From 981b670eb4e816c020016c3bd20a55a17c8c1540 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 4 Jun 2026 09:34:11 +0200 Subject: [PATCH] feat(services): show template update timestamps --- app/Livewire/Project/New/Select.php | 57 ++++++++++++++- .../livewire/project/new/select.blade.php | 19 ++++- .../ServiceTemplatesLastUpdatedHintTest.php | 70 +++++++++++++++++++ 3 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 tests/Feature/ServiceTemplatesLastUpdatedHintTest.php diff --git a/app/Livewire/Project/New/Select.php b/app/Livewire/Project/New/Select.php index 165e4b59e..d6d234b18 100644 --- a/app/Livewire/Project/New/Select.php +++ b/app/Livewire/Project/New/Select.php @@ -4,7 +4,9 @@ namespace App\Livewire\Project\New; use App\Models\Project; use App\Models\Server; +use Carbon\CarbonImmutable; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Cache; use Livewire\Component; class Select extends Component @@ -105,7 +107,9 @@ class Select extends Component public function loadServices() { $services = get_service_templates(); - $services = collect($services)->map(function ($service, $key) { + $templateLastUpdatedMap = $this->serviceTemplateLastUpdatedMap($services->keys()); + + $services = collect($services)->map(function ($service, $key) use ($templateLastUpdatedMap) { $default_logo = 'images/default.webp'; $logo = data_get($service, 'logo', $default_logo); $local_logo_path = public_path($logo); @@ -116,6 +120,7 @@ class Select extends Component 'logo_github_url' => file_exists($local_logo_path) ? 'https://raw.githubusercontent.com/coollabsio/coolify/refs/heads/main/public/'.$logo : asset($default_logo), + 'templateLastUpdated' => $templateLastUpdatedMap[(string) $key] ?? null, ] + (array) $service; })->all(); @@ -247,6 +252,7 @@ class Select extends Component ]; return [ + 'serviceTemplatesLastUpdated' => $this->serviceTemplatesLastUpdated(), 'services' => $services, 'categories' => $categories, 'gitBasedApplications' => $gitBasedApplications, @@ -268,6 +274,55 @@ class Select extends Component } } + private function serviceTemplatesLastUpdated(): ?string + { + return $this->formatLastModified($this->serviceTemplatesPath()); + } + + private function serviceTemplateLastUpdatedMap(Collection $serviceNames): array + { + $bundleMtime = file_exists($this->serviceTemplatesPath()) ? filemtime($this->serviceTemplatesPath()) : 0; + + return Cache::remember( + "service-template-last-updated-map:{$bundleMtime}", + now()->addDay(), + fn () => $serviceNames + ->mapWithKeys(fn ($serviceName) => [ + (string) $serviceName => $this->serviceTemplateLastUpdated((string) $serviceName), + ]) + ->all() + ); + } + + private function serviceTemplateLastUpdated(string $serviceName): ?string + { + foreach (['yaml', 'yml'] as $extension) { + $templatePath = base_path("templates/compose/{$serviceName}.{$extension}"); + + if (file_exists($templatePath)) { + return $this->formatLastModified($templatePath); + } + } + + return null; + } + + private function serviceTemplatesPath(): string + { + return base_path('templates/'.config('constants.services.file_name')); + } + + private function formatLastModified(string $path): ?string + { + if (! file_exists($path)) { + return null; + } + + return CarbonImmutable::createFromTimestamp(filemtime($path)) + ->timezone(config('app.timezone')) + ->format('M j, Y H:i'); + } + public function setType(string $type) { $type = str($type)->lower()->slug()->value(); diff --git a/resources/views/livewire/project/new/select.blade.php b/resources/views/livewire/project/new/select.blade.php index c5482d9f7..debe3326f 100644 --- a/resources/views/livewire/project/new/select.blade.php +++ b/resources/views/livewire/project/new/select.blade.php @@ -138,9 +138,14 @@