Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/app/Services/Backups/InitiateBackupService.php
10260 views
1
<?php
2
3
namespace Pterodactyl\Services\Backups;
4
5
use Ramsey\Uuid\Uuid;
6
use Carbon\CarbonImmutable;
7
use Webmozart\Assert\Assert;
8
use Pterodactyl\Models\Backup;
9
use Pterodactyl\Models\Server;
10
use Illuminate\Database\ConnectionInterface;
11
use Pterodactyl\Extensions\Backups\BackupManager;
12
use Pterodactyl\Repositories\Eloquent\BackupRepository;
13
use Pterodactyl\Repositories\Wings\DaemonBackupRepository;
14
use Pterodactyl\Exceptions\Service\Backup\TooManyBackupsException;
15
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
16
17
class InitiateBackupService
18
{
19
private array $ignoredFiles = [];
20
21
private bool $isLocked = false;
22
23
/**
24
* InitiateBackupService constructor.
25
*/
26
public function __construct(
27
private BackupRepository $repository,
28
private ConnectionInterface $connection,
29
private DaemonBackupRepository $daemonBackupRepository,
30
private DeleteBackupService $deleteBackupService,
31
private BackupManager $backupManager,
32
) {
33
}
34
35
/**
36
* Set if the backup should be locked once it is created which will prevent
37
* its deletion by users or automated system processes.
38
*/
39
public function setIsLocked(bool $isLocked): self
40
{
41
$this->isLocked = $isLocked;
42
43
return $this;
44
}
45
46
/**
47
* Sets the files to be ignored by this backup.
48
*
49
* @param string[]|null $ignored
50
*/
51
public function setIgnoredFiles(?array $ignored): self
52
{
53
if (is_array($ignored)) {
54
foreach ($ignored as $value) {
55
Assert::string($value); // @phpstan-ignore staticMethod.alreadyNarrowedType
56
}
57
}
58
59
// Set the ignored files to be any values that are not empty in the array. Don't use
60
// the PHP empty function here incase anything that is "empty" by default (0, false, etc.)
61
// were passed as a file or folder name.
62
$this->ignoredFiles = is_null($ignored) ? [] : array_filter($ignored, function ($value) {
63
return strlen($value) > 0;
64
});
65
66
return $this;
67
}
68
69
/**
70
* Initiates the backup process for a server on Wings.
71
*
72
* @throws \Throwable
73
* @throws TooManyBackupsException
74
* @throws TooManyRequestsHttpException
75
*/
76
public function handle(Server $server, ?string $name = null, bool $override = false): Backup
77
{
78
$limit = config('backups.throttles.limit');
79
$period = config('backups.throttles.period');
80
if ($period > 0) {
81
$previous = $this->repository->getBackupsGeneratedDuringTimespan($server->id, $period);
82
if ($previous->count() >= $limit) {
83
$message = sprintf('Only %d backups may be generated within a %d second span of time.', $limit, $period);
84
85
throw new TooManyRequestsHttpException((int) CarbonImmutable::now()->diffInSeconds($previous->last()->created_at->addSeconds($period)), $message);
86
}
87
}
88
89
// Check if the server has reached or exceeded its backup limit.
90
// completed_at == null will cover any ongoing backups, while is_successful == true will cover any completed backups.
91
$successful = $this->repository->getNonFailedBackups($server);
92
if (!$server->backup_limit || $successful->count() >= $server->backup_limit) {
93
// Do not allow the user to continue if this server is already at its limit and can't override.
94
if (!$override || $server->backup_limit <= 0) {
95
throw new TooManyBackupsException($server->backup_limit);
96
}
97
98
// Get the oldest backup the server has that is not "locked" (indicating a backup that should
99
// never be automatically purged). If we find a backup we will delete it and then continue with
100
// this process. If no backup is found that can be used an exception is thrown.
101
$oldest = $successful->where('is_locked', false)->orderBy('created_at')->first();
102
if (!$oldest) {
103
throw new TooManyBackupsException($server->backup_limit);
104
}
105
106
$this->deleteBackupService->handle($oldest);
107
}
108
109
return $this->connection->transaction(function () use ($server, $name) {
110
/** @var Backup $backup */
111
$backup = $this->repository->create([
112
'server_id' => $server->id,
113
'uuid' => Uuid::uuid4()->toString(),
114
'name' => trim($name) ?: sprintf('Backup at %s', CarbonImmutable::now()->toDateTimeString()),
115
'ignored_files' => array_values($this->ignoredFiles),
116
'disk' => $this->backupManager->getDefaultAdapter(),
117
'is_locked' => $this->isLocked,
118
], true, true);
119
120
$this->daemonBackupRepository->setServer($server)
121
->setBackupAdapter($this->backupManager->getDefaultAdapter())
122
->backup($backup);
123
124
return $backup;
125
});
126
}
127
}
128
129