fix(cache): accept numbers that Redis returns as strings

The root setup window and the scanner disk usage checked is_int() on
cached values, but the Redis store returns numbers as strings. The
setup window was closed right after each start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-11-09 17:52:03 +01:00
co-authored by Claude Opus 5.5
parent b81cc4116d
commit aa016e685d
6 changed files with 27 additions and 5 deletions
@@ -0,0 +1,4 @@
---
type: fixed
---
Closed root account setup window right after a fresh install, so the first account could not be created on the `Register` page
@@ -0,0 +1,6 @@
---
type: fixed
---
Missing scanner disk usage on the `Image Scanning` page of a server and in the `scanner_disk_used_bytes` API field
- The `Delete scanner data` button also stays hidden now after the scanner data is deleted
@@ -27,7 +27,6 @@ class ServerImageScanningController extends Controller
$state = Cache::get(PrepareImageScannerJob::stateCacheKey($server));
$failedStep = Cache::get(PrepareImageScannerJob::failedStepCacheKey($server));
$builtAt = Cache::get(PrepareImageScannerJob::builtAtCacheKey($server));
$diskUsed = Cache::get(PrepareImageScannerJob::diskUsedCacheKey($server));
return [
'is_image_scan_enabled' => (bool) $server->settings->is_image_scan_enabled,
@@ -35,7 +34,7 @@ class ServerImageScanningController extends Controller
'scanner_state' => is_string($state) ? $state : null,
'scanner_failed_step' => is_string($failedStep) ? $failedStep : null,
'scanner_built_at' => is_string($builtAt) ? CarbonImmutable::parse($builtAt)->toIso8601String() : null,
'scanner_disk_used_bytes' => is_int($diskUsed) ? $diskUsed : null,
'scanner_disk_used_bytes' => PrepareImageScannerJob::diskUsedBytes($server),
];
}
+10
View File
@@ -86,6 +86,16 @@ class PrepareImageScannerJob implements ShouldBeEncrypted, ShouldBeUnique, Shoul
return self::stateCacheKey($server).':disk-used';
}
/**
* The Redis store returns numbers as strings, so the cached value is not always an int.
*/
public static function diskUsedBytes(Server $server): ?int
{
$bytes = Cache::get(self::diskUsedCacheKey($server));
return is_numeric($bytes) ? (int) $bytes : null;
}
public static function failedStepCacheKey(Server $server): string
{
return self::stateCacheKey($server).':failed-step';
@@ -154,7 +154,7 @@ class ServerImageScanning extends Component
$state = Cache::get(PrepareImageScannerJob::stateCacheKey($this->server));
$builtAt = Cache::get(PrepareImageScannerJob::builtAtCacheKey($this->server));
$failedStep = Cache::get(PrepareImageScannerJob::failedStepCacheKey($this->server));
$diskUsed = Cache::get(PrepareImageScannerJob::diskUsedCacheKey($this->server));
$diskUsed = PrepareImageScannerJob::diskUsedBytes($this->server);
return view('livewire.image-scan.server-image-scanning', [
'enabled' => (bool) $this->server->settings->is_image_scan_enabled,
@@ -169,7 +169,7 @@ class ServerImageScanning extends Component
->map(fn (array $image) => ImageRef::parse($image['repo'])->registry)
->unique()
->implode(', '),
'diskUsed' => is_int($diskUsed) ? formatBytes($diskUsed, 1) : null,
'diskUsed' => $diskUsed !== null ? formatBytes($diskUsed, 1) : null,
// Hide the delete action only when the server is known to be empty. Missing cache data can still mean data on disk.
'hasScannerData' => is_string($state) || $diskUsed !== 0,
'toolVersions' => collect(['syft' => 'Syft', 'grype' => 'Grype', 'regctl' => 'regctl'])
+4 -1
View File
@@ -28,11 +28,14 @@ class RootSetupWindow
Cache::forever(self::CACHE_KEY, now()->addMinutes(self::MINUTES)->getTimestamp());
}
/**
* The Redis store returns numbers as strings, so the value is not always an int.
*/
public static function isOpen(): bool
{
$closesAt = Cache::get(self::CACHE_KEY);
return is_int($closesAt) && now()->getTimestamp() < $closesAt;
return is_numeric($closesAt) && now()->getTimestamp() < (int) $closesAt;
}
public static function isPending(): bool