add: filtering table of firewall + InterfaceSelector frontend fixes and improves

This commit is contained in:
Domingo Dirutigliano
2024-10-19 18:39:42 +02:00
parent 2658e74aca
commit d64e0aa73c
12 changed files with 147 additions and 86 deletions

View File

@@ -1,25 +1,27 @@
import { SegmentedControl, SegmentedControlProps } from "@mantine/core";
import { RuleMode } from "./utils";
import { RuleMode, Table } from "./utils";
import { table } from "console";
export const ModeSelector = (props:Omit<SegmentedControlProps, "data">) => (
<SegmentedControl
export const ModeSelector = (props:Omit<SegmentedControlProps, "data"> & { table: Table }) => {
const isFilterTable = props.table == Table.FILTER
return <SegmentedControl
data={[
{
value: RuleMode.IN,
label: 'IN',
label: isFilterTable?'IN':'PREROUTING',
},
{
...(isFilterTable?[{
value: RuleMode.FORWARD,
label: 'FWD',
},
}]:[]),
{
value: RuleMode.OUT,
label: 'OUT',
label: isFilterTable?'OUT':'POSTROUTING',
}
]}
size={props.size?props.size:"xs"}
{...props}
/>
)
}

View File

@@ -21,6 +21,11 @@ export enum RuleMode {
FORWARD = "forward"
}
export enum Table {
MANGLE = "mangle",
FILTER = "filter",
}
export type Rule = {
active: boolean
name:string,
@@ -33,6 +38,7 @@ export type Rule = {
port_dst_to: number,
action: ActionType,
mode: RuleMode,
table: Table
}
export type RuleInfo = {

View File

@@ -1,5 +1,5 @@
import { Combobox, TextInput, useCombobox } from "@mantine/core";
import { useState } from "react";
import { useEffect, useState } from "react";
import { ipInterfacesQuery } from "../js/utils";
interface ItemProps{
@@ -38,7 +38,7 @@ export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames,
},
});
const data = [...customIpInterfaces, ...interfaces]
const data = [...customIpInterfaces.filter(v => !interfaces.map(v => v.value).includes(v.value)), ...interfaces]
const [selectedValue, setSelectedValue] = useState<string | null>(null);
const [search, setSearch] = useState('');
@@ -52,12 +52,17 @@ export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames,
return a.value.localeCompare(b.value)
});
const options = filteredOptions.map((item) => (
const options = filteredOptions.sort( (a, b) => a.value == selectedValue? -1 : b.value == selectedValue? 1: a.value.localeCompare(b.value) ).map((item) => (
<Combobox.Option value={item.value} key={item.value}>
( <b>{item.netint}</b> ) -{">"} <b>{item.value}</b>
( <b>{item.value == selectedValue ? "SELECTED" : item.netint}</b> ) -{">"} <b>{item.value}</b>
</Combobox.Option>
));
useEffect(() => {
setSelectedValue(data.find((item) => item.value === defaultValue)?.value??null)
setSearch(defaultValue??'')
}, [])
return <>
<Combobox
store={combobox}
@@ -81,9 +86,8 @@ export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames,
<Combobox.Target>
<TextInput
style={{width:"100%"}}
defaultValue={defaultValue}
rightSection={<Combobox.Chevron />}
value={value??(defaultValue?undefined:search)}
value={value??search}
placeholder="10.1.1.1"
rightSectionPointerEvents="none"
onChange={(event) => {
@@ -107,7 +111,7 @@ export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames,
</Combobox.Target>
<Combobox.Dropdown>
<Combobox.Options mah={100} style={{ overflowY: 'auto' }}>
<Combobox.Options mah={200} style={{ overflowY: 'auto' }}>
{options}
{(exactOptionMatch==null) && search.trim().length > 0 && (
<Combobox.Option value="$create">+ Use this: {search}</Combobox.Option>

View File

@@ -17,7 +17,7 @@ export const regex_ipv4 = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.
export const regex_ipv4_no_cidr = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
export const regex_port = "^([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])?$"
export const regex_range_port = "^(([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(-([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])?)?)?$"
export const DEV_IP_BACKEND = "127.0.0.1:4444"
export const DEV_IP_BACKEND = "198.19.249.69:4444"
export const queryClient = new QueryClient({ defaultOptions: { queries: {
staleTime: Infinity

View File

@@ -1,8 +1,8 @@
import { ActionIcon, Badge, Box, Button, Divider, LoadingOverlay, Space, Switch, TextInput, Title, Tooltip, useMantineTheme } from "@mantine/core"
import { ActionIcon, Badge, Box, Divider, FloatingIndicator, LoadingOverlay, Space, Switch, Table, Tabs, TextInput, Title, Tooltip, useMantineTheme } from "@mantine/core"
import { useEffect, useState } from "react";
import { BsPlusLg, BsTrashFill } from "react-icons/bs"
import { rem } from '@mantine/core';
import { ActionType, Protocol, Rule, RuleMode, firewall, firewallRulesQuery } from "../../components/Firewall/utils";
import { ActionType, Protocol, Rule, RuleMode, Table as NFTables, firewall, firewallRulesQuery } from "../../components/Firewall/utils";
import { errorNotify, getErrorMessage, isMediumScreen, makeid, okNotify } from "../../js/utils";
import { useListState, useMediaQuery } from '@mantine/hooks';
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
@@ -62,6 +62,8 @@ export const Firewall = () => {
const {rule_id, ...rest} = v
return rest
})) || rules.data?.policy != currentPolicy
const [selectedTab, setSelectedTab] = useState<NFTables>(NFTables.FILTER)
const enableFirewall = () => {
if (valuesChanged){
@@ -122,7 +124,8 @@ export const Firewall = () => {
port_src_to: 65535,
port_dst_to: 8080,
action: ActionType.ACCEPT,
mode: RuleMode.IN
mode: RuleMode.IN,
table: selectedTab
})
}
@@ -145,7 +148,7 @@ export const Firewall = () => {
const items = state.map((item, index) => (
<Draggable key={item.rule_id} index={index} draggableId={item.rule_id}>
item.table == selectedTab && <Draggable key={item.rule_id} index={index} draggableId={item.rule_id}>
{(provided, snapshot) => {
const customInt = [
{ value: "0.0.0.0/0", netint: "ANY IPv4", label: "0.0.0.0/0" },
@@ -313,6 +316,7 @@ export const Firewall = () => {
value={item.mode}
onChange={(value)=>{item.mode = value as RuleMode;updateMe()}}
style={{width:"100%"}}
table={item.table}
/>, !isMedium)}
<Space h="xs" />
{condDiv(<ProtocolSelector
@@ -335,7 +339,7 @@ export const Firewall = () => {
</Box>
}}
</Draggable>
));
)).filter(v => v);
return <>
@@ -389,6 +393,18 @@ export const Firewall = () => {
</Box>
<Space h="xl" />
<Divider />
<Space h="md"/>
<Tabs variant="pills" value={selectedTab} onChange={(v)=>setSelectedTab(v==NFTables.MANGLE?NFTables.MANGLE:NFTables.FILTER)} style={{ display:"flex", justifyContent:"center", alignItems:"center"}}>
<Box mr="md">Filtering Table:</Box>
<Tabs.List>
<Tabs.Tab value={NFTables.FILTER}>
Filter
</Tabs.Tab>
<Tabs.Tab value={NFTables.MANGLE}>
Mangle
</Tabs.Tab>
</Tabs.List>
</Tabs>
{items.length > 0?<DragDropContext
onDragEnd={({ destination, source }) =>
handlers.reorder({ from: source.index, to: destination?.index || 0 })