diff --git a/app/Exceptions/Service/Egg/BadJsonFormatException.php b/app/Exceptions/Service/Egg/BadJsonFormatException.php new file mode 100644 index 000000000..0246f031c --- /dev/null +++ b/app/Exceptions/Service/Egg/BadJsonFormatException.php @@ -0,0 +1,9 @@ +alert = $alert; $this->exporterService = $exporterService; $this->importerService = $importerService; + $this->updateImporterService = $updateImporterService; } /** @@ -76,7 +85,8 @@ class EggShareController extends Controller * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException + * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException */ public function import(EggImportFormRequest $request): RedirectResponse { @@ -85,4 +95,24 @@ class EggShareController extends Controller return redirect()->route('admin.nests.egg.view', ['egg' => $egg->id]); } + + /** + * Update an existing Egg using a new imported file. + * + * @param \Pterodactyl\Http\Requests\Admin\Egg\EggImportFormRequest $request + * @param int $egg + * @return \Illuminate\Http\RedirectResponse + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException + */ + public function update(EggImportFormRequest $request, int $egg): RedirectResponse + { + $this->updateImporterService->handle($egg, $request->file('import_file')); + $this->alert->success(trans('admin/nests.eggs.notices.updated_via_import'))->flash(); + + return redirect()->route('admin.nests.egg.view', ['egg' => $egg]); + } } diff --git a/app/Http/Controllers/Admin/PackController.php b/app/Http/Controllers/Admin/PackController.php index 742f1ed0a..ae6c86fda 100644 --- a/app/Http/Controllers/Admin/PackController.php +++ b/app/Http/Controllers/Admin/PackController.php @@ -156,7 +156,7 @@ class PackController extends Controller * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException * @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException * @throws \Pterodactyl\Exceptions\Service\Pack\ZipExtractionException diff --git a/app/Http/Requests/Admin/Egg/EggImportFormRequest.php b/app/Http/Requests/Admin/Egg/EggImportFormRequest.php index 776c291e2..b6adb768e 100644 --- a/app/Http/Requests/Admin/Egg/EggImportFormRequest.php +++ b/app/Http/Requests/Admin/Egg/EggImportFormRequest.php @@ -18,9 +18,14 @@ class EggImportFormRequest extends AdminFormRequest */ public function rules() { - return [ + $rules = [ 'import_file' => 'bail|required|file|max:1000|mimetypes:application/json,text/plain', - 'import_to_nest' => 'bail|required|integer|exists:nests,id', ]; + + if ($this->method() !== 'PUT') { + $rules['import_to_nest'] = 'bail|required|integer|exists:nests,id'; + } + + return $rules; } } diff --git a/app/Services/Eggs/Sharing/EggImporterService.php b/app/Services/Eggs/Sharing/EggImporterService.php index 3155eefe8..7143bbc6e 100644 --- a/app/Services/Eggs/Sharing/EggImporterService.php +++ b/app/Services/Eggs/Sharing/EggImporterService.php @@ -15,7 +15,8 @@ use Illuminate\Http\UploadedFile; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Contracts\Repository\EggRepositoryInterface; use Pterodactyl\Contracts\Repository\NestRepositoryInterface; -use Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException; +use Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException; +use Pterodactyl\Exceptions\Service\InvalidFileUploadException; use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; class EggImporterService @@ -69,7 +70,8 @@ class EggImporterService * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException + * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException */ public function handle(UploadedFile $file, int $nest): Egg { @@ -78,6 +80,11 @@ class EggImporterService } $parsed = json_decode($file->openFile()->fread($file->getSize())); + if (json_last_error() !== 0) { + throw new BadJsonFormatException(trans('exceptions.nest.importer.json_error', [ + 'error' => json_last_error_msg(), + ])); + } if (object_get($parsed, 'meta.version') !== 'PTDL_v1') { throw new InvalidFileUploadException(trans('exceptions.nest.importer.invalid_json_provided')); diff --git a/app/Services/Eggs/Sharing/EggUpdateImporterService.php b/app/Services/Eggs/Sharing/EggUpdateImporterService.php new file mode 100644 index 000000000..41bc29f06 --- /dev/null +++ b/app/Services/Eggs/Sharing/EggUpdateImporterService.php @@ -0,0 +1,113 @@ +connection = $connection; + $this->repository = $repository; + $this->variableRepository = $variableRepository; + } + + /** + * Update an existing Egg using an uploaded JSON file. + * + * @param int $egg + * @param \Illuminate\Http\UploadedFile $file + * + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException + */ + public function handle(int $egg, UploadedFile $file) + { + if (! $file->isValid() || ! $file->isFile()) { + throw new InvalidFileUploadException(trans('exceptions.nest.importer.file_error')); + } + + $parsed = json_decode($file->openFile()->fread($file->getSize())); + if (json_last_error() !== 0) { + throw new BadJsonFormatException(trans('exceptions.nest.importer.json_error', [ + 'error' => json_last_error_msg(), + ])); + } + + if (object_get($parsed, 'meta.version') !== 'PTDL_v1') { + throw new InvalidFileUploadException(trans('exceptions.nest.importer.invalid_json_provided')); + } + + $this->connection->beginTransaction(); + $this->repository->update($egg, [ + 'author' => object_get($parsed, 'author'), + 'name' => object_get($parsed, 'name'), + 'description' => object_get($parsed, 'description'), + 'docker_image' => object_get($parsed, 'image'), + 'config_files' => object_get($parsed, 'config.files'), + 'config_startup' => object_get($parsed, 'config.startup'), + 'config_logs' => object_get($parsed, 'config.logs'), + 'config_stop' => object_get($parsed, 'config.stop'), + 'startup' => object_get($parsed, 'startup'), + 'script_install' => object_get($parsed, 'scripts.installation.script'), + 'script_entry' => object_get($parsed, 'scripts.installation.entrypoint'), + 'script_container' => object_get($parsed, 'scripts.installation.container'), + ], true, true); + + // Update Existing Variables + collect($parsed->variables)->each(function ($variable) use ($egg) { + $this->variableRepository->withoutFresh()->updateOrCreate([ + 'egg_id' => $egg, + 'env_variable' => $variable->env_variable, + ], collect($variable)->except(['egg_id', 'env_variable'])->toArray()); + }); + + $imported = collect($parsed->variables)->pluck('env_variable')->toArray(); + $existing = $this->variableRepository->withColumns(['id', 'env_variable'])->findWhere([['egg_id', '=', $egg]]); + + // Delete variables not present in the import. + collect($existing)->each(function ($variable) use ($egg, $imported) { + if (! in_array($variable->env_variable, $imported)) { + $this->variableRepository->deleteWhere([ + ['egg_id', '=', $egg], + ['env_variable', '=', $variable->env_variable], + ]); + } + }); + + $this->connection->commit(); + } +} diff --git a/app/Services/Packs/PackCreationService.php b/app/Services/Packs/PackCreationService.php index ff40f072a..80b452d88 100644 --- a/app/Services/Packs/PackCreationService.php +++ b/app/Services/Packs/PackCreationService.php @@ -13,8 +13,8 @@ use Ramsey\Uuid\Uuid; use Illuminate\Http\UploadedFile; use Illuminate\Database\ConnectionInterface; use Pterodactyl\Contracts\Repository\PackRepositoryInterface; +use Pterodactyl\Exceptions\Service\InvalidFileUploadException; use Illuminate\Contracts\Filesystem\Factory as FilesystemFactory; -use Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException; use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException; class PackCreationService @@ -65,7 +65,7 @@ class PackCreationService * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException */ public function handle(array $data, UploadedFile $file = null) { diff --git a/app/Services/Packs/TemplateUploadService.php b/app/Services/Packs/TemplateUploadService.php index 045bfec48..6495edd68 100644 --- a/app/Services/Packs/TemplateUploadService.php +++ b/app/Services/Packs/TemplateUploadService.php @@ -11,8 +11,8 @@ namespace Pterodactyl\Services\Packs; use ZipArchive; use Illuminate\Http\UploadedFile; +use Pterodactyl\Exceptions\Service\InvalidFileUploadException; use Pterodactyl\Exceptions\Service\Pack\ZipExtractionException; -use Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException; use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException; use Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException; use Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException; @@ -58,7 +58,7 @@ class TemplateUploadService * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Service\Pack\ZipExtractionException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException * @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException @@ -94,7 +94,7 @@ class TemplateUploadService * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Service\Pack\ZipExtractionException - * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException + * @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException * @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException * @throws \Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException diff --git a/resources/lang/en/admin/nests.php b/resources/lang/en/admin/nests.php index b78af4903..19e6b5a1e 100644 --- a/resources/lang/en/admin/nests.php +++ b/resources/lang/en/admin/nests.php @@ -16,6 +16,7 @@ return [ 'eggs' => [ 'notices' => [ 'imported' => 'Successfully imported this Egg and its associated variables.', + 'updated_via_import' => 'This Egg has been updated using the file provided.', 'deleted' => 'Successfully deleted the requested egg from the Panel.', 'updated' => 'Egg configuration has been updated successfully.', 'script_updated' => 'Egg install script has been updated and will run whenever servers are installed.', diff --git a/resources/lang/en/exceptions.php b/resources/lang/en/exceptions.php index c700b62ff..e5bee177d 100644 --- a/resources/lang/en/exceptions.php +++ b/resources/lang/en/exceptions.php @@ -31,6 +31,7 @@ return [ 'reserved_name' => 'The environment variable :name is protected and cannot be assigned to a variable.', ], 'importer' => [ + 'json_error' => 'There was an error while attempting to parse the JSON file: :error.', 'file_error' => 'The JSON file provided was not valid.', 'invalid_json_provided' => 'The JSON file provided is not in a format that can be recognized.', ], diff --git a/resources/themes/pterodactyl/admin/eggs/view.blade.php b/resources/themes/pterodactyl/admin/eggs/view.blade.php index 215273005..e5b615c6f 100644 --- a/resources/themes/pterodactyl/admin/eggs/view.blade.php +++ b/resources/themes/pterodactyl/admin/eggs/view.blade.php @@ -30,14 +30,39 @@ +