backupPath = storage_path('backups'); // Create backups directory jika belum ada if (!File::isDirectory($this->backupPath)) { File::makeDirectory($this->backupPath, 0755, true); } } public function index(Request $request) { $backups = []; if (File::isDirectory($this->backupPath)) { $files = File::files($this->backupPath); foreach ($files as $file) { $modifiedAt = Carbon::createFromTimestamp($file->getMTime(), config('app.timezone')); $backups[] = [ 'name' => $file->getFilename(), 'size' => $file->getSize(), 'date' => $modifiedAt->timestamp, 'date_display' => $modifiedAt->format('d M Y H:i'), 'path' => $file->getRealPath(), ]; } } // Sort by date descending usort($backups, function($a, $b) { return $b['date'] <=> $a['date']; }); return view('admin.backup.index', compact('backups')); } public function create(Request $request) { try { $exitCode = Artisan::call('database:backup'); if ($exitCode === 0) { $output = trim(Artisan::output()); return redirect()->back()->with('success', $output !== '' ? $output : 'Backup berhasil dibuat'); } $output = trim(Artisan::output()); return redirect()->back()->with('error', $output !== '' ? $output : 'Gagal membuat backup'); } catch (Exception $e) { return redirect()->back()->with('error', 'Error: ' . $e->getMessage()); } } public function download($backup) { // Sanitize: prevent path traversal $backup = basename($backup); $backupPath = $this->backupPath . '/' . $backup; if (!File::exists($backupPath)) { return redirect()->back()->with('error', 'File backup tidak ditemukan'); } return response()->download($backupPath); } public function delete($backup) { try { // Sanitize: prevent path traversal $backup = basename($backup); $backupPath = $this->backupPath . '/' . $backup; if (!File::exists($backupPath)) { return redirect()->back()->with('error', 'File backup tidak ditemukan'); } File::delete($backupPath); return redirect()->back()->with('success', 'Backup berhasil dihapus'); } catch (Exception $e) { return redirect()->back()->with('error', 'Error: ' . $e->getMessage()); } } public function restore(Request $request, $backup) { try { // Sanitize: prevent path traversal $backup = basename($backup); $backupPath = $this->backupPath . '/' . $backup; if (!File::exists($backupPath)) { return redirect()->back()->with('error', 'File backup tidak ditemukan'); } // Extract zip jika zip file if (pathinfo($backupPath, PATHINFO_EXTENSION) === 'zip') { $extractPath = $this->backupPath . '/temp_' . time(); $zip = new ZipArchive(); if ($zip->open($backupPath) === true) { $zip->extractTo($extractPath); $zip->close(); $sqlFile = $extractPath . '/database.sql'; if (!File::exists($sqlFile)) { File::deleteDirectory($extractPath); return redirect()->back()->with('error', 'File SQL tidak ditemukan dalam backup'); } } else { return redirect()->back()->with('error', 'Gagal membuka file zip'); } } else { $sqlFile = $backupPath; } // Restore database (use config() instead of env() for config:cache compatibility) $database = config('database.connections.mysql.database'); $username = config('database.connections.mysql.username'); $password = config('database.connections.mysql.password'); $host = config('database.connections.mysql.host'); $command = sprintf( 'mysql --user=%s --password=%s --host=%s %s < "%s"', escapeshellarg($username), escapeshellarg($password), escapeshellarg($host), escapeshellarg($database), $sqlFile ); $output = null; $exitCode = null; exec($command, $output, $exitCode); // Cleanup temp files if (isset($extractPath) && File::isDirectory($extractPath)) { File::deleteDirectory($extractPath); } if ($exitCode === 0) { return redirect()->back()->with('success', 'Database berhasil di-restore'); } else { return redirect()->back()->with('error', 'Gagal restore database'); } } catch (Exception $e) { return redirect()->back()->with('error', 'Error: ' . $e->getMessage()); } } }