]> BookStack Code Mirror - system-cli/blobdiff - src/Commands/RestoreCommand.php
Range of changes to MySQL execution
[system-cli] / src / Commands / RestoreCommand.php
index f46d571d23cb06aa9a50c43efa66dd02354d7fbe..4bbdbc8e11fc4cbd686407389f09320b13f99d1e 100644 (file)
@@ -5,14 +5,14 @@ namespace Cli\Commands;
 use Cli\Services\AppLocator;
 use Cli\Services\ArtisanRunner;
 use Cli\Services\BackupZip;
+use Cli\Services\Directories;
 use Cli\Services\EnvironmentLoader;
 use Cli\Services\InteractiveConsole;
 use Cli\Services\MySqlRunner;
+use Cli\Services\Paths;
 use Cli\Services\ProgramRunner;
 use Cli\Services\RequirementsValidator;
 use Exception;
-use RecursiveDirectoryIterator;
-use RecursiveIteratorIterator;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputInterface;
@@ -48,7 +48,13 @@ class RestoreCommand extends Command
         RequirementsValidator::validate();
         (new ProgramRunner('mysql', '/usr/bin/mysql'))->ensureFound();
 
-        $zipPath = realpath($input->getArgument('backup-zip'));
+        $providedZipPath = $input->getArgument('backup-zip');
+        $zipPath = realpath($providedZipPath);
+        if (!$zipPath || !file_exists($zipPath)) {
+            $pathToDisplay = $zipPath ?: $providedZipPath;
+            throw new CommandError("Could not find ZIP file for restoration at provided path [{$pathToDisplay}].");
+        }
+
         $zip = new BackupZip($zipPath);
         $contents = $zip->getContentsOverview();
 
@@ -74,7 +80,7 @@ class RestoreCommand extends Command
         }
 
         $output->writeln("<info>Extracting ZIP into temporary directory...</info>");
-        $extractDir = $appDir . DIRECTORY_SEPARATOR . 'restore-temp-' . time();
+        $extractDir = Paths::join($appDir, 'restore-temp-' . time());
         if (!mkdir($extractDir)) {
             throw new CommandError("Could not create temporary extraction directory at [{$extractDir}].");
         }
@@ -104,9 +110,9 @@ class RestoreCommand extends Command
         }
 
         if ($envChanges && $envChanges['old_url'] !== $envChanges['new_url']) {
-            $output->writeln("<info>App URL change made, Updating database with URL change...</info>");
+            $output->writeln("<info>App URL change made, updating database with URL change...</info>");
             $artisan->run([
-                'bookstack:update-url',
+                'bookstack:update-url', '--force',
                 $envChanges['old_url'], $envChanges['new_url'],
             ]);
         }
@@ -117,7 +123,7 @@ class RestoreCommand extends Command
         $artisan->run(['view:clear']);
 
         $output->writeln("<info>Cleaning up extract directory...</info>");
-        $this->deleteDirectoryAndContents($extractDir);
+        Directories::delete($extractDir);
 
         $output->writeln("<success>\nRestore operation complete!</success>");
         $output->writeln("<info>You may need to fix file/folder permissions so that the webserver has</info>");
@@ -128,10 +134,10 @@ class RestoreCommand extends Command
 
     protected function restoreEnv(string $extractDir, string $appDir, OutputInterface $output, InteractiveConsole $interactions): array
     {
-        $oldEnv = EnvironmentLoader::load($extractDir);
+        $oldEnv = EnvironmentLoader::loadMergedWithCurrentEnv($extractDir);
         $currentEnv = EnvironmentLoader::load($appDir);
-        $envContents = file_get_contents($extractDir . DIRECTORY_SEPARATOR . '.env');
-        $appEnvPath = $appDir . DIRECTORY_SEPARATOR . '.env';
+        $envContents = file_get_contents(Paths::join($extractDir, '.env'));
+        $appEnvPath = Paths::real(Paths::join($appDir, '.env'));
 
         $mysqlCurrent = MySqlRunner::fromEnvOptions($currentEnv);
         $mysqlOld = MySqlRunner::fromEnvOptions($oldEnv);
@@ -146,7 +152,7 @@ class RestoreCommand extends Command
             $currentEnvDbLines = array_values(array_filter(explode("\n", $currentEnvContents), function (string $line) {
                 return str_starts_with($line, 'DB_');
             }));
-            $oldEnvLines = array_values(array_filter(explode("\n", $currentEnvContents), function (string $line) {
+            $oldEnvLines = array_values(array_filter(explode("\n", $envContents), function (string $line) {
                 return !str_starts_with($line, 'DB_');
             }));
             $envContents = implode("\n", [
@@ -165,43 +171,28 @@ class RestoreCommand extends Command
         ];
 
         if ($oldUrl !== $newUrl) {
-            $output->writeln("Found different APP_URL values:");
-            $changedUrl = $interactions->choice('Which would you like to use?', array_filter([$oldUrl, $newUrl]));
-            $envContents = preg_replace('/^APP_URL=.*?$/', 'APP_URL="' . $changedUrl . '"', $envContents);
+            $question = 'Found different APP_URL values, which would you like to use?';
+            $changedUrl = $interactions->choice($question, array_filter([$oldUrl, $newUrl]));
+            $envContents = preg_replace('/^APP_URL=.*?$/m', 'APP_URL="' . $changedUrl . '"', $envContents);
             $returnData['new_url'] = $changedUrl;
         }
 
-        file_put_contents($appDir . DIRECTORY_SEPARATOR . '.env', $envContents);
+        file_put_contents($appEnvPath, $envContents);
 
         return $returnData;
     }
 
     protected function restoreFolder(string $folderSubPath, string $appDir, string $extractDir): void
     {
-        $fullAppFolderPath = $appDir . DIRECTORY_SEPARATOR . $folderSubPath;
-        $this->deleteDirectoryAndContents($fullAppFolderPath);
-        rename($extractDir . DIRECTORY_SEPARATOR . $folderSubPath, $fullAppFolderPath);
-    }
-
-    protected function deleteDirectoryAndContents(string $dir): void
-    {
-        $files = new RecursiveIteratorIterator(
-            new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
-            RecursiveIteratorIterator::CHILD_FIRST
-        );
-
-        foreach ($files as $fileinfo) {
-            $path = $fileinfo->getRealPath();
-            $fileinfo->isDir() ? rmdir($path) : unlink($path);
-        }
-
-        rmdir($dir);
+        $fullAppFolderPath = Paths::real(Paths::join($appDir, $folderSubPath));
+        Directories::delete($fullAppFolderPath);
+        Directories::move(Paths::join($extractDir, $folderSubPath), $fullAppFolderPath);
     }
 
     protected function restoreDatabase(string $appDir, string $extractDir): void
     {
-        $dbDump = $extractDir . DIRECTORY_SEPARATOR . 'db.sql';
-        $currentEnv = EnvironmentLoader::load($appDir);
+        $dbDump = Paths::join($extractDir, 'db.sql');
+        $currentEnv = EnvironmentLoader::loadMergedWithCurrentEnv($appDir);
         $mysql = MySqlRunner::fromEnvOptions($currentEnv);
 
         // Drop existing tables