From df895f4a9f495e40a655a984fe4c5ce36bf228c0 Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Thu, 16 Sep 2021 14:59:22 -0600 Subject: [PATCH] ui(admin): server edit cleanup, fix startup form --- .../Servers/UpdateServerRequest.php | 42 +-- .../Servers/UpdateServerStartupRequest.php | 18 +- .../Api/Application/ServerTransformer.php | 2 +- .../scripts/api/admin/servers/getServers.ts | 6 +- .../scripts/api/admin/servers/updateServer.ts | 62 ++-- .../api/admin/servers/updateServerStartup.ts | 28 ++ .../components/admin/servers/EggSelect.tsx | 33 +- .../components/admin/servers/ServerRouter.tsx | 4 +- .../admin/servers/ServerSettingsContainer.tsx | 286 +++++++++--------- .../admin/servers/ServerStartupContainer.tsx | 259 +++++++++------- .../components/elements/FormikSwitch.tsx | 2 +- routes/api-application.php | 2 +- 12 files changed, 422 insertions(+), 322 deletions(-) create mode 100644 resources/scripts/api/admin/servers/updateServerStartup.ts diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerRequest.php index 8d22a986..37637d66 100644 --- a/app/Http/Requests/Api/Application/Servers/UpdateServerRequest.php +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerRequest.php @@ -16,18 +16,20 @@ class UpdateServerRequest extends ApplicationApiRequest 'name' => $rules['name'], 'description' => array_merge(['nullable'], $rules['description']), 'owner_id' => $rules['owner_id'], - 'oom_killer' => 'sometimes|boolean', - 'memory' => $rules['memory'], - 'swap' => $rules['swap'], - 'disk' => $rules['disk'], - 'io' => $rules['io'], - 'threads' => $rules['threads'], - 'cpu' => $rules['cpu'], + 'limits' => 'sometimes|array', + 'limits.memory' => $rules['memory'], + 'limits.swap' => $rules['swap'], + 'limits.disk' => $rules['disk'], + 'limits.io' => $rules['io'], + 'limits.threads' => $rules['threads'], + 'limits.cpu' => $rules['cpu'], + 'limits.oom_killer' => 'sometimes|boolean', - 'databases' => $rules['database_limit'], - 'allocations' => $rules['allocation_limit'], - 'backups' => $rules['backup_limit'], + 'feature_limits' => 'required|array', + 'feature_limits.allocations' => $rules['allocation_limit'], + 'feature_limits.backups' => $rules['backup_limit'], + 'feature_limits.databases' => $rules['database_limit'], 'allocation_id' => 'bail|exists:allocations,id', 'add_allocations' => 'bail|array', @@ -46,18 +48,18 @@ class UpdateServerRequest extends ApplicationApiRequest 'name' => array_get($data, 'name'), 'description' => array_get($data, 'description'), 'owner_id' => array_get($data, 'owner_id'), - 'oom_disabled' => !array_get($data, 'oom_killer'), - 'memory' => array_get($data, 'memory'), - 'swap' => array_get($data, 'swap'), - 'disk' => array_get($data, 'disk'), - 'io' => array_get($data, 'io'), - 'threads' => array_get($data, 'threads'), - 'cpu' => array_get($data, 'cpu'), + 'memory' => array_get($data, 'limits.memory'), + 'swap' => array_get($data, 'limits.swap'), + 'disk' => array_get($data, 'limits.disk'), + 'io' => array_get($data, 'limits.io'), + 'threads' => array_get($data, 'limits.threads'), + 'cpu' => array_get($data, 'limits.cpu'), + 'oom_disabled' => array_get($data, 'limits.oom_disabled'), - 'database_limit' => array_get($data, 'databases'), - 'allocation_limit' => array_get($data, 'allocations'), - 'backup_limit' => array_get($data, 'backups'), + 'allocation_limit' => array_get($data, 'feature_limits.allocations'), + 'backup_limit' => array_get($data, 'feature_limits.backups'), + 'database_limit' => array_get($data, 'feature_limits.databases'), 'allocation_id' => array_get($data, 'allocation_id'), 'add_allocations' => array_get($data, 'add_allocations'), diff --git a/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php b/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php index 05d9bb0f..d8f92a1f 100644 --- a/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php +++ b/app/Http/Requests/Api/Application/Servers/UpdateServerStartupRequest.php @@ -9,24 +9,14 @@ class UpdateServerStartupRequest extends ApplicationApiRequest { public function rules(): array { - $data = Server::getRulesForUpdate($this->route()->parameter('server')->id); + $rules = Server::getRulesForUpdate($this->route()->parameter('server')->id); return [ - 'startup' => $data['startup'], + 'startup' => $rules['startup'], 'environment' => 'present|array', - 'egg' => $data['egg_id'], - 'image' => $data['image'], + 'egg_id' => $rules['egg_id'], + 'image' => $rules['image'], 'skip_scripts' => 'present|boolean', ]; } - - public function validated(): array - { - $data = parent::validated(); - - return collect($data)->only(['startup', 'environment', 'skip_scripts'])->merge([ - 'egg_id' => array_get($data, 'egg'), - 'docker_image' => array_get($data, 'image'), - ])->toArray(); - } } diff --git a/app/Transformers/Api/Application/ServerTransformer.php b/app/Transformers/Api/Application/ServerTransformer.php index d01c7890..f16ad194 100644 --- a/app/Transformers/Api/Application/ServerTransformer.php +++ b/app/Transformers/Api/Application/ServerTransformer.php @@ -69,7 +69,7 @@ class ServerTransformer extends Transformer 'nest_id' => $model->nest_id, 'egg_id' => $model->egg_id, 'container' => [ - 'startup_command' => $model->startup, + 'startup' => $model->startup, 'image' => $model->image, 'environment' => $this->environmentService->handle($model), ], diff --git a/resources/scripts/api/admin/servers/getServers.ts b/resources/scripts/api/admin/servers/getServers.ts index 0c6d49bf..8f40a30f 100644 --- a/resources/scripts/api/admin/servers/getServers.ts +++ b/resources/scripts/api/admin/servers/getServers.ts @@ -71,8 +71,7 @@ export interface Server { eggId: number; container: { - startupCommand: string; - defaultStartup: string; + startup: string; image: string; environment: Map; } @@ -121,8 +120,7 @@ export const rawDataToServer = ({ attributes }: FractalResponseData): Server => eggId: attributes.egg_id, container: { - startupCommand: attributes.container.startup_command, - defaultStartup: '', + startup: attributes.container.startup, image: attributes.container.image, environment: attributes.container.environment, }, diff --git a/resources/scripts/api/admin/servers/updateServer.ts b/resources/scripts/api/admin/servers/updateServer.ts index 47e09a96..b8c59d6c 100644 --- a/resources/scripts/api/admin/servers/updateServer.ts +++ b/resources/scripts/api/admin/servers/updateServer.ts @@ -6,17 +6,21 @@ export interface Values { name: string; ownerId: number; - memory: number; - swap: number; - disk: number; - io: number; - cpu: number; - threads: string; - oomDisabled: boolean; + limits: { + memory: number; + swap: number; + disk: number; + io: number; + cpu: number; + threads: string; + oomDisabled: boolean; + } - databases: number; - allocations: number; - backups: number; + featureLimits: { + allocations: number; + backups: number; + databases: number; + } allocationId: number; addAllocations: number[]; @@ -24,16 +28,36 @@ export interface Values { } export default (id: number, server: Partial, include: string[] = []): Promise => { - const data = {}; - - Object.keys(server).forEach((key) => { - const key2 = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`); - // @ts-ignore - data[key2] = server[key]; - }); - return new Promise((resolve, reject) => { - http.patch(`/api/application/servers/${id}`, data, { params: { include: include.join(',') } }) + http.patch( + `/api/application/servers/${id}`, + { + external_id: server.externalId, + name: server.name, + owner_id: server.ownerId, + + limits: { + memory: server.limits?.memory, + swap: server.limits?.swap, + disk: server.limits?.disk, + io: server.limits?.io, + cpu: server.limits?.cpu, + threads: server.limits?.threads, + oom_disabled: server.limits?.oomDisabled, + }, + + feature_limits: { + allocations: server.featureLimits?.allocations, + backups: server.featureLimits?.backups, + databases: server.featureLimits?.databases, + }, + + allocation_id: server.allocationId, + add_allocations: server.addAllocations, + remove_allocations: server.removeAllocations, + }, + { params: { include: include.join(',') } } + ) .then(({ data }) => resolve(rawDataToServer(data))) .catch(reject); }); diff --git a/resources/scripts/api/admin/servers/updateServerStartup.ts b/resources/scripts/api/admin/servers/updateServerStartup.ts new file mode 100644 index 00000000..33f598f1 --- /dev/null +++ b/resources/scripts/api/admin/servers/updateServerStartup.ts @@ -0,0 +1,28 @@ +import http from '@/api/http'; +import { Server, rawDataToServer } from '@/api/admin/servers/getServers'; + +export interface Values { + startup: string; + environment: Record; + eggId: number; + image: string; + skipScripts: boolean; +} + +export default (id: number, values: Partial, include: string[] = []): Promise => { + return new Promise((resolve, reject) => { + http.patch( + `/api/application/servers/${id}/startup`, + { + startup: values.startup, + environment: values.environment, + egg_id: values.eggId, + image: values.image, + skip_scripts: values.skipScripts, + }, + { params: { include: include.join(',') } } + ) + .then(({ data }) => resolve(rawDataToServer(data))) + .catch(reject); + }); +}; diff --git a/resources/scripts/components/admin/servers/EggSelect.tsx b/resources/scripts/components/admin/servers/EggSelect.tsx index e042d370..115633ca 100644 --- a/resources/scripts/components/admin/servers/EggSelect.tsx +++ b/resources/scripts/components/admin/servers/EggSelect.tsx @@ -1,12 +1,39 @@ import Label from '@/components/elements/Label'; import Select from '@/components/elements/Select'; +import { useFormikContext } from 'formik'; import React, { useEffect, useState } from 'react'; import { Egg } from '@/api/admin/eggs/getEgg'; import searchEggs from '@/api/admin/nests/searchEggs'; export default ({ nestId, egg, setEgg }: { nestId: number | null; egg: Egg | null, setEgg: (value: Egg | null) => void }) => { + const { setFieldValue } = useFormikContext(); + const [ eggs, setEggs ] = useState([]); + /** + * So you may be asking yourself, "what cluster-fuck of code is this?" + * + * Well, this code makes sure that when the egg changes, that the environment + * object has empty string values instead of undefined so React doesn't think + * the variable fields are uncontrolled. + */ + const setEgg2 = (newEgg: Egg | null) => { + if (newEgg === null) { + setEgg(null); + return; + } + + // Reset all variables to be empty, don't inherit the previous values. + const newVariables = newEgg?.relations.variables; + newVariables?.forEach(v => setFieldValue('environment.' + v.envVariable, '')); + const variables = egg?.relations.variables?.filter(v => newVariables?.find(v2 => v2.envVariable === v.envVariable) === undefined); + + setEgg(newEgg); + + // Clear any variables that don't exist on the new egg. + variables?.forEach(v => setFieldValue('environment.' + v.envVariable, undefined)); + }; + useEffect(() => { if (nestId === null) { return; @@ -16,10 +43,10 @@ export default ({ nestId, egg, setEgg }: { nestId: number | null; egg: Egg | nul .then(eggs => { setEggs(eggs); if (eggs.length < 1) { - setEgg(null); + setEgg2(null); return; } - setEgg(eggs[0]); + setEgg2(eggs[0]); }) .catch(error => console.error(error)); }, [ nestId ]); @@ -31,7 +58,7 @@ export default ({ nestId, egg, setEgg }: { nestId: number | null; egg: Egg | nul defaultValue={egg?.id || undefined} id={'eggId'} name={'eggId'} - onChange={e => setEgg(eggs.find(egg => egg.id.toString() === e.currentTarget.value) || null)} + onChange={e => setEgg2(eggs.find(egg => egg.id.toString() === e.currentTarget.value) || null)} > {eggs.map(v => (