Merge branch 'main' of github.com:Pwnzer0tt1/firegex

This commit is contained in:
Domingo Dirutigliano
2024-10-13 01:57:33 +02:00
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.loader import frontend_deploy, load_routers
from utils.models import ChangePasswordModel, IpInterface, PasswordChangeForm, PasswordForm, ResetRequest, StatusModel, StatusMessageModel from utils.models import ChangePasswordModel, IpInterface, PasswordChangeForm, PasswordForm, ResetRequest, StatusModel, StatusMessageModel
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from fastapi.middleware.cors import CORSMiddleware
# DB init # DB init
db = SQLite('db/firegex.db') db = SQLite('db/firegex.db')
@@ -32,6 +33,9 @@ async def lifespan(app):
app = FastAPI(debug=DEBUG, redoc_url=None, lifespan=lifespan) app = FastAPI(debug=DEBUG, redoc_url=None, lifespan=lifespan)
utils.socketio = SocketManager(app, "/sock", socketio_path="") 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 APP_STATUS(): return "init" if db.get("password") is None else "run"
def JWT_SECRET(): return db.get("secret") def JWT_SECRET(): return db.get("secret")
@@ -158,7 +162,7 @@ if __name__ == '__main__':
os.chdir(os.path.dirname(os.path.realpath(__file__))) os.chdir(os.path.dirname(os.path.realpath(__file__)))
uvicorn.run( uvicorn.run(
"app:app", "app:app",
host="0.0.0.0" if DEBUG else None, host="::" if DEBUG else None,
port=FIREGEX_PORT, port=FIREGEX_PORT,
reload=DEBUG, reload=DEBUG,
access_log=True, 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

@@ -1,40 +1,34 @@
{ {
"name": "firegex-frontend", "name": "firegex-frontend",
"type": "module",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@emotion/react": "^11.11.0", "@hello-pangea/dnd": "^16.6.0",
"@hello-pangea/dnd": "^16.3.0", "@mantine/core": "^7.13.2",
"@mantine/core": "^6.0.21", "@mantine/form": "^7.13.2",
"@mantine/form": "^6.0.21", "@mantine/hooks": "^7.13.2",
"@mantine/hooks": "^6.0.21", "@mantine/modals": "^7.13.2",
"@mantine/modals": "^6.0.21", "@mantine/notifications": "^7.13.2",
"@mantine/notifications": "^6.0.21", "@tanstack/react-query": "^4.36.1",
"@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",
"@types/jest": "^27.5.2", "@types/jest": "^27.5.2",
"@types/node": "^20.2.5", "@types/node": "^20.16.11",
"@types/react": "^18.0.12", "@types/react": "^18.3.11",
"@types/react-dom": "^18.0.5", "@types/react-dom": "^18.3.1",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"react": "^18.1.0", "react": "^18.3.1",
"react-dom": "^18.1.0", "react-dom": "^18.3.1",
"react-icons": "^4.4.0", "react-icons": "^5.3.0",
"react-router-dom": "^6.3.0", "react-router-dom": "^6.27.0",
"sass": "^1.62.1", "socket.io-client": "^4.8.0",
"socket.io-client": "^4.5.1", "typescript": "^4.9.5",
"typescript": "^4.7.3", "web-vitals": "^2.1.4",
"web-vitals": "^2.1.4" "zustand": "^5.0.0-rc.2"
}, },
"scripts": { "scripts": {
"start": "vite", "dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
"serve": "vite preview" "preview": "vite preview"
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
@@ -55,10 +49,10 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@tanstack/react-query-devtools": "^4.35.3", "@tanstack/react-query-devtools": "^4.36.1",
"@vitejs/plugin-react": "^4.0.0", "@vitejs/plugin-react": "^4.3.2",
"vite": "^4.3.9", "vite": "^4.5.5",
"vite-plugin-svgr": "^3.2.0", "vite-plugin-svgr": "^3.3.0",
"vite-tsconfig-paths": "^4.2.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 { useForm } from '@mantine/form';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { ImCross } from 'react-icons/im'; import { ImCross } from 'react-icons/im';
@@ -66,15 +66,15 @@ function App() {
if (loading){ if (loading){
return <LoadingOverlay visible/> return <LoadingOverlay visible/>
}else if (reqError){ }else if (reqError){
return <div className='center-flex-row' style={{padding:"100px"}}> return <Box className='center-flex-row' style={{padding:"100px"}}>
<Title order={1} align="center">Error launching Firegex! 🔥</Title> <Title order={1} style={{textAlign:"center"}}>Error launching Firegex! 🔥</Title>
<Space h="md" /> <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" /> <Space h="md" />
Error: {reqError} Error: {reqError}
<Space h="xl" /> <Space h="xl" />
<Loader /> <Loader />
</div> </Box>
}else if (systemStatus.status === "init"){ }else if (systemStatus.status === "init"){
const submitRequest = async (values:PasswordSend) => { const submitRequest = async (values:PasswordSend) => {
@@ -90,8 +90,8 @@ function App() {
} }
return <div className='center-flex-row' style={{padding:"100px"}}> return <Box className='center-flex-row' style={{padding:"100px"}}>
<Title order={3} align="center">Setup: Choose the password for access to the firewall 🔒</Title> <Title order={3} style={{textAlign:"center"}}>Setup: Choose the password for access to the firewall 🔒</Title>
<Space h="xl" /> <Space h="xl" />
<form onSubmit={form.onSubmit(submitRequest)} style={{width:"80%"}}> <form onSubmit={form.onSubmit(submitRequest)} style={{width:"80%"}}>
<PasswordInput <PasswordInput
@@ -99,7 +99,7 @@ function App() {
placeholder="$3cr3t" placeholder="$3cr3t"
{...form.getInputProps('password')} {...form.getInputProps('password')}
/> />
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button loading={loadinBtn} type="submit">Set Password</Button> <Button loading={loadinBtn} type="submit">Set Password</Button>
</Group> </Group>
</form> </form>
@@ -108,7 +108,7 @@ function App() {
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}> <Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error} Error: {error}
</Notification><Space h="md" /></>:null} </Notification><Space h="md" /></>:null}
</div> </Box>
}else if (systemStatus.status === "run" && !systemStatus.loggined){ }else if (systemStatus.status === "run" && !systemStatus.loggined){
const submitRequest = async (values:PasswordSend) => { const submitRequest = async (values:PasswordSend) => {
setLoadingBtn(true) setLoadingBtn(true)
@@ -123,10 +123,10 @@ function App() {
} }
return <div className='center-flex-row' style={{padding:"100px"}}> return <Box className='center-flex-row' style={{padding:"100px"}}>
<Title order={2} align="center">Welcome to Firegex 🔥</Title> <Title order={2} style={{textAlign:"center"}}>Welcome to Firegex 🔥</Title>
<Space h="xl" /> <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" /> <Space h="xl" />
<form onSubmit={form.onSubmit(submitRequest)} style={{width:"80%"}}> <form onSubmit={form.onSubmit(submitRequest)} style={{width:"80%"}}>
<PasswordInput <PasswordInput
@@ -134,7 +134,7 @@ function App() {
placeholder="$3cr3t" placeholder="$3cr3t"
{...form.getInputProps('password')} {...form.getInputProps('password')}
/> />
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button loading={loadinBtn} type="submit">Login</Button> <Button loading={loadinBtn} type="submit">Login</Button>
</Group> </Group>
</form> </form>
@@ -143,7 +143,7 @@ function App() {
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}> <Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
Error: {error} Error: {error}
</Notification><Space h="md" /></>:null} </Notification><Space h="md" /></>:null}
</div> </Box>
}else if (systemStatus.status === "run" && systemStatus.loggined){ }else if (systemStatus.status === "run" && systemStatus.loggined){
return <Routes> return <Routes>
<Route element={<MainLayout><Outlet /></MainLayout>}> <Route element={<MainLayout><Outlet /></MainLayout>}>
@@ -159,11 +159,11 @@ function App() {
</Route> </Route>
</Routes> </Routes>
}else{ }else{
return <div className='center-flex-row' style={{padding:"100px"}}> return <Box className='center-flex-row' style={{padding:"100px"}}>
<Title order={1} align="center">Error launching Firegex! 🔥</Title> <Title order={1} style={{textAlign:"center"}}>Error launching Firegex! 🔥</Title>
<Space h="md" /> <Space h="md" />
<Title order={4} align="center">Error communicating with backend</Title> <Title order={4} style={{textAlign:"center"}}>Error communicating with backend</Title>
</div> </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 />}> <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. 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} </Alert></>:null}
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Add Filter</Button> <Button loading={submitLoading} type="submit">Add Filter</Button>
</Group> </Group>

View File

@@ -1,5 +1,4 @@
import { Box, Center, SegmentedControl } from "@mantine/core"; import { Box, Center, SegmentedControl } from "@mantine/core";
import React from "react";
import { FaListAlt } from "react-icons/fa"; import { FaListAlt } from "react-icons/fa";
import { TiCancel } from "react-icons/ti"; 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' })} {...form.getInputProps('delete_data', { type: 'checkbox' })}
/> />
<Space h="md" /> <Space h="md" />
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button loading={loadingBtn} onClick={close} >Cancel</Button> <Button loading={loadingBtn} onClick={close} >Cancel</Button>
<Button loading={loadingBtn} type="submit" color="red">Reset</Button> <Button loading={loadingBtn} type="submit" color="red">Reset</Button>
</Group> </Group>

View File

@@ -46,7 +46,7 @@ function ResetPasswordModal({ opened, onClose }:{ opened: boolean, onClose: () =
{...form.getInputProps('expire', { type: 'checkbox' })} {...form.getInputProps('expire', { type: 'checkbox' })}
/> />
<Space h="md" /> <Space h="md" />
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button loading={loadingBtn} type="submit">Change Password</Button> <Button loading={loadingBtn} type="submit">Change Password</Button>
</Group> </Group>
</form> </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 React, { useState } from 'react';
import { ActionIcon, Divider, Image, Menu, Tooltip, Burger, Space, Header, Button, ThemeIcon } from '@mantine/core'; import { ActionIcon, Divider, Image, Menu, Tooltip, Burger, Space, AppShell, Box, Title } from '@mantine/core';
import style from "./index.module.scss"; import { errorNotify, getMainPath, isLargeScreen, logout } from '../../js/utils';
import { errorNotify, getmainpath, isLargeScreen, logout } from '../../js/utils';
import { AiFillHome } from "react-icons/ai" import { AiFillHome } from "react-icons/ai"
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { FaLock } from 'react-icons/fa'; import { FaLock } from 'react-icons/fa';
@@ -10,11 +9,13 @@ import { ImExit } from 'react-icons/im';
import ResetPasswordModal from './ResetPasswordModal'; import ResetPasswordModal from './ResetPasswordModal';
import ResetModal from './ResetModal'; import ResetModal from './ResetModal';
import { MenuDropDownWithButton } from '../MainLayout'; 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 navigator = useNavigate()
const { navOpened, toggleNav } = useNavbarStore()
const logout_action = () => { const logout_action = () => {
logout().then(r => { logout().then(r => {
@@ -25,40 +26,42 @@ function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:Reac
} }
const go_to_home = () => { const go_to_home = () => {
navigator(`/${getmainpath()}`) navigator(`/${getMainPath()}`)
} }
const [changePasswordModal, setChangePasswordModal] = useState(false); const [changePasswordModal, setChangePasswordModal] = useState(false);
const [resetFiregexModal, setResetFiregexModal] = useState(false); const [resetFiregexModal, setResetFiregexModal] = useState(false);
const [tooltipHomeOpened, setTooltipHomeOpened] = useState(false); const [tooltipHomeOpened, setTooltipHomeOpened] = useState(false);
const [tooltipLogoutOpened,setTooltipLogoutOpened] = useState(false); const [tooltipLogoutOpened,setTooltipLogoutOpened] = useState(false);
const isLarge = isLargeScreen() return <AppShell.Header className="firegex__header__header" {...props}>
console.log(isLarge) <Burger
return <Header height={100} className={style.header} {...other}> hiddenFrom='md'
<Space w="lg" /> ml="lg"
{isLarge?null:<div> opened={navOpened}
<Burger className="firegex__header__navbtn"
opened={navOpen} onClick={toggleNav}
className={style.navbtn} size="sm"
onClick={() => setNav((o) => !o)} />
size="sm" <Box style={{ display: "flex", justifyContent: "center", alignItems: "center"}} ml={5}>
mr="xl" <Box className="firegex__header__divlogo">
/> <Tooltip zIndex={0} label="Home" openDelay={1000} color="dark" position="right" >
</div>} <Image src="/header-logo.png" alt="Firegex logo" w={50} onClick={()=>navigator("/")}/>
<div className={style.divlogo}> </Tooltip>
<Tooltip zIndex={0} label="Home" openDelay={1000}color="dark" position="right" > </Box>
<Image src="/header-logo.png" alt="Firegex logo" onClick={()=>navigator("/")}/> <Box display="flex" style={{ flexDirection: "column" }} visibleFrom='xs'>
</Tooltip> <Title order={2} >[Fi]*regex</Title>
</div> <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> <MenuDropDownWithButton>
<Menu.Label>Firewall Access</Menu.Label> <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 /> <Divider />
<Menu.Label>Actions</Menu.Label> <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> </MenuDropDownWithButton>
<Space w="md" /> <Space w="md" />
<Tooltip label="Home" position='bottom' color="teal" opened={tooltipHomeOpened}> <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)} /> <ResetPasswordModal opened={changePasswordModal} onClose={() => setChangePasswordModal(false)} />
<ResetModal opened={resetFiregexModal} onClose={() => setResetFiregexModal(false)} /> <ResetModal opened={resetFiregexModal} onClose={() => setResetFiregexModal(false)} />
<Space w="xl" /> <Space w="xl" />
</Header> </AppShell.Header>
} }
export default HeaderPage; export default HeaderPage;

View File

@@ -1,55 +1,119 @@
import { AutocompleteItem, Select, SelectProps } from "@mantine/core"; import { Combobox, TextInput, useCombobox } from "@mantine/core";
import React, { useState } from "react"; import { useState } from "react";
import { ipInterfacesQuery } from "../js/utils"; import { ipInterfacesQuery } from "../js/utils";
interface ItemProps{
const AutoCompleteItem = React.forwardRef<HTMLDivElement, ItemProps>( value: string
({ netint, value, ...props }: ItemProps, ref) => <div ref={ref} {...props}>
( <b>{netint}</b> ) -{">"} <b>{value}</b>
</div>
);
interface ItemProps extends AutocompleteItem {
netint: string; netint: string;
} }
interface InterfaceInputProps extends Omit<SelectProps, "data">{ interface InterfaceInputProps{
initialCustomInterfaces?:AutocompleteItem[], initialCustomInterfaces?:ItemProps[],
includeInterfaceNames?:boolean 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 interfacesQuery = ipInterfacesQuery()
const getInterfaces = () => { const getInterfaces = () => {
if (interfacesQuery.isLoading || !interfacesQuery.data) return [] if (interfacesQuery.isLoading || !interfacesQuery.data) return []
if(includeInterfaceNames){ if(includeInterfaceNames){
const result = interfacesQuery.data.map(item => ({netint:"IP", value:item.addr, label:item.addr})) as AutocompleteItem[] 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, label:item})) 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 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() const interfaces = getInterfaces()
return <Select const combobox = useCombobox({
placeholder="10.1.1.1" onDropdownClose: () => {
itemComponent={AutoCompleteItem} combobox.resetSelectedOption()
data={[...customIpInterfaces, ...interfaces]} },
searchable });
dropdownPosition="bottom"
maxDropdownHeight={200} const data = [...customIpInterfaces, ...interfaces]
creatable const [selectedValue, setSelectedValue] = useState<string | null>(null);
getCreateLabel={(query) => `+ Use this: ${query}`} const [search, setSearch] = useState('');
onCreate={(query) => {
const item = { value: query, netint: "CUSTOM", label: query }; const exactOptionMatch: ItemProps|undefined = data.find((item) => item.value === search);
setCustomIpInterfaces((current) => [...current, item]);
return item; 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]);
setSelectedValue(search);
onChange?.(search)
} else {
setSelectedValue(value);
setSearch(value);
onChange?.(value)
}
combobox.closeDropdown();
}} }}
style={props.style?{width:"100%", ...props.style}:{width:"100%"}} >
{...props}
/> <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??'');
}}
/>
</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 { ActionIcon, Container, Menu, Space } from '@mantine/core';
import { AppShell } from '@mantine/core'; import { AppShell } from '@mantine/core';
import NavBar from './NavBar'; import NavBar from './NavBar';
import FooterPage from './Footer';
import HeaderPage from './Header'; import HeaderPage from './Header';
import { getmainpath } from '../js/utils'; import { getMainPath } from '../js/utils';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { RiMenu5Fill } from 'react-icons/ri'; import { RiMenu5Fill } from 'react-icons/ri';
import { useNavbarStore } from '../js/store';
function MainLayout({ children }:{ children:any }) { function MainLayout({ children }:{ children:any }) {
const [opened, setOpened] = useState(false); const { navOpened } = useNavbarStore()
const location = useLocation() const location = useLocation()
useEffect(()=>{ useEffect(()=>{
if (location.pathname !== "/"){ if (location.pathname !== "/"){
sessionStorage.setItem('home_section', getmainpath()) sessionStorage.setItem('home_section', getMainPath())
} }
},[location.pathname]) },[location.pathname])
return <AppShell
header={{ height: 70 }}
return <> navbar={{ width: 300 , breakpoint: "md", collapsed: { mobile: !navOpened } }}
p="md"
<AppShell
padding="md"
fixed
navbar={<NavBar closeNav={()=>setOpened(false)} opened={opened} />}
header={<HeaderPage navOpen={opened} setNav={setOpened} />}
footer={<FooterPage />}
> >
<Container size="lg"> <HeaderPage />
{children} <NavBar />
</Container> <AppShell.Main>
<Container size="lg">
{children}
</Container>
</AppShell.Main>
<Space h="lg" /> <Space h="lg" />
</AppShell> </AppShell>
</>
} }
export default MainLayout; export default MainLayout;
@@ -46,7 +40,7 @@ export default MainLayout;
export const MenuDropDownWithButton = ({children}:{children:any}) => <Menu withArrow> export const MenuDropDownWithButton = ({children}:{children:any}) => <Menu withArrow>
<Menu.Target> <Menu.Target>
<ActionIcon variant='transparent'> <ActionIcon variant='transparent'>
<RiMenu5Fill size={24} /> <RiMenu5Fill size={24} color='#FFF'/>
</ActionIcon> </ActionIcon>
</Menu.Target> </Menu.Target>
<Menu.Dropdown> <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 { useForm } from '@mantine/form';
import { useState } from 'react'; import { useState } from 'react';
import { okNotify, regex_ipv4, regex_ipv6 } from '../../js/utils'; 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)"} /> <PortAndInterface form={form} int_name="ip_int" port_name="port" label={"Public IP Interface and port (ipv4/ipv6 + CIDR allowed)"} />
<Space h="md" /> <Space h="md" />
<div className='center-flex'> <Box className='center-flex'>
<Switch <Switch
label="Auto-Start Service" label="Auto-Start Service"
{...form.getInputProps('autostart', { type: 'checkbox' })} {...form.getInputProps('autostart', { type: 'checkbox' })}
/> />
<div className="flex-spacer"></div> <Box className="flex-spacer"></Box>
<SegmentedControl <SegmentedControl
data={[ data={[
{ label: 'TCP', value: 'tcp' }, { label: 'TCP', value: 'tcp' },
@@ -84,9 +84,9 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
]} ]}
{...form.getInputProps('proto')} {...form.getInputProps('proto')}
/> />
</div> </Box>
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Add Service</Button> <Button loading={submitLoading} type="submit">Add Service</Button>
</Group> </Group>

View File

@@ -49,7 +49,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
placeholder="Awesome Service Name!" placeholder="Awesome Service Name!"
{...form.getInputProps('name')} {...form.getInputProps('name')}
/> />
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Rename</Button> <Button loading={submitLoading} type="submit">Rename</Button>
</Group> </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 { useState } from 'react';
import { FaPlay, FaStop } from 'react-icons/fa'; import { FaPlay, FaStop } from 'react-icons/fa';
import { nfregex, Service, serviceQueryKey } from '../utils'; import { nfregex, Service, serviceQueryKey } from '../utils';
import { MdOutlineArrowForwardIos } from "react-icons/md" import { MdOutlineArrowForwardIos } from "react-icons/md"
import style from "./index.module.scss";
import YesNoModal from '../../YesNoModal'; import YesNoModal from '../../YesNoModal';
import { errorNotify, isMediumScreen, okNotify, regex_ipv4 } from '../../../js/utils'; import { errorNotify, isMediumScreen, okNotify, regex_ipv4 } from '../../../js/utils';
import { BsTrashFill } from 'react-icons/bs'; import { BsTrashFill } from 'react-icons/bs';
@@ -72,71 +71,73 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
} }
return <> return <>
<Grid className={style.row} justify="flex-end" style={{width:"100%"}}> <Box className='firegex__nfregex__rowbox'>
<Grid.Col md={4} xs={12}> <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"}> <Box className={"center-flex-row"}>
<div className="center-flex"> <Title className="firegex__nfregex__name">
<Title className={style.name}>
{service.name} {service.name}
</Title> </Title>
<Badge size="xl" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient"> <Box className="center-flex" style={{ gap: 6 }}>
:{service.port} <Badge color={status_color} radius="md" size="lg" variant="filled">Status: <u>{service.status}</u></Badge>
</Badge> <Badge size="lg" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient" radius="md">
</div> :{service.port}
<Badge style={isMedium?{}:{marginLeft:"20px"}} color={status_color} radius="sm" size="lg" variant="filled">Status: <u>{service.status}</u></Badge> </Badge>
{isMedium?null:<Space w="xl" />} </Box>
</div> {isMedium?null:<Space w="xl" />}
{!isMedium?<Space h="xl" />:null} </Box>
</Grid.Col> </Grid.Col>
<Grid.Col className="center-flex" md={8} xs={12}> <Grid.Col className={isMedium?"center-flex":"center-flex-row"} span={{ md:8, xs: 12 }}>
{!isMedium?<div className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>} <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>
</Box>
{isMedium?<Box className='flex-spacer' />:<Space h="xl" />}
<Box className="center-flex">
<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>
<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={service.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(service.status)?true:false}>
<FaPlay size="20px" />
</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"}}>
<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size={25} />
</Box>:null}
{isMedium?<Space w="xl" />:null}
</Box>
<div className="center-flex-row"> </Grid.Col>
<Badge color="yellow" radius="sm" size="md" variant="filled">Connections Blocked: {service.n_packets}</Badge> </Grid>
<Space h="xs" /> </Box>
<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">
<MenuDropDownWithButton>
<Menu.Label><b>Rename service</b></Menu.Label>
<Menu.Item icon={<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>
</MenuDropDownWithButton>
<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={service.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(service.status)?true:false}>
<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}
</Grid.Col>
</Grid>
<Divider size="md" style={{width:"100%"}}/>
<YesNoModal <YesNoModal
title='Are you sure to delete this service?' 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! ⚠️`} 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 { useState } from "react";
import { IoMdGitNetwork } from "react-icons/io"; import { IoMdGitNetwork } from "react-icons/io";
import { MdOutlineExpandLess, MdOutlineExpandMore, MdTransform } from "react-icons/md"; import { MdOutlineExpandLess, MdOutlineExpandMore, MdTransform } from "react-icons/md";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { getmainpath } from "../../js/utils";
import { GrDirections } from "react-icons/gr"; import { GrDirections } from "react-icons/gr";
import { PiWallLight } from "react-icons/pi"; 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 }: function NavBarButton({ navigate, closeNav, name, icon, color, disabled, onClick }:
{ navigate?: string, closeNav: () => void, name:string, icon:any, color:MantineColor, disabled?:boolean, onClick?:CallableFunction }) { { navigate?: string, closeNav: () => void, name:string, icon:any, color:MantineColor, disabled?:boolean, onClick?:CallableFunction }) {
const navigator = useNavigate() const navigator = useNavigate()
return <UnstyledButton
return <UnstyledButton sx={(theme) => ({ className={`firegex__navbar__unstyled_button${navigate==getMainPath()?" selected":""}${disabled?" disabled":""}`}
display: 'block', onClick={()=>{
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={()=>{
if(navigate){navigator(`/${navigate}`);closeNav()} if(navigate){navigator(`/${navigate}`);closeNav()}
if (onClick) onClick() if (onClick) onClick()
}} disabled={disabled}> }} disabled={disabled}>
@@ -36,17 +26,16 @@ function NavBarButton({ navigate, closeNav, name, icon, color, disabled, onClick
</UnstyledButton> </UnstyledButton>
} }
export default function NavBar({ closeNav, opened }: {closeNav: () => void, opened: boolean}) { export default function NavBar() {
const [toggle, setToggleState] = useState(false); const [toggle, setToggleState] = useState(false);
const { navOpened, closeNav } = useNavbarStore()
return <AppShell.Navbar p="md" hidden={!navOpened}>
return <Navbar p="md" hiddenBreakpoint="md" hidden={!opened} width={{ md: 300 }}> <Box px="xs" mt="xs">
<Navbar.Section px="xs" mt="xs"> <Title order={4}>Options </Title>
<Title order={3}>[Fi]*regex 🔥</Title> </Box>
</Navbar.Section>
<Divider my="xs" /> <Divider my="xs" />
<Box style={{flexGrow: 1}} component={ScrollArea} px="xs" mt="xs">
<Navbar.Section grow component={ScrollArea} px="xs" mt="xs">
<NavBarButton navigate="nfregex" closeNav={closeNav} name="Netfilter Regex" color="lime" icon={<IoMdGitNetwork />} /> <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="firewall" closeNav={closeNav} name="Firewall Rules" color="red" icon={<PiWallLight />} />
<NavBarButton navigate="porthijack" closeNav={closeNav} name="Hijack Port to Proxy" color="blue" icon={<GrDirections />} /> <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}> <Collapse in={toggle}>
<NavBarButton navigate="regexproxy" closeNav={closeNav} name="TCP Proxy Regex Filter" color="grape" icon={<MdTransform />} /> <NavBarButton navigate="regexproxy" closeNav={closeNav} name="TCP Proxy Regex Filter" color="grape" icon={<MdTransform />} />
</Collapse> </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 { 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">{ interface IOnOffButtonProps extends Omit<PolymorphicComponentProps<"button",ActionIconProps>, "value">{
value: boolean, value: boolean,

View File

@@ -1,21 +1,20 @@
import { AutocompleteItem, Select, Space, Title } from "@mantine/core" import { Box, Space, Title } from "@mantine/core"
import React, { useEffect, useState } from "react" import React from "react"
import { ipInterfacesQuery } from "../js/utils";
import PortInput from "./PortInput"; import PortInput from "./PortInput";
import { UseFormReturnType } from "@mantine/form/lib/types"; import { UseFormReturnType } from "@mantine/form/lib/types";
import { InterfaceInput } from "./InterfaceInput"; import { InterfaceInput } from "./InterfaceInput";
interface ItemProps extends AutocompleteItem { type ItemProps = {
value: string;
netint: string; netint: string;
} } & any
const AutoCompleteItem = React.forwardRef<HTMLDivElement, ItemProps>( 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> ( <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" }) { 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?<> {label?<>
<Title order={6}>{label}</Title> <Title order={6}>{label}</Title>
<Space h="xs" /></> :null} <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 <InterfaceInput
{...form.getInputProps(int_name)} {...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 w="sm" /><span style={{marginTop:"-3px", fontSize:"1.5em"}}>:</span><Space w="sm" /></>:
<Space h="md" />} <Space h="md" />}
<PortInput {...form.getInputProps(port_name)} /> <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 { useForm } from '@mantine/form';
import { useState } from 'react'; import { useState } from 'react';
import { okNotify, regex_ipv6_no_cidr, regex_ipv4_no_cidr } from '../../js/utils'; 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)" /> <PortAndInterface form={form} int_name="ip_dst" port_name="proxy_port" label="Proxy/Internal IP Address and port (ipv4/ipv6)" />
<Space h="md" /> <Space h="md" />
<div className='center-flex'> <Box className='center-flex'>
<Switch <Switch
label="Auto-Start Service" label="Auto-Start Service"
{...form.getInputProps('autostart', { type: 'checkbox' })} {...form.getInputProps('autostart', { type: 'checkbox' })}
/> />
<div className="flex-spacer"></div> <Box className="flex-spacer" />
<SegmentedControl <SegmentedControl
data={[ data={[
{ label: 'TCP', value: 'tcp' }, { label: 'TCP', value: 'tcp' },
@@ -92,9 +92,9 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
]} ]}
{...form.getInputProps('proto')} {...form.getInputProps('proto')}
/> />
</div> </Box>
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Add Service</Button> <Button loading={submitLoading} type="submit">Add Service</Button>
</Group> </Group>

View File

@@ -53,7 +53,7 @@ function ChangeDestination({ opened, onClose, service }:{ opened:boolean, onClos
<form onSubmit={form.onSubmit(submitRequest)}> <form onSubmit={form.onSubmit(submitRequest)}>
<PortAndInterface form={form} int_name="ip_dst" port_name="proxy_port" /> <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> <Button loading={submitLoading} type="submit">Change</Button>
</Group> </Group>
<Space h="md" /> <Space h="md" />

View File

@@ -49,7 +49,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
placeholder="Awesome Service Name!" placeholder="Awesome Service Name!"
{...form.getInputProps('name')} {...form.getInputProps('name')}
/> />
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Rename</Button> <Button loading={submitLoading} type="submit">Rename</Button>
</Group> </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 React, { useState } from 'react';
import { FaPlay, FaStop } from 'react-icons/fa'; import { FaPlay, FaStop } from 'react-icons/fa';
import { porthijack, Service } from '../utils'; import { porthijack, Service } from '../utils';
import style from "./index.module.scss";
import YesNoModal from '../../YesNoModal'; import YesNoModal from '../../YesNoModal';
import { errorNotify, isMediumScreen, okNotify } from '../../../js/utils'; import { errorNotify, isMediumScreen, okNotify } from '../../../js/utils';
import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs'; import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs';
@@ -90,86 +89,91 @@ function ServiceRow({ service }:{ service:Service }) {
} }
return <> return <>
<div className={isMedium?style.row:style.row_mobile} style={{width:"100%"}}> <Box className='firegex__nfregex__rowbox'>
<Space w="xl" /><Space w="xl" /> <Grid className="firegex__nfregex__row" justify="flex-end" style={{width:"100%"}}>
<div> <Grid.Col span={{ md:4, xs: 12 }}>
<div className="center-flex-row"> <Box className={"center-flex-row"}>
<Title order={4} className={style.name}>{service.name}</Title> <Title className="firegex__nfregex__name">
<div className="center-flex"> {service.name}
<Badge color={status_color} radius="sm" size="md" variant="filled">Status: <u>{service.active?"ENABLED":"DISABLED"}</u></Badge> </Title>
<Space w="sm" /> <Box className="center-flex" style={{ gap: 6 }}>
<Badge color={service.proto === "tcp"?"cyan":"orange"} radius="sm" size="md" variant="filled"> <Badge color={status_color} radius="md" size="lg" variant="filled">Status: <u>{service.active?"ENABLED":"DISABLED"}</u></Badge>
{service.proto} <Badge color={service.proto === "tcp"?"cyan":"orange"} radius="md" size="lg" variant="filled">
</Badge> {service.proto}
</div> </Badge>
</div> </Box>
</div> {isMedium?null:<Space w="xl" />}
</Box>
</Grid.Col>
<div className='flex-spacer' /> <Grid.Col className={isMedium?"center-flex":"center-flex-row"} span={{ md:8, xs: 12 }}>
{isMedium?null:<Space h="xl" />} <Box visibleFrom='md' className='flex-spacer' />
<div className='center-flex'> <Space hiddenFrom='md' h="md" />
<div className="center-flex-row"> <Space hiddenFrom='md' w="xl" />
<Badge color="lime" radius="sm" size="md" variant="filled"> <Space hiddenFrom='md' w="md" />
FROM {service.ip_src} : {service.public_port}
</Badge>
<Space h="sm" />
<Badge color="blue" radius="sm" size="md" variant="filled">
<div className="center-flex">
TO {service.ip_dst} :
<form onSubmit={form.onSubmit((v)=>portInputRef.current?.blur())}>
<PortInput
defaultValue={service.proxy_port}
size="xs"
variant="unstyled"
style={{
width: (10+form.values.proxy_port.toString().length*6.2) +"px"
}}
className={style.portInput}
onBlur={(e)=>{onChangeProxyPort({proxy_port:parseInt(e.target.value)})}}
ref={portInputRef}
{...form.getInputProps("proxy_port")}
/>
</form>
</div>
</Badge>
</div>
<Space w="xl" /><Space w="xl" /> <Box className="center-flex-row">
<div className="center-flex"> <Badge color="lime" radius="sm" size="md" variant="filled">
<MenuDropDownWithButton> FROM {service.ip_src} : {service.public_port}
<Menu.Label><b>Rename service</b></Menu.Label> </Badge>
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item> <Space h="sm" />
<Menu.Label><b>Change destination</b></Menu.Label> <Badge color="blue" radius="sm" size="md" variant="filled">
<Menu.Item icon={<BsArrowRepeat size={18} />} onClick={()=>setChangeDestModal(true)}>Change hijacking destination</Menu.Item> <Box className="center-flex">
<Divider /> TO {service.ip_dst} :
<Menu.Label><b>Danger zone</b></Menu.Label> <form onSubmit={form.onSubmit((v)=>portInputRef.current?.blur())}>
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item> <PortInput
</MenuDropDownWithButton> defaultValue={service.proxy_port}
<Space w="md"/> size="xs"
<Tooltip label="Stop service" zIndex={0} color="red" opened={tooltipStopOpened}> variant="unstyled"
<ActionIcon color="red" loading={buttonLoading} style={{
onClick={stopService} size="xl" radius="md" variant="filled" width: (10+form.values.proxy_port.toString().length*6.2) +"px"
disabled={!service.active} }}
aria-describedby="tooltip-stop-id" className="firegex__porthijack__servicerow__portInput"
onFocus={() => setTooltipStopOpened(false)} onBlur={() => setTooltipStopOpened(false)} onBlur={(e)=>{onChangeProxyPort({proxy_port:parseInt(e.target.value)})}}
onMouseEnter={() => setTooltipStopOpened(true)} onMouseLeave={() => setTooltipStopOpened(false)}> ref={portInputRef}
<FaStop size="20px" /> {...form.getInputProps("proxy_port")}
</ActionIcon> />
</Tooltip> </form>
<Space w="md"/> </Box>
<Tooltip label="Start service" zIndex={0} color="teal"> </Badge>
<ActionIcon color="teal" size="xl" radius="md" onClick={startService} loading={buttonLoading} </Box>
variant="filled" disabled={service.active}> {isMedium?<Box className='flex-spacer' />:<Space h="xl" />}
<FaPlay size="20px" /> <Box className="center-flex">
</ActionIcon> <MenuDropDownWithButton>
</Tooltip> <Menu.Label><b>Rename service</b></Menu.Label>
</div> <Menu.Item leftSection={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
</div> <Menu.Label><b>Change destination</b></Menu.Label>
{isMedium?null:<Space h="xl" />} <Menu.Item leftSection={<BsArrowRepeat size={18} />} onClick={()=>setChangeDestModal(true)}>Change hijacking destination</Menu.Item>
<Space w="xl" /><Space w="xl" /> <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>
<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={!service.active}
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={service.active}>
<FaPlay size="20px" />
</ActionIcon>
</Tooltip>
</Box>
{isMedium?<Space w="xl" />:null}
</Grid.Col>
</Grid>
</Box>
</div>
<Divider size="sm" style={{width:"100%"}}/>
<YesNoModal <YesNoModal
title='Are you sure to delete this service?' 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' })} {...form.getInputProps('chosenInternalPort', { type: 'checkbox' })}
/> />
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Add Service</Button> <Button loading={submitLoading} type="submit">Add Service</Button>
</Group> </Group>

View File

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

View File

@@ -49,7 +49,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
placeholder="Awesome Service Name!" placeholder="Awesome Service Name!"
{...form.getInputProps('name')} {...form.getInputProps('name')}
/> />
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button loading={submitLoading} type="submit">Rename</Button> <Button loading={submitLoading} type="submit">Rename</Button>
</Group> </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 { useState } from 'react';
import { FaPause, FaPlay, FaStop } from 'react-icons/fa'; import { FaPause, FaPlay, FaStop } from 'react-icons/fa';
import { MdOutlineArrowForwardIos } from "react-icons/md" import { MdOutlineArrowForwardIos } from "react-icons/md"
import style from "./ServiceRow.module.scss";
import YesNoModal from '../../YesNoModal'; import YesNoModal from '../../YesNoModal';
import { errorNotify, isMediumScreen, okNotify } from '../../../js/utils'; import { errorNotify, isMediumScreen, okNotify } from '../../../js/utils';
import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs'; import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs';
@@ -101,34 +100,34 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
} }
return <> return <>
<Grid className={style.row} justify="flex-end" style={{width:"100%"}}> <Grid className="firegex__servicerow__row" justify="flex-end" style={{width:"100%"}}>
<Grid.Col md={4} xs={12}> <Grid.Col span={{ md:4, xs: 12 }}>
<div className={isMedium?"center-flex-row":"center-flex"}> <Box 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> <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> <Badge color={status_color} size="lg" radius="md">{service.internal_port} {"->"} {service.public_port}</Badge>
</div> </Box>
{!isMedium?<Space h="xl" />:null} {!isMedium?<Space h="xl" />:null}
</Grid.Col> </Grid.Col>
<Grid.Col className="center-flex" md={8} xs={12}> <Grid.Col className="center-flex" span={{ md:8, xs: 12 }}>
{!isMedium?<div className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>} {!isMedium?<Box className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
<div className="center-flex-row"> <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:"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 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> <Badge color="yellow" radius="sm" size="md" variant="filled">Connections Blocked: {service.n_packets}</Badge>
</div> </Box>
{isMedium?<div className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>} {isMedium?<Box className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
<div className="center-flex"> <Box className="center-flex">
<MenuDropDownWithButton> <MenuDropDownWithButton>
<Menu.Label><b>Rename service</b></Menu.Label> <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 /> <Divider />
<Menu.Label><b>Change ports</b></Menu.Label> <Menu.Label><b>Change ports</b></Menu.Label>
<Menu.Item icon={<TbNumbers size={18} />} onClick={()=>setChoosePortModal(true)}>Change port</Menu.Item> <Menu.Item leftSection={<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={<BsArrowRepeat size={18} />} onClick={()=>setChangePortModal(true)}>Regen proxy port</Menu.Item>
<Divider /> <Divider />
<Menu.Label><b>Danger zone</b></Menu.Label> <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> </MenuDropDownWithButton>
<Space w="md"/> <Space w="md"/>
{["pause","wait"].includes(service.status)? {["pause","wait"].includes(service.status)?
@@ -158,12 +157,12 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
<FaPlay size="20px" /> <FaPlay size="20px" />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
</div> </Box>
<Space w="xl" /><Space w="xl" /> <Space w="xl" /><Space w="xl" />
{onClick?<div> {onClick?<Box>
<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size={45} /> <MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size={45} />
<Space w="xl" /> <Space w="xl" />
</div>:null} </Box>:null}
{!isMedium?<><Space w="xl" /><Space w="xl" /></>:null} {!isMedium?<><Space w="xl" /><Space w="xl" /></>:null}
</Grid.Col> </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 { useState } from 'react';
import { RegexFilter } from '../../js/models'; import { RegexFilter } from '../../js/models';
import { b64decode, errorNotify, getapiobject, okNotify } from '../../js/utils'; import { b64decode, errorNotify, getapiobject, okNotify } from '../../js/utils';
import style from "./index.module.scss";
import { BsTrashFill } from "react-icons/bs" import { BsTrashFill } from "react-icons/bs"
import YesNoModal from '../YesNoModal'; import YesNoModal from '../YesNoModal';
import FilterTypeSelector from '../FilterTypeSelector'; 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}`)) }).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>
<Grid.Col span={2} className="center-flex"> <Grid.Col span={2} className="center-flex">
<Title order={4}>Regex:</Title> <Title order={4}>Regex:</Title>
</Grid.Col> </Grid.Col>
<Grid.Col span={8}> <Grid.Col span={8}>
<Box className={style.outer_regex_text}> <Box className="firegex__regexview__outer_regex_text">
<Text className={style.regex_text} onClick={()=>{ <Text className="firegex__regexview__regex_text" onClick={()=>{
clipboard.copy(regex_expr) clipboard.copy(regex_expr)
okNotify("Regex copied to clipboard!",`The regex '${regex_expr}' has been copied to the clipboard!`) okNotify("Regex copied to clipboard!",`The regex '${regex_expr}' has been copied to the clipboard!`)
}}>{regex_expr}</Text> }}>{regex_expr}</Text>
@@ -74,7 +73,7 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
</Grid.Col> </Grid.Col>
<Grid.Col className='center-flex' span={12}> <Grid.Col className='center-flex' span={12}>
<div className='center-flex-row'> <Box className='center-flex-row'>
<FilterTypeSelector <FilterTypeSelector
size="md" size="md"
color="gray" color="gray"
@@ -82,23 +81,23 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
value={regexInfo.is_blacklist?"blacklist":"whitelist"} value={regexInfo.is_blacklist?"blacklist":"whitelist"}
/> />
<Space h="md" /> <Space h="md" />
<div className='center-flex'> <Box className='center-flex'>
<Badge size="md" color="cyan" variant="filled">Service: {regexInfo.service_id}</Badge> <Badge size="md" color="cyan" variant="filled">Service: {regexInfo.service_id}</Badge>
<Space w="xs" /> <Space w="xs" />
<Badge size="md" color={regexInfo.active?"lime":"red"} variant="filled">{regexInfo.active?"ACTIVE":"DISABLED"}</Badge> <Badge size="md" color={regexInfo.active?"lime":"red"} variant="filled">{regexInfo.active?"ACTIVE":"DISABLED"}</Badge>
<Space w="xs" /> <Space w="xs" />
<Badge size="md" color="gray" variant="filled">ID: {regexInfo.id}</Badge> <Badge size="md" color="gray" variant="filled">ID: {regexInfo.id}</Badge>
</div> </Box>
</div> </Box>
<div className='flex-spacer' /> <Box className='flex-spacer' />
<div className='center-flex-row'> <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> <Badge size="md" color={regexInfo.is_case_sensitive?"grape":"pink"} variant="filled">Case: {regexInfo.is_case_sensitive?"SENSIIVE":"INSENSITIVE"}</Badge>
<Space h="xs" /> <Space h="xs" />
<Badge size="md" color="yellow" variant="filled">Packets filtered: {regexInfo.n_packets}</Badge> <Badge size="md" color="yellow" variant="filled">Packets filtered: {regexInfo.n_packets}</Badge>
<Space h="xs" /> <Space h="xs" />
<Badge size="md" color="blue" variant="filled">Mode: {mode_string}</Badge> <Badge size="md" color="blue" variant="filled">Mode: {mode_string}</Badge>
</div> </Box>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
<YesNoModal <YesNoModal
@@ -109,7 +108,7 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
opened={deleteModal} opened={deleteModal}
/> />
</div> </Box>
} }
export default RegexView; 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> return <Modal size="xl" title={title} opened={opened} onClose={onClose} centered>
{description} {description}
<Group position="right" mt="md"> <Group align="right" mt="md">
<Button onClick={()=>{ <Button onClick={()=>{
onClose() onClose()
action() 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 ReactDOM from 'react-dom/client';
import { BrowserRouter } from "react-router-dom" import { BrowserRouter } from "react-router-dom"
import './index.scss';
import App from './App'; import App from './App';
import { MantineProvider } from '@mantine/core'; import { createTheme, MantineProvider } from '@mantine/core';
import { Notifications } from '@mantine/notifications'; import { Notifications } from '@mantine/notifications';
import { import {
QueryClientProvider, QueryClientProvider,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import { queryClient } from './js/utils'; import { queryClient } from './js/utils';
import '@mantine/core/styles.css';
import '@mantine/notifications/styles.css';
import './index.css';
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement document.getElementById('root') as HTMLElement
@@ -16,10 +17,10 @@ const root = ReactDOM.createRoot(
root.render( root.render(
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<MantineProvider theme={{ colorScheme: 'dark' }} withGlobalStyles withNormalizeCSS> <MantineProvider defaultColorScheme='dark'>
<Notifications /> <Notifications />
<BrowserRouter> <BrowserRouter>
<App /> <App />
</BrowserRouter> </BrowserRouter>
</MantineProvider> </MantineProvider>
</QueryClientProvider> </QueryClientProvider>

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("/") const paths = window.location.pathname.split("/")
if (paths.length > 1) return paths[1] if (paths.length > 1) return paths[1]
return "" return ""
} }
export function getapiobject(){ export function getapiobject(){
switch(getmainpath()){ switch(getMainPath()){
case "nfregex": case "nfregex":
return nfregex return nfregex
case "regexproxy": 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})}/> <Switch label="Drop invalid packet" checked={settings.drop_invalid} onChange={v => setSettings({...settings, drop_invalid:v.target.checked})}/>
<Space h="md" /> <Space h="md" />
<Switch label="Allow DHCP" checked={settings.allow_dhcp} onChange={v => setSettings({...settings, allow_dhcp:v.target.checked})}/> <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> <Button loading={submitLoading} onClick={submitRequest}>Save Setting</Button>
</Group> </Group>
</Modal> </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 { useEffect, useState } from "react";
import { BsPlusLg, BsTrashFill } from "react-icons/bs" import { BsPlusLg, BsTrashFill } from "react-icons/bs"
import { rem } from '@mantine/core'; import { rem } from '@mantine/core';
@@ -37,8 +37,8 @@ export const Firewall = () => {
const [applyChangeModal, setApplyChangeModal] = useState(false) const [applyChangeModal, setApplyChangeModal] = useState(false)
const [settingsModal, setSettingsModal] = useState(false) const [settingsModal, setSettingsModal] = useState(false)
const theme = useMantineTheme(); const theme = useMantineTheme();
const isMedium = useMediaQuery(`(min-width: 950px)`) const isMedium = useMediaQuery(`(min-width: 950px)`)??true
const isSmall = useMediaQuery(`(max-width: 600px)`) const isSmall = useMediaQuery(`(max-width: 600px)`)??false
const [updateMevalueinternal, internalUpdateme] = useState(false) const [updateMevalueinternal, internalUpdateme] = useState(false)
const updateMe = () => { const updateMe = () => {
@@ -104,7 +104,7 @@ export const Firewall = () => {
const condDiv = (val:React.ReactNode, cond:boolean) => { const condDiv = (val:React.ReactNode, cond:boolean) => {
if (cond) if (cond)
return <div>{val}</div> return <Box>{val}</Box>
else else
return val return val
} }
@@ -201,18 +201,18 @@ export const Firewall = () => {
dst_port: disabletab.dst_port?disable_style:{} dst_port: disabletab.dst_port?disable_style:{}
} }
return <div return <Box
ref={provided.innerRef} ref={provided.innerRef}
{...provided.draggableProps} {...provided.draggableProps}
> >
<div className='center-flex' style={{width:"100%"}}> <Box className='center-flex' style={{width:"100%"}}>
<div {...provided.dragHandleProps}> <Box {...provided.dragHandleProps}>
<TbGripVertical style={{ width: rem(30), height: rem(40) }} /> <TbGripVertical style={{ width: rem(30), height: rem(40) }} />
</div> </Box>
<Space w="sm" /> <Space w="sm" />
<div className={isMedium?"center-flex":"center-flex-row"} style={{width:"100%"}}> <Box className={isMedium?"center-flex":"center-flex-row"} style={{width:"100%"}}>
<div className="center-flex-row" style={{width:"100%"}}> <Box className="center-flex-row" style={{width:"100%"}}>
<div className="center-flex" style={{width:"97%"}}> <Box className="center-flex" style={{width:"97%"}}>
<Switch <Switch
checked={item.active} checked={item.active}
onChange={() =>{ 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> <ActionIcon color="red" onClick={()=>handlers.remove(index)} size="lg" radius="md" variant="filled"><BsTrashFill size={18} /></ActionIcon>
<Space w="sm" /> <Space w="sm" />
<TextInput defaultValue={item.name} onChange={(v)=>{item.name = v.target.value;updateMe()}} style={{width:"100%"}}/> <TextInput defaultValue={item.name} onChange={(v)=>{item.name = v.target.value;updateMe()}} style={{width:"100%"}}/>
</div> </Box>
<Space h="sm" /> <Space h="sm" />
<div className="center-flex" style={{width:"97%"}}> <Box className="center-flex" style={{width:"97%"}}>
<div style={{width:"100%"}}> <Box style={{width:"100%"}}>
<InterfaceInput <InterfaceInput
initialCustomInterfaces={[...src_custom_int, ...customInt]} initialCustomInterfaces={[...src_custom_int, ...customInt]}
value={item.src} value={item.src}
@@ -250,7 +250,7 @@ export const Firewall = () => {
includeInterfaceNames includeInterfaceNames
/> />
<Space h="sm" /> <Space h="sm" />
<div className="center-flex" style={{width:"100%"}}> <Box className="center-flex" style={{width:"100%"}}>
<OnOffButton value={srcPortEnabled} onClick={() =>{ <OnOffButton value={srcPortEnabled} onClick={() =>{
const value = !srcPortEnabled const value = !srcPortEnabled
setSrcPortEnabled(value) setSrcPortEnabled(value)
@@ -270,12 +270,12 @@ export const Firewall = () => {
error={!disabletab.src_port && !srcPortValue} error={!disabletab.src_port && !srcPortValue}
style={{width:"100%", ...additionalStyle.src_port}} style={{width:"100%", ...additionalStyle.src_port}}
/> />
</div> </Box>
</div> </Box>
<Space w="lg" /> <Space w="lg" />
<LuArrowBigRightDash size={100} /> <LuArrowBigRightDash size={100} />
<Space w="lg" /> <Space w="lg" />
<div style={{width:"100%"}}> <Box style={{width:"100%"}}>
<InterfaceInput <InterfaceInput
initialCustomInterfaces={[...dst_custom_int, ...customInt]} initialCustomInterfaces={[...dst_custom_int, ...customInt]}
defaultValue={item.dst} defaultValue={item.dst}
@@ -283,7 +283,7 @@ export const Firewall = () => {
includeInterfaceNames includeInterfaceNames
/> />
<Space h="sm" /> <Space h="sm" />
<div className="center-flex" style={{width:"100%"}}> <Box className="center-flex" style={{width:"100%"}}>
<OnOffButton value={dstPortEnabled} onClick={() =>{ <OnOffButton value={dstPortEnabled} onClick={() =>{
const value = !dstPortEnabled const value = !dstPortEnabled
setDstPortEnabled(value) setDstPortEnabled(value)
@@ -303,12 +303,12 @@ export const Firewall = () => {
error={!disabletab.dst_port && !dstPortValue} error={!disabletab.dst_port && !dstPortValue}
style={{width:"100%", ...additionalStyle.dst_port}} style={{width:"100%", ...additionalStyle.dst_port}}
/> />
</div> </Box>
</div> </Box>
</div> </Box>
</div> </Box>
{!isMedium?<Space h="sm" />:null} {!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 {condDiv(<>{condDiv(<ModeSelector
value={item.mode} value={item.mode}
onChange={(value)=>{item.mode = value as RuleMode;updateMe()}} onChange={(value)=>{item.mode = value as RuleMode;updateMe()}}
@@ -326,13 +326,13 @@ export const Firewall = () => {
onChange={(value)=>{item.action = value as ActionType;updateMe()}} onChange={(value)=>{item.action = value as ActionType;updateMe()}}
style={{width:"100%"}} style={{width:"100%"}}
/>, !isMedium)} />, !isMedium)}
</div> </Box>
</div> </Box>
</div> </Box>
<Space h="md" /> <Space h="md" />
<Divider /> <Divider />
<Space h="md" /> <Space h="md" />
</div> </Box>
}} }}
</Draggable> </Draggable>
)); ));
@@ -341,10 +341,10 @@ export const Firewall = () => {
return <> return <>
<Space h="sm" /> <Space h="sm" />
<LoadingOverlay visible={rules.isLoading} /> <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> <Title order={3}>Firewall Rules</Title>
{isMedium?<div className='flex-spacer' />:<Space h="sm" />} {isMedium?<Box className='flex-spacer' />:<Space h="sm" />}
<div className='center-flex'> <Box className='center-flex'>
Enabled: <Space w="sm" /> <Switch checked={fwEnabled} onChange={switchState} /> Enabled: <Space w="sm" /> <Switch checked={fwEnabled} onChange={switchState} />
<Space w="sm" /> <Space w="sm" />
Policy: Policy:
@@ -353,9 +353,9 @@ export const Firewall = () => {
value={currentPolicy} value={currentPolicy}
onChange={(value)=>setCurrentPolicy(value as ActionType)} onChange={(value)=>setCurrentPolicy(value as ActionType)}
/> />
</div> </Box>
{isMedium?<div className='flex-spacer' />:<Space h="sm" />} {isMedium?<Box className='flex-spacer' />:<Space h="sm" />}
<div className='center-flex'> <Box className='center-flex'>
<Space w="xs" /> <Space w="xs" />
<Badge size="sm" color="green" variant="filled">Rules: {rules.isLoading?0:rules.data?.rules.length}</Badge> <Badge size="sm" color="green" variant="filled">Rules: {rules.isLoading?0:rules.data?.rules.length}</Badge>
<Space w="xs" /> <Space w="xs" />
@@ -385,8 +385,8 @@ export const Firewall = () => {
disabled={!valuesChanged} disabled={!valuesChanged}
><TiTick size={22} /></ActionIcon> ><TiTick size={22} /></ActionIcon>
</Tooltip> </Tooltip>
</div> </Box>
</div> </Box>
<Space h="xl" /> <Space h="xl" />
<Divider /> <Divider />
{items.length > 0?<DragDropContext {items.length > 0?<DragDropContext
@@ -397,22 +397,22 @@ export const Firewall = () => {
<Space h="md" /> <Space h="md" />
<Droppable droppableId="dnd-list" direction="vertical"> <Droppable droppableId="dnd-list" direction="vertical">
{(provided) => ( {(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}> <Box {...provided.droppableProps} ref={provided.innerRef}>
{items} {items}
{provided.placeholder} {provided.placeholder}
</div> </Box>
)} )}
</Droppable> </Droppable>
</DragDropContext>:<> </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" /> <Space h="xl" /> <Space h="xl" />
<div className='center-flex'> <Box className='center-flex'>
<Tooltip label="Add a new rule" color="blue" opened={tooltipAddRulOpened}> <Tooltip label="Add a new rule" color="blue" opened={tooltipAddRulOpened}>
<ActionIcon color="blue" onClick={emptyRuleAdd} size="xl" radius="md" variant="filled" <ActionIcon color="blue" onClick={emptyRuleAdd} size="xl" radius="md" variant="filled"
onFocus={() => setTooltipAddRulOpened(false)} onBlur={() => setTooltipAddRulOpened(false)} onFocus={() => setTooltipAddRulOpened(false)} onBlur={() => setTooltipAddRulOpened(false)}
onMouseEnter={() => setTooltipAddRulOpened(true)} onMouseLeave={() => setTooltipAddRulOpened(false)}><BsPlusLg size="20px" /></ActionIcon> onMouseEnter={() => setTooltipAddRulOpened(true)} onMouseLeave={() => setTooltipAddRulOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip> </Tooltip>
</div> </Box>
</>} </>}
<YesNoModal <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 { useState } from 'react';
import { Navigate, useParams } from 'react-router-dom'; import { Navigate, useParams } from 'react-router-dom';
import RegexView from '../../components/RegexView'; import RegexView from '../../components/RegexView';
@@ -23,19 +23,19 @@ function ServiceDetailsNFRegex() {
<ServiceRow service={serviceInfo} /> <ServiceRow service={serviceInfo} />
{(!regexesList.data || regexesList.data.length == 0)?<> {(!regexesList.data || regexesList.data.length == 0)?<>
<Space h="xl" /> <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" /> <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}> <Tooltip label="Add a new regex" zIndex={0} color="blue" opened={tooltipAddRegexOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled" <ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
aria-describedby="tooltip-AddRegex-id" aria-describedby="tooltip-AddRegex-id"
onFocus={() => setTooltipAddRegexOpened(false)} onBlur={() => setTooltipAddRegexOpened(false)} onFocus={() => setTooltipAddRegexOpened(false)} onBlur={() => setTooltipAddRegexOpened(false)}
onMouseEnter={() => setTooltipAddRegexOpened(true)} onMouseLeave={() => setTooltipAddRegexOpened(false)}><BsPlusLg size="20px" /></ActionIcon> onMouseEnter={() => setTooltipAddRegexOpened(true)} onMouseLeave={() => setTooltipAddRegexOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip> </Tooltip>
</div> </Box>
</>: </>:
<Grid> <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> </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 { useEffect, useState } from 'react';
import { BsPlusLg } from "react-icons/bs"; import { BsPlusLg } from "react-icons/bs";
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
@@ -32,19 +32,19 @@ function NFRegex({ children }: { children: any }) {
return <> return <>
<Space h="sm" /> <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> <Title order={4}>Netfilter Regex</Title>
{isMedium?<div className='flex-spacer' />:<Space h="sm" />} {isMedium?<Box className='flex-spacer' />:<Space h="sm" />}
<div className='center-flex' > <Box className='center-flex' >
<Badge size="sm" color="green" variant="filled">Services: {services.isLoading?0:services.data?.length}</Badge> <Badge size="sm" color="green" variant="filled">Services: {services.isLoading?0:services.data?.length}</Badge>
<Space w="xs" /> <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> <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" /> <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> <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" /> <Space w="xs" />
</div> </Box>
{isMedium?null:<Space h="md" />} {isMedium?null:<Space h="md" />}
<div className='center-flex' > <Box className='center-flex' >
{ srv? { srv?
<Tooltip label="Add a new regex" position='bottom' color="blue" opened={tooltipAddOpened}> <Tooltip label="Add a new regex" position='bottom' color="blue" opened={tooltipAddOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="lg" radius="md" variant="filled" <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)} onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon> onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
</Tooltip> </Tooltip>
</div> </Box>
</div> </Box>
<Space h="md" /> <Space h="md" />
<Divider size="md" style={{width:"100%"}}/> <Box className="center-flex-row" style={{gap: 20}}>
<div id="service-list" className="center-flex-row">
{srv?null:<> {srv?null:<>
<LoadingOverlay visible={services.isLoading} /> <LoadingOverlay visible={services.isLoading} />
{(services.data && services.data?.length > 0)?services.data.map( srv => <ServiceRow service={srv} key={srv.service_id} onClick={()=>{ {(services.data && services.data?.length > 0)?services.data.map( srv => <ServiceRow service={srv} key={srv.service_id} onClick={()=>{
navigator("/nfregex/"+srv.service_id) 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"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No services found! Add one clicking the "+" buttons</Title>
<Space h="xl" /> <Space h="xl" /> <Box className='center-flex'>
<div className='center-flex'>
<Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}> <Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled" <ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)} onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)}
onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon> onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip> </Tooltip>
</div> </Box>
</>} </>}
<AddNewService opened={open} onClose={closeModal} /> <AddNewService opened={open} onClose={closeModal} />
</>} </>}
</div> </Box>
{srv?children:null} {srv?children:null}
{srv? {srv?
<AddNewRegex opened={open} onClose={closeModal} service={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 { useEffect, useState } from 'react';
import { BsPlusLg } from "react-icons/bs"; import { BsPlusLg } from "react-icons/bs";
import ServiceRow from '../../components/PortHijack/ServiceRow'; import ServiceRow from '../../components/PortHijack/ServiceRow';
@@ -29,10 +29,10 @@ function PortHijack() {
return <> return <>
<Space h="sm" /> <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> <Title order={4}>Hijack port to proxy</Title>
{isMedium?<div className='flex-spacer' />:<Space h="sm" />} {isMedium?<Box className='flex-spacer' />:<Space h="sm" />}
<div className='center-flex'> <Box className='center-flex'>
<Badge size="sm" color="yellow" variant="filled">Services: {services.isLoading?0:services.data?.length}</Badge> <Badge size="sm" color="yellow" variant="filled">Services: {services.isLoading?0:services.data?.length}</Badge>
<Space w="xs" /> <Space w="xs" />
<Tooltip label="Add a new service" position='bottom' color="blue" opened={tooltipAddOpened}> <Tooltip label="Add a new service" position='bottom' color="blue" opened={tooltipAddOpened}>
@@ -47,25 +47,23 @@ function PortHijack() {
onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)} onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon> onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
</Tooltip> </Tooltip>
</div> </Box>
</div> </Box>
<Space h="md" /><Divider size="sm" style={{width:"100%"}}/> <Space h="md" />
<div id="service-list" className="center-flex-row"> <Box className="center-flex-row" style={{gap: 20}}>
<LoadingOverlay visible={services.isLoading} /> <LoadingOverlay visible={services.isLoading} />
{(services.data && services.data.length > 0) ?services.data.map( srv => <ServiceRow service={srv} key={srv.service_id} />):<> {(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"/> <Title className='center-flex' style={{textAlign:"center"}} order={3}>No services found! Add one clicking the "+" buttons</Title>
<Space h="xl" /> <Space h="xl" /> <Box className='center-flex'>
<div className='center-flex'>
<Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}> <Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled" <ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)} onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)}
onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon> onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip> </Tooltip>
</div> </Box>
</>} </>}
<AddNewService opened={open} onClose={closeModal} /> <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 { useState } from 'react';
import { Navigate, useParams } from 'react-router-dom'; import { Navigate, useParams } from 'react-router-dom';
import { BsPlusLg } from "react-icons/bs"; import { BsPlusLg } from "react-icons/bs";
@@ -18,31 +18,31 @@ function ServiceDetailsProxyRegex() {
if (!srv || !serviceInfo || regexesList.isError) return <Navigate to="/" replace /> if (!srv || !serviceInfo || regexesList.isError) return <Navigate to="/" replace />
return <div> return <Box>
<LoadingOverlay visible={regexesList.isLoading} /> <LoadingOverlay visible={regexesList.isLoading} />
<ServiceRow service={serviceInfo} /> <ServiceRow service={serviceInfo} />
{(!regexesList.data || regexesList.data.length == 0)?<> {(!regexesList.data || regexesList.data.length == 0)?<>
<Space h="xl" /> <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" /> <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}> <Tooltip label="Add a new regex" zIndex={0} color="blue" opened={tooltipAddRegexOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled" <ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
aria-describedby="tooltip-AddRegex-id" aria-describedby="tooltip-AddRegex-id"
onFocus={() => setTooltipAddRegexOpened(false)} onBlur={() => setTooltipAddRegexOpened(false)} onFocus={() => setTooltipAddRegexOpened(false)} onBlur={() => setTooltipAddRegexOpened(false)}
onMouseEnter={() => setTooltipAddRegexOpened(true)} onMouseLeave={() => setTooltipAddRegexOpened(false)}><BsPlusLg size="20px" /></ActionIcon> onMouseEnter={() => setTooltipAddRegexOpened(true)} onMouseLeave={() => setTooltipAddRegexOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip> </Tooltip>
</div> </Box>
</>: </>:
<Grid> <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> </Grid>
} }
{srv?<AddNewRegex opened={open} onClose={() => {setOpen(false)}} service={srv} />:null} {srv?<AddNewRegex opened={open} onClose={() => {setOpen(false)}} service={srv} />:null}
</div> </Box>
} }
export default ServiceDetailsProxyRegex; 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 { useEffect, useState } from 'react';
import { BsPlusLg } from "react-icons/bs"; import { BsPlusLg } from "react-icons/bs";
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
@@ -33,9 +33,9 @@ function RegexProxy({ children }: { children: any }) {
return <> return <>
<Space h="sm" /> <Space h="sm" />
<div className='center-flex'> <Box className='center-flex'>
<Title order={4}>TCP Proxy Regex Filter (IPv4 Only)</Title> <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> <Badge size="sm" color="green" variant="filled">Services: {services.isLoading?0:services.data?.length}</Badge>
<Space w="xs" /> <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> <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)} onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)}
onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon> onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}><TbReload size={18} /></ActionIcon>
</Tooltip> </Tooltip>
</div> </Box>
<div id="service-list" className="center-flex-row"> <Box className="center-flex-row">
{srv?null:<> {srv?null:<>
<LoadingOverlay visible={services.isLoading} /> <LoadingOverlay visible={services.isLoading} />
{(services.data && services.data?.length > 0)?services.data.map( srv => <ServiceRow service={srv} key={srv.id} onClick={()=>{ {(services.data && services.data?.length > 0)?services.data.map( srv => <ServiceRow service={srv} key={srv.id} onClick={()=>{
navigator("/regexproxy/"+srv.id) 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" /> <Space h="xl" /> <Space h="xl" />
<div className='center-flex'> <Box className='center-flex'>
<Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}> <Tooltip label="Add a new service" color="blue" opened={tooltipAddServOpened}>
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled" <ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)} onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)}
onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon> onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
</Tooltip> </Tooltip>
</div> </Box>
</>} </>}
<AddNewService opened={open} onClose={closeModal} /> <AddNewService opened={open} onClose={closeModal} />
</>} </>}
</div> </Box>
{srv?children:null} {srv?children:null}
{srv? {srv?
<AddNewRegex opened={open} onClose={closeModal} service={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 react from '@vitejs/plugin-react';
import viteTsconfigPaths from 'vite-tsconfig-paths'; import viteTsconfigPaths from 'vite-tsconfig-paths';
import svgrPlugin from 'vite-plugin-svgr'; import svgrPlugin from 'vite-plugin-svgr';
import { defineConfig } from 'vite';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react(), viteTsconfigPaths(), svgrPlugin()], plugins: [react(), viteTsconfigPaths(), svgrPlugin()],
}); })