$message, 'percent' => $percentage, 'timestamp' => now()->timestamp, ], 300); // 5-minute TTL $this->info("[$percentage%] $message"); } /** * The name and signature of the console command. * * @var string */ protected $signature = 'sw:site:restore {filename} {--force}'; /** * The console command description. * * @var string */ protected $description = 'Restore the site from a backup archive.'; /** * Execute the console command. */ public function handle() { $filename = $this->argument('filename'); $backupPath = storage_path("app/backups/{$filename}"); if (!File::exists($backupPath)) { $this->error("Backup file not found: {$filename}"); return 1; } if ($this->option('force') || $this->confirm("Are you sure you want to restore from {$filename}? This will overwrite your current database and files.")) { $this->updateProgress("Starting restoration...", 5); $tempPath = storage_path('app/temp_restore'); if (File::exists($tempPath)) { File::deleteDirectory($tempPath); } mkdir($tempPath, 0755, true); // Extract archive $this->updateProgress("Extracting backup archive...", 20); $command = "tar -xzf " . escapeshellarg($backupPath) . " -C " . escapeshellarg($tempPath); exec($command, $output, $returnVar); if ($returnVar !== 0) { $this->updateProgress("Failed to extract backup.", 0); $this->error("Failed to extract backup."); File::deleteDirectory($tempPath); return 1; } // Database restoration $this->updateProgress("Restoring database...", 40); $dbPath = config('database.connections.sqlite.database'); $dbFilename = basename($dbPath); // Skip restoration if database is in memory (tests) if ($dbPath === ':memory:') { $this->warn('Skipping database restoration because it is in-memory.'); // We'll still progress } else { // Look for database file in temp path. It should now be in the root of the temp path. $extractedDbFile = $tempPath . '/' . $dbFilename; if (File::exists($extractedDbFile)) { File::copy($extractedDbFile, $dbPath); } else { // Compatibility for old absolute path backups (tar stripping leading slash) $oldPathDb = $tempPath . ltrim($dbPath, '/'); if (File::exists($oldPathDb)) { File::copy($oldPathDb, $dbPath); } else { $this->warn("Database file ($dbFilename) not found in backup."); } } } // Media restoration $this->updateProgress("Restoring media files...", 60); $extractedMedia = $tempPath . '/media'; if (File::exists($extractedMedia)) { $targetMedia = storage_path('app/public/media'); if (File::exists($targetMedia)) { File::deleteDirectory($targetMedia); } File::moveDirectory($extractedMedia, $targetMedia); } // Themes restoration $this->updateProgress("Restoring themes...", 80); $extractedThemes = $tempPath . '/themes'; if (File::exists($extractedThemes)) { $targetThemes = base_path('themes'); if (File::exists($targetThemes)) { File::deleteDirectory($targetThemes); } File::moveDirectory($extractedThemes, $targetThemes); } $this->updateProgress("Cleaning up...", 95); File::deleteDirectory($tempPath); $this->updateProgress("Restoration completed successfully.", 100); return 0; } $this->info("Restoration cancelled."); return 0; } }