Can list and delete networks
This commit is contained in:
		@@ -19,6 +19,7 @@ import { VMListRoute } from "./routes/VMListRoute";
 | 
				
			|||||||
import { CreateVMRoute, EditVMRoute } from "./routes/EditVMRoute";
 | 
					import { CreateVMRoute, EditVMRoute } from "./routes/EditVMRoute";
 | 
				
			||||||
import { VMRoute } from "./routes/VMRoute";
 | 
					import { VMRoute } from "./routes/VMRoute";
 | 
				
			||||||
import { VNCRoute } from "./routes/VNCRoute";
 | 
					import { VNCRoute } from "./routes/VNCRoute";
 | 
				
			||||||
 | 
					import { NetworksListRoute } from "./routes/NetworksListRoute";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface AuthContext {
 | 
					interface AuthContext {
 | 
				
			||||||
  signedIn: boolean;
 | 
					  signedIn: boolean;
 | 
				
			||||||
@@ -47,6 +48,8 @@ export function App() {
 | 
				
			|||||||
          <Route path="vm/:uuid/edit" element={<EditVMRoute />} />
 | 
					          <Route path="vm/:uuid/edit" element={<EditVMRoute />} />
 | 
				
			||||||
          <Route path="vm/:uuid/vnc" element={<VNCRoute />} />
 | 
					          <Route path="vm/:uuid/vnc" element={<VNCRoute />} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <Route path="net" element={<NetworksListRoute />} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <Route path="sysinfo" element={<SysInfoRoute />} />
 | 
					          <Route path="sysinfo" element={<SysInfoRoute />} />
 | 
				
			||||||
          <Route path="*" element={<NotFoundRoute />} />
 | 
					          <Route path="*" element={<NotFoundRoute />} />
 | 
				
			||||||
        </Route>
 | 
					        </Route>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										50
									
								
								virtweb_frontend/src/api/NetworksApi.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								virtweb_frontend/src/api/NetworksApi.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					import { APIClient } from "./ApiClient";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IpConfig {
 | 
				
			||||||
 | 
					  bridge_address: string;
 | 
				
			||||||
 | 
					  prefix: number;
 | 
				
			||||||
 | 
					  dhcp_range?: [string, string];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface NetworkInfo {
 | 
				
			||||||
 | 
					  name: string;
 | 
				
			||||||
 | 
					  uuid: string;
 | 
				
			||||||
 | 
					  title?: string;
 | 
				
			||||||
 | 
					  description?: string;
 | 
				
			||||||
 | 
					  forward_mode: "NAT" | "Isolated";
 | 
				
			||||||
 | 
					  device?: string;
 | 
				
			||||||
 | 
					  dns_server?: string;
 | 
				
			||||||
 | 
					  domain?: string;
 | 
				
			||||||
 | 
					  ip_v4?: IpConfig;
 | 
				
			||||||
 | 
					  ip_v6?: IpConfig;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function NetworkURL(n: NetworkInfo, edit: boolean = false): string {
 | 
				
			||||||
 | 
					  return `/net/${n.uuid}${edit ? "/edit" : ""}`;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class NetworkApi {
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Get the entire list of networks
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  static async GetList(): Promise<NetworkInfo[]> {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      await APIClient.exec({
 | 
				
			||||||
 | 
					        method: "GET",
 | 
				
			||||||
 | 
					        uri: "/network/list",
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    ).data;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Delete a network
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  static async Delete(n: NetworkInfo): Promise<NetworkInfo[]> {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      await APIClient.exec({
 | 
				
			||||||
 | 
					        method: "DELETE",
 | 
				
			||||||
 | 
					        uri: `/network/${n.uuid}`,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    ).data;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										133
									
								
								virtweb_frontend/src/routes/NetworksListRoute.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								virtweb_frontend/src/routes/NetworksListRoute.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
				
			|||||||
 | 
					import DeleteIcon from "@mui/icons-material/Delete";
 | 
				
			||||||
 | 
					import VisibilityIcon from "@mui/icons-material/Visibility";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Button,
 | 
				
			||||||
 | 
					  IconButton,
 | 
				
			||||||
 | 
					  Paper,
 | 
				
			||||||
 | 
					  Table,
 | 
				
			||||||
 | 
					  TableBody,
 | 
				
			||||||
 | 
					  TableCell,
 | 
				
			||||||
 | 
					  TableContainer,
 | 
				
			||||||
 | 
					  TableHead,
 | 
				
			||||||
 | 
					  TableRow,
 | 
				
			||||||
 | 
					  Typography,
 | 
				
			||||||
 | 
					} from "@mui/material";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import { NetworkApi, NetworkInfo, NetworkURL } from "../api/NetworksApi";
 | 
				
			||||||
 | 
					import { AsyncWidget } from "../widgets/AsyncWidget";
 | 
				
			||||||
 | 
					import { RouterLink } from "../widgets/RouterLink";
 | 
				
			||||||
 | 
					import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
 | 
				
			||||||
 | 
					import { useConfirm } from "../hooks/providers/ConfirmDialogProvider";
 | 
				
			||||||
 | 
					import { useSnackbar } from "../hooks/providers/SnackbarProvider";
 | 
				
			||||||
 | 
					import { useAlert } from "../hooks/providers/AlertDialogProvider";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function NetworksListRoute(): React.ReactElement {
 | 
				
			||||||
 | 
					  const confirm = useConfirm();
 | 
				
			||||||
 | 
					  const snackbar = useSnackbar();
 | 
				
			||||||
 | 
					  const alert = useAlert();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [list, setList] = React.useState<NetworkInfo[] | undefined>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [count, setCount] = React.useState(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const load = async () => {
 | 
				
			||||||
 | 
					    setList(await NetworkApi.GetList());
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const reload = () => {
 | 
				
			||||||
 | 
					    setList(undefined);
 | 
				
			||||||
 | 
					    setCount(count + 1);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const requestDelete = async (n: NetworkInfo) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      if (
 | 
				
			||||||
 | 
					        !(await confirm(
 | 
				
			||||||
 | 
					          "Do you really want to delete this network?",
 | 
				
			||||||
 | 
					          `Delete network ${n.name}`,
 | 
				
			||||||
 | 
					          "Delete"
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await NetworkApi.Delete(n);
 | 
				
			||||||
 | 
					      reload();
 | 
				
			||||||
 | 
					      snackbar("The network was successfully deleted!");
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      console.error(e);
 | 
				
			||||||
 | 
					      alert("Failed to delete the network!");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <AsyncWidget
 | 
				
			||||||
 | 
					      loadKey={count}
 | 
				
			||||||
 | 
					      load={load}
 | 
				
			||||||
 | 
					      ready={list !== undefined}
 | 
				
			||||||
 | 
					      errMsg="Failed to load the list of networks!"
 | 
				
			||||||
 | 
					      build={() => (
 | 
				
			||||||
 | 
					        <NetworksListRouteInner list={list!} onRequestDelete={requestDelete} />
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function NetworksListRouteInner(p: {
 | 
				
			||||||
 | 
					  list: NetworkInfo[];
 | 
				
			||||||
 | 
					  onRequestDelete: (n: NetworkInfo) => void;
 | 
				
			||||||
 | 
					}): React.ReactElement {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <VirtWebRouteContainer
 | 
				
			||||||
 | 
					      label="Networks"
 | 
				
			||||||
 | 
					      actions={
 | 
				
			||||||
 | 
					        <RouterLink to="/net/new">
 | 
				
			||||||
 | 
					          <Button>New</Button>
 | 
				
			||||||
 | 
					        </RouterLink>
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <TableContainer component={Paper}>
 | 
				
			||||||
 | 
					        <Table>
 | 
				
			||||||
 | 
					          <TableHead>
 | 
				
			||||||
 | 
					            <TableRow>
 | 
				
			||||||
 | 
					              <TableCell>Name</TableCell>
 | 
				
			||||||
 | 
					              <TableCell>Description</TableCell>
 | 
				
			||||||
 | 
					              <TableCell>Network type</TableCell>
 | 
				
			||||||
 | 
					              <TableCell>IP</TableCell>
 | 
				
			||||||
 | 
					              <TableCell>Actions</TableCell>
 | 
				
			||||||
 | 
					            </TableRow>
 | 
				
			||||||
 | 
					          </TableHead>
 | 
				
			||||||
 | 
					          <TableBody>
 | 
				
			||||||
 | 
					            {p.list.map((t) => {
 | 
				
			||||||
 | 
					              return (
 | 
				
			||||||
 | 
					                <TableRow key={t.uuid} hover>
 | 
				
			||||||
 | 
					                  <TableCell>{t.name}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    {t.description ?? (
 | 
				
			||||||
 | 
					                      <Typography style={{ fontStyle: "italic" }}>
 | 
				
			||||||
 | 
					                        None
 | 
				
			||||||
 | 
					                      </Typography>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{t.forward_mode}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    {t.ip_v4 && "IPv4"} {t.ip_v6 && "IPv6"}
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <RouterLink to={NetworkURL(t)}>
 | 
				
			||||||
 | 
					                      <IconButton>
 | 
				
			||||||
 | 
					                        <VisibilityIcon />
 | 
				
			||||||
 | 
					                      </IconButton>
 | 
				
			||||||
 | 
					                    </RouterLink>
 | 
				
			||||||
 | 
					                    <IconButton onClick={() => p.onRequestDelete(t)}>
 | 
				
			||||||
 | 
					                      <DeleteIcon />
 | 
				
			||||||
 | 
					                    </IconButton>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                </TableRow>
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            })}
 | 
				
			||||||
 | 
					          </TableBody>
 | 
				
			||||||
 | 
					        </Table>
 | 
				
			||||||
 | 
					      </TableContainer>
 | 
				
			||||||
 | 
					    </VirtWebRouteContainer>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,11 @@
 | 
				
			|||||||
import { mdiBoxShadow, mdiDisc, mdiHome, mdiInformation } from "@mdi/js";
 | 
					import {
 | 
				
			||||||
 | 
					  mdiBoxShadow,
 | 
				
			||||||
 | 
					  mdiDisc,
 | 
				
			||||||
 | 
					  mdiHome,
 | 
				
			||||||
 | 
					  mdiInformation,
 | 
				
			||||||
 | 
					  mdiLan,
 | 
				
			||||||
 | 
					  mdiNetwork,
 | 
				
			||||||
 | 
					} from "@mdi/js";
 | 
				
			||||||
import Icon from "@mdi/react";
 | 
					import Icon from "@mdi/react";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Box,
 | 
					  Box,
 | 
				
			||||||
@@ -49,6 +56,11 @@ export function BaseAuthenticatedPage(): React.ReactElement {
 | 
				
			|||||||
            uri="/vms"
 | 
					            uri="/vms"
 | 
				
			||||||
            icon={<Icon path={mdiBoxShadow} size={1} />}
 | 
					            icon={<Icon path={mdiBoxShadow} size={1} />}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
 | 
					          <NavLink
 | 
				
			||||||
 | 
					            label="Networks"
 | 
				
			||||||
 | 
					            uri="/net"
 | 
				
			||||||
 | 
					            icon={<Icon path={mdiLan} size={1} />}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
          <NavLink
 | 
					          <NavLink
 | 
				
			||||||
            label="ISO files"
 | 
					            label="ISO files"
 | 
				
			||||||
            uri="/iso"
 | 
					            uri="/iso"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user