]> BookStack Code Mirror - system-cli/commitdiff
Outlined a general working backup approach
authorDan Brown <redacted>
Fri, 3 Mar 2023 02:44:08 +0000 (02:44 +0000)
committerDan Brown <redacted>
Thu, 9 Mar 2023 15:28:11 +0000 (15:28 +0000)
scripts/Commands/BackupCommand.php [new file with mode: 0644]
scripts/compile.php
scripts/composer.json
scripts/composer.lock
scripts/run

diff --git a/scripts/Commands/BackupCommand.php b/scripts/Commands/BackupCommand.php
new file mode 100644 (file)
index 0000000..2b30385
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+
+namespace Cli\Commands;
+
+use Minicli\Command\CommandCall;
+use RecursiveDirectoryIterator;
+use Symfony\Component\Process\ExecutableFinder;
+use Symfony\Component\Process\Process;
+use ZipArchive;
+
+final class BackupCommand
+{
+    public function __construct(
+        protected string $appDir
+    ) {
+    }
+
+    public function handle(CommandCall $input)
+    {
+        // TODO - Customizable output file
+        // TODO - Database only command
+        // TODO - Validate DB vars
+        // TODO - Error handle each stage
+        // TODO - Validate zip (and any other extensions required) are active.
+
+        $zipOutFile = getcwd() . DIRECTORY_SEPARATOR . 'backup.zip';
+
+        $dbHost = ($_SERVER['DB_HOST'] ?? '');
+        $dbUser = ($_SERVER['DB_USERNAME'] ?? '');
+        $dbPass = ($_SERVER['DB_PASSWORD'] ?? '');
+        $dbDatabase = ($_SERVER['DB_DATABASE'] ?? '');
+
+        // Create a mysqldump for the BookStack database
+        $executableFinder = new ExecutableFinder();
+        $mysqldumpPath = $executableFinder->find('mysqldump');
+
+        $process = new Process([
+            $mysqldumpPath,
+            '-h', $dbHost,
+            '-u', $dbUser,
+            '-p' . $dbPass,
+            '--single-transaction',
+            '--no-tablespaces',
+            $dbDatabase,
+        ]);
+        $process->start();
+
+        $errors = "";
+        $dumpTempFile = tempnam(sys_get_temp_dir(), 'bsbackup');
+        $dumpTempFileResource = fopen($dumpTempFile, 'w');
+        foreach ($process as $type => $data) {
+            if ($process::OUT === $type) {
+                fwrite($dumpTempFileResource, $data);
+            } else { // $process::ERR === $type
+                $errors .= $data . "\n";
+            }
+        }
+        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;
+        }
+    }
+}
index f1773d1bfccba1e0c3d9bc1335437cb05245ec22..ff7d16c1f68f9ce7ba295717d1bb2e07678fb125 100644 (file)
@@ -30,6 +30,7 @@ try {
     // Add the rest of the apps files
     $phar->addFile(__DIR__ . '/run', 'run');
     $phar->buildFromDirectory(__DIR__, '/vendor(.*)/');
+    $phar->buildFromDirectory(__DIR__, '/Commands(.*)/');
 
     // Customize the stub to add the shebang
     $stub = "#!/usr/bin/env php \n" . $defaultStub;
index 27c6ef9bce5a6dc26d585cb8c8633fadfc6fd35b..5bc28b41171f0913976e109516619337b86537f6 100644 (file)
@@ -1,8 +1,14 @@
 {
     "require": {
         "minicli/minicli": "^3.2",
+        "symfony/process": "^6.0",
         "vlucas/phpdotenv": "^5.5"
     },
+    "autoload": {
+        "psr-4": {
+            "Cli\\Commands\\": "Commands/"
+        }
+    },
     "config": {
         "optimize-autoloader": true,
         "preferred-install": "dist",
index 6cb3b992d18339eecc80d519066c2625da43be1e..3789592f22eb345739d711639b69f83eaf2555fd 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "d834df279b63d647dcaf31ae96468a27",
+    "content-hash": "b56ccc782d009482249ce5e3a4b62147",
     "packages": [
         {
             "name": "graham-campbell/result-type",
             ],
             "time": "2022-11-03T14:55:06+00:00"
         },
+        {
+            "name": "symfony/process",
+            "version": "v6.0.19",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/process.git",
+                "reference": "2114fd60f26a296cc403a7939ab91478475a33d4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/process/zipball/2114fd60f26a296cc403a7939ab91478475a33d4",
+                "reference": "2114fd60f26a296cc403a7939ab91478475a33d4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Process\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "[email protected]"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Executes commands in sub-processes",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/process/tree/v6.0.19"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2023-01-01T08:36:10+00:00"
+        },
         {
             "name": "vlucas/phpdotenv",
             "version": "v5.5.0",
index 435830f5245284de67254229495c2de1a99cf4c1..ef25ce7b65bff4bd07e9600b9b589e669cfdde01 100644 (file)
@@ -7,8 +7,8 @@ if (php_sapi_name() !== 'cli') {
 
 require __DIR__ . '/vendor/autoload.php';
 
+use Cli\Commands\BackupCommand;
 use Minicli\App;
-use Minicli\Command\CommandCall;
 
 // Get the directory of the CLI "entrypoint", adjusted to be the real
 // location where running via a phar.
@@ -16,22 +16,16 @@ $scriptDir = __DIR__;
 if (str_starts_with($scriptDir, 'phar://')) {
     $scriptDir = dirname(Phar::running(false));
 }
+$bsDir = dirname($scriptDir);
 
 // Load in our .env file from the parent directory
-$dotenv = Dotenv\Dotenv::createImmutable(dirname($scriptDir));
+$dotenv = Dotenv\Dotenv::createImmutable($bsDir);
 $dotenv->safeLoad();
 
 // Setup our CLI
 $app = new App();
-$app->setSignature('./run mycommand');
+$app->setSignature('./run');
 
-$app->registerCommand('mycommand', function (CommandCall $input) use ($scriptDir) {
-    echo "My Command!";
-    echo "BS URL is: " . ($_SERVER['BS_URL'] ?? '') . "\n";
-    echo "DB HOST is: " . ($_SERVER['DB_HOST'] ?? '') . "\n";
-    echo dirname($scriptDir);
-
-//    var_dump($input);
-});
+$app->registerCommand('backup', [new BackupCommand($bsDir), 'handle']);
 
 $app->runCommand($argv);