mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2024-12-27 07:00:29 +01:00
231 lines
6.5 KiB
PHP
231 lines
6.5 KiB
PHP
|
<?php
|
||
|
|
||
|
/**
|
||
|
* Automatische FTP-Backups
|
||
|
*
|
||
|
* Prozessstarter erzeugt jeden Tag ein Backup von
|
||
|
* - userdata-Verzeichnis (gezippt)
|
||
|
* - SQL-Dump der Datenbank (gezippt)
|
||
|
* und lädt diese Backups auf den hinterlegten FTP-Server hoch.
|
||
|
*
|
||
|
* FTP-Zugangsdaten pflegen über Ftpbackup-Modul
|
||
|
* (index.php?module=ftpbackup&action=list)
|
||
|
*/
|
||
|
|
||
|
use Xentral\Components\Filesystem\Adapter\FtpConfig;
|
||
|
use Xentral\Components\Filesystem\FilesystemFactory;
|
||
|
use Xentral\Components\Filesystem\FilesystemInterface;
|
||
|
|
||
|
try {
|
||
|
|
||
|
$ftpHost = $app->erp->GetKonfiguration('ftpbackup_list_server');
|
||
|
$ftpPort = (int)$app->erp->GetKonfiguration('ftpbackup_list_port');
|
||
|
$ftpUser = $app->erp->GetKonfiguration('ftpbackup_list_benutzer');
|
||
|
$ftpPass = $app->erp->GetKonfiguration('ftpbackup_list_passwort');
|
||
|
$ftpDir = $app->erp->GetKonfiguration('ftpbackup_list_verzeichnis');
|
||
|
|
||
|
if(empty($ftpHost)){
|
||
|
throw new RuntimeException('FTP hostname is missing or empty.');
|
||
|
}
|
||
|
if(empty($ftpUser)){
|
||
|
throw new RuntimeException('FTP username is missing or empty.');
|
||
|
}
|
||
|
if(empty($ftpPass)){
|
||
|
throw new RuntimeException('FTP password is missing or empty.');
|
||
|
}
|
||
|
if(empty($ftpDir)){
|
||
|
$ftpDir = '/';
|
||
|
}
|
||
|
if(empty($ftpPort) || $ftpPort <= 0){
|
||
|
$ftpPort = 21;
|
||
|
}
|
||
|
|
||
|
/** @var FilesystemFactory $factory */
|
||
|
$factory = $app->Container->get('FilesystemFactory');
|
||
|
$ftpConfig = new FtpConfig($ftpHost, $ftpUser, $ftpPass, $ftpDir, $ftpPort);
|
||
|
$ftpFs = $factory->createFtp($ftpConfig);
|
||
|
|
||
|
$ftpBackup = new FtpBackupCronjob($app->Conf, $ftpFs);
|
||
|
$ftpBackup->execute();
|
||
|
$ftpBackup->cleanup();
|
||
|
|
||
|
} catch (Exception $exception) {
|
||
|
if (isset($ftpBackup) && $ftpBackup !== null) {
|
||
|
$ftpBackup->cleanup();
|
||
|
}
|
||
|
throw $exception;
|
||
|
}
|
||
|
|
||
|
|
||
|
final class FtpBackupCronjob
|
||
|
{
|
||
|
/** @var Config $config */
|
||
|
private $config;
|
||
|
|
||
|
/** @var FilesystemInterface $ftp */
|
||
|
private $ftp;
|
||
|
|
||
|
/** @var string $tmpUserdataFilePath */
|
||
|
private $tmpUserdataFilePath;
|
||
|
|
||
|
/** @var string $tmpMysqlDumpFilePath */
|
||
|
private $tmpMysqlDumpFilePath;
|
||
|
|
||
|
/**
|
||
|
* @param Config $config
|
||
|
* @param FilesystemInterface $ftp
|
||
|
*/
|
||
|
public function __construct(Config $config, FilesystemInterface $ftp)
|
||
|
{
|
||
|
$this->ftp = $ftp;
|
||
|
$this->config = $config;
|
||
|
$this->tmpUserdataFilePath = tempnam(sys_get_temp_dir(), 'xentral_userdata_backup');
|
||
|
$this->tmpMysqlDumpFilePath = tempnam(sys_get_temp_dir(), 'xentral_mysql_backup');
|
||
|
|
||
|
if(!is_file($this->tmpMysqlDumpFilePath)){
|
||
|
throw new RuntimeException(sprintf('Could not create temp file: %s', $this->tmpMysqlDumpFilePath));
|
||
|
}
|
||
|
if(!is_file($this->tmpUserdataFilePath)){
|
||
|
throw new RuntimeException(sprintf('Could not create temp file: %s', $this->tmpUserdataFilePath));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*/
|
||
|
public function __destruct()
|
||
|
{
|
||
|
$this->cleanup();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return void
|
||
|
*/
|
||
|
public function cleanup()
|
||
|
{
|
||
|
$this->deleteFile($this->tmpUserdataFilePath);
|
||
|
$this->deleteFile($this->tmpMysqlDumpFilePath);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return void
|
||
|
*/
|
||
|
public function execute()
|
||
|
{
|
||
|
$this->createMysqlBackup($this->tmpMysqlDumpFilePath);
|
||
|
$this->createUserdataBackup($this->tmpUserdataFilePath);
|
||
|
|
||
|
// d =Day of the month, 2 digits with leading zeros; Excaple: 01 to 31
|
||
|
$this->copyFileToFtp($this->tmpUserdataFilePath, 'userdata_' . date('d') . '.tar.gz');
|
||
|
$this->copyFileToFtp($this->tmpMysqlDumpFilePath, 'mysqldump_' . date('d') . '.gz');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $dumpFilePath Absolute path to mysqldump
|
||
|
*
|
||
|
* @return void
|
||
|
* @throws RuntimeException
|
||
|
*
|
||
|
*/
|
||
|
private function createMysqlBackup($dumpFilePath)
|
||
|
{
|
||
|
$dbHost = property_exists($this->config, 'WFdbhost') ? $this->config->WFdbhost : 'localhost';
|
||
|
$dbPort = property_exists($this->config, 'WFdbport') ? (int)$this->config->WFdbport : 3306;
|
||
|
$dbName = property_exists($this->config, 'WFdbname') ? $this->config->WFdbname : null;
|
||
|
$dbUser = property_exists($this->config, 'WFdbuser') ? $this->config->WFdbuser : null;
|
||
|
$dbPass = property_exists($this->config, 'WFdbpass') ? $this->config->WFdbpass : null;
|
||
|
|
||
|
if(empty($dbName)){
|
||
|
throw new RuntimeException('Database name is missing or empty.');
|
||
|
}
|
||
|
if(empty($dbUser)){
|
||
|
throw new RuntimeException('Database user is missing or empty.');
|
||
|
}
|
||
|
if(empty($dbPass)){
|
||
|
throw new RuntimeException('Database password is missing or empty.');
|
||
|
}
|
||
|
|
||
|
$dumpCmd = sprintf(
|
||
|
'mysqldump --no-tablespaces --host=%s --port=%s --user=%s --password=%s --databases %s',
|
||
|
escapeshellarg($dbHost),
|
||
|
$dbPort,
|
||
|
escapeshellarg($dbUser),
|
||
|
escapeshellarg($dbPass),
|
||
|
escapeshellarg($dbName)
|
||
|
);
|
||
|
$zipCmd = sprintf('gzip > %s', escapeshellarg($dumpFilePath));
|
||
|
$command = $dumpCmd . ' | ' . $zipCmd;
|
||
|
|
||
|
@exec($command, $output, $returnVar);
|
||
|
$output = implode("\n", $output);
|
||
|
|
||
|
if($returnVar !== 0){
|
||
|
throw new RuntimeException('Mysql dump command failed. ' . $output);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $backupFilePath Absolute path to archive file
|
||
|
*
|
||
|
* @return void
|
||
|
* @throws RuntimeException
|
||
|
*
|
||
|
*/
|
||
|
private function createUserdataBackup($backupFilePath)
|
||
|
{
|
||
|
$userdataDir = property_exists($this->config, 'WFuserdata')
|
||
|
? $this->config->WFuserdata
|
||
|
: dirname(__DIR__) . '/userdata';
|
||
|
|
||
|
// 2>&1 = Fehlerausgabe-Kanal in Standardausgabe-Kanal schreiben
|
||
|
$command = sprintf(
|
||
|
'tar cfz %s %s 2>&1',
|
||
|
escapeshellarg($backupFilePath),
|
||
|
escapeshellarg($userdataDir)
|
||
|
);
|
||
|
|
||
|
@exec($command, $output, $returnVar);
|
||
|
$output = implode("\n", $output);
|
||
|
|
||
|
if($returnVar !== 0){
|
||
|
throw new RuntimeException('Userdata backup command failed. ' . $output);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $sourceFilePath Absolute path
|
||
|
* @param string $targetFilename File name; without dir
|
||
|
*
|
||
|
* @return void
|
||
|
* @throws RuntimeException If operation fails
|
||
|
*
|
||
|
*/
|
||
|
private function copyFileToFtp($sourceFilePath, $targetFilename)
|
||
|
{
|
||
|
if(!is_file($sourceFilePath)){
|
||
|
throw new RuntimeException(sprintf('Source file not found: %s', $sourceFilePath));
|
||
|
}
|
||
|
$resource = @fopen($sourceFilePath, 'rb');
|
||
|
if(!is_resource($resource)){
|
||
|
throw new RuntimeException(sprintf('Source file not readable: %s', $sourceFilePath));
|
||
|
}
|
||
|
|
||
|
// Datei anlegen oder überschreiben
|
||
|
$success = $this->ftp->putStream($targetFilename, $resource);
|
||
|
if(!$success){
|
||
|
throw new RuntimeException(sprintf('Could not write file to FTP: %s', $targetFilename));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $filePath
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
private function deleteFile($filePath)
|
||
|
{
|
||
|
if(is_file($filePath)){
|
||
|
@unlink($filePath);
|
||
|
}
|
||
|
}
|
||
|
}
|