$db['password']]); $process->setTimeout(120); $process->run(); if (!$process->isSuccessful()) { ActivityLog::record('backup', 'Gagal membuat backup database'); return response()->json(['message' => 'Backup gagal: ' . $process->getErrorOutput()], 500); } ActivityLog::record('backup', 'Mengunduh backup database'); $filename = 'backup-webgis-' . now()->format('Ymd-His') . '.sql'; return response($process->getOutput(), 200, [ 'Content-Type' => 'application/sql', 'Content-Disposition' => "attachment; filename=\"{$filename}\"", ]); } public function listBackups() { $dir = storage_path('app/backups'); $files = is_dir($dir) ? glob($dir . '/backup-webgis-*.sql') : []; usort($files, fn($a, $b) => filemtime($b) - filemtime($a)); $list = array_map(fn($f) => [ 'name' => basename($f), 'size_kb' => round(filesize($f) / 1024, 1), 'created_at' => date('d/m/Y H:i', filemtime($f)), ], $files); return response()->json($list); } public function downloadStored(string $filename) { if (!preg_match('/^backup-webgis-[\d-]+\.sql$/', $filename)) { abort(400, 'Nama file tidak valid'); } $path = storage_path('app/backups/' . $filename); if (!file_exists($path)) abort(404); return response()->download($path); } public function restore(\Illuminate\Http\Request $request) { $request->validate([ 'file' => 'required|file|mimes:sql,txt|max:51200', 'confirm' => 'required|in:RESTORE', ]); $db = config('database.connections.mysql'); $tmpPath = $request->file('file')->getRealPath(); $process = new Process([ 'mysql', '-h', $db['host'], '-P', (string) $db['port'], '-u', $db['username'], $db['database'], ], null, ['MYSQL_PWD' => $db['password']]); $process->setInput(file_get_contents($tmpPath)); $process->setTimeout(300); $process->run(); if (!$process->isSuccessful()) { ActivityLog::record('restore', 'Restore database GAGAL: ' . $process->getErrorOutput()); return response()->json(['message' => 'Restore gagal: ' . $process->getErrorOutput()], 500); } ActivityLog::record('restore', 'Restore database berhasil dari file yang diunggah'); return response()->json(['success' => true]); } }