From: Dan Brown Date: Fri, 3 Mar 2023 17:22:24 +0000 (+0000) Subject: Split command out to methods, added flags and out path options X-Git-Url: http://source.bookstackapp.com/system-cli/commitdiff_plain/eef0f6f182f5c73f51ca5920f5151771af80c249 Split command out to methods, added flags and out path options --- diff --git a/scripts/Commands/BackupCommand.php b/scripts/Commands/BackupCommand.php index 2b30385..cf2ca35 100644 --- a/scripts/Commands/BackupCommand.php +++ b/scripts/Commands/BackupCommand.php @@ -4,6 +4,7 @@ namespace Cli\Commands; use Minicli\Command\CommandCall; use RecursiveDirectoryIterator; +use SplFileInfo; use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\Process; use ZipArchive; @@ -17,14 +18,101 @@ final class BackupCommand public function handle(CommandCall $input) { - // TODO - Customizable output file - // TODO - Database only command + $handleDatabase = !$input->hasFlag('no-database'); + $handleUploads = !$input->hasFlag('no-uploads'); + $suggestedOutPath = $input->subcommand ?: ''; + // TODO - Validate DB vars + // TODO - Backup themes directory, extra flag for no-themes + // TODO - Backup the running phar? For easier direct restore... // TODO - Error handle each stage // TODO - Validate zip (and any other extensions required) are active. - $zipOutFile = getcwd() . DIRECTORY_SEPARATOR . 'backup.zip'; + $zipOutFile = $this->buildZipFilePath($suggestedOutPath); + $dumpTempFile = $this->createDatabaseDump(); + + // Create a new ZIP file + $zipTempFile = tempnam(sys_get_temp_dir(), 'bsbackup'); + $zip = new ZipArchive(); + $zip->open($zipTempFile, ZipArchive::CREATE); + $zip->addFile($this->appDir . DIRECTORY_SEPARATOR . '.env', '.env'); + + if ($handleDatabase) { + $zip->addFile($dumpTempFile, 'db.sql'); + } + + if ($handleUploads) { + $this->addUploadFoldersToZip($zip); + } + + // Close off our zip and move it to the required location + $zip->close(); + rename($zipTempFile, $zipOutFile); + + // Delete our temporary DB dump file + unlink($dumpTempFile); + + // Announce end and display errors + echo "Finished"; + } + + /** + * Build a full zip path from the given suggestion, which may be empty, + * a path to a folder, or a path to a file in relative or absolute form. + */ + protected function buildZipFilePath(string $suggestedOutPath): string + { + $zipDir = getcwd() ?: $this->appDir; + $zipName = "bookstack-backup-" . date('Y-m-d-His') . '.zip'; + + if ($suggestedOutPath) { + if (is_dir($suggestedOutPath)) { + $zipDir = realpath($suggestedOutPath); + } else if (is_dir(dirname($suggestedOutPath))) { + $zipDir = realpath(dirname($suggestedOutPath)); + $zipName = basename($suggestedOutPath); + } else { + // TODO - Handle not found output + } + } + + $fullPath = $zipDir . DIRECTORY_SEPARATOR . $zipName; + if (file_exists($fullPath)) { + // TODO + } + + return $fullPath; + } + + /** + * Add app-relative upload folders to the provided zip archive. + * Will recursively go through all directories to add all files. + */ + protected function addUploadFoldersToZip(ZipArchive $zip): void + { + $fileDirs = [ + $this->appDir . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'uploads' => 'public/uploads', + $this->appDir . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'uploads' => 'storage/uploads', + ]; + + foreach ($fileDirs as $fullFileDir => $relativeFileDir) { + $dirIter = new RecursiveDirectoryIterator($fullFileDir); + $fileIter = new \RecursiveIteratorIterator($dirIter); + /** @var SplFileInfo $file */ + foreach ($fileIter as $file) { + if (!$file->isDir()) { + $zip->addFile($file->getPathname(), $relativeFileDir . '/' . $fileIter->getSubPathname()); + } + } + } + } + + /** + * Create a database dump and return the path to the dumped SQL output. + */ + protected function createDatabaseDump(): string + { $dbHost = ($_SERVER['DB_HOST'] ?? ''); $dbUser = ($_SERVER['DB_USERNAME'] ?? ''); $dbPass = ($_SERVER['DB_PASSWORD'] ?? ''); @@ -57,42 +145,7 @@ final class BackupCommand } fclose($dumpTempFileResource); - - // Create a new ZIP file - $zipTempFile = tempnam(sys_get_temp_dir(), 'bsbackup'); - $zip = new ZipArchive(); - $sep = DIRECTORY_SEPARATOR; - $zip->open($zipTempFile, ZipArchive::CREATE); - $zip->addFile($this->appDir . $sep . '.env', '.env'); - $zip->addFile($dumpTempFile, 'db.sql'); - - $fileDirs = [ - $this->appDir . $sep . 'public' . $sep . 'uploads' => 'public/uploads', - $this->appDir . $sep . 'storage' . $sep . 'uploads' => 'storage/uploads', - ]; - - foreach ($fileDirs as $fullFileDir => $relativeFileDir) { - $dirIter = new RecursiveDirectoryIterator($fullFileDir); - $fileIter = new \RecursiveIteratorIterator($dirIter); - /** @var \SplFileInfo $file */ - foreach ($fileIter as $file) { - if (!$file->isDir()) { - $zip->addFile($file->getPathname(), $relativeFileDir . '/' . $fileIter->getSubPathname()); - } - } - } - - // Close off our zip and move it to the required location - $zip->close(); - rename($zipTempFile, $zipOutFile); - - // Delete our temporary DB dump file - unlink($dumpTempFile); - - // Announce end and display errors - echo "Finished"; - if ($errors) { - echo " with the following errors:\n" . $errors; - } + // TODO - Throw errors if existing + return $dumpTempFile; } }