mirror of
https://github.com/coollabsio/coolify.git
synced 2026-06-14 03:19:51 +00:00
feat(services): show template update timestamps
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -138,9 +138,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<div x-show="filteredServices.length > 0" class="mt-8">
|
||||
<div class="flex items-center gap-4" x-init="loadResources">
|
||||
<div class="flex flex-wrap items-center gap-4" x-init="loadResources">
|
||||
<h2>Services</h2>
|
||||
<x-forms.button x-on:click="loadResources">Reload List</x-forms.button>
|
||||
<div x-show="serviceTemplatesLastUpdated"
|
||||
class="text-xs text-neutral-500 dark:text-neutral-400">
|
||||
Last Updated on Service Templates:
|
||||
<span x-text="serviceTemplatesLastUpdated"></span>
|
||||
</div>
|
||||
</div>
|
||||
<x-callout type="info" title="Trademarks Policy" class="mt-4 mb-6">
|
||||
The respective trademarks mentioned here are owned by the respective companies, and use of them
|
||||
@@ -154,7 +159,14 @@
|
||||
<x-resource-view>
|
||||
<x-slot:title>
|
||||
<template x-if="service.name">
|
||||
<div>
|
||||
<span x-text="service.name"></span>
|
||||
<template x-if="service.templateLastUpdated">
|
||||
<div class="mt-1 text-[0.7rem] font-normal text-neutral-500 dark:text-neutral-500">
|
||||
Updated: <span x-text="service.templateLastUpdated"></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</x-slot>
|
||||
<x-slot:description>
|
||||
@@ -237,6 +249,7 @@
|
||||
isSticky: false,
|
||||
selecting: false,
|
||||
services: [],
|
||||
serviceTemplatesLastUpdated: null,
|
||||
gitBasedApplications: [],
|
||||
dockerBasedApplications: [],
|
||||
databases: [],
|
||||
@@ -251,12 +264,14 @@
|
||||
this.loading = true;
|
||||
const {
|
||||
services,
|
||||
serviceTemplatesLastUpdated,
|
||||
categories,
|
||||
gitBasedApplications,
|
||||
dockerBasedApplications,
|
||||
databases
|
||||
} = await this.$wire.loadServices();
|
||||
this.services = services;
|
||||
this.serviceTemplatesLastUpdated = serviceTemplatesLastUpdated;
|
||||
this.categories = categories || [];
|
||||
this.gitBasedApplications = gitBasedApplications;
|
||||
this.dockerBasedApplications = dockerBasedApplications;
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Project\New\Select;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\ViewErrorBag;
|
||||
|
||||
beforeEach(function () {
|
||||
Cache::flush();
|
||||
});
|
||||
|
||||
it('returns the service templates bundle last updated timestamp', function () {
|
||||
$component = new Select;
|
||||
$templatePath = base_path('templates/'.config('constants.services.file_name'));
|
||||
|
||||
$resources = $component->loadServices();
|
||||
|
||||
expect($resources)
|
||||
->toHaveKey('serviceTemplatesLastUpdated')
|
||||
->and($resources['serviceTemplatesLastUpdated'])
|
||||
->toBe(CarbonImmutable::createFromTimestamp(filemtime($templatePath))->timezone(config('app.timezone'))->format('M j, Y H:i'));
|
||||
});
|
||||
|
||||
it('returns each service template last updated timestamp', function () {
|
||||
$component = new Select;
|
||||
$templatePath = base_path('templates/compose/activepieces.yaml');
|
||||
|
||||
$resources = $component->loadServices();
|
||||
|
||||
expect($resources['services']['activepieces'])
|
||||
->toHaveKey('templateLastUpdated')
|
||||
->and($resources['services']['activepieces']['templateLastUpdated'])
|
||||
->toBe(CarbonImmutable::createFromTimestamp(filemtime($templatePath))->timezone(config('app.timezone'))->format('M j, Y H:i'));
|
||||
});
|
||||
|
||||
it('uses a service template timestamp cache keyed by bundle mtime', function () {
|
||||
$bundleMtime = filemtime(base_path('templates/'.config('constants.services.file_name')));
|
||||
Cache::put("service-template-last-updated-map:{$bundleMtime}", [
|
||||
'activepieces' => 'Cached timestamp',
|
||||
], now()->addDay());
|
||||
|
||||
$resources = (new Select)->loadServices();
|
||||
|
||||
expect($resources['services']['activepieces']['templateLastUpdated'])->toBe('Cached timestamp');
|
||||
});
|
||||
|
||||
it('does not use stale service template timestamp cache entries from another bundle mtime', function () {
|
||||
$bundleMtime = filemtime(base_path('templates/'.config('constants.services.file_name')));
|
||||
Cache::put('service-template-last-updated-map:'.($bundleMtime - 1), [
|
||||
'activepieces' => 'Stale cached timestamp',
|
||||
], now()->addDay());
|
||||
|
||||
$resources = (new Select)->loadServices();
|
||||
|
||||
expect($resources['services']['activepieces']['templateLastUpdated'])->not->toBe('Stale cached timestamp');
|
||||
});
|
||||
|
||||
it('renders the service templates last updated hint placeholder', function () {
|
||||
View::share('errors', new ViewErrorBag);
|
||||
|
||||
$view = $this->view('livewire.project.new.select', [
|
||||
'current_step' => 'type',
|
||||
'environments' => collect(),
|
||||
]);
|
||||
|
||||
$view->assertSee('Last Updated on Service Templates:');
|
||||
$view->assertSee('serviceTemplatesLastUpdated');
|
||||
$view->assertSee('service.templateLastUpdated');
|
||||
});
|
||||
Reference in New Issue
Block a user