error("No exports directory found at {$exportDir}"); return; } $zips = glob($exportDir . "/export_*.zip"); if (empty($zips)) { $this->error("No export archives found in {$exportDir}"); return; } rsort($zips); $choices = array_map(fn($p) => basename($p), $zips); $chosen = $this->choice("Select an export to import", $choices, 0); $zipFile = $exportDir . "/" . $chosen; $this->warn("This will REPLACE all records, revisions, edges, files data and uploaded files."); if (!$this->confirm("Import {$chosen}?", false)) { $this->info("Aborted."); return; } // Extract to temp directory $tempDir = storage_path("exports/.import_tmp_" . uniqid()); mkdir($tempDir, 0755, true); $zip = new ZipArchive(); if ($zip->open($zipFile) !== true) { $this->error("Could not open zip archive."); $this->cleanup($tempDir); return; } $zip->extractTo($tempDir); $zip->close(); // Restore database $sqlFiles = glob($tempDir . "/*.sql"); if (empty($sqlFiles)) { $this->error("No SQL dump found inside the archive."); $this->cleanup($tempDir); return; } $db = config("database.connections.pgsql"); $tables = [ "lucent_records", "lucent_revisions", "lucent_files", "lucent_edges", ]; // Truncate existing tables before restore $truncate = collect($tables) ->map(fn($t) => "TRUNCATE TABLE {$t} CASCADE;") ->join(" "); $truncateCmd = sprintf( "PGPASSWORD=%s psql -h %s -p %s -U %s -d %s -c \"%s\"", $db["password"], $db["host"], $db["port"], $db["username"], $db["database"], $truncate, ); exec($truncateCmd, result_code: $truncateCode); if ($truncateCode !== 0) { $this->error("Failed to truncate existing tables."); $this->cleanup($tempDir); return; } $restoreCmd = sprintf( "PGPASSWORD=%s psql -h %s -p %s -U %s -d %s -f %s", $db["password"], $db["host"], $db["port"], $db["username"], $db["database"], $sqlFiles[0], ); exec($restoreCmd, result_code: $restoreCode); if ($restoreCode !== 0) { $this->error("Database restore failed."); $this->cleanup($tempDir); return; } $this->info("Database restored."); // Replace files $srcFilesDir = $tempDir . "/files"; if (is_dir($srcFilesDir)) { $disk = Storage::disk(config("lucent.disk")); $destFilesDir = $disk->path("files"); // Remove existing files directory if (is_dir($destFilesDir)) { exec("rm -rf " . escapeshellarg($destFilesDir)); } exec( sprintf("cp -R %s %s", escapeshellarg($srcFilesDir), escapeshellarg($destFilesDir)), result_code: $copyCode, ); if ($copyCode !== 0) { $this->error("Failed to restore files."); $this->cleanup($tempDir); return; } $this->info("Files restored."); } else { $this->warn("No files directory found in archive — skipping file restore."); } $this->cleanup($tempDir); $this->info("Import complete."); } private function cleanup(string $dir): void { if (is_dir($dir)) { exec("rm -rf " . escapeshellarg($dir)); } } }