2021-10-23 21:19:41 +00:00
import { Egg } from '@/api/admin/egg' ;
import AdminBox from '@/components/admin/AdminBox' ;
2021-10-24 20:14:04 +00:00
import NodeSelect from '@/components/admin/servers/NodeSelect' ;
2021-10-23 21:19:41 +00:00
import { ServerImageContainer , ServerServiceContainer , ServerVariableContainer } from '@/components/admin/servers/ServerStartupContainer' ;
import BaseSettingsBox from '@/components/admin/servers/settings/BaseSettingsBox' ;
import FeatureLimitsBox from '@/components/admin/servers/settings/FeatureLimitsBox' ;
import ServerResourceBox from '@/components/admin/servers/settings/ServerResourceBox' ;
import Button from '@/components/elements/Button' ;
import Field from '@/components/elements/Field' ;
2021-10-24 20:14:04 +00:00
import FormikSwitch from '@/components/elements/FormikSwitch' ;
2021-10-23 21:19:41 +00:00
import Label from '@/components/elements/Label' ;
import Select from '@/components/elements/Select' ;
import SpinnerOverlay from '@/components/elements/SpinnerOverlay' ;
import FlashMessageRender from '@/components/FlashMessageRender' ;
2021-10-29 06:04:28 +00:00
import useFlash from '@/plugins/useFlash' ;
2021-10-23 21:19:41 +00:00
import { faNetworkWired } from '@fortawesome/free-solid-svg-icons' ;
2021-10-24 20:14:04 +00:00
import { Form , Formik , FormikHelpers , useFormikContext } from 'formik' ;
2021-10-29 06:04:28 +00:00
import React , { useEffect , useState } from 'react' ;
import { useHistory } from 'react-router-dom' ;
2021-01-05 21:52:49 +00:00
import tw from 'twin.macro' ;
import AdminContentBlock from '@/components/admin/AdminContentBlock' ;
2021-10-23 21:19:41 +00:00
import { object } from 'yup' ;
2021-10-29 06:04:28 +00:00
import createServer , { CreateServerRequest } from '@/api/admin/servers/createServer' ;
import { Allocation , Node , getAllocations } from '@/api/admin/node' ;
2021-10-24 20:14:04 +00:00
function InternalForm ( ) {
2021-10-29 06:04:28 +00:00
const { isSubmitting , isValid , setFieldValue , values : { environment } } = useFormikContext < CreateServerRequest > ( ) ;
2021-01-05 21:52:49 +00:00
2021-10-23 21:19:41 +00:00
const [ egg , setEgg ] = useState < Egg | null > ( null ) ;
2021-10-29 06:04:28 +00:00
const [ node , setNode ] = useState < Node | null > ( null ) ;
const [ allocations , setAllocations ] = useState < Allocation [ ] | null > ( null ) ;
useEffect ( ( ) = > {
if ( egg === null ) {
return ;
}
setFieldValue ( 'eggId' , egg . id ) ;
2021-11-01 18:23:06 +00:00
setFieldValue ( 'startup' , '' ) ;
2021-10-29 06:04:28 +00:00
setFieldValue ( 'image' , egg . dockerImages . length > 0 ? egg . dockerImages [ 0 ] : '' ) ;
} , [ egg ] ) ;
useEffect ( ( ) = > {
if ( node === null ) {
return ;
}
// server_id: 0 filters out assigned allocations
getAllocations ( node . id , { filters : { server_id : '0' } } )
. then ( setAllocations ) ;
} , [ node ] ) ;
2021-10-23 21:19:41 +00:00
2021-10-24 20:14:04 +00:00
return (
< Form >
< div css = { tw ` grid grid-cols-2 gap-y-6 gap-x-8 mb-16 ` } >
< div css = { tw ` grid grid-cols-1 gap-y-6 col-span-2 md:col-span-1 ` } >
< BaseSettingsBox >
2021-10-29 06:04:28 +00:00
< NodeSelect node = { node } setNode = { setNode } / >
2021-10-24 20:14:04 +00:00
< div css = { tw ` xl:col-span-2 bg-neutral-800 border border-neutral-900 shadow-inner p-4 rounded ` } >
< FormikSwitch
name = { 'startOnCompletion' }
label = { 'Start after installation' }
description = { 'Should the server be automatically started after it has been installed?' }
/ >
< / div >
< / BaseSettingsBox >
< FeatureLimitsBox / >
< ServerServiceContainer
egg = { egg }
setEgg = { setEgg }
2021-10-29 06:04:28 +00:00
nestId = { 0 }
2021-10-24 20:14:04 +00:00
/ >
< / div >
< div css = { tw ` grid grid-cols-1 gap-y-6 col-span-2 md:col-span-1 ` } >
< AdminBox icon = { faNetworkWired } title = { 'Networking' } isLoading = { isSubmitting } >
< div css = { tw ` grid grid-cols-1 gap-4 lg:gap-6 ` } >
< div >
2021-10-29 06:04:28 +00:00
< Label htmlFor = { 'allocation.default' } > Primary Allocation < / Label >
< Select
id = { 'allocation.default' }
name = { 'allocation.default' }
disabled = { node === null }
onChange = { e = > setFieldValue ( 'allocation.default' , Number ( e . currentTarget . value ) ) }
>
{ node === null ? < option value = "" > Select a node . . . < / option > : < option value = "" > Select an allocation . . . < / option > }
{ allocations ? . map ( a = > < option key = { a . id } value = { a . id . toString ( ) } > { a . getDisplayText ( ) } < / option > ) }
2021-10-24 20:14:04 +00:00
< / Select >
< / div >
2021-10-29 06:04:28 +00:00
{ /*<div>*/ }
{ /* /!* TODO: Multi-select *!/*/ }
{ /* <Label htmlFor={'allocation.additional'}>Additional Allocations</Label>*/ }
{ /* <Select id={'allocation.additional'} name={'allocation.additional'} disabled={node === null}>*/ }
{ /* {node === null ? <option value="">Select a node...</option> : <option value="">Select additional allocations...</option>}*/ }
{ /* {allocations?.map(a => <option key={a.id} value={a.id.toString()}>{a.getDisplayText()}</option>)}*/ }
{ /* </Select>*/ }
{ /*</div>*/ }
2021-10-24 20:14:04 +00:00
< / div >
< / AdminBox >
< ServerResourceBox / >
< ServerImageContainer / >
< / div >
< AdminBox title = { 'Startup Command' } css = { tw ` relative w-full col-span-2 ` } >
< SpinnerOverlay visible = { isSubmitting } / >
< Field
id = { 'startup' }
name = { 'startup' }
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}}.' }
placeholder = { egg ? . startup || '' }
/ >
< / AdminBox >
< div css = { tw ` col-span-2 grid grid-cols-1 md:grid-cols-2 gap-y-6 gap-x-8 ` } >
{ /* This ensures that no variables are rendered unless the environment has a value for the variable. */ }
{ egg ? . relationships . variables ? . filter ( v = > Object . keys ( environment ) . find ( e = > e === v . environmentVariable ) !== undefined ) . map ( ( v , i ) = > (
< ServerVariableContainer
key = { i }
variable = { v }
/ >
) ) }
< / div >
< div css = { tw ` bg-neutral-700 rounded shadow-md px-4 py-3 col-span-2 ` } >
< div css = { tw ` flex flex-row ` } >
< Button
type = "submit"
size = "small"
css = { tw ` ml-auto ` }
disabled = { isSubmitting || ! isValid }
>
Create Server
< / Button >
< / div >
< / div >
< / div >
< / Form >
) ;
}
export default ( ) = > {
2021-10-29 06:04:28 +00:00
const history = useHistory ( ) ;
const { clearFlashes , clearAndAddHttpError } = useFlash ( ) ;
2021-10-24 20:14:04 +00:00
const submit = ( r : CreateServerRequest , { setSubmitting } : FormikHelpers < CreateServerRequest > ) = > {
2021-10-29 06:04:28 +00:00
clearFlashes ( 'server:create' ) ;
createServer ( r )
. then ( s = > history . push ( ` /admin/servers/ ${ s . id } ` ) )
. catch ( error = > clearAndAddHttpError ( { key : 'server:create' , error } ) )
. then ( ( ) = > setSubmitting ( false ) ) ;
2021-10-23 21:19:41 +00:00
} ;
2021-01-05 21:52:49 +00:00
return (
2021-01-10 18:34:14 +00:00
< AdminContentBlock title = { 'New Server' } >
2021-01-05 21:52:49 +00:00
< div css = { tw ` w-full flex flex-row items-center mb-8 ` } >
2021-01-10 18:34:14 +00:00
< div css = { tw ` flex flex-col flex-shrink ` } style = { { minWidth : '0' } } >
2021-09-17 19:06:31 +00:00
< h2 css = { tw ` text-2xl text-neutral-50 font-header font-medium ` } > New Server < / h2 >
2021-01-10 18:34:14 +00:00
< p css = { tw ` text-base text-neutral-400 whitespace-nowrap overflow-ellipsis overflow-hidden ` } > Add a new server to the panel . < / p >
2021-01-05 21:52:49 +00:00
< / div >
< / div >
2021-10-23 21:19:41 +00:00
< FlashMessageRender byKey = { 'server:create' } css = { tw ` mb-4 ` } / >
< Formik
onSubmit = { submit }
initialValues = { {
externalId : '' ,
name : '' ,
2021-10-24 20:14:04 +00:00
description : '' ,
2021-10-23 21:19:41 +00:00
ownerId : 0 ,
2021-10-24 20:14:04 +00:00
nodeId : 0 ,
2021-10-23 21:19:41 +00:00
limits : {
2021-10-24 20:14:04 +00:00
memory : 1024 ,
2021-10-23 21:19:41 +00:00
swap : 0 ,
2021-10-24 20:14:04 +00:00
disk : 4096 ,
io : 500 ,
2021-10-23 21:19:41 +00:00
cpu : 0 ,
threads : '' ,
// This value is inverted to have the switch be on when the
// OOM Killer is enabled, rather than when disabled.
oomDisabled : false ,
} ,
featureLimits : {
allocations : 1 ,
backups : 0 ,
databases : 0 ,
} ,
2021-10-24 20:14:04 +00:00
allocation : {
default : 0 ,
additional : [ ] as number [ ] ,
} ,
startup : '' ,
environment : [ ] ,
eggId : 0 ,
image : '' ,
skipScripts : false ,
startOnCompletion : true ,
} as CreateServerRequest }
2021-10-23 21:19:41 +00:00
validationSchema = { object ( ) . shape ( { } ) }
>
2021-10-24 20:14:04 +00:00
< InternalForm / >
2021-10-23 21:19:41 +00:00
< / Formik >
2021-01-05 21:52:49 +00:00
< / AdminContentBlock >
) ;
} ;