From 97dc0519d66f82bc8fe71de96d185ec5e20b383f Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Wed, 18 Oct 2017 22:32:19 -0500 Subject: [PATCH] Add database management back to front-end and begin some refactoring Here we go again boys... --- .../DatabaseRepositoryInterface.php | 65 +++++--- .../Controllers/Admin/DatabaseController.php | 83 ++++++---- .../Controllers/Admin/ServersController.php | 21 ++- .../Controllers/Server/DatabaseController.php | 71 +++++++++ app/Http/Kernel.php | 13 +- .../Server/DatabaseBelongsToServer.php | 51 ++++++ ...Access.php => ScheduleBelongsToServer.php} | 2 +- ...rAccess.php => SubuserBelongsToServer.php} | 2 +- app/Http/Middleware/ServerAuthenticate.php | 5 + .../Middleware/SubuserAccessAuthenticate.php | 1 + .../Server/ServerDataComposer.php | 27 ++-- .../Eloquent/DatabaseRepository.php | 96 +++++++----- app/Services/Database/DatabaseHostService.php | 148 ------------------ .../DatabaseManagementService.php | 68 ++------ .../Databases/DatabasePasswordService.php | 86 ++++++++++ .../Databases/Hosts/HostCreationService.php | 92 +++++++++++ .../Databases/Hosts/HostDeletionService.php | 53 +++++++ .../Databases/Hosts/HostUpdateService.php | 96 ++++++++++++ .../Servers/ServerDeletionService.php | 6 +- .../Controllers/JavascriptInjection.php | 20 ++- public/js/laroute.js | 2 +- resources/lang/en/server.php | 2 +- .../admin/databases/view.blade.php | 5 +- .../admin/servers/view/database.blade.php | 7 +- .../pterodactyl/layouts/master.blade.php | 14 +- .../index.blade.php} | 85 ++++++---- routes/admin.php | 1 + routes/server.php | 41 ++--- .../Admin/DatabaseControllerTest.php | 5 +- .../Database/DatabaseHostServiceTest.php | 5 +- .../DatabaseManagementServiceTest.php | 4 +- .../Servers/ServerDeletionServiceTest.php | 4 +- 32 files changed, 774 insertions(+), 407 deletions(-) create mode 100644 app/Http/Controllers/Server/DatabaseController.php create mode 100644 app/Http/Middleware/Server/DatabaseBelongsToServer.php rename app/Http/Middleware/Server/{ScheduleAccess.php => ScheduleBelongsToServer.php} (98%) rename app/Http/Middleware/Server/{SubuserAccess.php => SubuserBelongsToServer.php} (98%) delete mode 100644 app/Services/Database/DatabaseHostService.php rename app/Services/{Database => Databases}/DatabaseManagementService.php (67%) create mode 100644 app/Services/Databases/DatabasePasswordService.php create mode 100644 app/Services/Databases/Hosts/HostCreationService.php create mode 100644 app/Services/Databases/Hosts/HostDeletionService.php create mode 100644 app/Services/Databases/Hosts/HostUpdateService.php rename resources/themes/pterodactyl/server/{settings/databases.blade.php => databases/index.blade.php} (55%) diff --git a/app/Contracts/Repository/DatabaseRepositoryInterface.php b/app/Contracts/Repository/DatabaseRepositoryInterface.php index ca5379df1..1e90d0e04 100644 --- a/app/Contracts/Repository/DatabaseRepositoryInterface.php +++ b/app/Contracts/Repository/DatabaseRepositoryInterface.php @@ -9,8 +9,35 @@ namespace Pterodactyl\Contracts\Repository; +use Illuminate\Support\Collection; + interface DatabaseRepositoryInterface extends RepositoryInterface { + const DEFAULT_CONNECTION_NAME = 'dynamic'; + + /** + * Set the connection name to execute statements against. + * + * @param string $connection + * @return $this + */ + public function setConnection(string $connection); + + /** + * Return the connection to execute statements aganist. + * + * @return string + */ + public function getConnection(): string; + + /** + * Return all of the databases belonging to a server. + * + * @param int $server + * @return \Illuminate\Support\Collection + */ + public function getDatabasesForServer(int $server): Collection; + /** * Create a new database if it does not already exist on the host with * the provided details. @@ -26,58 +53,52 @@ interface DatabaseRepositoryInterface extends RepositoryInterface /** * Create a new database on a given connection. * - * @param string $database - * @param null|string $connection + * @param string $database * @return bool */ - public function createDatabase($database, $connection = null); + public function createDatabase($database); /** * Create a new database user on a given connection. * - * @param string $username - * @param string $remote - * @param string $password - * @param null|string $connection + * @param string $username + * @param string $remote + * @param string $password * @return bool */ - public function createUser($username, $remote, $password, $connection = null); + public function createUser($username, $remote, $password); /** * Give a specific user access to a given database. * - * @param string $database - * @param string $username - * @param string $remote - * @param null|string $connection + * @param string $database + * @param string $username + * @param string $remote * @return bool */ - public function assignUserToDatabase($database, $username, $remote, $connection = null); + public function assignUserToDatabase($database, $username, $remote); /** * Flush the privileges for a given connection. * - * @param null|string $connection * @return mixed */ - public function flush($connection = null); + public function flush(); /** * Drop a given database on a specific connection. * - * @param string $database - * @param null|string $connection + * @param string $database * @return bool */ - public function dropDatabase($database, $connection = null); + public function dropDatabase($database); /** * Drop a given user on a specific connection. * - * @param string $username - * @param string $remote - * @param null|string $connection + * @param string $username + * @param string $remote * @return mixed */ - public function dropUser($username, $remote, $connection = null); + public function dropUser($username, $remote); } diff --git a/app/Http/Controllers/Admin/DatabaseController.php b/app/Http/Controllers/Admin/DatabaseController.php index 9eac33f69..02271d699 100644 --- a/app/Http/Controllers/Admin/DatabaseController.php +++ b/app/Http/Controllers/Admin/DatabaseController.php @@ -9,11 +9,15 @@ namespace Pterodactyl\Http\Controllers\Admin; -use Pterodactyl\Models\DatabaseHost; +use PDOException; +use Illuminate\View\View; +use Illuminate\Http\RedirectResponse; use Prologue\Alerts\AlertsMessageBag; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Services\Database\DatabaseHostService; +use Pterodactyl\Services\Databases\Hosts\HostUpdateService; use Pterodactyl\Http\Requests\Admin\DatabaseHostFormRequest; +use Pterodactyl\Services\Databases\Hosts\HostCreationService; +use Pterodactyl\Services\Databases\Hosts\HostDeletionService; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; @@ -22,41 +26,57 @@ class DatabaseController extends Controller /** * @var \Prologue\Alerts\AlertsMessageBag */ - protected $alert; + private $alert; + + /** + * @var \Pterodactyl\Services\Databases\Hosts\HostCreationService + */ + private $creationService; + + /** + * @var \Pterodactyl\Services\Databases\Hosts\HostDeletionService + */ + private $deletionService; /** * @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface */ - protected $locationRepository; + private $locationRepository; /** * @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface */ - protected $repository; + private $repository; /** - * @var \Pterodactyl\Services\Database\DatabaseHostService + * @var \Pterodactyl\Services\Databases\Hosts\HostUpdateService */ - protected $service; + private $updateService; /** * DatabaseController constructor. * * @param \Prologue\Alerts\AlertsMessageBag $alert * @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $repository - * @param \Pterodactyl\Services\Database\DatabaseHostService $service + * @param \Pterodactyl\Services\Databases\Hosts\HostCreationService $creationService + * @param \Pterodactyl\Services\Databases\Hosts\HostDeletionService $deletionService + * @param \Pterodactyl\Services\Databases\Hosts\HostUpdateService $updateService * @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $locationRepository */ public function __construct( AlertsMessageBag $alert, DatabaseHostRepositoryInterface $repository, - DatabaseHostService $service, + HostCreationService $creationService, + HostDeletionService $deletionService, + HostUpdateService $updateService, LocationRepositoryInterface $locationRepository ) { $this->alert = $alert; + $this->creationService = $creationService; + $this->deletionService = $deletionService; $this->repository = $repository; - $this->service = $service; $this->locationRepository = $locationRepository; + $this->updateService = $updateService; } /** @@ -64,7 +84,7 @@ class DatabaseController extends Controller * * @return \Illuminate\View\View */ - public function index() + public function index(): View { return view('admin.databases.index', [ 'locations' => $this->locationRepository->getAllWithNodes(), @@ -80,7 +100,7 @@ class DatabaseController extends Controller * * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function view($host) + public function view($host): View { return view('admin.databases.view', [ 'locations' => $this->locationRepository->getAllWithNodes(), @@ -94,42 +114,41 @@ class DatabaseController extends Controller * @param \Pterodactyl\Http\Requests\Admin\DatabaseHostFormRequest $request * @return \Illuminate\Http\RedirectResponse * - * @throws \Throwable + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function create(DatabaseHostFormRequest $request) + public function create(DatabaseHostFormRequest $request): RedirectResponse { try { - $host = $this->service->create($request->normalize()); - $this->alert->success('Successfully created a new database host on the system.')->flash(); - - return redirect()->route('admin.databases.view', $host->id); - } catch (\PDOException $ex) { + $host = $this->creationService->handle($request->normalize()); + } catch (PDOException $ex) { $this->alert->danger($ex->getMessage())->flash(); + + return redirect()->route('admin.databases'); } - return redirect()->route('admin.databases'); + $this->alert->success('Successfully created a new database host on the system.')->flash(); + + return redirect()->route('admin.databases.view', $host->id); } /** * Handle updating database host. * * @param \Pterodactyl\Http\Requests\Admin\DatabaseHostFormRequest $request - * @param \Pterodactyl\Models\DatabaseHost $host + * @param int $host * @return \Illuminate\Http\RedirectResponse * * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function update(DatabaseHostFormRequest $request, DatabaseHost $host) + public function update(DatabaseHostFormRequest $request, int $host): RedirectResponse { - if ($request->input('action') === 'delete') { - return $this->delete($host); - } - try { - $host = $this->service->update($host->id, $request->normalize()); + $host = $this->updateService->handle($host, $request->normalize()); $this->alert->success('Database host was updated successfully.')->flash(); - } catch (\PDOException $ex) { + } catch (PDOException $ex) { $this->alert->danger($ex->getMessage())->flash(); } @@ -139,14 +158,14 @@ class DatabaseController extends Controller /** * Handle request to delete a database host. * - * @param \Pterodactyl\Models\DatabaseHost $host + * @param int $host * @return \Illuminate\Http\RedirectResponse * - * @throws \Pterodactyl\Exceptions\DisplayException + * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException */ - public function delete(DatabaseHost $host) + public function delete(int $host): RedirectResponse { - $this->service->delete($host->id); + $this->deletionService->handle($host); $this->alert->success('The requested database host has been deleted from the system.')->flash(); return redirect()->route('admin.databases'); diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 5867d4788..a10c56904 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -22,12 +22,13 @@ use Pterodactyl\Services\Servers\ServerDeletionService; use Pterodactyl\Services\Servers\ReinstallServerService; use Pterodactyl\Services\Servers\ContainerRebuildService; use Pterodactyl\Services\Servers\BuildModificationService; -use Pterodactyl\Services\Database\DatabaseManagementService; +use Pterodactyl\Services\Databases\DatabasePasswordService; use Pterodactyl\Services\Servers\DetailsModificationService; use Pterodactyl\Services\Servers\StartupModificationService; use Pterodactyl\Contracts\Repository\NestRepositoryInterface; use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; use Pterodactyl\Repositories\Eloquent\DatabaseHostRepository; +use Pterodactyl\Services\Databases\DatabaseManagementService; use Illuminate\Contracts\Config\Repository as ConfigRepository; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; @@ -67,10 +68,15 @@ class ServersController extends Controller protected $databaseRepository; /** - * @var \Pterodactyl\Services\Database\DatabaseManagementService + * @var \Pterodactyl\Services\Databases\DatabaseManagementService */ protected $databaseManagementService; + /** + * @var \Pterodactyl\Services\Databases\DatabasePasswordService + */ + protected $databasePasswordService; + /** * @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface */ @@ -135,7 +141,8 @@ class ServersController extends Controller * @param \Illuminate\Contracts\Config\Repository $config * @param \Pterodactyl\Services\Servers\ContainerRebuildService $containerRebuildService * @param \Pterodactyl\Services\Servers\ServerCreationService $service - * @param \Pterodactyl\Services\Database\DatabaseManagementService $databaseManagementService + * @param \Pterodactyl\Services\Databases\DatabaseManagementService $databaseManagementService + * @param \Pterodactyl\Services\Databases\DatabasePasswordService $databasePasswordService * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $databaseRepository * @param \Pterodactyl\Repositories\Eloquent\DatabaseHostRepository $databaseHostRepository * @param \Pterodactyl\Services\Servers\ServerDeletionService $deletionService @@ -156,6 +163,7 @@ class ServersController extends Controller ContainerRebuildService $containerRebuildService, ServerCreationService $service, DatabaseManagementService $databaseManagementService, + DatabasePasswordService $databasePasswordService, DatabaseRepositoryInterface $databaseRepository, DatabaseHostRepository $databaseHostRepository, ServerDeletionService $deletionService, @@ -173,9 +181,10 @@ class ServersController extends Controller $this->buildModificationService = $buildModificationService; $this->config = $config; $this->containerRebuildService = $containerRebuildService; - $this->databaseManagementService = $databaseManagementService; - $this->databaseRepository = $databaseRepository; $this->databaseHostRepository = $databaseHostRepository; + $this->databaseManagementService = $databaseManagementService; + $this->databasePasswordService = $databasePasswordService; + $this->databaseRepository = $databaseRepository; $this->detailsModificationService = $detailsModificationService; $this->deletionService = $deletionService; $this->locationRepository = $locationRepository; @@ -609,7 +618,7 @@ class ServersController extends Controller ['id', '=', $request->input('database')], ]); - $this->databaseManagementService->changePassword($database->id, str_random(20)); + $this->databasePasswordService->handle($database, str_random(20)); return response('', 204); } diff --git a/app/Http/Controllers/Server/DatabaseController.php b/app/Http/Controllers/Server/DatabaseController.php new file mode 100644 index 000000000..15fb96c01 --- /dev/null +++ b/app/Http/Controllers/Server/DatabaseController.php @@ -0,0 +1,71 @@ +passwordService = $passwordService; + $this->repository = $repository; + } + + /** + * Render the database listing for a server. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View + */ + public function index(Request $request): View + { + $server = $request->attributes->get('server'); + $this->injectJavascript(); + + return view('server.databases.index', [ + 'databases' => $this->repository->getDatabasesForServer($server->id), + ]); + } + + /** + * Handle a request to update the password for a specific database. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(Request $request): JsonResponse + { + $password = str_random(20); + $this->passwordService->handle($request->attributes->get('database'), $password); + + return response()->json(['password' => $password]); + } +} diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index b1812a9d3..22e90a903 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -5,6 +5,9 @@ namespace Pterodactyl\Http; use Pterodactyl\Http\Middleware\DaemonAuthenticate; use Illuminate\Foundation\Http\Kernel as HttpKernel; use Illuminate\Routing\Middleware\SubstituteBindings; +use Pterodactyl\Http\Middleware\Server\SubuserBelongsToServer; +use Pterodactyl\Http\Middleware\Server\DatabaseBelongsToServer; +use Pterodactyl\Http\Middleware\Server\ScheduleBelongsToServer; class Kernel extends HttpKernel { @@ -63,7 +66,6 @@ class Kernel extends HttpKernel 'guest' => \Pterodactyl\Http\Middleware\RedirectIfAuthenticated::class, 'server' => \Pterodactyl\Http\Middleware\ServerAuthenticate::class, 'subuser.auth' => \Pterodactyl\Http\Middleware\SubuserAccessAuthenticate::class, - 'subuser' => \Pterodactyl\Http\Middleware\Server\SubuserAccess::class, 'admin' => \Pterodactyl\Http\Middleware\AdminAuthenticate::class, 'daemon-old' => DaemonAuthenticate::class, 'csrf' => \Pterodactyl\Http\Middleware\VerifyCsrfToken::class, @@ -71,6 +73,13 @@ class Kernel extends HttpKernel 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'recaptcha' => \Pterodactyl\Http\Middleware\VerifyReCaptcha::class, - 'schedule' => \Pterodactyl\Http\Middleware\Server\ScheduleAccess::class, + + // Server specific middleware (used for authenticating access to resources) + // + // These are only used for individual server authentication, and not gloabl + // actions from other resources. They are defined in the route files. + 'server..database' => DatabaseBelongsToServer::class, + 'server..subuser' => SubuserBelongsToServer::class, + 'server..schedule' => ScheduleBelongsToServer::class, ]; } diff --git a/app/Http/Middleware/Server/DatabaseBelongsToServer.php b/app/Http/Middleware/Server/DatabaseBelongsToServer.php new file mode 100644 index 000000000..bc31c29c8 --- /dev/null +++ b/app/Http/Middleware/Server/DatabaseBelongsToServer.php @@ -0,0 +1,51 @@ +repository = $repository; + } + + /** + * Check if a database being requested belongs to the currently loaded server. + * If it does not, throw a 404 error, otherwise continue on with the request + * and set an attribute with the database. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function handle(Request $request, Closure $next) + { + $server = $request->attributes->get('server'); + + $database = $this->repository->find($request->input('database')); + if ($database->server_id !== $server->id) { + throw new NotFoundHttpException; + } + + $request->attributes->set('database', $database); + + return $next($request); + } +} diff --git a/app/Http/Middleware/Server/ScheduleAccess.php b/app/Http/Middleware/Server/ScheduleBelongsToServer.php similarity index 98% rename from app/Http/Middleware/Server/ScheduleAccess.php rename to app/Http/Middleware/Server/ScheduleBelongsToServer.php index b54b07d47..145429f8c 100644 --- a/app/Http/Middleware/Server/ScheduleAccess.php +++ b/app/Http/Middleware/Server/ScheduleBelongsToServer.php @@ -14,7 +14,7 @@ use Illuminate\Contracts\Session\Session; use Pterodactyl\Contracts\Extensions\HashidsInterface; use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; -class ScheduleAccess +class ScheduleBelongsToServer { /** * @var \Pterodactyl\Contracts\Extensions\HashidsInterface diff --git a/app/Http/Middleware/Server/SubuserAccess.php b/app/Http/Middleware/Server/SubuserBelongsToServer.php similarity index 98% rename from app/Http/Middleware/Server/SubuserAccess.php rename to app/Http/Middleware/Server/SubuserBelongsToServer.php index 85ebe2640..b18620f51 100644 --- a/app/Http/Middleware/Server/SubuserAccess.php +++ b/app/Http/Middleware/Server/SubuserBelongsToServer.php @@ -15,7 +15,7 @@ use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -class SubuserAccess +class SubuserBelongsToServer { /** * @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface diff --git a/app/Http/Middleware/ServerAuthenticate.php b/app/Http/Middleware/ServerAuthenticate.php index 7df3d110a..538382f23 100644 --- a/app/Http/Middleware/ServerAuthenticate.php +++ b/app/Http/Middleware/ServerAuthenticate.php @@ -105,8 +105,13 @@ class ServerAuthenticate } // Store the server in the session. + // @todo remove from session. use request attributes. $this->session->now('server_data.model', $server); + // Add server to the request attributes. This will replace sessions + // as files are updated. + $request->attributes->set('server', $server); + return $next($request); } } diff --git a/app/Http/Middleware/SubuserAccessAuthenticate.php b/app/Http/Middleware/SubuserAccessAuthenticate.php index fd4b8cf4d..30a906884 100644 --- a/app/Http/Middleware/SubuserAccessAuthenticate.php +++ b/app/Http/Middleware/SubuserAccessAuthenticate.php @@ -60,6 +60,7 @@ class SubuserAccessAuthenticate try { $token = $this->keyProviderService->handle($server->id, $request->user()->id); $this->session->now('server_data.token', $token); + $request->attributes->set('server_token', $token); } catch (RecordNotFoundException $exception) { throw new AuthenticationException('This account does not have permission to access this server.'); } diff --git a/app/Http/ViewComposers/Server/ServerDataComposer.php b/app/Http/ViewComposers/Server/ServerDataComposer.php index 7da647587..9e1858645 100644 --- a/app/Http/ViewComposers/Server/ServerDataComposer.php +++ b/app/Http/ViewComposers/Server/ServerDataComposer.php @@ -1,32 +1,25 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Http\ViewComposers\Server; use Illuminate\View\View; -use Illuminate\Contracts\Session\Session; +use Illuminate\Http\Request; class ServerDataComposer { /** - * @var \Illuminate\Contracts\Session\Session + * @var \Illuminate\Http\Request */ - protected $session; + protected $request; /** * ServerDataComposer constructor. * - * @param \Illuminate\Contracts\Session\Session $session + * @param \Illuminate\Http\Request $request */ - public function __construct(Session $session) + public function __construct(Request $request) { - $this->session = $session; + $this->request = $request; } /** @@ -36,10 +29,10 @@ class ServerDataComposer */ public function compose(View $view) { - $data = $this->session->get('server_data'); + $server = $this->request->get('server'); - $view->with('server', array_get($data, 'model')); - $view->with('node', object_get($data['model'], 'node')); - $view->with('daemon_token', array_get($data, 'token')); + $view->with('server', $server); + $view->with('node', object_get($server, 'node')); + $view->with('daemon_token', $this->request->get('server_token')); } } diff --git a/app/Repositories/Eloquent/DatabaseRepository.php b/app/Repositories/Eloquent/DatabaseRepository.php index de3ff65bf..f030812b5 100644 --- a/app/Repositories/Eloquent/DatabaseRepository.php +++ b/app/Repositories/Eloquent/DatabaseRepository.php @@ -10,6 +10,7 @@ namespace Pterodactyl\Repositories\Eloquent; use Pterodactyl\Models\Database; +use Illuminate\Support\Collection; use Illuminate\Foundation\Application; use Illuminate\Database\DatabaseManager; use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; @@ -17,6 +18,11 @@ use Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException; class DatabaseRepository extends EloquentRepository implements DatabaseRepositoryInterface { + /** + * @var string + */ + protected $connection = self::DEFAULT_CONNECTION_NAME; + /** * @var \Illuminate\Database\DatabaseManager */ @@ -45,6 +51,40 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor return Database::class; } + /** + * Set the connection name to execute statements against. + * + * @param string $connection + * @return $this + */ + public function setConnection(string $connection) + { + $this->connection = $connection; + + return $this; + } + + /** + * Return the connection to execute statements aganist. + * + * @return string + */ + public function getConnection(): string + { + return $this->connection; + } + + /** + * Return all of the databases belonging to a server. + * + * @param int $server + * @return \Illuminate\Support\Collection + */ + public function getDatabasesForServer(int $server): Collection + { + return $this->getBuilder()->where('server_id', $server)->get($this->getColumns()); + } + /** * {@inheritdoc} * @return bool|\Illuminate\Database\Eloquent\Model @@ -67,80 +107,64 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor /** * {@inheritdoc} */ - public function createDatabase($database, $connection = null) + public function createDatabase($database) { - return $this->runStatement( - sprintf('CREATE DATABASE IF NOT EXISTS `%s`', $database), - $connection - ); + return $this->runStatement(sprintf('CREATE DATABASE IF NOT EXISTS `%s`', $database)); } /** * {@inheritdoc} */ - public function createUser($username, $remote, $password, $connection = null) + public function createUser($username, $remote, $password) { - return $this->runStatement( - sprintf('CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'', $username, $remote, $password), - $connection - ); + return $this->runStatement(sprintf('CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'', $username, $remote, $password)); } /** * {@inheritdoc} */ - public function assignUserToDatabase($database, $username, $remote, $connection = null) + public function assignUserToDatabase($database, $username, $remote) { - return $this->runStatement( - sprintf( - 'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, EXECUTE ON `%s`.* TO `%s`@`%s`', - $database, - $username, - $remote - ), - $connection - ); + return $this->runStatement(sprintf( + 'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, EXECUTE ON `%s`.* TO `%s`@`%s`', + $database, + $username, + $remote + )); } /** * {@inheritdoc} */ - public function flush($connection = null) + public function flush() { - return $this->runStatement('FLUSH PRIVILEGES', $connection); + return $this->runStatement('FLUSH PRIVILEGES'); } /** * {@inheritdoc} */ - public function dropDatabase($database, $connection = null) + public function dropDatabase($database) { - return $this->runStatement( - sprintf('DROP DATABASE IF EXISTS `%s`', $database), - $connection - ); + return $this->runStatement(sprintf('DROP DATABASE IF EXISTS `%s`', $database)); } /** * {@inheritdoc} */ - public function dropUser($username, $remote, $connection = null) + public function dropUser($username, $remote) { - return $this->runStatement( - sprintf('DROP USER IF EXISTS `%s`@`%s`', $username, $remote), - $connection - ); + return $this->runStatement(sprintf('DROP USER IF EXISTS `%s`@`%s`', $username, $remote)); } /** * Run the provided statement against the database on a given connection. * - * @param string $statement - * @param null|string $connection + * @param string $statement * @return bool */ - protected function runStatement($statement, $connection = null) + protected function runStatement($statement) { - return $this->database->connection($connection)->statement($statement); + return $this->database->connection($this->getConnection())->statement($statement); } } diff --git a/app/Services/Database/DatabaseHostService.php b/app/Services/Database/DatabaseHostService.php deleted file mode 100644 index cb7f1f9f3..000000000 --- a/app/Services/Database/DatabaseHostService.php +++ /dev/null @@ -1,148 +0,0 @@ -. - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ - -namespace Pterodactyl\Services\Database; - -use Illuminate\Database\DatabaseManager; -use Pterodactyl\Exceptions\DisplayException; -use Illuminate\Contracts\Encryption\Encrypter; -use Pterodactyl\Extensions\DynamicDatabaseConnection; -use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; -use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; - -class DatabaseHostService -{ - /** - * @var \Illuminate\Database\DatabaseManager - */ - protected $database; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface - */ - protected $databaseRepository; - - /** - * @var \Pterodactyl\Extensions\DynamicDatabaseConnection - */ - protected $dynamic; - - /** - * @var \Illuminate\Contracts\Encryption\Encrypter - */ - protected $encrypter; - - /** - * @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface - */ - protected $repository; - - /** - * DatabaseHostService constructor. - * - * @param \Illuminate\Database\DatabaseManager $database - * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $databaseRepository - * @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $repository - * @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic - * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter - */ - public function __construct( - DatabaseManager $database, - DatabaseRepositoryInterface $databaseRepository, - DatabaseHostRepositoryInterface $repository, - DynamicDatabaseConnection $dynamic, - Encrypter $encrypter - ) { - $this->database = $database; - $this->databaseRepository = $databaseRepository; - $this->dynamic = $dynamic; - $this->encrypter = $encrypter; - $this->repository = $repository; - } - - /** - * Create a new database host and persist it to the database. - * - * @param array $data - * @return \Pterodactyl\Models\DatabaseHost - * - * @throws \Throwable - * @throws \PDOException - */ - public function create(array $data) - { - $this->database->beginTransaction(); - - $host = $this->repository->create([ - 'password' => $this->encrypter->encrypt(array_get($data, 'password')), - 'name' => array_get($data, 'name'), - 'host' => array_get($data, 'host'), - 'port' => array_get($data, 'port'), - 'username' => array_get($data, 'username'), - 'max_databases' => null, - 'node_id' => array_get($data, 'node_id'), - ]); - - // Check Access - $this->dynamic->set('dynamic', $host); - $this->database->connection('dynamic')->select('SELECT 1 FROM dual'); - - $this->database->commit(); - - return $host; - } - - /** - * Update a database host and persist to the database. - * - * @param int $id - * @param array $data - * @return mixed - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function update($id, array $data) - { - $this->database->beginTransaction(); - - if (! empty(array_get($data, 'password'))) { - $data['password'] = $this->encrypter->encrypt($data['password']); - } else { - unset($data['password']); - } - - $host = $this->repository->update($id, $data); - - $this->dynamic->set('dynamic', $host); - $this->database->connection('dynamic')->select('SELECT 1 FROM dual'); - - $this->database->commit(); - - return $host; - } - - /** - * Delete a database host if it has no active databases attached to it. - * - * @param int $id - * @return bool|null - * - * @throws \Pterodactyl\Exceptions\DisplayException - */ - public function delete($id) - { - $count = $this->databaseRepository->findCountWhere([['database_host_id', '=', $id]]); - if ($count > 0) { - throw new DisplayException(trans('exceptions.databases.delete_has_databases')); - } - - return $this->repository->delete($id); - } -} diff --git a/app/Services/Database/DatabaseManagementService.php b/app/Services/Databases/DatabaseManagementService.php similarity index 67% rename from app/Services/Database/DatabaseManagementService.php rename to app/Services/Databases/DatabaseManagementService.php index 21d01809e..845ee6282 100644 --- a/app/Services/Database/DatabaseManagementService.php +++ b/app/Services/Databases/DatabaseManagementService.php @@ -7,7 +7,7 @@ * https://opensource.org/licenses/MIT */ -namespace Pterodactyl\Services\Database; +namespace Pterodactyl\Services\Databases; use Illuminate\Database\DatabaseManager; use Illuminate\Contracts\Encryption\Encrypter; @@ -79,28 +79,26 @@ class DatabaseManagementService $database = $this->repository->createIfNotExists($data); $this->dynamic->set('dynamic', $data['database_host_id']); - $this->repository->createDatabase($database->database, 'dynamic'); + $this->repository->createDatabase($database->database); $this->repository->createUser( $database->username, $database->remote, - $this->encrypter->decrypt($database->password), - 'dynamic' + $this->encrypter->decrypt($database->password) ); $this->repository->assignUserToDatabase( $database->database, $database->username, - $database->remote, - 'dynamic' + $database->remote ); - $this->repository->flush('dynamic'); + $this->repository->flush(); $this->database->commit(); } catch (\Exception $ex) { try { if (isset($database)) { - $this->repository->dropDatabase($database->database, 'dynamic'); - $this->repository->dropUser($database->username, $database->remote, 'dynamic'); - $this->repository->flush('dynamic'); + $this->repository->dropDatabase($database->database); + $this->repository->dropUser($database->username, $database->remote); + $this->repository->flush(); } } catch (\Exception $exTwo) { // ignore an exception @@ -113,62 +111,22 @@ class DatabaseManagementService return $database; } - /** - * Change the password for a specific user and database combination. - * - * @param int $id - * @param string $password - * @return bool - * - * @throws \Exception - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - */ - public function changePassword($id, $password) - { - $database = $this->repository->find($id); - $this->dynamic->set('dynamic', $database->database_host_id); - - $this->database->beginTransaction(); - - try { - $updated = $this->repository->withoutFresh()->update($id, [ - 'password' => $this->encrypter->encrypt($password), - ]); - - $this->repository->dropUser($database->username, $database->remote, 'dynamic'); - $this->repository->createUser($database->username, $database->remote, $password, 'dynamic'); - $this->repository->assignUserToDatabase( - $database->database, - $database->username, - $database->remote, - 'dynamic' - ); - $this->repository->flush('dynamic'); - - $this->database->commit(); - } catch (\Exception $ex) { - $this->database->rollBack(); - throw $ex; - } - - return $updated; - } - /** * Delete a database from the given host server. * * @param int $id * @return bool|null + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function delete($id) { $database = $this->repository->find($id); $this->dynamic->set('dynamic', $database->database_host_id); - $this->repository->dropDatabase($database->database, 'dynamic'); - $this->repository->dropUser($database->username, $database->remote, 'dynamic'); - $this->repository->flush('dynamic'); + $this->repository->dropDatabase($database->database); + $this->repository->dropUser($database->username, $database->remote); + $this->repository->flush(); return $this->repository->delete($id); } diff --git a/app/Services/Databases/DatabasePasswordService.php b/app/Services/Databases/DatabasePasswordService.php new file mode 100644 index 000000000..4f6443ced --- /dev/null +++ b/app/Services/Databases/DatabasePasswordService.php @@ -0,0 +1,86 @@ +connection = $connection; + $this->dynamic = $dynamic; + $this->encrypter = $encrypter; + $this->repository = $repository; + } + + /** + * Updates a password for a given database. + * + * @param \Pterodactyl\Models\Database|int $database + * @param string $password + * @return bool + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function handle($database, string $password): bool + { + if (! $database instanceof Database) { + $database = $this->repository->find($database); + } + + $this->dynamic->set('dynamic', $database->database_host_id); + $this->connection->beginTransaction(); + + $updated = $this->repository->withoutFresh()->update($database->id, [ + 'password' => $this->encrypter->encrypt($password), + ]); + + $this->repository->dropUser($database->username, $database->remote); + $this->repository->createUser($database->username, $database->remote, $password); + $this->repository->assignUserToDatabase($database->database, $database->username, $database->remote); + $this->repository->flush(); + + unset($password); + $this->connection->commit(); + + return $updated; + } +} diff --git a/app/Services/Databases/Hosts/HostCreationService.php b/app/Services/Databases/Hosts/HostCreationService.php new file mode 100644 index 000000000..15b32ea04 --- /dev/null +++ b/app/Services/Databases/Hosts/HostCreationService.php @@ -0,0 +1,92 @@ +connection = $connection; + $this->databaseManager = $databaseManager; + $this->dynamic = $dynamic; + $this->encrypter = $encrypter; + $this->repository = $repository; + } + + /** + * Create a new database host on the Panel. + * + * @param array $data + * @return \Pterodactyl\Models\DatabaseHost + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function handle(array $data): DatabaseHost + { + $this->connection->beginTransaction(); + + $host = $this->repository->create([ + 'password' => $this->encrypter->encrypt(array_get($data, 'password')), + 'name' => array_get($data, 'name'), + 'host' => array_get($data, 'host'), + 'port' => array_get($data, 'port'), + 'username' => array_get($data, 'username'), + 'max_databases' => null, + 'node_id' => array_get($data, 'node_id'), + ]); + + // Confirm access using the provided credentials before saving data. + $this->dynamic->set('dynamic', $host); + $this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual'); + $this->connection->commit(); + + return $host; + } +} diff --git a/app/Services/Databases/Hosts/HostDeletionService.php b/app/Services/Databases/Hosts/HostDeletionService.php new file mode 100644 index 000000000..b69c8dcf9 --- /dev/null +++ b/app/Services/Databases/Hosts/HostDeletionService.php @@ -0,0 +1,53 @@ +databaseRepository = $databaseRepository; + $this->repository = $repository; + } + + /** + * Delete a specified host from the Panel if no databases are + * attached to it. + * + * @param int $host + * @return int + * + * @throws \Pterodactyl\Exceptions\Service\HasActiveServersException + */ + public function handle(int $host): int + { + $count = $this->databaseRepository->findCountWhere([['database_host_id', '=', $host]]); + if ($count > 0) { + throw new HasActiveServersException(trans('exceptions.databases.delete_has_databases')); + } + + return $this->repository->delete($host); + } +} diff --git a/app/Services/Databases/Hosts/HostUpdateService.php b/app/Services/Databases/Hosts/HostUpdateService.php new file mode 100644 index 000000000..5f4b19b31 --- /dev/null +++ b/app/Services/Databases/Hosts/HostUpdateService.php @@ -0,0 +1,96 @@ +. + * + * This software is licensed under the terms of the MIT license. + * https://opensource.org/licenses/MIT + */ + +namespace Pterodactyl\Services\Databases\Hosts; + +use Pterodactyl\Models\DatabaseHost; +use Illuminate\Database\DatabaseManager; +use Illuminate\Database\ConnectionInterface; +use Illuminate\Contracts\Encryption\Encrypter; +use Pterodactyl\Extensions\DynamicDatabaseConnection; +use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; + +class HostUpdateService +{ + /** + * @var \Illuminate\Database\ConnectionInterface + */ + private $connection; + + /** + * @var \Illuminate\Database\DatabaseManager + */ + private $databaseManager; + + /** + * @var \Pterodactyl\Extensions\DynamicDatabaseConnection + */ + private $dynamic; + + /** + * @var \Illuminate\Contracts\Encryption\Encrypter + */ + private $encrypter; + + /** + * @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface + */ + private $repository; + + /** + * DatabaseHostService constructor. + * + * @param \Illuminate\Database\ConnectionInterface $connection + * @param \Illuminate\Database\DatabaseManager $databaseManager + * @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $repository + * @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic + * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter + */ + public function __construct( + ConnectionInterface $connection, + DatabaseManager $databaseManager, + DatabaseHostRepositoryInterface $repository, + DynamicDatabaseConnection $dynamic, + Encrypter $encrypter + ) { + $this->connection = $connection; + $this->databaseManager = $databaseManager; + $this->dynamic = $dynamic; + $this->encrypter = $encrypter; + $this->repository = $repository; + } + + /** + * Update a database host and persist to the database. + * + * @param int $hostId + * @param array $data + * @return mixed + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function handle(int $hostId, array $data): DatabaseHost + { + if (! empty(array_get($data, 'password'))) { + $data['password'] = $this->encrypter->encrypt($data['password']); + } else { + unset($data['password']); + } + + $this->connection->beginTransaction(); + $host = $this->repository->update($hostId, $data); + + $this->dynamic->set('dynamic', $host); + $this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual'); + $this->connection->commit(); + + return $host; + } +} diff --git a/app/Services/Servers/ServerDeletionService.php b/app/Services/Servers/ServerDeletionService.php index 836df7e4f..1129a187c 100644 --- a/app/Services/Servers/ServerDeletionService.php +++ b/app/Services/Servers/ServerDeletionService.php @@ -14,7 +14,7 @@ use Pterodactyl\Models\Server; use GuzzleHttp\Exception\RequestException; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Exceptions\DisplayException; -use Pterodactyl\Services\Database\DatabaseManagementService; +use Pterodactyl\Services\Databases\DatabaseManagementService; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; @@ -32,7 +32,7 @@ class ServerDeletionService protected $daemonServerRepository; /** - * @var \Pterodactyl\Services\Database\DatabaseManagementService + * @var \Pterodactyl\Services\Databases\DatabaseManagementService */ protected $databaseManagementService; @@ -62,7 +62,7 @@ class ServerDeletionService * @param \Illuminate\Database\ConnectionInterface $connection * @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonServerRepository * @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $databaseRepository - * @param \Pterodactyl\Services\Database\DatabaseManagementService $databaseManagementService + * @param \Pterodactyl\Services\Databases\DatabaseManagementService $databaseManagementService * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository * @param \Illuminate\Log\Writer $writer */ diff --git a/app/Traits/Controllers/JavascriptInjection.php b/app/Traits/Controllers/JavascriptInjection.php index 5063a50f2..cce577819 100644 --- a/app/Traits/Controllers/JavascriptInjection.php +++ b/app/Traits/Controllers/JavascriptInjection.php @@ -10,25 +10,23 @@ namespace Pterodactyl\Traits\Controllers; use Javascript; +use Illuminate\Http\Request; trait JavascriptInjection { - /** - * @var \Illuminate\Contracts\Session\Session - */ - protected $session; - /** * Injects server javascript into the page to be used by other services. * - * @param array $args - * @param bool $overwrite - * @return mixed + * @param array $args + * @param bool $overwrite + * @param \Illuminate\Http\Request|null $request + * @return array */ - public function injectJavascript($args = [], $overwrite = false) + public function injectJavascript($args = [], $overwrite = false, Request $request = null) { - $server = $this->session->get('server_data.model'); - $token = $this->session->get('server_data.token'); + $request = $request ?? app()->make(Request::class); + $server = $request->attributes->get('server'); + $token = $request->attributes->get('server_token'); $response = array_merge([ 'server' => [ diff --git a/public/js/laroute.js b/public/js/laroute.js index e6e9db4c7..deec07500 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":"api\/user","name":"api.user","action":"Pterodactyl\Http\Controllers\API\User\CoreController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/user\/server\/{server}","name":"api.user.server","action":"Pterodactyl\Http\Controllers\API\User\ServerController@index"},{"host":null,"methods":["POST"],"uri":"api\/user\/server\/{server}\/power","name":"api.user.server.power","action":"Pterodactyl\Http\Controllers\API\User\ServerController@power"},{"host":null,"methods":["POST"],"uri":"api\/user\/server\/{server}\/command","name":"api.user.server.command","action":"Pterodactyl\Http\Controllers\API\User\ServerController@command"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\CoreController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/servers","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/servers\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@view"},{"host":null,"methods":["POST"],"uri":"api\/admin\/servers","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@store"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/details","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@details"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/container","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@container"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/build","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@build"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/servers\/{id}\/startup","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@startup"},{"host":null,"methods":["PATCH"],"uri":"api\/admin\/servers\/{id}\/install","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@install"},{"host":null,"methods":["PATCH"],"uri":"api\/admin\/servers\/{id}\/rebuild","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@rebuild"},{"host":null,"methods":["PATCH"],"uri":"api\/admin\/servers\/{id}\/suspend","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@suspend"},{"host":null,"methods":["DELETE"],"uri":"api\/admin\/servers\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServerController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/locations","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\LocationController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/nodes","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/nodes\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/nodes\/{id}\/config","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@viewConfig"},{"host":null,"methods":["POST"],"uri":"api\/admin\/nodes","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@store"},{"host":null,"methods":["DELETE"],"uri":"api\/admin\/nodes\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\NodeController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/users","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/users\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@view"},{"host":null,"methods":["POST"],"uri":"api\/admin\/users","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@store"},{"host":null,"methods":["PUT"],"uri":"api\/admin\/users\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@update"},{"host":null,"methods":["DELETE"],"uri":"api\/admin\/users\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\UserController@delete"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/services","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServiceController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/admin\/services\/{id}","name":null,"action":"Pterodactyl\Http\Controllers\API\Admin\ServiceController@view"},{"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":["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\/services","name":"admin.services","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/new","name":"admin.services.new","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/view\/{service}","name":"admin.services.view","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/view\/{service}\/functions","name":"admin.services.view.functions","action":"Pterodactyl\Http\Controllers\Admin\ServiceController@viewFunctions"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/new","name":"admin.services.option.new","action":"Pterodactyl\Http\Controllers\Admin\OptionController@create"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/{option}","name":"admin.services.option.view","action":"Pterodactyl\Http\Controllers\Admin\OptionController@viewConfiguration"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/{option}\/variables","name":"admin.services.option.variables","action":"Pterodactyl\Http\Controllers\Admin\VariableController@view"},{"host":null,"methods":["GET","HEAD"],"uri":"admin\/services\/option\/{option}\/scripts","name":"admin.services.option.scripts","action":"Pterodactyl\Http\Controllers\Admin\OptionController@viewScripts"},{"host":null,"methods":["POST"],"uri":"admin\/services\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@store"},{"host":null,"methods":["POST"],"uri":"admin\/services\/option\/new","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@store"},{"host":null,"methods":["POST"],"uri":"admin\/services\/option\/{option}\/variables","name":null,"action":"Pterodactyl\Http\Controllers\Admin\VariableController@store"},{"host":null,"methods":["PATCH"],"uri":"admin\/services\/view\/{service}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@update"},{"host":null,"methods":["PATCH"],"uri":"admin\/services\/view\/{service}\/functions","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@updateFunctions"},{"host":null,"methods":["PATCH"],"uri":"admin\/services\/option\/{option}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@editConfiguration"},{"host":null,"methods":["PATCH"],"uri":"admin\/services\/option\/{option}\/scripts","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@updateScripts"},{"host":null,"methods":["PATCH"],"uri":"admin\/services\/option\/{option}\/variables\/{variable}","name":"admin.services.option.variables.edit","action":"Pterodactyl\Http\Controllers\Admin\VariableController@update"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/view\/{service}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\ServiceController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/option\/{option}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\OptionController@destroy"},{"host":null,"methods":["DELETE"],"uri":"admin\/services\/option\/{option}\/variables\/{variable}","name":null,"action":"Pterodactyl\Http\Controllers\Admin\VariableController@delete"},{"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\/databases","name":"server.settings.databases","action":"Pterodactyl\Http\Controllers\Server\ServerController@getDatabases"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/sftp","name":"server.settings.sftp","action":"Pterodactyl\Http\Controllers\Server\ServerController@getSFTP"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\ServerController@getStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAllocation"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/sftp","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsSFTP"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsStartup"},{"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":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{subuser}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@view"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@store"},{"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}\/delete","name":"server.subusers.delete","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":["GET","HEAD"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":"server.schedules.view","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@view"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@store"},{"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}\/delete","name":"server.schedules.delete","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@delete"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/ajax\/settings\/reset-database-password","name":"server.ajax.reset-database-password","action":"Pterodactyl\Http\Controllers\Server\AjaxController@postResetDatabasePassword"},{"host":null,"methods":["GET","HEAD"],"uri":"api\/remote\/authenticate\/{token}","name":"post.api.remote.authenticate","action":"Pterodactyl\Http\Controllers\API\Remote\ValidateKeyController@index"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services","name":"daemon.services","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@listServices"},{"host":null,"methods":["GET","HEAD"],"uri":"daemon\/services\/pull\/{service}\/{file}","name":"daemon.pull","action":"Pterodactyl\Http\Controllers\Daemon\ServiceController@pull"},{"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":"\/","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\/sftp","name":"server.settings.sftp","action":"Pterodactyl\Http\Controllers\Server\ServerController@getSFTP"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/startup","name":"server.settings.startup","action":"Pterodactyl\Http\Controllers\Server\ServerController@getStartup"},{"host":null,"methods":["GET","HEAD"],"uri":"server\/{server}\/settings\/allocation","name":"server.settings.allocation","action":"Pterodactyl\Http\Controllers\Server\ServerController@getAllocation"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/sftp","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsSFTP"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/settings\/startup","name":null,"action":"Pterodactyl\Http\Controllers\Server\ServerController@postSettingsStartup"},{"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":["GET","HEAD"],"uri":"server\/{server}\/users\/view\/{subuser}","name":"server.subusers.view","action":"Pterodactyl\Http\Controllers\Server\SubuserController@view"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/users\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\SubuserController@store"},{"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}\/delete","name":"server.subusers.delete","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":["GET","HEAD"],"uri":"server\/{server}\/schedules\/view\/{schedule}","name":"server.schedules.view","action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@view"},{"host":null,"methods":["POST"],"uri":"server\/{server}\/schedules\/new","name":null,"action":"Pterodactyl\Http\Controllers\Server\Tasks\TaskManagementController@store"},{"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}\/delete","name":"server.schedules.delete","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":"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"}], prefix: '', route : function (name, parameters, route) { diff --git a/resources/lang/en/server.php b/resources/lang/en/server.php index 0aa414fd2..ec570e4a8 100644 --- a/resources/lang/en/server.php +++ b/resources/lang/en/server.php @@ -302,7 +302,7 @@ return [ 'database' => [ 'header' => 'Databases', 'header_sub' => 'All databases available for this server.', - 'your_dbs' => 'Your Databases', + 'your_dbs' => 'Configured Databases', 'host' => 'MySQL Host', 'reset_password' => 'Reset Password', 'no_dbs' => 'There are no databases listed for this server.', diff --git a/resources/themes/pterodactyl/admin/databases/view.blade.php b/resources/themes/pterodactyl/admin/databases/view.blade.php index 65296e578..427fafb82 100644 --- a/resources/themes/pterodactyl/admin/databases/view.blade.php +++ b/resources/themes/pterodactyl/admin/databases/view.blade.php @@ -79,9 +79,8 @@ diff --git a/resources/themes/pterodactyl/admin/servers/view/database.blade.php b/resources/themes/pterodactyl/admin/servers/view/database.blade.php index c76d1fbba..6c556137d 100644 --- a/resources/themes/pterodactyl/admin/servers/view/database.blade.php +++ b/resources/themes/pterodactyl/admin/servers/view/database.blade.php @@ -40,6 +40,9 @@
+
+ Database passwords can be viewed when visiting this server on the front-end. +

Active Databases

@@ -128,7 +131,7 @@ }, function () { $.ajax({ method: 'DELETE', - url: Router.route('admin.servers.view.database.delete', { id: '{{ $server->id }}', database: self.data('id') }), + url: Router.route('admin.servers.view.database.delete', { server: '{{ $server->id }}', database: self.data('id') }), headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content') }, }).done(function () { self.parent().parent().slideUp(); @@ -149,7 +152,7 @@ $(this).addClass('disabled').find('i').addClass('fa-spin'); $.ajax({ type: 'PATCH', - url: Router.route('admin.servers.view.database', { id: '{{ $server->id }}' }), + url: Router.route('admin.servers.view.database', { server: '{{ $server->id }}' }), headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content') }, data: { database: $(this).data('id') }, }).done(function (data) { diff --git a/resources/themes/pterodactyl/layouts/master.blade.php b/resources/themes/pterodactyl/layouts/master.blade.php index 102d45fa9..651686a33 100644 --- a/resources/themes/pterodactyl/layouts/master.blade.php +++ b/resources/themes/pterodactyl/layouts/master.blade.php @@ -155,6 +155,17 @@ @endcan + @can('view-databases', $server) +
  • + + @lang('navigation.server.databases') + +
  • + @endcan @if(Gate::allows('view-startup', $server) || Gate::allows('view-sftp', $server) || Gate::allows('view-databases', $server) || Gate::allows('view-allocation', $server))
  • @lang('navigation.server.startup_parameters')
  • @endcan - @can('view-databases', $server) -
  • @lang('navigation.server.databases')
  • - @endcan @endif diff --git a/resources/themes/pterodactyl/server/settings/databases.blade.php b/resources/themes/pterodactyl/server/databases/index.blade.php similarity index 55% rename from resources/themes/pterodactyl/server/settings/databases.blade.php rename to resources/themes/pterodactyl/server/databases/index.blade.php index e2c400da1..6ba24c1ad 100644 --- a/resources/themes/pterodactyl/server/settings/databases.blade.php +++ b/resources/themes/pterodactyl/server/databases/index.blade.php @@ -25,6 +25,11 @@

    @lang('server.config.database.your_dbs')

    + @if(auth()->user()->root_admin) + + @endif
    @if(count($databases) > 0)
    @@ -41,7 +46,14 @@ {{ $database->database }} {{ $database->username }} - {{ Crypt::decrypt($database->password) }} + + + •••••••• + + + {{ $database->host->host }}:{{ $database->host->port }} @can('reset-db-password', $server) @@ -55,7 +67,7 @@
    @else
    -
    +
    @lang('server.config.database.no_dbs') @if(Auth::user()->root_admin === 1) @endsection diff --git a/routes/admin.php b/routes/admin.php index da1451cb9..9332b82de 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -38,6 +38,7 @@ Route::group(['prefix' => 'databases'], function () { Route::post('/', 'DatabaseController@create'); Route::patch('/view/{host}', 'DatabaseController@update'); + Route::delete('/view/{host}', 'DatabaseController@delete'); }); /* diff --git a/routes/server.php b/routes/server.php index c0d04dd39..6386b6618 100644 --- a/routes/server.php +++ b/routes/server.php @@ -18,7 +18,6 @@ Route::get('/console', 'ConsoleController@console')->name('server.console'); | */ Route::group(['prefix' => 'settings'], function () { - Route::get('/databases', 'ServerController@getDatabases')->name('server.settings.databases'); Route::get('/sftp', 'ServerController@getSFTP')->name('server.settings.sftp'); Route::get('/startup', 'ServerController@getStartup')->name('server.settings.startup'); Route::get('/allocation', 'ServerController@getAllocation')->name('server.settings.allocation'); @@ -27,6 +26,20 @@ Route::group(['prefix' => 'settings'], function () { Route::post('/startup', 'ServerController@postSettingsStartup'); }); +/* +|-------------------------------------------------------------------------- +| Server Database Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /server/{server}/databases +| +*/ +Route::group(['prefix' => 'databases'], function () { + Route::get('/', 'DatabaseController@index')->name('server.databases.index'); + + Route::patch('/password', 'DatabaseController@update')->middleware('server..database')->name('server.databases.password'); +}); + /* |-------------------------------------------------------------------------- | Server File Manager Controller Routes @@ -56,13 +69,13 @@ Route::group(['prefix' => 'files'], function () { Route::group(['prefix' => 'users'], function () { Route::get('/', 'SubuserController@index')->name('server.subusers'); Route::get('/new', 'SubuserController@create')->name('server.subusers.new'); - Route::get('/view/{subuser}', 'SubuserController@view')->middleware('subuser')->name('server.subusers.view'); + Route::get('/view/{subuser}', 'SubuserController@view')->middleware('server..subuser')->name('server.subusers.view'); Route::post('/new', 'SubuserController@store'); - Route::patch('/view/{subuser}', 'SubuserController@update')->middleware('subuser'); + Route::patch('/view/{subuser}', 'SubuserController@update')->middleware('server..subuser'); - Route::delete('/view/{subuser}/delete', 'SubuserController@delete')->middleware('subuser')->name('server.subusers.delete'); + Route::delete('/view/{subuser}/delete', 'SubuserController@delete')->middleware('server..subuser')->name('server.subusers.delete'); }); /* @@ -76,24 +89,12 @@ Route::group(['prefix' => 'users'], function () { Route::group(['prefix' => 'schedules'], function () { Route::get('/', 'Tasks\TaskManagementController@index')->name('server.schedules'); Route::get('/new', 'Tasks\TaskManagementController@create')->name('server.schedules.new'); - Route::get('/view/{schedule}', 'Tasks\TaskManagementController@view')->middleware('schedule')->name('server.schedules.view'); + Route::get('/view/{schedule}', 'Tasks\TaskManagementController@view')->middleware('server..schedule')->name('server.schedules.view'); Route::post('/new', 'Tasks\TaskManagementController@store'); - Route::patch('/view/{schedule}', 'Tasks\TaskManagementController@update')->middleware('schedule'); - Route::patch('/view/{schedule}/toggle', 'Tasks\TaskToggleController@index')->middleware('schedule')->name('server.schedules.toggle'); + Route::patch('/view/{schedule}', 'Tasks\TaskManagementController@update')->middleware('server..schedule'); + Route::patch('/view/{schedule}/toggle', 'Tasks\TaskToggleController@index')->middleware('server..schedule')->name('server.schedules.toggle'); - Route::delete('/view/{schedule}/delete', 'Tasks\TaskManagementController@delete')->middleware('schedule')->name('server.schedules.delete'); -}); - -/* -|-------------------------------------------------------------------------- -| Server Ajax Controller Routes -|-------------------------------------------------------------------------- -| -| Endpoint: /server/{server}/ajax -| -*/ -Route::group(['prefix' => 'ajax'], function () { - Route::post('/settings/reset-database-password', 'AjaxController@postResetDatabasePassword')->name('server.ajax.reset-database-password'); + Route::delete('/view/{schedule}/delete', 'Tasks\TaskManagementController@delete')->middleware('server..schedule')->name('server.schedules.delete'); }); diff --git a/tests/Unit/Http/Controllers/Admin/DatabaseControllerTest.php b/tests/Unit/Http/Controllers/Admin/DatabaseControllerTest.php index 3e98fad4c..ac723f6e9 100644 --- a/tests/Unit/Http/Controllers/Admin/DatabaseControllerTest.php +++ b/tests/Unit/Http/Controllers/Admin/DatabaseControllerTest.php @@ -13,7 +13,6 @@ use Mockery as m; use Tests\TestCase; use Prologue\Alerts\AlertsMessageBag; use Tests\Assertions\ControllerAssertionsTrait; -use Pterodactyl\Services\Database\DatabaseHostService; use Pterodactyl\Http\Controllers\Admin\DatabaseController; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; @@ -43,7 +42,7 @@ class DatabaseControllerTest extends TestCase protected $repository; /** - * @var \Pterodactyl\Services\Database\DatabaseHostService + * @var \Pterodactyl\Services\Databases\HostsUpdateService */ protected $service; @@ -57,7 +56,7 @@ class DatabaseControllerTest extends TestCase $this->alert = m::mock(AlertsMessageBag::class); $this->locationRepository = m::mock(LocationRepositoryInterface::class); $this->repository = m::mock(DatabaseHostRepositoryInterface::class); - $this->service = m::mock(DatabaseHostService::class); + $this->service = m::mock(HostUpdateService::class); $this->controller = new DatabaseController( $this->alert, diff --git a/tests/Unit/Services/Database/DatabaseHostServiceTest.php b/tests/Unit/Services/Database/DatabaseHostServiceTest.php index bf8b5dee7..f5e8d09f6 100644 --- a/tests/Unit/Services/Database/DatabaseHostServiceTest.php +++ b/tests/Unit/Services/Database/DatabaseHostServiceTest.php @@ -15,7 +15,6 @@ use Illuminate\Database\DatabaseManager; use Pterodactyl\Exceptions\DisplayException; use Illuminate\Contracts\Encryption\Encrypter; use Pterodactyl\Extensions\DynamicDatabaseConnection; -use Pterodactyl\Services\Database\DatabaseHostService; use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; @@ -47,7 +46,7 @@ class DatabaseHostServiceTest extends TestCase protected $repository; /** - * @var \Pterodactyl\Services\Database\DatabaseHostService + * @var \Pterodactyl\Services\Databases\HostsUpdateService */ protected $service; @@ -64,7 +63,7 @@ class DatabaseHostServiceTest extends TestCase $this->encrypter = m::mock(Encrypter::class); $this->repository = m::mock(DatabaseHostRepositoryInterface::class); - $this->service = new DatabaseHostService( + $this->service = new HostUpdateService( $this->database, $this->databaseRepository, $this->repository, diff --git a/tests/Unit/Services/Database/DatabaseManagementServiceTest.php b/tests/Unit/Services/Database/DatabaseManagementServiceTest.php index c679ffa12..a721b4760 100644 --- a/tests/Unit/Services/Database/DatabaseManagementServiceTest.php +++ b/tests/Unit/Services/Database/DatabaseManagementServiceTest.php @@ -16,7 +16,7 @@ use phpmock\phpunit\PHPMock; use Illuminate\Database\DatabaseManager; use Illuminate\Contracts\Encryption\Encrypter; use Pterodactyl\Extensions\DynamicDatabaseConnection; -use Pterodactyl\Services\Database\DatabaseManagementService; +use Pterodactyl\Services\Databases\DatabaseManagementService; use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; class DatabaseManagementServiceTest extends TestCase @@ -53,7 +53,7 @@ class DatabaseManagementServiceTest extends TestCase protected $repository; /** - * @var \Pterodactyl\Services\Database\DatabaseManagementService + * @var \Pterodactyl\Services\Databases\DatabaseManagementService */ protected $service; diff --git a/tests/Unit/Services/Servers/ServerDeletionServiceTest.php b/tests/Unit/Services/Servers/ServerDeletionServiceTest.php index 93702fd29..93fa478f2 100644 --- a/tests/Unit/Services/Servers/ServerDeletionServiceTest.php +++ b/tests/Unit/Services/Servers/ServerDeletionServiceTest.php @@ -18,7 +18,7 @@ use GuzzleHttp\Exception\RequestException; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Services\Servers\ServerDeletionService; -use Pterodactyl\Services\Database\DatabaseManagementService; +use Pterodactyl\Services\Databases\DatabaseManagementService; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; @@ -36,7 +36,7 @@ class ServerDeletionServiceTest extends TestCase protected $daemonServerRepository; /** - * @var \Pterodactyl\Services\Database\DatabaseManagementService + * @var \Pterodactyl\Services\Databases\DatabaseManagementService */ protected $databaseManagementService;