Frontend re-styling

This commit is contained in:
Domingo Dirutigliano
2025-02-12 22:24:59 +01:00
parent 2fb77a348f
commit ec3bd84aaf
14 changed files with 330 additions and 170 deletions

View File

@@ -55,7 +55,7 @@ This means that firegex is projected to avoid any possibility to have the servic
Initiially the project was based only on regex filters, and also now the main function uses regexes, but firegex have and will have also other filtering tools.
# Credits
- Copyright (c) 2022 Pwnzer0tt1
- Copyright (c) 2022-2025 Pwnzer0tt1
## Star History

View File

@@ -141,17 +141,17 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
"@mantine/core": ["@mantine/core@7.16.2", "", { "dependencies": { "@floating-ui/react": "^0.26.28", "clsx": "^2.1.1", "react-number-format": "^5.4.3", "react-remove-scroll": "^2.6.2", "react-textarea-autosize": "8.5.6", "type-fest": "^4.27.0" }, "peerDependencies": { "@mantine/hooks": "7.16.2", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-6dwFz+8HrOqFan7GezgpoWyZSCxedh10S8iILGVsc3GXiD4gzo+3VZndZKccktkYZ3GVC9E3cCS3SxbiyKSAVw=="],
"@mantine/core": ["@mantine/core@7.16.3", "", { "dependencies": { "@floating-ui/react": "^0.26.28", "clsx": "^2.1.1", "react-number-format": "^5.4.3", "react-remove-scroll": "^2.6.2", "react-textarea-autosize": "8.5.6", "type-fest": "^4.27.0" }, "peerDependencies": { "@mantine/hooks": "7.16.3", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-cxhIpfd2i0Zmk9TKdejYAoIvWouMGhzK3OOX+VRViZ5HEjnTQCGl2h3db56ThqB6NfVPCno6BPbt5lwekTtmuQ=="],
"@mantine/form": ["@mantine/form@7.16.2", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "klona": "^2.0.6" }, "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-JZkLbZ7xWAZndPrxObkf10gjHj57x8yvI/vobjDhfWN3zFPTSWmSSF6yBE1FpITseOs3oR03hlkqG6EclK6g+g=="],
"@mantine/form": ["@mantine/form@7.16.3", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "klona": "^2.0.6" }, "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-GqomUG2Ri5adxYsTU1S5IhKRPcqTG5JkPvMERns8PQAcUz/lvzsnk3wY1v4K5CEbCAdpimle4bSsZTM9g697vg=="],
"@mantine/hooks": ["@mantine/hooks@7.16.2", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-ZFHQhDi9T+r6VR5NEeE47gigPPIAHVIKDOCWsCsbCqHc3yz5l8kiO2RdfUmsTKV2KD/AiXnAw4b6pjQEP58GOg=="],
"@mantine/hooks": ["@mantine/hooks@7.16.3", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-B94FBWk5Sc81tAjV+B3dGh/gKzfqzpzVC/KHyBRWOOyJRqeeRbI/FAaJo4zwppyQo1POSl5ArdyjtDRrRIj2SQ=="],
"@mantine/modals": ["@mantine/modals@7.16.2", "", { "peerDependencies": { "@mantine/core": "7.16.2", "@mantine/hooks": "7.16.2", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-REwAV53Fcz021EE3zLyYdkdFlfG+b24y279Y+eA1jCCH9VMLivXL+gacrox4BcpzREsic9nGVInSNv3VJwPlAQ=="],
"@mantine/modals": ["@mantine/modals@7.16.3", "", { "peerDependencies": { "@mantine/core": "7.16.3", "@mantine/hooks": "7.16.3", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-BJuDzRugK6xLbuFTTo8NLJumVvVmSYsNVcEtmlXOWTE3NkDGktBXGKo8V1B0XfJ9/d/rZw7HCE0p4i76MtA+bQ=="],
"@mantine/notifications": ["@mantine/notifications@7.16.2", "", { "dependencies": { "@mantine/store": "7.16.2", "react-transition-group": "4.4.5" }, "peerDependencies": { "@mantine/core": "7.16.2", "@mantine/hooks": "7.16.2", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-U342XWiiRI1NvOlLsI6PH/pSNe0rxNClJ2w5orvjOMXvaAfDe52mhnzRmtzRxYENp06++3b/G7MjPH+466rF9Q=="],
"@mantine/notifications": ["@mantine/notifications@7.16.3", "", { "dependencies": { "@mantine/store": "7.16.3", "react-transition-group": "4.4.5" }, "peerDependencies": { "@mantine/core": "7.16.3", "@mantine/hooks": "7.16.3", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-wtEME9kSYfXWYmAmQUZ8c+rwNmhdWRBaW1mlPdQsPkzMqkv4q6yy0IpgwcnuHStSG9EHaQBXazmVxMZJdEAWBQ=="],
"@mantine/store": ["@mantine/store@7.16.2", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-9dEGLosrYSePlAwhfx3CxTLcWu2M98TtuYnelAiHEdNEkyafirvZxNt4paMoFXLKR1XPm5wdjDK7bdTaE0t7Og=="],
"@mantine/store": ["@mantine/store@7.16.3", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-6M2M5+0BrRtnVv+PUmr04tY1RjPqyapaHplo90uK1NMhP/1EIqrwTL9KoEtCNCJ5pog1AQtu0bj0QPbqUvxwLg=="],
"@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="],
@@ -205,7 +205,7 @@
"@types/jest": ["@types/jest@27.5.2", "", { "dependencies": { "jest-matcher-utils": "^27.0.0", "pretty-format": "^27.0.0" } }, "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA=="],
"@types/node": ["@types/node@20.17.16", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw=="],
"@types/node": ["@types/node@20.17.17", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg=="],
"@types/prop-types": ["@types/prop-types@15.7.14", "", {}, "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="],

View File

@@ -5,14 +5,14 @@
"private": true,
"dependencies": {
"@hello-pangea/dnd": "^16.6.0",
"@mantine/core": "^7.16.2",
"@mantine/form": "^7.16.2",
"@mantine/hooks": "^7.16.2",
"@mantine/modals": "^7.16.2",
"@mantine/notifications": "^7.16.2",
"@mantine/core": "^7.16.3",
"@mantine/form": "^7.16.3",
"@mantine/hooks": "^7.16.3",
"@mantine/modals": "^7.16.3",
"@mantine/notifications": "^7.16.3",
"@tanstack/react-query": "^4.36.1",
"@types/jest": "^27.5.2",
"@types/node": "^20.17.16",
"@types/node": "^20.17.17",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"buffer": "^6.0.3",

View File

@@ -86,16 +86,16 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
/>
</Box>
<Group align="right" mt="md">
<Group justify='flex-end' mt="md" mb="sm">
<Button loading={submitLoading} type="submit">Add Service</Button>
</Group>
<Space h="md" />
{error?<>
<Space h="md" />
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</Notification><Space h="md" />
</>:null}
</form>
</Modal>

View File

@@ -49,16 +49,16 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
placeholder="Awesome Service Name!"
{...form.getInputProps('name')}
/>
<Group align="right" mt="md">
<Group mt="md" justify="flex-end" mb="sm">
<Button loading={submitLoading} type="submit">Rename</Button>
</Group>
<Space h="md" />
{error?<>
<Space h="md" />
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</Notification><Space h="md" />
</>:null}
</form>
</Modal>

View File

@@ -2,7 +2,7 @@ import { ActionIcon, Badge, Box, Divider, Grid, Menu, Space, Title, Tooltip } fr
import { useState } from 'react';
import { FaPlay, FaStop } from 'react-icons/fa';
import { nfregex, Service, serviceQueryKey } from '../utils';
import { MdOutlineArrowForwardIos } from "react-icons/md"
import { MdDoubleArrow, MdOutlineArrowForwardIos } from "react-icons/md"
import YesNoModal from '../../YesNoModal';
import { errorNotify, isMediumScreen, okNotify, regex_ipv4 } from '../../../js/utils';
import { BsTrashFill } from 'react-icons/bs';
@@ -10,8 +10,10 @@ import { BiRename } from 'react-icons/bi'
import RenameForm from './RenameForm';
import { MenuDropDownWithButton } from '../../MainLayout';
import { useQueryClient } from '@tanstack/react-query';
import { FaFilter } from "react-icons/fa";
import { VscRegex } from "react-icons/vsc";
function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) {
export default function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) {
let status_color = "gray";
switch(service.status){
@@ -72,36 +74,34 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
return <>
<Box className='firegex__nfregex__rowbox'>
<Grid className="firegex__nfregex__row" justify="flex-end" style={{width:"100%"}}>
<Grid.Col span={{ md:4, xs: 12 }}>
<Box className={"center-flex-row"}>
<Title className="firegex__nfregex__name">
<Box className="firegex__nfregex__row" style={{width:"100%", flexDirection: isMedium?"row":"column"}}>
<Box>
<Box className="center-flex" style={{ justifyContent: "flex-start" }}>
<MdDoubleArrow size={30} style={{color: "white"}}/>
<Title className="firegex__nfregex__name" ml="xs">
{service.name}
</Title>
<Box className="center-flex" style={{ gap: 6 }}>
<Badge color={status_color} radius="md" size="lg" variant="filled">Status: <u>{service.status}</u></Badge>
<Badge size="lg" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient" radius="md">
</Box>
<Box className="center-flex" style={{ gap: 8, marginTop: 15, justifyContent: "flex-start" }}>
<Badge color={status_color} radius="md" size="lg" variant="filled">{service.status}</Badge>
<Badge size="lg" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient" radius="md" style={{ fontSize: "110%" }}>
:{service.port}
</Badge>
</Box>
{isMedium?null:<Space w="xl" />}
</Box>
</Grid.Col>
<Grid.Col className={isMedium?"center-flex":"center-flex-row"} span={{ md:8, xs: 12 }}>
<Box visibleFrom='md' className='flex-spacer' />
<Space hiddenFrom='md' h="md" />
<Space hiddenFrom='md' w="xl" />
<Space hiddenFrom='md' w="md" />
<Box className={isMedium?"center-flex":"center-flex-row"}>
<Box className="center-flex-row">
<Badge color="yellow" radius="sm" size="md" variant="filled">Connections Blocked: {service.n_packets}</Badge>
<Space h="xs" />
<Badge color="violet" radius="sm" size="md" variant="filled">Regex: {service.n_regex}</Badge>
<Space h="xs" />
<Badge color={service.ip_int.match(regex_ipv4)?"cyan":"pink"} radius="sm" size="md" variant="filled">{service.ip_int} on {service.proto}</Badge>
<Space h="xs" />
<Box className='center-flex'>
<Badge color="yellow" radius="sm" size="md" variant="filled"><FaFilter style={{ marginBottom: -2}} /> {service.n_packets}</Badge>
<Space w="xs" />
<Badge color="violet" radius="sm" size="md" variant="filled"><VscRegex style={{ marginBottom: -2}} size={13} /> {service.n_regex}</Badge>
</Box>
{isMedium?<Box className='flex-spacer' />:<Space h="xl" />}
</Box>
{isMedium?<Space w="xl" />:<Space h="lg" />}
<Box className="center-flex">
<MenuDropDownWithButton>
<Menu.Label><b>Rename service</b></Menu.Label>
@@ -129,14 +129,12 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
</ActionIcon>
</Tooltip>
{isMedium?<Space w="xl" />:<Space w="md" />}
{onClick?<Box style={{ backgroundColor: "var(--secondary_color)", borderRadius: "38%", width:"35px", height:"35px", display:"flex", justifyContent: "center", alignItems: "center", border:"#AAA 2px solid"}}>
{onClick?<Box className='firegex__service_forward_btn'>
<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size={25} />
</Box>:null}
{isMedium?<Space w="xl" />:null}
</Box>
</Grid.Col>
</Grid>
</Box>
</Box>
</Box>
<YesNoModal
title='Are you sure to delete this service?'
@@ -152,5 +150,3 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
/>
</>
}
export default ServiceRow;

View File

@@ -94,16 +94,16 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
/>
</Box>
<Group align="right" mt="md">
<Group justify='flex-end' mt="md" mb="sm">
<Button loading={submitLoading} type="submit">Add Service</Button>
</Group>
<Space h="md" />
{error?<>
<Space h="md" />
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</Notification><Space h="md" />
</>:null}
</form>
</Modal>

View File

@@ -53,15 +53,16 @@ function ChangeDestination({ opened, onClose, service }:{ opened:boolean, onClos
<form onSubmit={form.onSubmit(submitRequest)}>
<PortAndInterface form={form} int_name="ip_dst" port_name="proxy_port" />
<Group align="right" mt="md">
<Group justify='flex-end' mt="xl" mb="sm">
<Button loading={submitLoading} type="submit">Change</Button>
</Group>
<Space h="md" />
{error?<>
<Space h="md" />
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</Notification><Space h="md" />
</>:null}
</form>
</Modal>

View File

@@ -49,16 +49,16 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
placeholder="Awesome Service Name!"
{...form.getInputProps('name')}
/>
<Group align="right" mt="md">
<Group mt="md" justify="flex-end" mb="sm">
<Button loading={submitLoading} type="submit">Rename</Button>
</Group>
<Space h="md" />
{error?<>
<Space h="md" />
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</Notification><Space h="md" />
</>:null}
</form>
</Modal>

View File

@@ -8,11 +8,11 @@ import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs';
import { BiRename } from 'react-icons/bi'
import RenameForm from './RenameForm';
import ChangeDestination from './ChangeDestination';
import PortInput from '../../PortInput';
import { useForm } from '@mantine/form';
import { MenuDropDownWithButton } from '../../MainLayout';
import { MdDoubleArrow } from "react-icons/md";
function ServiceRow({ service }:{ service:Service }) {
export default function ServiceRow({ service }:{ service:Service }) {
let status_color = service.active ? "teal": "red"
@@ -72,40 +72,36 @@ function ServiceRow({ service }:{ service:Service }) {
return <>
<Box className='firegex__nfregex__rowbox'>
<Grid className="firegex__nfregex__row" justify="flex-end" style={{width:"100%"}}>
<Grid.Col span={{ md:4, xs: 12 }}>
<Box className={"center-flex-row"}>
<Title className="firegex__nfregex__name">
<Box className="firegex__nfregex__row" style={{width:"100%", flexDirection: isMedium?"row":"column"}}>
<Box>
<Box className="center-flex" style={{ justifyContent: "flex-start" }}>
<MdDoubleArrow size={30} style={{color: "white"}}/>
<Title className="firegex__nfregex__name" ml="xs">
{service.name}
</Title>
<Box className="center-flex" style={{ gap: 6 }}>
<Badge color={status_color} radius="md" size="lg" variant="filled">Status: <u>{service.active?"ENABLED":"DISABLED"}</u></Badge>
<Badge color={service.proto === "tcp"?"cyan":"orange"} radius="md" size="lg" variant="filled">
</Box>
<Box className="center-flex" style={{ gap: 8, marginTop: 15, justifyContent: "flex-start" }}>
<Badge color={status_color} radius="md" size="md" variant="filled">{service.active?"ENABLED":"DISABLED"}</Badge>
<Badge color={service.proto === "tcp"?"cyan":"orange"} radius="md" size="md" variant="filled">
{service.proto}
</Badge>
</Box>
{isMedium?null:<Space w="xl" />}
</Box>
</Grid.Col>
<Grid.Col className={isMedium?"center-flex":"center-flex-row"} span={{ md:8, xs: 12 }}>
<Box visibleFrom='md' className='flex-spacer' />
<Space hiddenFrom='md' h="md" />
<Space hiddenFrom='md' w="xl" />
<Space hiddenFrom='md' w="md" />
<Box className={isMedium?"center-flex":"center-flex-row"}>
<Box className="center-flex-row">
<Badge color="lime" radius="sm" size="md" variant="filled">
<Badge color="lime" radius="sm" size="lg" variant="filled">
FROM {service.ip_src} :{service.public_port}
</Badge>
<Space h="sm" />
<Badge color="blue" radius="sm" size="md" variant="filled">
<Badge color="blue" radius="sm" size="lg" variant="filled">
<Box className="center-flex">
TO {service.ip_dst} : service.proxy_port
TO {service.ip_dst} :{service.proxy_port}
</Box>
</Badge>
</Box>
{isMedium?<Box className='flex-spacer' />:<Space h="xl" />}
{isMedium?<Space w="xl" />:<Space h="lg" />}
<Box className="center-flex">
<MenuDropDownWithButton>
<Menu.Label><b>Rename service</b></Menu.Label>
@@ -134,14 +130,10 @@ function ServiceRow({ service }:{ service:Service }) {
<FaPlay size="20px" />
</ActionIcon>
</Tooltip>
</Box>
{isMedium?<Space w="xl" />:null}
</Grid.Col>
</Grid>
</Box>
</Box>
</Box>
<YesNoModal
title='Are you sure to delete this service?'
@@ -162,5 +154,3 @@ function ServiceRow({ service }:{ service:Service }) {
/>
</>
}
export default ServiceRow;

View File

@@ -1,18 +1,19 @@
import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip, Box } from '@mantine/core';
import { Text, Title, Badge, Space, ActionIcon, Tooltip, Box } from '@mantine/core';
import { useState } from 'react';
import { RegexFilter } from '../../js/models';
import { b64decode, errorNotify, getapiobject, okNotify } from '../../js/utils';
import { b64decode, errorNotify, getapiobject, isMediumScreen, okNotify } from '../../js/utils';
import { BsTrashFill } from "react-icons/bs"
import YesNoModal from '../YesNoModal';
import { FaPause, FaPlay } from 'react-icons/fa';
import { useClipboard } from '@mantine/hooks';
import { FaFilter } from "react-icons/fa";
import { VscRegex } from "react-icons/vsc";
function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
const mode_string = regexInfo.mode === "C"? "C -> S":
regexInfo.mode === "S"? "S -> C":
regexInfo.mode === "B"? "S <-> C": "🤔"
regexInfo.mode === "B"? "C <-> S": "🤔"
let regex_expr = b64decode(regexInfo.regex);
@@ -20,6 +21,7 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
const [deleteTooltipOpened, setDeleteTooltipOpened] = useState(false);
const [statusTooltipOpened, setStatusTooltipOpened] = useState(false);
const clipboard = useClipboard({ timeout: 500 });
const isMedium = isMediumScreen();
const deleteRegex = () => {
getapiobject().regexdelete(regexInfo.id).then(res => {
@@ -42,19 +44,14 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
}
return <Box className="firegex__regexview__box">
<Grid>
<Grid.Col span={2} className="center-flex">
<Title order={4}>Regex:</Title>
</Grid.Col>
<Grid.Col span={8}>
<Box>
<Box className='center-flex' style={{width: "100%"}}>
<Box className="firegex__regexview__outer_regex_text">
<Text className="firegex__regexview__regex_text" onClick={()=>{
clipboard.copy(regex_expr)
okNotify("Regex copied to clipboard!",`The regex '${regex_expr}' has been copied to the clipboard!`)
}}>{regex_expr}</Text>
</Box>
</Grid.Col>
<Grid.Col span={2} className='center-flex'>
<Space w="xs" />
<Tooltip label={regexInfo.active?"Deactivate":"Activate"} zIndex={0} color={regexInfo.active?"orange":"teal"} opened={statusTooltipOpened}>
<ActionIcon color={regexInfo.active?"orange":"teal"} onClick={changeRegexStatus} size="xl" radius="md" variant="filled"
@@ -69,30 +66,17 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
onMouseEnter={() => setDeleteTooltipOpened(true)} onMouseLeave={() => setDeleteTooltipOpened(false)}
><BsTrashFill size={22} /></ActionIcon>
</Tooltip>
</Grid.Col>
<Grid.Col className='center-flex' span={12}>
<Box className='center-flex-row'>
<Space h="md" />
<Box className='center-flex'>
<Badge size="md" color="cyan" variant="filled">Service: {regexInfo.service_id}</Badge>
</Box>
<Box display="flex" mt="sm" ml="xs">
<Badge size="md" color="yellow" variant="filled"><FaFilter style={{ marginBottom: -2}} /> {regexInfo.n_packets}</Badge>
<Space w="xs" />
<Badge size="md" color={regexInfo.active?"lime":"red"} variant="filled">{regexInfo.active?"ACTIVE":"DISABLED"}</Badge>
<Space w="xs" />
<Badge size="md" color="gray" variant="filled">ID: {regexInfo.id}</Badge>
<Badge size="md" color={regexInfo.is_case_sensitive?"grape":"pink"} variant="filled">{regexInfo.is_case_sensitive?"Strict":"Loose"}</Badge>
<Space w="xs" />
<Badge size="md" color="blue" variant="filled">{mode_string}</Badge>
</Box>
</Box>
<Box className='flex-spacer' />
<Box className='center-flex-row'>
<Badge size="md" color={regexInfo.is_case_sensitive?"grape":"pink"} variant="filled">Case: {regexInfo.is_case_sensitive?"SENSIIVE":"INSENSITIVE"}</Badge>
<Space h="xs" />
<Badge size="md" color="yellow" variant="filled">Packets filtered: {regexInfo.n_packets}</Badge>
<Space h="xs" />
<Badge size="md" color="blue" variant="filled">Mode: {mode_string}</Badge>
</Box>
</Grid.Col>
</Grid>
<YesNoModal
title='Are you sure to delete this regex?'
description={`You are going to delete the regex '${regex_expr}'.`}

View File

@@ -5,7 +5,7 @@ function YesNoModal( { title, description, action, onClose, opened}:{ title:stri
return <Modal size="xl" title={title} opened={opened} onClose={onClose} centered>
{description}
<Group align="right" mt="md">
<Group justify='flex-end' mt="md">
<Button onClick={()=>{
onClose()
action()

View File

@@ -32,14 +32,15 @@ body {
}
::-webkit-scrollbar {
width: 6px;
margin:3px;
width: 4px;
height: 4px;
margin:2px;
background: #333;
cursor: pointer;
}
::-webkit-scrollbar-thumb {
background: #757575;
border-radius: 8px;
width: 2px;
background: #777;
border-radius: 5px;
}
.mantine-Modal-content {
@@ -72,7 +73,7 @@ body {
}
.firegex__regexview__box{
padding:30px;
padding:10px;
margin:5px;
}
@@ -80,6 +81,7 @@ body {
border-radius: 8px;
overflow: hidden;
margin: 6px;
width: 100%;
}
.firegex__regexview__regex_text{
@@ -131,25 +133,25 @@ body {
text-decoration: underline;
}
.firegex__nfregex__row{
width: 95%;
padding: 20px;
border-radius: 20px;
padding: 10px;
padding-left: 35px;
padding-right: 35px;
display: flex;
justify-content: center;
justify-content: space-between;
align-items: center;
}
.firegex__nfregex__name{
font-size: 1.8em;
font-size: 1.6em;
font-weight: bolder;
margin-right: 10px;
margin-bottom: 13px;
text-transform: capitalize;
color:#FFF;
max-width: 300px;
overflow: hidden;
text-align: center;
margin-left: -20px;
margin-right: 20px;
}
.firegex__nfregex__name:hover{
@@ -207,7 +209,47 @@ body {
.firegex__nfregex__rowbox{
width: 100%;
height: 150px;
align-items: center;
justify-content: center;
display: flex;
background-color: var(--fourth_color);
border-radius: 20px;
border-radius: 10px;
border: #444 3px solid;
}
@media (max-width: 599px) {
.firegex__nfregex__row{
margin: 0;
padding: 0;
}
.firegex__nfregex__row > *{
padding: 10px 0;
}
.firegex__nfregex__rowbox{
height: 100%;
width: 90%;
padding: 20px 20px;
margin: 0;
}
.firegex__nfregex__name{
padding: 0;
margin: 0;
}
}
.firegex__service_forward_btn{
background-color: var(--primary_color);
border-radius: 38%;
width: 35px;
height: 35px;
display: flex;
justify-content: center;
align-items: center;
border: 2px solid #DDD;
transition: 0.3s;
}
.firegex__service_forward_btn:hover{
opacity: 0.8;
transition: 0.3s;
}

View File

@@ -1,13 +1,25 @@
import { ActionIcon, Box, Grid, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
import { useState } from 'react';
import { Navigate, useParams } from 'react-router-dom';
import { Navigate, useNavigate, useParams } from 'react-router-dom';
import RegexView from '../../components/RegexView';
import ServiceRow from '../../components/NFRegex/ServiceRow';
import AddNewRegex from '../../components/AddNewRegex';
import { BsPlusLg } from "react-icons/bs";
import { nfregexServiceQuery, nfregexServiceRegexesQuery } from '../../components/NFRegex/utils';
import { Badge, Divider, Menu } from '@mantine/core';
import { useState } from 'react';
import { FaFilter, FaPlay, FaStop } from 'react-icons/fa';
import { nfregex, serviceQueryKey } from '../../components/NFRegex/utils';
import { MdDoubleArrow } from "react-icons/md"
import YesNoModal from '../../components/YesNoModal';
import { errorNotify, isMediumScreen, okNotify, regex_ipv4 } from '../../js/utils';
import { BsTrashFill } from 'react-icons/bs';
import { BiRename } from 'react-icons/bi'
import RenameForm from '../../components/NFRegex/ServiceRow/RenameForm';
import { MenuDropDownWithButton } from '../../components/MainLayout';
import { useQueryClient } from '@tanstack/react-query';
import { FaArrowLeft } from "react-icons/fa";
import { VscRegex } from 'react-icons/vsc';
function ServiceDetailsNFRegex() {
export default function ServiceDetailsNFRegex() {
const {srv} = useParams()
const [open, setOpen] = useState(false)
@@ -15,13 +27,138 @@ function ServiceDetailsNFRegex() {
const serviceInfo = services.data?.find(s => s.service_id == srv)
const [tooltipAddRegexOpened, setTooltipAddRegexOpened] = useState(false)
const regexesList = nfregexServiceRegexesQuery(srv??"")
const [deleteModal, setDeleteModal] = useState(false)
const [renameModal, setRenameModal] = useState(false)
const [buttonLoading, setButtonLoading] = useState(false)
const queryClient = useQueryClient()
const [tooltipStopOpened, setTooltipStopOpened] = useState(false);
const [tooltipBackOpened, setTooltipBackOpened] = useState(false);
const navigate = useNavigate()
const isMedium = isMediumScreen()
if (services.isLoading) return <LoadingOverlay visible={true} />
if (!srv || !serviceInfo || regexesList.isError) return <Navigate to="/" replace />
let status_color = "gray";
switch(serviceInfo.status){
case "stop": status_color = "red"; break;
case "active": status_color = "teal"; break;
}
const startService = async () => {
setButtonLoading(true)
await nfregex.servicestart(serviceInfo.service_id).then(res => {
if(!res){
okNotify(`Service ${serviceInfo.name} started successfully!`,`The service on ${serviceInfo.port} has been started!`)
queryClient.invalidateQueries(serviceQueryKey)
}else{
errorNotify(`An error as occurred during the starting of the service ${serviceInfo.port}`,`Error: ${res}`)
}
}).catch(err => {
errorNotify(`An error as occurred during the starting of the service ${serviceInfo.port}`,`Error: ${err}`)
})
setButtonLoading(false)
}
const deleteService = () => {
nfregex.servicedelete(serviceInfo.service_id).then(res => {
if (!res){
okNotify("Service delete complete!",`The service ${serviceInfo.name} has been deleted!`)
queryClient.invalidateQueries(serviceQueryKey)
}else
errorNotify("An error occurred while deleting a service",`Error: ${res}`)
}).catch(err => {
errorNotify("An error occurred while deleting a service",`Error: ${err}`)
})
}
const stopService = async () => {
setButtonLoading(true)
await nfregex.servicestop(serviceInfo.service_id).then(res => {
if(!res){
okNotify(`Service ${serviceInfo.name} stopped successfully!`,`The service on ${serviceInfo.port} has been stopped!`)
queryClient.invalidateQueries(serviceQueryKey)
}else{
errorNotify(`An error as occurred during the stopping of the service ${serviceInfo.port}`,`Error: ${res}`)
}
}).catch(err => {
errorNotify(`An error as occurred during the stopping of the service ${serviceInfo.port}`,`Error: ${err}`)
})
setButtonLoading(false);
}
return <>
<LoadingOverlay visible={regexesList.isLoading} />
<ServiceRow service={serviceInfo} />
<Box className={isMedium?'center-flex':'center-flex-row'} style={{ justifyContent: "space-between"}} px="md" mt="lg">
<Box>
<Title order={1}>
<Box className="center-flex">
<MdDoubleArrow /><Space w="sm" />{serviceInfo.name}
</Box>
</Title>
</Box>
{isMedium?null:<Space h="md" />}
<Box className='center-flex'>
<Badge color={status_color} radius="md" size="xl" variant="filled" mr="sm">
{serviceInfo.status}
</Badge>
<Badge size="xl" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient" radius="md" mr="sm">
:{serviceInfo.port}
</Badge>
<MenuDropDownWithButton>
<Menu.Label><b>Rename service</b></Menu.Label>
<Menu.Item leftSection={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
<Divider />
<Menu.Label><b>Danger zone</b></Menu.Label>
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
</MenuDropDownWithButton>
</Box>
</Box>
{isMedium?null:<Space h="md" />}
<Box className={isMedium?'center-flex':'center-flex-row'} style={{ justifyContent: "space-between"}} px="md" mt="lg">
<Box className={isMedium?'center-flex':'center-flex-row'}>
<Box className='center-flex'>
<Badge color="yellow" radius="sm" size="md" variant="filled"><FaFilter style={{ marginBottom: -2}} /> {serviceInfo.n_packets}</Badge>
<Space w="xs" />
<Badge color="violet" radius="sm" size="md" variant="filled"><VscRegex style={{ marginBottom: -2}} size={13} /> {serviceInfo.n_regex}</Badge>
</Box>
{isMedium?<Space w="xs" />:<Space h="xs" />}
<Badge color={serviceInfo.ip_int.match(regex_ipv4)?"cyan":"pink"} radius="sm" size="md" variant="filled" mr="xs">{serviceInfo.ip_int} on {serviceInfo.proto}</Badge>
</Box>
{isMedium?null:<Space h="xl" />}
<Box className='center-flex'>
<Tooltip label="Go back" zIndex={0} color="cyan" opened={tooltipBackOpened}>
<ActionIcon color="cyan"
onClick={() => navigate("/")} size="xl" radius="md" variant="filled"
aria-describedby="tooltip-back-id"
onFocus={() => setTooltipBackOpened(false)} onBlur={() => setTooltipBackOpened(false)}
onMouseEnter={() => setTooltipBackOpened(true)} onMouseLeave={() => setTooltipBackOpened(false)}>
<FaArrowLeft size="25px" />
</ActionIcon>
</Tooltip>
<Space w="md"/>
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
<ActionIcon color="red" loading={buttonLoading}
onClick={stopService} size="xl" radius="md" variant="filled"
disabled={serviceInfo.status === "stop"}
aria-describedby="tooltip-stop-id"
onFocus={() => setTooltipStopOpened(false)} onBlur={() => setTooltipStopOpened(false)}
onMouseEnter={() => setTooltipStopOpened(true)} onMouseLeave={() => setTooltipStopOpened(false)}>
<FaStop size="20px" />
</ActionIcon>
</Tooltip>
<Space w="md"/>
<Tooltip label="Start service" zIndex={0} color="teal">
<ActionIcon color="teal" size="xl" radius="md" onClick={startService} loading={buttonLoading}
variant="filled" disabled={!["stop","pause"].includes(serviceInfo.status)?true:false}>
<FaPlay size="20px" />
</ActionIcon>
</Tooltip>
</Box>
</Box>
<Divider my="xl" />
{(!regexesList.data || regexesList.data.length == 0)?<>
<Space h="xl" />
<Title className='center-flex' style={{textAlign:"center"}} order={3}>No regex found for this service! Add one by clicking the "+" buttons</Title>
@@ -41,7 +178,17 @@ function ServiceDetailsNFRegex() {
}
{srv?<AddNewRegex opened={open} onClose={() => {setOpen(false);}} service={srv} />:null}
<YesNoModal
title='Are you sure to delete this service?'
description={`You are going to delete the service '${serviceInfo.port}', causing the stopping of the firewall and deleting all the regex associated. This will cause the shutdown of your service! ⚠️`}
onClose={()=>setDeleteModal(false) }
action={deleteService}
opened={deleteModal}
/>
<RenameForm
onClose={()=>setRenameModal(false)}
opened={renameModal}
service={serviceInfo}
/>
</>
}
export default ServiceDetailsNFRegex;