ui(admin): add egg exporting

This commit is contained in:
Matthew Penner 2021-10-30 14:23:29 -06:00
parent 469c0b40a3
commit 871d0bdd1c
No known key found for this signature in database
GPG key ID: BAB67850901908A8
7 changed files with 127 additions and 6 deletions

View file

@ -9,6 +9,7 @@ use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Spatie\QueryBuilder\QueryBuilder;
use Pterodactyl\Services\Eggs\Sharing\EggExporterService;
use Pterodactyl\Transformers\Api\Application\EggTransformer;
use Pterodactyl\Http\Requests\Api\Application\Eggs\GetEggRequest;
use Pterodactyl\Exceptions\Http\QueryValueOutOfRangeHttpException;
@ -16,10 +17,20 @@ use Pterodactyl\Http\Requests\Api\Application\Eggs\GetEggsRequest;
use Pterodactyl\Http\Requests\Api\Application\Eggs\StoreEggRequest;
use Pterodactyl\Http\Requests\Api\Application\Eggs\DeleteEggRequest;
use Pterodactyl\Http\Requests\Api\Application\Eggs\UpdateEggRequest;
use Pterodactyl\Http\Requests\Api\Application\Eggs\ExportEggRequest;
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
class EggController extends ApplicationApiController
{
private EggExporterService $eggExporterService;
public function __construct(EggExporterService $eggExporterService)
{
parent::__construct();
$this->eggExporterService = $eggExporterService;
}
/**
* Return an array of all eggs on a given nest.
*/
@ -71,7 +82,7 @@ class EggController extends ApplicationApiController
return $this->fractal->item($egg)
->transformWith(EggTransformer::class)
->respond(JsonResponse::HTTP_CREATED);
->respond(Response::HTTP_CREATED);
}
/**
@ -97,4 +108,14 @@ class EggController extends ApplicationApiController
return $this->returnNoContent();
}
/**
* Exports an egg.
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function export(ExportEggRequest $request, int $eggId): JsonResponse
{
return new JsonResponse($this->eggExporterService->handle($eggId));
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Pterodactyl\Http\Requests\Api\Application\Eggs;
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
class ExportEggRequest extends ApplicationApiRequest
{
}

View file

@ -27,11 +27,11 @@ class EggExporterService
*
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
public function handle(int $egg): string
public function handle(int $egg): array
{
$egg = $this->repository->getWithExportAttributes($egg);
$struct = [
return [
'_comment' => 'DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO',
'meta' => [
'version' => 'PTDL_v1',
@ -65,7 +65,5 @@ class EggExporterService
->toArray();
}),
];
return json_encode($struct, JSON_PRETTY_PRINT);
}
}

View file

@ -68,3 +68,8 @@ export const searchEggs = async (nestId: number, params: QueryBuilderParams<'nam
return data.data.map(AdminTransformers.toEgg);
};
export const exportEgg = async (eggId: number): Promise<Record<string, any>> => {
const { data } = await http.get(`/api/application/eggs/${eggId}/export`);
return data;
};

View file

@ -0,0 +1,85 @@
import { exportEgg } from '@/api/admin/egg';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
import useFlash from '@/plugins/useFlash';
import { jsonLanguage } from '@codemirror/lang-json';
import Editor from '@/components/elements/Editor';
import React, { useEffect, useState } from 'react';
import Button from '@/components/elements/Button';
import Modal from '@/components/elements/Modal';
import FlashMessageRender from '@/components/FlashMessageRender';
import { useRouteMatch } from 'react-router-dom';
import tw from 'twin.macro';
export default ({ className }: { className?: string }) => {
const { params: { id: eggId } } = useRouteMatch<{ id: string }>();
const { clearAndAddHttpError, clearFlashes } = useFlash();
const [ visible, setVisible ] = useState<boolean>(false);
const [ loading, setLoading ] = useState<boolean>(true);
const [ content, setContent ] = useState<Record<string, any> | null>(null);
useEffect(() => {
if (!visible) {
return;
}
clearFlashes('egg:export');
setLoading(true);
exportEgg(Number(eggId))
.then(setContent)
.catch(error => clearAndAddHttpError({ key: 'egg:export', error }))
.then(() => setLoading(false));
}, [ visible ]);
return (
<>
<Modal
visible={visible}
onDismissed={() => {
setVisible(false);
}}
css={tw`relative`}
>
<SpinnerOverlay visible={loading}/>
<h2 css={tw`mb-6 text-2xl text-neutral-100`}>Export Egg</h2>
<FlashMessageRender byKey={'egg:export'} css={tw`mb-6`}/>
<Editor
overrides={tw`h-[32rem] rounded`}
initialContent={content !== null ? JSON.stringify(content, null, '\t') : ''}
mode={jsonLanguage}
/>
<div css={tw`flex flex-wrap justify-end mt-4 sm:mt-6`}>
<Button
type={'button'}
css={tw`w-full sm:w-auto sm:mr-2`}
onClick={() => setVisible(false)}
isSecondary
>
Close
</Button>
<Button
css={tw`w-full sm:w-auto mt-4 sm:mt-0`}
// onClick={submit}
// TODO: When clicked, save as a JSON file.
>
Save
</Button>
</div>
</Modal>
<Button
type={'button'}
size={'small'}
css={tw`px-4 py-0 whitespace-nowrap`}
className={className}
onClick={() => setVisible(true)}
isSecondary
>
Export
</Button>
</>
);
};

View file

@ -1,5 +1,6 @@
import updateEgg from '@/api/admin/eggs/updateEgg';
import EggDeleteButton from '@/components/admin/nests/eggs/EggDeleteButton';
import EggExportButton from '@/components/admin/nests/eggs/EggExportButton';
import Button from '@/components/elements/Button';
import Editor from '@/components/elements/Editor';
import Field, { TextareaField } from '@/components/elements/Field';
@ -257,7 +258,8 @@ export default function EggSettingsContainer ({ egg }: { egg: Egg }) {
eggId={egg.id}
onDeleted={() => history.push('/admin/nests')}
/>
<Button type="submit" size="small" css={tw`ml-auto`} disabled={isSubmitting || !isValid}>
<EggExportButton css={tw`ml-auto mr-4`}/>
<Button type="submit" size="small" disabled={isSubmitting || !isValid}>
Save Changes
</Button>
</div>

View file

@ -33,6 +33,7 @@ Route::group(['prefix' => '/databases'], function () {
*/
Route::group(['prefix' => '/eggs'], function () {
Route::get('/{egg}', [\Pterodactyl\Http\Controllers\Api\Application\Eggs\EggController::class, 'view']);
Route::get('/{egg}/export', [\Pterodactyl\Http\Controllers\Api\Application\Eggs\EggController::class, 'export']);
Route::post('/', [\Pterodactyl\Http\Controllers\Api\Application\Eggs\EggController::class, 'store']);
Route::post('/{egg}/variables', [\Pterodactyl\Http\Controllers\Api\Application\Eggs\EggVariableController::class, 'store']);