From 22f9f96db68efb6009a3cd2085b317ab8d882992 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:24:53 +0200 Subject: [PATCH] chore: inspect staged changes for commit message --- app/Console/Commands/Generate/Services.php | 24 ++ app/Livewire/GlobalSearch.php | 8 + app/Livewire/Project/New/Select.php | 37 +- bootstrap/helpers/shared.php | 16 +- public/svgs/clickhouse-icon.svg | 8 + public/svgs/clickhouse.svg | 9 +- public/svgs/dragonfly.svg | 1 + public/svgs/keydb.svg | 1 + .../views/livewire/global-search.blade.php | 12 +- .../views/livewire/project/index.blade.php | 71 ++-- templates/service-templates-latest.json | 401 ++++++++++++++++-- templates/service-templates.json | 401 ++++++++++++++++-- tests/Feature/ProjectIndexEmptyStateTest.php | 38 ++ .../ServiceTemplateGitTimestampTest.php | 29 ++ .../ServiceTemplatesLastUpdatedHintTest.php | 51 ++- .../GlobalSearchNewImageQuickActionTest.php | 106 ++++- 16 files changed, 1071 insertions(+), 142 deletions(-) create mode 100644 public/svgs/clickhouse-icon.svg create mode 100644 public/svgs/dragonfly.svg create mode 100644 public/svgs/keydb.svg create mode 100644 tests/Feature/ProjectIndexEmptyStateTest.php create mode 100644 tests/Feature/ServiceTemplateGitTimestampTest.php diff --git a/app/Console/Commands/Generate/Services.php b/app/Console/Commands/Generate/Services.php index e316fc391..2978feb3c 100644 --- a/app/Console/Commands/Generate/Services.php +++ b/app/Console/Commands/Generate/Services.php @@ -4,6 +4,7 @@ namespace App\Console\Commands\Generate; use Illuminate\Console\Command; use Illuminate\Support\Arr; +use Illuminate\Support\Facades\Process; use Symfony\Component\Yaml\Yaml; class Services extends Command @@ -77,6 +78,7 @@ class Services extends Command 'category' => $data->get('category'), 'logo' => $data->get('logo', 'svgs/default.webp'), 'minversion' => $data->get('minversion', '0.0.0'), + 'template_last_updated_at' => $this->templateLastUpdatedAt($file), ]; if ($port = $data->get('port')) { @@ -99,6 +101,26 @@ class Services extends Command return $payload; } + private function templateLastUpdatedAt(string $file): ?string + { + $process = Process::path(base_path())->run([ + 'git', + 'log', + '-1', + '--format=%cI', + '--', + "templates/compose/{$file}", + ]); + + if ($process->failed()) { + return null; + } + + $timestamp = trim($process->output()); + + return $timestamp === '' ? null : $timestamp; + } + private function generateServiceTemplatesWithFqdn(): void { $serviceTemplatesWithFqdn = collect(array_merge( @@ -155,6 +177,7 @@ class Services extends Command 'category' => $data->get('category'), 'logo' => $data->get('logo', 'svgs/default.webp'), 'minversion' => $data->get('minversion', '0.0.0'), + 'template_last_updated_at' => $this->templateLastUpdatedAt($file), ]; if ($port = $data->get('port')) { @@ -232,6 +255,7 @@ class Services extends Command 'category' => $data->get('category'), 'logo' => $data->get('logo', 'svgs/default.webp'), 'minversion' => $data->get('minversion', '0.0.0'), + 'template_last_updated_at' => $this->templateLastUpdatedAt($file), ]; if ($port = $data->get('port')) { diff --git a/app/Livewire/GlobalSearch.php b/app/Livewire/GlobalSearch.php index df2adf22b..4148764de 100644 --- a/app/Livewire/GlobalSearch.php +++ b/app/Livewire/GlobalSearch.php @@ -1053,6 +1053,7 @@ class GlobalSearch extends Component 'quickcommand' => '(type: new postgresql)', 'type' => 'postgresql', 'category' => 'Databases', + 'logo' => 'svgs/postgresql.svg', 'resourceType' => 'database', ]); @@ -1062,6 +1063,7 @@ class GlobalSearch extends Component 'quickcommand' => '(type: new mysql)', 'type' => 'mysql', 'category' => 'Databases', + 'logo' => 'svgs/mysql.svg', 'resourceType' => 'database', ]); @@ -1071,6 +1073,7 @@ class GlobalSearch extends Component 'quickcommand' => '(type: new mariadb)', 'type' => 'mariadb', 'category' => 'Databases', + 'logo' => 'svgs/mariadb.svg', 'resourceType' => 'database', ]); @@ -1080,6 +1083,7 @@ class GlobalSearch extends Component 'quickcommand' => '(type: new redis)', 'type' => 'redis', 'category' => 'Databases', + 'logo' => 'svgs/redis.svg', 'resourceType' => 'database', ]); @@ -1089,6 +1093,7 @@ class GlobalSearch extends Component 'quickcommand' => '(type: new keydb)', 'type' => 'keydb', 'category' => 'Databases', + 'logo' => 'svgs/keydb.svg', 'resourceType' => 'database', ]); @@ -1098,6 +1103,7 @@ class GlobalSearch extends Component 'quickcommand' => '(type: new dragonfly)', 'type' => 'dragonfly', 'category' => 'Databases', + 'logo' => 'svgs/dragonfly.svg', 'resourceType' => 'database', ]); @@ -1107,6 +1113,7 @@ class GlobalSearch extends Component 'quickcommand' => '(type: new mongodb)', 'type' => 'mongodb', 'category' => 'Databases', + 'logo' => 'svgs/mongodb.svg', 'resourceType' => 'database', ]); @@ -1116,6 +1123,7 @@ class GlobalSearch extends Component 'quickcommand' => '(type: new clickhouse)', 'type' => 'clickhouse', 'category' => 'Databases', + 'logo' => 'svgs/clickhouse-icon.svg', 'resourceType' => 'database', ]); } diff --git a/app/Livewire/Project/New/Select.php b/app/Livewire/Project/New/Select.php index d6d234b18..cff886f98 100644 --- a/app/Livewire/Project/New/Select.php +++ b/app/Livewire/Project/New/Select.php @@ -6,7 +6,6 @@ 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 @@ -107,7 +106,7 @@ class Select extends Component public function loadServices() { $services = get_service_templates(); - $templateLastUpdatedMap = $this->serviceTemplateLastUpdatedMap($services->keys()); + $templateLastUpdatedMap = $this->serviceTemplateLastUpdatedMap($services); $services = collect($services)->map(function ($service, $key) use ($templateLastUpdatedMap) { $default_logo = 'images/default.webp'; @@ -279,19 +278,31 @@ class Select extends Component return $this->formatLastModified($this->serviceTemplatesPath()); } - private function serviceTemplateLastUpdatedMap(Collection $serviceNames): array + private function serviceTemplateLastUpdatedMap(Collection $services): array { - $bundleMtime = file_exists($this->serviceTemplatesPath()) ? filemtime($this->serviceTemplatesPath()) : 0; + return $services + ->mapWithKeys(fn ($service, $serviceName) => [ + (string) $serviceName => $this->serviceTemplateLastUpdatedFromPayload($service) + ?? $this->serviceTemplateLastUpdated((string) $serviceName), + ]) + ->all(); + } - 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 serviceTemplateLastUpdatedFromPayload(mixed $service): ?string + { + $timestamp = data_get($service, 'template_last_updated_at'); + + if (! is_string($timestamp) || $timestamp === '') { + return null; + } + + try { + return CarbonImmutable::parse($timestamp) + ->timezone(config('app.timezone')) + ->format('M j, Y H:i'); + } catch (\Throwable) { + return null; + } } private function serviceTemplateLastUpdated(string $serviceName): ?string diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index f2b672fef..8fdfceaf1 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1057,7 +1057,6 @@ function sslip(Server $server) function get_service_templates(bool $force = false): Collection { - if ($force) { try { $response = Http::retry(3, 1000)->get(config('constants.services.official')); @@ -1068,15 +1067,16 @@ function get_service_templates(bool $force = false): Collection return collect($services); } catch (Throwable) { - $services = File::get(base_path('templates/'.config('constants.services.file_name'))); - - return collect(json_decode($services))->sortKeys(); + return get_service_templates(); } - } else { - $services = File::get(base_path('templates/'.config('constants.services.file_name'))); - - return collect(json_decode($services))->sortKeys(); } + + $path = base_path('templates/'.config('constants.services.file_name')); + $mtime = filemtime($path) ?: 0; + + return Cache::remember("service-templates:{$mtime}", now()->addDay(), function () use ($path) { + return collect(json_decode(File::get($path)))->sortKeys(); + }); } function getResourceByUuid(string $uuid, ?int $teamId = null) diff --git a/public/svgs/clickhouse-icon.svg b/public/svgs/clickhouse-icon.svg new file mode 100644 index 000000000..e327a2a73 --- /dev/null +++ b/public/svgs/clickhouse-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/svgs/clickhouse.svg b/public/svgs/clickhouse.svg index d536536de..e327a2a73 100644 --- a/public/svgs/clickhouse.svg +++ b/public/svgs/clickhouse.svg @@ -1 +1,8 @@ - + + + + + + + + diff --git a/public/svgs/dragonfly.svg b/public/svgs/dragonfly.svg new file mode 100644 index 000000000..d762f3e03 --- /dev/null +++ b/public/svgs/dragonfly.svg @@ -0,0 +1 @@ + diff --git a/public/svgs/keydb.svg b/public/svgs/keydb.svg new file mode 100644 index 000000000..0abc24a81 --- /dev/null +++ b/public/svgs/keydb.svg @@ -0,0 +1 @@ + diff --git a/resources/views/livewire/global-search.blade.php b/resources/views/livewire/global-search.blade.php index 3316c110f..3169c7fc9 100644 --- a/resources/views/livewire/global-search.blade.php +++ b/resources/views/livewire/global-search.blade.php @@ -632,7 +632,7 @@ @foreach ($searchResults as $result) @if (!isset($result['is_creatable_suggestion'])) + class="search-result-item block px-4 py-3 hover:bg-neutral-100 dark:hover:bg-coolgray-200 transition-colors focus:outline-none focus:bg-neutral-100 dark:focus:bg-coolgray-200 focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-coollabs dark:focus-visible:ring-warning">
@@ -696,12 +696,12 @@ @foreach ($items as $item)