166 lines
5.1 KiB
PHP
166 lines
5.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Bnomei;
|
|
|
|
use Kirby\Cms\Page;
|
|
use Kirby\Toolkit\A;
|
|
use Kirby\Toolkit\Dir;
|
|
use Kirby\Toolkit\F;
|
|
use Symfony\Component\Finder\Finder;
|
|
use ZipArchive;
|
|
|
|
final class BackupZipJob extends JanitorJob
|
|
{
|
|
/** @var array */
|
|
private $options;
|
|
|
|
public function __construct(?Page $page = null, ?string $data = null)
|
|
{
|
|
parent::__construct($page, $data);
|
|
|
|
$this->options = [
|
|
'ulimit' => option('bnomei.janitor.backupzip.ulimit', 512), // 1024 seems to be unix default
|
|
'date' => option('bnomei.janitor.backupzip.date', null), // null to disable, 'since 1 day ago'
|
|
'roots' => option('bnomei.janitor.backupzip.roots', function () {
|
|
return [
|
|
kirby()->roots()->accounts(),
|
|
kirby()->roots()->content(),
|
|
];
|
|
}),
|
|
'target' => option('bnomei.janitor.backupzip.target', function () {
|
|
$dir = realpath(kirby()->roots()->accounts() . '/../') . '/backups';
|
|
Dir::make($dir);
|
|
$prefix = option('bnomei.janitor.backupzip.prefix', '');
|
|
return $dir . '/' . $prefix . time() . '.zip'; // date('Y-m-d')
|
|
}),
|
|
];
|
|
|
|
foreach ($this->options as $key => $value) {
|
|
if (!is_string($value) && is_callable($value)) {
|
|
$this->options[$key] = $value();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string|null $key
|
|
* @return array
|
|
*/
|
|
public function option(?string $key = null)
|
|
{
|
|
if ($key) {
|
|
return A::get($this->options, $key);
|
|
}
|
|
return $this->options;
|
|
}
|
|
|
|
public static function directory(): string
|
|
{
|
|
return \dirname((string) (new self())->option('target'));
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function job(): array
|
|
{
|
|
$time = time();
|
|
$climate = \Bnomei\Janitor::climate();
|
|
|
|
$zipPath = (string) $this->option('target');
|
|
$zip = new ZipArchive();
|
|
if ($zip->open($zipPath, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) !== true) {
|
|
if ($climate) {
|
|
$climate->red('Failed to create: ' . $zipPath);
|
|
}
|
|
return [
|
|
'status' => 500,
|
|
];
|
|
}
|
|
|
|
$roots = $this->option('roots');
|
|
$finder = new Finder();
|
|
$finder->files()->in($roots);
|
|
if ($date = $this->option('date')) {
|
|
$finder->date($date);
|
|
}
|
|
$count = iterator_count($finder); // closing of zip
|
|
|
|
$ulimit = $this->option('ulimit');
|
|
$zipped = 0;
|
|
if ($climate) {
|
|
$climate->out('Files: ' . $count);
|
|
}
|
|
|
|
$progress = null;
|
|
if ($count && $climate) {
|
|
$progress = $climate->progress()->total($count);
|
|
}
|
|
|
|
foreach ($finder as $file) {
|
|
$filePath = $file->getPath() . DIRECTORY_SEPARATOR . $file->getFilename();
|
|
$localFilePath = $filePath;
|
|
foreach ($roots as $root) {
|
|
$localFilePath = str_replace(dirname($root), '', $localFilePath);
|
|
}
|
|
if ($zip->addFile($filePath, $localFilePath)) {
|
|
$mime = F::mime($filePath);
|
|
|
|
if (in_array($mime, [
|
|
'application/json', 'text/json',
|
|
'application/yaml', 'text/yaml',
|
|
'text/html',
|
|
'text/plain',
|
|
'text/xml',
|
|
'application/x-javascript',
|
|
'text/css',
|
|
'text/csv', 'text/x-comma-separated-values',
|
|
'text/comma-separated-values', 'application/octet-stream',
|
|
])) {
|
|
$zip->setCompressionName($filePath, ZipArchive::CM_DEFLATE);
|
|
} else {
|
|
$zip->setCompressionName($filePath, ZipArchive::CM_STORE);
|
|
}
|
|
|
|
$zipped++;
|
|
|
|
if ($progress && $climate) {
|
|
$progress->current($zipped);
|
|
}
|
|
if ($zipped % $ulimit === 0) {
|
|
$zip->close();
|
|
if ($zip->open($zipPath) === false) {
|
|
@unlink($zipPath);
|
|
if ($climate) {
|
|
$climate->red('Hit ulimit but failed to reopen zip: ' . $zipPath);
|
|
}
|
|
return [
|
|
'status' => 500,
|
|
'error' => 'Hit ulimit but failed to reopen zip: ' . $zipPath,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($climate) {
|
|
$climate->out('Closing zip...');
|
|
}
|
|
$zip->close();
|
|
if ($climate) {
|
|
$climate->out($zipPath);
|
|
}
|
|
|
|
return [
|
|
'status' => $zipped > 0 ? 200 : 204,
|
|
'duration' => time() - $time,
|
|
'filename' => \basename($zipPath, '.zip'),
|
|
'files' => $zipped,
|
|
'nicesize' => \Kirby\Toolkit\F::niceSize($zipPath),
|
|
'modified' => date('d/m/Y, H:i:s', \Kirby\Toolkit\F::modified($zipPath)),
|
|
];
|
|
}
|
|
}
|