ui(admin): new egg page
This commit is contained in:
parent
336923ec18
commit
cddf2ce41c
15 changed files with 210 additions and 84 deletions
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Http\Controllers\Api\Application\Eggs;
|
namespace Pterodactyl\Http\Controllers\Api\Application\Eggs;
|
||||||
|
|
||||||
|
use Ramsey\Uuid\Uuid;
|
||||||
use Pterodactyl\Models\Egg;
|
use Pterodactyl\Models\Egg;
|
||||||
use Pterodactyl\Models\Nest;
|
use Pterodactyl\Models\Nest;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Spatie\QueryBuilder\QueryBuilder;
|
use Spatie\QueryBuilder\QueryBuilder;
|
||||||
use Pterodactyl\Transformers\Api\Application\EggTransformer;
|
use Pterodactyl\Transformers\Api\Application\EggTransformer;
|
||||||
use Pterodactyl\Http\Requests\Api\Application\Eggs\GetEggRequest;
|
use Pterodactyl\Http\Requests\Api\Application\Eggs\GetEggRequest;
|
||||||
|
@ -56,7 +58,16 @@ class EggController extends ApplicationApiController
|
||||||
*/
|
*/
|
||||||
public function store(StoreEggRequest $request): JsonResponse
|
public function store(StoreEggRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
$egg = Egg::query()->create($request->validated());
|
$validated = $request->validated();
|
||||||
|
Log::info(json_encode($validated, JSON_PRETTY_PRINT));
|
||||||
|
$merged = array_merge($validated, [
|
||||||
|
'uuid' => Uuid::uuid4()->toString(),
|
||||||
|
// TODO: allow this to be set in the request, and default to config value if null or not present.
|
||||||
|
'author' => config('pterodactyl.service.author'),
|
||||||
|
]);
|
||||||
|
Log::info(json_encode($merged, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
$egg = Egg::query()->create($merged);
|
||||||
|
|
||||||
return $this->fractal->item($egg)
|
return $this->fractal->item($egg)
|
||||||
->transformWith(EggTransformer::class)
|
->transformWith(EggTransformer::class)
|
||||||
|
|
|
@ -2,13 +2,29 @@
|
||||||
|
|
||||||
namespace Pterodactyl\Http\Requests\Api\Application\Eggs;
|
namespace Pterodactyl\Http\Requests\Api\Application\Eggs;
|
||||||
|
|
||||||
use Pterodactyl\Models\Egg;
|
|
||||||
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
|
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
|
||||||
|
|
||||||
class StoreEggRequest extends ApplicationApiRequest
|
class StoreEggRequest extends ApplicationApiRequest
|
||||||
{
|
{
|
||||||
public function rules(array $rules = null): array
|
public function rules(array $rules = null): array
|
||||||
{
|
{
|
||||||
return $rules ?? Egg::getRules();
|
return [
|
||||||
|
'nest_id' => 'required|bail|numeric|exists:nests,id',
|
||||||
|
'name' => 'required|string|max:191',
|
||||||
|
'description' => 'sometimes|string|nullable',
|
||||||
|
'features' => 'sometimes|array',
|
||||||
|
'docker_images' => 'required|array|min:1',
|
||||||
|
'docker_images.*' => 'required|string',
|
||||||
|
'file_denylist' => 'sometimes|array|nullable',
|
||||||
|
'file_denylist.*' => 'sometimes|string',
|
||||||
|
'config_files' => 'required|nullable|json',
|
||||||
|
'config_startup' => 'required|nullable|json',
|
||||||
|
'config_stop' => 'required|nullable|string|max:191',
|
||||||
|
// 'config_from' => 'sometimes|nullable|numeric|exists:eggs,id',
|
||||||
|
'startup' => 'required|string',
|
||||||
|
'script_container' => 'sometimes|string',
|
||||||
|
'script_entry' => 'sometimes|string',
|
||||||
|
'script_install' => 'sometimes|string',
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,16 +10,16 @@ class UpdateEggRequest extends StoreEggRequest
|
||||||
'nest_id' => 'sometimes|numeric|exists:nests,id',
|
'nest_id' => 'sometimes|numeric|exists:nests,id',
|
||||||
'name' => 'sometimes|string|max:191',
|
'name' => 'sometimes|string|max:191',
|
||||||
'description' => 'sometimes|string|nullable',
|
'description' => 'sometimes|string|nullable',
|
||||||
'features' => 'sometimes|array|nullable',
|
'features' => 'sometimes|array',
|
||||||
'docker_images' => 'sometimes|required|array|min:1',
|
'docker_images' => 'sometimes|array|min:1',
|
||||||
'docker_images.*' => 'sometimes|string',
|
'docker_images.*' => 'sometimes|string',
|
||||||
'file_denylist' => 'sometimes|array|nullable',
|
'file_denylist' => 'sometimes|array|nullable',
|
||||||
'file_denylist.*' => 'sometimes|string',
|
'file_denylist.*' => 'sometimes|string',
|
||||||
'config_files' => 'sometimes|nullable|json',
|
'config_files' => 'sometimes|nullable|json',
|
||||||
'config_startup' => 'sometimes|nullable|json',
|
'config_startup' => 'sometimes|nullable|json',
|
||||||
'config_stop' => 'sometimes|nullable|string|max:191',
|
'config_stop' => 'sometimes|nullable|string|max:191',
|
||||||
'config_from' => 'sometimes|nullable|numeric|exists:eggs,id',
|
// 'config_from' => 'sometimes|nullable|numeric|exists:eggs,id',
|
||||||
'startup' => 'sometimes|nullable|string',
|
'startup' => 'sometimes|string',
|
||||||
'script_container' => 'sometimes|string',
|
'script_container' => 'sometimes|string',
|
||||||
'script_entry' => 'sometimes|string',
|
'script_entry' => 'sometimes|string',
|
||||||
'script_install' => 'sometimes|string',
|
'script_install' => 'sometimes|string',
|
||||||
|
|
|
@ -16,7 +16,6 @@ namespace Pterodactyl\Models;
|
||||||
* @property array|null $file_denylist
|
* @property array|null $file_denylist
|
||||||
* @property string|null $config_files
|
* @property string|null $config_files
|
||||||
* @property string|null $config_startup
|
* @property string|null $config_startup
|
||||||
* @property string|null $config_logs
|
|
||||||
* @property string|null $config_stop
|
* @property string|null $config_stop
|
||||||
* @property int|null $config_from
|
* @property int|null $config_from
|
||||||
* @property string|null $startup
|
* @property string|null $startup
|
||||||
|
@ -32,7 +31,6 @@ namespace Pterodactyl\Models;
|
||||||
* @property string $copy_script_container
|
* @property string $copy_script_container
|
||||||
* @property string|null $inherit_config_files
|
* @property string|null $inherit_config_files
|
||||||
* @property string|null $inherit_config_startup
|
* @property string|null $inherit_config_startup
|
||||||
* @property string|null $inherit_config_logs
|
|
||||||
* @property string|null $inherit_config_stop
|
* @property string|null $inherit_config_stop
|
||||||
* @property string $inherit_file_denylist
|
* @property string $inherit_file_denylist
|
||||||
* @property array|null $inherit_features
|
* @property array|null $inherit_features
|
||||||
|
@ -75,14 +73,16 @@ class Egg extends Model
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
'nest_id',
|
||||||
|
'uuid',
|
||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'features',
|
'features',
|
||||||
|
'author',
|
||||||
'docker_images',
|
'docker_images',
|
||||||
'file_denylist',
|
'file_denylist',
|
||||||
'config_files',
|
'config_files',
|
||||||
'config_startup',
|
'config_startup',
|
||||||
'config_logs',
|
|
||||||
'config_stop',
|
'config_stop',
|
||||||
'config_from',
|
'config_from',
|
||||||
'startup',
|
'startup',
|
||||||
|
@ -123,7 +123,6 @@ class Egg extends Model
|
||||||
'config_from' => 'sometimes|bail|nullable|numeric|exists:eggs,id',
|
'config_from' => 'sometimes|bail|nullable|numeric|exists:eggs,id',
|
||||||
'config_stop' => 'required_without:config_from|nullable|string|max:191',
|
'config_stop' => 'required_without:config_from|nullable|string|max:191',
|
||||||
'config_startup' => 'required_without:config_from|nullable|json',
|
'config_startup' => 'required_without:config_from|nullable|json',
|
||||||
'config_logs' => 'required_without:config_from|nullable|json',
|
|
||||||
'config_files' => 'required_without:config_from|nullable|json',
|
'config_files' => 'required_without:config_from|nullable|json',
|
||||||
'update_url' => 'sometimes|nullable|string',
|
'update_url' => 'sometimes|nullable|string',
|
||||||
];
|
];
|
||||||
|
@ -136,7 +135,6 @@ class Egg extends Model
|
||||||
'file_denylist' => null,
|
'file_denylist' => null,
|
||||||
'config_stop' => null,
|
'config_stop' => null,
|
||||||
'config_startup' => null,
|
'config_startup' => null,
|
||||||
'config_logs' => null,
|
|
||||||
'config_files' => null,
|
'config_files' => null,
|
||||||
'update_url' => null,
|
'update_url' => null,
|
||||||
];
|
];
|
||||||
|
@ -214,20 +212,6 @@ class Egg extends Model
|
||||||
return $this->configFrom->config_startup;
|
return $this->configFrom->config_startup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the log reading configuration for an egg.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getInheritConfigLogsAttribute()
|
|
||||||
{
|
|
||||||
if (!is_null($this->config_logs) || is_null($this->config_from)) {
|
|
||||||
return $this->config_logs;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->configFrom->config_logs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the stop command configuration for an egg.
|
* Return the stop command configuration for an egg.
|
||||||
*
|
*
|
||||||
|
|
|
@ -50,7 +50,6 @@ class EggExporterService
|
||||||
'config' => [
|
'config' => [
|
||||||
'files' => $egg->inherit_config_files,
|
'files' => $egg->inherit_config_files,
|
||||||
'startup' => $egg->inherit_config_startup,
|
'startup' => $egg->inherit_config_startup,
|
||||||
'logs' => $egg->inherit_config_logs,
|
|
||||||
'stop' => $egg->inherit_config_stop,
|
'stop' => $egg->inherit_config_stop,
|
||||||
],
|
],
|
||||||
'scripts' => [
|
'scripts' => [
|
||||||
|
|
|
@ -151,7 +151,6 @@ class EggImporterService
|
||||||
'update_url' => Arr::get($parsed, 'meta.update_url'),
|
'update_url' => Arr::get($parsed, 'meta.update_url'),
|
||||||
'config_files' => Arr::get($parsed, 'config.files'),
|
'config_files' => Arr::get($parsed, 'config.files'),
|
||||||
'config_startup' => Arr::get($parsed, 'config.startup'),
|
'config_startup' => Arr::get($parsed, 'config.startup'),
|
||||||
'config_logs' => Arr::get($parsed, 'config.logs'),
|
|
||||||
'config_stop' => Arr::get($parsed, 'config.stop'),
|
'config_stop' => Arr::get($parsed, 'config.stop'),
|
||||||
'startup' => Arr::get($parsed, 'startup'),
|
'startup' => Arr::get($parsed, 'startup'),
|
||||||
'script_install' => Arr::get($parsed, 'scripts.installation.script'),
|
'script_install' => Arr::get($parsed, 'scripts.installation.script'),
|
||||||
|
|
|
@ -74,7 +74,6 @@ class EggUpdateImporterService
|
||||||
'docker_images' => object_get($parsed, 'images') ?? [object_get($parsed, 'image')],
|
'docker_images' => object_get($parsed, 'images') ?? [object_get($parsed, 'image')],
|
||||||
'config_files' => object_get($parsed, 'config.files'),
|
'config_files' => object_get($parsed, 'config.files'),
|
||||||
'config_startup' => object_get($parsed, 'config.startup'),
|
'config_startup' => object_get($parsed, 'config.startup'),
|
||||||
'config_logs' => object_get($parsed, 'config.logs'),
|
|
||||||
'config_stop' => object_get($parsed, 'config.stop'),
|
'config_stop' => object_get($parsed, 'config.stop'),
|
||||||
'startup' => object_get($parsed, 'startup'),
|
'startup' => object_get($parsed, 'startup'),
|
||||||
'script_install' => object_get($parsed, 'scripts.installation.script'),
|
'script_install' => object_get($parsed, 'scripts.installation.script'),
|
||||||
|
|
|
@ -41,10 +41,9 @@ class EggTransformer extends Transformer
|
||||||
'docker_image' => count($model->docker_images) > 0 ? $model->docker_images[0] : '',
|
'docker_image' => count($model->docker_images) > 0 ? $model->docker_images[0] : '',
|
||||||
'docker_images' => $model->docker_images,
|
'docker_images' => $model->docker_images,
|
||||||
'config' => [
|
'config' => [
|
||||||
'files' => json_decode($model->config_files, true),
|
'files' => json_decode($model->config_files),
|
||||||
'startup' => json_decode($model->config_startup, true),
|
'startup' => json_decode($model->config_startup),
|
||||||
'stop' => $model->config_stop,
|
'stop' => $model->config_stop,
|
||||||
'logs' => json_decode($model->config_logs, true),
|
|
||||||
'file_denylist' => $model->file_denylist,
|
'file_denylist' => $model->file_denylist,
|
||||||
'extends' => $model->config_from,
|
'extends' => $model->config_from,
|
||||||
],
|
],
|
||||||
|
@ -106,7 +105,6 @@ class EggTransformer extends Transformer
|
||||||
'files' => json_decode($model->inherit_config_files),
|
'files' => json_decode($model->inherit_config_files),
|
||||||
'startup' => json_decode($model->inherit_config_startup),
|
'startup' => json_decode($model->inherit_config_startup),
|
||||||
'stop' => $model->inherit_config_stop,
|
'stop' => $model->inherit_config_stop,
|
||||||
'logs' => json_decode($model->inherit_config_logs),
|
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class DropConfigLogsColumnFromEggsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('eggs', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('config_logs');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('eggs', function (Blueprint $table) {
|
||||||
|
$table->text('config_logs')->nullable()->after('docker_image');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
import http from '@/api/http';
|
import http from '@/api/http';
|
||||||
import { Egg, rawDataToEgg } from '@/api/admin/eggs/getEgg';
|
import { Egg, rawDataToEgg } from '@/api/admin/eggs/getEgg';
|
||||||
|
|
||||||
export default (egg: Partial<Egg>): Promise<Egg> => {
|
type Egg2 = Omit<Omit<Partial<Egg>, 'configFiles'>, 'configStartup'> & { configFiles: string, configStartup: string };
|
||||||
|
|
||||||
|
export default (egg: Partial<Egg2>): Promise<Egg> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.post(
|
http.post(
|
||||||
'/api/application/eggs',
|
'/api/application/eggs',
|
||||||
|
|
|
@ -196,9 +196,9 @@ export const Loading = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NoItems = () => {
|
export const NoItems = ({ className }: { className?: string }) => {
|
||||||
return (
|
return (
|
||||||
<div css={tw`w-full flex flex-col items-center justify-center py-6 px-8`}>
|
<div css={tw`w-full flex flex-col items-center justify-center py-6 px-8`} className={className}>
|
||||||
<div css={tw`h-48 flex`}>
|
<div css={tw`h-48 flex`}>
|
||||||
<img src={'/assets/svgs/not_found.svg'} alt={'No Items'} css={tw`h-full select-none`}/>
|
<img src={'/assets/svgs/not_found.svg'} alt={'No Items'} css={tw`h-full select-none`}/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -227,7 +227,8 @@ export const ContentWrapper = ({ checked, onSelectAllClick, onSearch, children }
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
onSearch(query).then(() => setLoading(false));
|
onSearch(query)
|
||||||
|
.then(() => setLoading(false));
|
||||||
}, 200),
|
}, 200),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,8 +1,51 @@
|
||||||
import React from 'react';
|
import createEgg from '@/api/admin/eggs/createEgg';
|
||||||
|
import { EggImageContainer, EggInformationContainer, EggLifecycleContainer, EggProcessContainer, EggProcessContainerRef, EggStartupContainer } from '@/components/admin/nests/eggs/EggSettingsContainer';
|
||||||
|
import Button from '@/components/elements/Button';
|
||||||
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
|
import useFlash from '@/plugins/useFlash';
|
||||||
|
import { Form, Formik, FormikHelpers } from 'formik';
|
||||||
|
import React, { useRef } from 'react';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
import tw from 'twin.macro';
|
import tw from 'twin.macro';
|
||||||
import AdminContentBlock from '@/components/admin/AdminContentBlock';
|
import AdminContentBlock from '@/components/admin/AdminContentBlock';
|
||||||
|
import { object } from 'yup';
|
||||||
|
|
||||||
|
interface Values {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
startup: string;
|
||||||
|
dockerImages: string;
|
||||||
|
configStop: string;
|
||||||
|
configStartup: string;
|
||||||
|
configFiles: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
const params = useParams<{ nestId: string }>();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const { clearFlashes, clearAndAddHttpError } = useFlash();
|
||||||
|
|
||||||
|
const ref = useRef<EggProcessContainerRef>();
|
||||||
|
|
||||||
|
const submit = async (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
|
||||||
|
clearFlashes('egg:create');
|
||||||
|
|
||||||
|
const nestId = Number(params.nestId);
|
||||||
|
|
||||||
|
values.configStartup = await ref.current?.getStartupConfiguration() || '';
|
||||||
|
values.configFiles = await ref.current?.getFilesConfiguration() || '';
|
||||||
|
|
||||||
|
createEgg({ ...values, dockerImages: values.dockerImages.split('\n'), nestId })
|
||||||
|
.then(egg => history.push(`/admin/nests/${nestId}/eggs/${egg.id}`))
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
clearAndAddHttpError({ key: 'egg:create', error });
|
||||||
|
})
|
||||||
|
.then(() => setSubmitting(false));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminContentBlock title={'New Egg'}>
|
<AdminContentBlock title={'New Egg'}>
|
||||||
<div css={tw`w-full flex flex-row items-center mb-8`}>
|
<div css={tw`w-full flex flex-row items-center mb-8`}>
|
||||||
|
@ -11,6 +54,48 @@ export default () => {
|
||||||
<p css={tw`text-base text-neutral-400 whitespace-nowrap overflow-ellipsis overflow-hidden`}>Add a new egg to the panel.</p>
|
<p css={tw`text-base text-neutral-400 whitespace-nowrap overflow-ellipsis overflow-hidden`}>Add a new egg to the panel.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<FlashMessageRender key={'egg:create'} css={tw`mb-4`}/>
|
||||||
|
|
||||||
|
<Formik
|
||||||
|
onSubmit={submit}
|
||||||
|
initialValues={{
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
startup: '',
|
||||||
|
dockerImages: '',
|
||||||
|
configStop: '',
|
||||||
|
configStartup: '{}',
|
||||||
|
configFiles: '{}',
|
||||||
|
}}
|
||||||
|
validationSchema={object().shape({
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{({ isSubmitting, isValid }) => (
|
||||||
|
<Form>
|
||||||
|
<div css={tw`grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6 mb-6`}>
|
||||||
|
<EggInformationContainer/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EggStartupContainer css={tw`mb-6`}/>
|
||||||
|
|
||||||
|
<div css={tw`grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6 mb-6`}>
|
||||||
|
<EggImageContainer/>
|
||||||
|
<EggLifecycleContainer/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EggProcessContainer ref={ref} css={tw`mb-6`}/>
|
||||||
|
|
||||||
|
<div css={tw`bg-neutral-700 rounded shadow-md py-2 px-6 mb-16`}>
|
||||||
|
<div css={tw`flex flex-row`}>
|
||||||
|
<Button type="submit" size="small" css={tw`ml-auto`} disabled={isSubmitting || !isValid}>
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
</AdminContentBlock>
|
</AdminContentBlock>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@ import tw from 'twin.macro';
|
||||||
import { object } from 'yup';
|
import { object } from 'yup';
|
||||||
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
||||||
|
|
||||||
function EggInformationContainer () {
|
export function EggInformationContainer () {
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -72,7 +72,7 @@ function EggDetailsContainer ({ egg }: { egg: Egg }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function EggStartupContainer ({ className }: { className?: string }) {
|
export function EggStartupContainer ({ className }: { className?: string }) {
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -90,7 +90,7 @@ function EggStartupContainer ({ className }: { className?: string }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function EggImageContainer () {
|
export function EggImageContainer () {
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -107,7 +107,7 @@ function EggImageContainer () {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function EggLifecycleContainer () {
|
export function EggLifecycleContainer () {
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -115,8 +115,8 @@ function EggLifecycleContainer () {
|
||||||
<SpinnerOverlay visible={isSubmitting}/>
|
<SpinnerOverlay visible={isSubmitting}/>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
id={'stopCommand'}
|
id={'configStop'}
|
||||||
name={'stopCommand'}
|
name={'configStop'}
|
||||||
label={'Stop Command'}
|
label={'Stop Command'}
|
||||||
type={'text'}
|
type={'text'}
|
||||||
css={tw`mb-1`}
|
css={tw`mb-1`}
|
||||||
|
@ -127,17 +127,16 @@ function EggLifecycleContainer () {
|
||||||
|
|
||||||
interface EggProcessContainerProps {
|
interface EggProcessContainerProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
egg: Egg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EggProcessContainerRef {
|
export interface EggProcessContainerRef {
|
||||||
getStartupConfiguration: () => Promise<string | null>;
|
getStartupConfiguration: () => Promise<string | null>;
|
||||||
getFilesConfiguration: () => Promise<string | null>;
|
getFilesConfiguration: () => Promise<string | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EggProcessContainer = forwardRef<any, EggProcessContainerProps>(
|
export const EggProcessContainer = forwardRef<any, EggProcessContainerProps>(
|
||||||
function EggProcessContainer ({ className, egg }, ref) {
|
function EggProcessContainer ({ className }, ref) {
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting, values } = useFormikContext<Values>();
|
||||||
|
|
||||||
let fetchStartupConfiguration: (() => Promise<string>) | null = null;
|
let fetchStartupConfiguration: (() => Promise<string>) | null = null;
|
||||||
let fetchFilesConfiguration: (() => Promise<string>) | null = null;
|
let fetchFilesConfiguration: (() => Promise<string>) | null = null;
|
||||||
|
@ -166,7 +165,7 @@ const EggProcessContainer = forwardRef<any, EggProcessContainerProps>(
|
||||||
<Label>Startup Configuration</Label>
|
<Label>Startup Configuration</Label>
|
||||||
<Editor
|
<Editor
|
||||||
mode={jsonLanguage}
|
mode={jsonLanguage}
|
||||||
initialContent={JSON.stringify(egg.configStartup, null, '\t') || ''}
|
initialContent={values.configStartup}
|
||||||
overrides={tw`h-32 rounded`}
|
overrides={tw`h-32 rounded`}
|
||||||
fetchContent={value => {
|
fetchContent={value => {
|
||||||
fetchStartupConfiguration = value;
|
fetchStartupConfiguration = value;
|
||||||
|
@ -178,7 +177,7 @@ const EggProcessContainer = forwardRef<any, EggProcessContainerProps>(
|
||||||
<Label>Configuration Files</Label>
|
<Label>Configuration Files</Label>
|
||||||
<Editor
|
<Editor
|
||||||
mode={jsonLanguage}
|
mode={jsonLanguage}
|
||||||
initialContent={JSON.stringify(egg.configFiles, null, '\t') || ''}
|
initialContent={values.configFiles}
|
||||||
overrides={tw`h-48 rounded`}
|
overrides={tw`h-48 rounded`}
|
||||||
fetchContent={value => {
|
fetchContent={value => {
|
||||||
fetchFilesConfiguration = value;
|
fetchFilesConfiguration = value;
|
||||||
|
@ -195,7 +194,7 @@ interface Values {
|
||||||
description: string;
|
description: string;
|
||||||
startup: string;
|
startup: string;
|
||||||
dockerImages: string;
|
dockerImages: string;
|
||||||
stopCommand: string;
|
configStop: string;
|
||||||
configStartup: string;
|
configStartup: string;
|
||||||
configFiles: string;
|
configFiles: string;
|
||||||
}
|
}
|
||||||
|
@ -229,9 +228,9 @@ export default function EggSettingsContainer ({ egg }: { egg: Egg }) {
|
||||||
description: egg.description || '',
|
description: egg.description || '',
|
||||||
startup: egg.startup,
|
startup: egg.startup,
|
||||||
dockerImages: egg.dockerImages.join('\n'),
|
dockerImages: egg.dockerImages.join('\n'),
|
||||||
stopCommand: egg.configStop || '',
|
configStop: egg.configStop || '',
|
||||||
configStartup: '',
|
configStartup: JSON.stringify(egg.configStartup, null, '\t') || '',
|
||||||
configFiles: '',
|
configFiles: JSON.stringify(egg.configFiles, null, '\t') || '',
|
||||||
}}
|
}}
|
||||||
validationSchema={object().shape({
|
validationSchema={object().shape({
|
||||||
})}
|
})}
|
||||||
|
@ -250,17 +249,13 @@ export default function EggSettingsContainer ({ egg }: { egg: Egg }) {
|
||||||
<EggLifecycleContainer/>
|
<EggLifecycleContainer/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EggProcessContainer
|
<EggProcessContainer ref={ref} css={tw`mb-6`}/>
|
||||||
ref={ref}
|
|
||||||
egg={egg}
|
|
||||||
css={tw`mb-6`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div css={tw`bg-neutral-700 rounded shadow-md py-2 px-6 mb-16`}>
|
<div css={tw`bg-neutral-700 rounded shadow-md py-2 px-6 mb-16`}>
|
||||||
<div css={tw`flex flex-row`}>
|
<div css={tw`flex flex-row`}>
|
||||||
<EggDeleteButton
|
<EggDeleteButton
|
||||||
eggId={egg.id}
|
eggId={egg.id}
|
||||||
onDeleted={() => history.push('/admin/nests')}
|
onDeleted={() => history.push(`/admin/nests/${egg.nestId}`)}
|
||||||
/>
|
/>
|
||||||
<Button type="submit" size="small" css={tw`ml-auto`} disabled={isSubmitting || !isValid}>
|
<Button type="submit" size="small" css={tw`ml-auto`} disabled={isSubmitting || !isValid}>
|
||||||
Save Changes
|
Save Changes
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import deleteEggVariable from '@/api/admin/eggs/deleteEggVariable';
|
import deleteEggVariable from '@/api/admin/eggs/deleteEggVariable';
|
||||||
|
import { NoItems } from '@/components/admin/AdminTable';
|
||||||
import ConfirmationModal from '@/components/elements/ConfirmationModal';
|
import ConfirmationModal from '@/components/elements/ConfirmationModal';
|
||||||
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
@ -160,28 +161,32 @@ export default function EggVariablesContainer ({ egg }: { egg: Egg }) {
|
||||||
{({ isSubmitting, isValid }) => (
|
{({ isSubmitting, isValid }) => (
|
||||||
<Form>
|
<Form>
|
||||||
<div css={tw`flex flex-col mb-16`}>
|
<div css={tw`flex flex-col mb-16`}>
|
||||||
<div css={tw`grid grid-cols-1 lg:grid-cols-2 gap-x-8 gap-y-6`}>
|
{egg.relations?.variables?.length === 0 ?
|
||||||
{egg.relations?.variables?.map((v, i) => (
|
<NoItems css={tw`bg-neutral-700 rounded-md shadow-md`}/>
|
||||||
<EggVariableBox
|
:
|
||||||
key={i}
|
<div css={tw`grid grid-cols-1 lg:grid-cols-2 gap-x-8 gap-y-6`}>
|
||||||
prefix={`[${i}].`}
|
{egg.relations?.variables?.map((v, i) => (
|
||||||
variable={v}
|
<EggVariableBox
|
||||||
onDeleteClick={(success) => {
|
key={i}
|
||||||
deleteEggVariable(egg.id, v.id)
|
prefix={`[${i}].`}
|
||||||
.then(async () => {
|
variable={v}
|
||||||
await mutate(egg => ({
|
onDeleteClick={(success) => {
|
||||||
...egg!,
|
deleteEggVariable(egg.id, v.id)
|
||||||
relations: {
|
.then(async () => {
|
||||||
variables: egg!.relations.variables!.filter(v2 => v.id === v2.id),
|
await mutate(egg => ({
|
||||||
},
|
...egg!,
|
||||||
}));
|
relations: {
|
||||||
success();
|
variables: egg!.relations.variables!.filter(v2 => v.id === v2.id),
|
||||||
})
|
},
|
||||||
.catch(error => clearAndAddHttpError({ key: 'egg', error }));
|
}));
|
||||||
}}
|
success();
|
||||||
/>
|
})
|
||||||
))}
|
.catch(error => clearAndAddHttpError({ key: 'egg', error }));
|
||||||
</div>
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div css={tw`bg-neutral-700 rounded shadow-md py-2 px-4 mt-6`}>
|
<div css={tw`bg-neutral-700 rounded shadow-md py-2 px-4 mt-6`}>
|
||||||
<div css={tw`flex flex-row`}>
|
<div css={tw`flex flex-row`}>
|
||||||
|
|
|
@ -80,7 +80,7 @@ export default function NewVariableButton ({ eggId }: { eggId: number }) {
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|
||||||
<Button type={'button'} color={'green'} onClick={() => setVisible(true)}>
|
<Button type={'button'} color={'green'} onClick={() => setVisible(true)}>
|
||||||
Add a fucking variable
|
New Variable
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue