From 12a8fb83fd69695220cf5a2ade063868b20ab0f3 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:05:38 +0200 Subject: [PATCH] fix(backups): show exact execution times in backup tables Closes #11316 --- resources/css/app.css | 10 +-- .../database/backup-executions.blade.php | 11 ++- .../service/backup-executions.blade.php | 14 +++- .../volume-backups/executions.blade.php | 8 +- .../Feature/BackupExecutionTimestampsTest.php | 80 +++++++++++++++++++ tests/Feature/VolumeBackupTest.php | 7 +- 6 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 tests/Feature/BackupExecutionTimestampsTest.php diff --git a/resources/css/app.css b/resources/css/app.css index f45494098d..a21c2e4344 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -3178,9 +3178,9 @@ input[type="search"]::-webkit-search-results-decoration { } .backup-executions-table-grid { - grid-template-columns: 6.5rem minmax(7rem, 0.8fr) minmax(14rem, 1.5fr) 7rem 5rem 4rem minmax(8rem, 1fr) 5rem; + grid-template-columns: 6.5rem minmax(7rem, 0.8fr) minmax(14rem, 1.5fr) 10rem 5rem 4rem minmax(8rem, 1fr) 5rem; gap: 0.75rem; - min-width: 66rem; + min-width: 69rem; } .backup-executions-table-scroll { @@ -3192,8 +3192,8 @@ input[type="search"]::-webkit-search-results-decoration { } .volume-backup-executions-grid { - grid-template-columns: 7.5rem minmax(14rem, 1fr) 8.5rem 7rem 9rem minmax(9rem, 0.7fr); - min-width: 50rem; + grid-template-columns: 7.5rem minmax(14rem, 1fr) 11rem 7rem 9rem minmax(9rem, 0.7fr); + min-width: 52.5rem; } .clone-destinations-table-grid { @@ -3239,7 +3239,7 @@ input[type="search"]::-webkit-search-results-decoration { } .backup-executions-table-grid { - grid-template-columns: 7.5rem minmax(9rem, 0.8fr) minmax(14rem, 1.5fr) 8rem 6rem minmax(9rem, auto); + grid-template-columns: 7.5rem minmax(9rem, 0.8fr) minmax(14rem, 1.5fr) 10rem 6rem minmax(9rem, auto); } .backup-executions-table-grid > :nth-child(6), diff --git a/resources/views/livewire/project/database/backup-executions.blade.php b/resources/views/livewire/project/database/backup-executions.blade.php index 6bd2851b73..f731b005f8 100644 --- a/resources/views/livewire/project/database/backup-executions.blade.php +++ b/resources/views/livewire/project/database/backup-executions.blade.php @@ -44,6 +44,9 @@ Availability Actions + @php + $server = $this->server(); + @endphp @foreach ($executions as $execution) @php $executionStatus = data_get($execution, 'status'); @@ -92,11 +95,15 @@ title="Backup path: {{ data_get($execution, 'filename', 'N/A') }}">{{ data_get($execution, 'filename', 'N/A') }} -
+
@if ($executionStatus === 'running') Running now @else - {{ \Carbon\Carbon::parse(data_get($execution, 'finished_at'))->diffForHumans() }} +
+ {{ formatDateInServerTimezone(data_get($execution, 'finished_at'), $server) }} +
+
{{ \Carbon\Carbon::parse(data_get($execution, 'finished_at'))->diffForHumans() }}
@endif
diff --git a/resources/views/livewire/project/service/backup-executions.blade.php b/resources/views/livewire/project/service/backup-executions.blade.php index ee992b9a47..61fd3e9d0b 100644 --- a/resources/views/livewire/project/service/backup-executions.blade.php +++ b/resources/views/livewire/project/service/backup-executions.blade.php @@ -1,4 +1,7 @@
+ @php + $server = $service->server; + @endphp @if ($selectedExecution) @@ -6,7 +9,7 @@

Target

{{ $selectedExecution['target'] }}

Status

{{ str($selectedExecution['status'])->headline() }}

-

Started

{{ $selectedExecution['started_at']->diffForHumans() }}

+

Started

{{ formatDateInServerTimezone($selectedExecution['started_at'], $server) }}

Size

{{ $selectedExecution['size'] ? formatBytes($selectedExecution['size']) : '-' }}

@if ($selectedExecution['filename']) @@ -32,7 +35,7 @@ @else
-
+
TargetTypeScheduleStatusStartedSizeActions
@foreach ($executions as $execution) @@ -47,7 +50,7 @@
+ class="data-table-row grid min-w-[900px] cursor-pointer grid-cols-[minmax(150px,1.4fr)_100px_100px_110px_190px_90px_48px] text-left text-[13px] text-neutral-700 dark:text-fg-dim"> {{ $execution['target'] }} {{ $execution['type'] }}{{ $execution['schedule'] }} - {{ $execution['started_at']->diffForHumans() }} + + {{ formatDateInServerTimezone($execution['started_at'], $server) }} + {{ $execution['started_at']->diffForHumans() }} + {{ $execution['size'] ? formatBytes($execution['size']) : '-' }} @if ($execution['download_url']) diff --git a/resources/views/livewire/project/shared/storages/volume-backups/executions.blade.php b/resources/views/livewire/project/shared/storages/volume-backups/executions.blade.php index b4bf4465ec..46077e4f52 100644 --- a/resources/views/livewire/project/shared/storages/volume-backups/executions.blade.php +++ b/resources/views/livewire/project/shared/storages/volume-backups/executions.blade.php @@ -3,6 +3,7 @@ $executionCount = method_exists($executions, 'total') ? $executions->total() : $executions->count(); $currentPage = method_exists($executions, 'currentPage') ? $executions->currentPage() : 1; $lastPage = method_exists($executions, 'lastPage') ? $executions->lastPage() : 1; + $server = $backup->server(); @endphp
@@ -82,12 +83,13 @@ - + @if ($execution->status === 'running') Running for {{ calculateDuration($execution->created_at, now()) }} @else - {{ $finishedAt->diffForHumans() }}
- {{ calculateDuration($execution->created_at, $finishedAt) }} + {{ formatDateInServerTimezone($finishedAt, $server) }} + {{ $finishedAt->diffForHumans() }} ยท {{ calculateDuration($execution->created_at, $finishedAt) }} @endif
diff --git a/tests/Feature/BackupExecutionTimestampsTest.php b/tests/Feature/BackupExecutionTimestampsTest.php new file mode 100644 index 0000000000..87b594e3b5 --- /dev/null +++ b/tests/Feature/BackupExecutionTimestampsTest.php @@ -0,0 +1,80 @@ + 'file']); + Server::flushIdentityMap(); + InstanceSettings::unguarded(fn () => InstanceSettings::firstOrCreate(['id' => 0])); + + $team = Team::factory()->create(); + $user = User::factory()->create(); + $user->teams()->attach($team, ['role' => 'owner']); + + $server = Server::factory()->create(['team_id' => $team->id]); + $server->settings->update(['server_timezone' => 'Europe/Budapest']); + $destination = StandaloneDocker::where('server_id', $server->id)->firstOrFail(); + $project = Project::factory()->create(['team_id' => $team->id]); + $environment = Environment::factory()->create(['project_id' => $project->id]); + $this->service = Service::factory()->create([ + 'server_id' => $server->id, + 'destination_id' => $destination->id, + 'destination_type' => $destination->getMorphClass(), + 'environment_id' => $environment->id, + ]); + $database = ServiceDatabase::create([ + 'service_id' => $this->service->id, + 'name' => 'timestamps-db', + 'image' => 'postgres:16-alpine', + 'custom_type' => 'postgresql', + ]); + $this->backup = ScheduledDatabaseBackup::create([ + 'team_id' => $team->id, + 'frequency' => '0 0 * * *', + 'database_id' => $database->id, + 'database_type' => $database->getMorphClass(), + ]); + + $this->travelTo(Carbon::parse('2026-10-16 14:00:00', 'UTC')); + ScheduledDatabaseBackupExecution::create([ + 'scheduled_database_backup_id' => $this->backup->id, + 'status' => 'success', + 'filename' => '/data/coolify/backups/timestamps.dmp', + 'finished_at' => Carbon::parse('2026-10-16 14:30:05', 'UTC'), + ]); + $this->travelTo(Carbon::parse('2026-10-16 18:00:00', 'UTC')); + + $this->actingAs($user); + session(['currentTeam' => $team]); +}); + +it('shows the exact finish time of database backup executions in the server timezone', function () { + Livewire::test(DatabaseBackupExecutions::class, ['backup' => $this->backup]) + ->call('refreshBackupExecutions') + ->assertSee('2026-10-16 16:30:05 CEST') + ->assertSee('Started: 2026-10-16 16:00:00 CEST', false) + ->assertSee('3 hours ago'); +}); + +it('shows the exact start time of service backup executions in the server timezone', function () { + Livewire::test(ServiceBackupExecutions::class, ['service' => $this->service]) + ->assertSee('2026-10-16 16:00:00 CEST') + ->assertSee('4 hours ago'); +}); diff --git a/tests/Feature/VolumeBackupTest.php b/tests/Feature/VolumeBackupTest.php index 3eebee1e34..02b8676e11 100644 --- a/tests/Feature/VolumeBackupTest.php +++ b/tests/Feature/VolumeBackupTest.php @@ -749,16 +749,21 @@ it('renders volume backup executions like database backup executions', function 'team_id' => $team->id, 'frequency' => 'daily', ]); + $this->travelTo(Carbon::parse('2026-10-16 14:00:00', 'UTC')); ScheduledVolumeBackupExecution::create([ 'scheduled_volume_backup_id' => $backup->id, 'status' => 'success', 'filename' => '/data/coolify/backups/volumes/test/archive.tar.gz', 'size' => 1024, - 'finished_at' => now(), + 'finished_at' => Carbon::parse('2026-10-16 14:30:05', 'UTC'), ]); + $this->travelTo(Carbon::parse('2026-10-16 18:00:00', 'UTC')); Livewire::test(VolumeBackups::class, ['storage' => $volume, 'resource' => $application, 'section' => 'executions']) ->assertSet('timezone', 'Europe/Budapest') + ->assertSee('2026-10-16 16:30:05 CEST') + ->assertSee('Started: 2026-10-16 16:00:00 CEST', false) + ->assertSee('3 hours ago') ->assertSeeInOrder([ 'Executions', 'Status',