environment("local")) { $this->error( "This command can only be run in the local environment.", ); return; } $localDisk = Storage::disk("local"); $exports = array_values(array_filter( $localDisk->files("exports"), fn($f) => fnmatch("exports/export_*.zip", $f), )); if (empty($exports)) { $this->error("No export files found."); return; } rsort($exports); $choices = array_map(fn($f) => basename($f), $exports); $chosen = $this->choice("Select an export file to import:", $choices, 0); $zipFile = $localDisk->path("exports/" . $chosen); $db = config("database.connections.pgsql"); $publicDisk = Storage::disk("public"); $tempPath = "boboko_import_" . uniqid(); $result = $this->extractZip($zipFile, $localDisk, $tempPath) ->flatMap(fn($path) => $this->restoreDatabase($db, $localDisk, $path)) ->flatMap(fn($path) => $this->restoreFiles($localDisk, $path, $publicDisk)); $localDisk->deleteDirectory($tempPath); if ($result->error()->isDefined()) { $this->error($result->error()->get()); return; } $this->info("Import complete."); $this->call("boboko:anonymize"); } /** @return Result */ private function extractZip( string $zipFile, Filesystem $localDisk, string $tempPath, ): Result { $zip = new ZipArchive(); if ($zip->open($zipFile) !== true) { return Error::create("Could not open zip archive."); } $localDisk->makeDirectory($tempPath); $zip->extractTo($localDisk->path($tempPath)); $zip->close(); $this->info("Archive extracted."); return Success::create($tempPath); } /** @return Result */ private function restoreDatabase( array $db, Filesystem $localDisk, string $tempPath, ): Result { $sqlFiles = array_values(array_filter( $localDisk->files($tempPath), fn($f) => fnmatch("dump_*.sql", basename($f)), )); if (empty($sqlFiles)) { return Error::create("No SQL dump found in archive."); } $dropCommand = sprintf( "PGPASSWORD=%s psql -h %s -p %s -U %s -d %s -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'", $db["password"], $db["host"], $db["port"], $db["username"], $db["database"], ); exec($dropCommand, result_code: $dropCode); if ($dropCode !== 0) { return Error::create("Failed to reset database schema."); } $command = sprintf( "PGPASSWORD=%s psql -h %s -p %s -U %s -d %s < %s", $db["password"], $db["host"], $db["port"], $db["username"], $db["database"], $localDisk->path($sqlFiles[0]), ); exec($command, result_code: $code); if ($code !== 0) { return Error::create("psql restore failed."); } $this->info("Database restored."); return Success::create($tempPath); } /** @return Result */ private function restoreFiles( Filesystem $localDisk, string $tempPath, Filesystem $publicDisk, ): Result { $filesSubPath = $tempPath . "/files"; if (!$localDisk->exists($filesSubPath)) { $this->info("No files to restore."); return Success::create($tempPath); } foreach ($publicDisk->allFiles() as $file) { $publicDisk->delete($file); } foreach ($publicDisk->directories() as $dir) { $publicDisk->deleteDirectory($dir); } $tempDisk = Storage::build([ "driver" => "local", "root" => $localDisk->path($filesSubPath), ]); foreach ($tempDisk->allFiles() as $relativePath) { $publicDisk->put($relativePath, $tempDisk->readStream($relativePath)); } $this->info("Files restored."); return Success::create($tempPath); } }