Switch file manager listing to use panel API endpoint

This commit is contained in:
Dane Everitt 2019-05-01 20:54:40 -07:00
parent e87c5f6657
commit c80c8564b8
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
8 changed files with 156 additions and 8 deletions

View file

@ -6,8 +6,10 @@ use Carbon\Carbon;
use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Uuid;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Contracts\Cache\Repository; use Illuminate\Contracts\Cache\Repository as CacheRepository;
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\ListFilesRequest;
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DownloadFileRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DownloadFileRequest;
class FileController extends ClientApiController class FileController extends ClientApiController
@ -17,16 +19,38 @@ class FileController extends ClientApiController
*/ */
private $cache; private $cache;
/**
* @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface
*/
private $fileRepository;
/** /**
* FileController constructor. * FileController constructor.
* *
* @param \Illuminate\Contracts\Cache\Repository $cache * @param \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface $fileRepository
* @param \Illuminate\Contracts\Cache\Repository $cache
*/ */
public function __construct(Repository $cache) public function __construct(FileRepositoryInterface $fileRepository, CacheRepository $cache)
{ {
parent::__construct(); parent::__construct();
$this->cache = $cache; $this->cache = $cache;
$this->fileRepository = $fileRepository;
}
/**
* Returns a listing of files in a given directory.
*
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\ListFilesRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function listDirectory(ListFilesRequest $request): JsonResponse
{
return JsonResponse::create([
'contents' => $this->fileRepository->setServer($request->getModel(Server::class))->getDirectory(
$request->get('directory') ?? '/'
),
]);
} }
/** /**

View file

@ -0,0 +1,20 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
use Pterodactyl\Models\Server;
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
class ListFilesRequest extends ClientApiRequest
{
/**
* Check that the user making this request to the API is authorized to list all
* of the files that exist for a given server.
*
* @return bool
*/
public function authorize(): bool
{
return $this->user()->can('list-files', $this->getModel(Server::class));
}
}

View file

@ -3,7 +3,7 @@
namespace Pterodactyl\Providers; namespace Pterodactyl\Providers;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Pterodactyl\Repositories\Daemon\FileRepository; use Pterodactyl\Repositories\Wings\FileRepository;
use Pterodactyl\Repositories\Daemon\PowerRepository; use Pterodactyl\Repositories\Daemon\PowerRepository;
use Pterodactyl\Repositories\Eloquent\EggRepository; use Pterodactyl\Repositories\Eloquent\EggRepository;
use Pterodactyl\Repositories\Eloquent\NestRepository; use Pterodactyl\Repositories\Eloquent\NestRepository;

View file

@ -0,0 +1,33 @@
<?php
namespace Pterodactyl\Repositories\Wings;
use GuzzleHttp\Client;
use Pterodactyl\Repositories\Daemon\BaseRepository;
use Pterodactyl\Contracts\Repository\Daemon\BaseRepositoryInterface;
abstract class BaseWingsRepository extends BaseRepository implements BaseRepositoryInterface
{
/**
* Return an instance of the Guzzle HTTP Client to be used for requests.
*
* @param array $headers
* @return \GuzzleHttp\Client
*/
public function getHttpClient(array $headers = []): Client
{
// We're just going to extend the parent client here since that logic is already quite
// sound and does everything we need it to aside from provide the correct base URL
// and authentication headers.
$client = parent::getHttpClient($headers);
return new Client(array_merge($client->getConfig(), [
'base_uri' => $this->getNode()->getConnectionAddress(),
'headers' => [
'Authorization' => 'Bearer ' . ($this->getToken() ?? $this->getNode()->daemonSecret),
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
]));
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace Pterodactyl\Repositories\Wings;
use stdClass;
use Psr\Http\Message\ResponseInterface;
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
class FileRepository extends BaseWingsRepository implements FileRepositoryInterface
{
/**
* Return stat information for a given file.
*
* @param string $path
* @return \stdClass
*
* @throws \GuzzleHttp\Exception\TransferException
*/
public function getFileStat(string $path): stdClass
{
// TODO: Implement getFileStat() method.
}
/**
* Return the contents of a given file if it can be edited in the Panel.
*
* @param string $path
* @return string
*
* @throws \GuzzleHttp\Exception\TransferException
*/
public function getContent(string $path): string
{
// TODO: Implement getContent() method.
}
/**
* Save new contents to a given file.
*
* @param string $path
* @param string $content
* @return \Psr\Http\Message\ResponseInterface
*
* @throws \GuzzleHttp\Exception\TransferException
*/
public function putContent(string $path, string $content): ResponseInterface
{
// TODO: Implement putContent() method.
}
/**
* Return a directory listing for a given path.
*
* @param string $path
* @return array
*
* @throws \GuzzleHttp\Exception\TransferException
*/
public function getDirectory(string $path): array
{
$response = $this->getHttpClient()->get(
// Reason for the path check is because it is unnecessary on the Daemon but we need
// to respect the interface.
sprintf('/api/servers/%s/files/list/%s', $this->getServer()->uuid, $path === '/' ? '' : $path)
);
return json_decode($response->getBody(), true);
}
}

View file

@ -1,7 +1,5 @@
import http from '../http'; import http from '../http';
import {filter, isObject} from 'lodash'; import {filter, isObject} from 'lodash';
// @ts-ignore
import route from '../../../../../vendor/tightenco/ziggy/src/js/route';
import {DirectoryContentObject, DirectoryContents} from "./types"; import {DirectoryContentObject, DirectoryContents} from "./types";
/** /**
@ -9,7 +7,9 @@ import {DirectoryContentObject, DirectoryContents} from "./types";
*/ */
export function getDirectoryContents(server: string, directory: string): Promise<DirectoryContents> { export function getDirectoryContents(server: string, directory: string): Promise<DirectoryContents> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
http.get(route('server.files', {server, directory})) http.get(`/api/client/servers/${server}/files/list`, {
params: {directory}
})
.then((response) => { .then((response) => {
return resolve({ return resolve({
files: filter(response.data.contents, function (o: DirectoryContentObject) { files: filter(response.data.contents, function (o: DirectoryContentObject) {

View file

@ -8,7 +8,7 @@ export type DirectoryContentObject = {
name: string, name: string,
created: string, created: string,
modified: string, modified: string,
mode: number, mode: string,
size: number, size: number,
directory: boolean, directory: boolean,
file: boolean, file: boolean,

View file

@ -42,6 +42,8 @@ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateServ
}); });
Route::group(['prefix' => '/files'], function () { Route::group(['prefix' => '/files'], function () {
Route::get('/list', 'Servers\FileController@listDirectory')->name('api.client.servers.files.list');
Route::post('/download/{file}', 'Servers\FileController@download') Route::post('/download/{file}', 'Servers\FileController@download')
->where('file', '.*') ->where('file', '.*')
->name('api.client.servers.files.download'); ->name('api.client.servers.files.download');