123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- <?php
- namespace App\Classes;
- use App\Models\Pterodactyl\Egg;
- use App\Models\Pterodactyl\Nest;
- use App\Models\Pterodactyl\Node;
- use App\Models\Product;
- use App\Models\Server;
- use Exception;
- use Illuminate\Http\Client\PendingRequest;
- use Illuminate\Http\Client\Response;
- use Illuminate\Support\Facades\Http;
- use App\Settings\PterodactylSettings;
- use App\Settings\ServerSettings;
- class PterodactylClient
- {
- //TODO: Extend error handling (maybe logger for more errors when debugging)
- private int $per_page_limit = 200;
- private int $allocation_limit = 200;
- public PendingRequest $client;
- public PendingRequest $application;
- public function __construct(PterodactylSettings $ptero_settings)
- {
- $server_settings = new ServerSettings();
- try {
- $this->client = $this->client($ptero_settings);
- $this->application = $this->clientAdmin($ptero_settings);
- $this->per_page_limit = $ptero_settings->per_page_limit;
- $this->allocation_limit = $server_settings->allocation_limit;
- } catch (Exception $exception) {
- logger('Failed to construct Pterodactyl client, Settings table not available?', ['exception' => $exception]);
- }
- }
- /**
- * @return PendingRequest
- */
- public function client(PterodactylSettings $ptero_settings)
- {
- return Http::withHeaders([
- 'Authorization' => 'Bearer ' . $ptero_settings->user_token,
- 'Content-type' => 'application/json',
- 'Accept' => 'Application/vnd.pterodactyl.v1+json',
- ])->baseUrl($ptero_settings->getUrl() . 'api' . '/');
- }
- public function clientAdmin(PterodactylSettings $ptero_settings)
- {
- return Http::withHeaders([
- 'Authorization' => 'Bearer ' . $ptero_settings->admin_token,
- 'Content-type' => 'application/json',
- 'Accept' => 'Application/vnd.pterodactyl.v1+json',
- ])->baseUrl($ptero_settings->getUrl() . 'api' . '/');
- }
- /**
- * @return Exception
- */
- private function getException(string $message = '', int $status = 0): Exception
- {
- if ($status == 404) {
- return new Exception('Ressource does not exist on pterodactyl - ' . $message, 404);
- }
- if ($status == 403) {
- return new Exception('No permission on pterodactyl, check pterodactyl token and permissions - ' . $message, 403);
- }
- if ($status == 401) {
- return new Exception('No pterodactyl token set - ' . $message, 401);
- }
- if ($status == 500) {
- return new Exception('Pterodactyl server error - ' . $message, 500);
- }
- return new Exception('Request Failed, is pterodactyl set-up correctly? - ' . $message);
- }
- /**
- * @param Nest $nest
- * @return mixed
- *
- * @throws Exception
- */
- public function getEggs(Nest $nest)
- {
- try {
- $response = $this->application->get("application/nests/{$nest->id}/eggs?include=nest,variables&per_page=" . $this->per_page_limit);
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- if ($response->failed()) {
- throw self::getException('Failed to get eggs from pterodactyl - ', $response->status());
- }
- return $response->json()['data'];
- }
- /**
- * @return mixed
- *
- * @throws Exception
- */
- public function getNodes()
- {
- try {
- $response = $this->application->get('application/nodes?per_page=' . $this->per_page_limit);
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- if ($response->failed()) {
- throw self::getException('Failed to get nodes from pterodactyl - ', $response->status());
- }
- return $response->json()['data'];
- }
- /**
- * @return mixed
- *
- * @throws Exception
- * @description Returns the infos of a single node
- */
- public function getNode($id)
- {
- try {
- $response = $this->application->get('application/nodes/' . $id);
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- if ($response->failed()) {
- throw self::getException('Failed to get node id ' . $id . ' - ' . $response->status());
- }
- return $response->json()['attributes'];
- }
- public function getServers()
- {
- try {
- $response = $this->application->get('application/servers?per_page=' . $this->per_page_limit);
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- if ($response->failed()) {
- throw self::getException('Failed to get list of servers - ', $response->status());
- }
- return $response->json()['data'];
- }
- /**
- * @return null
- *
- * @throws Exception
- */
- public function getNests()
- {
- try {
- $response = $this->application->get('application/nests?per_page=' . $this->per_page_limit);
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- if ($response->failed()) {
- throw self::getException('Failed to get nests from pterodactyl', $response->status());
- }
- return $response->json()['data'];
- }
- /**
- * @return mixed
- *
- * @throws Exception
- */
- public function getLocations()
- {
- try {
- $response = $this->application->get('application/locations?per_page=' . $this->per_page_limit);
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- if ($response->failed()) {
- throw self::getException('Failed to get locations from pterodactyl - ', $response->status());
- }
- return $response->json()['data'];
- }
- /**
- * @param Node $node
- * @return mixed
- *
- * @throws Exception
- */
- public function getFreeAllocationId(Node $node)
- {
- return self::getFreeAllocations($node)[0]['attributes']['id'] ?? null;
- }
- /**
- * @param Node $node
- * @return array|mixed|null
- *
- * @throws Exception
- */
- public function getFreeAllocations(Node $node)
- {
- $response = self::getAllocations($node);
- $freeAllocations = [];
- if (isset($response['data'])) {
- if (!empty($response['data'])) {
- foreach ($response['data'] as $allocation) {
- if (!$allocation['attributes']['assigned']) {
- array_push($freeAllocations, $allocation);
- }
- }
- }
- }
- return $freeAllocations;
- }
- /**
- * @param Node $node
- * @return array|mixed
- *
- * @throws Exception
- */
- public function getAllocations(Node $node)
- {
- try {
- $response = $this->application->get("application/nodes/{$node->id}/allocations?per_page={$this->allocation_limit}");
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- if ($response->failed()) {
- throw self::getException('Failed to get allocations from pterodactyl - ', $response->status());
- }
- return $response->json();
- }
- /**
- * @param Server $server
- * @param Egg $egg
- * @param int $allocationId
- * @return Response
- */
- public function createServer(Server $server, Egg $egg, int $allocationId)
- {
- return $this->application->post('application/servers', [
- 'name' => $server->name,
- 'external_id' => $server->id,
- 'user' => $server->user->pterodactyl_id,
- 'egg' => $egg->id,
- 'docker_image' => $egg->docker_image,
- 'startup' => $egg->startup,
- 'environment' => $egg->getEnvironmentVariables(),
- 'oom_disabled' => !$server->product->oom_killer,
- 'limits' => [
- 'memory' => $server->product->memory,
- 'swap' => $server->product->swap,
- 'disk' => $server->product->disk,
- 'io' => $server->product->io,
- 'cpu' => $server->product->cpu,
- ],
- 'feature_limits' => [
- 'databases' => $server->product->databases,
- 'backups' => $server->product->backups,
- 'allocations' => $server->product->allocations,
- ],
- 'allocation' => [
- 'default' => $allocationId,
- ],
- ]);
- }
- public function suspendServer(Server $server)
- {
- try {
- $response = $this->application->post("application/servers/$server->pterodactyl_id/suspend");
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- if ($response->failed()) {
- throw self::getException('Failed to suspend server from pterodactyl - ', $response->status());
- }
- return $response;
- }
- public function unSuspendServer(Server $server)
- {
- try {
- $response = $this->application->post("application/servers/$server->pterodactyl_id/unsuspend");
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- if ($response->failed()) {
- throw self::getException('Failed to unsuspend server from pterodactyl - ', $response->status());
- }
- return $response;
- }
- /**
- * Get user by pterodactyl id
- *
- * @param int $pterodactylId
- * @return mixed
- */
- public function getUser(int $pterodactylId)
- {
- try {
- $response = $this->application->get("application/users/{$pterodactylId}");
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- if ($response->failed()) {
- throw self::getException('Failed to get user from pterodactyl - ', $response->status());
- }
- return $response->json()['attributes'];
- }
- /**
- * Get serverAttributes by pterodactyl id
- *
- * @param int $pterodactylId
- * @return mixed
- */
- public function getServerAttributes(int $pterodactylId, bool $deleteOn404 = false)
- {
- try {
- $response = $this->application->get("application/servers/{$pterodactylId}?include=egg,node,nest,location");
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- //print response body
- if ($response->failed()) {
- if ($deleteOn404) { //Delete the server if it does not exist (server deleted on pterodactyl)
- Server::where('pterodactyl_id', $pterodactylId)->first()->delete();
- return;
- } else {
- throw self::getException('Failed to get server attributes from pterodactyl - ', $response->status());
- }
- }
- return $response->json()['attributes'];
- }
- /**
- * Update Server Resources
- *
- * @param Server $server
- * @param Product $product
- * @return Response
- */
- public function updateServer(Server $server, Product $product)
- {
- return $this->application->patch("application/servers/{$server->pterodactyl_id}/build", [
- 'allocation' => $server->allocation,
- 'memory' => $product->memory,
- 'swap' => $product->swap,
- 'disk' => $product->disk,
- 'io' => $product->io,
- 'cpu' => $product->cpu,
- 'threads' => null,
- 'oom_disabled' => !$server->product->oom_killer,
- 'feature_limits' => [
- 'databases' => $product->databases,
- 'backups' => $product->backups,
- 'allocations' => $product->allocations,
- ],
- ]);
- }
- /**
- * Update the owner of a server
- *
- * @param int $userId
- * @param Server $server
- * @return mixed
- */
- public function updateServerOwner(Server $server, int $userId)
- {
- return $this->application->patch("application/servers/{$server->pterodactyl_id}/details", [
- 'name' => $server->name,
- 'user' => $userId,
- ]);
- }
- /**
- * Power Action Specific Server
- *
- * @param Server $server
- * @param string $action
- * @return Response
- */
- public function powerAction(Server $server, $action)
- {
- return $this->client->post("client/servers/{$server->identifier}/power", [
- 'signal' => $action,
- ]);
- }
- /**
- * Get info about user
- */
- public function getClientUser()
- {
- return $this->client->get('client/account');
- }
- /**
- * Check if node has enough free resources to allocate the given resources
- *
- * @param Node $node
- * @param int $requireMemory
- * @param int $requireDisk
- * @return bool
- */
- public function checkNodeResources(Node $node, int $requireMemory, int $requireDisk)
- {
- try {
- $response = $this->application->get("application/nodes/{$node->id}");
- } catch (Exception $e) {
- throw self::getException($e->getMessage());
- }
- $node = $response['attributes'];
- $freeMemory = ($node['memory'] * ($node['memory_overallocate'] + 100) / 100) - $node['allocated_resources']['memory'];
- $freeDisk = ($node['disk'] * ($node['disk_overallocate'] + 100) / 100) - $node['allocated_resources']['disk'];
- if ($freeMemory < $requireMemory) {
- return false;
- }
- if ($freeDisk < $requireDisk) {
- return false;
- }
- return true;
- }
- }
|