add: frontend update
This commit is contained in:
@@ -108,7 +108,7 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>
|
||||
<Alert variant="light" color="yellow" radius="lg" title="You are using whitelists" icon={<AiFillWarning />}>
|
||||
Using whitelist means that EVERY packet that doesn't match the regex will be DROPPED... In most cases this cause the service interruption.
|
||||
</Alert></>:null}
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Add Filter</Button>
|
||||
</Group>
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Box, Center, SegmentedControl } from "@mantine/core";
|
||||
import React from "react";
|
||||
import { FaListAlt } from "react-icons/fa";
|
||||
import { TiCancel } from "react-icons/ti";
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
@use "../../vars" as *;
|
||||
@use "../../index.scss" as *;
|
||||
|
||||
.footer{
|
||||
margin-top: 50px;
|
||||
background-color: $primary_color;
|
||||
@extend .center-flex;
|
||||
}
|
||||
@@ -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 |
@@ -47,7 +47,7 @@ function ResetModal({ opened, onClose }:{ opened: boolean, onClose: () => void }
|
||||
{...form.getInputProps('delete_data', { type: 'checkbox' })}
|
||||
/>
|
||||
<Space h="md" />
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button loading={loadingBtn} onClick={close} >Cancel</Button>
|
||||
<Button loading={loadingBtn} type="submit" color="red">Reset</Button>
|
||||
</Group>
|
||||
|
||||
@@ -46,7 +46,7 @@ function ResetPasswordModal({ opened, onClose }:{ opened: boolean, onClose: () =
|
||||
{...form.getInputProps('expire', { type: 'checkbox' })}
|
||||
/>
|
||||
<Space h="md" />
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button loading={loadingBtn} type="submit">Change Password</Button>
|
||||
</Group>
|
||||
</form>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ActionIcon, Divider, Image, Menu, Tooltip, Burger, Space, Header, Button, ThemeIcon } from '@mantine/core';
|
||||
import style from "./index.module.scss";
|
||||
import { errorNotify, getmainpath, isLargeScreen, logout } from '../../js/utils';
|
||||
import { ActionIcon, Divider, Image, Menu, Tooltip, Burger, Space, AppShell, Box, Title } from '@mantine/core';
|
||||
import { errorNotify, getMainPath, isLargeScreen, logout } from '../../js/utils';
|
||||
import { AiFillHome } from "react-icons/ai"
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { FaLock } from 'react-icons/fa';
|
||||
@@ -10,11 +9,13 @@ import { ImExit } from 'react-icons/im';
|
||||
import ResetPasswordModal from './ResetPasswordModal';
|
||||
import ResetModal from './ResetModal';
|
||||
import { MenuDropDownWithButton } from '../MainLayout';
|
||||
import { useNavbarStore } from '../../js/store';
|
||||
|
||||
|
||||
function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:React.Dispatch<React.SetStateAction<boolean>>}) {
|
||||
function HeaderPage(props: any) {
|
||||
|
||||
const navigator = useNavigate()
|
||||
const { navOpened, toggleNav } = useNavbarStore()
|
||||
|
||||
const logout_action = () => {
|
||||
logout().then(r => {
|
||||
@@ -25,40 +26,42 @@ function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:Reac
|
||||
}
|
||||
|
||||
const go_to_home = () => {
|
||||
navigator(`/${getmainpath()}`)
|
||||
navigator(`/${getMainPath()}`)
|
||||
}
|
||||
|
||||
const [changePasswordModal, setChangePasswordModal] = useState(false);
|
||||
const [resetFiregexModal, setResetFiregexModal] = useState(false);
|
||||
const [tooltipHomeOpened, setTooltipHomeOpened] = useState(false);
|
||||
const [tooltipLogoutOpened,setTooltipLogoutOpened] = useState(false);
|
||||
const isLarge = isLargeScreen()
|
||||
console.log(isLarge)
|
||||
return <Header height={100} className={style.header} {...other}>
|
||||
<Space w="lg" />
|
||||
{isLarge?null:<div>
|
||||
<Burger
|
||||
opened={navOpen}
|
||||
className={style.navbtn}
|
||||
onClick={() => setNav((o) => !o)}
|
||||
size="sm"
|
||||
mr="xl"
|
||||
/>
|
||||
</div>}
|
||||
<div className={style.divlogo}>
|
||||
<Tooltip zIndex={0} label="Home" openDelay={1000}color="dark" position="right" >
|
||||
<Image src="/header-logo.png" alt="Firegex logo" onClick={()=>navigator("/")}/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
return <AppShell.Header className="firegex__header__header" {...props}>
|
||||
<Burger
|
||||
hiddenFrom='md'
|
||||
ml="lg"
|
||||
opened={navOpened}
|
||||
className="firegex__header__navbtn"
|
||||
onClick={toggleNav}
|
||||
size="sm"
|
||||
/>
|
||||
<Box style={{ display: "flex", justifyContent: "center", alignItems: "center"}} ml={5}>
|
||||
<Box className="firegex__header__divlogo">
|
||||
<Tooltip zIndex={0} label="Home" openDelay={1000} color="dark" position="right" >
|
||||
<Image src="/header-logo.png" alt="Firegex logo" w={50} onClick={()=>navigator("/")}/>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Box display="flex" style={{ flexDirection: "column" }} visibleFrom='xs'>
|
||||
<Title order={2} >[Fi]*regex</Title>
|
||||
<p style={{margin: 0, fontSize: "70%"}}>By <a href="https://pwnzer0tt1.it">Pwnzer0tt1</a></p>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<div className="flex-spacer" />
|
||||
<Box className="flex-spacer" />
|
||||
|
||||
<MenuDropDownWithButton>
|
||||
<Menu.Label>Firewall Access</Menu.Label>
|
||||
<Menu.Item icon={<FaLock size={14} />} onClick={() => setChangePasswordModal(true)}>Change Password</Menu.Item>
|
||||
<Menu.Item leftSection={<FaLock size={14} />} onClick={() => setChangePasswordModal(true)}>Change Password</Menu.Item>
|
||||
<Divider />
|
||||
<Menu.Label>Actions</Menu.Label>
|
||||
<Menu.Item color="red" icon={<MdOutlineSettingsBackupRestore size={18} />} onClick={() => setResetFiregexModal(true)}>Reset Firegex</Menu.Item>
|
||||
<Menu.Item color="red" leftSection={<MdOutlineSettingsBackupRestore size={18} />} onClick={() => setResetFiregexModal(true)}>Reset Firegex</Menu.Item>
|
||||
</MenuDropDownWithButton>
|
||||
<Space w="md" />
|
||||
<Tooltip label="Home" position='bottom' color="teal" opened={tooltipHomeOpened}>
|
||||
@@ -78,7 +81,7 @@ function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:Reac
|
||||
<ResetPasswordModal opened={changePasswordModal} onClose={() => setChangePasswordModal(false)} />
|
||||
<ResetModal opened={resetFiregexModal} onClose={() => setResetFiregexModal(false)} />
|
||||
<Space w="xl" />
|
||||
</Header>
|
||||
</AppShell.Header>
|
||||
}
|
||||
|
||||
export default HeaderPage;
|
||||
|
||||
@@ -1,55 +1,119 @@
|
||||
import { AutocompleteItem, Select, SelectProps } from "@mantine/core";
|
||||
import React, { useState } from "react";
|
||||
import { Combobox, TextInput, useCombobox } from "@mantine/core";
|
||||
import { useState } from "react";
|
||||
import { ipInterfacesQuery } from "../js/utils";
|
||||
|
||||
|
||||
const AutoCompleteItem = React.forwardRef<HTMLDivElement, ItemProps>(
|
||||
({ netint, value, ...props }: ItemProps, ref) => <div ref={ref} {...props}>
|
||||
( <b>{netint}</b> ) -{">"} <b>{value}</b>
|
||||
</div>
|
||||
);
|
||||
|
||||
interface ItemProps extends AutocompleteItem {
|
||||
interface ItemProps{
|
||||
value: string
|
||||
netint: string;
|
||||
}
|
||||
|
||||
interface InterfaceInputProps extends Omit<SelectProps, "data">{
|
||||
initialCustomInterfaces?:AutocompleteItem[],
|
||||
includeInterfaceNames?:boolean
|
||||
interface InterfaceInputProps{
|
||||
initialCustomInterfaces?:ItemProps[],
|
||||
includeInterfaceNames?:boolean,
|
||||
onChange?: (value:string) => void,
|
||||
value?:string,
|
||||
defaultValue?:string
|
||||
}
|
||||
|
||||
export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, ...props }:InterfaceInputProps) => {
|
||||
export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, onChange, value, defaultValue }:InterfaceInputProps) => {
|
||||
|
||||
const [customIpInterfaces, setCustomIpInterfaces] = useState<AutocompleteItem[]>(initialCustomInterfaces??[]);
|
||||
const [customIpInterfaces, setCustomIpInterfaces] = useState<ItemProps[]>(initialCustomInterfaces??[]);
|
||||
const interfacesQuery = ipInterfacesQuery()
|
||||
|
||||
const getInterfaces = () => {
|
||||
if (interfacesQuery.isLoading || !interfacesQuery.data) return []
|
||||
if(includeInterfaceNames){
|
||||
const result = interfacesQuery.data.map(item => ({netint:"IP", value:item.addr, label:item.addr})) as AutocompleteItem[]
|
||||
interfacesQuery.data.map(item => item.name).filter((item, index, arr) => arr.indexOf(item) === index).forEach(item => result.push({netint:"INT", value:item, label:item}))
|
||||
const result = interfacesQuery.data.map(item => ({netint:"IP", value:item.addr})) as ItemProps[]
|
||||
interfacesQuery.data.map(item => item.name).filter((item, index, arr) => arr.indexOf(item) === index).forEach(item => result.push({netint:"INT", value:item}))
|
||||
return result
|
||||
}
|
||||
return (interfacesQuery.data.map(item => ({netint:item.name, value:item.addr, label:item.addr})) as AutocompleteItem[])
|
||||
return (interfacesQuery.data.map(item => ({netint:item.name, value:item.addr})) as ItemProps[])
|
||||
}
|
||||
|
||||
const interfaces = getInterfaces()
|
||||
|
||||
return <Select
|
||||
placeholder="10.1.1.1"
|
||||
itemComponent={AutoCompleteItem}
|
||||
data={[...customIpInterfaces, ...interfaces]}
|
||||
searchable
|
||||
dropdownPosition="bottom"
|
||||
maxDropdownHeight={200}
|
||||
creatable
|
||||
getCreateLabel={(query) => `+ Use this: ${query}`}
|
||||
onCreate={(query) => {
|
||||
const item = { value: query, netint: "CUSTOM", label: query };
|
||||
setCustomIpInterfaces((current) => [...current, item]);
|
||||
return item;
|
||||
const combobox = useCombobox({
|
||||
onDropdownClose: () => {
|
||||
combobox.resetSelectedOption()
|
||||
},
|
||||
});
|
||||
|
||||
const data = [...customIpInterfaces, ...interfaces]
|
||||
const [selectedValue, setSelectedValue] = useState<string | null>(null);
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
const exactOptionMatch: ItemProps|undefined = data.find((item) => item.value === search);
|
||||
|
||||
const filteredOptions = data.filter((item) => item.value.toLowerCase().includes(search.toLowerCase().trim())).sort((a, b) => {
|
||||
if (exactOptionMatch != null) {
|
||||
if (a.value == exactOptionMatch.value) return -1
|
||||
if (b.value == exactOptionMatch.value) return 1
|
||||
}
|
||||
return a.value.localeCompare(b.value)
|
||||
});
|
||||
|
||||
const options = filteredOptions.map((item) => (
|
||||
<Combobox.Option value={item.value} key={item.value}>
|
||||
( <b>{item.netint}</b> ) -{">"} <b>{item.value}</b>
|
||||
</Combobox.Option>
|
||||
));
|
||||
|
||||
return <>
|
||||
<Combobox
|
||||
store={combobox}
|
||||
withinPortal={false}
|
||||
position="bottom-end"
|
||||
onOptionSubmit={(value) => {
|
||||
if (value === '$create') {
|
||||
const item = { value: search, netint: "CUSTOM" };
|
||||
setCustomIpInterfaces((current) => [...current, item]);
|
||||
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>
|
||||
</>
|
||||
}
|
||||
@@ -1,44 +1,38 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { ActionIcon, Container, Menu, Space } from '@mantine/core';
|
||||
import { AppShell } from '@mantine/core';
|
||||
import NavBar from './NavBar';
|
||||
import FooterPage from './Footer';
|
||||
import HeaderPage from './Header';
|
||||
import { getmainpath } from '../js/utils';
|
||||
import { getMainPath } from '../js/utils';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { RiMenu5Fill } from 'react-icons/ri';
|
||||
|
||||
|
||||
import { useNavbarStore } from '../js/store';
|
||||
|
||||
|
||||
function MainLayout({ children }:{ children:any }) {
|
||||
const [opened, setOpened] = useState(false);
|
||||
const { navOpened } = useNavbarStore()
|
||||
const location = useLocation()
|
||||
useEffect(()=>{
|
||||
if (location.pathname !== "/"){
|
||||
sessionStorage.setItem('home_section', getmainpath())
|
||||
sessionStorage.setItem('home_section', getMainPath())
|
||||
}
|
||||
},[location.pathname])
|
||||
|
||||
|
||||
return <>
|
||||
|
||||
<AppShell
|
||||
padding="md"
|
||||
fixed
|
||||
navbar={<NavBar closeNav={()=>setOpened(false)} opened={opened} />}
|
||||
header={<HeaderPage navOpen={opened} setNav={setOpened} />}
|
||||
footer={<FooterPage />}
|
||||
return <AppShell
|
||||
header={{ height: 70 }}
|
||||
navbar={{ width: 300 , breakpoint: "md", collapsed: { mobile: !navOpened } }}
|
||||
p="md"
|
||||
>
|
||||
<Container size="lg">
|
||||
{children}
|
||||
</Container>
|
||||
<HeaderPage />
|
||||
<NavBar />
|
||||
<AppShell.Main>
|
||||
<Container size="lg">
|
||||
{children}
|
||||
</Container>
|
||||
</AppShell.Main>
|
||||
<Space h="lg" />
|
||||
|
||||
</AppShell>
|
||||
|
||||
</>
|
||||
|
||||
}
|
||||
|
||||
export default MainLayout;
|
||||
@@ -46,7 +40,7 @@ export default MainLayout;
|
||||
export const MenuDropDownWithButton = ({children}:{children:any}) => <Menu withArrow>
|
||||
<Menu.Target>
|
||||
<ActionIcon variant='transparent'>
|
||||
<RiMenu5Fill size={24} />
|
||||
<RiMenu5Fill size={24} color='#FFF'/>
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Group, Space, TextInput, Notification, Modal, Switch, SegmentedControl } from '@mantine/core';
|
||||
import { Button, Group, Space, TextInput, Notification, Modal, Switch, SegmentedControl, Box } from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useState } from 'react';
|
||||
import { okNotify, regex_ipv4, regex_ipv6 } from '../../js/utils';
|
||||
@@ -71,12 +71,12 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
||||
<PortAndInterface form={form} int_name="ip_int" port_name="port" label={"Public IP Interface and port (ipv4/ipv6 + CIDR allowed)"} />
|
||||
<Space h="md" />
|
||||
|
||||
<div className='center-flex'>
|
||||
<Box className='center-flex'>
|
||||
<Switch
|
||||
label="Auto-Start Service"
|
||||
{...form.getInputProps('autostart', { type: 'checkbox' })}
|
||||
/>
|
||||
<div className="flex-spacer"></div>
|
||||
<Box className="flex-spacer"></Box>
|
||||
<SegmentedControl
|
||||
data={[
|
||||
{ label: 'TCP', value: 'tcp' },
|
||||
@@ -84,9 +84,9 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
||||
]}
|
||||
{...form.getInputProps('proto')}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Add Service</Button>
|
||||
</Group>
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
|
||||
placeholder="Awesome Service Name!"
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Rename</Button>
|
||||
</Group>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
import { ActionIcon, Badge, Divider, Grid, Menu, Space, Title, Tooltip } from '@mantine/core';
|
||||
import { ActionIcon, Badge, Box, Divider, Grid, Menu, Space, Title, Tooltip } from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import { FaPlay, FaStop } from 'react-icons/fa';
|
||||
import { nfregex, Service, serviceQueryKey } from '../utils';
|
||||
import { MdOutlineArrowForwardIos } from "react-icons/md"
|
||||
import style from "./index.module.scss";
|
||||
import YesNoModal from '../../YesNoModal';
|
||||
import { errorNotify, isMediumScreen, okNotify, regex_ipv4 } from '../../../js/utils';
|
||||
import { BsTrashFill } from 'react-icons/bs';
|
||||
@@ -72,71 +71,73 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
|
||||
}
|
||||
|
||||
return <>
|
||||
<Grid className={style.row} justify="flex-end" style={{width:"100%"}}>
|
||||
<Grid.Col md={4} xs={12}>
|
||||
<Box className='firegex__nfregex__rowbox'>
|
||||
<Grid className="firegex__nfregex__row" justify="flex-end" style={{width:"100%"}}>
|
||||
<Grid.Col span={{ md:4, xs: 12 }}>
|
||||
|
||||
<div className={isMedium?"center-flex-row":"center-flex"}>
|
||||
<div className="center-flex">
|
||||
<Title className={style.name}>
|
||||
<Box className={"center-flex-row"}>
|
||||
<Title className="firegex__nfregex__name">
|
||||
{service.name}
|
||||
</Title>
|
||||
<Badge size="xl" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient">
|
||||
:{service.port}
|
||||
</Badge>
|
||||
</div>
|
||||
<Badge style={isMedium?{}:{marginLeft:"20px"}} color={status_color} radius="sm" size="lg" variant="filled">Status: <u>{service.status}</u></Badge>
|
||||
{isMedium?null:<Space w="xl" />}
|
||||
</div>
|
||||
{!isMedium?<Space h="xl" />:null}
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col className="center-flex" md={8} xs={12}>
|
||||
{!isMedium?<div className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
|
||||
<Box className="center-flex" style={{ gap: 6 }}>
|
||||
<Badge color={status_color} radius="md" size="lg" variant="filled">Status: <u>{service.status}</u></Badge>
|
||||
<Badge size="lg" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient" radius="md">
|
||||
:{service.port}
|
||||
</Badge>
|
||||
</Box>
|
||||
{isMedium?null:<Space w="xl" />}
|
||||
</Box>
|
||||
</Grid.Col>
|
||||
|
||||
<div className="center-flex-row">
|
||||
<Badge color="yellow" radius="sm" size="md" variant="filled">Connections Blocked: {service.n_packets}</Badge>
|
||||
<Space h="xs" />
|
||||
<Badge color="violet" radius="sm" size="md" variant="filled">Regex: {service.n_regex}</Badge>
|
||||
<Space h="xs" />
|
||||
<Badge color={service.ip_int.match(regex_ipv4)?"cyan":"pink"} radius="sm" size="md" variant="filled">{service.ip_int} on {service.proto}</Badge>
|
||||
</div>
|
||||
{isMedium?<div className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
|
||||
<div className="center-flex">
|
||||
<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%"}}/>
|
||||
<Grid.Col className={isMedium?"center-flex":"center-flex-row"} span={{ md:8, xs: 12 }}>
|
||||
<Box visibleFrom='md' className='flex-spacer' />
|
||||
<Space hiddenFrom='md' h="md" />
|
||||
<Space hiddenFrom='md' w="xl" />
|
||||
<Space hiddenFrom='md' w="md" />
|
||||
<Box className="center-flex-row">
|
||||
<Badge color="yellow" radius="sm" size="md" variant="filled">Connections Blocked: {service.n_packets}</Badge>
|
||||
<Space h="xs" />
|
||||
<Badge color="violet" radius="sm" size="md" variant="filled">Regex: {service.n_regex}</Badge>
|
||||
<Space h="xs" />
|
||||
<Badge color={service.ip_int.match(regex_ipv4)?"cyan":"pink"} radius="sm" size="md" variant="filled">{service.ip_int} on {service.proto}</Badge>
|
||||
</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>
|
||||
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Box>
|
||||
<YesNoModal
|
||||
title='Are you sure to delete this service?'
|
||||
description={`You are going to delete the service '${service.port}', causing the stopping of the firewall and deleting all the regex associated. This will cause the shutdown of your service! ⚠️`}
|
||||
|
||||
@@ -1,29 +1,19 @@
|
||||
import { Collapse, Divider, Group, MantineColor, Navbar, ScrollArea, Text, ThemeIcon, Title, UnstyledButton } from "@mantine/core";
|
||||
import { Collapse, Divider, Group, MantineColor, ScrollArea, Text, ThemeIcon, Title, UnstyledButton, Box, AppShell } from "@mantine/core";
|
||||
import { useState } from "react";
|
||||
import { IoMdGitNetwork } from "react-icons/io";
|
||||
import { MdOutlineExpandLess, MdOutlineExpandMore, MdTransform } from "react-icons/md";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { getmainpath } from "../../js/utils";
|
||||
import { GrDirections } from "react-icons/gr";
|
||||
import { PiWallLight } from "react-icons/pi";
|
||||
import { useNavbarStore } from "../../js/store";
|
||||
import { getMainPath } from "../../js/utils";
|
||||
|
||||
function NavBarButton({ navigate, closeNav, name, icon, color, disabled, onClick }:
|
||||
{ navigate?: string, closeNav: () => void, name:string, icon:any, color:MantineColor, disabled?:boolean, onClick?:CallableFunction }) {
|
||||
const navigator = useNavigate()
|
||||
|
||||
return <UnstyledButton sx={(theme) => ({
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
padding: theme.spacing.xs,
|
||||
borderRadius: theme.radius.sm,
|
||||
opacity: disabled ? 0.4 : 1,
|
||||
color: theme.colorScheme === 'dark' ? theme.colors.dark[0] : theme.black,
|
||||
backgroundColor:(navigate===getmainpath()?(theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0]):"transparent"),
|
||||
'&:hover': {
|
||||
backgroundColor:
|
||||
theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
|
||||
},
|
||||
})} onClick={()=>{
|
||||
return <UnstyledButton
|
||||
className={`firegex__navbar__unstyled_button${navigate==getMainPath()?" selected":""}${disabled?" disabled":""}`}
|
||||
onClick={()=>{
|
||||
if(navigate){navigator(`/${navigate}`);closeNav()}
|
||||
if (onClick) onClick()
|
||||
}} disabled={disabled}>
|
||||
@@ -36,17 +26,16 @@ function NavBarButton({ navigate, closeNav, name, icon, color, disabled, onClick
|
||||
</UnstyledButton>
|
||||
}
|
||||
|
||||
export default function NavBar({ closeNav, opened }: {closeNav: () => void, opened: boolean}) {
|
||||
export default function NavBar() {
|
||||
const [toggle, setToggleState] = useState(false);
|
||||
const { navOpened, closeNav } = useNavbarStore()
|
||||
|
||||
|
||||
return <Navbar p="md" hiddenBreakpoint="md" hidden={!opened} width={{ md: 300 }}>
|
||||
<Navbar.Section px="xs" mt="xs">
|
||||
<Title order={3}>[Fi]*regex 🔥</Title>
|
||||
</Navbar.Section>
|
||||
return <AppShell.Navbar p="md" hidden={!navOpened}>
|
||||
<Box px="xs" mt="xs">
|
||||
<Title order={4}>Options ⚙️</Title>
|
||||
</Box>
|
||||
<Divider my="xs" />
|
||||
|
||||
<Navbar.Section grow component={ScrollArea} px="xs" mt="xs">
|
||||
<Box style={{flexGrow: 1}} component={ScrollArea} px="xs" mt="xs">
|
||||
<NavBarButton navigate="nfregex" closeNav={closeNav} name="Netfilter Regex" color="lime" icon={<IoMdGitNetwork />} />
|
||||
<NavBarButton navigate="firewall" closeNav={closeNav} name="Firewall Rules" color="red" icon={<PiWallLight />} />
|
||||
<NavBarButton navigate="porthijack" closeNav={closeNav} name="Hijack Port to Proxy" color="blue" icon={<GrDirections />} />
|
||||
@@ -55,7 +44,7 @@ export default function NavBar({ closeNav, opened }: {closeNav: () => void, open
|
||||
<Collapse in={toggle}>
|
||||
<NavBarButton navigate="regexproxy" closeNav={closeNav} name="TCP Proxy Regex Filter" color="grape" icon={<MdTransform />} />
|
||||
</Collapse>
|
||||
</Navbar.Section>
|
||||
</Box>
|
||||
|
||||
</Navbar>
|
||||
</AppShell.Navbar>
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { ActionIcon, ActionIconProps } from "@mantine/core"
|
||||
import { ActionIcon, ActionIconProps, PolymorphicComponentProps } from "@mantine/core"
|
||||
import { ImCheckmark, ImCross } from "react-icons/im"
|
||||
import { TiTick } from "react-icons/ti"
|
||||
import {PolymorphicComponentProps} from "@mantine/utils"
|
||||
|
||||
|
||||
|
||||
interface IOnOffButtonProps extends Omit<PolymorphicComponentProps<"button",ActionIconProps>, "value">{
|
||||
value: boolean,
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { AutocompleteItem, Select, Space, Title } from "@mantine/core"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { ipInterfacesQuery } from "../js/utils";
|
||||
import { Box, Space, Title } from "@mantine/core"
|
||||
import React from "react"
|
||||
import PortInput from "./PortInput";
|
||||
import { UseFormReturnType } from "@mantine/form/lib/types";
|
||||
import { InterfaceInput } from "./InterfaceInput";
|
||||
|
||||
interface ItemProps extends AutocompleteItem {
|
||||
type ItemProps = {
|
||||
value: string;
|
||||
netint: string;
|
||||
}
|
||||
} & any
|
||||
|
||||
const AutoCompleteItem = React.forwardRef<HTMLDivElement, ItemProps>(
|
||||
({ netint, value, ...props }: ItemProps, ref) => <div ref={ref} {...props}>
|
||||
({ netint, value, ...props }, ref) => <Box ref={ref} {...props}>
|
||||
( <b>{netint}</b> ) -{">"} <b>{value}</b>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
|
||||
|
||||
export default function PortAndInterface({ form, int_name, port_name, label, orientation }:{ form:UseFormReturnType<any>, int_name:string, port_name:string, label?:string, orientation?:"line"|"column" }) {
|
||||
|
||||
|
||||
@@ -24,7 +23,7 @@ export default function PortAndInterface({ form, int_name, port_name, label, ori
|
||||
{label?<>
|
||||
<Title order={6}>{label}</Title>
|
||||
<Space h="xs" /></> :null}
|
||||
<div className={(!orientation || orientation == "line")?'center-flex':"center-flex-row"} style={{width:"100%"}}>
|
||||
<Box className={(!orientation || orientation == "line")?'center-flex':"center-flex-row"} style={{width:"100%"}}>
|
||||
<InterfaceInput
|
||||
{...form.getInputProps(int_name)}
|
||||
/>
|
||||
@@ -32,6 +31,6 @@ export default function PortAndInterface({ form, int_name, port_name, label, ori
|
||||
<><Space w="sm" /><span style={{marginTop:"-3px", fontSize:"1.5em"}}>:</span><Space w="sm" /></>:
|
||||
<Space h="md" />}
|
||||
<PortInput {...form.getInputProps(port_name)} />
|
||||
</div>
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Group, Space, TextInput, Notification, Modal, Switch, SegmentedControl } from '@mantine/core';
|
||||
import { Button, Group, Space, TextInput, Notification, Modal, Switch, SegmentedControl, Box } from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useState } from 'react';
|
||||
import { okNotify, regex_ipv6_no_cidr, regex_ipv4_no_cidr } from '../../js/utils';
|
||||
@@ -79,12 +79,12 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
||||
<PortAndInterface form={form} int_name="ip_dst" port_name="proxy_port" label="Proxy/Internal IP Address and port (ipv4/ipv6)" />
|
||||
<Space h="md" />
|
||||
|
||||
<div className='center-flex'>
|
||||
<Box className='center-flex'>
|
||||
<Switch
|
||||
label="Auto-Start Service"
|
||||
{...form.getInputProps('autostart', { type: 'checkbox' })}
|
||||
/>
|
||||
<div className="flex-spacer"></div>
|
||||
<Box className="flex-spacer" />
|
||||
<SegmentedControl
|
||||
data={[
|
||||
{ label: 'TCP', value: 'tcp' },
|
||||
@@ -92,9 +92,9 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
||||
]}
|
||||
{...form.getInputProps('proto')}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Add Service</Button>
|
||||
</Group>
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ function ChangeDestination({ opened, onClose, service }:{ opened:boolean, onClos
|
||||
<form onSubmit={form.onSubmit(submitRequest)}>
|
||||
|
||||
<PortAndInterface form={form} int_name="ip_dst" port_name="proxy_port" />
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Change</Button>
|
||||
</Group>
|
||||
<Space h="md" />
|
||||
|
||||
@@ -49,7 +49,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
|
||||
placeholder="Awesome Service Name!"
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Rename</Button>
|
||||
</Group>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import { ActionIcon, Badge, Divider, Menu, Space, Title, Tooltip } from '@mantine/core';
|
||||
import { ActionIcon, Badge, Box, Divider, Grid, Menu, Space, Title, Tooltip } from '@mantine/core';
|
||||
import React, { useState } from 'react';
|
||||
import { FaPlay, FaStop } from 'react-icons/fa';
|
||||
import { porthijack, Service } from '../utils';
|
||||
import style from "./index.module.scss";
|
||||
import YesNoModal from '../../YesNoModal';
|
||||
import { errorNotify, isMediumScreen, okNotify } from '../../../js/utils';
|
||||
import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs';
|
||||
@@ -90,86 +89,91 @@ function ServiceRow({ service }:{ service:Service }) {
|
||||
}
|
||||
|
||||
return <>
|
||||
<div className={isMedium?style.row:style.row_mobile} style={{width:"100%"}}>
|
||||
<Space w="xl" /><Space w="xl" />
|
||||
<div>
|
||||
<div className="center-flex-row">
|
||||
<Title order={4} className={style.name}>{service.name}</Title>
|
||||
<div className="center-flex">
|
||||
<Badge color={status_color} radius="sm" size="md" variant="filled">Status: <u>{service.active?"ENABLED":"DISABLED"}</u></Badge>
|
||||
<Space w="sm" />
|
||||
<Badge color={service.proto === "tcp"?"cyan":"orange"} radius="sm" size="md" variant="filled">
|
||||
{service.proto}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex-spacer' />
|
||||
{isMedium?null:<Space h="xl" />}
|
||||
<div className='center-flex'>
|
||||
<div className="center-flex-row">
|
||||
<Badge color="lime" radius="sm" size="md" variant="filled">
|
||||
FROM {service.ip_src} : {service.public_port}
|
||||
</Badge>
|
||||
<Space h="sm" />
|
||||
<Badge color="blue" radius="sm" size="md" variant="filled">
|
||||
<div className="center-flex">
|
||||
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" />
|
||||
<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>
|
||||
<Menu.Label><b>Change destination</b></Menu.Label>
|
||||
<Menu.Item icon={<BsArrowRepeat size={18} />} onClick={()=>setChangeDestModal(true)}>Change hijacking destination</Menu.Item>
|
||||
<Divider />
|
||||
<Menu.Label><b>Danger zone</b></Menu.Label>
|
||||
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
{isMedium?null:<Space h="xl" />}
|
||||
<Space w="xl" /><Space w="xl" />
|
||||
<Box className='firegex__nfregex__rowbox'>
|
||||
<Grid className="firegex__nfregex__row" justify="flex-end" style={{width:"100%"}}>
|
||||
<Grid.Col span={{ md:4, xs: 12 }}>
|
||||
<Box className={"center-flex-row"}>
|
||||
<Title className="firegex__nfregex__name">
|
||||
{service.name}
|
||||
</Title>
|
||||
<Box className="center-flex" style={{ gap: 6 }}>
|
||||
<Badge color={status_color} radius="md" size="lg" variant="filled">Status: <u>{service.active?"ENABLED":"DISABLED"}</u></Badge>
|
||||
<Badge color={service.proto === "tcp"?"cyan":"orange"} radius="md" size="lg" variant="filled">
|
||||
{service.proto}
|
||||
</Badge>
|
||||
</Box>
|
||||
{isMedium?null:<Space w="xl" />}
|
||||
</Box>
|
||||
</Grid.Col>
|
||||
|
||||
</div>
|
||||
<Divider size="sm" style={{width:"100%"}}/>
|
||||
<Grid.Col className={isMedium?"center-flex":"center-flex-row"} span={{ md:8, xs: 12 }}>
|
||||
<Box visibleFrom='md' className='flex-spacer' />
|
||||
<Space hiddenFrom='md' h="md" />
|
||||
<Space hiddenFrom='md' w="xl" />
|
||||
<Space hiddenFrom='md' w="md" />
|
||||
|
||||
<Box className="center-flex-row">
|
||||
<Badge color="lime" radius="sm" size="md" variant="filled">
|
||||
FROM {service.ip_src} : {service.public_port}
|
||||
</Badge>
|
||||
<Space h="sm" />
|
||||
<Badge color="blue" radius="sm" size="md" variant="filled">
|
||||
<Box 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="firegex__porthijack__servicerow__portInput"
|
||||
onBlur={(e)=>{onChangeProxyPort({proxy_port:parseInt(e.target.value)})}}
|
||||
ref={portInputRef}
|
||||
{...form.getInputProps("proxy_port")}
|
||||
/>
|
||||
</form>
|
||||
</Box>
|
||||
</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>
|
||||
<Menu.Label><b>Change destination</b></Menu.Label>
|
||||
<Menu.Item leftSection={<BsArrowRepeat size={18} />} onClick={()=>setChangeDestModal(true)}>Change hijacking destination</Menu.Item>
|
||||
<Divider />
|
||||
<Menu.Label><b>Danger zone</b></Menu.Label>
|
||||
<Menu.Item color="red" 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>
|
||||
|
||||
|
||||
<YesNoModal
|
||||
title='Are you sure to delete this service?'
|
||||
|
||||
@@ -98,7 +98,7 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
||||
{...form.getInputProps('chosenInternalPort', { type: 'checkbox' })}
|
||||
/>
|
||||
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Add Service</Button>
|
||||
</Group>
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ function ChangePortModal({ service, opened, onClose }:{ service:Service, opened:
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button loading={submitLoading} disabled={
|
||||
service.internal_port === form.values.internalPort && service.public_port === form.values.port
|
||||
} type="submit">Change Port</Button>
|
||||
|
||||
@@ -49,7 +49,7 @@ function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>v
|
||||
placeholder="Awesome Service Name!"
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Rename</Button>
|
||||
</Group>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
import { ActionIcon, Badge, Divider, Grid, Menu, Space, Title, Tooltip } from '@mantine/core';
|
||||
import { ActionIcon, Badge, Box, Divider, Grid, Menu, Space, Title, Tooltip } from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import { FaPause, FaPlay, FaStop } from 'react-icons/fa';
|
||||
import { MdOutlineArrowForwardIos } from "react-icons/md"
|
||||
import style from "./ServiceRow.module.scss";
|
||||
import YesNoModal from '../../YesNoModal';
|
||||
import { errorNotify, isMediumScreen, okNotify } from '../../../js/utils';
|
||||
import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs';
|
||||
@@ -101,34 +100,34 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
|
||||
}
|
||||
|
||||
return <>
|
||||
<Grid className={style.row} justify="flex-end" style={{width:"100%"}}>
|
||||
<Grid.Col md={4} xs={12}>
|
||||
<div className={isMedium?"center-flex-row":"center-flex"}>
|
||||
<div className="center-flex"><Title className={style.name}>{service.name}</Title> <Badge size="xl" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient">:{service.public_port}</Badge></div>
|
||||
<Grid className="firegex__servicerow__row" justify="flex-end" style={{width:"100%"}}>
|
||||
<Grid.Col span={{ md:4, xs: 12 }}>
|
||||
<Box className={isMedium?"center-flex-row":"center-flex"}>
|
||||
<Box className="center-flex"><Title className="firegex__servicerow__name">{service.name}</Title> <Badge size="xl" gradient={{ from: 'indigo', to: 'cyan' }} variant="gradient">:{service.public_port}</Badge></Box>
|
||||
<Badge color={status_color} size="lg" radius="md">{service.internal_port} {"->"} {service.public_port}</Badge>
|
||||
</div>
|
||||
</Box>
|
||||
{!isMedium?<Space h="xl" />:null}
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col className="center-flex" md={8} xs={12}>
|
||||
{!isMedium?<div className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
|
||||
<div className="center-flex-row">
|
||||
<Grid.Col className="center-flex" span={{ md:8, xs: 12 }}>
|
||||
{!isMedium?<Box className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
|
||||
<Box className="center-flex-row">
|
||||
<Badge style={{marginBottom:"20px"}} color={status_color} radius="sm" size="lg" variant="filled">Status: <u>{service.status}</u></Badge>
|
||||
<Badge style={{marginBottom:"8px"}}color="violet" radius="sm" size="md" variant="filled">Regex: {service.n_regex}</Badge>
|
||||
<Badge color="yellow" radius="sm" size="md" variant="filled">Connections Blocked: {service.n_packets}</Badge>
|
||||
</div>
|
||||
{isMedium?<div className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
|
||||
<div className="center-flex">
|
||||
</Box>
|
||||
{isMedium?<Box className='flex-spacer' />:<><Space w="xl" /><Space w="xl" /></>}
|
||||
<Box className="center-flex">
|
||||
<MenuDropDownWithButton>
|
||||
<Menu.Label><b>Rename service</b></Menu.Label>
|
||||
<Menu.Item icon={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
|
||||
<Menu.Item leftSection={<BiRename size={18} />} onClick={()=>setRenameModal(true)}>Change service name</Menu.Item>
|
||||
<Divider />
|
||||
<Menu.Label><b>Change ports</b></Menu.Label>
|
||||
<Menu.Item icon={<TbNumbers size={18} />} onClick={()=>setChoosePortModal(true)}>Change port</Menu.Item>
|
||||
<Menu.Item icon={<BsArrowRepeat size={18} />} onClick={()=>setChangePortModal(true)}>Regen proxy port</Menu.Item>
|
||||
<Menu.Item leftSection={<TbNumbers size={18} />} onClick={()=>setChoosePortModal(true)}>Change port</Menu.Item>
|
||||
<Menu.Item leftSection={<BsArrowRepeat size={18} />} onClick={()=>setChangePortModal(true)}>Regen proxy port</Menu.Item>
|
||||
<Divider />
|
||||
<Menu.Label><b>Danger zone</b></Menu.Label>
|
||||
<Menu.Item color="red" icon={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
||||
<Menu.Item color="red" leftSection={<BsTrashFill size={18} />} onClick={()=>setDeleteModal(true)}>Delete Service</Menu.Item>
|
||||
</MenuDropDownWithButton>
|
||||
<Space w="md"/>
|
||||
{["pause","wait"].includes(service.status)?
|
||||
@@ -158,12 +157,12 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
|
||||
<FaPlay size="20px" />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Box>
|
||||
<Space w="xl" /><Space w="xl" />
|
||||
{onClick?<div>
|
||||
{onClick?<Box>
|
||||
<MdOutlineArrowForwardIos onClick={onClick} style={{cursor:"pointer"}} size={45} />
|
||||
<Space w="xl" />
|
||||
</div>:null}
|
||||
</Box>:null}
|
||||
{!isMedium?<><Space w="xl" /><Space w="xl" /></>:null}
|
||||
|
||||
</Grid.Col>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip, Box } from '@mant
|
||||
import { useState } from 'react';
|
||||
import { RegexFilter } from '../../js/models';
|
||||
import { b64decode, errorNotify, getapiobject, okNotify } from '../../js/utils';
|
||||
import style from "./index.module.scss";
|
||||
import { BsTrashFill } from "react-icons/bs"
|
||||
import YesNoModal from '../YesNoModal';
|
||||
import FilterTypeSelector from '../FilterTypeSelector';
|
||||
@@ -43,14 +42,14 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
||||
}).catch( err => errorNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivation":"activation"} failed!`,`Error: ${err}`))
|
||||
}
|
||||
|
||||
return <div className={style.box}>
|
||||
return <Box className="firegex__regexview__box">
|
||||
<Grid>
|
||||
<Grid.Col span={2} className="center-flex">
|
||||
<Title order={4}>Regex:</Title>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Box className={style.outer_regex_text}>
|
||||
<Text className={style.regex_text} onClick={()=>{
|
||||
<Box className="firegex__regexview__outer_regex_text">
|
||||
<Text className="firegex__regexview__regex_text" onClick={()=>{
|
||||
clipboard.copy(regex_expr)
|
||||
okNotify("Regex copied to clipboard!",`The regex '${regex_expr}' has been copied to the clipboard!`)
|
||||
}}>{regex_expr}</Text>
|
||||
@@ -74,7 +73,7 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
||||
|
||||
</Grid.Col>
|
||||
<Grid.Col className='center-flex' span={12}>
|
||||
<div className='center-flex-row'>
|
||||
<Box className='center-flex-row'>
|
||||
<FilterTypeSelector
|
||||
size="md"
|
||||
color="gray"
|
||||
@@ -82,23 +81,23 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
||||
value={regexInfo.is_blacklist?"blacklist":"whitelist"}
|
||||
/>
|
||||
<Space h="md" />
|
||||
<div className='center-flex'>
|
||||
<Box className='center-flex'>
|
||||
<Badge size="md" color="cyan" variant="filled">Service: {regexInfo.service_id}</Badge>
|
||||
<Space w="xs" />
|
||||
<Badge size="md" color={regexInfo.active?"lime":"red"} variant="filled">{regexInfo.active?"ACTIVE":"DISABLED"}</Badge>
|
||||
<Space w="xs" />
|
||||
<Badge size="md" color="gray" variant="filled">ID: {regexInfo.id}</Badge>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex-spacer' />
|
||||
<div className='center-flex-row'>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box className='flex-spacer' />
|
||||
<Box className='center-flex-row'>
|
||||
<Badge size="md" color={regexInfo.is_case_sensitive?"grape":"pink"} variant="filled">Case: {regexInfo.is_case_sensitive?"SENSIIVE":"INSENSITIVE"}</Badge>
|
||||
<Space h="xs" />
|
||||
<Badge size="md" color="yellow" variant="filled">Packets filtered: {regexInfo.n_packets}</Badge>
|
||||
<Space h="xs" />
|
||||
<Badge size="md" color="blue" variant="filled">Mode: {mode_string}</Badge>
|
||||
</div>
|
||||
</Box>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<YesNoModal
|
||||
@@ -109,7 +108,7 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
||||
opened={deleteModal}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</Box>
|
||||
}
|
||||
|
||||
export default RegexView;
|
||||
|
||||
@@ -5,7 +5,7 @@ function YesNoModal( { title, description, action, onClose, opened}:{ title:stri
|
||||
|
||||
return <Modal size="xl" title={title} opened={opened} onClose={onClose} centered>
|
||||
{description}
|
||||
<Group position="right" mt="md">
|
||||
<Group align="right" mt="md">
|
||||
<Button onClick={()=>{
|
||||
onClose()
|
||||
action()
|
||||
|
||||
Reference in New Issue
Block a user