From 871d0bdd1cde03ddf5cb2bc0325e578e5678093b Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sat, 30 Oct 2021 14:23:29 -0600 Subject: [PATCH] ui(admin): add egg exporting --- .../Api/Application/Eggs/EggController.php | 23 ++++- .../Api/Application/Eggs/ExportEggRequest.php | 9 ++ .../Eggs/Sharing/EggExporterService.php | 6 +- resources/scripts/api/admin/egg.ts | 5 ++ .../admin/nests/eggs/EggExportButton.tsx | 85 +++++++++++++++++++ .../admin/nests/eggs/EggSettingsContainer.tsx | 4 +- routes/api-application.php | 1 + 7 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 app/Http/Requests/Api/Application/Eggs/ExportEggRequest.php create mode 100644 resources/scripts/components/admin/nests/eggs/EggExportButton.tsx diff --git a/app/Http/Controllers/Api/Application/Eggs/EggController.php b/app/Http/Controllers/Api/Application/Eggs/EggController.php index e02e6ca14..34ad80a15 100644 --- a/app/Http/Controllers/Api/Application/Eggs/EggController.php +++ b/app/Http/Controllers/Api/Application/Eggs/EggController.php @@ -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)); + } } diff --git a/app/Http/Requests/Api/Application/Eggs/ExportEggRequest.php b/app/Http/Requests/Api/Application/Eggs/ExportEggRequest.php new file mode 100644 index 000000000..63893df54 --- /dev/null +++ b/app/Http/Requests/Api/Application/Eggs/ExportEggRequest.php @@ -0,0 +1,9 @@ +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); } } diff --git a/resources/scripts/api/admin/egg.ts b/resources/scripts/api/admin/egg.ts index fb7e52ed8..ec6c03e1d 100644 --- a/resources/scripts/api/admin/egg.ts +++ b/resources/scripts/api/admin/egg.ts @@ -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> => { + const { data } = await http.get(`/api/application/eggs/${eggId}/export`); + return data; +}; diff --git a/resources/scripts/components/admin/nests/eggs/EggExportButton.tsx b/resources/scripts/components/admin/nests/eggs/EggExportButton.tsx new file mode 100644 index 000000000..0a9442f05 --- /dev/null +++ b/resources/scripts/components/admin/nests/eggs/EggExportButton.tsx @@ -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(false); + const [ loading, setLoading ] = useState(true); + const [ content, setContent ] = useState | 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 ( + <> + { + setVisible(false); + }} + css={tw`relative`} + > + +

Export Egg

+ + + + +
+ + +
+
+ + + + ); +}; diff --git a/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx b/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx index dc2db3419..e411de441 100644 --- a/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx +++ b/resources/scripts/components/admin/nests/eggs/EggSettingsContainer.tsx @@ -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')} /> - diff --git a/routes/api-application.php b/routes/api-application.php index b58eddd8e..dceb9a4cc 100644 --- a/routes/api-application.php +++ b/routes/api-application.php @@ -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']);