user(); if (!$user || !$user->roles()->where('name', 'ROLE_ADMIN')->exists()) { abort(403, 'Forbidden'); } try { $storagePublic = storage_path('app/public'); if (!is_dir($storagePublic)) { mkdir($storagePublic, 0775, true); } // Create ZIP in memory $zip = new ZipArchive(); $tempFile = tempnam(sys_get_temp_dir(), 'speedbb_'); if ($zip->open($tempFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { return response()->json(['message' => 'Failed to create ZIP'], 500); } // Add a manifest file to ensure ZIP is created even if empty $zip->addFromString('MANIFEST.txt', "SpeedBB Assets Export\nExported: " . date('Y-m-d H:i:s') . "\n"); $this->addDirectoryToZip($zip, $storagePublic, ''); $zip->close(); if (!file_exists($tempFile)) { return response()->json(['message' => 'ZIP file was not created'], 500); } $filename = 'assets-'.date('YmdHis').'.zip'; return response()->file($tempFile, [ 'Content-Type' => 'application/zip', 'Content-Disposition' => 'attachment; filename="'.$filename.'"', ])->deleteFileAfterSend(true); } catch (\Exception $e) { return response()->json(['message' => 'Export error: '.$e->getMessage()], 500); } } public function import(Request $request): JsonResponse { $user = $request->user(); if (!$user || !$user->roles()->where('name', 'ROLE_ADMIN')->exists()) { return response()->json(['message' => 'Forbidden'], 403); } $data = $request->validate([ 'file' => ['required', 'file', 'mimes:zip', 'max:104857600'], ]); $file = $data['file']; $zip = new ZipArchive(); $zipPath = $file->getPathname(); if ($zip->open($zipPath) !== true) { return response()->json(['message' => 'Invalid zip file'], 400); } $storagePublic = storage_path('app/public'); for ($i = 0; $i < $zip->numFiles; $i++) { $filename = $zip->getNameIndex($i); $fileinfo = $zip->statIndex($i); if ($fileinfo['crc'] == 0) { continue; } $targetPath = $storagePublic.DIRECTORY_SEPARATOR.$filename; $targetDir = dirname($targetPath); if (!is_dir($targetDir)) { mkdir($targetDir, 0775, true); } copy('zip://'.$zipPath.'#'.$filename, $targetPath); } $zip->close(); return response()->json([ 'message' => 'Assets imported successfully', ]); } private function addDirectoryToZip(ZipArchive $zip, string $dir, string $zipPath): void { if (!is_dir($dir)) { return; } $files = @scandir($dir); if ($files === false) { return; } foreach ($files as $file) { if ($file === '.' || $file === '..') { continue; } $path = $dir.DIRECTORY_SEPARATOR.$file; $newZipPath = $zipPath === '' ? $file : $zipPath.'/'.$file; if (is_dir($path)) { $zip->addEmptyDir($newZipPath); $this->addDirectoryToZip($zip, $path, $newZipPath); } elseif (is_file($path)) { $zip->addFile($path, $newZipPath); } } } }