add: frontend update

This commit is contained in:
Domingo Dirutigliano
2024-10-13 01:50:52 +02:00
parent fffc9e3b89
commit cbf2c5a43d
53 changed files with 778 additions and 1255 deletions

View File

@@ -10,6 +10,7 @@ from utils import API_VERSION, FIREGEX_PORT, JWT_ALGORITHM, get_interfaces, sock
from utils.loader import frontend_deploy, load_routers
from utils.models import ChangePasswordModel, IpInterface, PasswordChangeForm, PasswordForm, ResetRequest, StatusModel, StatusMessageModel
from contextlib import asynccontextmanager
from fastapi.middleware.cors import CORSMiddleware
# DB init
db = SQLite('db/firegex.db')
@@ -32,6 +33,9 @@ async def lifespan(app):
app = FastAPI(debug=DEBUG, redoc_url=None, lifespan=lifespan)
utils.socketio = SocketManager(app, "/sock", socketio_path="")
if DEBUG:
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
def APP_STATUS(): return "init" if db.get("password") is None else "run"
def JWT_SECRET(): return db.get("secret")
@@ -158,7 +162,7 @@ if __name__ == '__main__':
os.chdir(os.path.dirname(os.path.realpath(__file__)))
uvicorn.run(
"app:app",
host="0.0.0.0" if DEBUG else None,
host="::" if DEBUG else None,
port=FIREGEX_PORT,
reload=DEBUG,
access_log=True,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

View File

@@ -2,39 +2,33 @@
"name": "firegex-frontend",
"version": "0.1.0",
"private": true,
"type": "module",
"dependencies": {
"@emotion/react": "^11.11.0",
"@hello-pangea/dnd": "^16.3.0",
"@mantine/core": "^6.0.21",
"@mantine/form": "^6.0.21",
"@mantine/hooks": "^6.0.21",
"@mantine/modals": "^6.0.21",
"@mantine/notifications": "^6.0.21",
"@mantine/prism": "^6.0.21",
"@mantine/spotlight": "^6.0.21",
"@tanstack/react-query": "^4.35.3",
"@testing-library/dom": "^9.3.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"@hello-pangea/dnd": "^16.6.0",
"@mantine/core": "^7.13.2",
"@mantine/form": "^7.13.2",
"@mantine/hooks": "^7.13.2",
"@mantine/modals": "^7.13.2",
"@mantine/notifications": "^7.13.2",
"@tanstack/react-query": "^4.36.1",
"@types/jest": "^27.5.2",
"@types/node": "^20.2.5",
"@types/react": "^18.0.12",
"@types/react-dom": "^18.0.5",
"@types/node": "^20.16.11",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"buffer": "^6.0.3",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-icons": "^4.4.0",
"react-router-dom": "^6.3.0",
"sass": "^1.62.1",
"socket.io-client": "^4.5.1",
"typescript": "^4.7.3",
"web-vitals": "^2.1.4"
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-router-dom": "^6.27.0",
"socket.io-client": "^4.8.0",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4",
"zustand": "^5.0.0-rc.2"
},
"scripts": {
"start": "vite",
"dev": "vite",
"build": "tsc && vite build",
"serve": "vite preview"
"preview": "vite preview"
},
"eslintConfig": {
"extends": [
@@ -55,10 +49,10 @@
]
},
"devDependencies": {
"@tanstack/react-query-devtools": "^4.35.3",
"@vitejs/plugin-react": "^4.0.0",
"vite": "^4.3.9",
"vite-plugin-svgr": "^3.2.0",
"vite-tsconfig-paths": "^4.2.0"
"@tanstack/react-query-devtools": "^4.36.1",
"@vitejs/plugin-react": "^4.3.2",
"vite": "^4.5.5",
"vite-plugin-svgr": "^3.3.0",
"vite-tsconfig-paths": "^4.3.2"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -0,0 +1,31 @@
Dockerfile
**/*.pyc
**/__pycache__/
**/.vscode/**
**/.vscode/
**/.mypy_cache/**
**/.mypy_cache/
**/node_modules
**/.pnp
**/.pnp.js
# testing
/coverage
/build/
/build/**
/node_modules/
# misc
**/.DS_Store
**/.env.local
**/.env.development.local
**/.env.test.local
**/.env.production.local
**/npm-debug.log*
**/yarn-debug.log*
**/yarn-error.log*

View File

@@ -1,4 +1,4 @@
import { Button, Group, Loader, LoadingOverlay, Notification, Space, PasswordInput, Title } from '@mantine/core';
import { Button, Group, Loader, LoadingOverlay, Notification, Space, PasswordInput, Title, Box } from '@mantine/core';
import { useForm } from '@mantine/form';
import { useEffect, useState } from 'react';
import { ImCross } from 'react-icons/im';
@@ -66,15 +66,15 @@ function App() {
if (loading){
return <LoadingOverlay visible/>
}else if (reqError){
return <div className='center-flex-row' style={{padding:"100px"}}>
<Title order={1} align="center">Error launching Firegex! 🔥</Title>
return <Box className='center-flex-row' style={{padding:"100px"}}>
<Title order={1} style={{textAlign:"center"}}>Error launching Firegex! 🔥</Title>
<Space h="md" />
<Title order={4} align="center">Error communicating with backend</Title>
<Title order={4} style={{textAlign:"center"}}>Error communicating with backend</Title>
<Space h="md" />
Error: {reqError}
<Space h="xl" />
<Loader />
</div>
</Box>
}else if (systemStatus.status === "init"){
const submitRequest = async (values:PasswordSend) => {
@@ -90,8 +90,8 @@ function App() {
}
return <div className='center-flex-row' style={{padding:"100px"}}>
<Title order={3} align="center">Setup: Choose the password for access to the firewall 🔒</Title>
return <Box className='center-flex-row' style={{padding:"100px"}}>
<Title order={3} style={{textAlign:"center"}}>Setup: Choose the password for access to the firewall 🔒</Title>
<Space h="xl" />
<form onSubmit={form.onSubmit(submitRequest)} style={{width:"80%"}}>
<PasswordInput
@@ -99,7 +99,7 @@ function App() {
placeholder="$3cr3t"
{...form.getInputProps('password')}
/>
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={loadinBtn} type="submit">Set Password</Button>
</Group>
</form>
@@ -108,7 +108,7 @@ function App() {
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</div>
</Box>
}else if (systemStatus.status === "run" && !systemStatus.loggined){
const submitRequest = async (values:PasswordSend) => {
setLoadingBtn(true)
@@ -123,10 +123,10 @@ function App() {
}
return <div className='center-flex-row' style={{padding:"100px"}}>
<Title order={2} align="center">Welcome to Firegex 🔥</Title>
return <Box className='center-flex-row' style={{padding:"100px"}}>
<Title order={2} style={{textAlign:"center"}}>Welcome to Firegex 🔥</Title>
<Space h="xl" />
<Title order={2} align="center">Before you use the firewall, insert the password 🔒</Title>
<Title order={2} style={{textAlign:"center"}}>Before you use the firewall, insert the password 🔒</Title>
<Space h="xl" />
<form onSubmit={form.onSubmit(submitRequest)} style={{width:"80%"}}>
<PasswordInput
@@ -134,7 +134,7 @@ function App() {
placeholder="$3cr3t"
{...form.getInputProps('password')}
/>
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={loadinBtn} type="submit">Login</Button>
</Group>
</form>
@@ -143,7 +143,7 @@ function App() {
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error}
</Notification><Space h="md" /></>:null}
</div>
</Box>
}else if (systemStatus.status === "run" && systemStatus.loggined){
return <Routes>
<Route element={<MainLayout><Outlet /></MainLayout>}>
@@ -159,11 +159,11 @@ function App() {
</Route>
</Routes>
}else{
return <div className='center-flex-row' style={{padding:"100px"}}>
<Title order={1} align="center">Error launching Firegex! 🔥</Title>
return <Box className='center-flex-row' style={{padding:"100px"}}>
<Title order={1} style={{textAlign:"center"}}>Error launching Firegex! 🔥</Title>
<Space h="md" />
<Title order={4} align="center">Error communicating with backend</Title>
</div>
<Title order={4} style={{textAlign:"center"}}>Error communicating with backend</Title>
</Box>
}
}

View File

@@ -1,4 +0,0 @@
$primary_color: #242a33;
$secondary_color: #1A1B1E;
$third_color:#25262b;

View File

@@ -108,7 +108,7 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>
<Alert variant="light" color="yellow" radius="lg" title="You are using whitelists" icon={<AiFillWarning />}>
Using whitelist means that EVERY packet that doesn't match the regex will be DROPPED... In most cases this cause the service interruption.
</Alert></>:null}
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Add Filter</Button>
</Group>

View File

@@ -1,5 +1,4 @@
import { Box, Center, SegmentedControl } from "@mantine/core";
import React from "react";
import { FaListAlt } from "react-icons/fa";
import { TiCancel } from "react-icons/ti";

View File

@@ -1,8 +0,0 @@
@use "../../vars" as *;
@use "../../index.scss" as *;
.footer{
margin-top: 50px;
background-color: $primary_color;
@extend .center-flex;
}

View File

@@ -1,13 +0,0 @@
import { Footer, Space } from '@mantine/core';
import image from "./pwnzer0tt1.svg"
import style from "./index.module.scss";
import { Link } from 'react-router-dom';
function FooterPage() {
return <Footer id="footer" height={70} className={style.footer}>
<img src={image} width={25} height={25} /> <Space w="xs" />Made by <div style={{marginLeft:"5px"}} /> <Link to="https://pwnzer0tt1.it">Pwnzer0tt1</Link> <Space w="xs" /> <img src={image} width={25} height={25} />
</Footer>
}
export default FooterPage;

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 139 KiB

View File

@@ -47,7 +47,7 @@ function ResetModal({ opened, onClose }:{ opened: boolean, onClose: () => void }
{...form.getInputProps('delete_data', { type: 'checkbox' })}
/>
<Space h="md" />
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={loadingBtn} onClick={close} >Cancel</Button>
<Button loading={loadingBtn} type="submit" color="red">Reset</Button>
</Group>

View File

@@ -46,7 +46,7 @@ function ResetPasswordModal({ opened, onClose }:{ opened: boolean, onClose: () =
{...form.getInputProps('expire', { type: 'checkbox' })}
/>
<Space h="md" />
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={loadingBtn} type="submit">Change Password</Button>
</Group>
</form>

View File

@@ -1,24 +0,0 @@
@use "../../vars" as *;
@use "../../index.scss" as *;
.header{
width: 100%;
background-color: $primary_color;
display: flex;
align-items: center;
justify-content: center;
}
.divlogo{
width: 110px;
height: 100%;
cursor: pointer;
@extend .center-flex;
}
.navbtn{
@extend .center-flex;
width: 30px;
margin:0;
}

View File

@@ -1,7 +1,6 @@
import React, { useState } from 'react';
import { ActionIcon, Divider, Image, Menu, Tooltip, Burger, Space, Header, Button, ThemeIcon } from '@mantine/core';
import style from "./index.module.scss";
import { errorNotify, getmainpath, isLargeScreen, logout } from '../../js/utils';
import { ActionIcon, Divider, Image, Menu, Tooltip, Burger, Space, AppShell, Box, Title } from '@mantine/core';
import { errorNotify, getMainPath, isLargeScreen, logout } from '../../js/utils';
import { AiFillHome } from "react-icons/ai"
import { useNavigate } from 'react-router-dom';
import { FaLock } from 'react-icons/fa';
@@ -10,11 +9,13 @@ import { ImExit } from 'react-icons/im';
import ResetPasswordModal from './ResetPasswordModal';
import ResetModal from './ResetModal';
import { MenuDropDownWithButton } from '../MainLayout';
import { useNavbarStore } from '../../js/store';
function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:React.Dispatch<React.SetStateAction<boolean>>}) {
function HeaderPage(props: any) {
const navigator = useNavigate()
const { navOpened, toggleNav } = useNavbarStore()
const logout_action = () => {
logout().then(r => {
@@ -25,40 +26,42 @@ function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:Reac
}
const go_to_home = () => {
navigator(`/${getmainpath()}`)
navigator(`/${getMainPath()}`)
}
const [changePasswordModal, setChangePasswordModal] = useState(false);
const [resetFiregexModal, setResetFiregexModal] = useState(false);
const [tooltipHomeOpened, setTooltipHomeOpened] = useState(false);
const [tooltipLogoutOpened,setTooltipLogoutOpened] = useState(false);
const isLarge = isLargeScreen()
console.log(isLarge)
return <Header height={100} className={style.header} {...other}>
<Space w="lg" />
{isLarge?null:<div>
return <AppShell.Header className="firegex__header__header" {...props}>
<Burger
opened={navOpen}
className={style.navbtn}
onClick={() => setNav((o) => !o)}
hiddenFrom='md'
ml="lg"
opened={navOpened}
className="firegex__header__navbtn"
onClick={toggleNav}
size="sm"
mr="xl"
/>
</div>}
<div className={style.divlogo}>
<Tooltip zIndex={0} label="Home" openDelay={1000}color="dark" position="right" >
<Image src="/header-logo.png" alt="Firegex logo" onClick={()=>navigator("/")}/>
<Box style={{ display: "flex", justifyContent: "center", alignItems: "center"}} ml={5}>
<Box className="firegex__header__divlogo">
<Tooltip zIndex={0} label="Home" openDelay={1000} color="dark" position="right" >
<Image src="/header-logo.png" alt="Firegex logo" w={50} onClick={()=>navigator("/")}/>
</Tooltip>
</div>
</Box>
<Box display="flex" style={{ flexDirection: "column" }} visibleFrom='xs'>
<Title order={2} >[Fi]*regex</Title>
<p style={{margin: 0, fontSize: "70%"}}>By <a href="https://pwnzer0tt1.it">Pwnzer0tt1</a></p>
</Box>
</Box>
<div className="flex-spacer" />
<Box className="flex-spacer" />
<MenuDropDownWithButton>
<Menu.Label>Firewall Access</Menu.Label>
<Menu.Item icon={<FaLock size={14} />} onClick={() => setChangePasswordModal(true)}>Change Password</Menu.Item>
<Menu.Item leftSection={<FaLock size={14} />} onClick={() => setChangePasswordModal(true)}>Change Password</Menu.Item>
<Divider />
<Menu.Label>Actions</Menu.Label>
<Menu.Item color="red" icon={<MdOutlineSettingsBackupRestore size={18} />} onClick={() => setResetFiregexModal(true)}>Reset Firegex</Menu.Item>
<Menu.Item color="red" leftSection={<MdOutlineSettingsBackupRestore size={18} />} onClick={() => setResetFiregexModal(true)}>Reset Firegex</Menu.Item>
</MenuDropDownWithButton>
<Space w="md" />
<Tooltip label="Home" position='bottom' color="teal" opened={tooltipHomeOpened}>
@@ -78,7 +81,7 @@ function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:Reac
<ResetPasswordModal opened={changePasswordModal} onClose={() => setChangePasswordModal(false)} />
<ResetModal opened={resetFiregexModal} onClose={() => setResetFiregexModal(false)} />
<Space w="xl" />
</Header>
</AppShell.Header>
}
export default HeaderPage;

View File

@@ -1,55 +1,119 @@
import { AutocompleteItem, Select, SelectProps } from "@mantine/core";
import React, { useState } from "react";
import { Combobox, TextInput, useCombobox } from "@mantine/core";
import { useState } from "react";
import { ipInterfacesQuery } from "../js/utils";
const AutoCompleteItem = React.forwardRef<HTMLDivElement, ItemProps>(
({ netint, value, ...props }: ItemProps, ref) => <div ref={ref} {...props}>
( <b>{netint}</b> ) -{">"} <b>{value}</b>
</div>
);
interface ItemProps extends AutocompleteItem {
interface ItemProps{
value: string
netint: string;
}
interface InterfaceInputProps extends Omit<SelectProps, "data">{
initialCustomInterfaces?:AutocompleteItem[],
includeInterfaceNames?:boolean
interface InterfaceInputProps{
initialCustomInterfaces?:ItemProps[],
includeInterfaceNames?:boolean,
onChange?: (value:string) => void,
value?:string,
defaultValue?:string
}
export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, ...props }:InterfaceInputProps) => {
export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, onChange, value, defaultValue }:InterfaceInputProps) => {
const [customIpInterfaces, setCustomIpInterfaces] = useState<AutocompleteItem[]>(initialCustomInterfaces??[]);
const [customIpInterfaces, setCustomIpInterfaces] = useState<ItemProps[]>(initialCustomInterfaces??[]);
const interfacesQuery = ipInterfacesQuery()
const getInterfaces = () => {
if (interfacesQuery.isLoading || !interfacesQuery.data) return []
if(includeInterfaceNames){
const result = interfacesQuery.data.map(item => ({netint:"IP", value:item.addr, label:item.addr})) as AutocompleteItem[]
interfacesQuery.data.map(item => item.name).filter((item, index, arr) => arr.indexOf(item) === index).forEach(item => result.push({netint:"INT", value:item, label:item}))
const result = interfacesQuery.data.map(item => ({netint:"IP", value:item.addr})) as ItemProps[]
interfacesQuery.data.map(item => item.name).filter((item, index, arr) => arr.indexOf(item) === index).forEach(item => result.push({netint:"INT", value:item}))
return result
}
return (interfacesQuery.data.map(item => ({netint:item.name, value:item.addr, label:item.addr})) as AutocompleteItem[])
return (interfacesQuery.data.map(item => ({netint:item.name, value:item.addr})) as ItemProps[])
}
const interfaces = getInterfaces()
return <Select
placeholder="10.1.1.1"
itemComponent={AutoCompleteItem}
data={[...customIpInterfaces, ...interfaces]}
searchable
dropdownPosition="bottom"
maxDropdownHeight={200}
creatable
getCreateLabel={(query) => `+ Use this: ${query}`}
onCreate={(query) => {
const item = { value: query, netint: "CUSTOM", label: query };
const combobox = useCombobox({
onDropdownClose: () => {
combobox.resetSelectedOption()
},
});
const data = [...customIpInterfaces, ...interfaces]
const [selectedValue, setSelectedValue] = useState<string | null>(null);
const [search, setSearch] = useState('');
const exactOptionMatch: ItemProps|undefined = data.find((item) => item.value === search);
const filteredOptions = data.filter((item) => item.value.toLowerCase().includes(search.toLowerCase().trim())).sort((a, b) => {
if (exactOptionMatch != null) {
if (a.value == exactOptionMatch.value) return -1
if (b.value == exactOptionMatch.value) return 1
}
return a.value.localeCompare(b.value)
});
const options = filteredOptions.map((item) => (
<Combobox.Option value={item.value} key={item.value}>
( <b>{item.netint}</b> ) -{">"} <b>{item.value}</b>
</Combobox.Option>
));
return <>
<Combobox
store={combobox}
withinPortal={false}
position="bottom-end"
onOptionSubmit={(value) => {
if (value === '$create') {
const item = { value: search, netint: "CUSTOM" };
setCustomIpInterfaces((current) => [...current, item]);
return item;
setSelectedValue(search);
onChange?.(search)
} else {
setSelectedValue(value);
setSearch(value);
onChange?.(value)
}
combobox.closeDropdown();
}}
>
<Combobox.Target>
<TextInput
style={{width:"100%"}}
defaultValue={defaultValue}
rightSection={<Combobox.Chevron />}
value={value??(defaultValue?undefined:search)}
placeholder="10.1.1.1"
rightSectionPointerEvents="none"
onChange={(event) => {
combobox.openDropdown();
combobox.updateSelectedOptionIndex();
setSearch(event.currentTarget.value)
onChange?.(event.currentTarget.value)
}}
onClick={(e) => {
combobox.openDropdown()
}}
onFocus={(e) => {
combobox.openDropdown()
}}
onBlur={(e) => {
combobox.closeDropdown();
setSearch(selectedValue??'');
}}
style={props.style?{width:"100%", ...props.style}:{width:"100%"}}
{...props}
/>
</Combobox.Target>
<Combobox.Dropdown>
<Combobox.Options mah={100} style={{ overflowY: 'auto' }}>
{options}
{(exactOptionMatch==null) && search.trim().length > 0 && (
<Combobox.Option value="$create">+ Use this: {search}</Combobox.Option>
)}
</Combobox.Options>
</Combobox.Dropdown>
</Combobox>
</>
}

View File

@@ -1,44 +1,38 @@
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import { ActionIcon, Container, Menu, Space } from '@mantine/core';
import { AppShell } from '@mantine/core';
import NavBar from './NavBar';
import FooterPage from './Footer';
import HeaderPage from './Header';
import { getmainpath } from '../js/utils';
import { getMainPath } from '../js/utils';
import { useLocation } from 'react-router-dom';
import { RiMenu5Fill } from 'react-icons/ri';
import { useNavbarStore } from '../js/store';
function MainLayout({ children }:{ children:any }) {
const [opened, setOpened] = useState(false);
const { navOpened } = useNavbarStore()
const location = useLocation()
useEffect(()=>{
if (location.pathname !== "/"){
sessionStorage.setItem('home_section', getmainpath())
sessionStorage.setItem('home_section', getMainPath())
}
},[location.pathname])
return <>
<AppShell
padding="md"
fixed
navbar={<NavBar closeNav={()=>setOpened(false)} opened={opened} />}
header={<HeaderPage navOpen={opened} setNav={setOpened} />}
footer={<FooterPage />}
return <AppShell
header={{ height: 70 }}
navbar={{ width: 300 , breakpoint: "md", collapsed: { mobile: !navOpened } }}
p="md"
>
<HeaderPage />
<NavBar />
<AppShell.Main>
<Container size="lg">
{children}
</Container>
</AppShell.Main>
<Space h="lg" />
</AppShell>
</>
}
export default MainLayout;
@@ -46,7 +40,7 @@ export default MainLayout;
export const MenuDropDownWithButton = ({children}:{children:any}) => <Menu withArrow>
<Menu.Target>
<ActionIcon variant='transparent'>
<RiMenu5Fill size={24} />
<RiMenu5Fill size={24} color='#FFF'/>
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>

View File

@@ -1,4 +1,4 @@
import { Button, Group, Space, TextInput, Notification, Modal, Switch, SegmentedControl } from '@mantine/core';
import { Button, Group, Space, TextInput, Notification, Modal, Switch, SegmentedControl, Box } from '@mantine/core';
import { useForm } from '@mantine/form';
import { useState } from 'react';
import { okNotify, regex_ipv4, regex_ipv6 } from '../../js/utils';
@@ -71,12 +71,12 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
<PortAndInterface form={form} int_name="ip_int" port_name="port" label={"Public IP Interface and port (ipv4/ipv6 + CIDR allowed)"} />
<Space h="md" />
<div className='center-flex'>
<Box className='center-flex'>
<Switch
label="Auto-Start Service"
{...form.getInputProps('autostart', { type: 'checkbox' })}
/>
<div className="flex-spacer"></div>
<Box className="flex-spacer"></Box>
<SegmentedControl
data={[
{ label: 'TCP', value: 'tcp' },
@@ -84,9 +84,9 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
]}
{...form.getInputProps('proto')}
/>
</div>
</Box>
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Add Service</Button>
</Group>

View File

@@ -49,7 +49,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
placeholder="Awesome Service Name!"
{...form.getInputProps('name')}
/>
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Rename</Button>
</Group>

View File

@@ -1,23 +0,0 @@
@use "../../../index.scss" as *;
.row{
width: 95%;
padding: 30px 0px;
border-radius: 20px;
margin: 10px;
@extend .center-flex;
}
.name{
font-size: 2.3em;
font-weight: bolder;
margin-right: 10px;
margin-bottom: 13px;
color:#FFF;
max-width: 300px;
overflow: hidden;
}
.name:hover{
overflow: auto;
}

View File

@@ -1,9 +1,8 @@
import { ActionIcon, Badge, Divider, Grid, Menu, Space, Title, Tooltip } from '@mantine/core';
import { ActionIcon, Badge, Box, Divider, Grid, Menu, Space, Title, Tooltip } from '@mantine/core';
import { useState } from 'react';
import { FaPlay, FaStop } from 'react-icons/fa';
import { nfregex, Service, serviceQueryKey } from '../utils';
import { MdOutlineArrowForwardIos } from "react-icons/md"
import style from "./index.module.scss";
import YesNoModal from '../../YesNoModal';
import { errorNotify, isMediumScreen, okNotify, regex_ipv4 } from '../../../js/utils';
import { BsTrashFill } from 'react-icons/bs';
@@ -72,42 +71,44 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
}
return <>
<Grid className={style.row} justify="flex-end" style={{width:"100%"}}>
<Grid.Col md={4} xs={12}>
<Box className='firegex__nfregex__rowbox'>
<Grid className="firegex__nfregex__row" justify="flex-end" style={{width:"100%"}}>
<Grid.Col span={{ md:4, xs: 12 }}>
<div className={isMedium?"center-flex-row":"center-flex"}>
<div className="center-flex">
<Title className={style.name}>
<Box className={"center-flex-row"}>
<Title className="firegex__nfregex__name">
{service.name}
</Title>
<Badge size="xl" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient">
<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">
:{service.port}
</Badge>
</div>
<Badge style={isMedium?{}:{marginLeft:"20px"}} color={status_color} radius="sm" size="lg" variant="filled">Status: <u>{service.status}</u></Badge>
</Box>
{isMedium?null:<Space w="xl" />}
</div>
{!isMedium?<Space h="xl" />:null}
</Box>
</Grid.Col>
<Grid.Col className="center-flex" md={8} xs={12}>
{!isMedium?<div className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
<div className="center-flex-row">
<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="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>
</div>
{isMedium?<div className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
<div className="center-flex">
</Box>
{isMedium?<Box className='flex-spacer' />:<Space h="xl" />}
<Box className="center-flex">
<MenuDropDownWithButton>
<Menu.Label><b>Rename service</b></Menu.Label>
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
<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" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
</MenuDropDownWithButton>
<Space w="md"/>
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
@@ -127,16 +128,16 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
<FaPlay size="20px" />
</ActionIcon>
</Tooltip>
</div>
<Space w="xl" /><Space w="xl" />
{onClick?<div>
<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size={45} />
<Space w="xl" />
</div>:null}
{isMedium?<><Space w="xl" /><Space w="xl" /></>:null}
{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"}}>
<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size={25} />
</Box>:null}
{isMedium?<Space w="xl" />:null}
</Box>
</Grid.Col>
</Grid>
<Divider size="md" style={{width:"100%"}}/>
</Box>
<YesNoModal
title='Are you sure to delete this service?'
description={`You are going to delete the service '${service.port}', causing the stopping of the firewall and deleting all the regex associated. This will cause the shutdown of your service! ⚠️`}

View File

@@ -1,29 +1,19 @@
import { Collapse, Divider, Group, MantineColor, Navbar, ScrollArea, Text, ThemeIcon, Title, UnstyledButton } from "@mantine/core";
import { Collapse, Divider, Group, MantineColor, ScrollArea, Text, ThemeIcon, Title, UnstyledButton, Box, AppShell } from "@mantine/core";
import { useState } from "react";
import { IoMdGitNetwork } from "react-icons/io";
import { MdOutlineExpandLess, MdOutlineExpandMore, MdTransform } from "react-icons/md";
import { useNavigate } from "react-router-dom";
import { getmainpath } from "../../js/utils";
import { GrDirections } from "react-icons/gr";
import { PiWallLight } from "react-icons/pi";
import { useNavbarStore } from "../../js/store";
import { getMainPath } from "../../js/utils";
function NavBarButton({ navigate, closeNav, name, icon, color, disabled, onClick }:
{ navigate?: string, closeNav: () => void, name:string, icon:any, color:MantineColor, disabled?:boolean, onClick?:CallableFunction }) {
const navigator = useNavigate()
return <UnstyledButton sx={(theme) => ({
display: 'block',
width: '100%',
padding: theme.spacing.xs,
borderRadius: theme.radius.sm,
opacity: disabled ? 0.4 : 1,
color: theme.colorScheme === 'dark' ? theme.colors.dark[0] : theme.black,
backgroundColor:(navigate===getmainpath()?(theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0]):"transparent"),
'&:hover': {
backgroundColor:
theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
},
})} onClick={()=>{
return <UnstyledButton
className={`firegex__navbar__unstyled_button${navigate==getMainPath()?" selected":""}${disabled?" disabled":""}`}
onClick={()=>{
if(navigate){navigator(`/${navigate}`);closeNav()}
if (onClick) onClick()
}} disabled={disabled}>
@@ -36,17 +26,16 @@ function NavBarButton({ navigate, closeNav, name, icon, color, disabled, onClick
</UnstyledButton>
}
export default function NavBar({ closeNav, opened }: {closeNav: () => void, opened: boolean}) {
export default function NavBar() {
const [toggle, setToggleState] = useState(false);
const { navOpened, closeNav } = useNavbarStore()
return <Navbar p="md" hiddenBreakpoint="md" hidden={!opened} width={{ md: 300 }}>
<Navbar.Section px="xs" mt="xs">
<Title order={3}>[Fi]*regex 🔥</Title>
</Navbar.Section>
return <AppShell.Navbar p="md" hidden={!navOpened}>
<Box px="xs" mt="xs">
<Title order={4}>Options </Title>
</Box>
<Divider my="xs" />
<Navbar.Section grow component={ScrollArea} px="xs" mt="xs">
<Box style={{flexGrow: 1}} component={ScrollArea} px="xs" mt="xs">
<NavBarButton navigate="nfregex" closeNav={closeNav} name="Netfilter Regex" color="lime" icon={<IoMdGitNetwork />} />
<NavBarButton navigate="firewall" closeNav={closeNav} name="Firewall Rules" color="red" icon={<PiWallLight />} />
<NavBarButton navigate="porthijack" closeNav={closeNav} name="Hijack Port to Proxy" color="blue" icon={<GrDirections />} />
@@ -55,7 +44,7 @@ export default function NavBar({ closeNav, opened }: {closeNav: () => void, open
<Collapse in={toggle}>
<NavBarButton navigate="regexproxy" closeNav={closeNav} name="TCP Proxy Regex Filter" color="grape" icon={<MdTransform />} />
</Collapse>
</Navbar.Section>
</Box>
</Navbar>
</AppShell.Navbar>
}

View File

@@ -1,9 +1,5 @@
import { ActionIcon, ActionIconProps } from "@mantine/core"
import { ActionIcon, ActionIconProps, PolymorphicComponentProps } from "@mantine/core"
import { ImCheckmark, ImCross } from "react-icons/im"
import { TiTick } from "react-icons/ti"
import {PolymorphicComponentProps} from "@mantine/utils"
interface IOnOffButtonProps extends Omit<PolymorphicComponentProps<"button",ActionIconProps>, "value">{
value: boolean,

View File

@@ -1,21 +1,20 @@
import { AutocompleteItem, Select, Space, Title } from "@mantine/core"
import React, { useEffect, useState } from "react"
import { ipInterfacesQuery } from "../js/utils";
import { Box, Space, Title } from "@mantine/core"
import React from "react"
import PortInput from "./PortInput";
import { UseFormReturnType } from "@mantine/form/lib/types";
import { InterfaceInput } from "./InterfaceInput";
interface ItemProps extends AutocompleteItem {
type ItemProps = {
value: string;
netint: string;
}
} & any
const AutoCompleteItem = React.forwardRef<HTMLDivElement, ItemProps>(
({ netint, value, ...props }: ItemProps, ref) => <div ref={ref} {...props}>
({ netint, value, ...props }, ref) => <Box ref={ref} {...props}>
( <b>{netint}</b> ) -{">"} <b>{value}</b>
</div>
</Box>
);
export default function PortAndInterface({ form, int_name, port_name, label, orientation }:{ form:UseFormReturnType<any>, int_name:string, port_name:string, label?:string, orientation?:"line"|"column" }) {
@@ -24,7 +23,7 @@ export default function PortAndInterface({ form, int_name, port_name, label, ori
{label?<>
<Title order={6}>{label}</Title>
<Space h="xs" /></> :null}
<div className={(!orientation || orientation == "line")?'center-flex':"center-flex-row"} style={{width:"100%"}}>
<Box className={(!orientation || orientation == "line")?'center-flex':"center-flex-row"} style={{width:"100%"}}>
<InterfaceInput
{...form.getInputProps(int_name)}
/>
@@ -32,6 +31,6 @@ export default function PortAndInterface({ form, int_name, port_name, label, ori
<><Space w="sm" /><span style={{marginTop:"-3px", fontSize:"1.5em"}}>:</span><Space w="sm" /></>:
<Space h="md" />}
<PortInput {...form.getInputProps(port_name)} />
</div>
</Box>
</>
}

View File

@@ -1,4 +1,4 @@
import { Button, Group, Space, TextInput, Notification, Modal, Switch, SegmentedControl } from '@mantine/core';
import { Button, Group, Space, TextInput, Notification, Modal, Switch, SegmentedControl, Box } from '@mantine/core';
import { useForm } from '@mantine/form';
import { useState } from 'react';
import { okNotify, regex_ipv6_no_cidr, regex_ipv4_no_cidr } from '../../js/utils';
@@ -79,12 +79,12 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
<PortAndInterface form={form} int_name="ip_dst" port_name="proxy_port" label="Proxy/Internal IP Address and port (ipv4/ipv6)" />
<Space h="md" />
<div className='center-flex'>
<Box className='center-flex'>
<Switch
label="Auto-Start Service"
{...form.getInputProps('autostart', { type: 'checkbox' })}
/>
<div className="flex-spacer"></div>
<Box className="flex-spacer" />
<SegmentedControl
data={[
{ label: 'TCP', value: 'tcp' },
@@ -92,9 +92,9 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
]}
{...form.getInputProps('proto')}
/>
</div>
</Box>
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Add Service</Button>
</Group>

View File

@@ -53,7 +53,7 @@ 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 position="right" mt="md">
<Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Change</Button>
</Group>
<Space h="md" />

View File

@@ -49,7 +49,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
placeholder="Awesome Service Name!"
{...form.getInputProps('name')}
/>
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Rename</Button>
</Group>

View File

@@ -1,31 +0,0 @@
@use "../../../index.scss" as *;
.row{
width: 95%;
padding: 15px 0px;
border-radius: 20px;
margin: 10px;
@extend .center-flex;
}
.row-mobile{
@extend .row;
@extend .center-flex-row;
}
.name{
font-size: 2.3em;
font-weight: bolder;
margin-right: 10px;
margin-bottom: 13px;
color:#FFF;
}
.portInput *{
color: #FFF;
font-weight: bold;
font-size: 1em;
margin-top: -1px;
text-decoration: underline;
}

View File

@@ -1,8 +1,7 @@
import { ActionIcon, Badge, Divider, Menu, Space, Title, Tooltip } from '@mantine/core';
import { ActionIcon, Badge, Box, Divider, Grid, Menu, Space, Title, Tooltip } from '@mantine/core';
import React, { useState } from 'react';
import { FaPlay, FaStop } from 'react-icons/fa';
import { porthijack, Service } from '../utils';
import style from "./index.module.scss";
import YesNoModal from '../../YesNoModal';
import { errorNotify, isMediumScreen, okNotify } from '../../../js/utils';
import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs';
@@ -90,31 +89,36 @@ function ServiceRow({ service }:{ service:Service }) {
}
return <>
<div className={isMedium?style.row:style.row_mobile} style={{width:"100%"}}>
<Space w="xl" /><Space w="xl" />
<div>
<div className="center-flex-row">
<Title order={4} className={style.name}>{service.name}</Title>
<div className="center-flex">
<Badge color={status_color} radius="sm" size="md" variant="filled">Status: <u>{service.active?"ENABLED":"DISABLED"}</u></Badge>
<Space w="sm" />
<Badge color={service.proto === "tcp"?"cyan":"orange"} radius="sm" size="md" variant="filled">
<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">
{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">
{service.proto}
</Badge>
</div>
</div>
</div>
</Box>
{isMedium?null:<Space w="xl" />}
</Box>
</Grid.Col>
<div className='flex-spacer' />
{isMedium?null:<Space h="xl" />}
<div className='center-flex'>
<div className="center-flex-row">
<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="center-flex-row">
<Badge color="lime" radius="sm" size="md" variant="filled">
FROM {service.ip_src} : {service.public_port}
</Badge>
<Space h="sm" />
<Badge color="blue" radius="sm" size="md" variant="filled">
<div className="center-flex">
<Box className="center-flex">
TO {service.ip_dst} :
<form onSubmit={form.onSubmit((v)=>portInputRef.current?.blur())}>
<PortInput
@@ -124,26 +128,25 @@ function ServiceRow({ service }:{ service:Service }) {
style={{
width: (10+form.values.proxy_port.toString().length*6.2) +"px"
}}
className={style.portInput}
className="firegex__porthijack__servicerow__portInput"
onBlur={(e)=>{onChangeProxyPort({proxy_port:parseInt(e.target.value)})}}
ref={portInputRef}
{...form.getInputProps("proxy_port")}
/>
</form>
</div>
</Box>
</Badge>
</div>
<Space w="xl" /><Space w="xl" />
<div className="center-flex">
</Box>
{isMedium?<Box className='flex-spacer' />:<Space h="xl" />}
<Box className="center-flex">
<MenuDropDownWithButton>
<Menu.Label><b>Rename service</b></Menu.Label>
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
<Menu.Item leftSection={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
<Menu.Label><b>Change destination</b></Menu.Label>
<Menu.Item icon={<BsArrowRepeat size={18} />} onClick={()=>setChangeDestModal(true)}>Change hijacking destination</Menu.Item>
<Menu.Item leftSection={<BsArrowRepeat size={18} />} onClick={()=>setChangeDestModal(true)}>Change hijacking destination</Menu.Item>
<Divider />
<Menu.Label><b>Danger zone</b></Menu.Label>
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
</MenuDropDownWithButton>
<Space w="md"/>
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}>
@@ -163,13 +166,14 @@ function ServiceRow({ service }:{ service:Service }) {
<FaPlay size="20px" />
</ActionIcon>
</Tooltip>
</div>
</div>
{isMedium?null:<Space h="xl" />}
<Space w="xl" /><Space w="xl" />
</div>
<Divider size="sm" style={{width:"100%"}}/>
</Box>
{isMedium?<Space w="xl" />:null}
</Grid.Col>
</Grid>
</Box>
<YesNoModal
title='Are you sure to delete this service?'

View File

@@ -98,7 +98,7 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
{...form.getInputProps('chosenInternalPort', { type: 'checkbox' })}
/>
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Add Service</Button>
</Group>

View File

@@ -80,7 +80,7 @@ function ChangePortModal({ service, opened, onClose }:{ service:Service, opened:
<Space h="md" />
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={submitLoading} disabled={
service.internal_port === form.values.internalPort && service.public_port === form.values.port
} type="submit">Change Port</Button>

View File

@@ -49,7 +49,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
placeholder="Awesome Service Name!"
{...form.getInputProps('name')}
/>
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Rename</Button>
</Group>

View File

@@ -1,18 +0,0 @@
@use "../../../index.scss" as *;
.row{
width: 95%;
padding: 30px 0px;
border-radius: 20px;
margin: 10px;
@extend .center-flex;
}
.name{
font-size: 2.3em;
font-weight: bolder;
margin-right: 10px;
margin-bottom: 13px;
color:#FFF;
}

View File

@@ -1,8 +1,7 @@
import { ActionIcon, Badge, Divider, Grid, Menu, Space, Title, Tooltip } from '@mantine/core';
import { ActionIcon, Badge, Box, Divider, Grid, Menu, Space, Title, Tooltip } from '@mantine/core';
import { useState } from 'react';
import { FaPause, FaPlay, FaStop } from 'react-icons/fa';
import { MdOutlineArrowForwardIos } from "react-icons/md"
import style from "./ServiceRow.module.scss";
import YesNoModal from '../../YesNoModal';
import { errorNotify, isMediumScreen, okNotify } from '../../../js/utils';
import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs';
@@ -101,34 +100,34 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
}
return <>
<Grid className={style.row} justify="flex-end" style={{width:"100%"}}>
<Grid.Col md={4} xs={12}>
<div className={isMedium?"center-flex-row":"center-flex"}>
<div className="center-flex"><Title className={style.name}>{service.name}</Title> <Badge size="xl" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient">:{service.public_port}</Badge></div>
<Grid className="firegex__servicerow__row" justify="flex-end" style={{width:"100%"}}>
<Grid.Col span={{ md:4, xs: 12 }}>
<Box className={isMedium?"center-flex-row":"center-flex"}>
<Box className="center-flex"><Title className="firegex__servicerow__name">{service.name}</Title> <Badge size="xl" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient">:{service.public_port}</Badge></Box>
<Badge color={status_color} size="lg" radius="md">{service.internal_port} {"->"} {service.public_port}</Badge>
</div>
</Box>
{!isMedium?<Space h="xl" />:null}
</Grid.Col>
<Grid.Col className="center-flex" md={8} xs={12}>
{!isMedium?<div className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
<div className="center-flex-row">
<Grid.Col className="center-flex" span={{ md:8, xs: 12 }}>
{!isMedium?<Box className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
<Box className="center-flex-row">
<Badge style={{marginBottom:"20px"}} color={status_color} radius="sm" size="lg" variant="filled">Status: <u>{service.status}</u></Badge>
<Badge style={{marginBottom:"8px"}}color="violet" radius="sm" size="md" variant="filled">Regex: {service.n_regex}</Badge>
<Badge color="yellow" radius="sm" size="md" variant="filled">Connections Blocked: {service.n_packets}</Badge>
</div>
{isMedium?<div className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
<div className="center-flex">
</Box>
{isMedium?<Box className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
<Box className="center-flex">
<MenuDropDownWithButton>
<Menu.Label><b>Rename service</b></Menu.Label>
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
<Menu.Item leftSection={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
<Divider />
<Menu.Label><b>Change ports</b></Menu.Label>
<Menu.Item icon={<TbNumbers size={18} />} onClick={()=>setChoosePortModal(true)}>Change port</Menu.Item>
<Menu.Item icon={<BsArrowRepeat size={18} />} onClick={()=>setChangePortModal(true)}>Regen proxy port</Menu.Item>
<Menu.Item leftSection={<TbNumbers size={18} />} onClick={()=>setChoosePortModal(true)}>Change port</Menu.Item>
<Menu.Item leftSection={<BsArrowRepeat size={18} />} onClick={()=>setChangePortModal(true)}>Regen proxy port</Menu.Item>
<Divider />
<Menu.Label><b>Danger zone</b></Menu.Label>
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
</MenuDropDownWithButton>
<Space w="md"/>
{["pause","wait"].includes(service.status)?
@@ -158,12 +157,12 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
<FaPlay size="20px" />
</ActionIcon>
</Tooltip>
</div>
</Box>
<Space w="xl" /><Space w="xl" />
{onClick?<div>
{onClick?<Box>
<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size={45} />
<Space w="xl" />
</div>:null}
</Box>:null}
{!isMedium?<><Space w="xl" /><Space w="xl" /></>:null}
</Grid.Col>

View File

@@ -1,25 +0,0 @@
@use "../../vars" as *;
.box{
padding:30px;
margin:5px;
}
.outer_regex_text{
border-radius: 8px;
overflow: hidden;
margin: 6px;
}
.regex_text{
padding: 10px;
background-color: $third_color;
overflow-x: hidden;
border-radius: 8px;
}
.regex_text:hover{
overflow-x: auto;
opacity: 0.8;
}

View File

@@ -2,7 +2,6 @@ import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip, Box } from '@mant
import { useState } from 'react';
import { RegexFilter } from '../../js/models';
import { b64decode, errorNotify, getapiobject, okNotify } from '../../js/utils';
import style from "./index.module.scss";
import { BsTrashFill } from "react-icons/bs"
import YesNoModal from '../YesNoModal';
import FilterTypeSelector from '../FilterTypeSelector';
@@ -43,14 +42,14 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
}).catch( err => errorNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivation":"activation"} failed!`,`Error: ${err}`))
}
return <div className={style.box}>
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 className={style.outer_regex_text}>
<Text className={style.regex_text} onClick={()=>{
<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>
@@ -74,7 +73,7 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
</Grid.Col>
<Grid.Col className='center-flex' span={12}>
<div className='center-flex-row'>
<Box className='center-flex-row'>
<FilterTypeSelector
size="md"
color="gray"
@@ -82,23 +81,23 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
value={regexInfo.is_blacklist?"blacklist":"whitelist"}
/>
<Space h="md" />
<div className='center-flex'>
<Box className='center-flex'>
<Badge size="md" color="cyan" variant="filled">Service: {regexInfo.service_id}</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>
</div>
</div>
<div className='flex-spacer' />
<div className='center-flex-row'>
</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>
</div>
</Box>
</Grid.Col>
</Grid>
<YesNoModal
@@ -109,7 +108,7 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
opened={deleteModal}
/>
</div>
</Box>
}
export default RegexView;

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 position="right" mt="md">
<Group align="right" mt="md">
<Button onClick={()=>{
onClose()
action()

213
frontend/src/index.css Normal file
View File

@@ -0,0 +1,213 @@
@import url('https://fonts.googleapis.com/css2?family=Lato&display=swap');
:root{
--primary_color: #242a33;
--secondary_color: #1A1B1E;
--third_color:#25262b;
--fourth_color: #25262b99;
--mantine-color-body: var(--secondary_color) !important;
}
body {
margin: 0;
font-family: 'Lato', sans-serif;
}
.center-flex{
display: flex;
justify-content: center;
align-items: center;
}
.center-flex-row{
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.flex-spacer{
flex-grow: 1;
}
::-webkit-scrollbar {
width: 6px;
margin:3px;
background: #333;
cursor: pointer;
}
::-webkit-scrollbar-thumb {
background: #757575;
border-radius: 8px;
}
.mantine-Modal-content {
overflow-y: visible;
}
.firegex__header__header{
width: 100%;
background-color: var(--primary_color);
display: flex;
align-items: center;
justify-content: center;
}
.firegex__header__divlogo{
width: 65px;
height: 100%;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
.firegex__header__navbtn{
display: flex;
justify-content: center;
align-items: center;
width: 30px;
margin:0;
}
.firegex__regexview__box{
padding:30px;
margin:5px;
}
.firegex__regexview__outer_regex_text{
border-radius: 8px;
overflow: hidden;
margin: 6px;
}
.firegex__regexview__regex_text{
padding: 10px;
background-color: var(--third_color);
overflow-x: hidden;
border-radius: 8px;
}
.firegex__regexview__regex_text:hover{
overflow-x: auto;
opacity: 0.8;
}
.firegex__porthijack__servicerow__row{
width: 95%;
padding: 15px 0px;
border-radius: 20px;
margin: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.firegex__porthijack__servicerow__row-mobile{
width: 95%;
padding: 15px 0px;
border-radius: 20px;
margin: 10px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.firegex__porthijack__servicerow__name{
font-size: 1.8em;
font-weight: bolder;
margin-right: 10px;
margin-bottom: 13px;
color:#FFF;
}
.firegex__porthijack__servicerow__portInput *{
color: #FFF;
font-weight: bold;
font-size: 1em;
margin-top: -1px;
text-decoration: underline;
}
.firegex__nfregex__row{
width: 95%;
padding: 20px;
border-radius: 20px;
display: flex;
justify-content: center;
align-items: center;
}
.firegex__nfregex__name{
font-size: 1.8em;
font-weight: bolder;
margin-right: 10px;
margin-bottom: 13px;
color:#FFF;
max-width: 300px;
overflow: hidden;
text-align: center;
}
.firegex__nfregex__name:hover{
overflow: auto;
}
.firegex__navbar__unstyled_button {
display: block;
width: 100%;
padding: var(--mantine-spacing-xs);
border-radius: var(--mantine-radius-sm);
color: var(--mantine-color-dark-0);
background-color: transparent;
opacity: 1;
}
.firegex__navbar__unstyled_button.disabled {
opacity: 0.4;
}
.firegex__navbar__unstyled_button.selected{
background-color: var(--mantine-color-dark-6);
}
.firegex__navbar__unstyled_button:hover {
background-color: var(--mantine-color-dark-6);
}
.firegex__footer{
margin-top: 50px;
background-color: var(--primary_color);
display: flex;
justify-content: center;
align-items: center;
}
.firegex__servicerow__row{
width: 95%;
padding: 30px 0px;
border-radius: 20px;
margin: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.firegex__servicerow__name{
font-size: 1.8em;
font-weight: bolder;
margin-right: 10px;
margin-bottom: 13px;
color:#FFF;
}
.firegex__nfregex__rowbox{
width: 100%;
background-color: var(--fourth_color);
border-radius: 20px;
border: #444 3px solid;
}

View File

@@ -1,39 +0,0 @@
@use "vars" as *;
@import url('https://fonts.googleapis.com/css2?family=Lato&display=swap');
body {
margin: 0;
font-family: 'Lato', sans-serif;
}
.center-flex{
display: flex;
justify-content: center;
align-items: center;
}
.center-flex-row{
@extend .center-flex;
flex-direction: column;
}
.flex-spacer{
flex-grow: 1;
}
::-webkit-scrollbar {
width: 6px;
margin:3px;
background: #333;
cursor: pointer;
}
::-webkit-scrollbar-thumb {
background: #757575;
border-radius: 8px;
}
.mantine-Modal-content {
overflow-y: visible;
}

View File

@@ -1,14 +1,15 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from "react-router-dom"
import './index.scss';
import App from './App';
import { MantineProvider } from '@mantine/core';
import { createTheme, MantineProvider } from '@mantine/core';
import { Notifications } from '@mantine/notifications';
import {
QueryClientProvider,
} from '@tanstack/react-query'
import { queryClient } from './js/utils';
import '@mantine/core/styles.css';
import '@mantine/notifications/styles.css';
import './index.css';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
@@ -16,7 +17,7 @@ const root = ReactDOM.createRoot(
root.render(
<QueryClientProvider client={queryClient}>
<MantineProvider theme={{ colorScheme: 'dark' }} withGlobalStyles withNormalizeCSS>
<MantineProvider defaultColorScheme='dark'>
<Notifications />
<BrowserRouter>
<App />

17
frontend/src/js/store.ts Normal file
View File

@@ -0,0 +1,17 @@
import { create } from 'zustand'
type NavbarStore = {
navOpened: boolean,
setOpenNav: (opened: boolean) => void,
closeNav: () => void,
openNav: () => void,
toggleNav: () => void,
}
export const useNavbarStore = create<NavbarStore>()((set) => ({
navOpened: false,
setOpenNav: (opened: boolean) => set({ navOpened: opened }),
closeNav: () => set({ navOpened: false }),
openNav: () => set({ navOpened: true }),
toggleNav: () => set((state) => ({ navOpened: !state.navOpened })),
}))

View File

@@ -101,14 +101,14 @@ export async function postapi(path:string,data:any,is_form:boolean=false):Promis
});
}
export function getmainpath(){
export function getMainPath(){
const paths = window.location.pathname.split("/")
if (paths.length > 1) return paths[1]
return ""
}
export function getapiobject(){
switch(getmainpath()){
switch(getMainPath()){
case "nfregex":
return nfregex
case "regexproxy":

View File

@@ -48,7 +48,7 @@ export function SettingsModal({ opened, onClose }:{ opened:boolean, onClose:()=>
<Switch label="Drop invalid packet" checked={settings.drop_invalid} onChange={v => setSettings({...settings, drop_invalid:v.target.checked})}/>
<Space h="md" />
<Switch label="Allow DHCP" checked={settings.allow_dhcp} onChange={v => setSettings({...settings, allow_dhcp:v.target.checked})}/>
<Group position="right" mt="md">
<Group align="right" mt="md">
<Button loading={submitLoading} onClick={submitRequest}>Save Setting</Button>
</Group>
</Modal>

View File

@@ -1,4 +1,4 @@
import { ActionIcon, Badge, Button, Divider, LoadingOverlay, Space, Switch, TextInput, Title, Tooltip, useMantineTheme } from "@mantine/core"
import { ActionIcon, Badge, Box, Button, Divider, LoadingOverlay, Space, Switch, TextInput, Title, Tooltip, useMantineTheme } from "@mantine/core"
import { useEffect, useState } from "react";
import { BsPlusLg, BsTrashFill } from "react-icons/bs"
import { rem } from '@mantine/core';
@@ -37,8 +37,8 @@ export const Firewall = () => {
const [applyChangeModal, setApplyChangeModal] = useState(false)
const [settingsModal, setSettingsModal] = useState(false)
const theme = useMantineTheme();
const isMedium = useMediaQuery(`(min-width: 950px)`)
const isSmall = useMediaQuery(`(max-width: 600px)`)
const isMedium = useMediaQuery(`(min-width: 950px)`)??true
const isSmall = useMediaQuery(`(max-width: 600px)`)??false
const [updateMevalueinternal, internalUpdateme] = useState(false)
const updateMe = () => {
@@ -104,7 +104,7 @@ export const Firewall = () => {
const condDiv = (val:React.ReactNode, cond:boolean) => {
if (cond)
return <div>{val}</div>
return <Box>{val}</Box>
else
return val
}
@@ -201,18 +201,18 @@ export const Firewall = () => {
dst_port: disabletab.dst_port?disable_style:{}
}
return <div
return <Box
ref={provided.innerRef}
{...provided.draggableProps}
>
<div className='center-flex' style={{width:"100%"}}>
<div {...provided.dragHandleProps}>
<Box className='center-flex' style={{width:"100%"}}>
<Box {...provided.dragHandleProps}>
<TbGripVertical style={{ width: rem(30), height: rem(40) }} />
</div>
</Box>
<Space w="sm" />
<div className={isMedium?"center-flex":"center-flex-row"} style={{width:"100%"}}>
<div className="center-flex-row" style={{width:"100%"}}>
<div className="center-flex" style={{width:"97%"}}>
<Box className={isMedium?"center-flex":"center-flex-row"} style={{width:"100%"}}>
<Box className="center-flex-row" style={{width:"100%"}}>
<Box className="center-flex" style={{width:"97%"}}>
<Switch
checked={item.active}
onChange={() =>{
@@ -239,10 +239,10 @@ export const Firewall = () => {
<ActionIcon color="red" onClick={()=>handlers.remove(index)} size="lg" radius="md" variant="filled"><BsTrashFill size={18} /></ActionIcon>
<Space w="sm" />
<TextInput defaultValue={item.name} onChange={(v)=>{item.name = v.target.value;updateMe()}} style={{width:"100%"}}/>
</div>
</Box>
<Space h="sm" />
<div className="center-flex" style={{width:"97%"}}>
<div style={{width:"100%"}}>
<Box className="center-flex" style={{width:"97%"}}>
<Box style={{width:"100%"}}>
<InterfaceInput
initialCustomInterfaces={[...src_custom_int, ...customInt]}
value={item.src}
@@ -250,7 +250,7 @@ export const Firewall = () => {
includeInterfaceNames
/>
<Space h="sm" />
<div className="center-flex" style={{width:"100%"}}>
<Box className="center-flex" style={{width:"100%"}}>
<OnOffButton value={srcPortEnabled} onClick={() =>{
const value = !srcPortEnabled
setSrcPortEnabled(value)
@@ -270,12 +270,12 @@ export const Firewall = () => {
error={!disabletab.src_port && !srcPortValue}
style={{width:"100%", ...additionalStyle.src_port}}
/>
</div>
</div>
</Box>
</Box>
<Space w="lg" />
<LuArrowBigRightDash size={100} />
<Space w="lg" />
<div style={{width:"100%"}}>
<Box style={{width:"100%"}}>
<InterfaceInput
initialCustomInterfaces={[...dst_custom_int, ...customInt]}
defaultValue={item.dst}
@@ -283,7 +283,7 @@ export const Firewall = () => {
includeInterfaceNames
/>
<Space h="sm" />
<div className="center-flex" style={{width:"100%"}}>
<Box className="center-flex" style={{width:"100%"}}>
<OnOffButton value={dstPortEnabled} onClick={() =>{
const value = !dstPortEnabled
setDstPortEnabled(value)
@@ -303,12 +303,12 @@ export const Firewall = () => {
error={!disabletab.dst_port && !dstPortValue}
style={{width:"100%", ...additionalStyle.dst_port}}
/>
</div>
</div>
</div>
</div>
</Box>
</Box>
</Box>
</Box>
{!isMedium?<Space h="sm" />:null}
<div className={isMedium?"center-flex-row":"center-flex"} style={isMedium?{}:{width:"100%", justifyContent:"space-around"}}>
<Box className={isMedium?"center-flex-row":"center-flex"} style={isMedium?{}:{width:"100%", justifyContent:"space-around"}}>
{condDiv(<>{condDiv(<ModeSelector
value={item.mode}
onChange={(value)=>{item.mode = value as RuleMode;updateMe()}}
@@ -326,13 +326,13 @@ export const Firewall = () => {
onChange={(value)=>{item.action = value as ActionType;updateMe()}}
style={{width:"100%"}}
/>, !isMedium)}
</div>
</div>
</div>
</Box>
</Box>
</Box>
<Space h="md" />
<Divider />
<Space h="md" />
</div>
</Box>
}}
</Draggable>
));
@@ -341,10 +341,10 @@ export const Firewall = () => {
return <>
<Space h="sm" />
<LoadingOverlay visible={rules.isLoading} />
<div className={isMedium?'center-flex':'center-flex-row'}>
<Box className={isMedium?'center-flex':'center-flex-row'}>
<Title order={3}>Firewall Rules</Title>
{isMedium?<div className='flex-spacer' />:<Space h="sm" />}
<div className='center-flex'>
{isMedium?<Box className='flex-spacer' />:<Space h="sm" />}
<Box className='center-flex'>
Enabled: <Space w="sm" /> <Switch checked={fwEnabled} onChange={switchState} />
<Space w="sm" />
Policy:
@@ -353,9 +353,9 @@ export const Firewall = () => {
value={currentPolicy}
onChange={(value)=>setCurrentPolicy(value as ActionType)}
/>
</div>
{isMedium?<div className='flex-spacer' />:<Space h="sm" />}
<div className='center-flex'>
</Box>
{isMedium?<Box className='flex-spacer' />:<Space h="sm" />}
<Box className='center-flex'>
<Space w="xs" />
<Badge size="sm" color="green" variant="filled">Rules: {rules.isLoading?0:rules.data?.rules.length}</Badge>
<Space w="xs" />
@@ -385,8 +385,8 @@ export const Firewall = () => {
disabled={!valuesChanged}
><TiTick size={22} /></ActionIcon>
</Tooltip>
</div>
</div>
</Box>
</Box>
<Space h="xl" />
<Divider />
{items.length > 0?<DragDropContext
@@ -397,22 +397,22 @@ export const Firewall = () => {
<Space h="md" />
<Droppable droppableId="dnd-list" direction="vertical">
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
<Box {...provided.droppableProps} ref={provided.innerRef}>
{items}
{provided.placeholder}
</div>
</Box>
)}
</Droppable>
</DragDropContext>:<>
<Space h="xl"/> <Title className='center-flex' align='center' order={3}>No rule found! Add one clicking the "+" buttons</Title>
<Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No rule found! Add one clicking the "+" buttons</Title>
<Space h="xl" /> <Space h="xl" />
<div className='center-flex'>
<Box className='center-flex'>
<Tooltip label="Add a new rule" color="blue" opened={tooltipAddRulOpened}>
<ActionIcon color="blue" onClick={emptyRuleAdd} size="xl" radius="md" variant="filled"
onFocus={() => setTooltipAddRulOpened(false)} onBlur={() => setTooltipAddRulOpened(false)}
onMouseEnter={() => setTooltipAddRulOpened(true)} onMouseLeave={() => setTooltipAddRulOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip>
</div>
</Box>
</>}
<YesNoModal

View File

@@ -1,4 +1,4 @@
import { ActionIcon, Grid, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
import { ActionIcon, Box, Grid, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
import { useState } from 'react';
import { Navigate, useParams } from 'react-router-dom';
import RegexView from '../../components/RegexView';
@@ -23,19 +23,19 @@ function ServiceDetailsNFRegex() {
<ServiceRow service={serviceInfo} />
{(!regexesList.data || regexesList.data.length == 0)?<>
<Space h="xl" />
<Title className='center-flex' align='center' order={3}>No regex found for this service! Add one by clicking the "+" buttons</Title>
<Title className='center-flex' style={{textAlign:"center"}} order={3}>No regex found for this service! Add one by clicking the "+" buttons</Title>
<Space h="xl" /> <Space h="xl" />
<div className='center-flex'>
<Box className='center-flex'>
<Tooltip label="Add a new regex" zIndex={0} color="blue" opened={tooltipAddRegexOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
aria-describedby="tooltip-AddRegex-id"
onFocus={() => setTooltipAddRegexOpened(false)} onBlur={() => setTooltipAddRegexOpened(false)}
onMouseEnter={() => setTooltipAddRegexOpened(true)} onMouseLeave={() => setTooltipAddRegexOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip>
</div>
</Box>
</>:
<Grid>
{regexesList.data?.map( (regexInfo) => <Grid.Col key={regexInfo.id} lg={6} xs={12}><RegexView regexInfo={regexInfo} /></Grid.Col>)}
{regexesList.data?.map( (regexInfo) => <Grid.Col key={regexInfo.id} span={{ lg:6, xs: 12 }}><RegexView regexInfo={regexInfo} /></Grid.Col>)}
</Grid>
}

View File

@@ -1,4 +1,4 @@
import { ActionIcon, Badge, Divider, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
import { ActionIcon, Badge, Box, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
import { useEffect, useState } from 'react';
import { BsPlusLg } from "react-icons/bs";
import { useNavigate, useParams } from 'react-router-dom';
@@ -32,19 +32,19 @@ function NFRegex({ children }: { children: any }) {
return <>
<Space h="sm" />
<div className={isMedium?'center-flex':'center-flex-row'}>
<Box className={isMedium?'center-flex':'center-flex-row'}>
<Title order={4}>Netfilter Regex</Title>
{isMedium?<div className='flex-spacer' />:<Space h="sm" />}
<div className='center-flex' >
{isMedium?<Box className='flex-spacer' />:<Space h="sm" />}
<Box className='center-flex' >
<Badge size="sm" color="green" variant="filled">Services: {services.isLoading?0:services.data?.length}</Badge>
<Space w="xs" />
<Badge size="sm" color="yellow" variant="filled">Filtered Connections: {services.isLoading?0:services.data?.reduce((acc, s)=> acc+=s.n_packets, 0)}</Badge>
<Space w="xs" />
<Badge size="sm" color="violet" variant="filled">Regexes: {services.isLoading?0:services.data?.reduce((acc, s)=> acc+=s.n_regex, 0)}</Badge>
<Space w="xs" />
</div>
</Box>
{isMedium?null:<Space h="md" />}
<div className='center-flex' >
<Box className='center-flex' >
{ srv?
<Tooltip label="Add a new regex" position='bottom' color="blue" opened={tooltipAddOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="lg" radius="md" variant="filled"
@@ -64,28 +64,26 @@ function NFRegex({ children }: { children: any }) {
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
</Tooltip>
</div>
</div>
</Box>
</Box>
<Space h="md" />
<Divider size="md" style={{width:"100%"}}/>
<div id="service-list" className="center-flex-row">
<Box className="center-flex-row" style={{gap: 20}}>
{srv?null:<>
<LoadingOverlay visible={services.isLoading} />
{(services.data && services.data?.length > 0)?services.data.map( srv => <ServiceRow service={srv} key={srv.service_id} onClick={()=>{
navigator("/nfregex/"+srv.service_id)
}} />):<><Space h="xl"/> <Title className='center-flex' align='center' order={3}>No services found! Add one clicking the "+" buttons</Title>
<Space h="xl" /> <Space h="xl" />
<div className='center-flex'>
}} />):<><Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No services found! Add one clicking the "+" buttons</Title>
<Box className='center-flex'>
<Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)}
onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip>
</div>
</Box>
</>}
<AddNewService opened={open} onClose={closeModal} />
</>}
</div>
</Box>
{srv?children:null}
{srv?
<AddNewRegex opened={open} onClose={closeModal} service={srv} />:

View File

@@ -1,4 +1,4 @@
import { ActionIcon, Badge, Divider, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
import { ActionIcon, Badge, Box, Divider, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
import { useEffect, useState } from 'react';
import { BsPlusLg } from "react-icons/bs";
import ServiceRow from '../../components/PortHijack/ServiceRow';
@@ -29,10 +29,10 @@ function PortHijack() {
return <>
<Space h="sm" />
<div className={isMedium?'center-flex':'center-flex-row'}>
<Box className={isMedium?'center-flex':'center-flex-row'}>
<Title order={4}>Hijack port to proxy</Title>
{isMedium?<div className='flex-spacer' />:<Space h="sm" />}
<div className='center-flex'>
{isMedium?<Box className='flex-spacer' />:<Space h="sm" />}
<Box className='center-flex'>
<Badge size="sm" color="yellow" variant="filled">Services: {services.isLoading?0:services.data?.length}</Badge>
<Space w="xs" />
<Tooltip label="Add a new service" position='bottom' color="blue" opened={tooltipAddOpened}>
@@ -47,25 +47,23 @@ function PortHijack() {
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
</Tooltip>
</div>
</div>
<Space h="md" /><Divider size="sm" style={{width:"100%"}}/>
<div id="service-list" className="center-flex-row">
</Box>
</Box>
<Space h="md" />
<Box className="center-flex-row" style={{gap: 20}}>
<LoadingOverlay visible={services.isLoading} />
{(services.data && services.data.length > 0) ?services.data.map( srv => <ServiceRow service={srv} key={srv.service_id} />):<>
<Space h="xl"/> <Title className='center-flex' align='center' order={3}>No services found! Add one clicking the "+" buttons</Title>
<Space h="xl" /> <Space h="xl" />
<div className='center-flex'>
<Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No services found! Add one clicking the "+" buttons</Title>
<Box className='center-flex'>
<Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)}
onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip>
</div>
</Box>
</>}
<AddNewService opened={open} onClose={closeModal} />
</div>
</Box>
</>
}

View File

@@ -1,4 +1,4 @@
import { ActionIcon, Grid, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
import { ActionIcon, Box, Grid, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
import { useState } from 'react';
import { Navigate, useParams } from 'react-router-dom';
import { BsPlusLg } from "react-icons/bs";
@@ -18,31 +18,31 @@ function ServiceDetailsProxyRegex() {
if (!srv || !serviceInfo || regexesList.isError) return <Navigate to="/" replace />
return <div>
return <Box>
<LoadingOverlay visible={regexesList.isLoading} />
<ServiceRow service={serviceInfo} />
{(!regexesList.data || regexesList.data.length == 0)?<>
<Space h="xl" />
<Title className='center-flex' align='center' order={3}>No regex found for this service! Add one by clicking the "+" buttons</Title>
<Title className='center-flex' style={{textAlign:"center"}} order={3}>No regex found for this service! Add one by clicking the "+" buttons</Title>
<Space h="xl" /> <Space h="xl" />
<div className='center-flex'>
<Box className='center-flex'>
<Tooltip label="Add a new regex" zIndex={0} color="blue" opened={tooltipAddRegexOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
aria-describedby="tooltip-AddRegex-id"
onFocus={() => setTooltipAddRegexOpened(false)} onBlur={() => setTooltipAddRegexOpened(false)}
onMouseEnter={() => setTooltipAddRegexOpened(true)} onMouseLeave={() => setTooltipAddRegexOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip>
</div>
</Box>
</>:
<Grid>
{regexesList.data.map( (regexInfo) => <Grid.Col key={regexInfo.id} lg={6} xs={12}><RegexView regexInfo={regexInfo} /></Grid.Col>)}
{regexesList.data.map( (regexInfo) => <Grid.Col key={regexInfo.id} span={{ lg:6, xs: 12 }}><RegexView regexInfo={regexInfo} /></Grid.Col>)}
</Grid>
}
{srv?<AddNewRegex opened={open} onClose={() => {setOpen(false)}} service={srv} />:null}
</div>
</Box>
}
export default ServiceDetailsProxyRegex;

View File

@@ -1,4 +1,4 @@
import { ActionIcon, Badge, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
import { ActionIcon, Badge, Box, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core';
import { useEffect, useState } from 'react';
import { BsPlusLg } from "react-icons/bs";
import { useNavigate, useParams } from 'react-router-dom';
@@ -33,9 +33,9 @@ function RegexProxy({ children }: { children: any }) {
return <>
<Space h="sm" />
<div className='center-flex'>
<Box className='center-flex'>
<Title order={4}>TCP Proxy Regex Filter (IPv4 Only)</Title>
<div className='flex-spacer' />
<Box className='flex-spacer' />
<Badge size="sm" color="green" variant="filled">Services: {services.isLoading?0:services.data?.length}</Badge>
<Space w="xs" />
<Badge size="sm" color="yellow" variant="filled">Filtered Connections: {services.isLoading?0:services.data?.reduce((acc, s)=> acc+=s.n_packets, 0)}</Badge>
@@ -61,25 +61,25 @@ function RegexProxy({ children }: { children: any }) {
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
</Tooltip>
</div>
<div id="service-list" className="center-flex-row">
</Box>
<Box className="center-flex-row">
{srv?null:<>
<LoadingOverlay visible={services.isLoading} />
{(services.data && services.data?.length > 0)?services.data.map( srv => <ServiceRow service={srv} key={srv.id} onClick={()=>{
navigator("/regexproxy/"+srv.id)
}} />):<><Space h="xl"/> <Title className='center-flex' align='center' order={3}>No services found! Add one clicking the "+" buttons</Title>
}} />):<><Space h="xl"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No services found! Add one clicking the "+" buttons</Title>
<Space h="xl" /> <Space h="xl" />
<div className='center-flex'>
<Box className='center-flex'>
<Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)}
onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip>
</div>
</Box>
</>}
<AddNewService opened={open} onClose={closeModal} />
</>}
</div>
</Box>
{srv?children:null}
{srv?
<AddNewRegex opened={open} onClose={closeModal} service={srv} />:

View File

@@ -1,10 +1,10 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import viteTsconfigPaths from 'vite-tsconfig-paths';
import svgrPlugin from 'vite-plugin-svgr';
import { defineConfig } from 'vite';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), viteTsconfigPaths(), svgrPlugin()],
});
})