Basic implemention of multiple selectable images for an egg
The admin side of this is quite ugly when creating/editing a server, but I'm not putting effort into that right now with React Admin soon™
This commit is contained in:
parent
3e65a2d055
commit
78c4ac80bc
11 changed files with 123 additions and 31 deletions
|
@ -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')
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class SupportMultipleDockerImagesAndUpdates extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('eggs', function (Blueprint $table) {
|
||||
$table->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');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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!'));
|
||||
|
|
|
@ -265,8 +265,9 @@
|
|||
<div class="box-body row">
|
||||
<div class="form-group col-xs-12">
|
||||
<label for="pDefaultContainer">Docker Image</label>
|
||||
<input id="pDefaultContainer" name="image" value="{{ old('image') }}" class="form-control" />
|
||||
<p class="small text-muted no-margin">This is the default Docker image that will be used to run this server.</p>
|
||||
<select id="pDefaultContainer" name="image" class="form-control"></select>
|
||||
<input id="pDefaultContainerCustom" name="custom_image" value="{{ old('custom_image') }}" class="form-control" placeholder="Or enter a custom image..." style="margin-top:1rem"/>
|
||||
<p class="small text-muted no-margin">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.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -323,11 +324,14 @@
|
|||
@endforeach
|
||||
@endif
|
||||
@endif
|
||||
@if(old('image'))
|
||||
$('#pDefaultContainer').val('{{ old('image') }}');
|
||||
@endif
|
||||
}
|
||||
// END Persist 'Service Variables'
|
||||
</script>
|
||||
|
||||
{!! Theme::js('js/admin/new-server.js?v=20201003') !!}
|
||||
{!! Theme::js('js/admin/new-server.js?v=20201212') !!}
|
||||
|
||||
<script type="application/javascript">
|
||||
$(document).ready(function() {
|
||||
|
|
|
@ -89,13 +89,14 @@
|
|||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Docker Container Configuration</h3>
|
||||
<h3 class="box-title">Docker Image Configuration</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="form-group">
|
||||
<label for="pDockerImage" class="control-label">Image</label>
|
||||
<input type="text" name="docker_image" id="pDockerImage" value="{{ $server->image }}" class="form-control" />
|
||||
<p class="text-muted small">The Docker image to use for this server. The default image for the selected egg is <code id="setDefaultImage"></code>.</p>
|
||||
<label for="pDockerImage">Image</label>
|
||||
<select id="pDockerImage" name="docker_image" class="form-control"></select>
|
||||
<input id="pDockerImageCustom" name="custom_docker_image" value="{{ old('custom_docker_image') }}" class="form-control" placeholder="Or enter a custom image..." style="margin-top:1rem"/>
|
||||
<p class="small text-muted no-margin">This is the Docker image that will be used to run this server. Select an image from the dropdown or enter a custom image in the text field above.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -117,10 +118,25 @@
|
|||
var parentChain = _.get(Pterodactyl.nests, $("#pNestId").val());
|
||||
var objectChain = _.get(parentChain, 'eggs.' + selectedEgg);
|
||||
|
||||
$('#setDefaultImage').html(_.get(objectChain, 'docker_image', 'undefined'));
|
||||
$('#pDockerImage').val(_.get(objectChain, 'docker_image', 'undefined'));
|
||||
$('#setDefaultImage').html(_.get(objectChain, 'docker_images.0', 'undefined'));
|
||||
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];
|
||||
if (objectChain.id === parseInt(Pterodactyl.server.egg_id) && Pterodactyl.server.image == opt.value) {
|
||||
opt.checked = true
|
||||
}
|
||||
$('#pDockerImage').append(opt);
|
||||
}
|
||||
$('#pDockerImage').on('change', function () {
|
||||
$('#pDockerImageCustom').val('');
|
||||
})
|
||||
|
||||
if (objectChain.id === parseInt(Pterodactyl.server.egg_id)) {
|
||||
$('#pDockerImage').val(Pterodactyl.server.image);
|
||||
if ($('#pDockerImage').val() != Pterodactyl.server.image) {
|
||||
$('#pDockerImageCustom').val(Pterodactyl.server.image);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_.get(objectChain, 'startup', false)) {
|
||||
|
|
Loading…
Reference in a new issue