From 23f2029deb62d2d87f877b04ea7ca14c2bf95bc4 Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Wed, 6 Dec 2023 18:48:07 +0100 Subject: [PATCH] Can configure all network settings --- .../src/libvirt_rest_structures.rs | 6 + .../src/widgets/forms/EditSection.tsx | 19 ++- .../src/widgets/forms/TextMaskInput.tsx | 60 +++++++ .../src/widgets/net/NetworkDetails.tsx | 160 +++++++++++++++++- 4 files changed, 232 insertions(+), 13 deletions(-) create mode 100644 virtweb_frontend/src/widgets/forms/TextMaskInput.tsx diff --git a/virtweb_backend/src/libvirt_rest_structures.rs b/virtweb_backend/src/libvirt_rest_structures.rs index 966429f..9ca1058 100644 --- a/virtweb_backend/src/libvirt_rest_structures.rs +++ b/virtweb_backend/src/libvirt_rest_structures.rs @@ -371,6 +371,12 @@ impl NetworkInfo { } } + if let Some(domain) = &self.domain { + if !regex!("^[a-zA-Z0-9.]+$").is_match(domain) { + return Err(StructureExtraction("Domain name is invalid!").into()); + } + } + let mut ips = Vec::with_capacity(2); if let Some(ipv4) = self.ip_v4 { diff --git a/virtweb_frontend/src/widgets/forms/EditSection.tsx b/virtweb_frontend/src/widgets/forms/EditSection.tsx index 01a0c1b..0dd3b8c 100644 --- a/virtweb_frontend/src/widgets/forms/EditSection.tsx +++ b/virtweb_frontend/src/widgets/forms/EditSection.tsx @@ -1,15 +1,24 @@ import { Grid, Paper, Typography } from "@mui/material"; -import { PropsWithChildren } from "react"; +import React, { PropsWithChildren } from "react"; export function EditSection( - p: { title: string } & PropsWithChildren + p: { title: string; actions?: React.ReactElement } & PropsWithChildren ): React.ReactElement { return ( - - {p.title} - + + + {p.title} + + {p.actions} + {p.children} diff --git a/virtweb_frontend/src/widgets/forms/TextMaskInput.tsx b/virtweb_frontend/src/widgets/forms/TextMaskInput.tsx new file mode 100644 index 0000000..29c8f41 --- /dev/null +++ b/virtweb_frontend/src/widgets/forms/TextMaskInput.tsx @@ -0,0 +1,60 @@ +import { FormControl, Input, InputLabel } from "@mui/material"; +import { TextInput } from "./TextInput"; +import { IMaskInput } from "react-imask"; +import React from "react"; + +interface CustomProps { + onChange: (event: { target: { name: string; value: string } }) => void; + name: string; + placeholder: string; +} + +const TextMaskCustom = React.forwardRef( + function TextMaskCustom(props, ref) { + const { onChange, placeholder, ...other } = props; + return ( + + onChange({ target: { name: props.name, value } }) + } + overwrite + /> + ); + } +); + +export function TextMaskInput(p: { + label: string; + editable: boolean; + value?: string; + onValueChange?: (newVal: string | undefined) => void; + mask: string; +}): React.ReactElement { + const id = React.useRef(Math.random()); + + if (!p.editable) return ; + + return ( + + {p.label} + + p.onValueChange?.( + c.target.value.length === 0 ? undefined : c.target.value + ) + } + id={`mi-${id.current}`} + inputComponent={TextMaskCustom as any} + placeholder={p.mask} + /> + + ); +} diff --git a/virtweb_frontend/src/widgets/net/NetworkDetails.tsx b/virtweb_frontend/src/widgets/net/NetworkDetails.tsx index 41d9f20..e3cdedc 100644 --- a/virtweb_frontend/src/widgets/net/NetworkDetails.tsx +++ b/virtweb_frontend/src/widgets/net/NetworkDetails.tsx @@ -1,12 +1,14 @@ -import { Grid } from "@mui/material"; -import { NetworkInfo } from "../../api/NetworksApi"; -import { ServerApi } from "../../api/ServerApi"; -import { EditSection } from "../forms/EditSection"; -import { TextInput } from "../forms/TextInput"; -import { SelectInput } from "../forms/SelectInput"; +import { Checkbox, Grid } from "@mui/material"; import React from "react"; +import { IpConfig, NetworkInfo } from "../../api/NetworksApi"; +import { ServerApi } from "../../api/ServerApi"; import { AsyncWidget } from "../AsyncWidget"; -import { IPv4Input } from "../forms/IPv4Input"; +import { EditSection } from "../forms/EditSection"; +import { IPInput } from "../forms/IPInput"; +import { SelectInput } from "../forms/SelectInput"; +import { TextInput } from "../forms/TextInput"; +import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider"; +import { CheckboxInput } from "../forms/CheckboxInput"; interface DetailsProps { net: NetworkInfo; @@ -116,7 +118,7 @@ function NetworkDetailsInner( /> )} - + + { + p.net.domain = v; + p.onChange?.(); + }} + multiline={true} /> + + { + p.net.ip_v4 = c; + p.onChange?.(); + }} + version={4} + /> + + { + p.net.ip_v6 = c; + p.onChange?.(); + }} + version={6} + /> ); } + +function IPSection(p: { + editable: boolean; + config?: IpConfig; + onChange: (c: IpConfig | undefined) => void; + version: 4 | 6; +}): React.ReactElement { + const confirm = useConfirm(); + + const toggleNetwork = async () => { + if (!!p.config) { + if ( + !(await confirm( + `Do you really want to disable IPv${p.version} on this network?` + )) + ) + return; + + p.onChange?.(undefined); + return; + } + + p.onChange?.({ + bridge_address: p.version === 4 ? "192.168.1.1" : "fd00::1", + prefix: p.version === 4 ? 24 : 8, + }); + }; + + if (!p.config && !p.editable) return <>; + + return ( + + } + > + {p.config && ( + <> + { + p.config!.bridge_address = v ?? ""; + p.onChange?.(p.config); + }} + /> + + { + p.config!.prefix = Number(v); + p.onChange?.(p.config); + }} + size={p.version === 4 ? { min: 0, max: 32 } : { min: 0, max: 128 }} + /> + + { + if (v) + p.config!.dhcp_range = + p.version === 4 + ? ["192.168.1.100", "192.168.1.200"] + : ["fd00::100", "fd00::f00"]; + else p.config!.dhcp_range = undefined; + p.onChange?.(p.config); + }} + /> + + )} + + {p.config?.dhcp_range && ( + <> + { + p.config!.dhcp_range![0] = v!; + p.onChange(p.config); + }} + /> + { + p.config!.dhcp_range![1] = v!; + p.onChange(p.config); + }} + /> + + )} + + ); +}