diff --git a/app/Console/Commands/Server/BulkReinstallActionCommand.php b/app/Console/Commands/Server/BulkReinstallActionCommand.php new file mode 100644 index 000000000..ccca701ad --- /dev/null +++ b/app/Console/Commands/Server/BulkReinstallActionCommand.php @@ -0,0 +1,113 @@ +. + * + * This software is licensed under the terms of the MIT license. + * https://opensource.org/licenses/MIT + */ + +namespace Pterodactyl\Console\Commands\Server; + +use Webmozart\Assert\Assert; +use Illuminate\Console\Command; +use GuzzleHttp\Exception\RequestException; +use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Services\Servers\ServerConfigurationStructureService; +use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; + +class BulkReinstallActionCommand extends Command +{ + /** + * @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService + */ + protected $configurationStructureService; + + /** + * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface + */ + protected $daemonRepository; + + /** + * @var string + */ + protected $description = 'Reinstall a single server, all servers on a node, or all servers on the panel.'; + + /** + * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface + */ + protected $repository; + + /** + * @var string + */ + protected $signature = 'p:server:reinstall + {server? : The ID of the server to reinstall.} + {--node= : ID of the node to reinstall all servers on. Ignored if server is passed.}'; + + /** + * BulkReinstallActionCommand constructor. + * + * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository + * @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService + * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository + */ + public function __construct( + DaemonServerRepositoryInterface $daemonRepository, + ServerConfigurationStructureService $configurationStructureService, + ServerRepositoryInterface $repository + ) { + parent::__construct(); + + $this->configurationStructureService = $configurationStructureService; + $this->daemonRepository = $daemonRepository; + $this->repository = $repository; + } + + /** + * Handle command execution. + */ + public function handle() + { + $servers = $this->getServersToProcess(); + + if (! $this->confirm(trans('command/messages.server.reinstall.confirm'))) { + return; + } + + $bar = $this->output->createProgressBar(count($servers)); + + $servers->each(function ($server) use ($bar) { + $bar->clear(); + + try { + $this->daemonRepository->setServer($server)->reinstall(); + } catch (RequestException $exception) { + $this->output->error(trans('command/messages.server.reinstall.failed', [ + 'name' => $server->name, + 'id' => $server->id, + 'node' => $server->node->name, + 'message' => $exception->getMessage(), + ])); + } + + $bar->advance(); + $bar->display(); + }); + + $this->line(''); + } + + /** + * Return the servers to be reinstalled. + * + * @return \Illuminate\Database\Eloquent\Collection + */ + private function getServersToProcess() + { + Assert::nullOrIntegerish($this->argument('server'), 'Value passed in server argument must be null or an integer, received %s.'); + Assert::nullOrIntegerish($this->option('node'), 'Value passed in node option must be null or integer, received %s.'); + + return $this->repository->getDataForReinstall($this->argument('server'), $this->option('node')); + } +} diff --git a/app/Contracts/Repository/ServerRepositoryInterface.php b/app/Contracts/Repository/ServerRepositoryInterface.php index ba853398e..6b758c3df 100644 --- a/app/Contracts/Repository/ServerRepositoryInterface.php +++ b/app/Contracts/Repository/ServerRepositoryInterface.php @@ -36,6 +36,15 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter */ public function getDataForRebuild(int $server = null, int $node = null): Collection; + /** + * Return a collection of servers with their associated data for reinstall operations. + * + * @param int|null $server + * @param int|null $node + * @return \Illuminate\Support\Collection + */ + public function getDataForReinstall(int $server = null, int $node = null): Collection; + /** * Return a server model and all variables associated with the server. * diff --git a/app/Repositories/Eloquent/ServerRepository.php b/app/Repositories/Eloquent/ServerRepository.php index 820093cf4..f806c0f98 100644 --- a/app/Repositories/Eloquent/ServerRepository.php +++ b/app/Repositories/Eloquent/ServerRepository.php @@ -76,6 +76,26 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt return $instance->get($this->getColumns()); } + /** + * Return a collection of servers with their associated data for reinstall operations. + * + * @param int|null $server + * @param int|null $node + * @return \Illuminate\Support\Collection + */ + public function getDataForReinstall(int $server = null, int $node = null): Collection + { + $instance = $this->getBuilder()->with(['allocation', 'allocations', 'pack', 'egg', 'node']); + + if (! is_null($server) && is_null($node)) { + $instance = $instance->where('id', '=', $server); + } elseif (is_null($server) && ! is_null($node)) { + $instance = $instance->where('node_id', '=', $node); + } + + return $instance->get($this->getColumns()); + } + /** * Return a server model and all variables associated with the server. * diff --git a/resources/lang/en/command/messages.php b/resources/lang/en/command/messages.php index 8741fc424..71731a2d0 100644 --- a/resources/lang/en/command/messages.php +++ b/resources/lang/en/command/messages.php @@ -42,6 +42,10 @@ return [ ], 'server' => [ 'rebuild_failed' => 'Rebuild request for ":name" (#:id) on node ":node" failed with error: :message', + 'reinstall' => [ + 'failed' => 'Reinstall request for ":name" (#:id) on node ":node" failed with error: :message', + 'confirm' => 'You are about to reinstall against a group of servers. Do you wish to continue?', + ], 'power' => [ 'confirm' => 'You are about to perform a :action against :count servers. Do you wish to continue?', 'action_failed' => 'Power action request for ":name" (#:id) on node ":node" failed with error: :message',