Create network filters route
This commit is contained in:
		
							
								
								
									
										19
									
								
								virtweb_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										19
									
								
								virtweb_backend/Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -2159,18 +2159,6 @@ dependencies = [
 | 
				
			|||||||
 "serde_derive",
 | 
					 "serde_derive",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "serde-xml-rs"
 | 
					 | 
				
			||||||
version = "0.6.0"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782"
 | 
					 | 
				
			||||||
dependencies = [
 | 
					 | 
				
			||||||
 "log",
 | 
					 | 
				
			||||||
 "serde",
 | 
					 | 
				
			||||||
 "thiserror",
 | 
					 | 
				
			||||||
 "xml-rs",
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "serde_derive"
 | 
					name = "serde_derive"
 | 
				
			||||||
version = "1.0.193"
 | 
					version = "1.0.193"
 | 
				
			||||||
@@ -2677,7 +2665,6 @@ dependencies = [
 | 
				
			|||||||
 "reqwest",
 | 
					 "reqwest",
 | 
				
			||||||
 "rust-embed",
 | 
					 "rust-embed",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
 "serde-xml-rs",
 | 
					 | 
				
			||||||
 "serde_json",
 | 
					 "serde_json",
 | 
				
			||||||
 "sysinfo",
 | 
					 "sysinfo",
 | 
				
			||||||
 "tempfile",
 | 
					 "tempfile",
 | 
				
			||||||
@@ -2981,12 +2968,6 @@ dependencies = [
 | 
				
			|||||||
 "windows-sys 0.48.0",
 | 
					 "windows-sys 0.48.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "xml-rs"
 | 
					 | 
				
			||||||
version = "0.8.19"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "zerocopy"
 | 
					name = "zerocopy"
 | 
				
			||||||
version = "0.7.31"
 | 
					version = "0.7.31"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,6 @@ actix-web-actors = "4.2.0"
 | 
				
			|||||||
actix-http = "3.4.0"
 | 
					actix-http = "3.4.0"
 | 
				
			||||||
serde = { version = "1.0.193", features = ["derive"] }
 | 
					serde = { version = "1.0.193", features = ["derive"] }
 | 
				
			||||||
serde_json = "1.0.108"
 | 
					serde_json = "1.0.108"
 | 
				
			||||||
serde-xml-rs = "0.6.0"
 | 
					 | 
				
			||||||
quick-xml = { version = "0.31.0", features = ["serialize", "overlapped-lists"] }
 | 
					quick-xml = { version = "0.31.0", features = ["serialize", "overlapped-lists"] }
 | 
				
			||||||
futures-util = "0.3.28"
 | 
					futures-util = "0.3.28"
 | 
				
			||||||
anyhow = "1.0.75"
 | 
					anyhow = "1.0.75"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ struct StaticConfig {
 | 
				
			|||||||
    iso_mimetypes: &'static [&'static str],
 | 
					    iso_mimetypes: &'static [&'static str],
 | 
				
			||||||
    net_mac_prefix: &'static str,
 | 
					    net_mac_prefix: &'static str,
 | 
				
			||||||
    constraints: ServerConstraints,
 | 
					    constraints: ServerConstraints,
 | 
				
			||||||
    builtin_network_rules: &'static [&'static str],
 | 
					    builtin_nwfilter_rules: &'static [&'static str],
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(serde::Serialize)]
 | 
					#[derive(serde::Serialize)]
 | 
				
			||||||
@@ -46,7 +46,7 @@ pub async fn static_config(local_auth: LocalAuthEnabled) -> impl Responder {
 | 
				
			|||||||
        oidc_auth_enabled: !AppConfig::get().disable_oidc,
 | 
					        oidc_auth_enabled: !AppConfig::get().disable_oidc,
 | 
				
			||||||
        iso_mimetypes: &constants::ALLOWED_ISO_MIME_TYPES,
 | 
					        iso_mimetypes: &constants::ALLOWED_ISO_MIME_TYPES,
 | 
				
			||||||
        net_mac_prefix: constants::NET_MAC_ADDR_PREFIX,
 | 
					        net_mac_prefix: constants::NET_MAC_ADDR_PREFIX,
 | 
				
			||||||
        builtin_network_rules: &constants::BUILTIN_NETWORK_FILTER_RULES,
 | 
					        builtin_nwfilter_rules: &constants::BUILTIN_NETWORK_FILTER_RULES,
 | 
				
			||||||
        constraints: ServerConstraints {
 | 
					        constraints: ServerConstraints {
 | 
				
			||||||
            iso_max_size: constants::ISO_MAX_SIZE,
 | 
					            iso_max_size: constants::ISO_MAX_SIZE,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,7 @@ import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
 | 
				
			|||||||
import { BaseLoginPage } from "./widgets/BaseLoginPage";
 | 
					import { BaseLoginPage } from "./widgets/BaseLoginPage";
 | 
				
			||||||
import { ViewNetworkRoute } from "./routes/ViewNetworkRoute";
 | 
					import { ViewNetworkRoute } from "./routes/ViewNetworkRoute";
 | 
				
			||||||
import { HomeRoute } from "./routes/HomeRoute";
 | 
					import { HomeRoute } from "./routes/HomeRoute";
 | 
				
			||||||
 | 
					import { NetworkFiltersListRoute } from "./routes/NetworkFiltersListRoute";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface AuthContext {
 | 
					interface AuthContext {
 | 
				
			||||||
  signedIn: boolean;
 | 
					  signedIn: boolean;
 | 
				
			||||||
@@ -61,6 +62,8 @@ export function App() {
 | 
				
			|||||||
          <Route path="net/:uuid" element={<ViewNetworkRoute />} />
 | 
					          <Route path="net/:uuid" element={<ViewNetworkRoute />} />
 | 
				
			||||||
          <Route path="net/:uuid/edit" element={<EditNetworkRoute />} />
 | 
					          <Route path="net/:uuid/edit" element={<EditNetworkRoute />} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <Route path="nwfilter" element={<NetworkFiltersListRoute />} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <Route path="sysinfo" element={<SysInfoRoute />} />
 | 
					          <Route path="sysinfo" element={<SysInfoRoute />} />
 | 
				
			||||||
          <Route path="*" element={<NotFoundRoute />} />
 | 
					          <Route path="*" element={<NotFoundRoute />} />
 | 
				
			||||||
        </Route>
 | 
					        </Route>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,6 +37,10 @@ export interface NWFilter {
 | 
				
			|||||||
  rules: NWFilterRule[];
 | 
					  rules: NWFilterRule[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function NWFilterURL(n: NWFilter, edit: boolean = false): string {
 | 
				
			||||||
 | 
					  return `/nwfilter/${n.uuid}${edit ? "/edit" : ""}`;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class NWFilterApi {
 | 
					export class NWFilterApi {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Get the entire list of networks
 | 
					   * Get the entire list of networks
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,10 +39,6 @@ export function NetworkURL(n: NetworkInfo, edit: boolean = false): string {
 | 
				
			|||||||
  return `/net/${n.uuid}${edit ? "/edit" : ""}`;
 | 
					  return `/net/${n.uuid}${edit ? "/edit" : ""}`;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function NetworkXMLURL(n: NetworkInfo): string {
 | 
					 | 
				
			||||||
  return `/net/${n.uuid}/xml`;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class NetworkApi {
 | 
					export class NetworkApi {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Create a new network
 | 
					   * Create a new network
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ export interface ServerConfig {
 | 
				
			|||||||
  iso_mimetypes: string[];
 | 
					  iso_mimetypes: string[];
 | 
				
			||||||
  net_mac_prefix: string;
 | 
					  net_mac_prefix: string;
 | 
				
			||||||
  constraints: ServerConstraints;
 | 
					  constraints: ServerConstraints;
 | 
				
			||||||
 | 
					  builtin_nwfilter_rules: string[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ServerConstraints {
 | 
					export interface ServerConstraints {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -133,10 +133,6 @@ export class VMInfo implements VMInfoInterface {
 | 
				
			|||||||
  get VNCURL(): string {
 | 
					  get VNCURL(): string {
 | 
				
			||||||
    return `/vm/${this.uuid}/vnc`;
 | 
					    return `/vm/${this.uuid}/vnc`;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  get XMLURL(): string {
 | 
					 | 
				
			||||||
    return `/vm/${this.uuid}/xml`;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class VMApi {
 | 
					export class VMApi {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										153
									
								
								virtweb_frontend/src/routes/NetworkFiltersListRoute.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								virtweb_frontend/src/routes/NetworkFiltersListRoute.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,153 @@
 | 
				
			|||||||
 | 
					import VisibilityIcon from "@mui/icons-material/Visibility";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Button,
 | 
				
			||||||
 | 
					  IconButton,
 | 
				
			||||||
 | 
					  Paper,
 | 
				
			||||||
 | 
					  Table,
 | 
				
			||||||
 | 
					  TableBody,
 | 
				
			||||||
 | 
					  TableCell,
 | 
				
			||||||
 | 
					  TableContainer,
 | 
				
			||||||
 | 
					  TableHead,
 | 
				
			||||||
 | 
					  TableRow,
 | 
				
			||||||
 | 
					  ToggleButton,
 | 
				
			||||||
 | 
					  ToggleButtonGroup,
 | 
				
			||||||
 | 
					  Typography,
 | 
				
			||||||
 | 
					} from "@mui/material";
 | 
				
			||||||
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import { useNavigate } from "react-router-dom";
 | 
				
			||||||
 | 
					import { NWFilter, NWFilterApi, NWFilterURL } from "../api/NWFilterApi";
 | 
				
			||||||
 | 
					import { AsyncWidget } from "../widgets/AsyncWidget";
 | 
				
			||||||
 | 
					import { RouterLink } from "../widgets/RouterLink";
 | 
				
			||||||
 | 
					import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
 | 
				
			||||||
 | 
					import { ServerApi } from "../api/ServerApi";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function NetworkFiltersListRoute(): React.ReactElement {
 | 
				
			||||||
 | 
					  const [list, setList] = React.useState<NWFilter[] | undefined>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [count] = React.useState(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const load = async () => {
 | 
				
			||||||
 | 
					    setList(await NWFilterApi.GetList());
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <AsyncWidget
 | 
				
			||||||
 | 
					      loadKey={count}
 | 
				
			||||||
 | 
					      load={load}
 | 
				
			||||||
 | 
					      ready={list !== undefined}
 | 
				
			||||||
 | 
					      errMsg="Failed to load the list of networks!"
 | 
				
			||||||
 | 
					      build={() => <NetworkFiltersListRouteInner list={list!} />}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum VisibleFilters {
 | 
				
			||||||
 | 
					  All,
 | 
				
			||||||
 | 
					  Builtin,
 | 
				
			||||||
 | 
					  Custom,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function NetworkFiltersListRouteInner(p: {
 | 
				
			||||||
 | 
					  list: NWFilter[];
 | 
				
			||||||
 | 
					}): React.ReactElement {
 | 
				
			||||||
 | 
					  const navigate = useNavigate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [visibleFilters, setVisibleFilters] = React.useState(
 | 
				
			||||||
 | 
					    VisibleFilters.All
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const filteredList = React.useMemo(() => {
 | 
				
			||||||
 | 
					    if (visibleFilters === VisibleFilters.All) return p.list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onlyBuiltin = visibleFilters === VisibleFilters.Builtin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return p.list.filter(
 | 
				
			||||||
 | 
					      (f) =>
 | 
				
			||||||
 | 
					        ServerApi.Config.builtin_nwfilter_rules.includes(f.name) === onlyBuiltin
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }, [visibleFilters]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <VirtWebRouteContainer
 | 
				
			||||||
 | 
					      label="Network filters"
 | 
				
			||||||
 | 
					      actions={
 | 
				
			||||||
 | 
					        <>
 | 
				
			||||||
 | 
					          <span style={{ flex: 10 }}></span>
 | 
				
			||||||
 | 
					          <ToggleButtonGroup
 | 
				
			||||||
 | 
					            size="small"
 | 
				
			||||||
 | 
					            value={visibleFilters}
 | 
				
			||||||
 | 
					            exclusive
 | 
				
			||||||
 | 
					            onChange={(_ev, v) => setVisibleFilters(v)}
 | 
				
			||||||
 | 
					            aria-label="visible filters"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <ToggleButton value={VisibleFilters.All}>All</ToggleButton>
 | 
				
			||||||
 | 
					            <ToggleButton value={VisibleFilters.Builtin}>Builtin</ToggleButton>
 | 
				
			||||||
 | 
					            <ToggleButton value={VisibleFilters.Custom}>Custom</ToggleButton>
 | 
				
			||||||
 | 
					          </ToggleButtonGroup>
 | 
				
			||||||
 | 
					          <span style={{ flex: 2 }}></span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <RouterLink to="/nwfilter/new">
 | 
				
			||||||
 | 
					            <Button>New</Button>
 | 
				
			||||||
 | 
					          </RouterLink>
 | 
				
			||||||
 | 
					        </>
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <TableContainer component={Paper}>
 | 
				
			||||||
 | 
					        <Table>
 | 
				
			||||||
 | 
					          <TableHead>
 | 
				
			||||||
 | 
					            <TableRow>
 | 
				
			||||||
 | 
					              <TableCell>Name</TableCell>
 | 
				
			||||||
 | 
					              <TableCell>Chain</TableCell>
 | 
				
			||||||
 | 
					              <TableCell>Priority</TableCell>
 | 
				
			||||||
 | 
					              <TableCell>Referenced filters</TableCell>
 | 
				
			||||||
 | 
					              <TableCell># of rules</TableCell>
 | 
				
			||||||
 | 
					              <TableCell>Actions</TableCell>
 | 
				
			||||||
 | 
					            </TableRow>
 | 
				
			||||||
 | 
					          </TableHead>
 | 
				
			||||||
 | 
					          <TableBody>
 | 
				
			||||||
 | 
					            {filteredList.map((t) => {
 | 
				
			||||||
 | 
					              return (
 | 
				
			||||||
 | 
					                <TableRow
 | 
				
			||||||
 | 
					                  key={t.uuid}
 | 
				
			||||||
 | 
					                  hover
 | 
				
			||||||
 | 
					                  onDoubleClick={() => navigate(NWFilterURL(t))}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <TableCell>{t.name}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    {t.chain?.protocol ?? (
 | 
				
			||||||
 | 
					                      <Typography style={{ fontStyle: "italic" }}>
 | 
				
			||||||
 | 
					                        None
 | 
				
			||||||
 | 
					                      </Typography>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    {t.priority ?? (
 | 
				
			||||||
 | 
					                      <Typography style={{ fontStyle: "italic" }}>
 | 
				
			||||||
 | 
					                        None
 | 
				
			||||||
 | 
					                      </Typography>
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <ul>
 | 
				
			||||||
 | 
					                      {t.join_filters.map((f, n) => (
 | 
				
			||||||
 | 
					                        <li key={n}>{f}</li>
 | 
				
			||||||
 | 
					                      ))}
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>{t.rules.length}</TableCell>
 | 
				
			||||||
 | 
					                  <TableCell>
 | 
				
			||||||
 | 
					                    <RouterLink to={NWFilterURL(t)}>
 | 
				
			||||||
 | 
					                      <IconButton>
 | 
				
			||||||
 | 
					                        <VisibilityIcon />
 | 
				
			||||||
 | 
					                      </IconButton>
 | 
				
			||||||
 | 
					                    </RouterLink>
 | 
				
			||||||
 | 
					                  </TableCell>
 | 
				
			||||||
 | 
					                </TableRow>
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            })}
 | 
				
			||||||
 | 
					          </TableBody>
 | 
				
			||||||
 | 
					        </Table>
 | 
				
			||||||
 | 
					      </TableContainer>
 | 
				
			||||||
 | 
					    </VirtWebRouteContainer>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								virtweb_frontend/src/utils/DebugUtils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								virtweb_frontend/src/utils/DebugUtils.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					export function isDebug(): boolean {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    !import.meta.env.NODE_ENV || import.meta.env.NODE_ENV === "development"
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,7 +3,9 @@ import {
 | 
				
			|||||||
  mdiDisc,
 | 
					  mdiDisc,
 | 
				
			||||||
  mdiHome,
 | 
					  mdiHome,
 | 
				
			||||||
  mdiInformation,
 | 
					  mdiInformation,
 | 
				
			||||||
  mdiLan
 | 
					  mdiLan,
 | 
				
			||||||
 | 
					  mdiSecurity,
 | 
				
			||||||
 | 
					  mdiSecurityNetwork,
 | 
				
			||||||
} from "@mdi/js";
 | 
					} from "@mdi/js";
 | 
				
			||||||
import Icon from "@mdi/react";
 | 
					import Icon from "@mdi/react";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@@ -17,6 +19,7 @@ import {
 | 
				
			|||||||
import { Outlet, useLocation } from "react-router-dom";
 | 
					import { Outlet, useLocation } from "react-router-dom";
 | 
				
			||||||
import { RouterLink } from "./RouterLink";
 | 
					import { RouterLink } from "./RouterLink";
 | 
				
			||||||
import { VirtWebAppBar } from "./VirtWebAppBar";
 | 
					import { VirtWebAppBar } from "./VirtWebAppBar";
 | 
				
			||||||
 | 
					import { isDebug } from "../utils/DebugUtils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function BaseAuthenticatedPage(): React.ReactElement {
 | 
					export function BaseAuthenticatedPage(): React.ReactElement {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
@@ -60,6 +63,14 @@ export function BaseAuthenticatedPage(): React.ReactElement {
 | 
				
			|||||||
            uri="/net"
 | 
					            uri="/net"
 | 
				
			||||||
            icon={<Icon path={mdiLan} size={1} />}
 | 
					            icon={<Icon path={mdiLan} size={1} />}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
 | 
					          {/* TODO : remove debug marker */}
 | 
				
			||||||
 | 
					          {isDebug() && (
 | 
				
			||||||
 | 
					            <NavLink
 | 
				
			||||||
 | 
					              label="Network filters"
 | 
				
			||||||
 | 
					              uri="/nwfilter"
 | 
				
			||||||
 | 
					              icon={<Icon path={mdiSecurityNetwork} size={1} />}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
          <NavLink
 | 
					          <NavLink
 | 
				
			||||||
            label="ISO files"
 | 
					            label="ISO files"
 | 
				
			||||||
            uri="/iso"
 | 
					            uri="/iso"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user