diff --git a/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php b/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php new file mode 100644 index 00000000..411b7d45 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Nodes/NodeDeploymentController.php @@ -0,0 +1,51 @@ +viableNodesService = $viableNodesService; + } + + /** + * Finds any nodes that are available using the given deployment criteria. This works + * similarly to the server creation process, but allows you to pass the deployment object + * to this endpoint and get back a list of all Nodes satisfying the requirements. + * + * @param \Pterodactyl\Http\Requests\Api\Application\Nodes\GetDeployableNodesRequest $request + * @return array + * + * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException + */ + public function __invoke(GetDeployableNodesRequest $request): array + { + $data = $request->validated(); + $nodes = $this->viableNodesService->setLocations($data['location_ids'] ?? []) + ->setMemory($data['memory']) + ->setDisk($data['disk']) + ->handle($request->input('page') ?? 0); + + return $this->fractal->collection($nodes) + ->transformWith($this->getTransformer(NodeTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Requests/Api/Application/Nodes/GetDeployableNodesRequest.php b/app/Http/Requests/Api/Application/Nodes/GetDeployableNodesRequest.php new file mode 100644 index 00000000..0eae1400 --- /dev/null +++ b/app/Http/Requests/Api/Application/Nodes/GetDeployableNodesRequest.php @@ -0,0 +1,20 @@ + 'integer', + 'memory' => 'required|integer|min:0', + 'disk' => 'required|integer|min:0', + 'location_ids' => 'array', + 'location_ids.*' => 'integer', + ]; + } +} diff --git a/app/Services/Deployment/FindViableNodesService.php b/app/Services/Deployment/FindViableNodesService.php index 8c3d7c75..0963d88e 100644 --- a/app/Services/Deployment/FindViableNodesService.php +++ b/app/Services/Deployment/FindViableNodesService.php @@ -77,10 +77,15 @@ class FindViableNodesService * are tossed out, as are any nodes marked as non-public, meaning automatic * deployments should not be done against them. * - * @return \Pterodactyl\Models\Node[]|\Illuminate\Support\Collection + * @param int|null $page If provided the results will be paginated by returning + * up to 50 nodes at a time starting at the provided page. + * If "null" is provided as the value no pagination will + * be used. + * @return \Illuminate\Support\Collection|\Illuminate\Contracts\Pagination\LengthAwarePaginator + * * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException */ - public function handle() + public function handle(int $page = null) { Assert::integer($this->disk, 'Disk space must be an int, got %s'); Assert::integer($this->memory, 'Memory usage must be an int, got %s'); @@ -97,9 +102,13 @@ class FindViableNodesService $results = $query->groupBy('nodes.id') ->havingRaw('(IFNULL(SUM(servers.memory), 0) + ?) <= (nodes.memory * (1 + (nodes.memory_overallocate / 100)))', [$this->memory]) - ->havingRaw('(IFNULL(SUM(servers.disk), 0) + ?) <= (nodes.disk * (1 + (nodes.disk_overallocate / 100)))', [$this->disk]) - ->get() - ->toBase(); + ->havingRaw('(IFNULL(SUM(servers.disk), 0) + ?) <= (nodes.disk * (1 + (nodes.disk_overallocate / 100)))', [$this->disk]); + + if (! is_null($page)) { + $results = $results->paginate(50, ['*'], 'page', $page); + } else { + $results = $results->get()->toBase(); + } if ($results->isEmpty()) { throw new NoViableNodeException(trans('exceptions.deployment.no_viable_nodes')); diff --git a/app/Services/Servers/ServerCreationService.php b/app/Services/Servers/ServerCreationService.php index d185089c..76d371ab 100644 --- a/app/Services/Servers/ServerCreationService.php +++ b/app/Services/Servers/ServerCreationService.php @@ -197,6 +197,7 @@ class ServerCreationService */ private function configureDeployment(array $data, DeploymentObject $deployment): Allocation { + /** @var \Illuminate\Support\Collection $nodes */ $nodes = $this->findViableNodesService->setLocations($deployment->getLocations()) ->setDisk(Arr::get($data, 'disk')) ->setMemory(Arr::get($data, 'memory')) diff --git a/routes/api-application.php b/routes/api-application.php index e3246171..160f5f8b 100644 --- a/routes/api-application.php +++ b/routes/api-application.php @@ -32,6 +32,7 @@ Route::group(['prefix' => '/users'], function () { */ Route::group(['prefix' => '/nodes'], function () { Route::get('/', 'Nodes\NodeController@index')->name('api.application.nodes'); + Route::get('/deployable', 'Nodes\NodeDeploymentController'); Route::get('/{node}', 'Nodes\NodeController@view')->name('api.application.nodes.view'); Route::get('/{node}/configuration', 'Nodes\NodeConfigurationController');