getExtension() === 'gz') { $backups[] = [ 'name' => $file->getFilename(), 'size' => round($file->getSize() / 1024 / 1024, 2) . ' MB', 'date' => date('Y-m-d H:i:s', $file->getMTime()), ]; } } // Sort by newest first usort($backups, fn($a, $b) => $b['date'] <=> $a['date']); return $backups; } /** * Trigger a new site backup process. * * @return bool True if backup was successful. */ public function create(): bool { return Artisan::call('sw:site:backup') === 0; } /** * Create a pre-restore snapshot of the current state. * * @return string The filename of the created snapshot. * @throws Exception If snapshot creation fails. */ public function createPreRestoreSnapshot(): string { $timestamp = date('Y-m-d_H-i-s'); $snapshotName = "pre-restore-snapshot_{$timestamp}.gz"; // We use the same artisan command logic but with a specific name if possible, // or rename the latest backup. Since sw:site:backup doesn't take a name, // we'll run it and then identify/rename if needed, but for KISS, // let's just run it and it will have its own timestamped name. if (Artisan::call('sw:site:backup') !== 0) { throw new Exception('Failed to create pre-restore snapshot.'); } // The sw:site:backup command creates a file like siteweaver_backup_Y-m-d_H-i-s.gz // Let's find the most recent one. $backups = $this->getBackups(); return $backups[0]['name']; } /** * Restore the site from a specific backup file. * * @param string $filename The name of the backup file to restore. * @param bool $createSnapshot Whether to create a pre-restore snapshot. * @return bool True if restore was successful. * @throws Exception If the backup file is missing or snapshot fails. */ public function restore(string $filename, bool $createSnapshot = true): bool { $backupPath = storage_path('app/backups/' . $filename); if (!File::exists($backupPath)) { throw new Exception('Backup file not found.'); } if ($createSnapshot) { $this->createPreRestoreSnapshot(); } return Artisan::call('sw:site:restore', [ 'filename' => $filename, '--force' => true, ]) === 0; } /** * Upload a backup file to the backup directory. * * @param UploadedFile $file The uploaded .gz backup file. * @return bool True if upload was successful. */ public function upload(UploadedFile $file): bool { $filename = $file->getClientOriginalName(); // Basic sanitization of filename $filename = preg_replace('/[^a-zA-Z0-9.\-_]/', '_', $filename); if (!str_ends_with($filename, '.gz')) { $filename .= '.gz'; } $backupDir = storage_path('app/backups'); if (!File::exists($backupDir)) { File::makeDirectory($backupDir, 0755, true); } $file->move($backupDir, $filename); return true; } /** * Get the current restoration progress. * * @return array|null The progress data or null if not found. */ public function getProgress(): ?array { return \Illuminate\Support\Facades\Cache::get(\App\Console\Commands\SiteRestore::PROGRESS_KEY); } }