mirror of
https://github.com/demostf/backup.git
synced 2026-06-03 14:04:10 +02:00
initial backup script
This commit is contained in:
commit
d5896cc6f3
6 changed files with 147 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.env
|
||||||
|
vendor
|
||||||
|
composer.lock
|
||||||
27
backup.php
Normal file
27
backup.php
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
require 'vendor/autoload.php';
|
||||||
|
|
||||||
|
if (!getenv('STORAGE_ROOT')) {
|
||||||
|
$env = new \Dotenv\Dotenv(__DIR__);
|
||||||
|
$env->load();
|
||||||
|
}
|
||||||
|
|
||||||
|
$store = new \Demostf\Backup\Store(getenv('STORAGE_ROOT'));
|
||||||
|
$source = new \Demostf\Backup\Source(getenv('SOURCE'));
|
||||||
|
$statePath = getenv('STATE_FILE');
|
||||||
|
|
||||||
|
|
||||||
|
if (file_exists($statePath)) {
|
||||||
|
$lastPage = intval(file_get_contents($statePath));
|
||||||
|
if (!$lastPage) {
|
||||||
|
$lastPage = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$lastPage = 1;
|
||||||
|
}
|
||||||
|
$backup = new \Demostf\Backup\Backup($source, $store);
|
||||||
|
|
||||||
|
$currentPage = $backup->backupFrom($lastPage);
|
||||||
|
|
||||||
|
file_put_contents($statePath, $currentPage);
|
||||||
11
composer.json
Normal file
11
composer.json
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Demostf\\Backup\\": "src/",
|
||||||
|
"Demostf\\Backup\\Test\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"vlucas/phpdotenv": "^2.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/Backup.php
Normal file
50
src/Backup.php
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Demostf\Backup;
|
||||||
|
|
||||||
|
class Backup {
|
||||||
|
const SUCCESS = 1;
|
||||||
|
const FAIL = -1;
|
||||||
|
const EMPTY_PAGE = 0;
|
||||||
|
|
||||||
|
private $source;
|
||||||
|
|
||||||
|
private $store;
|
||||||
|
|
||||||
|
public function __construct(Source $source, Store $store) {
|
||||||
|
$this->source = $source;
|
||||||
|
$this->store = $store;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function backupDemo(string $name, string $url, string $hash): bool {
|
||||||
|
$handle = fopen($url, 'r');
|
||||||
|
$storedHash = $this->store->store($name, $handle);
|
||||||
|
return $hash === '' || $hash === $storedHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function backupPage(int $page): int {
|
||||||
|
$demos = $this->source->listDemos($page, 'ASC');
|
||||||
|
if (count($demos) === 0) {
|
||||||
|
return self::EMPTY_PAGE;
|
||||||
|
}
|
||||||
|
foreach ($demos as $demo) {
|
||||||
|
$url = $demo['url'];
|
||||||
|
$name = basename($url);
|
||||||
|
if (!$this->store->exists($name)) {
|
||||||
|
if (!$this->backupDemo($name, $url, $demo['hash'])) {
|
||||||
|
echo "hash mismatch\n";
|
||||||
|
return self::FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function backupFrom(int $startPage): int {
|
||||||
|
$currentPage = $startPage;
|
||||||
|
while ($this->backupPage($currentPage) === self::SUCCESS) {
|
||||||
|
$currentPage++;
|
||||||
|
}
|
||||||
|
return $currentPage;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/Source.php
Normal file
23
src/Source.php
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Demostf\Backup;
|
||||||
|
|
||||||
|
class Source {
|
||||||
|
private $endpoint;
|
||||||
|
|
||||||
|
public function __construct($endpoint) {
|
||||||
|
$this->endpoint = $endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listDemos(int $page = 0, string $order = 'DESC'): array {
|
||||||
|
$url = $this->endpoint . '/demos?page=' . $page . '&order=' . $order;
|
||||||
|
$content = file_get_contents($url);
|
||||||
|
$demos = json_decode($content, true);
|
||||||
|
foreach ($demos as $data) {
|
||||||
|
$date = new \DateTime();
|
||||||
|
$date->setTimestamp($data['time']);
|
||||||
|
$data['time'] = $date;
|
||||||
|
}
|
||||||
|
return $demos;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/Store.php
Normal file
33
src/Store.php
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Demostf\Backup;
|
||||||
|
|
||||||
|
class Store {
|
||||||
|
private $baseDir;
|
||||||
|
|
||||||
|
public function __construct($baseDir) {
|
||||||
|
$this->baseDir = $baseDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(string $name, $handle): string {
|
||||||
|
$path = $this->generatePath($name);
|
||||||
|
if (!is_dir(dirname($path))) {
|
||||||
|
mkdir(dirname($path), 0775, true);
|
||||||
|
}
|
||||||
|
file_put_contents($path, $handle);
|
||||||
|
echo "$name\n";
|
||||||
|
return md5_file($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generatePath(string $name): string {
|
||||||
|
return $this->baseDir . $this->getPrefix($name) . $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPrefix(string $name): string {
|
||||||
|
return '/' . substr($name, 0, 2) . '/' . substr($name, 2, 2) . '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exists(string $name): bool {
|
||||||
|
return file_exists($this->generatePath($name));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue