mirror of
https://github.com/ConvoyPanel/panel.git
synced 2026-06-21 04:26:00 +08:00
207 lines
8.5 KiB
TypeScript
207 lines
8.5 KiB
TypeScript
import FlashMessageRender from '@/components/elements/FlashMessageRenderer'
|
|
import Modal from '@/components/elements/Modal'
|
|
import { FormikProvider, useFormik } from 'formik'
|
|
import * as yup from 'yup'
|
|
import { useFlashKey } from '@/util/useFlash'
|
|
import TextInputFormik from '@/components/elements/forms/TextInputFormik'
|
|
import { Text } from '@mantine/core'
|
|
import CheckboxFormik from '@/components/elements/forms/CheckboxFormik'
|
|
import NodesSelectFormik from '@/components/admin/servers/NodesSelectFormik'
|
|
import AddressesMultiSelectFormik from '@/components/admin/servers/AddressesMultiSelectFormik'
|
|
import UsersSelectFormik from '@/components/admin/servers/UsersSelectFormik'
|
|
import TemplatesSelectFormik from '@/components/admin/servers/TemplatesSelectFormik'
|
|
import createServer from '@/api/admin/servers/createServer'
|
|
import useServersSWR from '@/api/admin/servers/useServersSWR'
|
|
import usePagination from '@/util/usePagination'
|
|
import { ServerResponse } from '@/api/admin/servers/getServers'
|
|
|
|
interface Props {
|
|
nodeId?: number
|
|
userId?: number
|
|
open: boolean
|
|
onClose: () => void
|
|
}
|
|
|
|
const CreateServerModal = ({ nodeId, userId, open, onClose }: Props) => {
|
|
const [page] = usePagination()
|
|
const { mutate } = useServersSWR({ nodeId, userId, page, includes: ['node', 'user'] })
|
|
const { clearFlashes, clearAndAddHttpError } = useFlashKey('admin:servers.create')
|
|
|
|
const form = useFormik({
|
|
initialValues: {
|
|
name: '',
|
|
nodeId: nodeId?.toString() ?? '',
|
|
userId: userId?.toString() ?? '',
|
|
vmid: '',
|
|
hostname: '',
|
|
addressIds: [],
|
|
cpu: '',
|
|
memory: '',
|
|
disk: '',
|
|
snapshotLimit: '0',
|
|
backupLimit: '',
|
|
bandwidthLimit: '',
|
|
accountPassword: '',
|
|
shouldCreateServer: true,
|
|
startOnCompletion: false,
|
|
templateUuid: '',
|
|
},
|
|
validationSchema: yup.object({
|
|
name: yup.string().max(40, 'Do not exceed 40 characters').required('A name is required.'),
|
|
userId: yup.number().required('A user is required.'),
|
|
nodeId: yup.number().required('A node is required.'),
|
|
vmid: yup.number().min(100).max(999999999),
|
|
hostname: yup
|
|
.string()
|
|
.max(191, 'Do not exceed 191 characters')
|
|
.matches(
|
|
/((https?):\/\/)?(www.)?[a-z0-9]+(\.[a-z]{2,}){1,3}(#?\/?[a-zA-Z0-9#]+)*\/?(\?[a-zA-Z0-9-_]+=[a-zA-Z0-9-%]+&?)?$/,
|
|
'Enter a valid hostname'
|
|
)
|
|
.required(),
|
|
addressIds: yup.array().of(yup.number()),
|
|
cpu: yup.number().min(1, "Can't have zero cpus lol").required('A CPU value is required.'),
|
|
memory: yup.number().min(16, 'Please specify at least 16 MiB').required('A memory value is required.'),
|
|
disk: yup.number().min(1, "Can't have no disk lol").required('A disk value is required.'),
|
|
snapshotLimit: yup.number().min(0),
|
|
backupLimit: yup.number().min(0),
|
|
bandwidthLimit: yup.number().min(0),
|
|
accountPassword: yup
|
|
.string()
|
|
.matches(
|
|
/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})/,
|
|
'Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and One Special Case Character'
|
|
)
|
|
.optional(),
|
|
shouldCreateServer: yup.boolean(),
|
|
templateUuid: yup.string().when('createServer', {
|
|
is: true,
|
|
then: yup.string().required('Specify a template'),
|
|
}),
|
|
}),
|
|
onSubmit: async (
|
|
{
|
|
userId,
|
|
nodeId,
|
|
vmid,
|
|
cpu,
|
|
memory,
|
|
disk,
|
|
snapshotLimit,
|
|
backupLimit,
|
|
bandwidthLimit,
|
|
addressIds,
|
|
...values
|
|
},
|
|
{ setSubmitting }
|
|
) => {
|
|
clearFlashes()
|
|
try {
|
|
const server = await createServer({
|
|
...values,
|
|
vmid: vmid !== '' ? parseInt(vmid) : null,
|
|
nodeId: parseInt(nodeId),
|
|
userId: parseInt(userId),
|
|
limits: {
|
|
cpu: parseInt(cpu),
|
|
memory: parseInt(memory) * 1048576,
|
|
disk: parseInt(disk) * 1048576,
|
|
snapshots: snapshotLimit !== '' ? parseInt(snapshotLimit) : null,
|
|
backups: backupLimit !== '' ? parseInt(backupLimit) : null,
|
|
bandwidth: bandwidthLimit !== '' ? parseInt(bandwidthLimit) : null,
|
|
addressIds,
|
|
},
|
|
})
|
|
|
|
mutate(data => {
|
|
if (!data) return data
|
|
|
|
return {
|
|
...data,
|
|
items: [server, ...data.items],
|
|
} as ServerResponse
|
|
}, false)
|
|
|
|
handleClose()
|
|
} catch (e) {
|
|
clearAndAddHttpError(e as Error)
|
|
}
|
|
|
|
setSubmitting(false)
|
|
},
|
|
})
|
|
|
|
const handleClose = () => {
|
|
clearFlashes()
|
|
form.resetForm()
|
|
onClose()
|
|
}
|
|
|
|
return (
|
|
<Modal open={open} onClose={handleClose}>
|
|
<Modal.Header>
|
|
<Modal.Title>Create a Server</Modal.Title>
|
|
</Modal.Header>
|
|
|
|
<FormikProvider value={form}>
|
|
<form onSubmit={form.handleSubmit}>
|
|
<Modal.Body>
|
|
<FlashMessageRender className='mb-5' byKey={'admin:servers.create'} />
|
|
<TextInputFormik name={'name'} label={'Display Name'} />
|
|
{nodeId ? null : <NodesSelectFormik />}
|
|
{userId ? null : <UsersSelectFormik />}
|
|
<TextInputFormik name={'vmid'} label={'VMID'} placeholder={'Leave blank to generate'} />
|
|
<TextInputFormik name={'hostname'} label={'Hostname'} />
|
|
<AddressesMultiSelectFormik disabled={form.values.nodeId === ''} />
|
|
<div className={'grid grid-cols-2 gap-3'}>
|
|
<TextInputFormik name={'cpu'} label={'CPUs'} />
|
|
<TextInputFormik name={'memory'} label={'Memory (MiB)'} />
|
|
</div>
|
|
<TextInputFormik name={'disk'} label={'Disk (MiB)'} />
|
|
<div className={'grid grid-cols-2 gap-3'}>
|
|
<TextInputFormik
|
|
name={'backupLimit'}
|
|
label={'Backup Limit'}
|
|
placeholder={'Leave blank for no limit'}
|
|
/>
|
|
<TextInputFormik
|
|
name={'bandwidthLimit'}
|
|
label={'Bandwidth Limit (MiB)'}
|
|
placeholder={'Leave blank for no limit'}
|
|
/>
|
|
</div>
|
|
<TextInputFormik
|
|
name={'accountPassword'}
|
|
label={'Account Password'}
|
|
type={'password'}
|
|
/>
|
|
<CheckboxFormik
|
|
name={'shouldCreateServer'}
|
|
label={'Create Server'}
|
|
className={'mt-3 relative'}
|
|
/>
|
|
<TemplatesSelectFormik
|
|
disabled={!form.values.shouldCreateServer || form.values.nodeId === ''}
|
|
/>
|
|
<CheckboxFormik
|
|
name={'startOnCompletion'}
|
|
label={'Start Server On Completion'}
|
|
className={'mt-3 relative'}
|
|
/>
|
|
</Modal.Body>
|
|
<Modal.Actions>
|
|
<Modal.Action type='button' onClick={handleClose}>
|
|
Cancel
|
|
</Modal.Action>
|
|
<Modal.Action type='submit' loading={form.isSubmitting}>
|
|
Create
|
|
</Modal.Action>
|
|
</Modal.Actions>
|
|
</form>
|
|
</FormikProvider>
|
|
</Modal>
|
|
)
|
|
}
|
|
|
|
export default CreateServerModal
|