diff --git a/public/index.php b/public/index.php index ba5c11a..ccb5315 100644 --- a/public/index.php +++ b/public/index.php @@ -363,14 +363,22 @@ $app->get('/api/backup/all', function (Request $request, Response $response) use try { $pdo = $container->get('db'); $backup = new \App\BackupService($_SESSION['db_user'], $_SESSION['db_pass']); - $data = $backup->dumpAllDatabases($pdo); + $tempFile = $backup->dumpAllDatabases($pdo); $filename = 'backup_all_' . date('Y-m-d_H-i-s') . '.sql.gz'; - $response->getBody()->write($data); + $stream = fopen($tempFile, 'rb'); + $filesize = filesize($tempFile); + + // Удаляем временный файл после отправки + register_shutdown_function(function() use ($tempFile) { + if (file_exists($tempFile)) unlink($tempFile); + }); + return $response ->withHeader('Content-Type', 'application/gzip') ->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"') - ->withHeader('Content-Length', strlen($data)); + ->withHeader('Content-Length', $filesize) + ->withBody(new \Slim\Psr7\Stream($stream)); } catch (\Exception $e) { $response->getBody()->write(json_encode(['error' => $e->getMessage()])); return $response->withHeader('Content-Type', 'application/json')->withStatus(500); @@ -382,14 +390,21 @@ $app->get('/api/backup/database/{name}', function (Request $request, Response $r try { $database = $args['name']; $backup = new \App\BackupService($_SESSION['db_user'], $_SESSION['db_pass']); - $data = $backup->dumpDatabase($database); + $tempFile = $backup->dumpDatabase($database); $filename = $database . '_' . date('Y-m-d_H-i-s') . '.sql.gz'; - $response->getBody()->write($data); + $stream = fopen($tempFile, 'rb'); + $filesize = filesize($tempFile); + + register_shutdown_function(function() use ($tempFile) { + if (file_exists($tempFile)) unlink($tempFile); + }); + return $response ->withHeader('Content-Type', 'application/gzip') ->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"') - ->withHeader('Content-Length', strlen($data)); + ->withHeader('Content-Length', $filesize) + ->withBody(new \Slim\Psr7\Stream($stream)); } catch (\Exception $e) { $response->getBody()->write(json_encode(['error' => $e->getMessage()])); return $response->withHeader('Content-Type', 'application/json')->withStatus(500); diff --git a/src/BackupService.php b/src/BackupService.php index 39e1a3f..62f7482 100644 --- a/src/BackupService.php +++ b/src/BackupService.php @@ -23,30 +23,36 @@ class BackupService } /** - * Создать дамп и вернуть содержимое (gzip сжатое) + * Создать дамп и вернуть путь к временному файлу */ public function dumpDatabase(string $database): string { + $tempFile = sys_get_temp_dir() . '/backup_' . $database . '_' . uniqid() . '.sql.gz'; + $cmd = sprintf( - 'mysqldump --host=%s --port=%s --user=%s --password=%s --single-transaction --routines --triggers %s 2>/dev/null | gzip', + 'mysqldump --host=%s --port=%s --user=%s --password=%s --single-transaction --routines --triggers %s 2>&1 | gzip > %s', escapeshellarg($this->host), escapeshellarg($this->port), escapeshellarg($this->user), escapeshellarg($this->pass), - escapeshellarg($database) + escapeshellarg($database), + escapeshellarg($tempFile) ); - $output = shell_exec($cmd); + exec($cmd, $output, $returnCode); - if ($output === null || $output === '') { - throw new \RuntimeException("Failed to create dump for database: $database"); + if ($returnCode !== 0 || !file_exists($tempFile) || filesize($tempFile) < 100) { + if (file_exists($tempFile)) { + unlink($tempFile); + } + throw new \RuntimeException("mysqldump failed: " . implode("\n", $output)); } - return $output; + return $tempFile; } /** - * Создать дамп всех баз данных (один файл) + * Создать дамп всех баз данных */ public function dumpAllDatabases(\PDO $pdo): string { @@ -56,23 +62,28 @@ class BackupService throw new \RuntimeException("No databases available for backup"); } + $tempFile = sys_get_temp_dir() . '/backup_all_' . uniqid() . '.sql.gz'; $dbList = implode(' ', array_map('escapeshellarg', $databases)); $cmd = sprintf( - 'mysqldump --host=%s --port=%s --user=%s --password=%s --single-transaction --routines --triggers --databases %s 2>/dev/null | gzip', + 'mysqldump --host=%s --port=%s --user=%s --password=%s --single-transaction --routines --triggers --databases %s 2>&1 | gzip > %s', escapeshellarg($this->host), escapeshellarg($this->port), escapeshellarg($this->user), escapeshellarg($this->pass), - $dbList + $dbList, + escapeshellarg($tempFile) ); - $output = shell_exec($cmd); + exec($cmd, $output, $returnCode); - if ($output === null || $output === '') { - throw new \RuntimeException("Failed to create dump"); + if ($returnCode !== 0 || !file_exists($tempFile) || filesize($tempFile) < 100) { + if (file_exists($tempFile)) { + unlink($tempFile); + } + throw new \RuntimeException("mysqldump failed: " . implode("\n", $output)); } - return $output; + return $tempFile; } }