diff --git a/app/Http/Controllers/Admin/Servers/CreateServerController.php b/app/Http/Controllers/Admin/Servers/CreateServerController.php index f63cf814b..1cea7651a 100644 --- a/app/Http/Controllers/Admin/Servers/CreateServerController.php +++ b/app/Http/Controllers/Admin/Servers/CreateServerController.php @@ -111,17 +111,19 @@ class CreateServerController extends Controller * * @throws \Illuminate\Validation\ValidationException * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableAllocationException * @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException * @throws \Throwable */ public function store(ServerFormRequest $request) { - $server = $this->creationService->handle( - $request->except(['_token']) - ); + $data = $request->except(['_token']); + if (!empty($data['custom_image'])) { + $data['image'] = $data['custom_image']; + unset($data['custom_image']); + } + + $server = $this->creationService->handle($data); $this->alert->success( trans('admin/server.alerts.server_created') diff --git a/app/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 363737159..bec5ac4aa 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -334,14 +334,19 @@ class ServersController extends Controller * @return \Illuminate\Http\RedirectResponse * * @throws \Illuminate\Validation\ValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function saveStartup(Request $request, Server $server) { + $data = $request->except('_token'); + if (!empty($data['custom_docker_image'])) { + $data['docker_image'] = $data['custom_docker_image']; + unset($data['custom_docker_image']); + } + try { $this->startupModificationService ->setUserLevel(User::USER_LEVEL_ADMIN) - ->handle($server, $request->except('_token')); + ->handle($server, $data); } catch (DataValidationException $exception) { throw new ValidationException($exception->validator); } diff --git a/app/Http/Requests/Admin/ServerFormRequest.php b/app/Http/Requests/Admin/ServerFormRequest.php index 6f930615c..585834224 100644 --- a/app/Http/Requests/Admin/ServerFormRequest.php +++ b/app/Http/Requests/Admin/ServerFormRequest.php @@ -1,11 +1,4 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Http\Requests\Admin; @@ -23,6 +16,7 @@ class ServerFormRequest extends AdminFormRequest { $rules = Server::getRules(); $rules['description'][] = 'nullable'; + $rules['custom_image'] = 'sometimes|nullable|string'; return $rules; } diff --git a/app/Models/Egg.php b/app/Models/Egg.php index ce2b52c0c..aed4be7e3 100644 --- a/app/Models/Egg.php +++ b/app/Models/Egg.php @@ -10,7 +10,9 @@ namespace Pterodactyl\Models; * @property string $name * @property string|null $description * @property array|null $features - * @property string $docker_image + * @property string $docker_image -- deprecated, use $docker_images + * @property string $update_url + * @property array $docker_images * @property string|null $config_files * @property string|null $config_startup * @property string|null $config_logs @@ -76,7 +78,7 @@ class Egg extends Model 'name', 'description', 'features', - 'docker_image', + 'docker_images', 'config_files', 'config_startup', 'config_logs', @@ -101,6 +103,7 @@ class Egg extends Model 'script_is_privileged' => 'boolean', 'copy_script_from' => 'integer', 'features' => 'array', + 'docker_images' => 'array', ]; /** @@ -113,13 +116,15 @@ class Egg extends Model 'description' => 'string|nullable', 'features' => 'array|nullable', 'author' => 'required|string|email', - 'docker_image' => 'required|string|max:191', + 'docker_images' => 'required|array|min:1', + 'docker_images.*' => 'required|string', 'startup' => 'required|nullable|string', 'config_from' => 'sometimes|bail|nullable|numeric|exists:eggs,id', 'config_stop' => 'required_without:config_from|nullable|string|max:191', 'config_startup' => 'required_without:config_from|nullable|json', 'config_logs' => 'required_without:config_from|nullable|json', 'config_files' => 'required_without:config_from|nullable|json', + 'update_url' => 'sometimes|nullable|string', ]; /** @@ -131,6 +136,7 @@ class Egg extends Model 'config_startup' => null, 'config_logs' => null, 'config_files' => null, + 'update_url' => null, ]; /** diff --git a/app/Services/Eggs/Sharing/EggExporterService.php b/app/Services/Eggs/Sharing/EggExporterService.php index 2d32060ac..bd26a1ea1 100644 --- a/app/Services/Eggs/Sharing/EggExporterService.php +++ b/app/Services/Eggs/Sharing/EggExporterService.php @@ -38,13 +38,14 @@ class EggExporterService '_comment' => 'DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO', 'meta' => [ 'version' => 'PTDL_v1', + 'update_url' => $egg->update_url, ], 'exported_at' => Carbon::now()->toIso8601String(), 'name' => $egg->name, 'author' => $egg->author, 'description' => $egg->description, 'features' => $egg->features, - 'image' => $egg->docker_image, + 'images' => $egg->docker_images, 'startup' => $egg->startup, 'config' => [ 'files' => $egg->inherit_config_files, diff --git a/app/Services/Eggs/Sharing/EggImporterService.php b/app/Services/Eggs/Sharing/EggImporterService.php index 786cf7a50..8955b1870 100644 --- a/app/Services/Eggs/Sharing/EggImporterService.php +++ b/app/Services/Eggs/Sharing/EggImporterService.php @@ -102,7 +102,10 @@ class EggImporterService 'name' => object_get($parsed, 'name'), 'description' => object_get($parsed, 'description'), 'features' => object_get($parsed, 'features'), - 'docker_image' => object_get($parsed, 'image'), + // Maintain backwards compatability for eggs that are still using the old single image + // string format. New eggs can provide an array of Docker images that can be used. + 'docker_images' => object_get($parsed, 'images') ?? [object_get($parsed, 'image')], + 'update_url' => object_get($parsed, 'meta.update_url'), 'config_files' => object_get($parsed, 'config.files'), 'config_startup' => object_get($parsed, 'config.startup'), 'config_logs' => object_get($parsed, 'config.logs'), diff --git a/app/Transformers/Api/Application/EggTransformer.php b/app/Transformers/Api/Application/EggTransformer.php index a3686341e..96a0abfb9 100644 --- a/app/Transformers/Api/Application/EggTransformer.php +++ b/app/Transformers/Api/Application/EggTransformer.php @@ -45,7 +45,11 @@ class EggTransformer extends BaseTransformer 'nest' => $model->nest_id, 'author' => $model->author, 'description' => $model->description, - 'docker_image' => $model->docker_image, + // "docker_image" is deprecated, but left here to avoid breaking too many things at once + // in external software. We'll remove it down the road once things have gotten the chance + // to upgrade to using "docker_images". + 'docker_image' => count($model->docker_images) > 0 ? $model->docker_images[0] : '', + 'docker_images' => $model->docker_images, 'config' => [ 'files' => json_decode($model->config_files, true), 'startup' => json_decode($model->config_startup, true), diff --git a/database/migrations/2020_12_12_102435_support_multiple_docker_images_and_updates.php b/database/migrations/2020_12_12_102435_support_multiple_docker_images_and_updates.php new file mode 100644 index 000000000..a7cd7310d --- /dev/null +++ b/database/migrations/2020_12_12_102435_support_multiple_docker_images_and_updates.php @@ -0,0 +1,51 @@ +json('docker_images')->after('docker_image')->nullable(); + $table->text('update_url')->after('docker_images')->nullable(); + }); + + Schema::table('eggs', function (Blueprint $table) { + DB::statement('UPDATE `eggs` SET `docker_images` = JSON_ARRAY(docker_image)'); + }); + + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('docker_image'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('eggs', function (Blueprint $table) { + $table->text('docker_image')->after('docker_images'); + }); + + Schema::table('eggs', function (Blueprint $table) { + DB::statement('UPDATE `eggs` SET `docker_image` = JSON_UNQUOTE(JSON_EXTRACT(docker_images, "$[0]"))'); + }); + + Schema::table('eggs', function (Blueprint $table) { + $table->dropColumn('docker_images'); + $table->dropColumn('update_url'); + }); + } +} diff --git a/public/themes/pterodactyl/js/admin/new-server.js b/public/themes/pterodactyl/js/admin/new-server.js index cda0d5cf3..67827cf06 100644 --- a/public/themes/pterodactyl/js/admin/new-server.js +++ b/public/themes/pterodactyl/js/admin/new-server.js @@ -82,7 +82,13 @@ $('#pEggId').on('change', function (event) { let parentChain = _.get(Pterodactyl.nests, $('#pNestId').val(), null); let objectChain = _.get(parentChain, 'eggs.' + $(this).val(), null); - $('#pDefaultContainer').val(_.get(objectChain, 'docker_image', 'not defined!')); + const images = _.get(objectChain, 'docker_images', []) + for (let i = 0; i < images.length; i++) { + let opt = document.createElement('option'); + opt.value = images[i]; + opt.innerHTML = images[i]; + $('#pDefaultContainer').append(opt); + } if (!_.get(objectChain, 'startup', false)) { $('#pStartup').val(_.get(parentChain, 'startup', 'ERROR: Startup Not Defined!')); diff --git a/resources/views/admin/servers/new.blade.php b/resources/views/admin/servers/new.blade.php index 1b74610d0..50ae7b90d 100644 --- a/resources/views/admin/servers/new.blade.php +++ b/resources/views/admin/servers/new.blade.php @@ -265,8 +265,9 @@
This is the default Docker image that will be used to run this server.
+ + +This is the default Docker image that will be used to run this server. Select an image from the dropdown above, or enter a custom image in the text field above.