mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-02-12 04:29:36 +00:00
Extracted logic used by both BackupManager and UpdateExecutor to new service
This commit is contained in:
parent
68ff0721ce
commit
1ccc3ad440
3 changed files with 89 additions and 75 deletions
|
|
@ -35,17 +35,18 @@ use Symfony\Component\Process\Process;
|
||||||
* This service handles all backup-related operations and can be used
|
* This service handles all backup-related operations and can be used
|
||||||
* by the Update Manager, CLI commands, or other services.
|
* by the Update Manager, CLI commands, or other services.
|
||||||
*/
|
*/
|
||||||
class BackupManager
|
readonly class BackupManager
|
||||||
{
|
{
|
||||||
private const BACKUP_DIR = 'var/backups';
|
private const BACKUP_DIR = 'var/backups';
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
#[Autowire(param: 'kernel.project_dir')]
|
#[Autowire(param: 'kernel.project_dir')]
|
||||||
private readonly string $projectDir,
|
private string $projectDir,
|
||||||
private readonly LoggerInterface $logger,
|
private LoggerInterface $logger,
|
||||||
private readonly Filesystem $filesystem,
|
private Filesystem $filesystem,
|
||||||
private readonly VersionManagerInterface $versionManager,
|
private VersionManagerInterface $versionManager,
|
||||||
private readonly EntityManagerInterface $entityManager,
|
private EntityManagerInterface $entityManager,
|
||||||
|
private CommandRunHelper $commandRunHelper,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,7 +91,7 @@ class BackupManager
|
||||||
$backupFile = $backupDir . '/' . $prefix . '-v' . $currentVersion . '-' . date('Y-m-d-His') . '.zip';
|
$backupFile = $backupDir . '/' . $prefix . '-v' . $currentVersion . '-' . date('Y-m-d-His') . '.zip';
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->runCommand([
|
$this->commandRunHelper->runCommand([
|
||||||
'php', 'bin/console', 'partdb:backup',
|
'php', 'bin/console', 'partdb:backup',
|
||||||
'--full',
|
'--full',
|
||||||
'--overwrite',
|
'--overwrite',
|
||||||
|
|
@ -103,7 +104,7 @@ class BackupManager
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get list of backups.
|
* Get list of backups, that are available, sorted by date descending.
|
||||||
*
|
*
|
||||||
* @return array<array{file: string, path: string, date: int, size: int}>
|
* @return array<array{file: string, path: string, date: int, size: int}>
|
||||||
*/
|
*/
|
||||||
|
|
@ -126,7 +127,7 @@ class BackupManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by date descending
|
// Sort by date descending
|
||||||
usort($backups, fn($a, $b) => $b['date'] <=> $a['date']);
|
usort($backups, static fn($a, $b) => $b['date'] <=> $a['date']);
|
||||||
|
|
||||||
return $backups;
|
return $backups;
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +136,7 @@ class BackupManager
|
||||||
* Get details about a specific backup file.
|
* Get details about a specific backup file.
|
||||||
*
|
*
|
||||||
* @param string $filename The backup filename
|
* @param string $filename The backup filename
|
||||||
* @return array|null Backup details or null if not found
|
* @return null|array{file: string, path: string, date: int, size: int, from_version: ?string, to_version: ?string, contains_database?: bool, contains_config?: bool, contains_attachments?: bool} Backup details or null if not found
|
||||||
*/
|
*/
|
||||||
public function getBackupDetails(string $filename): ?array
|
public function getBackupDetails(string $filename): ?array
|
||||||
{
|
{
|
||||||
|
|
@ -449,39 +450,4 @@ class BackupManager
|
||||||
$this->filesystem->mirror($uploads, $this->projectDir . '/uploads', null, ['override' => true]);
|
$this->filesystem->mirror($uploads, $this->projectDir . '/uploads', null, ['override' => true]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Run a shell command with proper error handling.
|
|
||||||
*/
|
|
||||||
private function runCommand(array $command, string $description, int $timeout = 120): string
|
|
||||||
{
|
|
||||||
$process = new Process($command, $this->projectDir);
|
|
||||||
$process->setTimeout($timeout);
|
|
||||||
|
|
||||||
// Set environment variables
|
|
||||||
$currentEnv = getenv();
|
|
||||||
if (!is_array($currentEnv)) {
|
|
||||||
$currentEnv = [];
|
|
||||||
}
|
|
||||||
$env = array_merge($currentEnv, [
|
|
||||||
'HOME' => $this->projectDir,
|
|
||||||
'COMPOSER_HOME' => $this->projectDir . '/var/composer',
|
|
||||||
'PATH' => getenv('PATH') ?: '/usr/local/bin:/usr/bin:/bin',
|
|
||||||
]);
|
|
||||||
$process->setEnv($env);
|
|
||||||
|
|
||||||
$output = '';
|
|
||||||
$process->run(function ($type, $buffer) use (&$output) {
|
|
||||||
$output .= $buffer;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!$process->isSuccessful()) {
|
|
||||||
$errorOutput = $process->getErrorOutput() ?: $process->getOutput();
|
|
||||||
throw new \RuntimeException(
|
|
||||||
sprintf('%s failed: %s', $description, trim($errorOutput))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
src/Services/System/CommandRunHelper.php
Normal file
75
src/Services/System/CommandRunHelper.php
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Services\System;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
|
use Symfony\Component\Process\Process;
|
||||||
|
|
||||||
|
class CommandRunHelper
|
||||||
|
{
|
||||||
|
private UpdateExecutor $updateExecutor;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
#[Autowire(param: 'kernel.project_dir')] private readonly string $project_dir
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a shell command with proper error handling.
|
||||||
|
*/
|
||||||
|
public function runCommand(array $command, string $description, int $timeout = 120): string
|
||||||
|
{
|
||||||
|
$process = new Process($command, $this->project_dir);
|
||||||
|
$process->setTimeout($timeout);
|
||||||
|
|
||||||
|
// Set environment variables needed for Composer and other tools
|
||||||
|
// This is especially important when running as www-data which may not have HOME set
|
||||||
|
// We inherit from current environment and override/add specific variables
|
||||||
|
$currentEnv = getenv();
|
||||||
|
if (!is_array($currentEnv)) {
|
||||||
|
$currentEnv = [];
|
||||||
|
}
|
||||||
|
$env = array_merge($currentEnv, [
|
||||||
|
'HOME' => $this->project_dir.'/var/www-data-home',
|
||||||
|
'COMPOSER_HOME' => $this->project_dir.'/var/composer',
|
||||||
|
'PATH' => getenv('PATH') ?: '/usr/local/bin:/usr/bin:/bin',
|
||||||
|
]);
|
||||||
|
$process->setEnv($env);
|
||||||
|
|
||||||
|
$output = '';
|
||||||
|
$process->run(function ($type, $buffer) use (&$output) {
|
||||||
|
$output .= $buffer;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!$process->isSuccessful()) {
|
||||||
|
$errorOutput = $process->getErrorOutput() ?: $process->getOutput();
|
||||||
|
throw new \RuntimeException(
|
||||||
|
sprintf('%s failed: %s', $description, trim($errorOutput))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -46,6 +46,7 @@ class UpdateExecutor
|
||||||
private array $steps = [];
|
private array $steps = [];
|
||||||
|
|
||||||
private ?string $currentLogFile = null;
|
private ?string $currentLogFile = null;
|
||||||
|
private CommandRunHelper $commandRunHelper;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
#[Autowire(param: 'kernel.project_dir')]
|
#[Autowire(param: 'kernel.project_dir')]
|
||||||
|
|
@ -58,6 +59,7 @@ class UpdateExecutor
|
||||||
#[Autowire(param: 'app.debug_mode')]
|
#[Autowire(param: 'app.debug_mode')]
|
||||||
private readonly bool $debugMode = false,
|
private readonly bool $debugMode = false,
|
||||||
) {
|
) {
|
||||||
|
$this->commandRunHelper = new CommandRunHelper($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -516,36 +518,7 @@ class UpdateExecutor
|
||||||
*/
|
*/
|
||||||
private function runCommand(array $command, string $description, int $timeout = 120): string
|
private function runCommand(array $command, string $description, int $timeout = 120): string
|
||||||
{
|
{
|
||||||
$process = new Process($command, $this->project_dir);
|
return $this->commandRunHelper->runCommand($command, $description, $timeout);
|
||||||
$process->setTimeout($timeout);
|
|
||||||
|
|
||||||
// Set environment variables needed for Composer and other tools
|
|
||||||
// This is especially important when running as www-data which may not have HOME set
|
|
||||||
// We inherit from current environment and override/add specific variables
|
|
||||||
$currentEnv = getenv();
|
|
||||||
if (!is_array($currentEnv)) {
|
|
||||||
$currentEnv = [];
|
|
||||||
}
|
|
||||||
$env = array_merge($currentEnv, [
|
|
||||||
'HOME' => $this->project_dir,
|
|
||||||
'COMPOSER_HOME' => $this->project_dir . '/var/composer',
|
|
||||||
'PATH' => getenv('PATH') ?: '/usr/local/bin:/usr/bin:/bin',
|
|
||||||
]);
|
|
||||||
$process->setEnv($env);
|
|
||||||
|
|
||||||
$output = '';
|
|
||||||
$process->run(function ($type, $buffer) use (&$output) {
|
|
||||||
$output .= $buffer;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!$process->isSuccessful()) {
|
|
||||||
$errorOutput = $process->getErrorOutput() ?: $process->getOutput();
|
|
||||||
throw new \RuntimeException(
|
|
||||||
sprintf('%s failed: %s', $description, trim($errorOutput))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue