argument('file'); if (!is_file($file) || !is_readable($file)) { $this->error('File backup tidak ditemukan atau tidak bisa dibaca: ' . $file); return Command::FAILURE; } $baseUrl = rtrim((string) ($this->option('url') ?: config('cloud_backup.api_url')), '/'); $token = (string) ($this->option('token') ?: config('cloud_backup.api_token')); $backupName = (string) ($this->option('name') ?: config('cloud_backup.backup_name', 'lismikro')); $chunkSize = (int) ($this->option('chunk-size') ?: config('cloud_backup.chunk_size', 5 * 1024 * 1024)); if ($baseUrl === '' || $token === '') { $this->error('CLOUD_BACKUP_API_URL dan CLOUD_BACKUP_API_TOKEN wajib diisi.'); return Command::FAILURE; } if ($chunkSize < 1024 * 1024) { $this->error('Chunk size minimal 1 MB.'); return Command::FAILURE; } $size = filesize($file); $checksum = hash_file('sha256', $file); $totalChunks = (int) ceil($size / $chunkSize); $uploadId = now()->format('YmdHis') . '_' . Str::random(16); $endpoint = $baseUrl . '/api/backups/chunk'; $this->info('Upload backup: ' . basename($file)); $this->info('Total chunk: ' . $totalChunks); $handle = fopen($file, 'rb'); if (!$handle) { $this->error('Gagal membuka file backup.'); return Command::FAILURE; } try { for ($index = 0; $index < $totalChunks; $index++) { $chunk = fread($handle, $chunkSize); if ($chunk === false) { $this->error('Gagal membaca chunk ke-' . $index); return Command::FAILURE; } $response = Http::withToken($token) ->timeout(300) ->attach('chunk', $chunk, 'chunk_' . $index . '.part') ->post($endpoint, [ 'backup_name' => $backupName, 'upload_id' => $uploadId, 'filename' => basename($file), 'chunk_index' => $index, 'total_chunks' => $totalChunks, 'total_size' => $size, 'checksum' => $checksum, ]); if (!$response->successful()) { $this->error('Upload chunk ke-' . $index . ' gagal. HTTP ' . $response->status()); $this->line($response->body()); return Command::FAILURE; } $this->line('Chunk ' . ($index + 1) . '/' . $totalChunks . ' terkirim.'); } } finally { fclose($handle); } $this->info('Upload backup selesai.'); return Command::SUCCESS; } }