*/ public array $pendingDownloads = []; /** * Authorize the user and load the backup from a record scoped to the * component before you call this. Pass a null server or file name when * the local file is gone; the download route then answers the request. */ protected function prepareBackupDownload(?Server $server, ?string $filename, string $streamUrl): void { $storage = FileDownloadStorageService::configuredStorage(); $size = $storage && $server && filled($filename) ? FileDownloadStorageService::hostFileSize($server, $filename) : null; if ($size === null || $size > FileDownloadStorageService::MAX_SIZE) { $this->dispatch('open-backup-download', url: $streamUrl); return; } if (count($this->pendingDownloads) >= self::MAX_PENDING_S3_DOWNLOADS || ! RateLimiter::attempt('file-browser-s3-download:'.auth()->id(), 10, fn () => true, 3600)) { $this->dispatch('error', 'Too many downloads are in progress. Wait for them to finish, then try again.'); return; } $downloadId = (string) Str::uuid(); $this->pendingDownloads[] = $downloadId; PrepareFileDownloadJob::dispatch($downloadId, auth()->id(), $server->id, null, $filename, false, basename($filename), $storage->id); $this->dispatch('info', 'Preparing '.basename($filename).'. The download starts when it is ready.'); } /** * Polled while S3 downloads are prepared. Opens each finished link once. */ public function checkDownloads(): void { foreach ($this->pendingDownloads as $index => $downloadId) { $result = Cache::get(PrepareFileDownloadJob::cacheKey($downloadId)); if (! is_array($result) || ($result['user_id'] ?? null) !== auth()->id()) { continue; } unset($this->pendingDownloads[$index]); Cache::forget(PrepareFileDownloadJob::cacheKey($downloadId)); if ($result['status'] === 'ready') { $this->dispatch('open-backup-download', url: $result['url']); } else { $this->dispatch('error', $result['message']); } } $this->pendingDownloads = array_values($this->pendingDownloads); } protected function s3BackupDownloadsEnabled(): bool { return FileDownloadStorageService::configuredStorage() !== null; } }