From 1f01c653f162809bdf5864f84506fc5dec0b9718 Mon Sep 17 00:00:00 2001 From: Gonzalo Chavez Date: Mon, 9 Nov 2020 20:35:57 -0300 Subject: [PATCH 01/17] Backup rotation for schedules. --- app/Jobs/Schedule/RunTaskJob.php | 2 +- .../Backups/InitiateBackupService.php | 31 +++++++++++++------ .../server/schedules/TaskDetailsModal.tsx | 2 +- 3 files changed, 24 insertions(+), 11 deletions(-) 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..9b6ae9a6f 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 = null): 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,17 @@ class InitiateBackupService ); } + if (! $server->backup_limit || $server->backups()->where('is_successful', true)->count() >= $server->backup_limit) { + if($override){ + // Remove latest backup + $last_backup = $server->backups()->where('is_successful', true)->oldest()->first(); + $this->deleteBackupService->handle($last_backup); + }else{ + // Do not allow the user to continue if this server is already at its limit. + throw new TooManyBackupsException($server->backup_limit); + } + } + return $this->connection->transaction(function () use ($server, $name) { /** @var \Pterodactyl\Models\Backup $backup */ $backup = $this->repository->create([ @@ -121,9 +136,7 @@ class InitiateBackupService 'disk' => $this->backupManager->getDefaultAdapter(), ], true, true); - $this->daemonBackupRepository->setServer($server) - ->setBackupAdapter($this->backupManager->getDefaultAdapter()) - ->backup($backup); + $this->daemonBackupRepository->setServer($server)->setBackupAdapter($this->backupManager->getDefaultAdapter())->backup($backup); return $backup; }); diff --git a/resources/scripts/components/server/schedules/TaskDetailsModal.tsx b/resources/scripts/components/server/schedules/TaskDetailsModal.tsx index e1fca0cb0..5ff75555d 100644 --- a/resources/scripts/components/server/schedules/TaskDetailsModal.tsx +++ b/resources/scripts/components/server/schedules/TaskDetailsModal.tsx @@ -91,7 +91,7 @@ const TaskDetailsForm = ({ isEditingTask }: { isEditingTask: boolean }) => { From 1eaf486eaa944f30741a95db997bbea725ec5b13 Mon Sep 17 00:00:00 2001 From: Gonzalo Chavez Date: Mon, 9 Nov 2020 21:14:47 -0300 Subject: [PATCH 02/17] Backup Rotation - Minor Changes --- app/Services/Backups/InitiateBackupService.php | 11 +++++++---- .../components/server/schedules/TaskDetailsModal.tsx | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/Services/Backups/InitiateBackupService.php b/app/Services/Backups/InitiateBackupService.php index 9b6ae9a6f..3246328c9 100644 --- a/app/Services/Backups/InitiateBackupService.php +++ b/app/Services/Backups/InitiateBackupService.php @@ -105,7 +105,7 @@ class InitiateBackupService * @throws \Pterodactyl\Exceptions\Service\Backup\TooManyBackupsException * @throws \Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException */ - public function handle(Server $server, string $name = null, bool $override = null): Backup + public function handle(Server $server, string $name = null, bool $override = false): Backup { $previous = $this->repository->getBackupsGeneratedDuringTimespan($server->id, 10); if ($previous->count() >= 2) { @@ -115,12 +115,13 @@ 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) { - if($override){ + if($override) { // Remove latest backup $last_backup = $server->backups()->where('is_successful', true)->oldest()->first(); $this->deleteBackupService->handle($last_backup); - }else{ + } else { // Do not allow the user to continue if this server is already at its limit. throw new TooManyBackupsException($server->backup_limit); } @@ -136,7 +137,9 @@ class InitiateBackupService 'disk' => $this->backupManager->getDefaultAdapter(), ], true, true); - $this->daemonBackupRepository->setServer($server)->setBackupAdapter($this->backupManager->getDefaultAdapter())->backup($backup); + $this->daemonBackupRepository->setServer($server) + ->setBackupAdapter($this->backupManager->getDefaultAdapter()) + ->backup($backup); return $backup; }); diff --git a/resources/scripts/components/server/schedules/TaskDetailsModal.tsx b/resources/scripts/components/server/schedules/TaskDetailsModal.tsx index 5ff75555d..65c647219 100644 --- a/resources/scripts/components/server/schedules/TaskDetailsModal.tsx +++ b/resources/scripts/components/server/schedules/TaskDetailsModal.tsx @@ -91,7 +91,7 @@ const TaskDetailsForm = ({ isEditingTask }: { isEditingTask: boolean }) => { From 7b8322e9c9ff978d4a608a6c85dd0c59f5fb16a6 Mon Sep 17 00:00:00 2001 From: Gonzalo Chavez Date: Wed, 11 Nov 2020 10:52:28 -0300 Subject: [PATCH 03/17] Backup Rotation - Minor changes --- app/Services/Backups/InitiateBackupService.php | 16 ++++++++-------- .../server/schedules/TaskDetailsModal.tsx | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/Services/Backups/InitiateBackupService.php b/app/Services/Backups/InitiateBackupService.php index 3246328c9..aa53b88ae 100644 --- a/app/Services/Backups/InitiateBackupService.php +++ b/app/Services/Backups/InitiateBackupService.php @@ -116,16 +116,16 @@ 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) { - if($override) { - // Remove latest backup - $last_backup = $server->backups()->where('is_successful', true)->oldest()->first(); - $this->deleteBackupService->handle($last_backup); - } else { - // 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) { + // 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 latest backup + $lastBackup = $server->backups()->where('is_successful', true)->orderByDesc('created_at')->first(); + $this->deleteBackupService->handle($lastBackup); + } return $this->connection->transaction(function () use ($server, $name) { /** @var \Pterodactyl\Models\Backup $backup */ diff --git a/resources/scripts/components/server/schedules/TaskDetailsModal.tsx b/resources/scripts/components/server/schedules/TaskDetailsModal.tsx index 65c647219..08f33b161 100644 --- a/resources/scripts/components/server/schedules/TaskDetailsModal.tsx +++ b/resources/scripts/components/server/schedules/TaskDetailsModal.tsx @@ -91,7 +91,7 @@ const TaskDetailsForm = ({ isEditingTask }: { isEditingTask: boolean }) => { From e6a4a179226b1e745134624019b56c55235f411d Mon Sep 17 00:00:00 2001 From: Gonzalo Chavez Date: Wed, 11 Nov 2020 16:02:39 -0300 Subject: [PATCH 04/17] Backup Rotation - Comment change --- app/Services/Backups/InitiateBackupService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Services/Backups/InitiateBackupService.php b/app/Services/Backups/InitiateBackupService.php index aa53b88ae..89caf8cdc 100644 --- a/app/Services/Backups/InitiateBackupService.php +++ b/app/Services/Backups/InitiateBackupService.php @@ -122,7 +122,7 @@ class InitiateBackupService throw new TooManyBackupsException($server->backup_limit); } - // Remove latest backup + // Remove oldest backup $lastBackup = $server->backups()->where('is_successful', true)->orderByDesc('created_at')->first(); $this->deleteBackupService->handle($lastBackup); } From ebc8d40db83d7da7d900278584211186ddc43af3 Mon Sep 17 00:00:00 2001 From: Gonzalo Chavez Date: Wed, 11 Nov 2020 16:03:57 -0300 Subject: [PATCH 05/17] Backup Rotation - Variable name changed --- app/Services/Backups/InitiateBackupService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Services/Backups/InitiateBackupService.php b/app/Services/Backups/InitiateBackupService.php index 89caf8cdc..5c34961a4 100644 --- a/app/Services/Backups/InitiateBackupService.php +++ b/app/Services/Backups/InitiateBackupService.php @@ -123,8 +123,8 @@ class InitiateBackupService } // Remove oldest backup - $lastBackup = $server->backups()->where('is_successful', true)->orderByDesc('created_at')->first(); - $this->deleteBackupService->handle($lastBackup); + $oldestBackup = $server->backups()->where('is_successful', true)->orderByDesc('created_at')->first(); + $this->deleteBackupService->handle($oldestBackup); } return $this->connection->transaction(function () use ($server, $name) { From 4b1f3200049a5948a0df8dfb5c971b63ae0e4ee3 Mon Sep 17 00:00:00 2001 From: Josh Miles <19347018+j00005h@users.noreply.github.com> Date: Mon, 16 Nov 2020 13:51:02 +0000 Subject: [PATCH 06/17] Update index.blade.php --- resources/views/admin/nodes/view/index.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/admin/nodes/view/index.blade.php b/resources/views/admin/nodes/view/index.blade.php index 993f233dc..13d62e9ee 100644 --- a/resources/views/admin/nodes/view/index.blade.php +++ b/resources/views/admin/nodes/view/index.blade.php @@ -51,7 +51,7 @@ - Total CPU Cores + Total CPU Threads From bdad2621b262fad7eab4a5ea12633d31da313bec Mon Sep 17 00:00:00 2001 From: Charles Morgan Date: Sun, 22 Nov 2020 03:06:18 -0500 Subject: [PATCH 07/17] Rename duplicated input id Renames the field id so its unique and chrome dosen't complain <3 Closes https://github.com/pterodactyl/panel/issues/2710 --- .../scripts/components/dashboard/forms/UpdatePasswordForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx b/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx index 7dec924a5..1c91ef4d9 100644 --- a/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx +++ b/resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx @@ -77,7 +77,7 @@ export default () => {
Date: Tue, 24 Nov 2020 22:48:27 +0300 Subject: [PATCH 08/17] Fixed an error when trying to install dependencies without a .env file --- config/database.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/database.php b/config/database.php index 5a4e8bc77..e69e19ed0 100644 --- a/config/database.php +++ b/config/database.php @@ -45,7 +45,7 @@ return [ 'collation' => 'utf8mb4_unicode_ci', 'prefix' => env('DB_PREFIX', ''), 'strict' => env('DB_STRICT_MODE', false), - 'timezone' => env('DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE'))) + 'timezone' => env('DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE', 'UTC'))) ], /* @@ -68,7 +68,7 @@ return [ 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => false, - 'timezone' => env('DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE'))) + 'timezone' => env('DB_TIMEZONE', Time::getMySQLTimezoneOffset(env('APP_TIMEZONE', 'UTC'))) ], ], From 2a54ac4f52c77a69aed427d039782adcecb555d1 Mon Sep 17 00:00:00 2001 From: m0uka Date: Tue, 24 Nov 2020 22:04:44 +0100 Subject: [PATCH 09/17] Add clickable URLs in console --- package.json | 1 + resources/scripts/components/server/Console.tsx | 3 +++ yarn.lock | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/package.json b/package.json index 2f947b532..e62c7ea55 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "xterm-addon-fit": "^0.4.0", "xterm-addon-search": "^0.7.0", "xterm-addon-search-bar": "^0.2.0", + "xterm-addon-web-links": "^0.4.0", "yup": "^0.29.1" }, "devDependencies": { diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index c14354a75..f2710de87 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -3,6 +3,7 @@ import { ITerminalOptions, Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; import { SearchAddon } from 'xterm-addon-search'; import { SearchBarAddon } from 'xterm-addon-search-bar'; +import { WebLinksAddon } from 'xterm-addon-web-links'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; import { ServerContext } from '@/state/server'; import styled from 'styled-components/macro'; @@ -62,6 +63,7 @@ export default () => { const fitAddon = new FitAddon(); const searchAddon = new SearchAddon(); const searchBar = new SearchBarAddon({ searchAddon }); + const webLinksAddon = new WebLinksAddon(); const { connected, instance } = ServerContext.useStoreState(state => state.socket); const [ canSendCommands ] = usePermissions([ 'control.console' ]); const serverId = ServerContext.useStoreState(state => state.server.data!.id); @@ -115,6 +117,7 @@ export default () => { terminal.loadAddon(fitAddon); terminal.loadAddon(searchAddon); terminal.loadAddon(searchBar); + terminal.loadAddon(webLinksAddon); fitAddon.fit(); // Add support for capturing keys diff --git a/yarn.lock b/yarn.lock index fff07b1d4..28348823b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7621,6 +7621,11 @@ xterm-addon-search@^0.7.0: resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0.tgz#c929d3e5cbb335e82bff72f158ea82936d9cd4ef" integrity sha512-6060evmJJ+tZcjnx33FXaeEHLpuXEa7l9UzUsYfMlCKbu88AbE+5LJocTKCHYd71cwCwb9pjmv/G1o9Rf9Zbcg== +xterm-addon-web-links@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.4.0.tgz#265cbf8221b9b315d0a748e1323bee331cd5da03" + integrity sha512-xv8GeiINmx0zENO9hf5k+5bnkaE8mRzF+OBAr9WeFq2eLaQSudioQSiT34M1ofKbzcdjSsKiZm19Rw3i4eXamg== + xterm@^4.9.0: version "4.9.0" resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0.tgz#7a4c097a433d565339b5533b468bbc60c6c87969" From 76958cc6e7ee8692f9f8ba36dc44b515afd719e2 Mon Sep 17 00:00:00 2001 From: booky10 Date: Thu, 26 Nov 2020 17:59:29 +0100 Subject: [PATCH 10/17] Changed "Mojang" to "Minecraft" in EulaModalFeature.tsx --- .../components/server/features/eula/EulaModalFeature.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/scripts/components/server/features/eula/EulaModalFeature.tsx b/resources/scripts/components/server/features/eula/EulaModalFeature.tsx index d48eef1fa..6453a5097 100644 --- a/resources/scripts/components/server/features/eula/EulaModalFeature.tsx +++ b/resources/scripts/components/server/features/eula/EulaModalFeature.tsx @@ -62,7 +62,7 @@ const EulaModalFeature = () => { : setVisible(false)} closeOnBackground={false} showSpinnerOverlay={loading}> -

Accept Minecraft® EULA

+

Accept Minecraft® EULA

By pressing {'"I Accept"'} below you are indicating your agreement to the  { rel={'noreferrer noopener'} href="https://account.mojang.com/documents/minecraft_eula" > - Mojang EULA + Minecraft® EULA .

From 26d409c29ca277f7e77b7e89b8afc53112c71f52 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Nov 2020 11:50:20 -0800 Subject: [PATCH 11/17] Don't try to flip null values; closes #2753 --- ...03_22_163911_merge_permissions_table_into_subusers.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php b/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php index 67461ecc8..f46481b47 100644 --- a/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php +++ b/database/migrations/2020_03_22_163911_merge_permissions_table_into_subusers.php @@ -83,10 +83,10 @@ class MergePermissionsTableIntoSubusers extends Migration ->map(function ($value) { return self::$permissionsMap[$value] ?? null; })->filter(function ($value) { - return !is_null($value) && $value !== Permission::ACTION_WEBSOCKET_CONNECT; + return ! is_null($value) && $value !== Permission::ACTION_WEBSOCKET_CONNECT; }) // All subusers get this permission, so make sure it gets pushed into the array. - ->merge([ Permission::ACTION_WEBSOCKET_CONNECT ]) + ->merge([Permission::ACTION_WEBSOCKET_CONNECT]) ->unique() ->values() ->toJson(); @@ -103,12 +103,12 @@ class MergePermissionsTableIntoSubusers extends Migration */ public function down() { - $flipped = array_flip(self::$permissionsMap); + $flipped = array_flip(array_filter(self::$permissionsMap)); foreach (DB::select('SELECT id, permissions FROM subusers') as $datum) { $values = []; foreach (json_decode($datum->permissions, true) as $permission) { - if (!empty($v = $flipped[$permission])) { + if (! empty($v = $flipped[$permission])) { $values[] = $datum->id; $values[] = $v; } From 16f49f8dc1be84ed06441dc72eb8f4ced86c6e6b Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Nov 2020 12:50:22 -0800 Subject: [PATCH 12/17] Close cleanup; only try to run power actions against non-suspended & installed servers; closes #2760 --- CHANGELOG.md | 4 + .../Server/BulkPowerActionCommand.php | 81 ++++++++----------- .../Repository/ServerRepositoryInterface.php | 19 ----- .../Eloquent/ServerRepository.php | 39 --------- .../Server/BulkPowerActionCommandTest.php | 4 +- 5 files changed, 40 insertions(+), 107 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 936c2b6a0..cfc6c0f9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ This file is a running track of new features and fixes to each version of the pa This project follows [Semantic Versioning](http://semver.org) guidelines. +## v1.1.3 +### Fixed +* Server bulk power actions command will no longer attempt to run commands against installing or suspended servers. + ## v1.1.2 ### Fixed * Fixes an exception thrown while trying to validate IP access for the client API. diff --git a/app/Console/Commands/Server/BulkPowerActionCommand.php b/app/Console/Commands/Server/BulkPowerActionCommand.php index 383879902..32d9868b5 100644 --- a/app/Console/Commands/Server/BulkPowerActionCommand.php +++ b/app/Console/Commands/Server/BulkPowerActionCommand.php @@ -2,30 +2,15 @@ namespace Pterodactyl\Console\Commands\Server; +use Pterodactyl\Models\Server; use Illuminate\Console\Command; -use GuzzleHttp\Exception\RequestException; use Illuminate\Validation\ValidationException; use Illuminate\Validation\Factory as ValidatorFactory; use Pterodactyl\Repositories\Wings\DaemonPowerRepository; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; class BulkPowerActionCommand extends Command { - /** - * @var \Pterodactyl\Repositories\Wings\DaemonPowerRepository - */ - private $powerRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface - */ - private $repository; - - /** - * @var \Illuminate\Validation\Factory - */ - private $validator; - /** * @var string */ @@ -39,37 +24,20 @@ class BulkPowerActionCommand extends Command */ protected $description = 'Perform bulk power management on large groupings of servers or nodes at once.'; - /** - * BulkPowerActionCommand constructor. - * - * @param \Pterodactyl\Repositories\Wings\DaemonPowerRepository $powerRepository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - * @param \Illuminate\Validation\Factory $validator - */ - public function __construct( - DaemonPowerRepository $powerRepository, - ServerRepositoryInterface $repository, - ValidatorFactory $validator - ) { - parent::__construct(); - - $this->repository = $repository; - $this->validator = $validator; - $this->powerRepository = $powerRepository; - } - /** * Handle the bulk power request. * + * @param \Pterodactyl\Repositories\Wings\DaemonPowerRepository $powerRepository + * @param \Illuminate\Validation\Factory $validator * @throws \Illuminate\Validation\ValidationException */ - public function handle() + public function handle(DaemonPowerRepository $powerRepository, ValidatorFactory $validator) { $action = $this->argument('action'); $nodes = empty($this->option('nodes')) ? [] : explode(',', $this->option('nodes')); $servers = empty($this->option('servers')) ? [] : explode(',', $this->option('servers')); - $validator = $this->validator->make([ + $validator = $validator->make([ 'action' => $action, 'nodes' => $nodes, 'servers' => $servers, @@ -89,23 +57,18 @@ class BulkPowerActionCommand extends Command throw new ValidationException($validator); } - $count = $this->repository->getServersForPowerActionCount($servers, $nodes); + $count = $this->getQueryBuilder($servers, $nodes)->count(); if (! $this->confirm(trans('command/messages.server.power.confirm', ['action' => $action, 'count' => $count])) && $this->input->isInteractive()) { return; } $bar = $this->output->createProgressBar($count); - $servers = $this->repository->getServersForPowerAction($servers, $nodes); - - $servers->each(function ($server) use ($action, &$bar) { + $this->getQueryBuilder($servers, $nodes)->each(function (Server $server) use ($action, $powerRepository, &$bar) { $bar->clear(); try { - $this->powerRepository - ->setNode($server->node) - ->setServer($server) - ->send($action); - } catch (RequestException $exception) { + $powerRepository->setServer($server)->send($action); + } catch (DaemonConnectionException $exception) { $this->output->error(trans('command/messages.server.power.action_failed', [ 'name' => $server->name, 'id' => $server->id, @@ -120,4 +83,28 @@ class BulkPowerActionCommand extends Command $this->line(''); } + + /** + * Returns the query builder instance that will return the servers that should be affected. + * + * @param array $servers + * @param array $nodes + * @return \Illuminate\Database\Eloquent\Builder + */ + protected function getQueryBuilder(array $servers, array $nodes) + { + $instance = Server::query() + ->where('suspended', false) + ->where('installed', Server::STATUS_INSTALLED); + + if (! empty($nodes) && ! empty($servers)) { + $instance->whereIn('id', $servers)->orWhereIn('node_id', $nodes); + } else if (empty($nodes) && ! empty($servers)) { + $instance->whereIn('id', $servers); + } else if (! empty($nodes) && empty($servers)) { + $instance->whereIn('node_id', $nodes); + } + + return $instance->with('node'); + } } diff --git a/app/Contracts/Repository/ServerRepositoryInterface.php b/app/Contracts/Repository/ServerRepositoryInterface.php index d7e3dfc5d..55cf79a39 100644 --- a/app/Contracts/Repository/ServerRepositoryInterface.php +++ b/app/Contracts/Repository/ServerRepositoryInterface.php @@ -95,25 +95,6 @@ interface ServerRepositoryInterface extends RepositoryInterface */ public function getByUuid(string $uuid): Server; - /** - * Return all of the servers that should have a power action performed against them. - * - * @param int[] $servers - * @param int[] $nodes - * @param bool $returnCount - * @return int|\Illuminate\Support\LazyCollection - */ - public function getServersForPowerAction(array $servers = [], array $nodes = [], bool $returnCount = false); - - /** - * Return the total number of servers that will be affected by the query. - * - * @param int[] $servers - * @param int[] $nodes - * @return int - */ - public function getServersForPowerActionCount(array $servers = [], array $nodes = []): int; - /** * Check if a given UUID and UUID-Short string are unique to a server. * diff --git a/app/Repositories/Eloquent/ServerRepository.php b/app/Repositories/Eloquent/ServerRepository.php index fdfd82fc2..5c16664b6 100644 --- a/app/Repositories/Eloquent/ServerRepository.php +++ b/app/Repositories/Eloquent/ServerRepository.php @@ -194,45 +194,6 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt } } - /** - * Return all of the servers that should have a power action performed against them. - * - * @param int[] $servers - * @param int[] $nodes - * @param bool $returnCount - * @return int|\Illuminate\Support\LazyCollection - */ - public function getServersForPowerAction(array $servers = [], array $nodes = [], bool $returnCount = false) - { - $instance = $this->getBuilder(); - - if (! empty($nodes) && ! empty($servers)) { - $instance->whereIn('id', $servers)->orWhereIn('node_id', $nodes); - } else if (empty($nodes) && ! empty($servers)) { - $instance->whereIn('id', $servers); - } else if (! empty($nodes) && empty($servers)) { - $instance->whereIn('node_id', $nodes); - } - - if ($returnCount) { - return $instance->count(); - } - - return $instance->with('node')->cursor(); - } - - /** - * Return the total number of servers that will be affected by the query. - * - * @param int[] $servers - * @param int[] $nodes - * @return int - */ - public function getServersForPowerActionCount(array $servers = [], array $nodes = []): int - { - return $this->getServersForPowerAction($servers, $nodes, true); - } - /** * Check if a given UUID and UUID-Short string are unique to a server. * diff --git a/tests/Unit/Commands/Server/BulkPowerActionCommandTest.php b/tests/Unit/Commands/Server/BulkPowerActionCommandTest.php index d1ba90cf4..92d42ed72 100644 --- a/tests/Unit/Commands/Server/BulkPowerActionCommandTest.php +++ b/tests/Unit/Commands/Server/BulkPowerActionCommandTest.php @@ -53,7 +53,7 @@ class BulkPowerActionCommandTest extends CommandTestCase $this->repository->expects('getServersForPowerAction')->with([], [])->andReturn($servers); for ($i = 0; $i < count($servers); $i++) { - $this->powerRepository->expects('setNode->setServer->send')->with('kill')->andReturnNull(); + $this->powerRepository->expects('setServer->send')->with('kill')->andReturnNull(); } $display = $this->runCommand($this->getCommand(), ['action' => 'kill'], ['yes']); @@ -107,7 +107,7 @@ class BulkPowerActionCommandTest extends CommandTestCase ->andReturn(1); $this->repository->expects('getServersForPowerAction')->with([], [])->andReturn(Collection::make([$server])); - $this->powerRepository->expects('setNode->setServer->send')->with('kill')->andReturnNull(); + $this->powerRepository->expects('setServer->send')->with('kill')->andReturnNull(); $display = $this->runCommand($this->getCommand(), [ 'action' => 'kill', From 7ebe04fb911b0c2db87f8adbb9e1179fcdd890b7 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 29 Nov 2020 13:28:46 -0800 Subject: [PATCH 13/17] Don't allow blank passwords on the password change endpoint; closes #2750 --- .../Client/Account/UpdatePasswordRequest.php | 7 +++--- .../Api/Client/AccountControllerTest.php | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/app/Http/Requests/Api/Client/Account/UpdatePasswordRequest.php b/app/Http/Requests/Api/Client/Account/UpdatePasswordRequest.php index f52b71297..3bbff3d48 100644 --- a/app/Http/Requests/Api/Client/Account/UpdatePasswordRequest.php +++ b/app/Http/Requests/Api/Client/Account/UpdatePasswordRequest.php @@ -2,7 +2,6 @@ namespace Pterodactyl\Http\Requests\Api\Client\Account; -use Pterodactyl\Models\User; use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest; use Pterodactyl\Exceptions\Http\Base\InvalidPasswordProvidedException; @@ -32,8 +31,8 @@ class UpdatePasswordRequest extends ClientApiRequest */ public function rules(): array { - $rules = User::getRulesForUpdate($this->user()); - - return ['password' => array_merge($rules['password'], ['confirmed'])]; + return [ + 'password' => ['required', 'string', 'confirmed', 'min:8'], + ]; } } diff --git a/tests/Integration/Api/Client/AccountControllerTest.php b/tests/Integration/Api/Client/AccountControllerTest.php index 75b152090..6534bd508 100644 --- a/tests/Integration/Api/Client/AccountControllerTest.php +++ b/tests/Integration/Api/Client/AccountControllerTest.php @@ -140,6 +140,29 @@ class AccountControllerTest extends ClientApiIntegrationTestCase $response->assertJsonPath('errors.0.detail', 'The password provided was invalid for this account.'); } + /** + * Test that a validation error is returned to the user if no password is provided or if + * the password is below the minimum password length. + */ + public function testErrorIsReturnedForInvalidRequestData() + { + $user = factory(User::class)->create(); + + $this->actingAs($user)->putJson('/api/client/account/password', [ + 'current_password' => 'password', + ]) + ->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY) + ->assertJsonPath('errors.0.meta.rule', 'required'); + + $this->actingAs($user)->putJson('/api/client/account/password', [ + 'current_password' => 'password', + 'password' => 'pass', + 'password_confirmation' => 'pass', + ]) + ->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY) + ->assertJsonPath('errors.0.meta.rule', 'min'); + } + /** * Test that a validation error is returned if the password passed in the request * does not have a confirmation, or the confirmation is not the same as the password. From b97ebcbe392b96d3d689fd91d4de84036a548490 Mon Sep 17 00:00:00 2001 From: danny6167 Date: Fri, 4 Dec 2020 19:18:08 +0800 Subject: [PATCH 14/17] Allow 0 in first octet of database client IP address Fixes #2779 Allow user to enter database connection IP address that contains a a 0 in the first octet. Fixes #2779 --- .../components/server/databases/CreateDatabaseButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 () => { From ce0e9b7ded579c620593fe71745e5e6995fec8c3 Mon Sep 17 00:00:00 2001 From: Omar Kamel <30291302+TekExplorer@users.noreply.github.com> Date: Sun, 6 Dec 2020 13:17:47 -0500 Subject: [PATCH 15/17] Correct JDBC typo from `JBDC CONNECTION STRING` > `JDBC CONNECTION STRING` --- resources/scripts/components/server/databases/DatabaseRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) => {
- + Date: Sun, 6 Dec 2020 10:53:36 -0800 Subject: [PATCH 16/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ace35378b..418135fa3 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ I would like to extend my sincere thanks to the following sponsors for helping f | [**MineStrator**](https://minestrator.com/) | Looking for a French highend hosting company for you minecraft server? More than 14,000 members on our discord, trust us. | | [**DedicatedMC**](https://dedicatedmc.io/) | DedicatedMC provides Raw Power hosting at affordable pricing, making sure to never compromise on your performance and giving you the best performance money can buy. | | [**Skynode**](https://www.skynode.pro/) | Skynode provides blazing fast game servers along with a top-notch user experience. Whatever our clients are looking for, we're able to provide it! | -| [**XCORE-SERVER.de**](https://xcore-server.de/) | XCORE-SERVER.de offers High-End Servers for hosting and gaming since 2012. Fast, excellent and well-known for eSports Gaming. | +| [**XCORE**](https://xcore-server.de/) | XCORE offers High-End Servers for hosting and gaming since 2012. Fast, excellent and well-known for eSports Gaming. | | [**RoyaleHosting**](https://royalehosting.net/) | Build your dreams and deploy them with RoyaleHosting’s reliable servers and network. Easy to use, provisioned in a couple of minutes. | | [**Spill Hosting**](https://spillhosting.no/) | Spill Hosting is a Norwegian hosting service, which aims to cheap services on quality servers. Premium i9-9900K processors will run your game like a dream. | | [**DeinServerHost**](https://deinserverhost.de/) | DeinServerHost offers Dedicated, vps and Gameservers for many popular Games like Minecraft and Rust in Germany since 2013. | From 8a97b73a6ccab355be4e5e63b0d6a55a591e2338 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 6 Dec 2020 11:25:26 -0800 Subject: [PATCH 17/17] Fix failing tests --- .../Server/BulkPowerActionCommandTest.php | 161 ------------------ 1 file changed, 161 deletions(-) delete mode 100644 tests/Unit/Commands/Server/BulkPowerActionCommandTest.php diff --git a/tests/Unit/Commands/Server/BulkPowerActionCommandTest.php b/tests/Unit/Commands/Server/BulkPowerActionCommandTest.php deleted file mode 100644 index 92d42ed72..000000000 --- a/tests/Unit/Commands/Server/BulkPowerActionCommandTest.php +++ /dev/null @@ -1,161 +0,0 @@ -powerRepository = m::mock(DaemonPowerRepository::class); - $this->repository = m::mock(ServerRepositoryInterface::class); - } - - /** - * Test that an action can be sent to all servers. - */ - public function testSendAction() - { - /** @var \Pterodactyl\Models\Server[] $servers */ - $servers = factory(Server::class)->times(2)->make(); - - foreach ($servers as &$server) { - $server->setRelation('node', factory(Node::class)->make()); - } - - $this->repository->expects('getServersForPowerActionCount')->with([], [])->andReturn(2); - $this->repository->expects('getServersForPowerAction')->with([], [])->andReturn($servers); - - for ($i = 0; $i < count($servers); $i++) { - $this->powerRepository->expects('setServer->send')->with('kill')->andReturnNull(); - } - - $display = $this->runCommand($this->getCommand(), ['action' => 'kill'], ['yes']); - - $this->assertNotEmpty($display); - $this->assertStringContainsString('2/2', $display); - $this->assertStringContainsString(trans('command/messages.server.power.confirm', ['action' => 'kill', 'count' => 2]), $display); - } - - /** - * Test filtering servers and nodes. - */ - public function testSendWithFilters() - { - $server = factory(Server::class)->make(); - $server->setRelation('node', $node = factory(Node::class)->make()); - - $this->repository->expects('getServersForPowerActionCount') - ->with([1, 2], [3, 4]) - ->andReturn(1); - - $this->repository->expects('getServersForPowerAction') - ->with([1, 2], [3, 4]) - ->andReturn(Collection::make([$server])); - - $this->powerRepository->expects('setNode')->with($node)->andReturnSelf(); - $this->powerRepository->expects('setServer')->with($server)->andReturnSelf(); - $this->powerRepository->expects('send')->with('kill')->andReturn(new Response); - - $display = $this->runCommand($this->getCommand(), [ - 'action' => 'kill', - '--servers' => '1,2', - '--nodes' => '3,4', - ], ['yes']); - - $this->assertNotEmpty($display); - $this->assertStringContainsString('1/1', $display); - $this->assertStringContainsString(trans('command/messages.server.power.confirm', ['action' => 'kill', 'count' => 1]), $display); - } - - /** - * Test that sending empty options returns the expected results. - */ - public function testSendWithEmptyOptions() - { - $server = factory(Server::class)->make(); - $server->setRelation('node', factory(Node::class)->make()); - - $this->repository->expects('getServersForPowerActionCount') - ->with([], []) - ->andReturn(1); - - $this->repository->expects('getServersForPowerAction')->with([], [])->andReturn(Collection::make([$server])); - $this->powerRepository->expects('setServer->send')->with('kill')->andReturnNull(); - - $display = $this->runCommand($this->getCommand(), [ - 'action' => 'kill', - '--servers' => '', - '--nodes' => '', - ], ['yes']); - - $this->assertNotEmpty($display); - $this->assertStringContainsString('1/1', $display); - $this->assertStringContainsString(trans('command/messages.server.power.confirm', ['action' => 'kill', 'count' => 1]), $display); - } - - /** - * Test that validation occurs correctly. - * - * @param array $data - * - * @dataProvider validationFailureDataProvider - */ - public function testValidationErrors(array $data) - { - $this->expectException(ValidationException::class); - $this->runCommand($this->getCommand(), $data); - } - - /** - * Provide invalid data for the command. - * - * @return array - */ - public function validationFailureDataProvider(): array - { - return [ - [['action' => 'hodor']], - [['action' => 'hodor', '--servers' => 'hodor']], - [['action' => 'kill', '--servers' => 'hodor']], - [['action' => 'kill', '--servers' => '1,2,3', '--nodes' => 'hodor']], - [['action' => 'kill', '--servers' => '1,2,3', '--nodes' => '1,2,test']], - ]; - } - - /** - * Return an instance of the command with mocked dependencies. - * - * @return \Pterodactyl\Console\Commands\Server\BulkPowerActionCommand - */ - private function getCommand(): BulkPowerActionCommand - { - return new BulkPowerActionCommand($this->powerRepository, $this->repository, $this->app->make(Factory::class)); - } -}