diff --git a/app/Jobs/Schedule/RunTaskJob.php b/app/Jobs/Schedule/RunTaskJob.php index 8784a37cb..ee3c3e16d 100644 --- a/app/Jobs/Schedule/RunTaskJob.php +++ b/app/Jobs/Schedule/RunTaskJob.php @@ -69,7 +69,7 @@ class RunTaskJob extends Job implements ShouldQueue $commandRepository->setServer($server)->send($this->task->payload); break; case 'backup': - $backupService->setIgnoredFiles(explode(PHP_EOL, $this->task->payload))->handle($server, null); + $backupService->setIgnoredFiles(explode(PHP_EOL, $this->task->payload))->handle($server, null, true); break; default: throw new InvalidArgumentException('Cannot run a task that points to a non-existent action.'); diff --git a/app/Services/Backups/InitiateBackupService.php b/app/Services/Backups/InitiateBackupService.php index ca8c3decd..5c34961a4 100644 --- a/app/Services/Backups/InitiateBackupService.php +++ b/app/Services/Backups/InitiateBackupService.php @@ -13,6 +13,7 @@ use Pterodactyl\Repositories\Eloquent\BackupRepository; use Pterodactyl\Repositories\Wings\DaemonBackupRepository; use Pterodactyl\Exceptions\Service\Backup\TooManyBackupsException; use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; +use Pterodactyl\Services\Backups\DeleteBackupService; class InitiateBackupService { @@ -41,24 +42,32 @@ class InitiateBackupService */ private $backupManager; + /** + * @var \Pterodactyl\Services\Backups\DeleteBackupService + */ + private $deleteBackupService; + /** * InitiateBackupService constructor. * * @param \Pterodactyl\Repositories\Eloquent\BackupRepository $repository * @param \Illuminate\Database\ConnectionInterface $connection * @param \Pterodactyl\Repositories\Wings\DaemonBackupRepository $daemonBackupRepository + * @param \Pterodactyl\Services\Backups\DeleteBackupService $deleteBackupService * @param \Pterodactyl\Extensions\Backups\BackupManager $backupManager */ public function __construct( BackupRepository $repository, ConnectionInterface $connection, DaemonBackupRepository $daemonBackupRepository, + DeleteBackupService $deleteBackupService, BackupManager $backupManager ) { $this->repository = $repository; $this->connection = $connection; $this->daemonBackupRepository = $daemonBackupRepository; $this->backupManager = $backupManager; + $this->deleteBackupService = $deleteBackupService; } /** @@ -96,13 +105,8 @@ class InitiateBackupService * @throws \Pterodactyl\Exceptions\Service\Backup\TooManyBackupsException * @throws \Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException */ - public function handle(Server $server, string $name = null): Backup + public function handle(Server $server, string $name = null, bool $override = false): Backup { - // Do not allow the user to continue if this server is already at its limit. - if (! $server->backup_limit || $server->backups()->where('is_successful', true)->count() >= $server->backup_limit) { - throw new TooManyBackupsException($server->backup_limit); - } - $previous = $this->repository->getBackupsGeneratedDuringTimespan($server->id, 10); if ($previous->count() >= 2) { throw new TooManyRequestsHttpException( @@ -111,6 +115,18 @@ class InitiateBackupService ); } + // Check if the server has reached or exceeded it's backup limit + if (!$server->backup_limit || $server->backups()->where('is_successful', true)->count() >= $server->backup_limit) { + // Do not allow the user to continue if this server is already at its limit and can't override. + if (!$override || $server->backup_limit <= 0) { + throw new TooManyBackupsException($server->backup_limit); + } + + // Remove oldest backup + $oldestBackup = $server->backups()->where('is_successful', true)->orderByDesc('created_at')->first(); + $this->deleteBackupService->handle($oldestBackup); + } + return $this->connection->transaction(function () use ($server, $name) { /** @var \Pterodactyl\Models\Backup $backup */ $backup = $this->repository->create([ diff --git a/resources/scripts/components/server/databases/CreateDatabaseButton.tsx b/resources/scripts/components/server/databases/CreateDatabaseButton.tsx index 2ce843365..d70faffba 100644 --- a/resources/scripts/components/server/databases/CreateDatabaseButton.tsx +++ b/resources/scripts/components/server/databases/CreateDatabaseButton.tsx @@ -24,7 +24,7 @@ const schema = object().shape({ .matches(/^[A-Za-z0-9_\-.]{3,48}$/, 'Database name should only contain alphanumeric characters, underscores, dashes, and/or periods.'), connectionsFrom: string() .required('A connection value must be provided.') - .matches(/^([1-9]{1,3}|%)(\.([0-9]{1,3}|%))?(\.([0-9]{1,3}|%))?(\.([0-9]{1,3}|%))?$/, 'A valid connection address must be provided.'), + .matches(/^([0-9]{1,3}|%)(\.([0-9]{1,3}|%))?(\.([0-9]{1,3}|%))?(\.([0-9]{1,3}|%))?$/, 'A valid connection address must be provided.'), }); export default () => { diff --git a/resources/scripts/components/server/databases/DatabaseRow.tsx b/resources/scripts/components/server/databases/DatabaseRow.tsx index 3eeaa981e..952a7b746 100644 --- a/resources/scripts/components/server/databases/DatabaseRow.tsx +++ b/resources/scripts/components/server/databases/DatabaseRow.tsx @@ -131,7 +131,7 @@ export default ({ database, className }: Props) => {