ui(admin): start work on server startup settings

This commit is contained in:
Matthew Penner 2021-09-12 23:50:12 -06:00
parent 6362731d55
commit a615b7fa70
No known key found for this signature in database
GPG key ID: 030E4AB751DC756F
7 changed files with 239 additions and 157 deletions

View file

@ -39,8 +39,7 @@ rules:
comma-dangle:
- warn
- always-multiline
spaced-comment:
- warn
spaced-comment: 0
array-bracket-spacing:
- warn
- always

View file

@ -0,0 +1,24 @@
import http from '@/api/http';
import { Egg, rawDataToEgg } from '@/api/admin/eggs/getEgg';
interface Filters {
name?: string;
}
export default (nestId: number, filters?: Filters): Promise<Egg[]> => {
const params = {};
if (filters !== undefined) {
Object.keys(filters).forEach(key => {
// @ts-ignore
params['filter[' + key + ']'] = filters[key];
});
}
return new Promise((resolve, reject) => {
http.get(`/api/application/nests/${nestId}/eggs`, { params: { ...params } })
.then(response => resolve(
(response.data.data || []).map(rawDataToEgg)
))
.catch(reject);
});
};

View file

@ -0,0 +1,24 @@
import http from '@/api/http';
import { Nest, rawDataToNest } from '@/api/admin/nests/getNests';
interface Filters {
name?: string;
}
export default (filters?: Filters): Promise<Nest[]> => {
const params = {};
if (filters !== undefined) {
Object.keys(filters).forEach(key => {
// @ts-ignore
params['filter[' + key + ']'] = filters[key];
});
}
return new Promise((resolve, reject) => {
http.get('/api/application/nests', { params: { ...params } })
.then(response => resolve(
(response.data.data || []).map(rawDataToNest)
))
.catch(reject);
});
};

View file

@ -0,0 +1,32 @@
import Label from '@/components/elements/Label';
import Select from '@/components/elements/Select';
import React, { useEffect, useState } from 'react';
import { Egg } from '@/api/admin/eggs/getEgg';
import searchEggs from '@/api/admin/nests/searchEggs';
export default ({ nestId, eggId }: { nestId: number | null; eggId?: number }) => {
const [ eggs, setEggs ] = useState<Egg[]>([]);
useEffect(() => {
if (nestId === null) {
return;
}
searchEggs(nestId, {})
.then(eggs => setEggs(eggs))
.catch(error => console.error(error));
}, [ nestId ]);
return (
<>
<Label>Egg</Label>
<Select defaultValue={eggId || undefined} id={'eggId'} name={'eggId'}>
{eggs.map(v => (
<option key={v.id} value={v.id.toString()}>
{v.name}
</option>
))}
</Select>
</>
);
};

View file

@ -0,0 +1,30 @@
import Label from '@/components/elements/Label';
import Select from '@/components/elements/Select';
import React, { useEffect, useState } from 'react';
import { Nest } from '@/api/admin/nests/getNests';
import searchNests from '@/api/admin/nests/searchNests';
export default ({ nestId, setNestId }: { nestId: number | null; setNestId: (value: number | null) => void }) => {
const [ nests, setNests ] = useState<Nest[] | null>(null);
useEffect(() => {
console.log(nestId || undefined);
searchNests({})
.then(nests => setNests(nests))
.catch(error => console.error(error));
}, []);
return (
<>
<Label>Nest</Label>
<Select value={nestId || undefined} onChange={e => setNestId(Number(e.currentTarget.value))}>
{nests?.map(v => (
<option key={v.id} value={v.id.toString()}>
{v.name}
</option>
))}
</Select>
</>
);
};

View file

@ -135,7 +135,7 @@ export function ServerResourceContainer () {
<FormikSwitch
name={'oomKiller'}
label={'Out of Memory Killer'}
description={'Enabling OOM killer may cause server processes to exit unexpectedly. '}
description={'Enabling OOM killer may cause server processes to exit unexpectedly.'}
/>
</div>
</div>

View file

@ -1,153 +1,112 @@
import React from 'react';
import Button from '@/components/elements/Button';
import EggSelect from '@/components/admin/servers/EggSelect';
import NestSelect from '@/components/admin/servers/NestSelect';
import FormikSwitch from '@/components/elements/FormikSwitch';
import React, { useState } from 'react';
import Button from '@/components/elements/Button';
import Input from '@/components/elements/Input';
import AdminBox from '@/components/admin/AdminBox';
import tw from 'twin.macro';
import { object } from 'yup';
import updateServer from '@/api/admin/servers/updateServer';
import Field from '@/components/elements/Field';
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
import { Form, Formik, useFormikContext } from 'formik';
import { Context } from '@/components/admin/servers/ServerRouter';
import { ApplicationStore } from '@/state';
import { Actions, useStoreActions } from 'easy-peasy';
import Label from '@/components/elements/Label';
// import { ServerEggVariable } from '@/api/server/types';
/* interface Props {
variable: ServerEggVariable;
} */
// interface Values {
// startupCommand: string;
// image: string;
//
// eggId: number;
// skipScripts: boolean;
// }
interface Values {
startupCommand: string;
nestId: number;
eggId: number;
}
/* const VariableBox = ({ variable }: Props) => {
function ServerStartupLineContainer () {
const { isSubmitting } = useFormikContext();
const server = Context.useStoreState(state => state.server);
if (server === undefined) {
return (
<></>
);
}
return (
<AdminBox title={'Service Configuration'} css={tw`relative w-full`}>
<SpinnerOverlay visible={isSubmitting}/>
<Form css={tw`mb-0`}>
<div css={tw`md:w-full md:flex md:flex-col`}>
<Field
name={variable.envVariable}
defaultValue={variable.serverValue}
placeholder={variable.defaultValue}
description={variable.description}
/>
</div>
</Form>
</AdminBox>
);
}; */
const ServerServiceContainer = () => {
const { isSubmitting } = useFormikContext();
const server = Context.useStoreState(state => state.server);
if (server === undefined) {
return (
<></>
);
}
return (
<AdminBox title={'Service Configuration'} css={tw`relative w-full`}>
<SpinnerOverlay visible={isSubmitting}/>
<Form css={tw`mb-0`}>
<div css={tw`md:w-full md:flex md:flex-col`}>
<div css={tw`flex-1`}>
<div css={tw`p-3 mb-6 border-l-4 border-red-500`}>
<p css={tw`text-xs text-neutral-200`}>
This is a destructive operation in many cases. This server will be stopped immediately in order for this action to proceed.
</p>
</div>
<div css={tw`p-3 mb-6 border-l-4 border-red-500`}>
<p css={tw`text-xs text-neutral-200`}>
Changing any of the below values will result in the server processing a re-install command. The server will be stopped and will then proceed. If you would like the service scripts to not run, ensure the box is checked at the bottom.
</p>
</div>
</div>
<div css={tw`pb-4 mb-6 md:w-full md:flex md:flex-col md:mb-0`}>
Nest/Egg Selector HERE
</div>
<div css={tw`pb-4 mb-6 md:w-full md:flex md:flex-col md:mb-0`}>
<div css={tw`mt-6 bg-neutral-800 border border-neutral-900 shadow-inner p-4 rounded`}>
<FormikSwitch
name={'skip_install_script'}
label={'Skip Egg Install Script'}
description={'If the selected Egg has an install script attached to it, the script will run during install. If you would like to skip this step, check this box.'}
/>
</div>
</div>
</div>
</Form>
</AdminBox>
);
};
const ServerStartupContainer = () => {
const { isSubmitting } = useFormikContext();
const server = Context.useStoreState(state => state.server);
if (server === undefined) {
return (
<></>
);
}
return (
<AdminBox title={'Startup Command'} css={tw`relative w-full`}>
<SpinnerOverlay visible={isSubmitting}/>
<Form css={tw`mb-0`}>
<div css={tw`mb-6 md:w-full md:flex md:flex-col`}>
<div css={tw`mb-6 md:w-full md:flex md:flex-col md:mr-4`}>
<Field
id={'startupCommand'}
name={'startupCommand'}
label={'Startup Command'}
type={'string'}
description={'Edit your server\'s startup command here. The following variables are available by default: {{SERVER_MEMORY}}, {{SERVER_IP}}, and {{SERVER_PORT}}.'}
/>
</div>
<div css={tw`mb-6`}>
<Field
id={'startupCommand'}
name={'startupCommand'}
label={'Startup Command'}
type={'text'}
description={'Edit your server\'s startup command here. The following variables are available by default: {{SERVER_MEMORY}}, {{SERVER_IP}}, and {{SERVER_PORT}}.'}
/>
</div>
<div css={tw`mb-6 md:w-full md:flex md:flex-col md:mb-0`}>
<div>
<Label>Default Startup Command</Label>
<Input
disabled
value={server.relations.egg?.configStartup || ''}
/>
</div>
<div>
<Label>Default Startup Command</Label>
<Input disabled/>
</div>
</Form>
</AdminBox>
);
}
function ServerServiceContainer ({ nestId: nestId2, eggId }: { nestId: number | null; eggId: number | null }) {
const { isSubmitting } = useFormikContext();
const [ nestId, setNestId ] = useState<number | null>(nestId2);
return (
<AdminBox title={'Service Configuration'} css={tw`relative w-full`}>
<SpinnerOverlay visible={isSubmitting}/>
<Form css={tw`mb-0`}>
<div css={tw`mb-6`}>
<NestSelect nestId={nestId} setNestId={setNestId}/>
</div>
<div css={tw`mb-6`}>
<EggSelect nestId={nestId} eggId={eggId || undefined}/>
</div>
<div css={tw`bg-neutral-800 border border-neutral-900 shadow-inner p-4 rounded`}>
<FormikSwitch
name={'skipScript'}
label={'Skip Egg Install Script'}
description={'SoonTM'}
/>
</div>
</Form>
</AdminBox>
);
}
function ServerImageContainer () {
const { isSubmitting } = useFormikContext();
return (
<AdminBox title={'Image Configuration'} css={tw`relative w-full`}>
<SpinnerOverlay visible={isSubmitting}/>
<Form css={tw`mb-0`}>
<div css={tw`md:w-full md:flex md:flex-col`}>
<div>
<Field
id={'image'}
name={'image'}
label={'Docker Image'}
type={'text'}
/>
</div>
</div>
</Form>
</AdminBox>
);
};
}
export default () => {
const { clearFlashes, clearAndAddHttpError } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
export default function ServerStartupContainer () {
const { clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
const server = Context.useStoreState(state => state.server);
const setServer = Context.useStoreActions(actions => actions.setServer);
if (server === undefined) {
return (
@ -155,16 +114,8 @@ export default () => {
);
}
const submit = (values: Values, { setSubmitting }: FormikHelpers<Values>) => {
const submit = () => {
clearFlashes('server');
// updateServer(server.id, values)
// .then(() => setServer({ ...server, ...values }))
// .catch(error => {
// console.error(error);
// clearAndAddHttpError({ key: 'server', error });
// })
// .then(() => setSubmitting(false));
};
return (
@ -172,34 +123,56 @@ export default () => {
onSubmit={submit}
initialValues={{
startupCommand: server.container.startupCommand,
nestId: server.nestId,
eggId: server.eggId,
image: server.container.image,
eggId: 0,
skipScripts: false,
}}
validationSchema={object().shape({
})}
validationSchema={object().shape({})}
>
{
({ isSubmitting, isValid }) => (
<div css={tw`flex flex-col`}>
<div css={tw`flex flex-col w-full mb-4 mr-0 lg:mr-2`}>
<ServerStartupContainer/>
{({ isSubmitting, isValid }) => (
<div css={tw`flex flex-col`}>
<div css={tw`flex flex-row mb-8`}>
<ServerStartupLineContainer/>
</div>
<div css={tw`grid grid-cols-2 gap-x-8 mb-8`}>
<div css={tw`flex`}>
<ServerServiceContainer nestId={server?.nestId || null} eggId={server?.eggId || null}/>
</div>
<div css={tw`flex flex-col w-1/2 mr-0 lg:mr-2`}>
<ServerServiceContainer/>
</div>
<div css={tw`flex flex-col w-1/2 mr-0 lg:mr-2`}>
Server Startup variables go here
</div>
<div css={tw`py-2 pr-6 mt-4 rounded shadow-md bg-neutral-700`}>
<div css={tw`flex flex-row`}>
<Button type="submit" size="small" css={tw`ml-auto`} disabled={isSubmitting || !isValid}>
Save Changes
</Button>
</div>
<div css={tw`flex`}>
<ServerImageContainer/>
</div>
</div>
)
}
{/*<div css={tw`grid gap-8 md:grid-cols-2`}>*/}
{/* {variables.map((variable, i) => (*/}
{/* <TitledGreyBox*/}
{/* key={i}*/}
{/* title={<p css={tw`text-sm uppercase`}>{variable.name}</p>}*/}
{/* >*/}
{/* <InputSpinner visible={false}>*/}
{/* <Input*/}
{/* name={variable.envVariable}*/}
{/* defaultValue={variable.serverValue}*/}
{/* placeholder={variable.defaultValue}*/}
{/* />*/}
{/* </InputSpinner>*/}
{/* <p css={tw`mt-1 text-xs text-neutral-300`}>*/}
{/* {variable.description}*/}
{/* </p>*/}
{/* </TitledGreyBox>*/}
{/* ))}*/}
{/*</div>*/}
<div css={tw`py-2 pr-6 mt-4 rounded shadow-md bg-neutral-700`}>
<div css={tw`flex flex-row`}>
<Button type="submit" size="small" css={tw`ml-auto`} disabled={isSubmitting || !isValid}>
Save Changes
</Button>
</div>
</div>
</div>
)}
</Formik>
);
};
}