diff --git a/CHANGELOG.md b/CHANGELOG.md index a623e9d09..1bbceffb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,10 +11,18 @@ This project follows [Semantic Versioning](http://semver.org) guidelines. * `[beta.3]` — Fixes error handling of the settings service provider when no migrations have been run. * `[beta.3]` — Fixes validation error when trying to use 'None' as the 'Copy Script From' option for an egg script. * Fixes a design bug in the database that prevented the storage of negative numbers, thus preventing a server from being assigned unlimited swap. +* Fixes a bug where the 'Assign New Allocations' box would only show IPs that were present in the current pagination block. ### Added * Nest and Egg listings now show the associated ID in order to make API requests easier. +### Changed +* Changed behavior of allocation IP Address/Ports box to automatically store the value entered if a user unfocuses the field without hitting space. +* Changed order in which allocations are displayed to prioritize those with servers attached (in ascending IP & port order) followed by ascending IP & port order where no server is attached. + +### Removed +* OOM exceptions can no longer be disabled on servers due to a startling number of users that were using it to avoid allocating proper amounts of resources to servers. + ## v0.7.0-beta.3 (Derelict Dermodactylus) ### Fixed * `[beta.2]` — Fixes a bug that would cause an endless exception message stream in the console when attemping to setup environment settings in certain instances. diff --git a/app/Console/Commands/Server/RebuildServerCommand.php b/app/Console/Commands/Server/RebuildServerCommand.php index c6b562b06..ac239b1ee 100644 --- a/app/Console/Commands/Server/RebuildServerCommand.php +++ b/app/Console/Commands/Server/RebuildServerCommand.php @@ -77,7 +77,7 @@ class RebuildServerCommand extends Command $json = array_merge($this->configurationStructureService->handle($server), ['rebuild' => true]); try { - $this->daemonRepository->setNode($server->node_id)->setAccessServer($server->uuid)->update($json); + $this->daemonRepository->setServer($server)->update($json); } catch (RequestException $exception) { $this->output->error(trans('command/messages.server.rebuild_failed', [ 'name' => $server->name, diff --git a/app/Console/Commands/User/DeleteUserCommand.php b/app/Console/Commands/User/DeleteUserCommand.php index ce79a493d..c9a69bee8 100644 --- a/app/Console/Commands/User/DeleteUserCommand.php +++ b/app/Console/Commands/User/DeleteUserCommand.php @@ -61,7 +61,7 @@ class DeleteUserCommand extends Command $search = $this->option('user') ?? $this->ask(trans('command/messages.user.search_users')); Assert::notEmpty($search, 'Search term must be a non-null value, received %s.'); - $results = $this->repository->search($search)->all(); + $results = $this->repository->setSearchTerm($search)->all(); if (count($results) < 1) { $this->error(trans('command/messages.user.no_users_found')); if ($this->input->isInteractive()) { diff --git a/app/Console/Commands/User/DisableTwoFactorCommand.php b/app/Console/Commands/User/DisableTwoFactorCommand.php index 568a5a95e..f2cab910d 100644 --- a/app/Console/Commands/User/DisableTwoFactorCommand.php +++ b/app/Console/Commands/User/DisableTwoFactorCommand.php @@ -54,9 +54,9 @@ class DisableTwoFactorCommand extends Command } $email = $this->option('email') ?? $this->ask(trans('command/messages.user.ask_email')); - $user = $this->repository->withColumns(['id', 'email'])->findFirstWhere([['email', '=', $email]]); + $user = $this->repository->setColumns(['id', 'email'])->findFirstWhere([['email', '=', $email]]); - $this->repository->withoutFresh()->update($user->id, [ + $this->repository->withoutFreshModel()->update($user->id, [ 'use_totp' => false, 'totp_secret' => null, ]); diff --git a/app/Contracts/Repository/AllocationRepositoryInterface.php b/app/Contracts/Repository/AllocationRepositoryInterface.php index 90b75fced..22ca07656 100644 --- a/app/Contracts/Repository/AllocationRepositoryInterface.php +++ b/app/Contracts/Repository/AllocationRepositoryInterface.php @@ -1,14 +1,9 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; +use Illuminate\Support\Collection; + interface AllocationRepositoryInterface extends RepositoryInterface { /** @@ -18,13 +13,21 @@ interface AllocationRepositoryInterface extends RepositoryInterface * @param array $ids * @return int */ - public function assignAllocationsToServer($server, array $ids); + public function assignAllocationsToServer(int $server = null, array $ids): int; /** * Return all of the allocations for a specific node. * * @param int $node - * @return \Illuminate\Database\Eloquent\Collection + * @return \Illuminate\Support\Collection */ - public function getAllocationsForNode($node); + public function getAllocationsForNode(int $node): Collection; + + /** + * Return all of the unique IPs that exist for a given node. + * + * @param int $node + * @return \Illuminate\Support\Collection + */ + public function getUniqueAllocationIpsForNode(int $node): Collection; } diff --git a/app/Contracts/Repository/Attributes/SearchableInterface.php b/app/Contracts/Repository/Attributes/SearchableInterface.php index 30b2a202a..f1ab6e804 100644 --- a/app/Contracts/Repository/Attributes/SearchableInterface.php +++ b/app/Contracts/Repository/Attributes/SearchableInterface.php @@ -1,21 +1,38 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository\Attributes; interface SearchableInterface { /** - * Filter results by search term. + * Set the search term. * - * @param string $term + * @param string|null $term * @return $this + * @deprecated */ public function search($term); + + /** + * Set the search term to use when requesting all records from + * the model. + * + * @param string|null $term + * @return $this + */ + public function setSearchTerm(string $term = null); + + /** + * Determine if a valid search term is set on this repository. + * + * @return bool + */ + public function hasSearchTerm(): bool; + + /** + * Return the search term. + * + * @return string|null + */ + public function getSearchTerm(); } diff --git a/app/Contracts/Repository/Daemon/BaseRepositoryInterface.php b/app/Contracts/Repository/Daemon/BaseRepositoryInterface.php index 803edca64..b34c7420e 100644 --- a/app/Contracts/Repository/Daemon/BaseRepositoryInterface.php +++ b/app/Contracts/Repository/Daemon/BaseRepositoryInterface.php @@ -1,68 +1,65 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository\Daemon; +use GuzzleHttp\Client; +use Pterodactyl\Models\Node; +use Pterodactyl\Models\Server; + interface BaseRepositoryInterface { /** * Set the node model to be used for this daemon connection. * - * @param int $id + * @param \Pterodactyl\Models\Node $node * @return $this - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function setNode($id); + public function setNode(Node $node); /** * Return the node model being used. * - * @return \Pterodactyl\Models\Node + * @return \Pterodactyl\Models\Node|null */ public function getNode(); /** - * Set the UUID for the server to be used in the X-Access-Server header for daemon requests. + * Set the Server model to use when requesting information from the Daemon. * - * @param null|string $server + * @param \Pterodactyl\Models\Server $server * @return $this */ - public function setAccessServer($server = null); + public function setServer(Server $server); /** - * Return the UUID of the server being used in requests. + * Return the Server model. * - * @return string + * @return \Pterodactyl\Models\Server|null */ - public function getAccessServer(); + public function getServer(); /** * Set the token to be used in the X-Access-Token header for requests to the daemon. * - * @param null|string $token + * @param string $token * @return $this */ - public function setAccessToken($token = null); + public function setToken(string $token); /** * Return the access token being used for requests. * - * @return string + * @return string|null */ - public function getAccessToken(); + public function getToken(); /** * Return an instance of the Guzzle HTTP Client to be used for requests. * * @param array $headers * @return \GuzzleHttp\Client + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getHttpClient(array $headers = []); + public function getHttpClient(array $headers = []): Client; } diff --git a/app/Contracts/Repository/Daemon/CommandRepositoryInterface.php b/app/Contracts/Repository/Daemon/CommandRepositoryInterface.php index 398c1dc16..ff6c33cce 100644 --- a/app/Contracts/Repository/Daemon/CommandRepositoryInterface.php +++ b/app/Contracts/Repository/Daemon/CommandRepositoryInterface.php @@ -1,14 +1,9 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository\Daemon; +use Psr\Http\Message\ResponseInterface; + interface CommandRepositoryInterface extends BaseRepositoryInterface { /** @@ -17,5 +12,5 @@ interface CommandRepositoryInterface extends BaseRepositoryInterface * @param string $command * @return \Psr\Http\Message\ResponseInterface */ - public function send($command); + public function send(string $command): ResponseInterface; } diff --git a/app/Contracts/Repository/Daemon/ConfigurationRepositoryInterface.php b/app/Contracts/Repository/Daemon/ConfigurationRepositoryInterface.php index d0a3772da..2b8bceda9 100644 --- a/app/Contracts/Repository/Daemon/ConfigurationRepositoryInterface.php +++ b/app/Contracts/Repository/Daemon/ConfigurationRepositoryInterface.php @@ -1,14 +1,9 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository\Daemon; +use Psr\Http\Message\ResponseInterface; + interface ConfigurationRepositoryInterface extends BaseRepositoryInterface { /** @@ -17,5 +12,5 @@ interface ConfigurationRepositoryInterface extends BaseRepositoryInterface * @param array $overrides * @return \Psr\Http\Message\ResponseInterface */ - public function update(array $overrides = []); + public function update(array $overrides = []): ResponseInterface; } diff --git a/app/Contracts/Repository/Daemon/FileRepositoryInterface.php b/app/Contracts/Repository/Daemon/FileRepositoryInterface.php index 6b9facc09..aba7af0ea 100644 --- a/app/Contracts/Repository/Daemon/FileRepositoryInterface.php +++ b/app/Contracts/Repository/Daemon/FileRepositoryInterface.php @@ -1,35 +1,31 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository\Daemon; +use stdClass; +use Psr\Http\Message\ResponseInterface; + interface FileRepositoryInterface extends BaseRepositoryInterface { /** * Return stat information for a given file. * * @param string $path - * @return object + * @return \stdClass * * @throws \GuzzleHttp\Exception\RequestException */ - public function getFileStat($path); + public function getFileStat(string $path): stdClass; /** * Return the contents of a given file if it can be edited in the Panel. * * @param string $path - * @return object + * @return \stdClass * * @throws \GuzzleHttp\Exception\RequestException */ - public function getContent($path); + public function getContent(string $path): stdClass; /** * Save new contents to a given file. @@ -40,7 +36,7 @@ interface FileRepositoryInterface extends BaseRepositoryInterface * * @throws \GuzzleHttp\Exception\RequestException */ - public function putContent($path, $content); + public function putContent(string $path, string $content): ResponseInterface; /** * Return a directory listing for a given path. @@ -50,5 +46,5 @@ interface FileRepositoryInterface extends BaseRepositoryInterface * * @throws \GuzzleHttp\Exception\RequestException */ - public function getDirectory($path); + public function getDirectory(string $path): array; } diff --git a/app/Contracts/Repository/Daemon/PowerRepositoryInterface.php b/app/Contracts/Repository/Daemon/PowerRepositoryInterface.php index 255109cfd..6b6210431 100644 --- a/app/Contracts/Repository/Daemon/PowerRepositoryInterface.php +++ b/app/Contracts/Repository/Daemon/PowerRepositoryInterface.php @@ -1,14 +1,9 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository\Daemon; +use Psr\Http\Message\ResponseInterface; + interface PowerRepositoryInterface extends BaseRepositoryInterface { const SIGNAL_START = 'start'; @@ -24,5 +19,5 @@ interface PowerRepositoryInterface extends BaseRepositoryInterface * * @throws \Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException */ - public function sendSignal($signal); + public function sendSignal(string $signal): ResponseInterface; } diff --git a/app/Contracts/Repository/Daemon/ServerRepositoryInterface.php b/app/Contracts/Repository/Daemon/ServerRepositoryInterface.php index 19806ac49..a00686a05 100644 --- a/app/Contracts/Repository/Daemon/ServerRepositoryInterface.php +++ b/app/Contracts/Repository/Daemon/ServerRepositoryInterface.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository\Daemon; @@ -30,7 +23,7 @@ interface ServerRepositoryInterface extends BaseRepositoryInterface * @param array $data * @return \Psr\Http\Message\ResponseInterface */ - public function update(array $data); + public function update(array $data): ResponseInterface; /** * Mark a server to be reinstalled on the system. @@ -38,42 +31,42 @@ interface ServerRepositoryInterface extends BaseRepositoryInterface * @param array|null $data * @return \Psr\Http\Message\ResponseInterface */ - public function reinstall($data = null); + public function reinstall(array $data = null): ResponseInterface; /** * Mark a server as needing a container rebuild the next time the server is booted. * * @return \Psr\Http\Message\ResponseInterface */ - public function rebuild(); + public function rebuild(): ResponseInterface; /** * Suspend a server on the daemon. * * @return \Psr\Http\Message\ResponseInterface */ - public function suspend(); + public function suspend(): ResponseInterface; /** * Un-suspend a server on the daemon. * * @return \Psr\Http\Message\ResponseInterface */ - public function unsuspend(); + public function unsuspend(): ResponseInterface; /** * Delete a server on the daemon. * * @return \Psr\Http\Message\ResponseInterface */ - public function delete(); + public function delete(): ResponseInterface; /** * Return detials on a specific server. * * @return \Psr\Http\Message\ResponseInterface */ - public function details(); + public function details(): ResponseInterface; /** * Revoke an access key on the daemon before the time is expired. @@ -83,5 +76,5 @@ interface ServerRepositoryInterface extends BaseRepositoryInterface * * @throws \GuzzleHttp\Exception\RequestException */ - public function revokeAccessKey($key); + public function revokeAccessKey($key): ResponseInterface; } diff --git a/app/Contracts/Repository/DaemonKeyRepositoryInterface.php b/app/Contracts/Repository/DaemonKeyRepositoryInterface.php index 4f2156ad5..7979980c0 100644 --- a/app/Contracts/Repository/DaemonKeyRepositoryInterface.php +++ b/app/Contracts/Repository/DaemonKeyRepositoryInterface.php @@ -1,26 +1,4 @@ . - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ namespace Pterodactyl\Contracts\Repository; @@ -44,14 +22,6 @@ interface DaemonKeyRepositoryInterface extends RepositoryInterface */ public function loadServerAndUserRelations(DaemonKey $key, bool $refresh = false): DaemonKey; - /** - * Gets the daemon keys associated with a specific server. - * - * @param int $server - * @return \Illuminate\Support\Collection - */ - public function getServerKeys($server); - /** * Return a daemon key with the associated server relation attached. * @@ -60,7 +30,7 @@ interface DaemonKeyRepositoryInterface extends RepositoryInterface * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getKeyWithServer($key); + public function getKeyWithServer(string $key): DaemonKey; /** * Get all of the keys for a specific user including the information needed diff --git a/app/Contracts/Repository/DatabaseHostRepositoryInterface.php b/app/Contracts/Repository/DatabaseHostRepositoryInterface.php index 955e1f7f3..dfd29e9ab 100644 --- a/app/Contracts/Repository/DatabaseHostRepositoryInterface.php +++ b/app/Contracts/Repository/DatabaseHostRepositoryInterface.php @@ -1,30 +1,28 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; +use Illuminate\Support\Collection; +use Pterodactyl\Models\DatabaseHost; + interface DatabaseHostRepositoryInterface extends RepositoryInterface { /** - * Return database hosts with a count of databases and the node information for which it is attached. + * Return database hosts with a count of databases and the node + * information for which it is attached. * * @return \Illuminate\Support\Collection */ - public function getWithViewDetails(); + public function getWithViewDetails(): Collection; /** - * Return a database host with the databases and associated servers that are attached to said databases. + * Return a database host with the databases and associated servers + * that are attached to said databases. * * @param int $id - * @return mixed + * @return \Pterodactyl\Models\DatabaseHost * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getWithServers($id); + public function getWithServers(int $id): DatabaseHost; } diff --git a/app/Contracts/Repository/DatabaseRepositoryInterface.php b/app/Contracts/Repository/DatabaseRepositoryInterface.php index 1e90d0e04..7fc3bf045 100644 --- a/app/Contracts/Repository/DatabaseRepositoryInterface.php +++ b/app/Contracts/Repository/DatabaseRepositoryInterface.php @@ -9,6 +9,7 @@ namespace Pterodactyl\Contracts\Repository; +use Pterodactyl\Models\Database; use Illuminate\Support\Collection; interface DatabaseRepositoryInterface extends RepositoryInterface @@ -43,12 +44,12 @@ interface DatabaseRepositoryInterface extends RepositoryInterface * the provided details. * * @param array $data - * @return mixed + * @return \Pterodactyl\Models\Database * - * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException */ - public function createIfNotExists(array $data); + public function createIfNotExists(array $data): Database; /** * Create a new database on a given connection. @@ -56,7 +57,7 @@ interface DatabaseRepositoryInterface extends RepositoryInterface * @param string $database * @return bool */ - public function createDatabase($database); + public function createDatabase(string $database): bool; /** * Create a new database user on a given connection. @@ -66,7 +67,7 @@ interface DatabaseRepositoryInterface extends RepositoryInterface * @param string $password * @return bool */ - public function createUser($username, $remote, $password); + public function createUser(string $username, string $remote, string $password): bool; /** * Give a specific user access to a given database. @@ -76,14 +77,14 @@ interface DatabaseRepositoryInterface extends RepositoryInterface * @param string $remote * @return bool */ - public function assignUserToDatabase($database, $username, $remote); + public function assignUserToDatabase(string $database, string $username, string $remote): bool; /** * Flush the privileges for a given connection. * - * @return mixed + * @return bool */ - public function flush(); + public function flush(): bool; /** * Drop a given database on a specific connection. @@ -91,7 +92,7 @@ interface DatabaseRepositoryInterface extends RepositoryInterface * @param string $database * @return bool */ - public function dropDatabase($database); + public function dropDatabase(string $database): bool; /** * Drop a given user on a specific connection. @@ -100,5 +101,5 @@ interface DatabaseRepositoryInterface extends RepositoryInterface * @param string $remote * @return mixed */ - public function dropUser($username, $remote); + public function dropUser(string $username, string $remote): bool; } diff --git a/app/Contracts/Repository/LocationRepositoryInterface.php b/app/Contracts/Repository/LocationRepositoryInterface.php index ef68fdf28..e91da6c43 100644 --- a/app/Contracts/Repository/LocationRepositoryInterface.php +++ b/app/Contracts/Repository/LocationRepositoryInterface.php @@ -1,14 +1,9 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; +use Pterodactyl\Models\Location; +use Illuminate\Support\Collection; use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; interface LocationRepositoryInterface extends RepositoryInterface, SearchableInterface @@ -16,16 +11,16 @@ interface LocationRepositoryInterface extends RepositoryInterface, SearchableInt /** * Return locations with a count of nodes and servers attached to it. * - * @return mixed + * @return \Illuminate\Support\Collection */ - public function getAllWithDetails(); + public function getAllWithDetails(): Collection; /** * Return all of the available locations with the nodes as a relationship. * * @return \Illuminate\Support\Collection */ - public function getAllWithNodes(); + public function getAllWithNodes(): Collection; /** * Return all of the nodes and their respective count of servers for a location. @@ -35,7 +30,7 @@ interface LocationRepositoryInterface extends RepositoryInterface, SearchableInt * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getWithNodes($id); + public function getWithNodes(int $id): Location; /** * Return a location and the count of nodes in that location. @@ -45,5 +40,5 @@ interface LocationRepositoryInterface extends RepositoryInterface, SearchableInt * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getWithNodeCount($id); + public function getWithNodeCount(int $id): Location; } diff --git a/app/Contracts/Repository/NodeRepositoryInterface.php b/app/Contracts/Repository/NodeRepositoryInterface.php index 078a274db..49db33be8 100644 --- a/app/Contracts/Repository/NodeRepositoryInterface.php +++ b/app/Contracts/Repository/NodeRepositoryInterface.php @@ -1,68 +1,65 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; +use Pterodactyl\Models\Node; +use Illuminate\Support\Collection; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; interface NodeRepositoryInterface extends RepositoryInterface, SearchableInterface { + const THRESHOLD_PERCENTAGE_LOW = 75; + const THRESHOLD_PERCENTAGE_MEDIUM = 90; + /** * Return the usage stats for a single node. * - * @param int $id + * @param \Pterodactyl\Models\Node $node * @return array */ - public function getUsageStats($id); + public function getUsageStats(Node $node): array; /** * Return all available nodes with a searchable interface. * - * @param int $count * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function getNodeListingData($count = 25); + public function getNodeListingData(): LengthAwarePaginator; /** * Return a single node with location and server information. * - * @param int $id - * @return mixed - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @param \Pterodactyl\Models\Node $node + * @param bool $refresh + * @return \Pterodactyl\Models\Node */ - public function getSingleNode($id); + public function loadLocationAndServerCount(Node $node, bool $refresh = false): Node; /** - * Return a node with all of the associated allocations and servers that are attached to said allocations. + * Attach a paginated set of allocations to a node mode including + * any servers that are also attached to those allocations. * - * @param int $id - * @return mixed - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @param \Pterodactyl\Models\Node $node + * @param bool $refresh + * @return \Pterodactyl\Models\Node */ - public function getNodeAllocations($id); + public function loadNodeAllocations(Node $node, bool $refresh = false): Node; /** * Return a node with all of the servers attached to that node. * * @param int $id - * @return mixed + * @return \Pterodactyl\Models\Node * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getNodeServers($id); + public function getNodeServers(int $id): Node; /** * Return a collection of nodes for all locations to use in server creation UI. * - * @return mixed + * @return \Illuminate\Support\Collection */ - public function getNodesForServerCreation(); + public function getNodesForServerCreation(): Collection; } diff --git a/app/Contracts/Repository/PackRepositoryInterface.php b/app/Contracts/Repository/PackRepositoryInterface.php index d6ad80785..6ef7cef7f 100644 --- a/app/Contracts/Repository/PackRepositoryInterface.php +++ b/app/Contracts/Repository/PackRepositoryInterface.php @@ -1,44 +1,26 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; +use Pterodactyl\Models\Pack; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; interface PackRepositoryInterface extends RepositoryInterface, SearchableInterface { - /** - * Return a paginated listing of packs with their associated egg and server count. - * - * @param int $paginate - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator - */ - public function paginateWithEggAndServerCount($paginate = 50); - /** * Return a pack with the associated server models attached to it. * - * @param int $id - * @return \Illuminate\Database\Eloquent\Collection - * - * @throws \Illuminate\Database\Eloquent\ModelNotFoundException + * @param \Pterodactyl\Models\Pack $pack + * @param bool $refresh + * @return \Pterodactyl\Models\Pack */ - public function getWithServers($id); + public function loadServerData(Pack $pack, bool $refresh = false): Pack; /** - * Return all of the file archives for a given pack. + * Return a paginated listing of packs with their associated egg and server count. * - * @param int $id - * @param bool $collection - * @return object|\Illuminate\Support\Collection - * - * @throws \Illuminate\Database\Eloquent\ModelNotFoundException + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function getFileArchives($id, $collection = false); + public function paginateWithEggAndServerCount(): LengthAwarePaginator; } diff --git a/app/Contracts/Repository/RepositoryInterface.php b/app/Contracts/Repository/RepositoryInterface.php index cfe5fde62..028058af4 100644 --- a/app/Contracts/Repository/RepositoryInterface.php +++ b/app/Contracts/Repository/RepositoryInterface.php @@ -1,14 +1,9 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; +use Illuminate\Support\Collection; + interface RepositoryInterface { /** @@ -42,17 +37,34 @@ interface RepositoryInterface /** * An array of columns to filter the response by. * - * @param array $columns + * @param array|string $columns * @return $this */ - public function withColumns($columns = ['*']); + public function setColumns($columns = ['*']); /** - * Disable returning a fresh model when data is inserted or updated. + * Stop repository update functions from returning a fresh + * model when changes are committed. * * @return $this */ - public function withoutFresh(); + public function withoutFreshModel(); + + /** + * Return a fresh model with a repository updates a model. + * + * @return $this + */ + public function withFreshModel(); + + /** + * Set wether or not the repository should return a fresh model + * when changes are committed. + * + * @param bool $fresh + * @return $this + */ + public function setFreshModel(bool $fresh = true); /** * Create a new model instance and persist it to the database. @@ -64,23 +76,7 @@ interface RepositoryInterface * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ - public function create(array $fields, $validate = true, $force = false); - - /** - * Delete a given record from the database. - * - * @param int $id - * @return int - */ - public function delete($id); - - /** - * Delete records matching the given attributes. - * - * @param array $attributes - * @return int - */ - public function deleteWhere(array $attributes); + public function create(array $fields, bool $validate = true, bool $force = false); /** * Find a model that has the specific ID passed. @@ -90,15 +86,15 @@ interface RepositoryInterface * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function find($id); + public function find(int $id); /** * Find a model matching an array of where clauses. * * @param array $fields - * @return mixed + * @return \Illuminate\Support\Collection */ - public function findWhere(array $fields); + public function findWhere(array $fields): Collection; /** * Find and return the first matching instance for the given fields. @@ -116,7 +112,23 @@ interface RepositoryInterface * @param array $fields * @return int */ - public function findCountWhere(array $fields); + public function findCountWhere(array $fields): int; + + /** + * Delete a given record from the database. + * + * @param int $id + * @return int + */ + public function delete(int $id): int; + + /** + * Delete records matching the given attributes. + * + * @param array $attributes + * @return int + */ + public function deleteWhere(array $attributes): int; /** * Update a given ID with the passed array of fields. @@ -130,7 +142,7 @@ interface RepositoryInterface * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function update($id, array $fields, $validate = true, $force = false); + public function update($id, array $fields, bool $validate = true, bool $force = false); /** * Perform a mass update where matching records are updated using whereIn. @@ -141,7 +153,7 @@ interface RepositoryInterface * @param array $fields * @return int */ - public function updateWhereIn($column, array $values, array $fields); + public function updateWhereIn(string $column, array $values, array $fields): int; /** * Update a record if it exists in the database, otherwise create it. @@ -154,24 +166,14 @@ interface RepositoryInterface * * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ - public function updateOrCreate(array $where, array $fields, $validate = true, $force = false); + public function updateOrCreate(array $where, array $fields, bool $validate = true, bool $force = false); /** - * Update multiple records matching the passed clauses. + * Return all records associated with the given model. * - * @param array $where - * @param array $fields - * @return mixed + * @return Collection */ - public function massUpdate(array $where, array $fields); - - /** - * Return all records from the model. - * - * @param null $paginate - * @return mixed - */ - public function all($paginate = null); + public function all(): Collection; /** * Insert a single or multiple records into the database at once skipping @@ -180,7 +182,7 @@ interface RepositoryInterface * @param array $data * @return bool */ - public function insert(array $data); + public function insert(array $data): bool; /** * Insert multiple records into the database and ignore duplicates. @@ -188,5 +190,5 @@ interface RepositoryInterface * @param array $values * @return bool */ - public function insertIgnore(array $values); + public function insertIgnore(array $values): bool; } diff --git a/app/Contracts/Repository/ScheduleRepositoryInterface.php b/app/Contracts/Repository/ScheduleRepositoryInterface.php index 7e28b016f..67bf40271 100644 --- a/app/Contracts/Repository/ScheduleRepositoryInterface.php +++ b/app/Contracts/Repository/ScheduleRepositoryInterface.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; @@ -22,6 +15,16 @@ interface ScheduleRepositoryInterface extends RepositoryInterface */ public function findServerSchedules(int $server): Collection; + /** + * Load the tasks relationship onto the Schedule module if they are not + * already present. + * + * @param \Pterodactyl\Models\Schedule $schedule + * @param bool $refresh + * @return \Pterodactyl\Models\Schedule + */ + public function loadTasks(Schedule $schedule, bool $refresh = false): Schedule; + /** * Return a schedule model with all of the associated tasks as a relationship. * @@ -38,5 +41,5 @@ interface ScheduleRepositoryInterface extends RepositoryInterface * @param string $timestamp * @return \Illuminate\Support\Collection */ - public function getSchedulesToProcess($timestamp); + public function getSchedulesToProcess(string $timestamp): Collection; } diff --git a/app/Contracts/Repository/ServerRepositoryInterface.php b/app/Contracts/Repository/ServerRepositoryInterface.php index 2fb8349e3..6d1c14e78 100644 --- a/app/Contracts/Repository/ServerRepositoryInterface.php +++ b/app/Contracts/Repository/ServerRepositoryInterface.php @@ -1,15 +1,11 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; +use Pterodactyl\Models\User; use Pterodactyl\Models\Server; +use Illuminate\Support\Collection; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; interface ServerRepositoryInterface extends RepositoryInterface, SearchableInterface @@ -17,19 +13,10 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter /** * Returns a listing of all servers that exist including relationships. * - * @param int|null $paginate - * @return mixed + * @param int $paginate + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function getAllServers($paginate); - - /** - * Return a collection of servers with their associated data for rebuild operations. - * - * @param int|null $server - * @param int|null $node - * @return \Illuminate\Database\Eloquent\Collection - */ - public function getDataForRebuild($server = null, $node = null); + public function getAllServers(int $paginate): LengthAwarePaginator; /** * Load the egg relations onto the server model. @@ -40,15 +27,35 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter */ public function loadEggRelations(Server $server, bool $refresh = false): Server; + /** + * Return a collection of servers with their associated data for rebuild operations. + * + * @param int|null $server + * @param int|null $node + * @return \Illuminate\Support\Collection + */ + public function getDataForRebuild(int $server = null, int $node = null): Collection; + /** * Return a server model and all variables associated with the server. * * @param int $id - * @return mixed + * @return \Pterodactyl\Models\Server * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function findWithVariables($id); + public function findWithVariables(int $id): Server; + + /** + * Get the primary allocation for a given server. If a model is passed into + * the function, load the allocation relationship onto it. Otherwise, find and + * return the server from the database. + * + * @param \Pterodactyl\Models\Server $server + * @param bool $refresh + * @return \Pterodactyl\Models\Server + */ + public function getPrimaryAllocation(Server $server, bool $refresh = false): Server; /** * Return all of the server variables possible and default to the variable @@ -60,20 +67,7 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getVariablesWithValues($id, $returnAsObject = false); - - /** - * Get the primary allocation for a given server. If a model is passed into - * the function, load the allocation relationship onto it. Otherwise, find and - * return the server from the database. - * - * @param int|\Pterodactyl\Models\Server $server - * @param bool $refresh - * @return \Pterodactyl\Models\Server - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function getPrimaryAllocation($server, bool $refresh = false): Server; + public function getVariablesWithValues(int $id, bool $returnAsObject = false); /** * Return enough data to be used for the creation of a server via the daemon. @@ -85,14 +79,13 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter public function getDataForCreation(Server $server, bool $refresh = false): Server; /** - * Return a server as well as associated databases and their hosts. + * Load associated databases onto the server model. * - * @param int $id - * @return mixed - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @param \Pterodactyl\Models\Server $server + * @param bool $refresh + * @return \Pterodactyl\Models\Server */ - public function getWithDatabases($id); + public function loadDatabaseRelations(Server $server, bool $refresh = false): Server; /** * Get data for use when updating a server on the Daemon. Returns an array of @@ -105,24 +98,14 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter */ public function getDaemonServiceData(Server $server, bool $refresh = false): array; - /** - * Return an array of server IDs that a given user can access based on owner and subuser permissions. - * - * @param int $user - * @return array - */ - public function getUserAccessServers($user); - /** * Return a paginated list of servers that a user can access at a given level. * - * @param int $user - * @param string $level - * @param bool $admin - * @param array $relations + * @param \Pterodactyl\Models\User $user + * @param int $level * @return \Illuminate\Pagination\LengthAwarePaginator */ - public function filterUserAccessServers($user, $admin = false, $level = 'all', array $relations = []); + public function filterUserAccessServers(User $user, int $level): LengthAwarePaginator; /** * Return a server by UUID. @@ -132,5 +115,5 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getByUuid($uuid); + public function getByUuid(string $uuid): Server; } diff --git a/app/Contracts/Repository/ServerVariableRepositoryInterface.php b/app/Contracts/Repository/ServerVariableRepositoryInterface.php index a04f30e01..dc3e241a9 100644 --- a/app/Contracts/Repository/ServerVariableRepositoryInterface.php +++ b/app/Contracts/Repository/ServerVariableRepositoryInterface.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; diff --git a/app/Contracts/Repository/SessionRepositoryInterface.php b/app/Contracts/Repository/SessionRepositoryInterface.php index b95bad44e..1f2dd73e8 100644 --- a/app/Contracts/Repository/SessionRepositoryInterface.php +++ b/app/Contracts/Repository/SessionRepositoryInterface.php @@ -1,16 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; +use Illuminate\Support\Collection; + interface SessionRepositoryInterface extends RepositoryInterface { + /** + * Return all of the active sessions for a user. + * + * @param int $user + * @return \Illuminate\Support\Collection + */ + public function getUserSessions(int $user): Collection; + /** * Delete a session for a given user. * @@ -18,13 +21,5 @@ interface SessionRepositoryInterface extends RepositoryInterface * @param int $session * @return null|int */ - public function deleteUserSession($user, $session); - - /** - * Return all of the active sessions for a user. - * - * @param int $user - * @return \Illuminate\Support\Collection - */ - public function getUserSessions($user); + public function deleteUserSession(int $user, int $session); } diff --git a/app/Contracts/Repository/SettingsRepositoryInterface.php b/app/Contracts/Repository/SettingsRepositoryInterface.php index dbf87f744..9128e7f78 100644 --- a/app/Contracts/Repository/SettingsRepositoryInterface.php +++ b/app/Contracts/Repository/SettingsRepositoryInterface.php @@ -9,7 +9,9 @@ interface SettingsRepositoryInterface extends RepositoryInterface * * @param string $key * @param string $value - * @return mixed + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function set(string $key, string $value); @@ -26,7 +28,6 @@ interface SettingsRepositoryInterface extends RepositoryInterface * Remove a key from the database cache. * * @param string $key - * @return mixed */ public function forget(string $key); } diff --git a/app/Contracts/Repository/SubuserRepositoryInterface.php b/app/Contracts/Repository/SubuserRepositoryInterface.php index 06a9efc38..2d722119a 100644 --- a/app/Contracts/Repository/SubuserRepositoryInterface.php +++ b/app/Contracts/Repository/SubuserRepositoryInterface.php @@ -34,25 +34,4 @@ interface SubuserRepositoryInterface extends RepositoryInterface * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getWithPermissionsUsingUserAndServer(int $user, int $server): Subuser; - - /** - * Find a subuser and return with server and permissions relationships. - * - * @param int $id - * @return \Illuminate\Database\Eloquent\Collection - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function getWithServerAndPermissions($id); - - /** - * Return a subuser and their associated connection key for a server. - * - * @param int $user - * @param int $server - * @return \Pterodactyl\Models\Subuser - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function getWithKey($user, $server); } diff --git a/app/Contracts/Repository/TaskRepositoryInterface.php b/app/Contracts/Repository/TaskRepositoryInterface.php index 4f400adee..11cb704e1 100644 --- a/app/Contracts/Repository/TaskRepositoryInterface.php +++ b/app/Contracts/Repository/TaskRepositoryInterface.php @@ -1,14 +1,9 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; +use Pterodactyl\Models\Task; + interface TaskRepositoryInterface extends RepositoryInterface { /** @@ -19,14 +14,14 @@ interface TaskRepositoryInterface extends RepositoryInterface * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getTaskWithServer($id); + public function getTaskForJobProcess(int $id): Task; /** * Returns the next task in a schedule. * - * @param int $schedule the ID of the schedule to select the next task from - * @param int $index the index of the current task + * @param int $schedule + * @param int $index * @return null|\Pterodactyl\Models\Task */ - public function getNextTask($schedule, $index); + public function getNextTask(int $schedule, int $index); } diff --git a/app/Contracts/Repository/UserRepositoryInterface.php b/app/Contracts/Repository/UserRepositoryInterface.php index fac353ed4..b5003e0b2 100644 --- a/app/Contracts/Repository/UserRepositoryInterface.php +++ b/app/Contracts/Repository/UserRepositoryInterface.php @@ -1,14 +1,9 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Contracts\Repository; +use Illuminate\Support\Collection; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; interface UserRepositoryInterface extends RepositoryInterface, SearchableInterface @@ -18,13 +13,13 @@ interface UserRepositoryInterface extends RepositoryInterface, SearchableInterfa * * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function getAllUsersWithCounts(); + public function getAllUsersWithCounts(): LengthAwarePaginator; /** * Return all matching models for a user in a format that can be used for dropdowns. * * @param string $query - * @return \Illuminate\Database\Eloquent\Collection + * @return \Illuminate\Support\Collection */ - public function filterUsersByQuery($query); + public function filterUsersByQuery(string $query): Collection; } diff --git a/app/Http/Controllers/Admin/DatabaseController.php b/app/Http/Controllers/Admin/DatabaseController.php index 02271d699..dbf2766a1 100644 --- a/app/Http/Controllers/Admin/DatabaseController.php +++ b/app/Http/Controllers/Admin/DatabaseController.php @@ -139,7 +139,6 @@ class DatabaseController extends Controller * @param int $host * @return \Illuminate\Http\RedirectResponse * - * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ diff --git a/app/Http/Controllers/Admin/NodesController.php b/app/Http/Controllers/Admin/NodesController.php index d68a47556..e5d50fb40 100644 --- a/app/Http/Controllers/Admin/NodesController.php +++ b/app/Http/Controllers/Admin/NodesController.php @@ -126,7 +126,7 @@ class NodesController extends Controller public function index(Request $request) { return view('admin.nodes.index', [ - 'nodes' => $this->repository->search($request->input('query'))->getNodeListingData(), + 'nodes' => $this->repository->setSearchTerm($request->input('query'))->getNodeListingData(), ]); } @@ -166,15 +166,15 @@ class NodesController extends Controller /** * Shows the index overview page for a specific node. * - * @param int $node + * @param \Pterodactyl\Models\Node $node * @return \Illuminate\View\View * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function viewIndex($node) + public function viewIndex(Node $node) { return view('admin.nodes.view.index', [ - 'node' => $this->repository->getSingleNode($node), + 'node' => $this->repository->loadLocationAndServerCount($node), 'stats' => $this->repository->getUsageStats($node), 'version' => $this->versionService, ]); @@ -208,17 +208,18 @@ class NodesController extends Controller /** * Shows the allocation page for a specific node. * - * @param int $node + * @param \Pterodactyl\Models\Node $node * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function viewAllocation($node) + public function viewAllocation(Node $node) { - $node = $this->repository->getNodeAllocations($node); + $this->repository->loadNodeAllocations($node); Javascript::put(['node' => collect($node)->only(['id'])]); - return view('admin.nodes.view.allocation', ['node' => $node]); + return view('admin.nodes.view.allocation', [ + 'allocations' => $this->allocationRepository->setColumns(['ip'])->getUniqueAllocationIpsForNode($node->id), + 'node' => $node, + ]); } /** diff --git a/app/Http/Controllers/Admin/PackController.php b/app/Http/Controllers/Admin/PackController.php index fed86f3a0..1dd7c881c 100644 --- a/app/Http/Controllers/Admin/PackController.php +++ b/app/Http/Controllers/Admin/PackController.php @@ -114,9 +114,7 @@ class PackController extends Controller public function index(Request $request) { return view('admin.packs.index', [ - 'packs' => $this->repository->search($request->input('query'))->paginateWithEggAndServerCount( - $this->config->get('pterodactyl.paginate.admin.packs') - ), + 'packs' => $this->repository->setSearchTerm($request->input('query'))->paginateWithEggAndServerCount(), ]); } @@ -177,14 +175,14 @@ class PackController extends Controller /** * Display pack view template to user. * - * @param int $pack + * @param \Pterodactyl\Models\Pack $pack * @return \Illuminate\View\View * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function view($pack) + public function view(Pack $pack) { return view('admin.packs.view', [ - 'pack' => $this->repository->getWithServers($pack), + 'pack' => $this->repository->loadServerData($pack), 'nests' => $this->serviceRepository->getWithEggs(), ]); } diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 427bb7d20..c3e33b964 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -254,6 +254,7 @@ class ServersController extends Controller * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Illuminate\Validation\ValidationException */ public function store(ServerFormRequest $request) { @@ -352,14 +353,12 @@ class ServersController extends Controller /** * Display the database management page for a specific server. * - * @param int $server + * @param \Pterodactyl\Models\Server $server * @return \Illuminate\View\View - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function viewDatabase($server) + public function viewDatabase(Server $server) { - $server = $this->repository->getWithDatabases($server); + $this->repository->loadDatabaseRelations($server); return view('admin.servers.view.database', [ 'hosts' => $this->databaseHostRepository->all(), @@ -459,12 +458,10 @@ class ServersController extends Controller * * @param \Pterodactyl\Models\Server $server * @return \Illuminate\Http\RedirectResponse - * - * @throws \Pterodactyl\Exceptions\DisplayException */ public function rebuildContainer(Server $server) { - $this->containerRebuildService->rebuild($server); + $this->containerRebuildService->handle($server); $this->alert->success(trans('admin/server.alerts.rebuild_on_boot'))->flash(); return redirect()->route('admin.servers.view.manage', $server->id); diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index faa535b6a..5656ae124 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -83,7 +83,7 @@ class UserController extends Controller */ public function index(Request $request) { - $users = $this->repository->search($request->input('query'))->getAllUsersWithCounts(); + $users = $this->repository->setSearchTerm($request->input('query'))->getAllUsersWithCounts(); return view('admin.users.index', ['users' => $users]); } diff --git a/app/Http/Controllers/Base/IndexController.php b/app/Http/Controllers/Base/IndexController.php index 8e16f8605..70b5250f0 100644 --- a/app/Http/Controllers/Base/IndexController.php +++ b/app/Http/Controllers/Base/IndexController.php @@ -1,31 +1,9 @@ - * Some Modifications (c) 2015 Dylan Seidt . - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ namespace Pterodactyl\Http\Controllers\Base; use Illuminate\Http\Request; +use Pterodactyl\Models\User; use GuzzleHttp\Exception\RequestException; use Pterodactyl\Http\Controllers\Controller; use Symfony\Component\HttpKernel\Exception\HttpException; @@ -75,8 +53,8 @@ class IndexController extends Controller */ public function getIndex(Request $request) { - $servers = $this->repository->search($request->input('query'))->filterUserAccessServers( - $request->user()->id, $request->user()->root_admin, 'all', ['user'] + $servers = $this->repository->setSearchTerm($request->input('query'))->filterUserAccessServers( + $request->user(), User::FILTER_LEVEL_ALL ); return view('base.index', ['servers' => $servers]); @@ -102,10 +80,7 @@ class IndexController extends Controller } try { - $response = $this->daemonRepository->setNode($server->node_id) - ->setAccessServer($server->uuid) - ->setAccessToken($token) - ->details(); + $response = $this->daemonRepository->setServer($server)->setToken($token)->details(); } catch (RequestException $exception) { throw new HttpException(500, $exception->getMessage()); } diff --git a/app/Http/Controllers/Server/Files/FileActionsController.php b/app/Http/Controllers/Server/Files/FileActionsController.php index 08e6fa881..ca3c093aa 100644 --- a/app/Http/Controllers/Server/Files/FileActionsController.php +++ b/app/Http/Controllers/Server/Files/FileActionsController.php @@ -96,7 +96,6 @@ class FileActionsController extends Controller * @return \Illuminate\View\View * * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function view(UpdateFileContentsFormRequest $request, string $uuid, string $file): View { @@ -104,9 +103,7 @@ class FileActionsController extends Controller $dirname = pathinfo($file, PATHINFO_DIRNAME); try { - $content = $this->repository->setNode($server->node_id)->setAccessServer($server->uuid) - ->setAccessToken($request->attributes->get('server_token')) - ->getContent($file); + $content = $this->repository->setServer($server)->setToken($request->attributes->get('server_token'))->getContent($file); } catch (RequestException $exception) { throw new DaemonConnectionException($exception); } diff --git a/app/Http/Controllers/Server/Files/RemoteRequestController.php b/app/Http/Controllers/Server/Files/RemoteRequestController.php index e5ec82539..ab58037d0 100644 --- a/app/Http/Controllers/Server/Files/RemoteRequestController.php +++ b/app/Http/Controllers/Server/Files/RemoteRequestController.php @@ -66,10 +66,7 @@ class RemoteRequestController extends Controller } try { - $listing = $this->repository->setNode($server->node_id) - ->setAccessServer($server->uuid) - ->setAccessToken($request->attributes->get('server_token')) - ->getDirectory($requestDirectory); + $listing = $this->repository->setServer($server)->setToken($request->attributes->get('server_token'))->getDirectory($requestDirectory); } catch (RequestException $exception) { throw new DaemonConnectionException($exception, true); } @@ -90,7 +87,6 @@ class RemoteRequestController extends Controller * * @throws \Illuminate\Auth\Access\AuthorizationException * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function store(Request $request): Response { @@ -98,9 +94,7 @@ class RemoteRequestController extends Controller $this->authorize('save-files', $server); try { - $this->repository->setNode($server->node_id) - ->setAccessServer($server->uuid) - ->setAccessToken($request->attributes->get('server_token')) + $this->repository->setServer($server)->setToken($request->attributes->get('server_token')) ->putContent($request->input('file'), $request->input('contents') ?? ''); return response('', 204); diff --git a/app/Http/Controllers/Server/Tasks/ActionController.php b/app/Http/Controllers/Server/Tasks/ActionController.php new file mode 100644 index 000000000..410d7c189 --- /dev/null +++ b/app/Http/Controllers/Server/Tasks/ActionController.php @@ -0,0 +1,70 @@ +processScheduleService = $processScheduleService; + $this->repository = $repository; + } + + /** + * Toggle a task to be active or inactive for a given server. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + * + * @throws \Illuminate\Auth\Access\AuthorizationException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function toggle(Request $request): Response + { + $server = $request->attributes->get('server'); + $schedule = $request->attributes->get('schedule'); + $this->authorize('toggle-schedule', $server); + + $this->repository->update($schedule->id, [ + 'is_active' => ! $schedule->is_active, + ]); + + return response('', 204); + } + + /** + * Trigger a schedule to run now. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + * + * @throws \Illuminate\Auth\Access\AuthorizationException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function trigger(Request $request): Response + { + $server = $request->attributes->get('server'); + $this->authorize('toggle-schedule', $server); + + $this->processScheduleService->setRunTimeOverride(Carbon::now())->handle( + $request->attributes->get('schedule') + ); + + return response('', 204); + } +} diff --git a/app/Http/Controllers/Server/Tasks/TaskManagementController.php b/app/Http/Controllers/Server/Tasks/TaskManagementController.php index 9e6782e93..238846258 100644 --- a/app/Http/Controllers/Server/Tasks/TaskManagementController.php +++ b/app/Http/Controllers/Server/Tasks/TaskManagementController.php @@ -112,7 +112,7 @@ class TaskManagementController extends Controller $server = $request->attributes->get('server'); $schedule = $this->creationService->handle($server, $request->normalize(), $request->getTasks()); - $this->alert->success(trans('server.schedules.task_created'))->flash(); + $this->alert->success(trans('server.schedule.task_created'))->flash(); return redirect()->route('server.schedules.view', [ 'server' => $server->uuidShort, diff --git a/app/Http/Middleware/DaemonAuthenticate.php b/app/Http/Middleware/DaemonAuthenticate.php index 6bd2908cf..b8e83bf8c 100644 --- a/app/Http/Middleware/DaemonAuthenticate.php +++ b/app/Http/Middleware/DaemonAuthenticate.php @@ -34,6 +34,7 @@ class DaemonAuthenticate * Create a new filter instance. * * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository + * @deprecated */ public function __construct(NodeRepositoryInterface $repository) { @@ -47,6 +48,7 @@ class DaemonAuthenticate * @param \Closure $next * @return mixed * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException */ public function handle(Request $request, Closure $next) @@ -59,7 +61,7 @@ class DaemonAuthenticate throw new AccessDeniedHttpException; } - $node = $this->repository->findWhere(['daemonSecret' => $request->header('X-Access-Node')]); + $node = $this->repository->findFirstWhere(['daemonSecret' => $request->header('X-Access-Node')]); $request->attributes->set('node', $node); return $next($request); diff --git a/app/Http/Requests/Admin/Node/AllocationFormRequest.php b/app/Http/Requests/Admin/Node/AllocationFormRequest.php index 3cece4efc..777d3033f 100644 --- a/app/Http/Requests/Admin/Node/AllocationFormRequest.php +++ b/app/Http/Requests/Admin/Node/AllocationFormRequest.php @@ -20,7 +20,7 @@ class AllocationFormRequest extends AdminFormRequest { return [ 'allocation_ip' => 'required|string', - 'allocation_alias' => 'sometimes|string|max:255', + 'allocation_alias' => 'sometimes|nullable|string|max:255', 'allocation_ports' => 'required|array', ]; } diff --git a/app/Http/Requests/Admin/ServerFormRequest.php b/app/Http/Requests/Admin/ServerFormRequest.php index f448a953c..33b9c8ffd 100644 --- a/app/Http/Requests/Admin/ServerFormRequest.php +++ b/app/Http/Requests/Admin/ServerFormRequest.php @@ -21,7 +21,10 @@ class ServerFormRequest extends AdminFormRequest */ public function rules() { - return Server::getCreateRules(); + $rules = Server::getCreateRules(); + $rules['description'][] = 'nullable'; + + return $rules; } /** diff --git a/app/Http/Requests/Server/UpdateFileContentsFormRequest.php b/app/Http/Requests/Server/UpdateFileContentsFormRequest.php index d2337ba6a..da316cc22 100644 --- a/app/Http/Requests/Server/UpdateFileContentsFormRequest.php +++ b/app/Http/Requests/Server/UpdateFileContentsFormRequest.php @@ -69,7 +69,6 @@ class UpdateFileContentsFormRequest extends ServerFormRequest * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException * @throws \Pterodactyl\Exceptions\Http\Server\FileTypeNotEditableException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ private function checkFileCanBeEdited($server, $token) { @@ -77,9 +76,7 @@ class UpdateFileContentsFormRequest extends ServerFormRequest $repository = app()->make(FileRepositoryInterface::class); try { - $stats = $repository->setNode($server->node_id)->setAccessServer($server->uuid) - ->setAccessToken($token) - ->getFileStat($this->route()->parameter('file')); + $stats = $repository->setServer($server)->setToken($token)->getFileStat($this->route()->parameter('file')); } catch (RequestException $exception) { switch ($exception->getCode()) { case 404: diff --git a/app/Jobs/Schedule/RunTaskJob.php b/app/Jobs/Schedule/RunTaskJob.php index 88590883a..a00052b1a 100644 --- a/app/Jobs/Schedule/RunTaskJob.php +++ b/app/Jobs/Schedule/RunTaskJob.php @@ -1,18 +1,10 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Jobs\Schedule; use Exception; use Carbon\Carbon; use Pterodactyl\Jobs\Job; -use Webmozart\Assert\Assert; use InvalidArgumentException; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; @@ -59,12 +51,9 @@ class RunTaskJob extends Job implements ShouldQueue * @param int $task * @param int $schedule */ - public function __construct($task, $schedule) + public function __construct(int $task, int $schedule) { - Assert::integerish($task, 'First argument passed to constructor must be integer, received %s.'); - Assert::integerish($schedule, 'Second argument passed to constructor must be integer, received %s.'); - - $this->queue = app()->make('config')->get('pterodactyl.queues.standard'); + $this->queue = config('pterodactyl.queues.standard'); $this->task = $task; $this->schedule = $schedule; } @@ -91,26 +80,32 @@ class RunTaskJob extends Job implements ShouldQueue $this->powerRepository = $powerRepository; $this->taskRepository = $taskRepository; - $task = $this->taskRepository->getTaskWithServer($this->task); + $task = $this->taskRepository->getTaskForJobProcess($this->task); $server = $task->getRelation('server'); $user = $server->getRelation('user'); + // Do not process a task that is not set to active. + if (! $task->getRelation('schedule')->is_active) { + $this->markTaskNotQueued(); + $this->markScheduleComplete(); + + return; + } + // Perform the provided task aganist the daemon. switch ($task->action) { case 'power': - $this->powerRepository->setNode($server->node_id) - ->setAccessServer($server->uuid) - ->setAccessToken($keyProviderService->handle($server, $user)) + $this->powerRepository->setServer($server) + ->setToken($keyProviderService->handle($server, $user)) ->sendSignal($task->payload); break; case 'command': - $this->commandRepository->setNode($server->node_id) - ->setAccessServer($server->uuid) - ->setAccessToken($keyProviderService->handle($server, $user)) + $this->commandRepository->setServer($server) + ->setToken($keyProviderService->handle($server, $user)) ->send($task->payload); break; default: - throw new InvalidArgumentException('Cannot run a task that points to a non-existant action.'); + throw new InvalidArgumentException('Cannot run a task that points to a non-existent action.'); } $this->markTaskNotQueued(); @@ -161,7 +156,7 @@ class RunTaskJob extends Job implements ShouldQueue private function markScheduleComplete() { $repository = app()->make(ScheduleRepositoryInterface::class); - $repository->withoutFresh()->update($this->schedule, [ + $repository->withoutFreshModel()->update($this->schedule, [ 'is_processing' => false, 'last_run_at' => Carbon::now()->toDateTimeString(), ]); diff --git a/app/Models/DaemonKey.php b/app/Models/DaemonKey.php index 625df0c9c..59cab2354 100644 --- a/app/Models/DaemonKey.php +++ b/app/Models/DaemonKey.php @@ -27,12 +27,13 @@ namespace Pterodactyl\Models; use Sofa\Eloquence\Eloquence; use Sofa\Eloquence\Validable; use Illuminate\Database\Eloquent\Model; +use Znck\Eloquent\Traits\BelongsToThrough; use Sofa\Eloquence\Contracts\CleansAttributes; use Sofa\Eloquence\Contracts\Validable as ValidableContract; class DaemonKey extends Model implements CleansAttributes, ValidableContract { - use Eloquence, Validable; + use BelongsToThrough, Eloquence, Validable; /** * @var string @@ -91,6 +92,17 @@ class DaemonKey extends Model implements CleansAttributes, ValidableContract return $this->belongsTo(Server::class); } + /** + * Return the node relation. + * + * @return \Znck\Eloquent\Relations\BelongsToThrough + * @throws \Exception + */ + public function node() + { + return $this->belongsToThrough(Node::class, Server::class); + } + /** * Return the user relation. * diff --git a/app/Models/Server.php b/app/Models/Server.php index 6b5af5112..9f81584c8 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -119,13 +119,13 @@ class Server extends Model implements CleansAttributes, ValidableContract * @var array */ protected $searchableColumns = [ - 'name' => 10, - 'uuidShort' => 9, - 'uuid' => 8, - 'pack.name' => 7, - 'user.email' => 6, - 'user.username' => 6, - 'node.name' => 2, + 'name' => 50, + 'uuidShort' => 10, + 'uuid' => 10, + 'pack.name' => 5, + 'user.email' => 20, + 'user.username' => 20, + 'node.name' => 10, ]; /** diff --git a/app/Models/User.php b/app/Models/User.php index 6d83026c8..8c1bf5854 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -32,6 +32,11 @@ class User extends Model implements const USER_LEVEL_USER = 0; const USER_LEVEL_ADMIN = 1; + const FILTER_LEVEL_ALL = 0; + const FILTER_LEVEL_OWNER = 1; + const FILTER_LEVEL_ADMIN = 2; + const FILTER_LEVEL_SUBUSER = 3; + /** * Level of servers to display when using access() on a user. * diff --git a/app/Policies/APIKeyPolicy.php b/app/Policies/APIKeyPolicy.php index 69ce45c04..e8556ca9c 100644 --- a/app/Policies/APIKeyPolicy.php +++ b/app/Policies/APIKeyPolicy.php @@ -39,7 +39,7 @@ class APIKeyPolicy })->values(); }); - return $permissions->search($permission, true) !== false; + return $permissions->setSearchTerm($permission, true) !== false; } /** diff --git a/app/Policies/ServerPolicy.php b/app/Policies/ServerPolicy.php index 9b4db6f05..8891fa87f 100644 --- a/app/Policies/ServerPolicy.php +++ b/app/Policies/ServerPolicy.php @@ -32,7 +32,7 @@ class ServerPolicy })->values(); }); - return $permissions->search($permission, true) !== false; + return $permissions->setSearchTerm($permission, true) !== false; } /** diff --git a/app/Repositories/Concerns/Searchable.php b/app/Repositories/Concerns/Searchable.php index 0d396cdc4..26ed6544a 100644 --- a/app/Repositories/Concerns/Searchable.php +++ b/app/Repositories/Concerns/Searchable.php @@ -1,30 +1,36 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Concerns; trait Searchable { /** - * The term to search. + * The search term to use when filtering results. * - * @var bool|string + * @var null|string */ - protected $searchTerm = false; + protected $searchTerm; /** - * Perform a search of the model using the given term. + * Set the search term. * - * @param string $term + * @param string|null $term * @return $this + * @deprecated */ public function search($term) + { + return $this->setSearchTerm($term); + } + + /** + * Set the search term to use when requesting all records from + * the model. + * + * @param string|null $term + * @return $this + */ + public function setSearchTerm(string $term = null) { if (empty($term)) { return $this; @@ -35,4 +41,24 @@ trait Searchable return $clone; } + + /** + * Determine if a valid search term is set on this repository. + * + * @return bool + */ + public function hasSearchTerm(): bool + { + return ! empty($this->searchTerm); + } + + /** + * Return the search term. + * + * @return string|null + */ + public function getSearchTerm() + { + return $this->searchTerm; + } } diff --git a/app/Repositories/Daemon/BaseRepository.php b/app/Repositories/Daemon/BaseRepository.php index 42e343aeb..e15b17ce1 100644 --- a/app/Repositories/Daemon/BaseRepository.php +++ b/app/Repositories/Daemon/BaseRepository.php @@ -1,84 +1,71 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Daemon; +use RuntimeException; use GuzzleHttp\Client; -use Webmozart\Assert\Assert; +use Pterodactyl\Models\Node; +use Pterodactyl\Models\Server; use Illuminate\Foundation\Application; use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; -use Illuminate\Contracts\Config\Repository as ConfigRepository; use Pterodactyl\Contracts\Repository\Daemon\BaseRepositoryInterface; -class BaseRepository implements BaseRepositoryInterface +abstract class BaseRepository implements BaseRepositoryInterface { /** * @var \Illuminate\Foundation\Application */ - protected $app; + private $app; /** - * @var + * @var \Pterodactyl\Models\Server */ - protected $accessServer; + private $server; /** - * @var + * @var string|null */ - protected $accessToken; + private $token; /** - * @var + * @var \Pterodactyl\Models\Node|null */ - protected $node; - - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; + private $node; /** * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface */ - protected $nodeRepository; + private $nodeRepository; /** * BaseRepository constructor. * * @param \Illuminate\Foundation\Application $app - * @param \Illuminate\Contracts\Config\Repository $config * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $nodeRepository */ - public function __construct( - Application $app, - ConfigRepository $config, - NodeRepositoryInterface $nodeRepository - ) { + public function __construct(Application $app, NodeRepositoryInterface $nodeRepository) + { $this->app = $app; - $this->config = $config; $this->nodeRepository = $nodeRepository; } /** - * {@inheritdoc} + * Set the node model to be used for this daemon connection. + * + * @param \Pterodactyl\Models\Node $node + * @return $this */ - public function setNode($id) + public function setNode(Node $node) { - Assert::numeric($id, 'The first argument passed to setNode must be numeric, received %s.'); - - $this->node = $this->nodeRepository->find($id); + $this->node = $node; return $this; } /** - * {@inheritdoc} + * Return the node model being used. + * + * @return \Pterodactyl\Models\Node|null */ public function getNode() { @@ -86,64 +73,80 @@ class BaseRepository implements BaseRepositoryInterface } /** - * {@inheritdoc} + * Set the Server model to use when requesting information from the Daemon. + * + * @param \Pterodactyl\Models\Server $server + * @return $this */ - public function setAccessServer($server = null) + public function setServer(Server $server) { - Assert::nullOrString($server, 'The first argument passed to setAccessServer must be null or a string, received %s.'); - - $this->accessServer = $server; + $this->server = $server; return $this; } /** - * {@inheritdoc} + * Return the Server model. + * + * @return \Pterodactyl\Models\Server|null */ - public function getAccessServer() + public function getServer() { - return $this->accessServer; + return $this->server; } /** - * {@inheritdoc} + * Set the token to be used in the X-Access-Token header for requests to the daemon. + * + * @param string $token + * @return $this */ - public function setAccessToken($token = null) + public function setToken(string $token) { - Assert::nullOrString($token, 'The first argument passed to setAccessToken must be null or a string, received %s.'); - - $this->accessToken = $token; + $this->token = $token; return $this; } /** - * {@inheritdoc} + * Return the access token being used for requests. + * + * @return string|null */ - public function getAccessToken() + public function getToken() { - return $this->accessToken; + return $this->token; } /** - * {@inheritdoc} + * Return an instance of the Guzzle HTTP Client to be used for requests. + * + * @param array $headers + * @return \GuzzleHttp\Client */ - public function getHttpClient(array $headers = []) + public function getHttpClient(array $headers = []): Client { - if (! is_null($this->accessServer)) { - $headers['X-Access-Server'] = $this->getAccessServer(); + // If no node is set, load the relationship onto the Server model + // and pass that to the setNode function. + if (! $this->getNode() instanceof Node) { + if (! $this->getServer() instanceof Server) { + throw new RuntimeException('An instance of ' . Node::class . ' or ' . Server::class . ' must be set on this repository in order to return a client.'); + } + + $this->getServer()->loadMissing('node'); + $this->setNode($this->getServer()->getRelation('node')); } - if (! is_null($this->accessToken)) { - $headers['X-Access-Token'] = $this->getAccessToken(); - } elseif (! is_null($this->node)) { - $headers['X-Access-Token'] = $this->getNode()->daemonSecret; + if ($this->getServer() instanceof Server) { + $headers['X-Access-Server'] = $this->getServer()->uuid; } + $headers['X-Access-Token'] = $this->getToken() ?? $this->getNode()->daemonSecret; + return new Client([ 'base_uri' => sprintf('%s://%s:%s/v1/', $this->getNode()->scheme, $this->getNode()->fqdn, $this->getNode()->daemonListen), - 'timeout' => $this->config->get('pterodactyl.guzzle.timeout'), - 'connect_timeout' => $this->config->get('pterodactyl.guzzle.connect_timeout'), + 'timeout' => config('pterodactyl.guzzle.timeout'), + 'connect_timeout' => config('pterodactyl.guzzle.connect_timeout'), 'headers' => $headers, ]); } diff --git a/app/Repositories/Daemon/CommandRepository.php b/app/Repositories/Daemon/CommandRepository.php index 29720c69d..31cb6b9b7 100644 --- a/app/Repositories/Daemon/CommandRepository.php +++ b/app/Repositories/Daemon/CommandRepository.php @@ -1,26 +1,21 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Daemon; -use Webmozart\Assert\Assert; +use Psr\Http\Message\ResponseInterface; use Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface; class CommandRepository extends BaseRepository implements CommandRepositoryInterface { /** - * {@inheritdoc} + * Send a command to a server. + * + * @param string $command + * @return \Psr\Http\Message\ResponseInterface + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function send($command) + public function send(string $command): ResponseInterface { - Assert::stringNotEmpty($command, 'First argument passed to send must be a non-empty string, received %s.'); - return $this->getHttpClient()->request('POST', 'server/command', [ 'json' => [ 'command' => $command, diff --git a/app/Repositories/Daemon/ConfigurationRepository.php b/app/Repositories/Daemon/ConfigurationRepository.php index 42cf476e2..f1b063694 100644 --- a/app/Repositories/Daemon/ConfigurationRepository.php +++ b/app/Repositories/Daemon/ConfigurationRepository.php @@ -1,22 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Daemon; +use Psr\Http\Message\ResponseInterface; use Pterodactyl\Contracts\Repository\Daemon\ConfigurationRepositoryInterface; class ConfigurationRepository extends BaseRepository implements ConfigurationRepositoryInterface { /** - * {@inheritdoc} + * Update the configuration details for the specified node using data from the database. + * + * @param array $overrides + * @return \Psr\Http\Message\ResponseInterface */ - public function update(array $overrides = []) + public function update(array $overrides = []): ResponseInterface { $node = $this->getNode(); $structure = [ diff --git a/app/Repositories/Daemon/FileRepository.php b/app/Repositories/Daemon/FileRepository.php index 3a5bb980a..2f702d13d 100644 --- a/app/Repositories/Daemon/FileRepository.php +++ b/app/Repositories/Daemon/FileRepository.php @@ -1,23 +1,23 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Daemon; -use Webmozart\Assert\Assert; +use stdClass; +use Psr\Http\Message\ResponseInterface; use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; class FileRepository extends BaseRepository implements FileRepositoryInterface { - public function getFileStat($path) + /** + * Return stat information for a given file. + * + * @param string $path + * @return \stdClass + * + * @throws \GuzzleHttp\Exception\RequestException + */ + public function getFileStat(string $path): stdClass { - Assert::stringNotEmpty($path, 'First argument passed to getStat must be a non-empty string, received %s.'); - $file = pathinfo($path); $file['dirname'] = in_array($file['dirname'], ['.', './', '/']) ? null : trim($file['dirname'], '/') . '/'; @@ -30,12 +30,15 @@ class FileRepository extends BaseRepository implements FileRepositoryInterface } /** - * {@inheritdoc} + * Return the contents of a given file if it can be edited in the Panel. + * + * @param string $path + * @return \stdClass + * + * @throws \GuzzleHttp\Exception\RequestException */ - public function getContent($path) + public function getContent(string $path): stdClass { - Assert::stringNotEmpty($path, 'First argument passed to getContent must be a non-empty string, received %s.'); - $file = pathinfo($path); $file['dirname'] = in_array($file['dirname'], ['.', './', '/']) ? null : trim($file['dirname'], '/') . '/'; @@ -48,13 +51,16 @@ class FileRepository extends BaseRepository implements FileRepositoryInterface } /** - * {@inheritdoc} + * Save new contents to a given file. + * + * @param string $path + * @param string $content + * @return \Psr\Http\Message\ResponseInterface + * + * @throws \GuzzleHttp\Exception\RequestException */ - public function putContent($path, $content) + public function putContent(string $path, string $content): ResponseInterface { - Assert::stringNotEmpty($path, 'First argument passed to putContent must be a non-empty string, received %s.'); - Assert::string($content, 'Second argument passed to putContent must be a string, received %s.'); - $file = pathinfo($path); $file['dirname'] = in_array($file['dirname'], ['.', './', '/']) ? null : trim($file['dirname'], '/') . '/'; @@ -67,20 +73,19 @@ class FileRepository extends BaseRepository implements FileRepositoryInterface } /** - * {@inheritdoc} + * Return a directory listing for a given path. + * + * @param string $path + * @return array + * + * @throws \GuzzleHttp\Exception\RequestException */ - public function getDirectory($path) + public function getDirectory(string $path): array { - Assert::string($path, 'First argument passed to getDirectory must be a string, received %s.'); - - $response = $this->getHttpClient()->request('GET', sprintf( - 'server/directory/%s', - rawurlencode($path) - )); + $response = $this->getHttpClient()->request('GET', sprintf('server/directory/%s', rawurlencode($path))); $contents = json_decode($response->getBody()); - $files = []; - $folders = []; + $files = $folders = []; foreach ($contents as $value) { if ($value->directory) { diff --git a/app/Repositories/Daemon/PowerRepository.php b/app/Repositories/Daemon/PowerRepository.php index 2feaa8010..20fc79338 100644 --- a/app/Repositories/Daemon/PowerRepository.php +++ b/app/Repositories/Daemon/PowerRepository.php @@ -1,27 +1,23 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Daemon; -use Webmozart\Assert\Assert; +use Psr\Http\Message\ResponseInterface; use Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface; use Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException; class PowerRepository extends BaseRepository implements PowerRepositoryInterface { /** - * {@inheritdoc} + * Send a power signal to a server. + * + * @param string $signal + * @return \Psr\Http\Message\ResponseInterface + * + * @throws \Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException */ - public function sendSignal($signal) + public function sendSignal(string $signal): ResponseInterface { - Assert::stringNotEmpty($signal, 'The first argument passed to sendSignal must be a non-empty string, received %s.'); - switch ($signal) { case self::SIGNAL_START: case self::SIGNAL_STOP: @@ -32,9 +28,8 @@ class PowerRepository extends BaseRepository implements PowerRepositoryInterface 'action' => $signal, ], ]); - break; default: - throw new InvalidPowerSignalException('The signal ' . $signal . ' is not defined and could not be processed.'); + throw new InvalidPowerSignalException('The signal "' . $signal . '" is not defined and could not be processed.'); } } } diff --git a/app/Repositories/Daemon/ServerRepository.php b/app/Repositories/Daemon/ServerRepository.php index f21ec197a..3af381d1c 100644 --- a/app/Repositories/Daemon/ServerRepository.php +++ b/app/Repositories/Daemon/ServerRepository.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Daemon; @@ -26,9 +19,8 @@ class ServerRepository extends BaseRepository implements ServerRepositoryInterfa */ public function create(array $structure, array $overrides = []): ResponseInterface { - // Loop through overrides. foreach ($overrides as $key => $value) { - array_set($structure, $key, $value); + $structure[$key] = value($value); } return $this->getHttpClient()->request('POST', 'servers', [ @@ -37,9 +29,12 @@ class ServerRepository extends BaseRepository implements ServerRepositoryInterfa } /** - * {@inheritdoc} + * Update server details on the daemon. + * + * @param array $data + * @return \Psr\Http\Message\ResponseInterface */ - public function update(array $data) + public function update(array $data): ResponseInterface { return $this->getHttpClient()->request('PATCH', 'server', [ 'json' => $data, @@ -47,65 +42,77 @@ class ServerRepository extends BaseRepository implements ServerRepositoryInterfa } /** - * {@inheritdoc} + * Mark a server to be reinstalled on the system. + * + * @param array|null $data + * @return \Psr\Http\Message\ResponseInterface */ - public function reinstall($data = null) + public function reinstall(array $data = null): ResponseInterface { - Assert::nullOrIsArray($data, 'First argument passed to reinstall must be null or an array, received %s.'); - - if (is_null($data)) { - return $this->getHttpClient()->request('POST', 'server/reinstall'); - } - return $this->getHttpClient()->request('POST', 'server/reinstall', [ - 'json' => $data, + 'json' => $data ?? [], ]); } /** - * {@inheritdoc} + * Mark a server as needing a container rebuild the next time the server is booted. + * + * @return \Psr\Http\Message\ResponseInterface */ - public function rebuild() + public function rebuild(): ResponseInterface { return $this->getHttpClient()->request('POST', 'server/rebuild'); } /** - * {@inheritdoc} + * Suspend a server on the daemon. + * + * @return \Psr\Http\Message\ResponseInterface */ - public function suspend() + public function suspend(): ResponseInterface { return $this->getHttpClient()->request('POST', 'server/suspend'); } /** - * {@inheritdoc} + * Un-suspend a server on the daemon. + * + * @return \Psr\Http\Message\ResponseInterface */ - public function unsuspend() + public function unsuspend(): ResponseInterface { return $this->getHttpClient()->request('POST', 'server/unsuspend'); } /** - * {@inheritdoc} + * Delete a server on the daemon. + * + * @return \Psr\Http\Message\ResponseInterface */ - public function delete() + public function delete(): ResponseInterface { return $this->getHttpClient()->request('DELETE', 'servers'); } /** - * {@inheritdoc} + * Return detials on a specific server. + * + * @return \Psr\Http\Message\ResponseInterface */ - public function details() + public function details(): ResponseInterface { return $this->getHttpClient()->request('GET', 'server'); } /** - * {@inheritdoc} + * Revoke an access key on the daemon before the time is expired. + * + * @param string|array $key + * @return \Psr\Http\Message\ResponseInterface + * + * @throws \GuzzleHttp\Exception\RequestException */ - public function revokeAccessKey($key) + public function revokeAccessKey($key): ResponseInterface { if (is_array($key)) { return $this->getHttpClient()->request('POST', 'keys', [ diff --git a/app/Repositories/Eloquent/AllocationRepository.php b/app/Repositories/Eloquent/AllocationRepository.php index f613f734f..1a89134ca 100644 --- a/app/Repositories/Eloquent/AllocationRepository.php +++ b/app/Repositories/Eloquent/AllocationRepository.php @@ -1,21 +1,17 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; +use Illuminate\Support\Collection; use Pterodactyl\Models\Allocation; use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; class AllocationRepository extends EloquentRepository implements AllocationRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -23,18 +19,39 @@ class AllocationRepository extends EloquentRepository implements AllocationRepos } /** - * {@inheritdoc} + * Set an array of allocation IDs to be assigned to a specific server. + * + * @param int|null $server + * @param array $ids + * @return int */ - public function assignAllocationsToServer($server, array $ids) + public function assignAllocationsToServer(int $server = null, array $ids): int { return $this->getBuilder()->whereIn('id', $ids)->update(['server_id' => $server]); } /** - * {@inheritdoc} + * Return all of the allocations for a specific node. + * + * @param int $node + * @return \Illuminate\Support\Collection */ - public function getAllocationsForNode($node) + public function getAllocationsForNode(int $node): Collection { - return $this->getBuilder()->where('node_id', $node)->get(); + return $this->getBuilder()->where('node_id', $node)->get($this->getColumns()); + } + + /** + * Return all of the unique IPs that exist for a given node. + * + * @param int $node + * @return \Illuminate\Support\Collection + */ + public function getUniqueAllocationIpsForNode(int $node): Collection + { + return $this->getBuilder()->where('node_id', $node) + ->groupBy('ip') + ->orderByRaw('INET_ATON(ip) ASC') + ->get($this->getColumns()); } } diff --git a/app/Repositories/Eloquent/ApiKeyRepository.php b/app/Repositories/Eloquent/ApiKeyRepository.php index 107e0b6c9..f6169c939 100644 --- a/app/Repositories/Eloquent/ApiKeyRepository.php +++ b/app/Repositories/Eloquent/ApiKeyRepository.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; @@ -15,7 +8,9 @@ use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface; class ApiKeyRepository extends EloquentRepository implements ApiKeyRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { diff --git a/app/Repositories/Eloquent/ApiPermissionRepository.php b/app/Repositories/Eloquent/ApiPermissionRepository.php index b88fdfd1c..d94771327 100644 --- a/app/Repositories/Eloquent/ApiPermissionRepository.php +++ b/app/Repositories/Eloquent/ApiPermissionRepository.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; @@ -15,7 +8,9 @@ use Pterodactyl\Contracts\Repository\ApiPermissionRepositoryInterface; class ApiPermissionRepository extends EloquentRepository implements ApiPermissionRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { diff --git a/app/Repositories/Eloquent/DaemonKeyRepository.php b/app/Repositories/Eloquent/DaemonKeyRepository.php index 97fe0c2bb..c53f8a471 100644 --- a/app/Repositories/Eloquent/DaemonKeyRepository.php +++ b/app/Repositories/Eloquent/DaemonKeyRepository.php @@ -1,26 +1,4 @@ . - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ namespace Pterodactyl\Repositories\Eloquent; @@ -28,13 +6,16 @@ use Pterodactyl\Models\User; use Webmozart\Assert\Assert; use Pterodactyl\Models\DaemonKey; use Illuminate\Support\Collection; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface; class DaemonKeyRepository extends EloquentRepository implements DaemonKeyRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -62,28 +43,22 @@ class DaemonKeyRepository extends EloquentRepository implements DaemonKeyReposit } /** - * {@inheritdoc} + * Return a daemon key with the associated server relation attached. + * + * @param string $key + * @return \Pterodactyl\Models\DaemonKey + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getServerKeys($server) + public function getKeyWithServer(string $key): DaemonKey { - Assert::integerish($server, 'First argument passed to getServerKeys must be integer, received %s.'); + Assert::notEmpty($key, 'Expected non-empty string as first argument passed to ' . __METHOD__); - return $this->getBuilder()->where('server_id', $server)->get($this->getColumns()); - } - - /** - * {@inheritdoc} - */ - public function getKeyWithServer($key) - { - Assert::stringNotEmpty($key, 'First argument passed to getServerByKey must be string, received %s.'); - - $instance = $this->getBuilder()->with('server')->where('secret', '=', $key)->first(); - if (is_null($instance)) { + try { + return $this->getBuilder()->with('server')->where('secret', '=', $key)->firstOrFail($this->getColumns()); + } catch (ModelNotFoundException $exception) { throw new RecordNotFoundException; } - - return $instance; } /** @@ -95,7 +70,7 @@ class DaemonKeyRepository extends EloquentRepository implements DaemonKeyReposit */ public function getKeysForRevocation(User $user): Collection { - return $this->getBuilder()->with('server:id,uuid,node_id')->where('user_id', $user->id)->get($this->getColumns()); + return $this->getBuilder()->with('node')->where('user_id', $user->id)->get($this->getColumns()); } /** diff --git a/app/Repositories/Eloquent/DatabaseHostRepository.php b/app/Repositories/Eloquent/DatabaseHostRepository.php index 1bfe8d75b..6bfb94d70 100644 --- a/app/Repositories/Eloquent/DatabaseHostRepository.php +++ b/app/Repositories/Eloquent/DatabaseHostRepository.php @@ -1,23 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; -use Webmozart\Assert\Assert; +use Illuminate\Support\Collection; use Pterodactyl\Models\DatabaseHost; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; class DatabaseHostRepository extends EloquentRepository implements DatabaseHostRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -25,25 +21,31 @@ class DatabaseHostRepository extends EloquentRepository implements DatabaseHostR } /** - * {@inheritdoc} + * Return database hosts with a count of databases and the node + * information for which it is attached. + * + * @return \Illuminate\Support\Collection */ - public function getWithViewDetails() + public function getWithViewDetails(): Collection { return $this->getBuilder()->withCount('databases')->with('node')->get(); } /** - * {@inheritdoc} + * Return a database host with the databases and associated servers + * that are attached to said databases. + * + * @param int $id + * @return \Pterodactyl\Models\DatabaseHost + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getWithServers($id) + public function getWithServers(int $id): DatabaseHost { - Assert::numeric($id, 'First argument passed to getWithServers must be numeric, recieved %s.'); - - $instance = $this->getBuilder()->with('databases.server')->find($id, $this->getColumns()); - if (! $instance) { - throw new RecordNotFoundException(); + try { + return $this->getBuilder()->with('databases.server')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { + throw new RecordNotFoundException; } - - return $instance; } } diff --git a/app/Repositories/Eloquent/DatabaseRepository.php b/app/Repositories/Eloquent/DatabaseRepository.php index f030812b5..d24dd177e 100644 --- a/app/Repositories/Eloquent/DatabaseRepository.php +++ b/app/Repositories/Eloquent/DatabaseRepository.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; @@ -34,17 +27,17 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor * @param \Illuminate\Foundation\Application $application * @param \Illuminate\Database\DatabaseManager $database */ - public function __construct( - Application $application, - DatabaseManager $database - ) { + public function __construct(Application $application, DatabaseManager $database) + { parent::__construct($application); $this->database = $database; } /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -86,18 +79,24 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor } /** - * {@inheritdoc} - * @return bool|\Illuminate\Database\Eloquent\Model + * Create a new database if it does not already exist on the host with + * the provided details. + * + * @param array $data + * @return \Pterodactyl\Models\Database + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException */ - public function createIfNotExists(array $data) + public function createIfNotExists(array $data): Database { - $instance = $this->getBuilder()->where([ + $count = $this->getBuilder()->where([ ['server_id', '=', array_get($data, 'server_id')], ['database_host_id', '=', array_get($data, 'database_host_id')], ['database', '=', array_get($data, 'database')], ])->count(); - if ($instance > 0) { + if ($count > 0) { throw new DuplicateDatabaseNameException('A database with those details already exists for the specified server.'); } @@ -105,27 +104,40 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor } /** - * {@inheritdoc} + * Create a new database on a given connection. + * + * @param string $database + * @return bool */ - public function createDatabase($database) + public function createDatabase(string $database): bool { - return $this->runStatement(sprintf('CREATE DATABASE IF NOT EXISTS `%s`', $database)); + return $this->run(sprintf('CREATE DATABASE IF NOT EXISTS `%s`', $database)); } /** - * {@inheritdoc} + * Create a new database user on a given connection. + * + * @param string $username + * @param string $remote + * @param string $password + * @return bool */ - public function createUser($username, $remote, $password) + public function createUser(string $username, string $remote, string $password): bool { - return $this->runStatement(sprintf('CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'', $username, $remote, $password)); + return $this->run(sprintf('CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'', $username, $remote, $password)); } /** - * {@inheritdoc} + * Give a specific user access to a given database. + * + * @param string $database + * @param string $username + * @param string $remote + * @return bool */ - public function assignUserToDatabase($database, $username, $remote) + public function assignUserToDatabase(string $database, string $username, string $remote): bool { - return $this->runStatement(sprintf( + return $this->run(sprintf( 'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, EXECUTE ON `%s`.* TO `%s`@`%s`', $database, $username, @@ -134,27 +146,36 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor } /** - * {@inheritdoc} + * Flush the privileges for a given connection. + * + * @return bool */ - public function flush() + public function flush(): bool { - return $this->runStatement('FLUSH PRIVILEGES'); + return $this->run('FLUSH PRIVILEGES'); } /** - * {@inheritdoc} + * Drop a given database on a specific connection. + * + * @param string $database + * @return bool */ - public function dropDatabase($database) + public function dropDatabase(string $database): bool { - return $this->runStatement(sprintf('DROP DATABASE IF EXISTS `%s`', $database)); + return $this->run(sprintf('DROP DATABASE IF EXISTS `%s`', $database)); } /** - * {@inheritdoc} + * Drop a given user on a specific connection. + * + * @param string $username + * @param string $remote + * @return mixed */ - public function dropUser($username, $remote) + public function dropUser(string $username, string $remote): bool { - return $this->runStatement(sprintf('DROP USER IF EXISTS `%s`@`%s`', $username, $remote)); + return $this->run(sprintf('DROP USER IF EXISTS `%s`@`%s`', $username, $remote)); } /** @@ -163,7 +184,7 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor * @param string $statement * @return bool */ - protected function runStatement($statement) + private function run(string $statement): bool { return $this->database->connection($this->getConnection())->statement($statement); } diff --git a/app/Repositories/Eloquent/EggRepository.php b/app/Repositories/Eloquent/EggRepository.php index c1fcc583f..10d27f284 100644 --- a/app/Repositories/Eloquent/EggRepository.php +++ b/app/Repositories/Eloquent/EggRepository.php @@ -1,24 +1,20 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\Egg; use Webmozart\Assert\Assert; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Pterodactyl\Contracts\Repository\EggRepositoryInterface; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; class EggRepository extends EloquentRepository implements EggRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -35,13 +31,11 @@ class EggRepository extends EloquentRepository implements EggRepositoryInterface */ public function getWithVariables(int $id): Egg { - /** @var \Pterodactyl\Models\Egg $instance */ - $instance = $this->getBuilder()->with('variables')->find($id, $this->getColumns()); - if (! $instance) { + try { + return $this->getBuilder()->with('variables')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { throw new RecordNotFoundException; } - - return $instance; } /** @@ -67,13 +61,11 @@ class EggRepository extends EloquentRepository implements EggRepositoryInterface { Assert::true((is_digit($value) || is_string($value)), 'First argument passed to getWithCopyAttributes must be an integer or string, received %s.'); - /** @var \Pterodactyl\Models\Egg $instance */ - $instance = $this->getBuilder()->with('scriptFrom', 'configFrom')->where($column, '=', $value)->first($this->getColumns()); - if (! $instance) { + try { + return $this->getBuilder()->with('scriptFrom', 'configFrom')->where($column, '=', $value)->firstOrFail($this->getColumns()); + } catch (ModelNotFoundException $exception) { throw new RecordNotFoundException; } - - return $instance; } /** @@ -86,13 +78,11 @@ class EggRepository extends EloquentRepository implements EggRepositoryInterface */ public function getWithExportAttributes(int $id): Egg { - /** @var \Pterodactyl\Models\Egg $instance */ - $instance = $this->getBuilder()->with('scriptFrom', 'configFrom', 'variables')->find($id, $this->getColumns()); - if (! $instance) { + try { + return $this->getBuilder()->with('scriptFrom', 'configFrom', 'variables')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { throw new RecordNotFoundException; } - - return $instance; } /** diff --git a/app/Repositories/Eloquent/EggVariableRepository.php b/app/Repositories/Eloquent/EggVariableRepository.php index 2c34c7527..9d84b9db1 100644 --- a/app/Repositories/Eloquent/EggVariableRepository.php +++ b/app/Repositories/Eloquent/EggVariableRepository.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; @@ -16,7 +9,9 @@ use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; class EggVariableRepository extends EloquentRepository implements EggVariableRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { diff --git a/app/Repositories/Eloquent/EloquentRepository.php b/app/Repositories/Eloquent/EloquentRepository.php index f9124a718..e285ba318 100644 --- a/app/Repositories/Eloquent/EloquentRepository.php +++ b/app/Repositories/Eloquent/EloquentRepository.php @@ -1,17 +1,12 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; use Webmozart\Assert\Assert; +use Illuminate\Support\Collection; use Pterodactyl\Repositories\Repository; use Illuminate\Database\Query\Expression; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Pterodactyl\Contracts\Repository\RepositoryInterface; use Pterodactyl\Exceptions\Model\DataValidationException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; @@ -20,7 +15,19 @@ use Pterodactyl\Contracts\Repository\Attributes\SearchableInterface; abstract class EloquentRepository extends Repository implements RepositoryInterface { /** - * {@inheritdoc} + * Return an instance of the eloquent model bound to this + * repository instance. + * + * @return \Illuminate\Database\Eloquent\Model + */ + public function getModel() + { + return $this->model; + } + + /** + * Return an instance of the builder to use for this repository. + * * @return \Illuminate\Database\Eloquent\Builder */ public function getBuilder() @@ -29,22 +36,19 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf } /** - * {@inheritdoc} - * @param bool $force + * Create a new record in the database and return the associated model. + * + * @param array $fields + * @param bool $validate + * @param bool $force * @return \Illuminate\Database\Eloquent\Model|bool + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ - public function create(array $fields, $validate = true, $force = false) + public function create(array $fields, bool $validate = true, bool $force = false) { - Assert::boolean($validate, 'Second argument passed to create must be boolean, recieved %s.'); - Assert::boolean($force, 'Third argument passed to create must be boolean, received %s.'); - $instance = $this->getBuilder()->newModelInstance(); - - if ($force) { - $instance->forceFill($fields); - } else { - $instance->fill($fields); - } + ($force) ? $instance->forceFill($fields) : $instance->fill($fields); if (! $validate) { $saved = $instance->skipValidation()->save(); @@ -58,99 +62,108 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf } /** - * {@inheritdoc} - * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model + * Find a model that has the specific ID passed. + * + * @param int $id + * @return \Illuminate\Database\Eloquent\Model + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function find($id) + public function find(int $id) { - Assert::numeric($id, 'First argument passed to find must be numeric, received %s.'); - - $instance = $this->getBuilder()->find($id, $this->getColumns()); - - if (! $instance) { - throw new RecordNotFoundException(); + try { + return $this->getBuilder()->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { + throw new RecordNotFoundException; } - - return $instance; } /** - * {@inheritdoc} - * @return \Illuminate\Database\Eloquent\Collection + * Find a model matching an array of where clauses. + * + * @param array $fields + * @return \Illuminate\Support\Collection */ - public function findWhere(array $fields) + public function findWhere(array $fields): Collection { return $this->getBuilder()->where($fields)->get($this->getColumns()); } /** - * {@inheritdoc} + * Find and return the first matching instance for the given fields. + * + * @param array $fields * @return \Illuminate\Database\Eloquent\Model + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function findFirstWhere(array $fields) { - $instance = $this->getBuilder()->where($fields)->first($this->getColumns()); - - if (! $instance) { + try { + return $this->getBuilder()->where($fields)->firstOrFail($this->getColumns()); + } catch (ModelNotFoundException $exception) { throw new RecordNotFoundException; } - - return $instance; } /** - * {@inheritdoc}. + * Return a count of records matching the passed arguments. + * + * @param array $fields + * @return int */ - public function findCountWhere(array $fields) + public function findCountWhere(array $fields): int { return $this->getBuilder()->where($fields)->count($this->getColumns()); } /** - * {@inheritdoc} + * Delete a given record from the database. + * + * @param int $id + * @param bool $destroy + * @return int */ - public function delete($id, $destroy = false) + public function delete(int $id, bool $destroy = false): int { - Assert::numeric($id, 'First argument passed to delete must be numeric, received %s.'); - Assert::boolean($destroy, 'Second argument passed to delete must be boolean, received %s.'); - - $instance = $this->getBuilder()->where($this->getModel()->getKeyName(), $id); - - return ($destroy) ? $instance->forceDelete() : $instance->delete(); + return $this->deleteWhere(['id' => $id], $destroy); } /** - * {@inheritdoc} + * Delete records matching the given attributes. + * + * @param array $attributes + * @param bool $force + * @return int */ - public function deleteWhere(array $attributes, $force = false) + public function deleteWhere(array $attributes, bool $force = false): int { - Assert::boolean($force, 'Second argument passed to deleteWhere must be boolean, received %s.'); - $instance = $this->getBuilder()->where($attributes); return ($force) ? $instance->forceDelete() : $instance->delete(); } /** - * {@inheritdoc} + * Update a given ID with the passed array of fields. + * + * @param int $id + * @param array $fields + * @param bool $validate + * @param bool $force + * @return \Illuminate\Database\Eloquent\Model|bool + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function update($id, array $fields, $validate = true, $force = false) + public function update($id, array $fields, bool $validate = true, bool $force = false) { - Assert::numeric($id, 'First argument passed to update must be numeric, received %s.'); - Assert::boolean($validate, 'Third argument passed to update must be boolean, received %s.'); - Assert::boolean($force, 'Fourth argument passed to update must be boolean, received %s.'); - - $instance = $this->getBuilder()->where('id', $id)->first(); - - if (! $instance) { - throw new RecordNotFoundException(); + try { + $instance = $this->getBuilder()->where('id', $id)->firstOrFail(); + } catch (ModelNotFoundException $exception) { + throw new RecordNotFoundException; } - if ($force) { - $instance->forceFill($fields); - } else { - $instance->fill($fields); - } + ($force) ? $instance->forceFill($fields) : $instance->fill($fields); if (! $validate) { $saved = $instance->skipValidation()->save(); @@ -164,46 +177,71 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf } /** - * {@inheritdoc} + * Perform a mass update where matching records are updated using whereIn. + * This does not perform any model data validation. + * + * @param string $column + * @param array $values + * @param array $fields + * @return int */ - public function updateWhereIn($column, array $values, array $fields) + public function updateWhereIn(string $column, array $values, array $fields): int { - Assert::stringNotEmpty($column, 'First argument passed to updateWhereIn must be a non-empty string, received %s.'); + Assert::notEmpty($column, 'First argument passed to updateWhereIn must be a non-empty string.'); return $this->getBuilder()->whereIn($column, $values)->update($fields); } /** - * {@inheritdoc} + * Update a record if it exists in the database, otherwise create it. + * + * @param array $where + * @param array $fields + * @param bool $validate + * @param bool $force + * @return \Illuminate\Database\Eloquent\Model + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function massUpdate(array $where, array $fields) + public function updateOrCreate(array $where, array $fields, bool $validate = true, bool $force = false) { - // TODO: Implement massUpdate() method. + foreach ($where as $item) { + Assert::true(is_scalar($item) || is_null($item), 'First argument passed to updateOrCreate should be an array of scalar or null values, received an array value of %s.'); + } + + try { + $instance = $this->setColumns('id')->findFirstWhere($where); + } catch (RecordNotFoundException $exception) { + return $this->create(array_merge($where, $fields), $validate, $force); + } + + return $this->update($instance->id, $fields, $validate, $force); } /** - * {@inheritdoc} + * Return all records associated with the given model. + * + * @return \Illuminate\Support\Collection */ - public function all($paginate = null) + public function all(): Collection { - Assert::nullOrIntegerish($paginate, 'First argument passed to all must be null or integer, received %s.'); - $instance = $this->getBuilder(); - if (is_subclass_of(get_called_class(), SearchableInterface::class)) { - $instance = $instance->search($this->searchTerm); + if (is_subclass_of(get_called_class(), SearchableInterface::class) && $this->hasSearchTerm()) { + $instance = $instance->search($this->getSearchTerm()); } - if (is_null($paginate)) { - return $instance->get($this->getColumns()); - } - - return $instance->paginate($paginate, $this->getColumns()); + return $instance->get($this->getColumns()); } /** - * {@inheritdoc} + * Insert a single or multiple records into the database at once skipping + * validation and mass assignment checking. + * + * @param array $data + * @return bool */ - public function insert(array $data) + public function insert(array $data): bool { return $this->getBuilder()->insert($data); } @@ -214,19 +252,15 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf * @param array $values * @return bool */ - public function insertIgnore(array $values) + public function insertIgnore(array $values): bool { if (empty($values)) { return true; } - if (! is_array(reset($values))) { - $values = [$values]; - } else { - foreach ($values as $key => $value) { - ksort($value); - $values[$key] = $value; - } + foreach ($values as $key => $value) { + ksort($value); + $values[$key] = $value; } $bindings = array_values(array_filter(array_flatten($values, 1), function ($binding) { @@ -245,26 +279,4 @@ abstract class EloquentRepository extends Repository implements RepositoryInterf return $this->getBuilder()->getConnection()->statement($statement, $bindings); } - - /** - * {@inheritdoc} - * @return bool|\Illuminate\Database\Eloquent\Model - */ - public function updateOrCreate(array $where, array $fields, $validate = true, $force = false) - { - Assert::boolean($validate, 'Third argument passed to updateOrCreate must be boolean, received %s.'); - Assert::boolean($force, 'Fourth argument passed to updateOrCreate must be boolean, received %s.'); - - foreach ($where as $item) { - Assert::true(is_scalar($item) || is_null($item), 'First argument passed to updateOrCreate should be an array of scalar or null values, received an array value of %s.'); - } - - $instance = $this->withColumns('id')->findWhere($where)->first(); - - if (! $instance) { - return $this->create(array_merge($where, $fields), $validate, $force); - } - - return $this->update($instance->id, $fields, $validate, $force); - } } diff --git a/app/Repositories/Eloquent/LocationRepository.php b/app/Repositories/Eloquent/LocationRepository.php index b893a0588..47d5e321a 100644 --- a/app/Repositories/Eloquent/LocationRepository.php +++ b/app/Repositories/Eloquent/LocationRepository.php @@ -1,16 +1,11 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\Location; +use Illuminate\Support\Collection; use Pterodactyl\Repositories\Concerns\Searchable; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; @@ -19,7 +14,9 @@ class LocationRepository extends EloquentRepository implements LocationRepositor use Searchable; /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -27,44 +24,56 @@ class LocationRepository extends EloquentRepository implements LocationRepositor } /** - * {@inheritdoc} + * Return locations with a count of nodes and servers attached to it. + * + * @return \Illuminate\Support\Collection */ - public function getAllWithDetails() + public function getAllWithDetails(): Collection { return $this->getBuilder()->withCount('nodes', 'servers')->get($this->getColumns()); } /** - * {@inheritdoc} + * Return all of the available locations with the nodes as a relationship. + * + * @return \Illuminate\Support\Collection */ - public function getAllWithNodes() + public function getAllWithNodes(): Collection { return $this->getBuilder()->with('nodes')->get($this->getColumns()); } /** - * {@inheritdoc} + * Return all of the nodes and their respective count of servers for a location. + * + * @param int $id + * @return mixed + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getWithNodes($id) + public function getWithNodes(int $id): Location { - $instance = $this->getBuilder()->with('nodes.servers')->find($id, $this->getColumns()); - if (! $instance) { + try { + return $this->getBuilder()->with('nodes.servers')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { throw new RecordNotFoundException; } - - return $instance; } /** - * {@inheritdoc} + * Return a location and the count of nodes in that location. + * + * @param int $id + * @return mixed + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getWithNodeCount($id) + public function getWithNodeCount(int $id): Location { - $instance = $this->getBuilder()->withCount('nodes')->find($id, $this->getColumns()); - if (! $instance) { + try { + return $this->getBuilder()->withCount('nodes')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { throw new RecordNotFoundException; } - - return $instance; } } diff --git a/app/Repositories/Eloquent/NestRepository.php b/app/Repositories/Eloquent/NestRepository.php index 923186c26..9c0fcf73c 100644 --- a/app/Repositories/Eloquent/NestRepository.php +++ b/app/Repositories/Eloquent/NestRepository.php @@ -16,7 +16,9 @@ use Pterodactyl\Exceptions\Repository\RecordNotFoundException; class NestRepository extends EloquentRepository implements NestRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { diff --git a/app/Repositories/Eloquent/NodeRepository.php b/app/Repositories/Eloquent/NodeRepository.php index c1dd68a8a..ed0dce003 100644 --- a/app/Repositories/Eloquent/NodeRepository.php +++ b/app/Repositories/Eloquent/NodeRepository.php @@ -1,16 +1,12 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\Node; +use Illuminate\Support\Collection; use Pterodactyl\Repositories\Concerns\Searchable; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; @@ -18,11 +14,10 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa { use Searchable; - const THRESHOLD_PERCENTAGE_LOW = 75; - const THRESHOLD_PERCENTAGE_MEDIUM = 90; - /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -30,113 +25,121 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa } /** - * {@inheritdoc} + * Return the usage stats for a single node. + * + * @param \Pterodactyl\Models\Node $node + * @return array */ - public function getUsageStats($id) + public function getUsageStats(Node $node): array { - $node = $this->getBuilder()->select([ - 'nodes.disk_overallocate', - 'nodes.memory_overallocate', - 'nodes.disk', - 'nodes.memory', - ])->where('id', $id)->first(); - $stats = $this->getBuilder()->select( $this->getBuilder()->raw('IFNULL(SUM(servers.memory), 0) as sum_memory, IFNULL(SUM(servers.disk), 0) as sum_disk') - )->join('servers', 'servers.node_id', '=', 'nodes.id')->where('node_id', $id)->first(); + )->join('servers', 'servers.node_id', '=', 'nodes.id')->where('node_id', $node->id)->first(); - return collect(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory]) - ->mapWithKeys(function ($value, $key) use ($node) { - $maxUsage = $node->{$key}; - if ($node->{$key . '_overallocate'} > 0) { - $maxUsage = $node->{$key} * (1 + ($node->{$key . '_overallocate'} / 100)); - } + return collect(['disk' => $stats->sum_disk, 'memory' => $stats->sum_memory])->mapWithKeys(function ($value, $key) use ($node) { + $maxUsage = $node->{$key}; + if ($node->{$key . '_overallocate'} > 0) { + $maxUsage = $node->{$key} * (1 + ($node->{$key . '_overallocate'} / 100)); + } - $percent = ($value / $maxUsage) * 100; + $percent = ($value / $maxUsage) * 100; - return [ - $key => [ - 'value' => number_format($value), - 'max' => number_format($maxUsage), - 'percent' => $percent, - 'css' => ($percent <= self::THRESHOLD_PERCENTAGE_LOW) ? 'green' : (($percent > self::THRESHOLD_PERCENTAGE_MEDIUM) ? 'red' : 'yellow'), - ], - ]; - }) - ->toArray(); + return [ + $key => [ + 'value' => number_format($value), + 'max' => number_format($maxUsage), + 'percent' => $percent, + 'css' => ($percent <= self::THRESHOLD_PERCENTAGE_LOW) ? 'green' : (($percent > self::THRESHOLD_PERCENTAGE_MEDIUM) ? 'red' : 'yellow'), + ], + ]; + })->toArray(); } /** - * {@inheritdoc} + * Return all available nodes with a searchable interface. + * + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function getNodeListingData($count = 25) + public function getNodeListingData(): LengthAwarePaginator { $instance = $this->getBuilder()->with('location')->withCount('servers'); - if ($this->searchTerm) { - $instance->search($this->searchTerm); + if ($this->hasSearchTerm()) { + $instance->setSearchTerm($this->getSearchTerm()); } - return $instance->paginate($count, $this->getColumns()); + return $instance->paginate(25, $this->getColumns()); } /** - * {@inheritdoc} + * Return a single node with location and server information. + * + * @param \Pterodactyl\Models\Node $node + * @param bool $refresh + * @return \Pterodactyl\Models\Node */ - public function getSingleNode($id) + public function loadLocationAndServerCount(Node $node, bool $refresh = false): Node { - $instance = $this->getBuilder()->with('location')->withCount('servers')->find($id, $this->getColumns()); - - if (! $instance) { - throw new RecordNotFoundException(); + if (! $node->relationLoaded('location') || $refresh) { + $node->load('location'); } - return $instance; + // This is quite ugly and can probably be improved down the road. + // And by probably, I mean it should. + if (is_null($node->servers_count) || $refresh) { + $node->load('servers'); + $node->setRelation('servers_count', count($node->getRelation('servers'))); + unset($node->servers); + } + + return $node; } /** - * {@inheritdoc} + * Attach a paginated set of allocations to a node mode including + * any servers that are also attached to those allocations. + * + * @param \Pterodactyl\Models\Node $node + * @param bool $refresh + * @return \Pterodactyl\Models\Node */ - public function getNodeAllocations($id) + public function loadNodeAllocations(Node $node, bool $refresh = false): Node { - $instance = $this->getBuilder()->find($id, $this->getColumns()); - - if (! $instance) { - throw new RecordNotFoundException(); - } - - $instance->setRelation( - 'allocations', - $instance->allocations()->orderBy('ip', 'asc')->orderBy('port', 'asc')->with('server')->paginate(50) + $node->setRelation('allocations', + $node->allocations()->orderByRaw('server_id IS NOT NULL DESC, server_id IS NULL')->orderByRaw('INET_ATON(ip) ASC')->orderBy('port', 'asc')->with('server:id,name')->paginate(50) ); - return $instance; + return $node; } /** - * {@inheritdoc} + * Return a node with all of the servers attached to that node. + * + * @param int $id + * @return \Pterodactyl\Models\Node + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getNodeServers($id) + public function getNodeServers(int $id): Node { - $instance = $this->getBuilder()->with('servers.user', 'servers.nest', 'servers.egg') - ->find($id, $this->getColumns()); - - if (! $instance) { - throw new RecordNotFoundException(); + try { + return $this->getBuilder()->with([ + 'servers.user', 'servers.nest', 'servers.egg', + ])->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { + throw new RecordNotFoundException; } - - return $instance; } /** - * {@inheritdoc} + * Return a collection of nodes for all locations to use in server creation UI. + * + * @return \Illuminate\Support\Collection */ - public function getNodesForServerCreation() + public function getNodesForServerCreation(): Collection { - $instance = $this->getBuilder()->with('allocations')->get(); - - return $instance->map(function ($item) { - $filtered = $item->allocations->where('server_id', null)->map(function ($map) { + return $this->getBuilder()->with('allocations')->get()->map(function (Node $item) { + $filtered = $item->getRelation('allocations')->where('server_id', null)->map(function ($map) { return collect($map)->only(['id', 'ip', 'port']); }); diff --git a/app/Repositories/Eloquent/PackRepository.php b/app/Repositories/Eloquent/PackRepository.php index b2c555512..922e6415a 100644 --- a/app/Repositories/Eloquent/PackRepository.php +++ b/app/Repositories/Eloquent/PackRepository.php @@ -1,27 +1,20 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\Pack; -use Webmozart\Assert\Assert; use Pterodactyl\Repositories\Concerns\Searchable; -use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\PackRepositoryInterface; -use Illuminate\Contracts\Filesystem\Factory as FilesystemFactory; class PackRepository extends EloquentRepository implements PackRepositoryInterface { use Searchable; /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -29,58 +22,32 @@ class PackRepository extends EloquentRepository implements PackRepositoryInterfa } /** - * {@inheritdoc} + * Return a pack with the associated server models attached to it. + * + * @param \Pterodactyl\Models\Pack $pack + * @param bool $refresh + * @return \Pterodactyl\Models\Pack */ - public function getFileArchives($id, $collection = false) + public function loadServerData(Pack $pack, bool $refresh = false): Pack { - Assert::numeric($id, 'First argument passed to getFileArchives must be numeric, received %s.'); - Assert::boolean($collection, 'Second argument passed to getFileArchives must be boolean, received %s.'); - - $pack = $this->getBuilder()->find($id, ['id', 'uuid']); - if (! $pack) { - throw new ModelNotFoundException; + if ($refresh) { + $pack->load(['servers.node', 'servers.user']); } - $storage = $this->app->make(FilesystemFactory::class); - $files = collect($storage->disk('default')->files('packs/' . $pack->uuid)); + $pack->loadMissing(['servers.node', 'servers.user']); - $files = $files->map(function ($file) { - $path = storage_path('app/' . $file); - - return (object) [ - 'name' => basename($file), - 'hash' => sha1_file($path), - 'size' => human_readable($path), - ]; - }); - - return ($collection) ? $files : (object) $files->all(); + return $pack; } /** - * {@inheritdoc} + * Return a paginated listing of packs with their associated egg and server count. + * + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function getWithServers($id) + public function paginateWithEggAndServerCount(): LengthAwarePaginator { - Assert::numeric($id, 'First argument passed to getWithServers must be numeric, received %s.'); - - $instance = $this->getBuilder()->with('servers.node', 'servers.user')->find($id, $this->getColumns()); - if (! $instance) { - throw new ModelNotFoundException; - } - - return $instance; - } - - /** - * {@inheritdoc} - */ - public function paginateWithEggAndServerCount($paginate = 50) - { - Assert::integer($paginate, 'First argument passed to paginateWithOptionAndServerCount must be integer, received %s.'); - return $this->getBuilder()->with('egg')->withCount('servers') - ->search($this->searchTerm) - ->paginate($paginate, $this->getColumns()); + ->search($this->getSearchTerm()) + ->paginate(50, $this->getColumns()); } } diff --git a/app/Repositories/Eloquent/PermissionRepository.php b/app/Repositories/Eloquent/PermissionRepository.php index ca67b3975..ad2fa6386 100644 --- a/app/Repositories/Eloquent/PermissionRepository.php +++ b/app/Repositories/Eloquent/PermissionRepository.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; @@ -15,7 +8,9 @@ use Pterodactyl\Contracts\Repository\PermissionRepositoryInterface; class PermissionRepository extends EloquentRepository implements PermissionRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { diff --git a/app/Repositories/Eloquent/ScheduleRepository.php b/app/Repositories/Eloquent/ScheduleRepository.php index 4c0f5b0f0..a7b53602b 100644 --- a/app/Repositories/Eloquent/ScheduleRepository.php +++ b/app/Repositories/Eloquent/ScheduleRepository.php @@ -1,23 +1,19 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\Schedule; use Illuminate\Support\Collection; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; class ScheduleRepository extends EloquentRepository implements ScheduleRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -35,6 +31,23 @@ class ScheduleRepository extends EloquentRepository implements ScheduleRepositor return $this->getBuilder()->withCount('tasks')->where('server_id', '=', $server)->get($this->getColumns()); } + /** + * Load the tasks relationship onto the Schedule module if they are not + * already present. + * + * @param \Pterodactyl\Models\Schedule $schedule + * @param bool $refresh + * @return \Pterodactyl\Models\Schedule + */ + public function loadTasks(Schedule $schedule, bool $refresh = false): Schedule + { + if (! $schedule->relationLoaded('tasks') || $refresh) { + $schedule->load('tasks'); + } + + return $schedule; + } + /** * Return a schedule model with all of the associated tasks as a relationship. * @@ -45,19 +58,20 @@ class ScheduleRepository extends EloquentRepository implements ScheduleRepositor */ public function getScheduleWithTasks(int $schedule): Schedule { - /** @var \Pterodactyl\Models\Schedule $instance */ - $instance = $this->getBuilder()->with('tasks')->find($schedule, $this->getColumns()); - if (! $instance) { + try { + return $this->getBuilder()->with('tasks')->findOrFail($schedule, $this->getColumns()); + } catch (ModelNotFoundException $exception) { throw new RecordNotFoundException; } - - return $instance; } /** - * {@inheritdoc} + * Return all of the schedules that should be processed. + * + * @param string $timestamp + * @return \Illuminate\Support\Collection */ - public function getSchedulesToProcess($timestamp) + public function getSchedulesToProcess(string $timestamp): Collection { return $this->getBuilder()->with('tasks') ->where('is_active', true) diff --git a/app/Repositories/Eloquent/ServerRepository.php b/app/Repositories/Eloquent/ServerRepository.php index b8f5cc6fc..4bf058a72 100644 --- a/app/Repositories/Eloquent/ServerRepository.php +++ b/app/Repositories/Eloquent/ServerRepository.php @@ -1,17 +1,14 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; +use Pterodactyl\Models\User; use Webmozart\Assert\Assert; use Pterodactyl\Models\Server; +use Illuminate\Support\Collection; use Pterodactyl\Repositories\Concerns\Searchable; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; @@ -20,7 +17,9 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt use Searchable; /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -28,15 +27,16 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt } /** - * {@inheritdoc} + * Returns a listing of all servers that exist including relationships. + * + * @param int $paginate + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function getAllServers($paginate = 25) + public function getAllServers(int $paginate): LengthAwarePaginator { - Assert::nullOrIntegerish($paginate, 'First argument passed to getAllServers must be integer or null, received %s.'); + $instance = $this->getBuilder()->with('node', 'user', 'allocation')->search($this->getSearchTerm()); - $instance = $this->getBuilder()->with('node', 'user', 'allocation')->search($this->searchTerm); - - return is_null($paginate) ? $instance->get($this->getColumns()) : $instance->paginate($paginate, $this->getColumns()); + return $instance->paginate($paginate, $this->getColumns()); } /** @@ -56,14 +56,15 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt } /** - * {@inheritdoc} + * Return a collection of servers with their associated data for rebuild operations. + * + * @param int|null $server + * @param int|null $node + * @return \Illuminate\Support\Collection */ - public function getDataForRebuild($server = null, $node = null) + public function getDataForRebuild(int $server = null, int $node = null): Collection { - Assert::nullOrIntegerish($server, 'First argument passed to getDataForRebuild must be null or integer, received %s.'); - Assert::nullOrIntegerish($node, 'Second argument passed to getDataForRebuild must be null or integer, received %s.'); - - $instance = $this->getBuilder()->with('allocation', 'allocations', 'pack', 'egg', 'node'); + $instance = $this->getBuilder()->with(['allocation', 'allocations', 'pack', 'egg', 'node']); if (! is_null($server) && is_null($node)) { $instance = $instance->where('id', '=', $server); @@ -75,22 +76,22 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt } /** - * {@inheritdoc} - * @return \Illuminate\Database\Eloquent\Model + * Return a server model and all variables associated with the server. + * + * @param int $id + * @return \Pterodactyl\Models\Server + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function findWithVariables($id) + public function findWithVariables(int $id): Server { - Assert::integerish($id, 'First argument passed to findWithVariables must be integer, received %s.'); - - $instance = $this->getBuilder()->with('egg.variables', 'variables') - ->where($this->getModel()->getKeyName(), '=', $id) - ->first($this->getColumns()); - - if (is_null($instance)) { - throw new RecordNotFoundException(); + try { + return $this->getBuilder()->with('egg.variables', 'variables') + ->where($this->getModel()->getKeyName(), '=', $id) + ->firstOrFail($this->getColumns()); + } catch (ModelNotFoundException $exception) { + throw new RecordNotFoundException; } - - return $instance; } /** @@ -98,51 +99,45 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt * the function, load the allocation relationship onto it. Otherwise, find and * return the server from the database. * - * @param int|\Pterodactyl\Models\Server $server - * @param bool $refresh + * @param \Pterodactyl\Models\Server $server + * @param bool $refresh * @return \Pterodactyl\Models\Server - * - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getPrimaryAllocation($server, bool $refresh = false): Server + public function getPrimaryAllocation(Server $server, bool $refresh = false): Server { - $instance = $server; - if (! $instance instanceof Server) { - Assert::integerish($server, 'First argument passed to getPrimaryAllocation must be instance of \Pterodactyl\Models\Server or integer, received %s.'); - $instance = $this->getBuilder()->find($server, $this->getColumns()); + if (! $server->relationLoaded('allocation') || $refresh) { + $server->load('allocation'); } - if (! $instance) { - throw new RecordNotFoundException; - } - - if (! $instance->relationLoaded('allocation') || $refresh) { - $instance->load('allocation'); - } - - return $instance; + return $server; } /** - * {@inheritdoc} + * Return all of the server variables possible and default to the variable + * default if there is no value defined for the specific server requested. + * + * @param int $id + * @param bool $returnAsObject + * @return array|object + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getVariablesWithValues($id, $returnWithObject = false) + public function getVariablesWithValues(int $id, bool $returnAsObject = false) { - $instance = $this->getBuilder()->with('variables', 'egg.variables') - ->find($id, $this->getColumns()); - - if (! $instance) { - throw new RecordNotFoundException(); + try { + $instance = $this->getBuilder()->with('variables', 'egg.variables')->find($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { + throw new RecordNotFoundException; } $data = []; - $instance->egg->variables->each(function ($item) use (&$data, $instance) { - $display = $instance->variables->where('variable_id', $item->id)->pluck('variable_value')->first(); + $instance->getRelation('egg')->getRelation('variables')->each(function ($item) use (&$data, $instance) { + $display = $instance->getRelation('variables')->where('variable_id', $item->id)->pluck('variable_value')->first(); $data[$item->env_variable] = $display ?? $item->default_value; }); - if ($returnWithObject) { + if ($returnAsObject) { return (object) [ 'data' => $data, 'server' => $instance, @@ -171,19 +166,19 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt } /** - * {@inheritdoc} + * Load associated databases onto the server model. + * + * @param \Pterodactyl\Models\Server $server + * @param bool $refresh + * @return \Pterodactyl\Models\Server */ - public function getWithDatabases($id) + public function loadDatabaseRelations(Server $server, bool $refresh = false): Server { - $instance = $this->getBuilder()->with('databases.host') - ->where('installed', 1) - ->find($id, $this->getColumns()); - - if (! $instance) { - throw new RecordNotFoundException(); + if (! $server->relationLoaded('databases') || $refresh) { + $server->load('databases.host'); } - return $instance; + return $server; } /** @@ -212,71 +207,70 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt } /** - * {@inheritdoc} + * Return a paginated list of servers that a user can access at a given level. + * + * @param \Pterodactyl\Models\User $user + * @param int $level + * @return \Illuminate\Pagination\LengthAwarePaginator */ - public function getUserAccessServers($user) + public function filterUserAccessServers(User $user, int $level): LengthAwarePaginator { - Assert::numeric($user, 'First argument passed to getUserAccessServers must be numeric, received %s.'); - - $subuser = $this->app->make(SubuserRepository::class); - - return $this->getBuilder()->select('id')->where('owner_id', $user)->union( - $subuser->getBuilder()->select('server_id')->where('user_id', $user) - )->pluck('id')->all(); - } - - /** - * {@inheritdoc} - */ - public function filterUserAccessServers($user, $admin = false, $level = 'all', array $relations = []) - { - Assert::numeric($user, 'First argument passed to filterUserAccessServers must be numeric, received %s.'); - Assert::boolean($admin, 'Second argument passed to filterUserAccessServers must be boolean, received %s.'); - Assert::stringNotEmpty($level, 'Third argument passed to filterUserAccessServers must be a non-empty string, received %s.'); - - $instance = $this->getBuilder()->with($relations); + $instance = $this->getBuilder()->with(['user']); // If access level is set to owner, only display servers // that the user owns. - if ($level === 'owner') { - $instance->where('owner_id', $user); + if ($level === User::FILTER_LEVEL_OWNER) { + $instance->where('owner_id', $user->id); } // If set to all, display all servers they can access, including - // those they access as an admin. - // - // If set to subuser, only return the servers they can access because + // those they access as an admin. If set to subuser, only return the servers they can access because // they are owner, or marked as a subuser of the server. - if (($level === 'all' && ! $admin) || $level === 'subuser') { - $instance->whereIn('id', $this->getUserAccessServers($user)); + elseif (($level === User::FILTER_LEVEL_ALL && ! $user->root_admin) || $level === User::FILTER_LEVEL_SUBUSER) { + $instance->whereIn('id', $this->getUserAccessServers($user->id)); } // If set to admin, only display the servers a user can access // as an administrator (leaves out owned and subuser of). - if ($level === 'admin' && $admin) { - $instance->whereIn('id', $this->getUserAccessServers($user)); + elseif ($level === User::FILTER_LEVEL_ADMIN && $user->root_admin) { + $instance->whereNotIn('id', $this->getUserAccessServers($user->id)); } - return $instance->search($this->searchTerm)->paginate( - $this->app->make('config')->get('pterodactyl.paginate.frontend.servers') - ); + return $instance->search($this->getSearchTerm())->paginate(25); } /** - * {@inheritdoc} + * Return a server by UUID. + * + * @param string $uuid + * @return \Pterodactyl\Models\Server + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getByUuid($uuid) + public function getByUuid(string $uuid): Server { - Assert::stringNotEmpty($uuid, 'First argument passed to getByUuid must be a non-empty string, received %s.'); + Assert::notEmpty($uuid, 'Expected non-empty string as first argument passed to ' . __METHOD__); - $instance = $this->getBuilder()->with('nest', 'node')->where(function ($query) use ($uuid) { - $query->where('uuidShort', $uuid)->orWhere('uuid', $uuid); - })->first($this->getColumns()); - - if (! $instance) { + try { + return $this->getBuilder()->with('nest', 'node')->where(function ($query) use ($uuid) { + $query->where('uuidShort', $uuid)->orWhere('uuid', $uuid); + })->firstOrFail($this->getColumns()); + } catch (ModelNotFoundException $exception) { throw new RecordNotFoundException; } + } - return $instance; + /** + * Return an array of server IDs that a given user can access based + * on owner and subuser permissions. + * + * @param int $user + * @return int[] + */ + private function getUserAccessServers(int $user): array + { + return $this->getBuilder()->select('id')->where('owner_id', $user)->union( + $this->app->make(SubuserRepository::class)->getBuilder()->select('server_id')->where('user_id', $user) + )->pluck('id')->all(); } } diff --git a/app/Repositories/Eloquent/ServerVariableRepository.php b/app/Repositories/Eloquent/ServerVariableRepository.php index 49a81430a..d0d5e4dba 100644 --- a/app/Repositories/Eloquent/ServerVariableRepository.php +++ b/app/Repositories/Eloquent/ServerVariableRepository.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; @@ -15,7 +8,9 @@ use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface; class ServerVariableRepository extends EloquentRepository implements ServerVariableRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { diff --git a/app/Repositories/Eloquent/SessionRepository.php b/app/Repositories/Eloquent/SessionRepository.php index 8ba826696..47d955077 100644 --- a/app/Repositories/Eloquent/SessionRepository.php +++ b/app/Repositories/Eloquent/SessionRepository.php @@ -1,21 +1,17 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\Session; +use Illuminate\Support\Collection; use Pterodactyl\Contracts\Repository\SessionRepositoryInterface; class SessionRepository extends EloquentRepository implements SessionRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -23,17 +19,24 @@ class SessionRepository extends EloquentRepository implements SessionRepositoryI } /** - * {@inheritdoc} + * Return all of the active sessions for a user. + * + * @param int $user + * @return \Illuminate\Support\Collection */ - public function getUserSessions($user) + public function getUserSessions(int $user): Collection { return $this->getBuilder()->where('user_id', $user)->get($this->getColumns()); } /** - * {@inheritdoc} + * Delete a session for a given user. + * + * @param int $user + * @param int $session + * @return null|int */ - public function deleteUserSession($user, $session) + public function deleteUserSession(int $user, int $session) { return $this->getBuilder()->where('user_id', $user)->where('id', $session)->delete(); } diff --git a/app/Repositories/Eloquent/SettingsRepository.php b/app/Repositories/Eloquent/SettingsRepository.php index b6937bf31..dd516d68e 100644 --- a/app/Repositories/Eloquent/SettingsRepository.php +++ b/app/Repositories/Eloquent/SettingsRepository.php @@ -10,16 +10,15 @@ class SettingsRepository extends EloquentRepository implements SettingsRepositor /** * @var array */ - private $cache = []; + private static $cache = []; /** * @var array */ - private $databaseMiss = []; + private static $databaseMiss = []; /** - * Return an instance of the model that acts as the base for - * this repository. + * Return the model backing this repository. * * @return string */ @@ -33,12 +32,17 @@ class SettingsRepository extends EloquentRepository implements SettingsRepositor * * @param string $key * @param string $value + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function set(string $key, string $value) { // Clear item from the cache. $this->clearCache($key); - $this->withoutFresh()->updateOrCreate(['key' => $key], ['value' => $value]); + $this->withoutFreshModel()->updateOrCreate(['key' => $key], ['value' => $value]); + + self::$cache[$key] = $value; } /** @@ -51,32 +55,27 @@ class SettingsRepository extends EloquentRepository implements SettingsRepositor public function get(string $key, $default = null) { // If item has already been requested return it from the cache. If - // we already know it is missing, immediately return the default - // value. - if (array_key_exists($key, $this->cache)) { - return $this->cache[$key]; - } elseif (array_key_exists($key, $this->databaseMiss)) { - return $default; + // we already know it is missing, immediately return the default value. + if (array_key_exists($key, self::$cache)) { + return self::$cache[$key]; + } elseif (array_key_exists($key, self::$databaseMiss)) { + return value($default); } $instance = $this->getBuilder()->where('key', $key)->first(); - if (is_null($instance)) { - $this->databaseMiss[$key] = true; + self::$databaseMiss[$key] = true; - return $default; + return value($default); } - $this->cache[$key] = $instance->value; - - return $this->cache[$key]; + return self::$cache[$key] = $instance->value; } /** * Remove a key from the database cache. * * @param string $key - * @return mixed */ public function forget(string $key) { @@ -89,8 +88,8 @@ class SettingsRepository extends EloquentRepository implements SettingsRepositor * * @param string $key */ - protected function clearCache(string $key) + private function clearCache(string $key) { - unset($this->cache[$key], $this->databaseMiss[$key]); + unset(self::$cache[$key], self::$databaseMiss[$key]); } } diff --git a/app/Repositories/Eloquent/SubuserRepository.php b/app/Repositories/Eloquent/SubuserRepository.php index e1387b530..0296e0dbd 100644 --- a/app/Repositories/Eloquent/SubuserRepository.php +++ b/app/Repositories/Eloquent/SubuserRepository.php @@ -1,15 +1,7 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; -use Webmozart\Assert\Assert; use Pterodactyl\Models\Subuser; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; @@ -17,7 +9,9 @@ use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; class SubuserRepository extends EloquentRepository implements SubuserRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -86,39 +80,4 @@ class SubuserRepository extends EloquentRepository implements SubuserRepositoryI return $instance; } - - /** - * {@inheritdoc} - */ - public function getWithServerAndPermissions($id) - { - Assert::numeric($id, 'First argument passed to getWithServerAndPermissions must be numeric, received %s.'); - - $instance = $this->getBuilder()->with('server', 'permission', 'user')->find($id, $this->getColumns()); - if (! $instance) { - throw new RecordNotFoundException; - } - - return $instance; - } - - /** - * {@inheritdoc} - */ - public function getWithKey($user, $server) - { - Assert::integerish($user, 'First argument passed to getWithKey must be integer, received %s.'); - Assert::integerish($server, 'Second argument passed to getWithKey must be integer, received %s.'); - - $instance = $this->getBuilder()->with('key')->where([ - ['user_id', '=', $user], - ['server_id', '=', $server], - ])->first(); - - if (is_null($instance)) { - throw new RecordNotFoundException; - } - - return $instance; - } } diff --git a/app/Repositories/Eloquent/TaskRepository.php b/app/Repositories/Eloquent/TaskRepository.php index c44aa2fc6..0c1202f59 100644 --- a/app/Repositories/Eloquent/TaskRepository.php +++ b/app/Repositories/Eloquent/TaskRepository.php @@ -1,23 +1,18 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\Task; -use Webmozart\Assert\Assert; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; class TaskRepository extends EloquentRepository implements TaskRepositoryInterface { /** - * {@inheritdoc} + * Return the model backing this repository. + * + * @return string */ public function model() { @@ -25,28 +20,31 @@ class TaskRepository extends EloquentRepository implements TaskRepositoryInterfa } /** - * {@inheritdoc} + * Get a task and the server relationship for that task. + * + * @param int $id + * @return \Pterodactyl\Models\Task + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function getTaskWithServer($id) + public function getTaskForJobProcess(int $id): Task { - Assert::integerish($id, 'First argument passed to getTaskWithServer must be numeric, received %s.'); - - $instance = $this->getBuilder()->with('server.user')->find($id, $this->getColumns()); - if (! $instance) { + try { + return $this->getBuilder()->with('server.user', 'schedule')->findOrFail($id, $this->getColumns()); + } catch (ModelNotFoundException $exception) { throw new RecordNotFoundException; } - - return $instance; } /** - * {@inheritdoc} + * Returns the next task in a schedule. + * + * @param int $schedule + * @param int $index + * @return null|\Pterodactyl\Models\Task */ - public function getNextTask($schedule, $index) + public function getNextTask(int $schedule, int $index) { - Assert::integerish($schedule, 'First argument passed to getNextTask must be integer, received %s.'); - Assert::integerish($index, 'Second argument passed to getNextTask must be integer, received %s.'); - return $this->getBuilder()->where('schedule_id', '=', $schedule) ->where('sequence_id', '=', $index + 1) ->first($this->getColumns()); diff --git a/app/Repositories/Eloquent/UserRepository.php b/app/Repositories/Eloquent/UserRepository.php index 6dbb5d8c3..008916de2 100644 --- a/app/Repositories/Eloquent/UserRepository.php +++ b/app/Repositories/Eloquent/UserRepository.php @@ -1,44 +1,21 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\User; -use Illuminate\Foundation\Application; +use Illuminate\Support\Collection; use Pterodactyl\Repositories\Concerns\Searchable; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; -use Illuminate\Contracts\Config\Repository as ConfigRepository; class UserRepository extends EloquentRepository implements UserRepositoryInterface { use Searchable; /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; - - /** - * UserRepository constructor. + * Return the model backing this repository. * - * @param \Illuminate\Foundation\Application $application - * @param \Illuminate\Contracts\Config\Repository $config - */ - public function __construct(Application $application, ConfigRepository $config) - { - parent::__construct($application); - - $this->config = $config; - } - - /** - * {@inheritdoc} + * @return string */ public function model() { @@ -46,28 +23,26 @@ class UserRepository extends EloquentRepository implements UserRepositoryInterfa } /** - * {@inheritdoc} + * Return all users with counts of servers and subusers of servers. + * + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function getAllUsersWithCounts() + public function getAllUsersWithCounts(): LengthAwarePaginator { - $users = $this->getBuilder()->withCount('servers', 'subuserOf'); - - if ($this->searchTerm) { - $users->search($this->searchTerm); - } - - return $users->paginate( - $this->config->get('pterodactyl.paginate.admin.users'), - $this->getColumns() - ); + return $this->getBuilder()->withCount('servers', 'subuserOf') + ->search($this->getSearchTerm()) + ->paginate(50, $this->getColumns()); } /** - * {@inheritdoc} + * Return all matching models for a user in a format that can be used for dropdowns. + * + * @param string $query + * @return \Illuminate\Support\Collection */ - public function filterUsersByQuery($query) + public function filterUsersByQuery(string $query): Collection { - $this->withColumns([ + $this->setColumns([ 'id', 'email', 'username', 'name_first', 'name_last', ]); diff --git a/app/Repositories/Repository.php b/app/Repositories/Repository.php index f9164d284..c3014a31a 100644 --- a/app/Repositories/Repository.php +++ b/app/Repositories/Repository.php @@ -1,14 +1,8 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Repositories; +use InvalidArgumentException; use Illuminate\Foundation\Application; use Pterodactyl\Contracts\Repository\RepositoryInterface; @@ -43,35 +37,13 @@ abstract class Repository implements RepositoryInterface { $this->app = $application; - $this->setModel($this->model()); + $this->initalizeModel($this->model()); } /** - * Take the provided model and make it accessible to the rest of the repository. + * Return the model backing this repository. * - * @param string|array $model - * @return mixed - */ - protected function setModel($model) - { - if (is_array($model)) { - if (count($model) !== 2) { - throw new \InvalidArgumentException( - printf('setModel expected exactly 2 parameters, %d received.', count($model)) - ); - } - - return $this->model = call_user_func( - $model[1], - $this->app->make($model[0]) - ); - } - - return $this->model = $this->app->make($model); - } - - /** - * @return mixed + * @return string|\Closure|object */ abstract public function model(); @@ -88,10 +60,10 @@ abstract class Repository implements RepositoryInterface /** * Setup column selection functionality. * - * @param array $columns + * @param array|string $columns * @return $this */ - public function withColumns($columns = ['*']) + public function setColumns($columns = ['*']) { $clone = clone $this; $clone->columns = is_array($columns) ? $columns : func_get_args(); @@ -110,15 +82,56 @@ abstract class Repository implements RepositoryInterface } /** - * Set repository to not return a fresh record from the DB when completed. + * Stop repository update functions from returning a fresh + * model when changes are committed. * * @return $this */ - public function withoutFresh() + public function withoutFreshModel() + { + return $this->setFreshModel(false); + } + + /** + * Return a fresh model with a repository updates a model. + * + * @return $this + */ + public function withFreshModel() + { + return $this->setFreshModel(true); + } + + /** + * Set wether or not the repository should return a fresh model + * when changes are committed. + * + * @param bool $fresh + * @return $this + */ + public function setFreshModel(bool $fresh = true) { $clone = clone $this; - $clone->withFresh = false; + $clone->withFresh = $fresh; return $clone; } + + /** + * Take the provided model and make it accessible to the rest of the repository. + * + * @param array $model + * @return mixed + */ + protected function initalizeModel(...$model) + { + switch (count($model)) { + case 1: + return $this->model = $this->app->make($model[0]); + case 2: + return $this->model = call_user_func([$this->app->make($model[0]), $model[1]]); + default: + throw new InvalidArgumentException('Model must be a FQCN or an array with a count of two.'); + } + } } diff --git a/app/Repositories/Wings/BaseRepository.php b/app/Repositories/Wings/BaseRepository.php deleted file mode 100644 index eee71104a..000000000 --- a/app/Repositories/Wings/BaseRepository.php +++ /dev/null @@ -1,146 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Repositories\Wings; - -use GuzzleHttp\Client; -use Webmozart\Assert\Assert; -use Illuminate\Foundation\Application; -use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; -use Illuminate\Contracts\Config\Repository as ConfigRepository; -use Pterodactyl\Contracts\Repository\Daemon\BaseRepositoryInterface; - -class BaseRepository implements BaseRepositoryInterface -{ - /** - * @var \Illuminate\Foundation\Application - */ - protected $app; - - /** - * @var - */ - protected $accessServer; - - /** - * @var - */ - protected $accessToken; - - /** - * @var - */ - protected $node; - - /** - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; - - /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface - */ - protected $nodeRepository; - - /** - * BaseRepository constructor. - * - * @param \Illuminate\Foundation\Application $app - * @param \Illuminate\Contracts\Config\Repository $config - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $nodeRepository - */ - public function __construct( - Application $app, - ConfigRepository $config, - NodeRepositoryInterface $nodeRepository - ) { - $this->app = $app; - $this->config = $config; - $this->nodeRepository = $nodeRepository; - } - - /** - * {@inheritdoc} - */ - public function setNode($id) - { - Assert::numeric($id, 'The first argument passed to setNode must be numeric, received %s.'); - - $this->node = $this->nodeRepository->find($id); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getNode() - { - return $this->node; - } - - /** - * {@inheritdoc} - */ - public function setAccessServer($server = null) - { - Assert::nullOrString($server, 'The first argument passed to setAccessServer must be null or a string, received %s.'); - - $this->accessServer = $server; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getAccessServer() - { - return $this->accessServer; - } - - /** - * {@inheritdoc} - */ - public function setAccessToken($token = null) - { - Assert::nullOrString($token, 'The first argument passed to setAccessToken must be null or a string, received %s.'); - - $this->accessToken = $token; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getAccessToken() - { - return $this->accessToken; - } - - /** - * {@inheritdoc} - */ - public function getHttpClient(array $headers = []) - { - if (! is_null($this->accessToken)) { - $headers['Authorization'] = 'Bearer ' . $this->getAccessToken(); - } elseif (! is_null($this->node)) { - $headers['Authorization'] = 'Bearer ' . $this->getNode()->daemonSecret; - } - - return new Client([ - 'base_uri' => sprintf('%s://%s:%s/v1/', $this->getNode()->scheme, $this->getNode()->fqdn, $this->getNode()->daemonListen), - 'timeout' => $this->config->get('pterodactyl.guzzle.timeout'), - 'connect_timeout' => $this->config->get('pterodactyl.guzzle.connect_timeout'), - 'headers' => $headers, - ]); - } -} diff --git a/app/Repositories/Wings/CommandRepository.php b/app/Repositories/Wings/CommandRepository.php deleted file mode 100644 index 7c1e3eb19..000000000 --- a/app/Repositories/Wings/CommandRepository.php +++ /dev/null @@ -1,30 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Repositories\Wings; - -use Webmozart\Assert\Assert; -use Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface; - -class CommandRepository extends BaseRepository implements CommandRepositoryInterface -{ - /** - * {@inheritdoc} - */ - public function send($command) - { - Assert::stringNotEmpty($command, 'First argument passed to send must be a non-empty string, received %s.'); - - return $this->getHttpClient()->request('POST', 'server/' . $this->getAccessServer() . '/command', [ - 'json' => [ - 'command' => $command, - ], - ]); - } -} diff --git a/app/Repositories/Wings/ConfigurationRepository.php b/app/Repositories/Wings/ConfigurationRepository.php deleted file mode 100644 index db487d6ae..000000000 --- a/app/Repositories/Wings/ConfigurationRepository.php +++ /dev/null @@ -1,24 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Repositories\Wings; - -use Pterodactyl\Exceptions\PterodactylException; -use Pterodactyl\Contracts\Repository\Daemon\ConfigurationRepositoryInterface; - -class ConfigurationRepository extends BaseRepository implements ConfigurationRepositoryInterface -{ - /** - * {@inheritdoc} - */ - public function update(array $overrides = []) - { - throw new PterodactylException('This has not yet been configured.'); - } -} diff --git a/app/Repositories/Wings/FileRepository.php b/app/Repositories/Wings/FileRepository.php deleted file mode 100644 index ab515cab8..000000000 --- a/app/Repositories/Wings/FileRepository.php +++ /dev/null @@ -1,114 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Repositories\Wings; - -use Webmozart\Assert\Assert; -use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; - -class FileRepository extends BaseRepository implements FileRepositoryInterface -{ - /** - * {@inheritdoc} - */ - public function getFileStat($path) - { - Assert::stringNotEmpty($path, 'First argument passed to getStat must be a non-empty string, received %s.'); - - $file = pathinfo($path); - $file['dirname'] = in_array($file['dirname'], ['.', './', '/']) ? null : trim($file['dirname'], '/') . '/'; - - $response = $this->getHttpClient()->request('GET', sprintf( - 'server/' . $this->getAccessServer() . '/file/stat/%s', - rawurlencode($file['dirname'] . $file['basename']) - )); - - return json_decode($response->getBody()); - } - - /** - * {@inheritdoc} - */ - public function getContent($path) - { - Assert::stringNotEmpty($path, 'First argument passed to getContent must be a non-empty string, received %s.'); - - $file = pathinfo($path); - $file['dirname'] = in_array($file['dirname'], ['.', './', '/']) ? null : trim($file['dirname'], '/') . '/'; - - $response = $this->getHttpClient()->request('GET', sprintf( - 'server/' . $this->getAccessServer() . '/file/f/%s', - rawurlencode($file['dirname'] . $file['basename']) - )); - - return object_get(json_decode($response->getBody()), 'content'); - } - - /** - * {@inheritdoc} - */ - public function putContent($path, $content) - { - Assert::stringNotEmpty($path, 'First argument passed to putContent must be a non-empty string, received %s.'); - Assert::string($content, 'Second argument passed to putContent must be a string, received %s.'); - - $file = pathinfo($path); - $file['dirname'] = in_array($file['dirname'], ['.', './', '/']) ? null : trim($file['dirname'], '/') . '/'; - - return $this->getHttpClient()->request('POST', 'server/' . $this->getAccessServer() . '/file/save', [ - 'json' => [ - 'path' => rawurlencode($file['dirname'] . $file['basename']), - 'content' => $content, - ], - ]); - } - - /** - * {@inheritdoc} - */ - public function getDirectory($path) - { - Assert::string($path, 'First argument passed to getDirectory must be a string, received %s.'); - - $response = $this->getHttpClient()->request('GET', sprintf( - 'server/' . $this->getAccessServer() . '/directory/%s', - rawurlencode($path) - )); - - $contents = json_decode($response->getBody()); - $files = []; - $folders = []; - - foreach ($contents as $value) { - if ($value->directory) { - array_push($folders, [ - 'entry' => $value->name, - 'directory' => trim($path, '/'), - 'size' => null, - 'date' => strtotime($value->modified), - 'mime' => $value->mime, - ]); - } elseif ($value->file) { - array_push($files, [ - 'entry' => $value->name, - 'directory' => trim($path, '/'), - 'extension' => pathinfo($value->name, PATHINFO_EXTENSION), - 'size' => human_readable($value->size), - 'date' => strtotime($value->modified), - 'mime' => $value->mime, - ]); - } - } - - return [ - 'files' => $files, - 'folders' => $folders, - ]; - } -} diff --git a/app/Repositories/Wings/PowerRepository.php b/app/Repositories/Wings/PowerRepository.php deleted file mode 100644 index 281cdca28..000000000 --- a/app/Repositories/Wings/PowerRepository.php +++ /dev/null @@ -1,40 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Repositories\Wings; - -use Webmozart\Assert\Assert; -use Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface; -use Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException; - -class PowerRepository extends BaseRepository implements PowerRepositoryInterface -{ - /** - * {@inheritdoc} - */ - public function sendSignal($signal) - { - Assert::stringNotEmpty($signal, 'The first argument passed to sendSignal must be a non-empty string, received %s.'); - - switch ($signal) { - case self::SIGNAL_START: - case self::SIGNAL_STOP: - case self::SIGNAL_RESTART: - case self::SIGNAL_KILL: - return $this->getHttpClient()->request('PUT', 'server/' . $this->getAccessServer() . '/power', [ - 'json' => [ - 'action' => $signal, - ], - ]); - break; - default: - throw new InvalidPowerSignalException('The signal ' . $signal . ' is not defined and could not be processed.'); - } - } -} diff --git a/app/Repositories/Wings/ServerRepository.php b/app/Repositories/Wings/ServerRepository.php deleted file mode 100644 index 3a7653d96..000000000 --- a/app/Repositories/Wings/ServerRepository.php +++ /dev/null @@ -1,89 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Repositories\Wings; - -use Psr\Http\Message\ResponseInterface; -use Pterodactyl\Exceptions\PterodactylException; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface; - -class ServerRepository extends BaseRepository implements ServerRepositoryInterface -{ - /** - * {@inheritdoc} - */ - public function create(array $structure, array $overrides = []): ResponseInterface - { - throw new PterodactylException('This feature is not yet implemented.'); - } - - /** - * {@inheritdoc} - */ - public function update(array $data) - { - throw new PterodactylException('This feature is not yet implemented.'); - } - - /** - * {@inheritdoc} - */ - public function reinstall($data = null) - { - throw new PterodactylException('This feature is not yet implemented.'); - } - - /** - * {@inheritdoc} - */ - public function rebuild() - { - return $this->getHttpClient()->request('POST', 'server/' . $this->getAccessServer() . '/rebuild'); - } - - /** - * {@inheritdoc} - */ - public function suspend() - { - return $this->getHttpClient()->request('POST', 'server/' . $this->getAccessServer() . '/suspend'); - } - - /** - * {@inheritdoc} - */ - public function unsuspend() - { - return $this->getHttpClient()->request('POST', 'server/' . $this->getAccessServer() . '/unsuspend'); - } - - /** - * {@inheritdoc} - */ - public function delete() - { - return $this->getHttpClient()->request('DELETE', 'server/' . $this->getAccessServer()); - } - - /** - * {@inheritdoc} - */ - public function details() - { - return $this->getHttpClient()->request('GET', 'server/' . $this->getAccessServer()); - } - - /** - * {@inheritdoc} - */ - public function revokeAccessKey($key) - { - throw new PterodactylException('This feature is not yet implemented.'); - } -} diff --git a/app/Services/Allocations/SetDefaultAllocationService.php b/app/Services/Allocations/SetDefaultAllocationService.php index 66a858be3..6e9010316 100644 --- a/app/Services/Allocations/SetDefaultAllocationService.php +++ b/app/Services/Allocations/SetDefaultAllocationService.php @@ -83,11 +83,11 @@ class SetDefaultAllocationService } $this->connection->beginTransaction(); - $this->serverRepository->withoutFresh()->update($server->id, ['allocation_id' => $model->id]); + $this->serverRepository->withoutFreshModel()->update($server->id, ['allocation_id' => $model->id]); // Update on the daemon. try { - $this->daemonRepository->setAccessServer($server->uuid)->setNode($server->node_id)->update([ + $this->daemonRepository->setServer($server)->update([ 'build' => [ 'default' => [ 'ip' => $model->ip, diff --git a/app/Services/Api/PermissionService.php b/app/Services/Api/PermissionService.php index 14bdc235d..24dbf381f 100644 --- a/app/Services/Api/PermissionService.php +++ b/app/Services/Api/PermissionService.php @@ -40,7 +40,7 @@ class PermissionService public function create($key, $permission) { // @todo handle an array of permissions to do a mass assignment? - return $this->repository->withoutFresh()->create([ + return $this->repository->withoutFreshModel()->create([ 'key_id' => $key, 'permission' => $permission, ]); diff --git a/app/Services/DaemonKeys/DaemonKeyCreationService.php b/app/Services/DaemonKeys/DaemonKeyCreationService.php index b9431d28f..23aa1c0aa 100644 --- a/app/Services/DaemonKeys/DaemonKeyCreationService.php +++ b/app/Services/DaemonKeys/DaemonKeyCreationService.php @@ -75,7 +75,7 @@ class DaemonKeyCreationService { $secret = DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40); - $this->repository->withoutFresh()->create([ + $this->repository->withoutFreshModel()->create([ 'user_id' => $user, 'server_id' => $server, 'secret' => $secret, diff --git a/app/Services/DaemonKeys/DaemonKeyDeletionService.php b/app/Services/DaemonKeys/DaemonKeyDeletionService.php index 6cc605eb8..553258ce7 100644 --- a/app/Services/DaemonKeys/DaemonKeyDeletionService.php +++ b/app/Services/DaemonKeys/DaemonKeyDeletionService.php @@ -108,7 +108,7 @@ class DaemonKeyDeletionService $this->repository->delete($key->id); try { - $this->daemonRepository->setNode($server->node_id)->revokeAccessKey($key->secret); + $this->daemonRepository->setServer($server)->revokeAccessKey($key->secret); } catch (RequestException $exception) { $response = $exception->getResponse(); $this->connection->rollBack(); diff --git a/app/Services/DaemonKeys/DaemonKeyUpdateService.php b/app/Services/DaemonKeys/DaemonKeyUpdateService.php index 337b3d173..750d833d9 100644 --- a/app/Services/DaemonKeys/DaemonKeyUpdateService.php +++ b/app/Services/DaemonKeys/DaemonKeyUpdateService.php @@ -78,7 +78,7 @@ class DaemonKeyUpdateService Assert::integerish($key, 'First argument passed to handle must be an integer, received %s.'); $secret = DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40); - $this->repository->withoutFresh()->update($key, [ + $this->repository->withoutFreshModel()->update($key, [ 'secret' => $secret, 'expires_at' => $this->carbon->now()->addMinutes($this->config->get('pterodactyl.api.key_expire_time'))->toDateTimeString(), ]); diff --git a/app/Services/DaemonKeys/RevokeMultipleDaemonKeysService.php b/app/Services/DaemonKeys/RevokeMultipleDaemonKeysService.php index 93b8b2041..7059be88e 100644 --- a/app/Services/DaemonKeys/RevokeMultipleDaemonKeysService.php +++ b/app/Services/DaemonKeys/RevokeMultipleDaemonKeysService.php @@ -46,22 +46,20 @@ class RevokeMultipleDaemonKeysService * * @param \Pterodactyl\Models\User $user * @param bool $ignoreConnectionErrors - * - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function handle(User $user, bool $ignoreConnectionErrors = false) { $keys = $this->repository->getKeysForRevocation($user); - $keys->groupBy('server.node_id')->each(function ($group, $node) use ($ignoreConnectionErrors) { + $keys->groupBy('node.id')->each(function ($group, $nodeId) use ($ignoreConnectionErrors) { try { - $this->daemonRepository->setNode($node)->revokeAccessKey(collect($group)->pluck('secret')->toArray()); + $this->daemonRepository->setNode(collect($group)->first()->getRelation('node'))->revokeAccessKey(collect($group)->pluck('secret')->toArray()); } catch (RequestException $exception) { if (! $ignoreConnectionErrors) { throw new DaemonConnectionException($exception); } - $this->setConnectionException($node, $exception); + $this->setConnectionException($nodeId, $exception); } $this->repository->deleteKeys(collect($group)->pluck('id')->toArray()); diff --git a/app/Services/Databases/DatabasePasswordService.php b/app/Services/Databases/DatabasePasswordService.php index 4f6443ced..8f8a9582d 100644 --- a/app/Services/Databases/DatabasePasswordService.php +++ b/app/Services/Databases/DatabasePasswordService.php @@ -69,7 +69,7 @@ class DatabasePasswordService $this->dynamic->set('dynamic', $database->database_host_id); $this->connection->beginTransaction(); - $updated = $this->repository->withoutFresh()->update($database->id, [ + $updated = $this->repository->withoutFreshModel()->update($database->id, [ 'password' => $this->encrypter->encrypt($password), ]); diff --git a/app/Services/Eggs/EggUpdateService.php b/app/Services/Eggs/EggUpdateService.php index 2932b7457..14d655178 100644 --- a/app/Services/Eggs/EggUpdateService.php +++ b/app/Services/Eggs/EggUpdateService.php @@ -57,6 +57,6 @@ class EggUpdateService } } - $this->repository->withoutFresh()->update($egg->id, $data); + $this->repository->withoutFreshModel()->update($egg->id, $data); } } diff --git a/app/Services/Eggs/Scripts/InstallScriptService.php b/app/Services/Eggs/Scripts/InstallScriptService.php index 094469944..0d9e66bcc 100644 --- a/app/Services/Eggs/Scripts/InstallScriptService.php +++ b/app/Services/Eggs/Scripts/InstallScriptService.php @@ -52,7 +52,7 @@ class InstallScriptService } } - $this->repository->withoutFresh()->update($egg->id, [ + $this->repository->withoutFreshModel()->update($egg->id, [ 'script_install' => array_get($data, 'script_install'), 'script_is_privileged' => array_get($data, 'script_is_privileged', 1), 'script_entry' => array_get($data, 'script_entry'), diff --git a/app/Services/Eggs/Sharing/EggUpdateImporterService.php b/app/Services/Eggs/Sharing/EggUpdateImporterService.php index f0df63ad7..50342667d 100644 --- a/app/Services/Eggs/Sharing/EggUpdateImporterService.php +++ b/app/Services/Eggs/Sharing/EggUpdateImporterService.php @@ -89,14 +89,14 @@ class EggUpdateImporterService // Update Existing Variables collect($parsed->variables)->each(function ($variable) use ($egg) { - $this->variableRepository->withoutFresh()->updateOrCreate([ + $this->variableRepository->withoutFreshModel()->updateOrCreate([ 'egg_id' => $egg, 'env_variable' => $variable->env_variable, ], collect($variable)->except(['egg_id', 'env_variable'])->toArray()); }); $imported = collect($parsed->variables)->pluck('env_variable')->toArray(); - $existing = $this->variableRepository->withColumns(['id', 'env_variable'])->findWhere([['egg_id', '=', $egg]]); + $existing = $this->variableRepository->setColumns(['id', 'env_variable'])->findWhere([['egg_id', '=', $egg]]); // Delete variables not present in the import. collect($existing)->each(function ($variable) use ($egg, $imported) { diff --git a/app/Services/Eggs/Variables/VariableUpdateService.php b/app/Services/Eggs/Variables/VariableUpdateService.php index 0249e4fba..12930f892 100644 --- a/app/Services/Eggs/Variables/VariableUpdateService.php +++ b/app/Services/Eggs/Variables/VariableUpdateService.php @@ -49,7 +49,7 @@ class VariableUpdateService ])); } - $search = $this->repository->withColumns('id')->findCountWhere([ + $search = $this->repository->setColumns('id')->findCountWhere([ ['env_variable', '=', array_get($data, 'env_variable')], ['egg_id', '=', $variable->egg_id], ['id', '!=', $variable->id], @@ -64,7 +64,7 @@ class VariableUpdateService $options = array_get($data, 'options') ?? []; - return $this->repository->withoutFresh()->update($variable->id, array_merge($data, [ + return $this->repository->withoutFreshModel()->update($variable->id, array_merge($data, [ 'user_viewable' => in_array('user_viewable', $options), 'user_editable' => in_array('user_editable', $options), ])); diff --git a/app/Services/Nests/NestUpdateService.php b/app/Services/Nests/NestUpdateService.php index 12f3a506a..c3cdcdd5d 100644 --- a/app/Services/Nests/NestUpdateService.php +++ b/app/Services/Nests/NestUpdateService.php @@ -42,6 +42,6 @@ class NestUpdateService unset($data['author']); } - $this->repository->withoutFresh()->update($nest, $data); + $this->repository->withoutFreshModel()->update($nest, $data); } } diff --git a/app/Services/Nodes/NodeDeletionService.php b/app/Services/Nodes/NodeDeletionService.php index 9df62be6a..e4d2ed999 100644 --- a/app/Services/Nodes/NodeDeletionService.php +++ b/app/Services/Nodes/NodeDeletionService.php @@ -63,7 +63,7 @@ class NodeDeletionService $node = $node->id; } - $servers = $this->serverRepository->withColumns('id')->findCountWhere([['node_id', '=', $node]]); + $servers = $this->serverRepository->setColumns('id')->findCountWhere([['node_id', '=', $node]]); if ($servers > 0) { throw new HasActiveServersException($this->translator->trans('exceptions.node.servers_attached')); } diff --git a/app/Services/Nodes/NodeUpdateService.php b/app/Services/Nodes/NodeUpdateService.php index 76e304dd3..124a5b825 100644 --- a/app/Services/Nodes/NodeUpdateService.php +++ b/app/Services/Nodes/NodeUpdateService.php @@ -65,11 +65,11 @@ class NodeUpdateService if ($this->getUpdatedModel()) { $response = $this->repository->update($node->id, $data); } else { - $response = $this->repository->withoutFresh()->update($node->id, $data); + $response = $this->repository->withoutFreshModel()->update($node->id, $data); } try { - $this->configRepository->setNode($node->id)->update(); + $this->configRepository->setNode($node)->update(); } catch (RequestException $exception) { throw new DaemonConnectionException($exception); } diff --git a/app/Services/Packs/PackDeletionService.php b/app/Services/Packs/PackDeletionService.php index 5118990ad..9d4743310 100644 --- a/app/Services/Packs/PackDeletionService.php +++ b/app/Services/Packs/PackDeletionService.php @@ -69,7 +69,7 @@ class PackDeletionService public function handle($pack) { if (! $pack instanceof Pack) { - $pack = $this->repository->withColumns(['id', 'uuid'])->find($pack); + $pack = $this->repository->setColumns(['id', 'uuid'])->find($pack); } $count = $this->serverRepository->findCountWhere([['pack_id', '=', $pack->id]]); diff --git a/app/Services/Packs/PackUpdateService.php b/app/Services/Packs/PackUpdateService.php index bb84f7b98..a22a55b9f 100644 --- a/app/Services/Packs/PackUpdateService.php +++ b/app/Services/Packs/PackUpdateService.php @@ -54,7 +54,7 @@ class PackUpdateService public function handle($pack, array $data) { if (! $pack instanceof Pack) { - $pack = $this->repository->withColumns(['id', 'egg_id'])->find($pack); + $pack = $this->repository->setColumns(['id', 'egg_id'])->find($pack); } if ((int) array_get($data, 'egg_id', $pack->egg_id) !== $pack->egg_id) { @@ -70,6 +70,6 @@ class PackUpdateService $data['visible'] = isset($data['visible']); $data['locked'] = isset($data['locked']); - return $this->repository->withoutFresh()->update($pack->id, $data); + return $this->repository->withoutFreshModel()->update($pack->id, $data); } } diff --git a/app/Services/Schedules/ProcessScheduleService.php b/app/Services/Schedules/ProcessScheduleService.php index f5420b3a3..ec6ea5f12 100644 --- a/app/Services/Schedules/ProcessScheduleService.php +++ b/app/Services/Schedules/ProcessScheduleService.php @@ -1,16 +1,9 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Schedules; +use Carbon\Carbon; use Cron\CronExpression; -use Webmozart\Assert\Assert; use Pterodactyl\Models\Schedule; use Pterodactyl\Services\Schedules\Tasks\RunTaskService; use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; @@ -20,12 +13,17 @@ class ProcessScheduleService /** * @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface */ - protected $repository; + private $repository; /** * @var \Pterodactyl\Services\Schedules\Tasks\RunTaskService */ - protected $runnerService; + private $runnerService; + + /** + * @var \Carbon\Carbon|null + */ + private $runTimeOverride; /** * ProcessScheduleService constructor. @@ -39,23 +37,31 @@ class ProcessScheduleService $this->runnerService = $runnerService; } + /** + * Set the time that this schedule should be run at. This will override the time + * defined on the schedule itself. Useful for triggering one-off task runs. + * + * @param \Carbon\Carbon $time + * @return $this + */ + public function setRunTimeOverride(Carbon $time) + { + $this->runTimeOverride = $time; + + return $this; + } + /** * Process a schedule and push the first task onto the queue worker. * - * @param int|\Pterodactyl\Models\Schedule $schedule + * @param \Pterodactyl\Models\Schedule $schedule * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function handle($schedule) + public function handle(Schedule $schedule) { - Assert::true(($schedule instanceof Schedule || is_digit($schedule)), - 'First argument passed to handle must be instance of \Pterodactyl\Models\Schedule or an integer, received %s.' - ); - - if (($schedule instanceof Schedule && ! $schedule->relationLoaded('tasks')) || ! $schedule instanceof Schedule) { - $schedule = $this->repository->getScheduleWithTasks(is_digit($schedule) ? $schedule : $schedule->id); - } + $this->repository->loadTasks($schedule); $formattedCron = sprintf('%s %s %s * %s *', $schedule->cron_minute, @@ -66,10 +72,25 @@ class ProcessScheduleService $this->repository->update($schedule->id, [ 'is_processing' => true, - 'next_run_at' => CronExpression::factory($formattedCron)->getNextRunDate(), + 'next_run_at' => $this->getRunAtTime($formattedCron), ]); - $task = $schedule->tasks->where('sequence_id', 1)->first(); + $task = $schedule->getRelation('tasks')->where('sequence_id', 1)->first(); $this->runnerService->handle($task); } + + /** + * Get the timestamp to store in the database as the next_run time for a schedule. + * + * @param string $formatted + * @return \DateTime|string + */ + private function getRunAtTime(string $formatted) + { + if ($this->runTimeOverride instanceof Carbon) { + return $this->runTimeOverride->toDateTimeString(); + } + + return CronExpression::factory($formatted)->getNextRunDate(); + } } diff --git a/app/Services/Schedules/ScheduleCreationService.php b/app/Services/Schedules/ScheduleCreationService.php index 5dbe9e9f3..9f676e246 100644 --- a/app/Services/Schedules/ScheduleCreationService.php +++ b/app/Services/Schedules/ScheduleCreationService.php @@ -1,16 +1,8 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Schedules; use Cron\CronExpression; -use Webmozart\Assert\Assert; use Pterodactyl\Models\Server; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Services\Schedules\Tasks\TaskCreationService; @@ -53,37 +45,32 @@ class ScheduleCreationService /** * Create a new schedule for a specific server. * - * @param int|\Pterodactyl\Models\Server $server - * @param array $data - * @param array $tasks + * @param \Pterodactyl\Models\Server $server + * @param array $data + * @param array $tasks * @return \Pterodactyl\Models\Schedule * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException */ - public function handle($server, array $data, array $tasks = []) + public function handle(Server $server, array $data, array $tasks = []) { - Assert::true(($server instanceof Server || is_digit($server)), - 'First argument passed to handle must be numeric or instance of \Pterodactyl\Models\Server, received %s.' - ); - - $server = ($server instanceof Server) ? $server->id : $server; - $data['server_id'] = $server; - $data['next_run_at'] = $this->getCronTimestamp($data); + $data = array_merge($data, [ + 'server_id' => $server->id, + 'next_run_at' => $this->getCronTimestamp($data), + ]); $this->connection->beginTransaction(); $schedule = $this->repository->create($data); - if (! empty($tasks)) { - foreach ($tasks as $index => $task) { - $this->taskCreationService->handle($schedule, [ - 'time_interval' => array_get($task, 'time_interval'), - 'time_value' => array_get($task, 'time_value'), - 'sequence_id' => $index + 1, - 'action' => array_get($task, 'action'), - 'payload' => array_get($task, 'payload'), - ], false); - } + foreach ($tasks as $index => $task) { + $this->taskCreationService->handle($schedule, [ + 'time_interval' => array_get($task, 'time_interval'), + 'time_value' => array_get($task, 'time_value'), + 'sequence_id' => $index + 1, + 'action' => array_get($task, 'action'), + 'payload' => array_get($task, 'payload'), + ], false); } $this->connection->commit(); diff --git a/app/Services/Schedules/Tasks/TaskCreationService.php b/app/Services/Schedules/Tasks/TaskCreationService.php index 9ea1c1783..8ed06e5fd 100644 --- a/app/Services/Schedules/Tasks/TaskCreationService.php +++ b/app/Services/Schedules/Tasks/TaskCreationService.php @@ -56,7 +56,7 @@ class TaskCreationService throw new TaskIntervalTooLongException(trans('exceptions.tasks.chain_interval_too_long')); } - $repository = ($returnModel) ? $this->repository : $this->repository->withoutFresh(); + $repository = ($returnModel) ? $this->repository : $this->repository->withoutFreshModel(); $task = $repository->create([ 'schedule_id' => $schedule, 'sequence_id' => $data['sequence_id'], diff --git a/app/Services/Servers/BuildModificationService.php b/app/Services/Servers/BuildModificationService.php index 9aa1fa4c5..e952941c8 100644 --- a/app/Services/Servers/BuildModificationService.php +++ b/app/Services/Servers/BuildModificationService.php @@ -154,7 +154,7 @@ class BuildModificationService })->toArray()); try { - $this->daemonServerRepository->setNode($server->node_id)->setAccessServer($server->uuid)->update([ + $this->daemonServerRepository->setServer($server)->update([ 'build' => $this->getBuild(), ]); diff --git a/app/Services/Servers/ContainerRebuildService.php b/app/Services/Servers/ContainerRebuildService.php index 49c51ed5a..0dc25b0e4 100644 --- a/app/Services/Servers/ContainerRebuildService.php +++ b/app/Services/Servers/ContainerRebuildService.php @@ -1,77 +1,42 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Servers; -use Illuminate\Log\Writer; use Pterodactyl\Models\Server; use GuzzleHttp\Exception\RequestException; -use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; +use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface; class ContainerRebuildService { - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface - */ - protected $daemonServerRepository; - /** * @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface */ - protected $repository; - - /** - * @var \Illuminate\Log\Writer - */ - protected $writer; + private $repository; /** * ContainerRebuildService constructor. * - * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonServerRepository - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - * @param \Illuminate\Log\Writer $writer + * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $repository */ - public function __construct( - DaemonServerRepositoryInterface $daemonServerRepository, - ServerRepositoryInterface $repository, - Writer $writer - ) { - $this->daemonServerRepository = $daemonServerRepository; + public function __construct(ServerRepositoryInterface $repository) + { $this->repository = $repository; - $this->writer = $writer; } /** * Mark a server for rebuild on next boot cycle. * - * @param int|\Pterodactyl\Models\Server $server + * @param \Pterodactyl\Models\Server $server * - * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ - public function rebuild($server) + public function handle(Server $server) { - if (! $server instanceof Server) { - $server = $this->repository->find($server); - } - try { - $this->daemonServerRepository->setNode($server->node_id)->setAccessServer($server->uuid)->rebuild(); + $this->repository->setServer($server)->rebuild(); } catch (RequestException $exception) { - $response = $exception->getResponse(); - $this->writer->warning($exception); - - throw new DisplayException(trans('admin/server.exceptions.daemon_exception', [ - 'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(), - ])); + throw new DaemonConnectionException($exception); } } } diff --git a/app/Services/Servers/DetailsModificationService.php b/app/Services/Servers/DetailsModificationService.php index d18bccd73..5ba4f779b 100644 --- a/app/Services/Servers/DetailsModificationService.php +++ b/app/Services/Servers/DetailsModificationService.php @@ -94,7 +94,7 @@ class DetailsModificationService } $this->connection->beginTransaction(); - $this->repository->withoutFresh()->update($server->id, [ + $this->repository->withoutFreshModel()->update($server->id, [ 'owner_id' => array_get($data, 'owner_id'), 'name' => array_get($data, 'name'), 'description' => array_get($data, 'description', ''), @@ -125,10 +125,10 @@ class DetailsModificationService } $this->connection->beginTransaction(); - $this->repository->withoutFresh()->update($server->id, ['image' => $image]); + $this->repository->withoutFreshModel()->update($server->id, ['image' => $image]); try { - $this->daemonServerRepository->setNode($server->node_id)->setAccessServer($server->uuid)->update([ + $this->daemonServerRepository->setServer($server)->update([ 'build' => [ 'image' => $image, ], diff --git a/app/Services/Servers/ReinstallServerService.php b/app/Services/Servers/ReinstallServerService.php index d5292adf2..682813e36 100644 --- a/app/Services/Servers/ReinstallServerService.php +++ b/app/Services/Servers/ReinstallServerService.php @@ -9,12 +9,11 @@ namespace Pterodactyl\Services\Servers; -use Illuminate\Log\Writer; use Pterodactyl\Models\Server; use GuzzleHttp\Exception\RequestException; use Illuminate\Database\ConnectionInterface; -use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; class ReinstallServerService @@ -34,29 +33,21 @@ class ReinstallServerService */ protected $repository; - /** - * @var \Illuminate\Log\Writer - */ - protected $writer; - /** * ReinstallService constructor. * * @param \Illuminate\Database\ConnectionInterface $database * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonServerRepository * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository - * @param \Illuminate\Log\Writer $writer */ public function __construct( ConnectionInterface $database, DaemonServerRepositoryInterface $daemonServerRepository, - ServerRepositoryInterface $repository, - Writer $writer + ServerRepositoryInterface $repository ) { $this->daemonServerRepository = $daemonServerRepository; $this->database = $database; $this->repository = $repository; - $this->writer = $writer; } /** @@ -64,6 +55,7 @@ class ReinstallServerService * * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function reinstall($server) { @@ -72,20 +64,15 @@ class ReinstallServerService } $this->database->beginTransaction(); - $this->repository->withoutFresh()->update($server->id, [ + $this->repository->withoutFreshModel()->update($server->id, [ 'installed' => 0, ]); try { - $this->daemonServerRepository->setNode($server->node_id)->setAccessServer($server->uuid)->reinstall(); + $this->daemonServerRepository->setServer($server)->reinstall(); $this->database->commit(); } catch (RequestException $exception) { - $response = $exception->getResponse(); - $this->writer->warning($exception); - - throw new DisplayException(trans('admin/server.exceptions.daemon_exception', [ - 'code' => is_null($response) ? 'E_CONN_REFUSED' : $response->getStatusCode(), - ])); + throw new DaemonConnectionException($exception); } } } diff --git a/app/Services/Servers/ServerCreationService.php b/app/Services/Servers/ServerCreationService.php index 5aa1d7b24..1859ec77a 100644 --- a/app/Services/Servers/ServerCreationService.php +++ b/app/Services/Servers/ServerCreationService.php @@ -118,7 +118,7 @@ class ServerCreationService 'uuidShort' => str_random(8), 'node_id' => array_get($data, 'node_id'), 'name' => array_get($data, 'name'), - 'description' => array_get($data, 'description', ''), + 'description' => array_get($data, 'description') ?? '', 'skip_scripts' => isset($data['skip_scripts']), 'suspended' => false, 'owner_id' => array_get($data, 'owner_id'), @@ -127,7 +127,7 @@ class ServerCreationService 'disk' => array_get($data, 'disk'), 'io' => array_get($data, 'io'), 'cpu' => array_get($data, 'cpu'), - 'oom_disabled' => isset($data['oom_disabled']), + 'oom_disabled' => false, 'allocation_id' => array_get($data, 'allocation_id'), 'nest_id' => array_get($data, 'nest_id'), 'egg_id' => array_get($data, 'egg_id'), @@ -163,8 +163,9 @@ class ServerCreationService $structure = $this->configurationStructureService->handle($server); // Create the server on the daemon & commit it to the database. + $node = $this->nodeRepository->find($server->node_id); try { - $this->daemonServerRepository->setNode($server->node_id)->create($structure, [ + $this->daemonServerRepository->setNode($node)->create($structure, [ 'start_on_completion' => (bool) array_get($data, 'start_on_completion', false), ]); $this->connection->commit(); diff --git a/app/Services/Servers/ServerDeletionService.php b/app/Services/Servers/ServerDeletionService.php index 1129a187c..e6c098165 100644 --- a/app/Services/Servers/ServerDeletionService.php +++ b/app/Services/Servers/ServerDeletionService.php @@ -106,11 +106,11 @@ class ServerDeletionService public function handle($server) { if (! $server instanceof Server) { - $server = $this->repository->withColumns(['id', 'node_id', 'uuid'])->find($server); + $server = $this->repository->setColumns(['id', 'node_id', 'uuid'])->find($server); } try { - $this->daemonServerRepository->setNode($server->node_id)->setAccessServer($server->uuid)->delete(); + $this->daemonServerRepository->setServer($server)->delete(); } catch (RequestException $exception) { $response = $exception->getResponse(); @@ -128,7 +128,7 @@ class ServerDeletionService } $this->connection->beginTransaction(); - $this->databaseRepository->withColumns('id')->findWhere([['server_id', '=', $server->id]])->each(function ($item) { + $this->databaseRepository->setColumns('id')->findWhere([['server_id', '=', $server->id]])->each(function ($item) { $this->databaseManagementService->delete($item->id); }); diff --git a/app/Services/Servers/StartupModificationService.php b/app/Services/Servers/StartupModificationService.php index 784b60a05..76a10ad0d 100644 --- a/app/Services/Servers/StartupModificationService.php +++ b/app/Services/Servers/StartupModificationService.php @@ -91,7 +91,7 @@ class StartupModificationService $results = $this->validatorService->handle(array_get($data, 'egg_id', $server->egg_id), array_get($data, 'environment', [])); $results->each(function ($result) use ($server) { - $this->serverVariableRepository->withoutFresh()->updateOrCreate([ + $this->serverVariableRepository->withoutFreshModel()->updateOrCreate([ 'server_id' => $server->id, 'variable_id' => $result->id, ], [ @@ -112,7 +112,7 @@ class StartupModificationService ]); try { - $this->daemonServerRepository->setNode($server->node_id)->setAccessServer($server->uuid)->update($daemonData); + $this->daemonServerRepository->setServer($server)->update($daemonData); } catch (RequestException $exception) { $this->connection->rollBack(); throw new DaemonConnectionException($exception); diff --git a/app/Services/Servers/SuspensionService.php b/app/Services/Servers/SuspensionService.php index dec29da50..1feb2887d 100644 --- a/app/Services/Servers/SuspensionService.php +++ b/app/Services/Servers/SuspensionService.php @@ -91,12 +91,12 @@ class SuspensionService } $this->database->beginTransaction(); - $this->repository->withoutFresh()->update($server->id, [ + $this->repository->withoutFreshModel()->update($server->id, [ 'suspended' => $action === 'suspend', ]); try { - $this->daemonServerRepository->setNode($server->node_id)->setAccessServer($server->uuid)->$action(); + $this->daemonServerRepository->setServer($server)->$action(); $this->database->commit(); return true; diff --git a/app/Services/Sftp/AuthenticateUsingPasswordService.php b/app/Services/Sftp/AuthenticateUsingPasswordService.php index 16ac56b67..1cf64bd8e 100644 --- a/app/Services/Sftp/AuthenticateUsingPasswordService.php +++ b/app/Services/Sftp/AuthenticateUsingPasswordService.php @@ -68,7 +68,7 @@ class AuthenticateUsingPasswordService } try { - $user = $this->userRepository->withColumns(['id', 'root_admin', 'password'])->findFirstWhere([['username', '=', $username]]); + $user = $this->userRepository->setColumns(['id', 'root_admin', 'password'])->findFirstWhere([['username', '=', $username]]); if (! password_verify($password, $user->password)) { throw new AuthenticationException; @@ -77,7 +77,7 @@ class AuthenticateUsingPasswordService throw new AuthenticationException; } - $server = $this->repository->withColumns(['id', 'node_id', 'owner_id', 'uuid'])->getByUuid($server); + $server = $this->repository->setColumns(['id', 'node_id', 'owner_id', 'uuid'])->getByUuid($server); if ($server->node_id !== $node || (! $user->root_admin && $server->owner_id !== $user->id)) { throw new RecordNotFoundException; } diff --git a/app/Services/Subusers/PermissionCreationService.php b/app/Services/Subusers/PermissionCreationService.php index d44d63008..0173e8015 100644 --- a/app/Services/Subusers/PermissionCreationService.php +++ b/app/Services/Subusers/PermissionCreationService.php @@ -56,6 +56,6 @@ class PermissionCreationService } } - $this->repository->withoutFresh()->insert($insertPermissions); + $this->repository->withoutFreshModel()->insert($insertPermissions); } } diff --git a/app/Services/Subusers/SubuserUpdateService.php b/app/Services/Subusers/SubuserUpdateService.php index 9cf02e4b4..f56e47b9d 100644 --- a/app/Services/Subusers/SubuserUpdateService.php +++ b/app/Services/Subusers/SubuserUpdateService.php @@ -96,7 +96,7 @@ class SubuserUpdateService try { $token = $this->keyProviderService->handle($subuser->getRelation('server'), $subuser->getRelation('user'), false); - $this->daemonRepository->setNode($subuser->getRelation('server')->node_id)->revokeAccessKey($token); + $this->daemonRepository->setServer($subuser->getRelation('server'))->revokeAccessKey($token); } catch (RequestException $exception) { $this->connection->rollBack(); throw new DaemonConnectionException($exception); diff --git a/app/Services/Users/ToggleTwoFactorService.php b/app/Services/Users/ToggleTwoFactorService.php index e03a76389..6fe8bc9dd 100644 --- a/app/Services/Users/ToggleTwoFactorService.php +++ b/app/Services/Users/ToggleTwoFactorService.php @@ -75,7 +75,7 @@ class ToggleTwoFactorService throw new TwoFactorAuthenticationTokenInvalid; } - $this->repository->withoutFresh()->update($user->id, [ + $this->repository->withoutFreshModel()->update($user->id, [ 'totp_authenticated_at' => Carbon::now(), 'use_totp' => (is_null($toggleState) ? ! $user->use_totp : $toggleState), ]); diff --git a/app/Services/Users/TwoFactorSetupService.php b/app/Services/Users/TwoFactorSetupService.php index a8554ccfc..4d2ecff8a 100644 --- a/app/Services/Users/TwoFactorSetupService.php +++ b/app/Services/Users/TwoFactorSetupService.php @@ -72,7 +72,7 @@ class TwoFactorSetupService $secret = $this->google2FA->generateSecretKey($this->config->get('pterodactyl.auth.2fa.bytes')); $image = $this->google2FA->getQRCodeGoogleUrl($this->config->get('app.name'), $user->email, $secret); - $this->repository->withoutFresh()->update($user->id, [ + $this->repository->withoutFreshModel()->update($user->id, [ 'totp_secret' => $this->encrypter->encrypt($secret), ]); diff --git a/app/Services/Users/UserDeletionService.php b/app/Services/Users/UserDeletionService.php index 6b335be4b..942e76faf 100644 --- a/app/Services/Users/UserDeletionService.php +++ b/app/Services/Users/UserDeletionService.php @@ -63,7 +63,7 @@ class UserDeletionService $user = $user->id; } - $servers = $this->serverRepository->withColumns('id')->findCountWhere([['owner_id', '=', $user]]); + $servers = $this->serverRepository->setColumns('id')->findCountWhere([['owner_id', '=', $user]]); if ($servers > 0) { throw new DisplayException($this->translator->trans('admin/user.exceptions.user_has_servers')); } diff --git a/app/Services/Users/UserUpdateService.php b/app/Services/Users/UserUpdateService.php index 9e755dd9f..1a767ae1a 100644 --- a/app/Services/Users/UserUpdateService.php +++ b/app/Services/Users/UserUpdateService.php @@ -55,7 +55,6 @@ class UserUpdateService * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function handle(User $user, array $data): Collection { diff --git a/config/database.php b/config/database.php index 15d708f16..acbc8627b 100644 --- a/config/database.php +++ b/config/database.php @@ -40,7 +40,7 @@ return [ 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', - 'prefix' => '', + 'prefix' => env('DB_PREFIX', ''), 'strict' => env('DB_STRICT_MODE', false), ], ], diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php index c5501d119..6e585d4d6 100644 --- a/database/factories/ModelFactory.php +++ b/database/factories/ModelFactory.php @@ -186,6 +186,7 @@ $factory->define(Pterodactyl\Models\Database::class, function (Faker $faker) { 'database_host_id' => $faker->randomNumber(), 'database' => str_random(10), 'username' => str_random(10), + 'remote' => '%', 'password' => $password ?: bcrypt('test123'), 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), diff --git a/database/seeds/EggSeeder.php b/database/seeds/EggSeeder.php index 4f5915a2d..245d74ac3 100644 --- a/database/seeds/EggSeeder.php +++ b/database/seeds/EggSeeder.php @@ -122,7 +122,7 @@ class EggSeeder extends Seeder $file = new UploadedFile($file->getPathname(), $file->getFilename(), 'application/json', $file->getSize()); try { - $egg = $this->repository->withColumns('id')->findFirstWhere([ + $egg = $this->repository->setColumns('id')->findFirstWhere([ ['author', '=', $decoded->author], ['name', '=', $decoded->name], ['nest_id', '=', $nest->id], diff --git a/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json b/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json index af296e55d..683e9e7df 100644 --- a/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json +++ b/database/seeds/eggs/source-engine/egg-custom-source-engine-game.json @@ -6,7 +6,7 @@ "exported_at": "2017-11-03T22:30:06-05:00", "name": "Custom Source Engine Game", "author": "support@pterodactyl.io", - "description": "This option allows modifying the startup arguments and other details to run a custo SRCDS based game on the panel.", + "description": "This option allows modifying the startup arguments and other details to run a custom SRCDS based game on the panel.", "image": "quay.io\/pterodactyl\/core:source", "startup": ".\/srcds_run -game {{SRCDS_GAME}} -console -port {{SERVER_PORT}} +map {{SRCDS_MAP}} +ip 0.0.0.0 -strictportbind -norestart", "config": { @@ -42,4 +42,4 @@ "rules": "required|alpha_dash|between:1,100" } ] -} \ No newline at end of file +} diff --git a/public/js/laroute.js b/public/js/laroute.js index 3c0ad073f..ea2d1a03d 100644 --- a/public/js/laroute.js +++ b/public/js/laroute.js @@ -6,7 +6,7 @@ absolute: false, rootUrl: 'http://pterodactyl.app', - routes : [{"host":null,"methods":["GET","HEAD"],"uri":"\/","name":"index","action":"Pterodactyl\Http\Controllers\Base\IndexController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"status\/{server}","name":"index.status","action":"Pterodactyl\Http\Controllers\Base\IndexController@status"},{"host":null,"methods":["GET","HEAD"],"uri":"account","name":"account","action":"Pterodactyl\Http\Controllers\Base\AccountController@index"},{"host":null,"methods":["POST"],"uri":"account","name":null,"action":"Pterodactyl\Http\Controllers\Base\AccountController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api","name":"account.api","action":"Pterodactyl\Http\Controllers\Base\APIController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api\/new","name":"account.api.new","action":"Pterodactyl\Http\Controllers\Base\APIController@create"},{"host":null,"methods":["POST"],"uri":"account\/api\/new","name":null,"action":"Pterodactyl\Http\Controllers\Base\APIController@store"},{"host":null,"methods":["DELETE"],"uri":"account\/api\/revoke\/{key}","name":"account.api.revoke","action":"Pterodactyl\Http\Controllers\Base\APIController@revoke"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security","name":"account.security","action":"Pterodactyl\Http\Controllers\Base\SecurityController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security\/revoke\/{id}","name":"account.security.revoke","action":"Pterodactyl\Http\Controllers\Base\SecurityController@revoke"},{"host":null,"methods":["PUT"],"uri":"account\/security\/totp","name":"account.security.totp","action":"Pterodactyl\Http\Controllers\Base\SecurityController@generateTotp"},{"host":null,"methods":["POST"],"uri":"account\/security\/totp","name":"account.security.totp.set","action":"Pterodactyl\Http\Controllers\Base\SecurityController@setTotp"},{"host":null,"methods":["DELETE"],"uri":"account\/security\/totp","name":"account.security.totp.disable","action":"Pterodactyl\Http\Controllers\Base\SecurityController@disableTotp"},{"host":null,"methods":["GET","HEAD"],"uri":"admin","name":"admin.index","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations","name":"admin.locations","action":"Pterodactyl\Http\Controllers\Admin\LocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations\/view\/{location}","name":"admin.locations.view","action":"Pterodactyl\Http\Controllers\Admin\LocationController@view"},{"host":null,"methods":["POST"],"uri":"admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@create"},{"host":null,"methods":["PATCH"],"uri":"admin\/locations\/view\/{location}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases","name":"admin.databases","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases\/view\/{host}","name":"admin.databases.view","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@view"},{"host":null,"methods":["POST"],"uri":"admin\/databases","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@create"},{"host":null,"methods":["PATCH"],"uri":"admin\/databases\/view\/{host}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/view\/{host}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings","name":"admin.settings","action":"Pterodactyl\Http\Controllers\Admin\BaseController@getSettings"},{"host":null,"methods":["POST"],"uri":"admin\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\BaseController@postSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users","name":"admin.users","action":"Pterodactyl\Http\Controllers\Admin\UserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/accounts.json","name":"admin.users.json","action":"Pterodactyl\Http\Controllers\Admin\UserController@json"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/new","name":"admin.users.new","action":"Pterodactyl\Http\Controllers\Admin\UserController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/view\/{user}","name":"admin.users.view","action":"Pterodactyl\Http\Controllers\Admin\UserController@view"},{"host":null,"methods":["POST"],"uri":"admin\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@store"},{"host":null,"methods":["PATCH"],"uri":"admin\/users\/view\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/users\/view\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers","name":"admin.servers","action":"Pterodactyl\Http\Controllers\Admin\ServersController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/new","name":"admin.servers.new","action":"Pterodactyl\Http\Controllers\Admin\ServersController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}","name":"admin.servers.view","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/details","name":"admin.servers.view.details","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDetails"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/build","name":"admin.servers.view.build","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewBuild"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/startup","name":"admin.servers.view.startup","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/database","name":"admin.servers.view.database","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/manage","name":"admin.servers.view.manage","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewManage"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/delete","name":"admin.servers.view.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDelete"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@store"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new\/nodes","name":"admin.servers.new.nodes","action":"Pterodactyl\Http\Controllers\Admin\ServersController@nodes"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/build","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@updateBuild"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@saveStartup"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@newDatabase"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/toggle","name":"admin.servers.view.manage.toggle","action":"Pterodactyl\Http\Controllers\Admin\ServersController@toggleInstall"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/rebuild","name":"admin.servers.view.manage.rebuild","action":"Pterodactyl\Http\Controllers\Admin\ServersController@rebuildContainer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/suspension","name":"admin.servers.view.manage.suspension","action":"Pterodactyl\Http\Controllers\Admin\ServersController@manageSuspension"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/reinstall","name":"admin.servers.view.manage.reinstall","action":"Pterodactyl\Http\Controllers\Admin\ServersController@reinstallServer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/delete","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@delete"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/details","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@setDetails"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/details\/container","name":"admin.servers.view.details.container","action":"Pterodactyl\Http\Controllers\Admin\ServersController@setContainer"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@resetDatabasePassword"},{"host":null,"methods":["DELETE"],"uri":"admin\/servers\/view\/{server}\/database\/{database}\/delete","name":"admin.servers.view.database.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@deleteDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes","name":"admin.nodes","action":"Pterodactyl\Http\Controllers\Admin\NodesController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/new","name":"admin.nodes.new","action":"Pterodactyl\Http\Controllers\Admin\NodesController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}","name":"admin.nodes.view","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/settings","name":"admin.nodes.view.settings","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/configuration","name":"admin.nodes.view.configuration","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/allocation","name":"admin.nodes.view.allocation","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/servers","name":"admin.nodes.view.servers","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewServers"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/settings\/token","name":"admin.nodes.view.configuration.token","action":"Pterodactyl\Http\Controllers\Admin\NodesController@setToken"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@createAllocation"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation\/remove","name":"admin.nodes.view.allocation.removeBlock","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveBlock"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation\/alias","name":"admin.nodes.view.allocation.setAlias","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationSetAlias"},{"host":null,"methods":["PATCH"],"uri":"admin\/nodes\/view\/{node}\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@updateSettings"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/delete","name":"admin.nodes.view.delete","action":"Pterodactyl\Http\Controllers\Admin\NodesController@delete"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/allocation\/remove\/{allocation}","name":"admin.nodes.view.allocation.removeSingle","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveSingle"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests","name":"admin.nests","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/new","name":"admin.nests.new","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/view\/{nest}","name":"admin.nests.view","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/new","name":"admin.nests.egg.new","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}","name":"admin.nests.egg.view","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/export","name":"admin.nests.egg.export","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@export"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/variables","name":"admin.nests.egg.variables","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/scripts","name":"admin.nests.egg.scripts","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggScriptController@index"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/import","name":"admin.nests.egg.import","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@import"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/egg\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/egg\/{egg}\/variables","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@store"},{"host":null,"methods":["PUT"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/view\/{nest}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}\/scripts","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggScriptController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}\/variables\/{variable}","name":"admin.nests.egg.variables.edit","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/view\/{nest}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/egg\/{egg}\/variables\/{variable}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@destroy"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs","name":"admin.packs","action":"Pterodactyl\Http\Controllers\Admin\PackController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/new","name":"admin.packs.new","action":"Pterodactyl\Http\Controllers\Admin\PackController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/new\/template","name":"admin.packs.new.template","action":"Pterodactyl\Http\Controllers\Admin\PackController@newTemplate"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/view\/{pack}","name":"admin.packs.view","action":"Pterodactyl\Http\Controllers\Admin\PackController@view"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@store"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/view\/{pack}\/export\/{files?}","name":"admin.packs.view.export","action":"Pterodactyl\Http\Controllers\Admin\PackController@export"},{"host":null,"methods":["PATCH"],"uri":"admin\/packs\/view\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/packs\/view\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@destroy"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/logout","name":"auth.logout","action":"Pterodactyl\Http\Controllers\Auth\LoginController@logout"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login","name":"auth.login","action":"Pterodactyl\Http\Controllers\Auth\LoginController@showLoginForm"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login\/totp","name":"auth.totp","action":"Pterodactyl\Http\Controllers\Auth\LoginController@totp"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password","name":"auth.password","action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password\/reset\/{token}","name":"auth.reset","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@showResetForm"},{"host":null,"methods":["POST"],"uri":"auth\/login","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@login"},{"host":null,"methods":["POST"],"uri":"auth\/login\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@totpCheckpoint"},{"host":null,"methods":["POST"],"uri":"auth\/password","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset","name":"auth.reset.post","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@reset"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset\/{token}","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}","name":"server.index","action":"Pterodactyl\Http\Controllers\Server\ConsoleController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/console","name":"server.console","action":"Pterodactyl\Http\Controllers\Server\ConsoleController@console"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\Settings\AllocationController@index"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/settings\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Server\Settings\AllocationController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/sftp","name":"server.settings.sftp","action":"Pterodactyl\Http\Controllers\Server\Settings\SftpController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\Settings\StartupController@index"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/settings\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Server\Settings\StartupController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/databases","name":"server.databases.index","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@index"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/databases\/password","name":"server.databases.password","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files","name":"server.files.index","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/add","name":"server.files.add","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/edit\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/download\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\Files\DownloadController@index"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/directory-list","name":"server.files.directory-list","action":"Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController@directory"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/save","name":"server.files.save","action":"Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users","name":"server.subusers","action":"Pterodactyl\Http\Controllers\Server\SubuserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/new","name":"server.subusers.new","action":"Pterodactyl\Http\Controllers\Server\SubuserController@create"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{subuser}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@view"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/users\/view\/{subuser}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@update"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/users\/view\/{subuser}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules","name":"server.schedules","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules\/new","name":"server.schedules.new","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@create"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":"server.schedules.view","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@view"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@update"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/schedules\/view\/{schedule}\/toggle","name":"server.schedules.toggle","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskToggleController@index"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/authenticate\/{token}","name":"api.remote.authenticate","action":"Pterodactyl\Http\Controllers\API\Remote\ValidateKeyController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/eggs","name":"api.remote.eggs","action":"Pterodactyl\Http\Controllers\API\Remote\EggRetrievalController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/eggs\/{uuid}","name":"api.remote.eggs.download","action":"Pterodactyl\Http\Controllers\API\Remote\EggRetrievalController@download"},{"host":null,"methods":["POST"],"uri":"api\/remote\/sftp","name":"api.remote.sftp","action":"Pterodactyl\Http\Controllers\API\Remote\SftpController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}","name":"daemon.pack.pull","action":"Pterodactyl\Http\Controllers\Daemon\PackController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}\/hash","name":"daemon.pack.hash","action":"Pterodactyl\Http\Controllers\Daemon\PackController@hash"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/details\/option\/{server}","name":"daemon.option.details","action":"Pterodactyl\Http\Controllers\Daemon\OptionController@details"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/configure\/{token}","name":"daemon.configuration","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@configuration"},{"host":null,"methods":["POST"],"uri":"daemon\/download","name":"daemon.download","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@authenticateDownload"},{"host":null,"methods":["POST"],"uri":"daemon\/install","name":"daemon.install","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@markInstall"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/open","name":"debugbar.openhandler","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@handle"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/clockwork\/{id}","name":"debugbar.clockwork","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@clockwork"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/stylesheets","name":"debugbar.assets.css","action":"Barryvdh\Debugbar\Controllers\AssetController@css"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/javascript","name":"debugbar.assets.js","action":"Barryvdh\Debugbar\Controllers\AssetController@js"}], + routes : [{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/open","name":"debugbar.openhandler","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@handle"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/clockwork\/{id}","name":"debugbar.clockwork","action":"Barryvdh\Debugbar\Controllers\OpenHandlerController@clockwork"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/stylesheets","name":"debugbar.assets.css","action":"Barryvdh\Debugbar\Controllers\AssetController@css"},{"host":null,"methods":["GET","HEAD"],"uri":"_debugbar\/assets\/javascript","name":"debugbar.assets.js","action":"Barryvdh\Debugbar\Controllers\AssetController@js"},{"host":null,"methods":["GET","HEAD"],"uri":"\/","name":"index","action":"Pterodactyl\Http\Controllers\Base\IndexController@getIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"status\/{server}","name":"index.status","action":"Pterodactyl\Http\Controllers\Base\IndexController@status"},{"host":null,"methods":["GET","HEAD"],"uri":"account","name":"account","action":"Pterodactyl\Http\Controllers\Base\AccountController@index"},{"host":null,"methods":["POST"],"uri":"account","name":null,"action":"Pterodactyl\Http\Controllers\Base\AccountController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api","name":"account.api","action":"Pterodactyl\Http\Controllers\Base\APIController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/api\/new","name":"account.api.new","action":"Pterodactyl\Http\Controllers\Base\APIController@create"},{"host":null,"methods":["POST"],"uri":"account\/api\/new","name":null,"action":"Pterodactyl\Http\Controllers\Base\APIController@store"},{"host":null,"methods":["DELETE"],"uri":"account\/api\/revoke\/{key}","name":"account.api.revoke","action":"Pterodactyl\Http\Controllers\Base\APIController@revoke"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security","name":"account.security","action":"Pterodactyl\Http\Controllers\Base\SecurityController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"account\/security\/revoke\/{id}","name":"account.security.revoke","action":"Pterodactyl\Http\Controllers\Base\SecurityController@revoke"},{"host":null,"methods":["PUT"],"uri":"account\/security\/totp","name":"account.security.totp","action":"Pterodactyl\Http\Controllers\Base\SecurityController@generateTotp"},{"host":null,"methods":["POST"],"uri":"account\/security\/totp","name":"account.security.totp.set","action":"Pterodactyl\Http\Controllers\Base\SecurityController@setTotp"},{"host":null,"methods":["DELETE"],"uri":"account\/security\/totp","name":"account.security.totp.disable","action":"Pterodactyl\Http\Controllers\Base\SecurityController@disableTotp"},{"host":null,"methods":["GET","HEAD"],"uri":"admin","name":"admin.index","action":"Pterodactyl\Http\Controllers\Admin\BaseController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations","name":"admin.locations","action":"Pterodactyl\Http\Controllers\Admin\LocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/locations\/view\/{location}","name":"admin.locations.view","action":"Pterodactyl\Http\Controllers\Admin\LocationController@view"},{"host":null,"methods":["POST"],"uri":"admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@create"},{"host":null,"methods":["PATCH"],"uri":"admin\/locations\/view\/{location}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\LocationController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases","name":"admin.databases","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/databases\/view\/{host}","name":"admin.databases.view","action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@view"},{"host":null,"methods":["POST"],"uri":"admin\/databases","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@create"},{"host":null,"methods":["PATCH"],"uri":"admin\/databases\/view\/{host}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/databases\/view\/{host}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\DatabaseController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings","name":"admin.settings","action":"Pterodactyl\Http\Controllers\Admin\Settings\IndexController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings\/mail","name":"admin.settings.mail","action":"Pterodactyl\Http\Controllers\Admin\Settings\MailController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/settings\/advanced","name":"admin.settings.advanced","action":"Pterodactyl\Http\Controllers\Admin\Settings\AdvancedController@index"},{"host":null,"methods":["PATCH"],"uri":"admin\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Settings\IndexController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/settings\/mail","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Settings\MailController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/settings\/advanced","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Settings\AdvancedController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users","name":"admin.users","action":"Pterodactyl\Http\Controllers\Admin\UserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/accounts.json","name":"admin.users.json","action":"Pterodactyl\Http\Controllers\Admin\UserController@json"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/new","name":"admin.users.new","action":"Pterodactyl\Http\Controllers\Admin\UserController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/users\/view\/{user}","name":"admin.users.view","action":"Pterodactyl\Http\Controllers\Admin\UserController@view"},{"host":null,"methods":["POST"],"uri":"admin\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@store"},{"host":null,"methods":["PATCH"],"uri":"admin\/users\/view\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/users\/view\/{user}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\UserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers","name":"admin.servers","action":"Pterodactyl\Http\Controllers\Admin\ServersController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/new","name":"admin.servers.new","action":"Pterodactyl\Http\Controllers\Admin\ServersController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}","name":"admin.servers.view","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/details","name":"admin.servers.view.details","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDetails"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/build","name":"admin.servers.view.build","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewBuild"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/startup","name":"admin.servers.view.startup","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/database","name":"admin.servers.view.database","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/manage","name":"admin.servers.view.manage","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewManage"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/servers\/view\/{server}\/delete","name":"admin.servers.view.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@viewDelete"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@store"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/build","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@updateBuild"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@saveStartup"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@newDatabase"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/toggle","name":"admin.servers.view.manage.toggle","action":"Pterodactyl\Http\Controllers\Admin\ServersController@toggleInstall"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/rebuild","name":"admin.servers.view.manage.rebuild","action":"Pterodactyl\Http\Controllers\Admin\ServersController@rebuildContainer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/suspension","name":"admin.servers.view.manage.suspension","action":"Pterodactyl\Http\Controllers\Admin\ServersController@manageSuspension"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/manage\/reinstall","name":"admin.servers.view.manage.reinstall","action":"Pterodactyl\Http\Controllers\Admin\ServersController@reinstallServer"},{"host":null,"methods":["POST"],"uri":"admin\/servers\/view\/{server}\/delete","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@delete"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/details","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@setDetails"},{"host":null,"methods":["PATCH"],"uri":"admin\/servers\/view\/{server}\/database","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServersController@resetDatabasePassword"},{"host":null,"methods":["DELETE"],"uri":"admin\/servers\/view\/{server}\/database\/{database}\/delete","name":"admin.servers.view.database.delete","action":"Pterodactyl\Http\Controllers\Admin\ServersController@deleteDatabase"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes","name":"admin.nodes","action":"Pterodactyl\Http\Controllers\Admin\NodesController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/new","name":"admin.nodes.new","action":"Pterodactyl\Http\Controllers\Admin\NodesController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}","name":"admin.nodes.view","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewIndex"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/settings","name":"admin.nodes.view.settings","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewSettings"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/configuration","name":"admin.nodes.view.configuration","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/allocation","name":"admin.nodes.view.allocation","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewAllocation"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/servers","name":"admin.nodes.view.servers","action":"Pterodactyl\Http\Controllers\Admin\NodesController@viewServers"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nodes\/view\/{node}\/settings\/token","name":"admin.nodes.view.configuration.token","action":"Pterodactyl\Http\Controllers\Admin\NodesController@setToken"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@createAllocation"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation\/remove","name":"admin.nodes.view.allocation.removeBlock","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveBlock"},{"host":null,"methods":["POST"],"uri":"admin\/nodes\/view\/{node}\/allocation\/alias","name":"admin.nodes.view.allocation.setAlias","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationSetAlias"},{"host":null,"methods":["PATCH"],"uri":"admin\/nodes\/view\/{node}\/settings","name":null,"action":"Pterodactyl\Http\Controllers\Admin\NodesController@updateSettings"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/delete","name":"admin.nodes.view.delete","action":"Pterodactyl\Http\Controllers\Admin\NodesController@delete"},{"host":null,"methods":["DELETE"],"uri":"admin\/nodes\/view\/{node}\/allocation\/remove\/{allocation}","name":"admin.nodes.view.allocation.removeSingle","action":"Pterodactyl\Http\Controllers\Admin\NodesController@allocationRemoveSingle"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests","name":"admin.nests","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/new","name":"admin.nests.new","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/view\/{nest}","name":"admin.nests.view","action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/new","name":"admin.nests.egg.new","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}","name":"admin.nests.egg.view","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/export","name":"admin.nests.egg.export","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@export"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/variables","name":"admin.nests.egg.variables","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/nests\/egg\/{egg}\/scripts","name":"admin.nests.egg.scripts","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggScriptController@index"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/import","name":"admin.nests.egg.import","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@import"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/egg\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@store"},{"host":null,"methods":["POST"],"uri":"admin\/nests\/egg\/{egg}\/variables","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@store"},{"host":null,"methods":["PUT"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggShareController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/view\/{nest}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}\/scripts","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggScriptController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/nests\/egg\/{egg}\/variables\/{variable}","name":"admin.nests.egg.variables.edit","action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/view\/{nest}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\NestController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/egg\/{egg}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/nests\/egg\/{egg}\/variables\/{variable}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\Nests\EggVariableController@destroy"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs","name":"admin.packs","action":"Pterodactyl\Http\Controllers\Admin\PackController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/new","name":"admin.packs.new","action":"Pterodactyl\Http\Controllers\Admin\PackController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/new\/template","name":"admin.packs.new.template","action":"Pterodactyl\Http\Controllers\Admin\PackController@newTemplate"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/packs\/view\/{pack}","name":"admin.packs.view","action":"Pterodactyl\Http\Controllers\Admin\PackController@view"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@store"},{"host":null,"methods":["POST"],"uri":"admin\/packs\/view\/{pack}\/export\/{files?}","name":"admin.packs.view.export","action":"Pterodactyl\Http\Controllers\Admin\PackController@export"},{"host":null,"methods":["PATCH"],"uri":"admin\/packs\/view\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/packs\/view\/{pack}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\PackController@destroy"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login","name":"auth.login","action":"Pterodactyl\Http\Controllers\Auth\LoginController@showLoginForm"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/login\/totp","name":"auth.totp","action":"Pterodactyl\Http\Controllers\Auth\LoginController@totp"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password","name":"auth.password","action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/password\/reset\/{token}","name":"auth.reset","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@showResetForm"},{"host":null,"methods":["POST"],"uri":"auth\/login","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@login"},{"host":null,"methods":["POST"],"uri":"auth\/login\/totp","name":null,"action":"Pterodactyl\Http\Controllers\Auth\LoginController@loginUsingTotp"},{"host":null,"methods":["POST"],"uri":"auth\/password","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset","name":"auth.reset.post","action":"Pterodactyl\Http\Controllers\Auth\ResetPasswordController@reset"},{"host":null,"methods":["POST"],"uri":"auth\/password\/reset\/{token}","name":null,"action":"Pterodactyl\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail"},{"host":null,"methods":["GET","HEAD"],"uri":"auth\/logout","name":"auth.logout","action":"Pterodactyl\Http\Controllers\Auth\LoginController@logout"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}","name":"server.index","action":"Pterodactyl\Http\Controllers\Server\ConsoleController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/console","name":"server.console","action":"Pterodactyl\Http\Controllers\Server\ConsoleController@console"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\Settings\AllocationController@index"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/settings\/allocation","name":null,"action":"Pterodactyl\Http\Controllers\Server\Settings\AllocationController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/sftp","name":"server.settings.sftp","action":"Pterodactyl\Http\Controllers\Server\Settings\SftpController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\Settings\StartupController@index"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/settings\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Server\Settings\StartupController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/databases","name":"server.databases.index","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@index"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/databases\/password","name":"server.databases.password","action":"Pterodactyl\Http\Controllers\Server\DatabaseController@update"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files","name":"server.files.index","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/add","name":"server.files.add","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/edit\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\Files\FileActionsController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/files\/download\/{file}","name":"server.files.edit","action":"Pterodactyl\Http\Controllers\Server\Files\DownloadController@index"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/directory-list","name":"server.files.directory-list","action":"Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController@directory"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/files\/save","name":"server.files.save","action":"Pterodactyl\Http\Controllers\Server\Files\RemoteRequestController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users","name":"server.subusers","action":"Pterodactyl\Http\Controllers\Server\SubuserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/new","name":"server.subusers.new","action":"Pterodactyl\Http\Controllers\Server\SubuserController@create"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{subuser}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@view"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/users\/view\/{subuser}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@update"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/users\/view\/{subuser}","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules","name":"server.schedules","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules\/new","name":"server.schedules.new","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@create"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@store"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":"server.schedules.view","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@view"},{"host":null,"methods":["PATCH"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@update"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/view\/{schedule}\/toggle","name":"server.schedules.toggle","action":"Pterodactyl\Http\Controllers\Server\Tasks\ActionController@toggle"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/view\/{schedule}\/trigger","name":"server.schedules.trigger","action":"Pterodactyl\Http\Controllers\Server\Tasks\ActionController@trigger"},{"host":null,"methods":["DELETE"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/authenticate\/{token}","name":"api.remote.authenticate","action":"Pterodactyl\Http\Controllers\API\Remote\ValidateKeyController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/eggs","name":"api.remote.eggs","action":"Pterodactyl\Http\Controllers\API\Remote\EggRetrievalController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/eggs\/{uuid}","name":"api.remote.eggs.download","action":"Pterodactyl\Http\Controllers\API\Remote\EggRetrievalController@download"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/scripts\/{uuid}","name":"api.remote.scripts","action":"Pterodactyl\Http\Controllers\API\Remote\EggInstallController@index"},{"host":null,"methods":["POST"],"uri":"api\/remote\/sftp","name":"api.remote.sftp","action":"Pterodactyl\Http\Controllers\API\Remote\SftpController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}","name":"daemon.pack.pull","action":"Pterodactyl\Http\Controllers\Daemon\PackController@pull"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/packs\/pull\/{uuid}\/hash","name":"daemon.pack.hash","action":"Pterodactyl\Http\Controllers\Daemon\PackController@hash"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/configure\/{token}","name":"daemon.configuration","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@configuration"},{"host":null,"methods":["POST"],"uri":"daemon\/download","name":"daemon.download","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@authenticateDownload"},{"host":null,"methods":["POST"],"uri":"daemon\/install","name":"daemon.install","action":"Pterodactyl\Http\Controllers\Daemon\ActionController@markInstall"}], prefix: '', route : function (name, parameters, route) { diff --git a/public/themes/pterodactyl/js/frontend/tasks/management-actions.js b/public/themes/pterodactyl/js/frontend/tasks/management-actions.js index 60a8194bf..3c344a971 100644 --- a/public/themes/pterodactyl/js/frontend/tasks/management-actions.js +++ b/public/themes/pterodactyl/js/frontend/tasks/management-actions.js @@ -19,6 +19,7 @@ // SOFTWARE. $(document).ready(function () { + $('[data-toggle="tooltip"]').tooltip(); $('[data-action="delete-schedule"]').click(function () { var self = $(this); swal({ @@ -59,6 +60,45 @@ $(document).ready(function () { }); }); + $('[data-action="trigger-schedule"]').click(function (event) { + event.preventDefault(); + var self = $(this); + swal({ + type: 'info', + title: 'Trigger Schedule', + text: 'This will run the selected schedule now.', + showCancelButton: true, + allowOutsideClick: true, + closeOnConfirm: false, + confirmButtonText: 'Continue', + showLoaderOnConfirm: true + }, function () { + $.ajax({ + method: 'POST', + url: Router.route('server.schedules.trigger', { + server: Pterodactyl.server.uuidShort, + schedule: self.data('schedule-id'), + }), + headers: { + 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content'), + }, + }).done(function (data) { + swal({ + type: 'success', + title: '', + text: 'Schedule has been added to the next-run queue.' + }); + }).fail(function (jqXHR) { + console.error(jqXHR); + swal({ + type: 'error', + title: 'Whoops!', + text: 'An error occured while attempting to trigger this schedule.' + }); + }); + }); + }); + $('[data-action="toggle-schedule"]').click(function (event) { var self = $(this); swal({ diff --git a/resources/lang/en/admin/node.php b/resources/lang/en/admin/node.php index 875bde890..5d59e41ce 100644 --- a/resources/lang/en/admin/node.php +++ b/resources/lang/en/admin/node.php @@ -18,6 +18,6 @@ return [ 'location_required' => 'You must have at least one location configured before you can add a node to this panel.', 'node_created' => 'Successfully created new node. You can automatically configure the daemon on this machine by visiting the \'Configuration\' tab. Before you can add any servers you must first allocate at least one IP address and port.', 'node_updated' => 'Node information has been updated. If any daemon settings were changed you will need to reboot it for those changes to take effect.', - 'unallocated_deleted' => 'Deleted all unallocatred ports for :ip.', + 'unallocated_deleted' => 'Deleted all un-allocated ports for :ip.', ], ]; diff --git a/resources/lang/en/server.php b/resources/lang/en/server.php index 3aa27e5b2..4cb73a0f6 100644 --- a/resources/lang/en/server.php +++ b/resources/lang/en/server.php @@ -30,6 +30,9 @@ return [ 'command' => 'Send Command', 'power' => 'Power Action', ], + 'toggle' => 'Toggle Status', + 'run_now' => 'Trigger Schedule', + 'schedule_created' => 'Successfully created a new schedule for this server.', 'unnamed' => 'Unnamed Schedule', 'setup' => 'Schedule Setup', 'day_of_week' => 'Day of Week', diff --git a/resources/themes/pterodactyl/admin/nodes/view/allocation.blade.php b/resources/themes/pterodactyl/admin/nodes/view/allocation.blade.php index 50a6a84e5..75b40811c 100644 --- a/resources/themes/pterodactyl/admin/nodes/view/allocation.blade.php +++ b/resources/themes/pterodactyl/admin/nodes/view/allocation.blade.php @@ -90,7 +90,7 @@
@@ -132,7 +132,7 @@
@@ -156,10 +156,12 @@ $('#pAllocationIP').select2({ tags: true, maximumSelectionLength: 1, + selectOnClose: true, tokenSeparators: [',', ' '], }); $('#pAllocationPorts').select2({ tags: true, + selectOnClose: true, tokenSeparators: [',', ' '], }); $('button[data-action="deallocate"]').click(function (event) { diff --git a/resources/themes/pterodactyl/admin/packs/view.blade.php b/resources/themes/pterodactyl/admin/packs/view.blade.php index d9bb50390..0f4a38e7b 100644 --- a/resources/themes/pterodactyl/admin/packs/view.blade.php +++ b/resources/themes/pterodactyl/admin/packs/view.blade.php @@ -41,6 +41,11 @@

The version of this package, or the version of the files contained within the package.

+
+ + +

If you would like to modify the stored pack you will need to upload a new archive.tar.gz to the location defined above.

+
@@ -100,28 +105,6 @@ -
-
-
-
-

Stored Files

-
-
- - - - - - -
NameSHA1 HashFile Size
-
- -
-
-
diff --git a/resources/themes/pterodactyl/admin/servers/index.blade.php b/resources/themes/pterodactyl/admin/servers/index.blade.php index ca21e5823..f9a82497a 100644 --- a/resources/themes/pterodactyl/admin/servers/index.blade.php +++ b/resources/themes/pterodactyl/admin/servers/index.blade.php @@ -45,6 +45,7 @@ Node Connection + @foreach ($servers as $server) @@ -64,6 +65,10 @@ Active @endif + + + + @endforeach @@ -78,3 +83,13 @@
@endsection + +@section('footer-scripts') + @parent + +@endsection diff --git a/resources/themes/pterodactyl/admin/servers/new.blade.php b/resources/themes/pterodactyl/admin/servers/new.blade.php index 09c14fcec..bbed3de6c 100644 --- a/resources/themes/pterodactyl/admin/servers/new.blade.php +++ b/resources/themes/pterodactyl/admin/servers/new.blade.php @@ -121,18 +121,9 @@ MB
-
- -
- - - - -
-
diff --git a/resources/themes/pterodactyl/admin/servers/view/index.blade.php b/resources/themes/pterodactyl/admin/servers/view/index.blade.php index 6e9855337..58deaa924 100644 --- a/resources/themes/pterodactyl/admin/servers/view/index.blade.php +++ b/resources/themes/pterodactyl/admin/servers/view/index.blade.php @@ -10,7 +10,7 @@ @endsection @section('content-header') -

{{ $server->name }}{{ $server->uuid }}

+

{{ $server->name }}{{ str_limit($server->description) }}