Improve filemanager, get first level folders listing

This commit is contained in:
Dane Everitt 2018-08-06 23:14:13 -07:00
parent 00a3d7df87
commit 92a9146b61
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
20 changed files with 125 additions and 1318 deletions

View file

@ -13,7 +13,7 @@ interface FileRepositoryInterface extends BaseRepositoryInterface
* @param string $path * @param string $path
* @return \stdClass * @return \stdClass
* *
* @throws \GuzzleHttp\Exception\RequestException * @throws \GuzzleHttp\Exception\TransferException
*/ */
public function getFileStat(string $path): stdClass; public function getFileStat(string $path): stdClass;
@ -23,7 +23,7 @@ interface FileRepositoryInterface extends BaseRepositoryInterface
* @param string $path * @param string $path
* @return string * @return string
* *
* @throws \GuzzleHttp\Exception\RequestException * @throws \GuzzleHttp\Exception\TransferException
*/ */
public function getContent(string $path): string; public function getContent(string $path): string;
@ -34,7 +34,7 @@ interface FileRepositoryInterface extends BaseRepositoryInterface
* @param string $content * @param string $content
* @return \Psr\Http\Message\ResponseInterface * @return \Psr\Http\Message\ResponseInterface
* *
* @throws \GuzzleHttp\Exception\RequestException * @throws \GuzzleHttp\Exception\TransferException
*/ */
public function putContent(string $path, string $content): ResponseInterface; public function putContent(string $path, string $content): ResponseInterface;
@ -44,7 +44,7 @@ interface FileRepositoryInterface extends BaseRepositoryInterface
* @param string $path * @param string $path
* @return array * @return array
* *
* @throws \GuzzleHttp\Exception\RequestException * @throws \GuzzleHttp\Exception\TransferException
*/ */
public function getDirectory(string $path): array; public function getDirectory(string $path): array;
} }

View file

@ -1,73 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Server;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
class ConsoleController extends Controller
{
use JavascriptInjection;
/**
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* ConsoleController constructor.
*
* @param \Illuminate\Contracts\Config\Repository $config
*/
public function __construct(ConfigRepository $config)
{
$this->config = $config;
}
/**
* Render server index page with the console and power options.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request): View
{
$server = $request->attributes->get('server');
$this->setRequest($request)->injectJavascript([
'server' => [
'cpu' => $server->cpu,
],
'meta' => [
'saveFile' => route('server.files.save', $server->uuidShort),
'csrfToken' => csrf_token(),
],
'config' => [
'console_count' => $this->config->get('pterodactyl.console.count'),
'console_freq' => $this->config->get('pterodactyl.console.frequency'),
],
]);
//return view('server.index');
return view('templates/base.core');
}
/**
* Render a stand-alone console in the browser.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function console(Request $request): View
{
$this->setRequest($request)->injectJavascript(['config' => [
'console_count' => $this->config->get('pterodactyl.console.count'),
'console_freq' => $this->config->get('pterodactyl.console.frequency'),
]]);
return view('server.console');
}
}

View file

@ -1,165 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Server;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Services\Databases\DatabasePasswordService;
use Pterodactyl\Services\Databases\DatabaseManagementService;
use Pterodactyl\Services\Databases\DeployServerDatabaseService;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
use Pterodactyl\Http\Requests\Server\Database\StoreServerDatabaseRequest;
use Pterodactyl\Http\Requests\Server\Database\DeleteServerDatabaseRequest;
class DatabaseController extends Controller
{
use JavascriptInjection;
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
private $alert;
/**
* @var \Pterodactyl\Services\Databases\DeployServerDatabaseService
*/
private $deployServerDatabaseService;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface
*/
private $databaseHostRepository;
/**
* @var \Pterodactyl\Services\Databases\DatabaseManagementService
*/
private $managementService;
/**
* @var \Pterodactyl\Services\Databases\DatabasePasswordService
*/
private $passwordService;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
*/
private $repository;
/**
* DatabaseController constructor.
*
* @param \Prologue\Alerts\AlertsMessageBag $alert
* @param \Pterodactyl\Services\Databases\DeployServerDatabaseService $deployServerDatabaseService
* @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $databaseHostRepository
* @param \Pterodactyl\Services\Databases\DatabaseManagementService $managementService
* @param \Pterodactyl\Services\Databases\DatabasePasswordService $passwordService
* @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository
*/
public function __construct(
AlertsMessageBag $alert,
DeployServerDatabaseService $deployServerDatabaseService,
DatabaseHostRepositoryInterface $databaseHostRepository,
DatabaseManagementService $managementService,
DatabasePasswordService $passwordService,
DatabaseRepositoryInterface $repository
) {
$this->alert = $alert;
$this->databaseHostRepository = $databaseHostRepository;
$this->deployServerDatabaseService = $deployServerDatabaseService;
$this->managementService = $managementService;
$this->passwordService = $passwordService;
$this->repository = $repository;
}
/**
* Render the database listing for a server.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index(Request $request): View
{
$server = $request->attributes->get('server');
$this->authorize('view-databases', $server);
$this->setRequest($request)->injectJavascript();
$canCreateDatabase = config('pterodactyl.client_features.databases.enabled');
$allowRandom = config('pterodactyl.client_features.databases.allow_random');
if ($this->databaseHostRepository->findCountWhere([['node_id', '=', $server->node_id]]) === 0) {
if ($canCreateDatabase && ! $allowRandom) {
$canCreateDatabase = false;
}
}
$databases = $this->repository->getDatabasesForServer($server->id);
return view('server.databases.index', [
'allowCreation' => $canCreateDatabase,
'overLimit' => ! is_null($server->database_limit) && count($databases) >= $server->database_limit,
'databases' => $databases,
]);
}
/**
* Handle a request from a user to create a new database for the server.
*
* @param \Pterodactyl\Http\Requests\Server\Database\StoreServerDatabaseRequest $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException
*/
public function store(StoreServerDatabaseRequest $request): RedirectResponse
{
$this->deployServerDatabaseService->handle($request->getServer(), $request->validated());
$this->alert->success('Successfully created a new database.')->flash();
return redirect()->route('server.databases.index', $request->getServer()->uuidShort);
}
/**
* Handle a request to update the password for a specific database.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(Request $request): JsonResponse
{
$this->authorize('reset-db-password', $request->attributes->get('server'));
$password = str_random(20);
$this->passwordService->handle($request->attributes->get('database'), $password);
return response()->json(['password' => $password]);
}
/**
* Delete a database for this server from the SQL server and Panel database.
*
* @param \Pterodactyl\Http\Requests\Server\Database\DeleteServerDatabaseRequest $request
* @return \Illuminate\Http\Response
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function delete(DeleteServerDatabaseRequest $request): Response
{
$this->managementService->delete($request->attributes->get('database')->id);
return response('', Response::HTTP_NO_CONTENT);
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace Pterodactyl\Http\Controllers\Server;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use GuzzleHttp\Exception\TransferException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
class FileController extends Controller
{
/**
* @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface
*/
private $fileRepository;
/**
* FileController constructor.
*
* @param \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface $fileRepository
*/
public function __construct(FileRepositoryInterface $fileRepository)
{
$this->fileRepository = $fileRepository;
}
/**
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/
public function index(Request $request): JsonResponse
{
$server = $request->attributes->get('server');
$this->authorize('list-files', $server);
$requestDirectory = '/' . trim(urldecode($request->route()->parameter('directory', '/')), '/');
$directory = [
'header' => $requestDirectory !== '/' ? $requestDirectory : '',
'first' => $requestDirectory !== '/',
];
$goBack = explode('/', trim($requestDirectory, '/'));
if (! empty(array_filter($goBack)) && count($goBack) >= 2) {
array_pop($goBack);
$directory['show'] = true;
$directory['link'] = '/' . implode('/', $goBack);
$directory['link_show'] = implode('/', $goBack) . '/';
}
try {
$contents = $this->fileRepository->setServer($server)->setToken(
$request->attributes->get('server_token')
)->getDirectory($requestDirectory);
} catch (TransferException $exception) {
throw new DaemonConnectionException($exception, true);
}
return JsonResponse::create([
'contents' => $contents,
'editable' => config('pterodactyl.files.editable'),
'current_directory' => $directory,
]);
}
}

View file

@ -1,57 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Http\Controllers\Server\Files;
use Ramsey\Uuid\Uuid;
use Illuminate\Http\Request;
use Illuminate\Cache\Repository;
use Illuminate\Http\RedirectResponse;
use Pterodactyl\Http\Controllers\Controller;
class DownloadController extends Controller
{
/**
* @var \Illuminate\Cache\Repository
*/
protected $cache;
/**
* DownloadController constructor.
*
* @param \Illuminate\Cache\Repository $cache
*/
public function __construct(Repository $cache)
{
$this->cache = $cache;
}
/**
* Setup a unique download link for a user to download a file from.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @param string $file
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index(Request $request, string $uuid, string $file): RedirectResponse
{
$server = $request->attributes->get('server');
$this->authorize('download-files', $server);
$token = Uuid::uuid4()->toString();
$node = $server->getRelation('node');
$this->cache->put('Server:Downloads:' . $token, ['server' => $server->uuid, 'path' => $file], 5);
return redirect(sprintf('%s://%s:%s/v1/server/file/download/%s', $node->scheme, $node->fqdn, $node->daemonListen, $token));
}
}

View file

@ -1,120 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Http\Controllers\Server\Files;
use Illuminate\View\View;
use Illuminate\Http\Request;
use GuzzleHttp\Exception\RequestException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Http\Requests\Server\UpdateFileContentsFormRequest;
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
class FileActionsController extends Controller
{
use JavascriptInjection;
/**
* @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface
*/
protected $repository;
/**
* FileActionsController constructor.
*
* @param \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface $repository
*/
public function __construct(FileRepositoryInterface $repository)
{
$this->repository = $repository;
}
/**
* Display server file index list.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index(Request $request): View
{
$server = $request->attributes->get('server');
$this->authorize('list-files', $server);
$this->setRequest($request)->injectJavascript([
'meta' => [
'directoryList' => route('server.files.directory-list', $server->uuidShort),
'csrftoken' => csrf_token(),
],
'permissions' => [
'moveFiles' => $request->user()->can('move-files', $server),
'copyFiles' => $request->user()->can('copy-files', $server),
'compressFiles' => $request->user()->can('compress-files', $server),
'decompressFiles' => $request->user()->can('decompress-files', $server),
'createFiles' => $request->user()->can('create-files', $server),
'downloadFiles' => $request->user()->can('download-files', $server),
'deleteFiles' => $request->user()->can('delete-files', $server),
],
]);
return view('server.files.index');
}
/**
* Render page to manually create a file in the panel.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create(Request $request): View
{
$this->authorize('create-files', $request->attributes->get('server'));
$this->setRequest($request)->injectJavascript();
return view('server.files.add', [
'directory' => (in_array($request->get('dir'), [null, '/', ''])) ? '' : trim($request->get('dir'), '/') . '/',
]);
}
/**
* Display a form to allow for editing of a file.
*
* @param \Pterodactyl\Http\Requests\Server\UpdateFileContentsFormRequest $request
* @param string $uuid
* @param string $file
* @return \Illuminate\View\View
*
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/
public function view(UpdateFileContentsFormRequest $request, string $uuid, string $file): View
{
$server = $request->attributes->get('server');
$dirname = str_replace('\\', '/', pathinfo($file, PATHINFO_DIRNAME));
try {
$content = $this->repository->setServer($server)->setToken($request->attributes->get('server_token'))->getContent($file);
} catch (RequestException $exception) {
throw new DaemonConnectionException($exception);
}
$this->setRequest($request)->injectJavascript(['stat' => $request->attributes->get('file_stats')]);
return view('server.files.edit', [
'file' => $file,
'stat' => $request->attributes->get('file_stats'),
'contents' => $content,
'directory' => (in_array($dirname, ['.', './', '/'])) ? '/' : trim($dirname, '/') . '/',
]);
}
}

View file

@ -1,105 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Server\Files;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use GuzzleHttp\Exception\RequestException;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
class RemoteRequestController extends Controller
{
/**
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface
*/
protected $repository;
/**
* RemoteRequestController constructor.
*
* @param \Illuminate\Contracts\Config\Repository $config
* @param \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface $repository
*/
public function __construct(ConfigRepository $config, FileRepositoryInterface $repository)
{
$this->config = $config;
$this->repository = $repository;
}
/**
* Return a listing of a servers file directory.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function directory(Request $request): View
{
$server = $request->attributes->get('server');
$this->authorize('list-files', $server);
$requestDirectory = '/' . trim(urldecode($request->input('directory', '/')), '/');
$directory = [
'header' => $requestDirectory !== '/' ? $requestDirectory : '',
'first' => $requestDirectory !== '/',
];
$goBack = explode('/', trim($requestDirectory, '/'));
if (! empty(array_filter($goBack)) && count($goBack) >= 2) {
array_pop($goBack);
$directory['show'] = true;
$directory['link'] = '/' . implode('/', $goBack);
$directory['link_show'] = implode('/', $goBack) . '/';
}
try {
$listing = $this->repository->setServer($server)->setToken($request->attributes->get('server_token'))->getDirectory($requestDirectory);
} catch (RequestException $exception) {
throw new DaemonConnectionException($exception, true);
}
return view('server.files.list', [
'files' => $listing['files'],
'folders' => $listing['folders'],
'editableMime' => $this->config->get('pterodactyl.files.editable'),
'directory' => $directory,
]);
}
/**
* Put the contents of a file onto the daemon.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/
public function store(Request $request): Response
{
$server = $request->attributes->get('server');
$this->authorize('save-files', $server);
try {
$this->repository->setServer($server)->setToken($request->attributes->get('server_token'))
->putContent($request->input('file'), $request->input('contents') ?? '');
return response('', 204);
} catch (RequestException $exception) {
throw new DaemonConnectionException($exception);
}
}
}

View file

@ -1,96 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Server\Settings;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Contracts\Extensions\HashidsInterface;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Services\Allocations\SetDefaultAllocationService;
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
use Pterodactyl\Exceptions\Service\Allocation\AllocationDoesNotBelongToServerException;
class AllocationController extends Controller
{
use JavascriptInjection;
/**
* @var \Pterodactyl\Services\Allocations\SetDefaultAllocationService
*/
private $defaultAllocationService;
/**
* @var \Pterodactyl\Contracts\Extensions\HashidsInterface
*/
private $hashids;
/**
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
*/
private $repository;
/**
* AllocationController constructor.
*
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository
* @param \Pterodactyl\Contracts\Extensions\HashidsInterface $hashids
* @param \Pterodactyl\Services\Allocations\SetDefaultAllocationService $defaultAllocationService
*/
public function __construct(
AllocationRepositoryInterface $repository,
HashidsInterface $hashids,
SetDefaultAllocationService $defaultAllocationService
) {
$this->defaultAllocationService = $defaultAllocationService;
$this->hashids = $hashids;
$this->repository = $repository;
}
/**
* Render the allocation management overview page for a server.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index(Request $request): View
{
$server = $request->attributes->get('server');
$this->authorize('view-allocations', $server);
$this->setRequest($request)->injectJavascript();
return view('server.settings.allocation', [
'allocations' => $this->repository->findWhere([['server_id', '=', $server->id]]),
]);
}
/**
* Update the default allocation for a server.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(Request $request): JsonResponse
{
$server = $request->attributes->get('server');
$this->authorize('edit-allocation', $server);
$allocation = $this->hashids->decodeFirst($request->input('allocation'), 0);
try {
$this->defaultAllocationService->handle($server->id, $allocation);
} catch (AllocationDoesNotBelongToServerException $exception) {
return response()->json(['error' => 'No matching allocation was located for this server.'], 404);
}
return response()->json();
}
}

View file

@ -1,59 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Server\Settings;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
use Pterodactyl\Http\Requests\Server\Settings\ChangeServerNameRequest;
class NameController extends Controller
{
use JavascriptInjection;
/**
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
*/
private $repository;
/**
* NameController constructor.
*
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
*/
public function __construct(ServerRepositoryInterface $repository)
{
$this->repository = $repository;
}
/**
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index(Request $request)
{
$this->authorize('view-name', $request->attributes->get('server'));
$this->setRequest($request)->injectJavascript();
return view('server.settings.name');
}
/**
* Update the stored name for a specific server.
*
* @param \Pterodactyl\Http\Requests\Server\Settings\ChangeServerNameRequest $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(ChangeServerNameRequest $request): RedirectResponse
{
$this->repository->update($request->getServer()->id, $request->validated());
return redirect()->route('server.settings.name', $request->getServer()->uuidShort);
}
}

View file

@ -1,29 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Server\Settings;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
class SftpController extends Controller
{
use JavascriptInjection;
/**
* Render the server SFTP settings page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index(Request $request): View
{
$this->authorize('access-sftp', $request->attributes->get('server'));
$this->setRequest($request)->injectJavascript();
return view('server.settings.sftp');
}
}

View file

@ -1,96 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Server\Settings;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Pterodactyl\Models\User;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Services\Servers\StartupCommandViewService;
use Pterodactyl\Services\Servers\StartupModificationService;
use Pterodactyl\Http\Requests\Server\UpdateStartupParametersFormRequest;
class StartupController extends Controller
{
use JavascriptInjection;
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
private $alert;
/**
* @var \Pterodactyl\Services\Servers\StartupCommandViewService
*/
private $commandViewService;
/**
* @var \Pterodactyl\Services\Servers\StartupModificationService
*/
private $modificationService;
/**
* StartupController constructor.
*
* @param \Prologue\Alerts\AlertsMessageBag $alert
* @param \Pterodactyl\Services\Servers\StartupCommandViewService $commandViewService
* @param \Pterodactyl\Services\Servers\StartupModificationService $modificationService
*/
public function __construct(
AlertsMessageBag $alert,
StartupCommandViewService $commandViewService,
StartupModificationService $modificationService
) {
$this->alert = $alert;
$this->commandViewService = $commandViewService;
$this->modificationService = $modificationService;
}
/**
* Render the server startup page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function index(Request $request): View
{
$server = $request->attributes->get('server');
$this->authorize('view-startup', $server);
$this->setRequest($request)->injectJavascript();
$data = $this->commandViewService->handle($server->id);
return view('server.settings.startup', [
'variables' => $data->get('variables'),
'server_values' => $data->get('server_values'),
'startup' => $data->get('startup'),
]);
}
/**
* Handle request to update the startup variables for a server. Authorization
* is handled in the form request.
*
* @param \Pterodactyl\Http\Requests\Server\UpdateStartupParametersFormRequest $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Illuminate\Validation\ValidationException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(UpdateStartupParametersFormRequest $request): RedirectResponse
{
$this->modificationService->setUserLevel(User::USER_LEVEL_USER);
$this->modificationService->handle($request->attributes->get('server'), $request->normalize());
$this->alert->success(trans('server.config.startup.edited'))->flash();
return redirect()->route('server.settings.startup', ['server' => $request->attributes->get('server')->uuidShort]);
}
}

View file

@ -1,197 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Server;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Pterodactyl\Models\Permission;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Subusers\SubuserUpdateService;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Services\Subusers\SubuserCreationService;
use Pterodactyl\Services\Subusers\SubuserDeletionService;
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
use Pterodactyl\Http\Requests\Server\Subuser\SubuserStoreFormRequest;
use Pterodactyl\Http\Requests\Server\Subuser\SubuserUpdateFormRequest;
class SubuserController extends Controller
{
use JavascriptInjection;
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
/**
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Services\Subusers\SubuserCreationService
*/
protected $subuserCreationService;
/**
* @var \Pterodactyl\Services\Subusers\SubuserDeletionService
*/
protected $subuserDeletionService;
/**
* @var \Pterodactyl\Services\Subusers\SubuserUpdateService
*/
protected $subuserUpdateService;
/**
* SubuserController constructor.
*
* @param \Prologue\Alerts\AlertsMessageBag $alert
* @param \Pterodactyl\Services\Subusers\SubuserCreationService $subuserCreationService
* @param \Pterodactyl\Services\Subusers\SubuserDeletionService $subuserDeletionService
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $repository
* @param \Pterodactyl\Services\Subusers\SubuserUpdateService $subuserUpdateService
*/
public function __construct(
AlertsMessageBag $alert,
SubuserCreationService $subuserCreationService,
SubuserDeletionService $subuserDeletionService,
SubuserRepositoryInterface $repository,
SubuserUpdateService $subuserUpdateService
) {
$this->alert = $alert;
$this->repository = $repository;
$this->subuserCreationService = $subuserCreationService;
$this->subuserDeletionService = $subuserDeletionService;
$this->subuserUpdateService = $subuserUpdateService;
}
/**
* Displays the subuser overview index.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index(Request $request): View
{
$server = $request->attributes->get('server');
$this->authorize('list-subusers', $server);
$this->setRequest($request)->injectJavascript();
return view('server.users.index', [
'subusers' => $this->repository->findWhere([['server_id', '=', $server->id]]),
]);
}
/**
* Displays a single subuser overview.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function view(Request $request): View
{
$server = $request->attributes->get('server');
$this->authorize('view-subuser', $server);
$subuser = $this->repository->getWithPermissions($request->attributes->get('subuser'));
$this->setRequest($request)->injectJavascript();
return view('server.users.view', [
'subuser' => $subuser,
'permlist' => Permission::getPermissions(),
'permissions' => $subuser->getRelation('permissions')->mapWithKeys(function ($item) {
return [$item->permission => true];
}),
]);
}
/**
* Handles editing a subuser.
*
* @param \Pterodactyl\Http\Requests\Server\Subuser\SubuserUpdateFormRequest $request
* @param string $uuid
* @param string $hash
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function update(SubuserUpdateFormRequest $request, string $uuid, string $hash): RedirectResponse
{
$this->subuserUpdateService->handle($request->attributes->get('subuser'), $request->input('permissions', []));
$this->alert->success(trans('server.users.user_updated'))->flash();
return redirect()->route('server.subusers.view', ['uuid' => $uuid, 'subuser' => $hash]);
}
/**
* Display new subuser creation page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create(Request $request): View
{
$server = $request->attributes->get('server');
$this->authorize('create-subuser', $server);
$this->setRequest($request)->injectJavascript();
return view('server.users.new', ['permissions' => Permission::getPermissions()]);
}
/**
* Handles creating a new subuser.
*
* @param \Pterodactyl\Http\Requests\Server\Subuser\SubuserStoreFormRequest $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException
* @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException
*/
public function store(SubuserStoreFormRequest $request): RedirectResponse
{
$server = $request->attributes->get('server');
$subuser = $this->subuserCreationService->handle($server, $request->input('email'), $request->input('permissions', []));
$this->alert->success(trans('server.users.user_assigned'))->flash();
return redirect()->route('server.subusers.view', [
'uuid' => $server->uuidShort,
'id' => $subuser->hashid,
]);
}
/**
* Handles deleting a subuser.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\DisplayException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function delete(Request $request): Response
{
$server = $request->attributes->get('server');
$this->authorize('delete-subuser', $server);
$this->subuserDeletionService->handle($request->attributes->get('subuser'));
return response('', 204);
}
}

View file

@ -1,70 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Server\Tasks;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Services\Schedules\ProcessScheduleService;
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
class ActionController extends Controller
{
private $processScheduleService;
/**
* @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface
*/
private $repository;
public function __construct(ProcessScheduleService $processScheduleService, ScheduleRepositoryInterface $repository)
{
$this->processScheduleService = $processScheduleService;
$this->repository = $repository;
}
/**
* Toggle a task to be active or inactive for a given server.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function toggle(Request $request): Response
{
$server = $request->attributes->get('server');
$schedule = $request->attributes->get('schedule');
$this->authorize('toggle-schedule', $server);
$this->repository->update($schedule->id, [
'is_active' => ! $schedule->is_active,
]);
return response('', 204);
}
/**
* Trigger a schedule to run now.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*
* @throws \Illuminate\Auth\Access\AuthorizationException
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function trigger(Request $request): Response
{
$server = $request->attributes->get('server');
$this->authorize('toggle-schedule', $server);
$this->processScheduleService->setRunTimeOverride(Carbon::now())->handle(
$request->attributes->get('schedule')
);
return response('', 204);
}
}

View file

@ -1,198 +0,0 @@
<?php
namespace Pterodactyl\Http\Controllers\Server\Tasks;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Http\RedirectResponse;
use Prologue\Alerts\AlertsMessageBag;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Contracts\Extensions\HashidsInterface;
use Pterodactyl\Traits\Controllers\JavascriptInjection;
use Pterodactyl\Services\Schedules\ScheduleUpdateService;
use Pterodactyl\Services\Schedules\ScheduleCreationService;
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
use Pterodactyl\Http\Requests\Server\ScheduleCreationFormRequest;
class TaskManagementController extends Controller
{
use JavascriptInjection;
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
/**
* @var \Pterodactyl\Services\Schedules\ScheduleCreationService
*/
protected $creationService;
/**
* @var \Pterodactyl\Contracts\Extensions\HashidsInterface
*/
protected $hashids;
/**
* @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Services\Schedules\ScheduleUpdateService
*/
private $updateService;
/**
* TaskManagementController constructor.
*
* @param \Prologue\Alerts\AlertsMessageBag $alert
* @param \Pterodactyl\Contracts\Extensions\HashidsInterface $hashids
* @param \Pterodactyl\Services\Schedules\ScheduleCreationService $creationService
* @param \Pterodactyl\Services\Schedules\ScheduleUpdateService $updateService
* @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository
*/
public function __construct(
AlertsMessageBag $alert,
HashidsInterface $hashids,
ScheduleCreationService $creationService,
ScheduleUpdateService $updateService,
ScheduleRepositoryInterface $repository
) {
$this->alert = $alert;
$this->creationService = $creationService;
$this->hashids = $hashids;
$this->repository = $repository;
$this->updateService = $updateService;
}
/**
* Display the task page listing.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function index(Request $request): View
{
$server = $request->attributes->get('server');
$this->authorize('list-schedules', $server);
$this->setRequest($request)->injectJavascript();
return view('server.schedules.index', [
'schedules' => $this->repository->findServerSchedules($server->id),
'actions' => [
'command' => trans('server.schedule.actions.command'),
'power' => trans('server.schedule.actions.power'),
],
]);
}
/**
* Display the task creation page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create(Request $request): View
{
$server = $request->attributes->get('server');
$this->authorize('create-schedule', $server);
$this->setRequest($request)->injectJavascript();
return view('server.schedules.new');
}
/**
* Handle request to store a new schedule and tasks in the database.
*
* @param \Pterodactyl\Http\Requests\Server\ScheduleCreationFormRequest $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException
*/
public function store(ScheduleCreationFormRequest $request): RedirectResponse
{
$server = $request->attributes->get('server');
$schedule = $this->creationService->handle($server, $request->normalize(), $request->getTasks());
$this->alert->success(trans('server.schedule.schedule_created'))->flash();
return redirect()->route('server.schedules.view', [
'server' => $server->uuidShort,
'schedule' => $schedule->hashid,
]);
}
/**
* Return a view to modify a schedule.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function view(Request $request): View
{
$server = $request->attributes->get('server');
$schedule = $request->attributes->get('schedule');
$this->authorize('view-schedule', $server);
$this->setRequest($request)->injectJavascript([
'tasks' => $schedule->getRelation('tasks')->map(function ($task) {
/* @var \Pterodactyl\Models\Task $task */
return collect($task->toArray())->only('action', 'time_offset', 'payload')->all();
}),
]);
return view('server.schedules.view', ['schedule' => $schedule]);
}
/**
* Update a specific parent task on the system.
*
* @param \Pterodactyl\Http\Requests\Server\ScheduleCreationFormRequest $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException
*/
public function update(ScheduleCreationFormRequest $request): RedirectResponse
{
$server = $request->attributes->get('server');
$schedule = $request->attributes->get('schedule');
$this->updateService->handle($schedule, $request->normalize(), $request->getTasks());
$this->alert->success(trans('server.schedule.schedule_updated'))->flash();
return redirect()->route('server.schedules.view', [
'server' => $server->uuidShort,
'schedule' => $schedule->hashid,
]);
}
/**
* Delete a parent task from the Panel.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function delete(Request $request): Response
{
$server = $request->attributes->get('server');
$schedule = $request->attributes->get('schedule');
$this->authorize('delete-schedule', $server);
$this->repository->delete($schedule->id);
return response('', 204);
}
}

View file

@ -33,7 +33,8 @@ class RouteServiceProvider extends ServiceProvider
->namespace($this->namespace . '\Auth') ->namespace($this->namespace . '\Auth')
->group(base_path('routes/auth.php')); ->group(base_path('routes/auth.php'));
Route::middleware(['web', 'csrf', 'auth', 'server', 'subuser.auth', 'node.maintenance'])->prefix('/server/{server}') Route::middleware(['web', 'csrf', 'auth', 'server', 'subuser.auth', 'node.maintenance'])
->prefix('/api/server/{server}')
->namespace($this->namespace . '\Server') ->namespace($this->namespace . '\Server')
->group(base_path('routes/server.php')); ->group(base_path('routes/server.php'));

View file

@ -84,33 +84,6 @@ class FileRepository extends BaseRepository implements FileRepositoryInterface
{ {
$response = $this->getHttpClient()->request('GET', sprintf('server/directory/%s', rawurlencode($path))); $response = $this->getHttpClient()->request('GET', sprintf('server/directory/%s', rawurlencode($path)));
$contents = json_decode($response->getBody()); return json_decode($response->getBody());
$files = $folders = [];
foreach ($contents as $value) {
if ($value->directory) {
array_push($folders, [
'entry' => $value->name,
'directory' => trim($path, '/'),
'size' => null,
'date' => strtotime($value->modified),
'mime' => $value->mime,
]);
} elseif ($value->file) {
array_push($files, [
'entry' => $value->name,
'directory' => trim($path, '/'),
'extension' => str_replace('\\', '/', pathinfo($value->name, PATHINFO_EXTENSION)),
'size' => human_readable($value->size),
'date' => strtotime($value->modified),
'mime' => $value->mime,
]);
}
}
return [
'files' => $files,
'folders' => $folders,
];
} }
} }

View file

@ -11,14 +11,14 @@
<div class="flex-1 text-right">Modified</div> <div class="flex-1 text-right">Modified</div>
<div class="flex-none w-1/6">Actions</div> <div class="flex-none w-1/6">Actions</div>
</div> </div>
<div class="row" v-for="folder in folders"> <div class="row clickable" v-for="directory in directories" v-on:click="currentDirectory = directory.name">
<div class="flex-none icon"><folder-icon/></div> <div class="flex-none icon"><folder-icon/></div>
<div class="flex-1">{{folder.name}}</div> <div class="flex-1">{{directory.name}}</div>
<div class="flex-1 text-right text-grey-dark"></div> <div class="flex-1 text-right text-grey-dark"></div>
<div class="flex-1 text-right text-grey-dark">{{formatDate(folder.modified)}}</div> <div class="flex-1 text-right text-grey-dark">{{formatDate(directory.modified)}}</div>
<div class="flex-none w-1/6"></div> <div class="flex-none w-1/6"></div>
</div> </div>
<div class="row" v-for="file in files"> <div class="row" v-for="file in files" :class="{ clickable: canEdit(file) }">
<div class="flex-none icon"> <div class="flex-none icon">
<file-text-icon v-if="!file.symlink"/> <file-text-icon v-if="!file.symlink"/>
<link2-icon v-else/> <link2-icon v-else/>
@ -46,13 +46,20 @@
...mapState('server', ['server', 'credentials']), ...mapState('server', ['server', 'credentials']),
}, },
watch: {
currentDirectory: function () {
this.listDirectory();
},
},
data: function () { data: function () {
return { return {
directory: '/', currentDirectory: '/',
loading: true, loading: true,
directories: [],
editableFiles: [],
files: [], files: [],
folders: [],
}; };
}, },
@ -65,20 +72,22 @@
* List the contents of a directory. * List the contents of a directory.
*/ */
listDirectory: function () { listDirectory: function () {
window.axios.get(`${this.credentials.node}/v1/server/directory/${this.directory}`, { this.loading = true;
headers: {
'X-Access-Server': this.server.uuid, window.axios.get(this.route('server.files', {
'X-Access-Token': this.credentials.key, server: this.$route.params.id,
} directory: this.currentDirectory,
}) }))
.then((response) => { .then((response) => {
this.files = filter(response.data, function (o) { this.files = filter(response.data.contents, function (o) {
return o.file; return o.file;
}); });
this.folders = filter(response.data, function (o) { this.directories = filter(response.data.contents, function (o) {
return o.directory; return o.directory;
}); });
this.editableFiles = response.data.editable;
}) })
.catch(console.error) .catch(console.error)
.finally(() => { .finally(() => {
@ -86,6 +95,16 @@
}); });
}, },
/**
* Determine if a file can be edited on the Panel.
*
* @param {Object} file
* @return {Boolean}
*/
canEdit: function (file) {
return this.editableFiles.indexOf(file.mime) >= 0;
},
/** /**
* Return the human readable filesize for a given number of bytes. This * Return the human readable filesize for a given number of bytes. This
* uses 1024 as the base, so the response is denoted accordingly. * uses 1024 as the base, so the response is denoted accordingly.

File diff suppressed because one or more lines are too long

View file

@ -8,15 +8,19 @@
} }
& > .row { & > .row {
@apply .flex .text-sm .py-3 .text-sm .rounded .cursor-pointer .border .border-transparent; @apply .flex .text-sm .py-3 .text-sm .border .border-transparent;
& > div { & > div {
@apply .pr-4; @apply .pr-4;
} }
&.clickable {
@apply .rounded .cursor-pointer;
&:hover { &:hover {
@apply .bg-grey-lightest .border-blue-light .text-blue-dark; @apply .bg-grey-lightest .border-blue-light .text-blue-dark;
} }
}
& > .icon { & > .icon {
@apply .w-8 .text-center; @apply .w-8 .text-center;

View file

@ -7,3 +7,9 @@
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
Route::get('/credentials', 'CredentialsController@index')->name('server.credentials'); Route::get('/credentials', 'CredentialsController@index')->name('server.credentials');
Route::group(['prefix' => '/files'], function () {
Route::get('/{directory?}', 'FileController@index')
->name('server.files')
->where('directory', '.*');
});