3 namespace Cli\Commands;
5 use Minicli\Command\CommandCall;
6 use RecursiveDirectoryIterator;
7 use Symfony\Component\Process\ExecutableFinder;
8 use Symfony\Component\Process\Process;
11 final class BackupCommand
13 public function __construct(
14 protected string $appDir
18 public function handle(CommandCall $input)
20 // TODO - Customizable output file
21 // TODO - Database only command
22 // TODO - Validate DB vars
23 // TODO - Error handle each stage
24 // TODO - Validate zip (and any other extensions required) are active.
26 $zipOutFile = getcwd() . DIRECTORY_SEPARATOR . 'backup.zip';
28 $dbHost = ($_SERVER['DB_HOST'] ?? '');
29 $dbUser = ($_SERVER['DB_USERNAME'] ?? '');
30 $dbPass = ($_SERVER['DB_PASSWORD'] ?? '');
31 $dbDatabase = ($_SERVER['DB_DATABASE'] ?? '');
33 // Create a mysqldump for the BookStack database
34 $executableFinder = new ExecutableFinder();
35 $mysqldumpPath = $executableFinder->find('mysqldump');
37 $process = new Process([
42 '--single-transaction',
49 $dumpTempFile = tempnam(sys_get_temp_dir(), 'bsbackup');
50 $dumpTempFileResource = fopen($dumpTempFile, 'w');
51 foreach ($process as $type => $data) {
52 if ($process::OUT === $type) {
53 fwrite($dumpTempFileResource, $data);
54 } else { // $process::ERR === $type
55 $errors .= $data . "\n";
58 fclose($dumpTempFileResource);
61 // Create a new ZIP file
62 $zipTempFile = tempnam(sys_get_temp_dir(), 'bsbackup');
63 $zip = new ZipArchive();
64 $sep = DIRECTORY_SEPARATOR;
65 $zip->open($zipTempFile, ZipArchive::CREATE);
66 $zip->addFile($this->appDir . $sep . '.env', '.env');
67 $zip->addFile($dumpTempFile, 'db.sql');
70 $this->appDir . $sep . 'public' . $sep . 'uploads' => 'public/uploads',
71 $this->appDir . $sep . 'storage' . $sep . 'uploads' => 'storage/uploads',
74 foreach ($fileDirs as $fullFileDir => $relativeFileDir) {
75 $dirIter = new RecursiveDirectoryIterator($fullFileDir);
76 $fileIter = new \RecursiveIteratorIterator($dirIter);
77 /** @var \SplFileInfo $file */
78 foreach ($fileIter as $file) {
79 if (!$file->isDir()) {
80 $zip->addFile($file->getPathname(), $relativeFileDir . '/' . $fileIter->getSubPathname());
85 // Close off our zip and move it to the required location
87 rename($zipTempFile, $zipOutFile);
89 // Delete our temporary DB dump file
90 unlink($dumpTempFile);
92 // Announce end and display errors
95 echo " with the following errors:\n" . $errors;