Add ability to update an Egg by re-uploading a file.
This commit is contained in:
parent
e2cb789b2b
commit
e01d7497f5
17 changed files with 449 additions and 23 deletions
9
app/Exceptions/Service/Egg/BadJsonFormatException.php
Normal file
9
app/Exceptions/Service/Egg/BadJsonFormatException.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pterodactyl\Exceptions\Service\Egg;
|
||||||
|
|
||||||
|
use Pterodactyl\Exceptions\DisplayException;
|
||||||
|
|
||||||
|
class BadJsonFormatException extends DisplayException
|
||||||
|
{
|
||||||
|
}
|
|
@ -7,7 +7,7 @@
|
||||||
* https://opensource.org/licenses/MIT
|
* https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Pterodactyl\Exceptions\Service\Pack;
|
namespace Pterodactyl\Exceptions\Service;
|
||||||
|
|
||||||
use Pterodactyl\Exceptions\DisplayException;
|
use Pterodactyl\Exceptions\DisplayException;
|
||||||
|
|
|
@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||||
use Pterodactyl\Services\Eggs\Sharing\EggExporterService;
|
use Pterodactyl\Services\Eggs\Sharing\EggExporterService;
|
||||||
use Pterodactyl\Services\Eggs\Sharing\EggImporterService;
|
use Pterodactyl\Services\Eggs\Sharing\EggImporterService;
|
||||||
use Pterodactyl\Http\Requests\Admin\Egg\EggImportFormRequest;
|
use Pterodactyl\Http\Requests\Admin\Egg\EggImportFormRequest;
|
||||||
|
use Pterodactyl\Services\Eggs\Sharing\EggUpdateImporterService;
|
||||||
|
|
||||||
class EggShareController extends Controller
|
class EggShareController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -35,21 +36,29 @@ class EggShareController extends Controller
|
||||||
*/
|
*/
|
||||||
protected $importerService;
|
protected $importerService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Services\Eggs\Sharing\EggUpdateImporterService
|
||||||
|
*/
|
||||||
|
protected $updateImporterService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OptionShareController constructor.
|
* OptionShareController constructor.
|
||||||
*
|
*
|
||||||
* @param \Prologue\Alerts\AlertsMessageBag $alert
|
* @param \Prologue\Alerts\AlertsMessageBag $alert
|
||||||
* @param \Pterodactyl\Services\Eggs\Sharing\EggExporterService $exporterService
|
* @param \Pterodactyl\Services\Eggs\Sharing\EggExporterService $exporterService
|
||||||
* @param \Pterodactyl\Services\Eggs\Sharing\EggImporterService $importerService
|
* @param \Pterodactyl\Services\Eggs\Sharing\EggImporterService $importerService
|
||||||
|
* @param \Pterodactyl\Services\Eggs\Sharing\EggUpdateImporterService $updateImporterService
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
AlertsMessageBag $alert,
|
AlertsMessageBag $alert,
|
||||||
EggExporterService $exporterService,
|
EggExporterService $exporterService,
|
||||||
EggImporterService $importerService
|
EggImporterService $importerService,
|
||||||
|
EggUpdateImporterService $updateImporterService
|
||||||
) {
|
) {
|
||||||
$this->alert = $alert;
|
$this->alert = $alert;
|
||||||
$this->exporterService = $exporterService;
|
$this->exporterService = $exporterService;
|
||||||
$this->importerService = $importerService;
|
$this->importerService = $importerService;
|
||||||
|
$this->updateImporterService = $updateImporterService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,7 +85,8 @@ class EggShareController extends Controller
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
* @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
|
public function import(EggImportFormRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
|
@ -85,4 +95,24 @@ class EggShareController extends Controller
|
||||||
|
|
||||||
return redirect()->route('admin.nests.egg.view', ['egg' => $egg->id]);
|
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ class PackController extends Controller
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException
|
* @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\InvalidPackArchiveFormatException
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException
|
* @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Pack\ZipExtractionException
|
* @throws \Pterodactyl\Exceptions\Service\Pack\ZipExtractionException
|
||||||
|
|
|
@ -18,9 +18,14 @@ class EggImportFormRequest extends AdminFormRequest
|
||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
return [
|
$rules = [
|
||||||
'import_file' => 'bail|required|file|max:1000|mimetypes:application/json,text/plain',
|
'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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,8 @@ use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
|
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;
|
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
|
||||||
|
|
||||||
class EggImporterService
|
class EggImporterService
|
||||||
|
@ -69,7 +70,8 @@ class EggImporterService
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
* @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
|
public function handle(UploadedFile $file, int $nest): Egg
|
||||||
{
|
{
|
||||||
|
@ -78,6 +80,11 @@ class EggImporterService
|
||||||
}
|
}
|
||||||
|
|
||||||
$parsed = json_decode($file->openFile()->fread($file->getSize()));
|
$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') {
|
if (object_get($parsed, 'meta.version') !== 'PTDL_v1') {
|
||||||
throw new InvalidFileUploadException(trans('exceptions.nest.importer.invalid_json_provided'));
|
throw new InvalidFileUploadException(trans('exceptions.nest.importer.invalid_json_provided'));
|
||||||
|
|
113
app/Services/Eggs/Sharing/EggUpdateImporterService.php
Normal file
113
app/Services/Eggs/Sharing/EggUpdateImporterService.php
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pterodactyl\Services\Eggs\Sharing;
|
||||||
|
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Database\ConnectionInterface;
|
||||||
|
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
||||||
|
use Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException;
|
||||||
|
use Pterodactyl\Exceptions\Service\InvalidFileUploadException;
|
||||||
|
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
|
||||||
|
|
||||||
|
class EggUpdateImporterService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Database\ConnectionInterface
|
||||||
|
*/
|
||||||
|
protected $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $variableRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EggUpdateImporterService constructor.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||||
|
* @param \Pterodactyl\Contracts\Repository\EggRepositoryInterface $repository
|
||||||
|
* @param \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface $variableRepository
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
ConnectionInterface $connection,
|
||||||
|
EggRepositoryInterface $repository,
|
||||||
|
EggVariableRepositoryInterface $variableRepository
|
||||||
|
) {
|
||||||
|
$this->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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,8 +13,8 @@ use Ramsey\Uuid\Uuid;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Pterodactyl\Contracts\Repository\PackRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\PackRepositoryInterface;
|
||||||
|
use Pterodactyl\Exceptions\Service\InvalidFileUploadException;
|
||||||
use Illuminate\Contracts\Filesystem\Factory as FilesystemFactory;
|
use Illuminate\Contracts\Filesystem\Factory as FilesystemFactory;
|
||||||
use Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException;
|
|
||||||
use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException;
|
use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException;
|
||||||
|
|
||||||
class PackCreationService
|
class PackCreationService
|
||||||
|
@ -65,7 +65,7 @@ class PackCreationService
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException
|
* @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)
|
public function handle(array $data, UploadedFile $file = null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,8 +11,8 @@ namespace Pterodactyl\Services\Packs;
|
||||||
|
|
||||||
use ZipArchive;
|
use ZipArchive;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Pterodactyl\Exceptions\Service\InvalidFileUploadException;
|
||||||
use Pterodactyl\Exceptions\Service\Pack\ZipExtractionException;
|
use Pterodactyl\Exceptions\Service\Pack\ZipExtractionException;
|
||||||
use Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException;
|
|
||||||
use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException;
|
use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException;
|
||||||
use Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException;
|
use Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException;
|
||||||
use Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException;
|
use Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException;
|
||||||
|
@ -58,7 +58,7 @@ class TemplateUploadService
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Pack\ZipExtractionException
|
* @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\InvalidFileMimeTypeException
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException
|
* @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException
|
* @throws \Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException
|
||||||
|
@ -94,7 +94,7 @@ class TemplateUploadService
|
||||||
*
|
*
|
||||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Pack\ZipExtractionException
|
* @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\InvalidFileMimeTypeException
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException
|
* @throws \Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException
|
||||||
* @throws \Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException
|
* @throws \Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException
|
||||||
|
|
|
@ -16,6 +16,7 @@ return [
|
||||||
'eggs' => [
|
'eggs' => [
|
||||||
'notices' => [
|
'notices' => [
|
||||||
'imported' => 'Successfully imported this Egg and its associated variables.',
|
'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.',
|
'deleted' => 'Successfully deleted the requested egg from the Panel.',
|
||||||
'updated' => 'Egg configuration has been updated successfully.',
|
'updated' => 'Egg configuration has been updated successfully.',
|
||||||
'script_updated' => 'Egg install script has been updated and will run whenever servers are installed.',
|
'script_updated' => 'Egg install script has been updated and will run whenever servers are installed.',
|
||||||
|
|
|
@ -31,6 +31,7 @@ return [
|
||||||
'reserved_name' => 'The environment variable :name is protected and cannot be assigned to a variable.',
|
'reserved_name' => 'The environment variable :name is protected and cannot be assigned to a variable.',
|
||||||
],
|
],
|
||||||
'importer' => [
|
'importer' => [
|
||||||
|
'json_error' => 'There was an error while attempting to parse the JSON file: :error.',
|
||||||
'file_error' => 'The JSON file provided was not valid.',
|
'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.',
|
'invalid_json_provided' => 'The JSON file provided is not in a format that can be recognized.',
|
||||||
],
|
],
|
||||||
|
|
|
@ -30,14 +30,39 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<div class="callout callout-info">
|
||||||
|
<strong>Notice:</strong> Editing an Egg or any of the Process Management fields <em>requires</em> that each Daemon be rebooted in order to apply the changes.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form action="{{ route('admin.nests.egg.view', $egg->id) }}" method="POST">
|
<form action="{{ route('admin.nests.egg.view', $egg->id) }}" enctype="multipart/form-data" method="POST">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<div class="callout callout-info">
|
<div class="box box-danger">
|
||||||
<strong>Notice:</strong> Editing an Egg or any of the Process Management fields <em>requires</em> that each Daemon be rebooted in order to apply the changes.
|
<div class="box-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-8">
|
||||||
|
<div class="form-group no-margin-bottom">
|
||||||
|
<label for="pName" class="control-label">Egg File</label>
|
||||||
|
<div>
|
||||||
|
<input type="file" name="import_file" class="form-control" style="border: 0;margin-left:-10px;" />
|
||||||
|
<p class="text-muted small no-margin-bottom">If you would like to replace settings for this Egg by uploading a new JSON file, simply select it here and press "Update Egg". This will not change any existing startup strings or Docker images for existing servers.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4">
|
||||||
|
{!! csrf_field() !!}
|
||||||
|
<button type="submit" name="_method" value="PUT" class="btn btn-sm btn-danger pull-right">Update Egg</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<form action="{{ route('admin.nests.egg.view', $egg->id) }}" method="POST">
|
||||||
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header with-border">
|
<div class="box-header with-border">
|
||||||
|
|
|
@ -163,6 +163,8 @@ Route::group(['prefix' => 'nests'], function () {
|
||||||
Route::post('/egg/new', 'Nests\EggController@store');
|
Route::post('/egg/new', 'Nests\EggController@store');
|
||||||
Route::post('/egg/{egg}/variables', 'Nests\EggVariableController@store');
|
Route::post('/egg/{egg}/variables', 'Nests\EggVariableController@store');
|
||||||
|
|
||||||
|
Route::put('/egg/{egg}', 'Nests\EggShareController@update');
|
||||||
|
|
||||||
Route::patch('/view/{nest}', 'Nests\NestController@update');
|
Route::patch('/view/{nest}', 'Nests\NestController@update');
|
||||||
Route::patch('/egg/{egg}', 'Nests\EggController@update');
|
Route::patch('/egg/{egg}', 'Nests\EggController@update');
|
||||||
Route::patch('/egg/{egg}/scripts', 'Nests\EggScriptController@update');
|
Route::patch('/egg/{egg}/scripts', 'Nests\EggScriptController@update');
|
||||||
|
|
|
@ -21,7 +21,8 @@ use Pterodactyl\Exceptions\PterodactylException;
|
||||||
use Pterodactyl\Services\Eggs\Sharing\EggImporterService;
|
use Pterodactyl\Services\Eggs\Sharing\EggImporterService;
|
||||||
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
||||||
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
|
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;
|
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
|
||||||
|
|
||||||
class EggImporterServiceTest extends TestCase
|
class EggImporterServiceTest extends TestCase
|
||||||
|
@ -90,7 +91,7 @@ class EggImporterServiceTest extends TestCase
|
||||||
$this->file->shouldReceive('openFile->fread')->with(100)->once()->andReturn(json_encode([
|
$this->file->shouldReceive('openFile->fread')->with(100)->once()->andReturn(json_encode([
|
||||||
'meta' => ['version' => 'PTDL_v1'],
|
'meta' => ['version' => 'PTDL_v1'],
|
||||||
'name' => $egg->name,
|
'name' => $egg->name,
|
||||||
'tag' => $egg->tag,
|
'author' => $egg->author,
|
||||||
'variables' => [
|
'variables' => [
|
||||||
$variable = factory(EggVariable::class)->make(),
|
$variable = factory(EggVariable::class)->make(),
|
||||||
],
|
],
|
||||||
|
@ -165,4 +166,24 @@ class EggImporterServiceTest extends TestCase
|
||||||
$this->assertEquals(trans('exceptions.nest.importer.invalid_json_provided'), $exception->getMessage());
|
$this->assertEquals(trans('exceptions.nest.importer.invalid_json_provided'), $exception->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an exception is thrown if bad JSON is provided.
|
||||||
|
*/
|
||||||
|
public function testExceptionIsThrownIfBadJsonIsProvided()
|
||||||
|
{
|
||||||
|
$this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true);
|
||||||
|
$this->file->shouldReceive('isFile')->withNoArgs()->once()->andReturn(true);
|
||||||
|
$this->file->shouldReceive('getSize')->withNoArgs()->once()->andReturn(100);
|
||||||
|
$this->file->shouldReceive('openFile->fread')->with(100)->once()->andReturn('}');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->service->handle($this->file, 1234);
|
||||||
|
} catch (PterodactylException $exception) {
|
||||||
|
$this->assertInstanceOf(BadJsonFormatException::class, $exception);
|
||||||
|
$this->assertEquals(trans('exceptions.nest.importer.json_error', [
|
||||||
|
'error' => json_last_error_msg(),
|
||||||
|
]), $exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,212 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit\Services\Eggs\Sharing;
|
||||||
|
|
||||||
|
use Mockery as m;
|
||||||
|
use Tests\TestCase;
|
||||||
|
use Pterodactyl\Models\Egg;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Pterodactyl\Models\EggVariable;
|
||||||
|
use Illuminate\Database\ConnectionInterface;
|
||||||
|
use Pterodactyl\Exceptions\PterodactylException;
|
||||||
|
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
|
||||||
|
use Pterodactyl\Exceptions\Service\Egg\BadJsonFormatException;
|
||||||
|
use Pterodactyl\Exceptions\Service\InvalidFileUploadException;
|
||||||
|
use Pterodactyl\Services\Eggs\Sharing\EggUpdateImporterService;
|
||||||
|
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
|
||||||
|
|
||||||
|
class EggUpdateImporterServiceTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Database\ConnectionInterface|\Mockery\Mock
|
||||||
|
*/
|
||||||
|
protected $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Http\UploadedFile|\Mockery\Mock
|
||||||
|
*/
|
||||||
|
protected $file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface|\Mockery\Mock
|
||||||
|
*/
|
||||||
|
protected $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Services\Eggs\Sharing\EggUpdateImporterService
|
||||||
|
*/
|
||||||
|
protected $service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface|\Mockery\Mock
|
||||||
|
*/
|
||||||
|
protected $variableRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup tests.
|
||||||
|
*/
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->connection = m::mock(ConnectionInterface::class);
|
||||||
|
$this->file = m::mock(UploadedFile::class);
|
||||||
|
$this->repository = m::mock(EggRepositoryInterface::class);
|
||||||
|
$this->variableRepository = m::mock(EggVariableRepositoryInterface::class);
|
||||||
|
|
||||||
|
$this->service = new EggUpdateImporterService($this->connection, $this->repository, $this->variableRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an egg update is handled correctly using an uploaded file.
|
||||||
|
*/
|
||||||
|
public function testEggIsUpdated()
|
||||||
|
{
|
||||||
|
$egg = factory(Egg::class)->make();
|
||||||
|
$variable = factory(EggVariable::class)->make();
|
||||||
|
|
||||||
|
$this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true);
|
||||||
|
$this->file->shouldReceive('isFile')->withNoArgs()->once()->andReturn(true);
|
||||||
|
$this->file->shouldReceive('getSize')->withNoArgs()->once()->andReturn(100);
|
||||||
|
$this->file->shouldReceive('openFile->fread')->with(100)->once()->andReturn(json_encode([
|
||||||
|
'meta' => ['version' => 'PTDL_v1'],
|
||||||
|
'name' => $egg->name,
|
||||||
|
'author' => 'newauthor@example.com',
|
||||||
|
'variables' => [$variable->toArray()],
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('update')->with($egg->id, m::subset([
|
||||||
|
'author' => 'newauthor@example.com',
|
||||||
|
'name' => $egg->name,
|
||||||
|
]), true, true)->once()->andReturn($egg);
|
||||||
|
|
||||||
|
$this->variableRepository->shouldReceive('withoutFresh->updateOrCreate')->with([
|
||||||
|
'egg_id' => $egg->id,
|
||||||
|
'env_variable' => $variable->env_variable,
|
||||||
|
], collect($variable)->except(['egg_id', 'env_variable'])->toArray())->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->variableRepository->shouldReceive('withColumns')->with(['id', 'env_variable'])->once()->andReturnSelf()
|
||||||
|
->shouldReceive('findWhere')->with([['egg_id', '=', $egg->id]])->once()->andReturn([$variable]);
|
||||||
|
|
||||||
|
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->service->handle($egg->id, $this->file);
|
||||||
|
$this->assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an imported file with less variables than currently existing deletes
|
||||||
|
* the un-needed variables from the database.
|
||||||
|
*/
|
||||||
|
public function testVariablesMissingFromImportAreDeleted()
|
||||||
|
{
|
||||||
|
$egg = factory(Egg::class)->make();
|
||||||
|
$variable1 = factory(EggVariable::class)->make();
|
||||||
|
$variable2 = factory(EggVariable::class)->make();
|
||||||
|
|
||||||
|
$this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true);
|
||||||
|
$this->file->shouldReceive('isFile')->withNoArgs()->once()->andReturn(true);
|
||||||
|
$this->file->shouldReceive('getSize')->withNoArgs()->once()->andReturn(100);
|
||||||
|
$this->file->shouldReceive('openFile->fread')->with(100)->once()->andReturn(json_encode([
|
||||||
|
'meta' => ['version' => 'PTDL_v1'],
|
||||||
|
'name' => $egg->name,
|
||||||
|
'author' => 'newauthor@example.com',
|
||||||
|
'variables' => [$variable1->toArray()],
|
||||||
|
]));
|
||||||
|
|
||||||
|
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||||
|
$this->repository->shouldReceive('update')->with($egg->id, m::subset([
|
||||||
|
'author' => 'newauthor@example.com',
|
||||||
|
'name' => $egg->name,
|
||||||
|
]), true, true)->once()->andReturn($egg);
|
||||||
|
|
||||||
|
$this->variableRepository->shouldReceive('withoutFresh->updateOrCreate')->with([
|
||||||
|
'egg_id' => $egg->id,
|
||||||
|
'env_variable' => $variable1->env_variable,
|
||||||
|
], collect($variable1)->except(['egg_id', 'env_variable'])->toArray())->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->variableRepository->shouldReceive('withColumns')->with(['id', 'env_variable'])->once()->andReturnSelf()
|
||||||
|
->shouldReceive('findWhere')->with([['egg_id', '=', $egg->id]])->once()->andReturn([$variable1, $variable2]);
|
||||||
|
|
||||||
|
$this->variableRepository->shouldReceive('deleteWhere')->with([
|
||||||
|
['egg_id', '=', $egg->id],
|
||||||
|
['env_variable', '=', $variable2->env_variable],
|
||||||
|
])->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
|
||||||
|
|
||||||
|
$this->service->handle($egg->id, $this->file);
|
||||||
|
$this->assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an exception is thrown if the file is invalid.
|
||||||
|
*/
|
||||||
|
public function testExceptionIsThrownIfFileIsInvalid()
|
||||||
|
{
|
||||||
|
$this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(false);
|
||||||
|
try {
|
||||||
|
$this->service->handle(1234, $this->file);
|
||||||
|
} catch (PterodactylException $exception) {
|
||||||
|
$this->assertInstanceOf(InvalidFileUploadException::class, $exception);
|
||||||
|
$this->assertEquals(trans('exceptions.nest.importer.file_error'), $exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an exception is thrown if the file is not a file.
|
||||||
|
*/
|
||||||
|
public function testExceptionIsThrownIfFileIsNotAFile()
|
||||||
|
{
|
||||||
|
$this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true);
|
||||||
|
$this->file->shouldReceive('isFile')->withNoArgs()->once()->andReturn(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->service->handle(1234, $this->file);
|
||||||
|
} catch (PterodactylException $exception) {
|
||||||
|
$this->assertInstanceOf(InvalidFileUploadException::class, $exception);
|
||||||
|
$this->assertEquals(trans('exceptions.nest.importer.file_error'), $exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an exception is thrown if the JSON metadata is invalid.
|
||||||
|
*/
|
||||||
|
public function testExceptionIsThrownIfJsonMetaDataIsInvalid()
|
||||||
|
{
|
||||||
|
$this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true);
|
||||||
|
$this->file->shouldReceive('isFile')->withNoArgs()->once()->andReturn(true);
|
||||||
|
$this->file->shouldReceive('getSize')->withNoArgs()->once()->andReturn(100);
|
||||||
|
$this->file->shouldReceive('openFile->fread')->with(100)->once()->andReturn(json_encode([
|
||||||
|
'meta' => ['version' => 'hodor'],
|
||||||
|
]));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->service->handle(1234, $this->file);
|
||||||
|
} catch (PterodactylException $exception) {
|
||||||
|
$this->assertInstanceOf(InvalidFileUploadException::class, $exception);
|
||||||
|
$this->assertEquals(trans('exceptions.nest.importer.invalid_json_provided'), $exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an exception is thrown if bad JSON is provided.
|
||||||
|
*/
|
||||||
|
public function testExceptionIsThrownIfBadJsonIsProvided()
|
||||||
|
{
|
||||||
|
$this->file->shouldReceive('isValid')->withNoArgs()->once()->andReturn(true);
|
||||||
|
$this->file->shouldReceive('isFile')->withNoArgs()->once()->andReturn(true);
|
||||||
|
$this->file->shouldReceive('getSize')->withNoArgs()->once()->andReturn(100);
|
||||||
|
$this->file->shouldReceive('openFile->fread')->with(100)->once()->andReturn('}');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->service->handle(1234, $this->file);
|
||||||
|
} catch (PterodactylException $exception) {
|
||||||
|
$this->assertInstanceOf(BadJsonFormatException::class, $exception);
|
||||||
|
$this->assertEquals(trans('exceptions.nest.importer.json_error', [
|
||||||
|
'error' => json_last_error_msg(),
|
||||||
|
]), $exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ use Illuminate\Contracts\Filesystem\Factory;
|
||||||
use Illuminate\Database\ConnectionInterface;
|
use Illuminate\Database\ConnectionInterface;
|
||||||
use Pterodactyl\Services\Packs\PackCreationService;
|
use Pterodactyl\Services\Packs\PackCreationService;
|
||||||
use Pterodactyl\Contracts\Repository\PackRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\PackRepositoryInterface;
|
||||||
use Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException;
|
use Pterodactyl\Exceptions\Service\InvalidFileUploadException;
|
||||||
use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException;
|
use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException;
|
||||||
|
|
||||||
class PackCreationServiceTest extends TestCase
|
class PackCreationServiceTest extends TestCase
|
||||||
|
|
|
@ -16,8 +16,8 @@ use Pterodactyl\Models\Pack;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
use Pterodactyl\Services\Packs\PackCreationService;
|
use Pterodactyl\Services\Packs\PackCreationService;
|
||||||
use Pterodactyl\Services\Packs\TemplateUploadService;
|
use Pterodactyl\Services\Packs\TemplateUploadService;
|
||||||
|
use Pterodactyl\Exceptions\Service\InvalidFileUploadException;
|
||||||
use Pterodactyl\Exceptions\Service\Pack\ZipExtractionException;
|
use Pterodactyl\Exceptions\Service\Pack\ZipExtractionException;
|
||||||
use Pterodactyl\Exceptions\Service\Pack\InvalidFileUploadException;
|
|
||||||
use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException;
|
use Pterodactyl\Exceptions\Service\Pack\InvalidFileMimeTypeException;
|
||||||
use Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException;
|
use Pterodactyl\Exceptions\Service\Pack\UnreadableZipArchiveException;
|
||||||
use Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException;
|
use Pterodactyl\Exceptions\Service\Pack\InvalidPackArchiveFormatException;
|
||||||
|
|
Loading…
Reference in a new issue