First version
This commit is contained in:
		
							
								
								
									
										65
									
								
								src/CommandBackup.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/CommandBackup.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace App;
 | 
			
		||||
 | 
			
		||||
use Symfony\Component\Console\Command\Command;
 | 
			
		||||
use Symfony\Component\Console\Input\InputInterface;
 | 
			
		||||
use Symfony\Component\Console\Input\InputArgument;
 | 
			
		||||
use Symfony\Component\Console\Output\OutputInterface;
 | 
			
		||||
 | 
			
		||||
use Symfony\Component\Yaml\Yaml;
 | 
			
		||||
use Symfony\Component\Yaml\Exception\ParseException;
 | 
			
		||||
 | 
			
		||||
class CommandBackup extends Command
 | 
			
		||||
{
 | 
			
		||||
    static $defaultName = "backup";
 | 
			
		||||
    static $defaultDescription = "Start backup to assigned buckets";
 | 
			
		||||
 | 
			
		||||
    protected function configure(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addArgument('config', InputArgument::OPTIONAL, 'Configuration file', "config.yml");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function execute(InputInterface $input, OutputInterface $output): int
 | 
			
		||||
    {
 | 
			
		||||
        $output->writeln('Start backup!');
 | 
			
		||||
 | 
			
		||||
        $output->writeln('Opening: '.$input->getArgument('config'));
 | 
			
		||||
        try {
 | 
			
		||||
            $config = Yaml::parseFile($input->getArgument('config'));
 | 
			
		||||
        } catch (ParseException $exception) {
 | 
			
		||||
            $output->writeln('Unable to parse the YAML string: '. $exception->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        $rclone = new Rclone\Rclone();
 | 
			
		||||
        $output->writeln("Rclone version: ". $rclone->getVersion());
 | 
			
		||||
        $ntfy = new Ntfy\Ntfy($config['notification']['domain']);
 | 
			
		||||
 | 
			
		||||
        $loader = new \Twig\Loader\ArrayLoader($config['templates']);
 | 
			
		||||
        $twig = new \Twig\Environment($loader);
 | 
			
		||||
        $twig->addExtension(new Twig\AppExtension());
 | 
			
		||||
 | 
			
		||||
        foreach($config['backup'] as $conf)
 | 
			
		||||
        {   
 | 
			
		||||
            try {
 | 
			
		||||
                $template['config'] = $conf;
 | 
			
		||||
                $template['start'] = new \DateTime();
 | 
			
		||||
                $template['source_size'] = $rclone->getSize($conf['source']);
 | 
			
		||||
                $template['destination_size_before'] = $rclone->getSize($conf['destination']);
 | 
			
		||||
 | 
			
		||||
                $rclone->copy($conf['source'], $conf['destination'], "6M");
 | 
			
		||||
 | 
			
		||||
                $template['destination_size_after'] = $rclone->getSize($conf['destination']);
 | 
			
		||||
                $template['end'] = new \DateTime();
 | 
			
		||||
 | 
			
		||||
                $message =  $twig->render('notify', $template);
 | 
			
		||||
                echo $message;
 | 
			
		||||
            
 | 
			
		||||
            } catch (\Throwable $e) {
 | 
			
		||||
                $message = $e->getMessage();
 | 
			
		||||
            }
 | 
			
		||||
            $ntfy->send($config['notification']['topic'], $conf['title'], $message);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Command::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								src/CommandShow.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/CommandShow.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace App;
 | 
			
		||||
 | 
			
		||||
use Symfony\Component\Console\Command\Command;
 | 
			
		||||
use Symfony\Component\Console\Input\InputInterface;
 | 
			
		||||
use Symfony\Component\Console\Input\InputArgument;
 | 
			
		||||
use Symfony\Component\Console\Output\OutputInterface;
 | 
			
		||||
use Symfony\Component\Console\Helper\Table;
 | 
			
		||||
 | 
			
		||||
use Symfony\Component\Yaml\Yaml;
 | 
			
		||||
use Symfony\Component\Yaml\Exception\ParseException;
 | 
			
		||||
 | 
			
		||||
class CommandShow extends Command
 | 
			
		||||
{
 | 
			
		||||
    static $defaultName = "show";
 | 
			
		||||
    static $defaultDescription = "Show all backup entries";
 | 
			
		||||
 | 
			
		||||
    protected function configure(): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addArgument('config', InputArgument::OPTIONAL, 'Configuration file', "config.yml");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function execute(InputInterface $input, OutputInterface $output): int
 | 
			
		||||
    {
 | 
			
		||||
        $output->writeln('Reading from: '.$input->getArgument('config'));
 | 
			
		||||
        try {
 | 
			
		||||
            $config = Yaml::parseFile($input->getArgument('config'));
 | 
			
		||||
        } catch (ParseException $exception) {
 | 
			
		||||
            $output->writeln('Unable to parse the YAML string: '. $exception->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
        $table = new Table($output);
 | 
			
		||||
        $table
 | 
			
		||||
            ->setHeaders(['Description', 'Source', 'Destination'])
 | 
			
		||||
            ->setRows($config['backup']);
 | 
			
		||||
        ;
 | 
			
		||||
        $table->render();        
 | 
			
		||||
 | 
			
		||||
        return Command::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								src/Ntfy/Ntfy.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/Ntfy/Ntfy.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace App\Ntfy;
 | 
			
		||||
 | 
			
		||||
class Ntfy {
 | 
			
		||||
    private $domain;
 | 
			
		||||
    function __construct($domain)
 | 
			
		||||
    {
 | 
			
		||||
        $this->domain = $domain;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function send($topic,$title, $message)
 | 
			
		||||
    {
 | 
			
		||||
        file_get_contents('https://'.$this->domain.'/'.$topic, false, stream_context_create([
 | 
			
		||||
            'http' => [
 | 
			
		||||
                'method' => 'POST', 
 | 
			
		||||
                'header' => 'Content-Type: text/plain',
 | 
			
		||||
                'header' =>
 | 
			
		||||
                    "Content-Type: text/plain\r\n" .
 | 
			
		||||
                    "Title: $title\r\n",
 | 
			
		||||
                'content' => $message
 | 
			
		||||
            ]
 | 
			
		||||
        ]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								src/Rclone/Rclone.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/Rclone/Rclone.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace App\Rclone;
 | 
			
		||||
 | 
			
		||||
use Symfony\Component\Process\Process;
 | 
			
		||||
use Symfony\Component\Process\Exception\ProcessFailedException;
 | 
			
		||||
 | 
			
		||||
class Rclone {
 | 
			
		||||
    private $rclone_path;
 | 
			
		||||
    private $global_options = [];
 | 
			
		||||
 | 
			
		||||
    function __construct($rclone_path = "rclone")
 | 
			
		||||
    {
 | 
			
		||||
        $this->rclone_path = $rclone_path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getVersion()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->exec('--version');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getSize($path)
 | 
			
		||||
    {
 | 
			
		||||
        $output = $this->exec('size', ['--json', $path]);
 | 
			
		||||
        
 | 
			
		||||
        return (int)json_decode($output[0])->bytes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function copy($src, $dest, $bandwidth = null)
 | 
			
		||||
    {   
 | 
			
		||||
        $options = array();
 | 
			
		||||
 | 
			
		||||
        $options[] = $src;
 | 
			
		||||
        $options[] = $dest;
 | 
			
		||||
        if($bandwidth)
 | 
			
		||||
        {
 | 
			
		||||
            $options[] = "--bwlimit";
 | 
			
		||||
            $options[] = $bandwidth;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->exec('copy', $options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function exec(string $command, array $options = array())
 | 
			
		||||
    {   
 | 
			
		||||
        $process = new Process(array_merge([$this->rclone_path], $this->global_options, [$command], $options));
 | 
			
		||||
        $process->setTimeout(4*3600);
 | 
			
		||||
        $process->run();
 | 
			
		||||
 | 
			
		||||
        // executes after the command finishes
 | 
			
		||||
        if (!$process->isSuccessful()) {
 | 
			
		||||
            throw new ProcessFailedException($process);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $process->getOutput();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								src/Twig/AppExtension.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/Twig/AppExtension.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Twig;
 | 
			
		||||
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerInterface;
 | 
			
		||||
use Twig\Extension\AbstractExtension;
 | 
			
		||||
use Twig\TwigFilter;
 | 
			
		||||
 | 
			
		||||
class AppExtension extends AbstractExtension
 | 
			
		||||
{
 | 
			
		||||
    public function getFilters()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
            new TwigFilter('formatBytes', array($this, 'formatBytes')),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param $bytes
 | 
			
		||||
     * @param int $precision
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function formatBytes($bytes, $precision = 2)
 | 
			
		||||
    {
 | 
			
		||||
        $size = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
 | 
			
		||||
        $factor = floor((strlen($bytes) - 1) / 3);
 | 
			
		||||
        return sprintf("%.{$precision}f", $bytes / pow(1024, $factor)) . @$size[$factor];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user