Moduled Firegex, Merging pt1 (not finished and not working yet)
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
@use "../../index.scss" as *;
|
||||
|
||||
.footer{
|
||||
height: 150px;
|
||||
margin-top: 50px;
|
||||
background-color: $primary_color;
|
||||
@extend .center-flex;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Footer } from '@mantine/core';
|
||||
import React from 'react';
|
||||
|
||||
import style from "./index.module.scss";
|
||||
|
||||
|
||||
function Footer() {
|
||||
return <div id="footer" className={style.footer}>
|
||||
function FooterPage() {
|
||||
return <Footer id="footer" height={70} className={style.footer}>
|
||||
Made by Pwnzer0tt1
|
||||
</div>
|
||||
</Footer>
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
export default FooterPage;
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
|
||||
@use "../../vars" as *;
|
||||
@use "../../index.scss" as *;
|
||||
|
||||
.header{
|
||||
width: 100%;
|
||||
height: 140px;
|
||||
background-color: $primary_color;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo{
|
||||
width: 200px;
|
||||
margin-left: 40px;
|
||||
height: 70%;
|
||||
.divlogo{
|
||||
width: 110px;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
@extend .center-flex;
|
||||
}
|
||||
|
||||
.navbtn{
|
||||
@extend .center-flex;
|
||||
width: 30px;
|
||||
margin:0;
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ActionIcon, Divider, Image, Menu, Tooltip, FloatingTooltip } from '@mantine/core';
|
||||
import { ActionIcon, Divider, Image, Menu, Tooltip, FloatingTooltip, MediaQuery, Burger, Space, Header } from '@mantine/core';
|
||||
import style from "./index.module.scss";
|
||||
import { errorNotify, logout } from '../../js/utils';
|
||||
import { BsPlusLg } from "react-icons/bs"
|
||||
import { errorNotify, gatmainpath, logout } from '../../js/utils';
|
||||
import { AiFillHome } from "react-icons/ai"
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import AddNewRegex from '../NFRegex/AddNewRegex';
|
||||
import AddNewService from '../NFRegex/AddNewService';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { FaLock } from 'react-icons/fa';
|
||||
import { MdOutlineSettingsBackupRestore } from 'react-icons/md';
|
||||
import { ImExit } from 'react-icons/im';
|
||||
@@ -14,7 +11,7 @@ import ResetPasswordModal from './ResetPasswordModal';
|
||||
import ResetModal from './ResetModal';
|
||||
|
||||
|
||||
function Header() {
|
||||
function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:React.Dispatch<React.SetStateAction<boolean>>}) {
|
||||
|
||||
const navigator = useNavigate()
|
||||
|
||||
@@ -26,19 +23,27 @@ function Header() {
|
||||
})
|
||||
}
|
||||
|
||||
const go_to_home = () => {
|
||||
navigator(`/${gatmainpath()}`)
|
||||
}
|
||||
|
||||
const [changePasswordModal, setChangePasswordModal] = useState(false);
|
||||
const [resetFiregexModal, setResetFiregexModal] = useState(false);
|
||||
const [tooltipAddOpened, setTooltipAddOpened] = useState(false);
|
||||
const [tooltipHomeOpened, setTooltipHomeOpened] = useState(false);
|
||||
const [tooltipLogoutOpened,setTooltipLogoutOpened] = useState(false);
|
||||
|
||||
const {srv} = useParams()
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const closeModal = () => {setOpen(false);}
|
||||
|
||||
return <div id="header-page" className={style.header}>
|
||||
|
||||
<div style={{ width: 240, marginLeft: 'auto', marginRight: 'auto', padding:"40px", cursor: 'pointer' }}>
|
||||
return <Header height={100} className={style.header} {...other}>
|
||||
<Space w="lg" />
|
||||
<MediaQuery largerThan="md" styles={{ display: 'none' }}><div>
|
||||
<Burger
|
||||
opened={navOpen}
|
||||
className={style.navbtn}
|
||||
onClick={() => setNav((o) => !o)}
|
||||
size="sm"
|
||||
mr="xl"
|
||||
/>
|
||||
</div></MediaQuery>
|
||||
<div className={style.divlogo}>
|
||||
<FloatingTooltip zIndex={0} label="Home" transition="pop" transitionDuration={200} openDelay={1000} transitionTimingFunction="ease" color="dark" position="right" >
|
||||
<Image src="/header-logo.png" alt="Firegex logo" onClick={()=>navigator("/")}/>
|
||||
</FloatingTooltip>
|
||||
@@ -46,50 +51,34 @@ function Header() {
|
||||
|
||||
<div className="flex-spacer" />
|
||||
|
||||
<div style={{marginLeft:"20px"}}></div>
|
||||
|
||||
<Menu>
|
||||
<Menu.Label>Firewall Access</Menu.Label>
|
||||
<Menu.Item icon={<ImExit size={14} />} onClick={logout_action}>Logout</Menu.Item>
|
||||
<Menu.Item color="red" icon={<FaLock size={14} />} onClick={() => setChangePasswordModal(true)}>Change Password</Menu.Item>
|
||||
<Menu.Item icon={<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>
|
||||
<div style={{marginLeft:"20px"}}></div>
|
||||
<Tooltip zIndex={0} label="Home" position='bottom' transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="teal" opened={tooltipHomeOpened} tooltipId="tooltip-home-id">
|
||||
<Space w="md" />
|
||||
<Tooltip label="Home" position='bottom' transition="pop" transitionDuration={200} transitionTimingFunction="ease" color="teal" opened={tooltipHomeOpened} tooltipId="tooltip-home-id">
|
||||
<ActionIcon color="teal" style={{marginRight:"10px"}}
|
||||
size="xl" radius="md" variant="filled"
|
||||
onClick={()=>navigator("/")}
|
||||
aria-describedby="tooltip-home-id"
|
||||
onClick={go_to_home}
|
||||
onFocus={() => setTooltipHomeOpened(false)} onBlur={() => setTooltipHomeOpened(false)}
|
||||
onMouseEnter={() => setTooltipHomeOpened(true)} onMouseLeave={() => setTooltipHomeOpened(false)}>
|
||||
<AiFillHome size="25px" />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
{ srv?
|
||||
<Tooltip label="Add a new regex" zIndex={0} position='bottom' transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="blue" opened={tooltipAddOpened} tooltipId="tooltip-add-id">
|
||||
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
|
||||
aria-describedby="tooltip-add-id"
|
||||
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
|
||||
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
|
||||
</Tooltip>
|
||||
: <Tooltip label="Add a new service" zIndex={0} position='bottom' transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="blue" opened={tooltipAddOpened} tooltipId="tooltip-add-id">
|
||||
<ActionIcon color="blue" onClick={()=>setOpen(true)} size="xl" radius="md" variant="filled"
|
||||
aria-describedby="tooltip-add-id"
|
||||
onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)}
|
||||
onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}><BsPlusLg size="20px" /></ActionIcon>
|
||||
</Tooltip>
|
||||
}
|
||||
|
||||
{srv?
|
||||
<AddNewRegex opened={open} onClose={closeModal} service={srv} />:
|
||||
<AddNewService opened={open} onClose={closeModal} />
|
||||
}
|
||||
<Tooltip label="Logout" position='bottom' transition="pop" transitionDuration={200} transitionTimingFunction="ease" color="blue" opened={tooltipLogoutOpened} tooltipId="tooltip-add-id">
|
||||
<ActionIcon color="blue" onClick={logout_action} size="xl" radius="md" variant="filled"
|
||||
onFocus={() => setTooltipLogoutOpened(false)} onBlur={() => setTooltipLogoutOpened(false)}
|
||||
onMouseEnter={() => setTooltipLogoutOpened(true)} onMouseLeave={() => setTooltipLogoutOpened(false)}><ImExit size={23} style={{marginTop:"3px", marginLeft:"2px"}}/></ActionIcon>
|
||||
</Tooltip>
|
||||
<ResetPasswordModal opened={changePasswordModal} onClose={() => setChangePasswordModal(false)} />
|
||||
<ResetModal opened={resetFiregexModal} onClose={() => setResetFiregexModal(false)} />
|
||||
<div style={{marginLeft:"40px"}}></div>
|
||||
</div>
|
||||
<Space w="xl" />
|
||||
</Header>
|
||||
}
|
||||
|
||||
export default Header;
|
||||
export default HeaderPage;
|
||||
|
||||
@@ -1,23 +1,33 @@
|
||||
import React from 'react';
|
||||
import { Container, Space, Tabs } from '@mantine/core';
|
||||
import Footer from './Footer';
|
||||
import Header from './Header';
|
||||
import React, { useState } from 'react';
|
||||
import { Container, Space } from '@mantine/core';
|
||||
import { AppShell } from '@mantine/core';
|
||||
import NavBar from './NavBar';
|
||||
import FooterPage from './Footer';
|
||||
import HeaderPage from './Header';
|
||||
|
||||
|
||||
|
||||
|
||||
function MainLayout({ children }:{ children:any }) {
|
||||
const [opened, setOpened] = useState(false);
|
||||
return <>
|
||||
<Header/>
|
||||
<Tabs grow variant="pills">
|
||||
<Tabs.Tab label="Regex Prox"></Tabs.Tab>
|
||||
<Tabs.Tab label="Port Hijacking"></Tabs.Tab>
|
||||
<Tabs.Tab label="Netfilter regex"></Tabs.Tab>
|
||||
</Tabs>
|
||||
<Space h="xl" />
|
||||
<Container size="lg" style={{minHeight:"57.5vh"}}>
|
||||
{children}
|
||||
</Container>
|
||||
<Space h="xl" />
|
||||
<Footer />
|
||||
</>
|
||||
|
||||
<AppShell
|
||||
padding="md"
|
||||
fixed
|
||||
navbar={<NavBar closeNav={()=>setOpened(false)} opened={opened} />}
|
||||
header={<HeaderPage navOpen={opened} setNav={setOpened} />}
|
||||
footer={<FooterPage />}
|
||||
>
|
||||
<Container size="lg">
|
||||
{children}
|
||||
</Container>
|
||||
<Space h="lg" />
|
||||
|
||||
</AppShell>
|
||||
|
||||
</>
|
||||
|
||||
}
|
||||
|
||||
export default MainLayout;
|
||||
|
||||
101
frontend/src/components/NFRegex/utils.ts
Executable file
101
frontend/src/components/NFRegex/utils.ts
Executable file
@@ -0,0 +1,101 @@
|
||||
import { ServerResponse } from "../../js/models"
|
||||
import { getapi, postapi } from "../../js/utils"
|
||||
|
||||
export type GeneralStats = {
|
||||
services:number,
|
||||
closed:number,
|
||||
regexes:number
|
||||
}
|
||||
|
||||
export type Service = {
|
||||
name:string,
|
||||
service_id:string,
|
||||
status:string,
|
||||
port:number,
|
||||
proto: string,
|
||||
ip_int: string,
|
||||
n_packets:number,
|
||||
n_regex:number,
|
||||
}
|
||||
|
||||
export type ServiceAddForm = {
|
||||
name:string,
|
||||
port:number,
|
||||
proto:string,
|
||||
ip_int:string,
|
||||
}
|
||||
|
||||
export type ServiceAddResponse = {
|
||||
status: string,
|
||||
service_id?: string,
|
||||
}
|
||||
|
||||
export type RegexFilter = {
|
||||
id:number,
|
||||
service_id:string,
|
||||
regex:string
|
||||
is_blacklist:boolean,
|
||||
is_case_sensitive:boolean,
|
||||
mode:string //C S B => C->S S->C BOTH
|
||||
n_packets:number,
|
||||
active:boolean
|
||||
}
|
||||
|
||||
export type RegexAddForm = {
|
||||
service_id:string,
|
||||
regex:string,
|
||||
is_case_sensitive:boolean,
|
||||
is_blacklist:boolean,
|
||||
mode:string, // C->S S->C BOTH,
|
||||
active: boolean
|
||||
}
|
||||
|
||||
export const nfregex = {
|
||||
stats: async () => {
|
||||
return await getapi("nfregex/stats") as GeneralStats;
|
||||
},
|
||||
services: async () => {
|
||||
return await getapi("nfregex/services") as Service[];
|
||||
},
|
||||
serviceinfo: async (service_id:string) => {
|
||||
return await getapi(`nfregex/service/${service_id}`) as Service;
|
||||
},
|
||||
regexdelete: async (regex_id:number) => {
|
||||
const { status } = await getapi(`nfregex/regex/${regex_id}/delete`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
regexenable: async (regex_id:number) => {
|
||||
const { status } = await getapi(`nfregex/regex/${regex_id}/enable`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
regexdisable: async (regex_id:number) => {
|
||||
const { status } = await getapi(`nfregex/regex/${regex_id}/disable`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
servicestart: async (service_id:string) => {
|
||||
const { status } = await getapi(`nfregex/service/${service_id}/start`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
servicerename: async (service_id:string, name: string) => {
|
||||
const { status } = await postapi(`nfregex/service/${service_id}/rename`,{ name }) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
servicestop: async (service_id:string) => {
|
||||
const { status } = await getapi(`nfregex/service/${service_id}/stop`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
servicesadd: async (data:ServiceAddForm) => {
|
||||
return await postapi("nfregex/services/add",data) as ServiceAddResponse;
|
||||
},
|
||||
servicedelete: async (service_id:string) => {
|
||||
const { status } = await getapi(`nfregex/service/${service_id}/delete`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
regexesadd: async (data:RegexAddForm) => {
|
||||
const { status } = await postapi("nfregex/regexes/add",data) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
serviceregexes: async (service_id:string) => {
|
||||
return await getapi(`nfregex/service/${service_id}/regexes`) as RegexFilter[];
|
||||
}
|
||||
}
|
||||
49
frontend/src/components/NavBar/index.tsx
Normal file
49
frontend/src/components/NavBar/index.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Group, MantineColor, Navbar, ScrollArea, Text, ThemeIcon, Title, UnstyledButton } from "@mantine/core";
|
||||
import React from "react";
|
||||
import { IoMdGitNetwork } from "react-icons/io";
|
||||
import { MdTransform } from "react-icons/md";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { gatmainpath } from "../../js/utils";
|
||||
import { GrDirections } from "react-icons/gr";
|
||||
|
||||
function NavBarButton({ navigate, closeNav, name, icon, color, disabled }:
|
||||
{ navigate: string, closeNav: () => void, name:string, icon:any, color:MantineColor, disabled?:boolean }) {
|
||||
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===gatmainpath()?(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={()=>{navigator(`/${navigate}`);closeNav()}} disabled={disabled}>
|
||||
<Group>
|
||||
<ThemeIcon color={color} variant="light">
|
||||
{icon}
|
||||
</ThemeIcon>
|
||||
<Text size="sm">{name}</Text>
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
}
|
||||
|
||||
export default function NavBar({ closeNav, opened }: {closeNav: () => void, opened: boolean}) {
|
||||
|
||||
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>
|
||||
<hr style={{width:"100%"}}/>
|
||||
<Navbar.Section grow component={ScrollArea} px="xs" mt="xs">
|
||||
<NavBarButton navigate="nfregex" closeNav={closeNav} name="Netfilter Regex" color="blue" icon={<IoMdGitNetwork />} />
|
||||
<NavBarButton navigate="regexproxy" closeNav={closeNav} name="TCP Proxy Regex Filter" color="lime" icon={<MdTransform />} />
|
||||
<NavBarButton navigate="porthijack" closeNav={closeNav} name="Hijack Port to Proxy" color="red" icon={<GrDirections />} disabled/>
|
||||
</Navbar.Section>
|
||||
|
||||
</Navbar>
|
||||
}
|
||||
124
frontend/src/components/RegexProxy/AddNewRegex.tsx
Executable file
124
frontend/src/components/RegexProxy/AddNewRegex.tsx
Executable file
@@ -0,0 +1,124 @@
|
||||
import { Button, Group, Space, TextInput, Notification, Switch, NativeSelect, Modal } from '@mantine/core';
|
||||
import { useForm } from '@mantine/hooks';
|
||||
import React, { useState } from 'react';
|
||||
import { RegexAddForm } from '../js/models';
|
||||
import { addregex, b64decode, b64encode, fireUpdateRequest, okNotify } from '../js/utils';
|
||||
import { ImCross } from "react-icons/im"
|
||||
import FilterTypeSelector from './FilterTypeSelector';
|
||||
|
||||
|
||||
type RegexAddInfo = {
|
||||
regex:string,
|
||||
type:string,
|
||||
mode:string,
|
||||
is_case_insensitive:boolean,
|
||||
deactive:boolean
|
||||
}
|
||||
|
||||
function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>void, service:string }) {
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
regex:"",
|
||||
type:"blacklist",
|
||||
mode:"C -> S",
|
||||
is_case_insensitive:false,
|
||||
deactive:false
|
||||
},
|
||||
validationRules:{
|
||||
regex: (value) => value !== "",
|
||||
type: (value) => ["blacklist","whitelist"].includes(value),
|
||||
mode: (value) => ['C -> S', 'S -> C', 'C <-> S'].includes(value)
|
||||
}
|
||||
})
|
||||
|
||||
const close = () =>{
|
||||
onClose()
|
||||
form.reset()
|
||||
setError(null)
|
||||
}
|
||||
|
||||
const [submitLoading, setSubmitLoading] = useState(false)
|
||||
const [error, setError] = useState<string|null>(null)
|
||||
|
||||
const submitRequest = (values:RegexAddInfo) => {
|
||||
setSubmitLoading(true)
|
||||
const filter_mode = ({'C -> S':'C', 'S -> C':'S', 'C <-> S':'B'}[values.mode])
|
||||
|
||||
const request:RegexAddForm = {
|
||||
is_blacklist:values.type !== "whitelist",
|
||||
is_case_sensitive: !values.is_case_insensitive,
|
||||
service_id: service,
|
||||
mode: filter_mode?filter_mode:"B",
|
||||
regex: b64encode(values.regex),
|
||||
active: !values.deactive
|
||||
}
|
||||
setSubmitLoading(false)
|
||||
addregex(request).then( res => {
|
||||
if (!res){
|
||||
setSubmitLoading(false)
|
||||
close();
|
||||
fireUpdateRequest();
|
||||
okNotify(`Regex ${b64decode(request.regex)} has been added`, `Successfully added ${request.is_case_sensitive?"case sensitive":"case insensitive"} ${request.is_blacklist?"blacklist":"whitelist"} regex to ${request.service_id} service`)
|
||||
}else if (res.toLowerCase() === "invalid regex"){
|
||||
setSubmitLoading(false)
|
||||
form.setFieldError("regex", "Invalid Regex")
|
||||
}else{
|
||||
setSubmitLoading(false)
|
||||
setError("Error: [ "+res+" ]")
|
||||
}
|
||||
}).catch( err => {
|
||||
setSubmitLoading(false)
|
||||
setError("Request Failed! [ "+err+" ]")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
return <Modal size="xl" title="Add a new regex filter" opened={opened} onClose={close} closeOnClickOutside={false} centered>
|
||||
<form onSubmit={form.onSubmit(submitRequest)}>
|
||||
<TextInput
|
||||
label="Regex"
|
||||
placeholder="[A-Z0-9]{31}="
|
||||
{...form.getInputProps('regex')}
|
||||
/>
|
||||
<Space h="md" />
|
||||
<Switch
|
||||
label="Case insensitive"
|
||||
{...form.getInputProps('is_case_insensitive', { type: 'checkbox' })}
|
||||
/>
|
||||
<Space h="md" />
|
||||
<Switch
|
||||
label="Deactivate"
|
||||
{...form.getInputProps('deactive', { type: 'checkbox' })}
|
||||
/>
|
||||
<Space h="md" />
|
||||
<NativeSelect
|
||||
data={['C -> S', 'S -> C', 'C <-> S']}
|
||||
label="Choose the source of the packets to filter"
|
||||
variant="filled"
|
||||
{...form.getInputProps('mode')}
|
||||
/>
|
||||
<Space h="md" />
|
||||
<FilterTypeSelector
|
||||
size="md"
|
||||
color="gray"
|
||||
{...form.getInputProps('type')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Add Filter</Button>
|
||||
</Group>
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
{error?<>
|
||||
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
|
||||
Error: {error}
|
||||
</Notification><Space h="md" /></>:null}
|
||||
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
}
|
||||
|
||||
export default AddNewRegex;
|
||||
120
frontend/src/components/RegexProxy/AddNewService.tsx
Executable file
120
frontend/src/components/RegexProxy/AddNewService.tsx
Executable file
@@ -0,0 +1,120 @@
|
||||
import { Button, Group, NumberInput, Space, TextInput, Notification, Modal, Switch } from '@mantine/core';
|
||||
import { useForm } from '@mantine/hooks';
|
||||
import React, { useState } from 'react';
|
||||
import { addservice, fireUpdateRequest, okNotify, startservice } from '../js/utils';
|
||||
import { ImCross } from "react-icons/im"
|
||||
|
||||
type ServiceAddForm = {
|
||||
name:string,
|
||||
port:number,
|
||||
autostart: boolean,
|
||||
chosenInternalPort: boolean,
|
||||
internalPort: number
|
||||
}
|
||||
|
||||
function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void }) {
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
name:"",
|
||||
port:8080,
|
||||
internalPort:30001,
|
||||
chosenInternalPort:false,
|
||||
autostart: true
|
||||
},
|
||||
validationRules:{
|
||||
name: (value) => value !== ""?true:false,
|
||||
port: (value) => value>0 && value<65536,
|
||||
internalPort: (value) => value>0 && value<65536,
|
||||
}
|
||||
})
|
||||
|
||||
const close = () =>{
|
||||
onClose()
|
||||
form.reset()
|
||||
setError(null)
|
||||
}
|
||||
|
||||
const [submitLoading, setSubmitLoading] = useState(false)
|
||||
const [error, setError] = useState<string|null>(null)
|
||||
|
||||
const submitRequest = ({ name, port, autostart, chosenInternalPort, internalPort }:ServiceAddForm) =>{
|
||||
setSubmitLoading(true)
|
||||
addservice(chosenInternalPort?{ internalPort, name, port }:{ name, port }).then( res => {
|
||||
if (res.status === "ok"){
|
||||
setSubmitLoading(false)
|
||||
close();
|
||||
fireUpdateRequest();
|
||||
if (autostart) startservice(res.id)
|
||||
okNotify(`Service ${name} has been added`, `Successfully added ${res.id} with port ${port}`)
|
||||
}else{
|
||||
setSubmitLoading(false)
|
||||
setError("Invalid request! [ "+res.status+" ]")
|
||||
}
|
||||
}).catch( err => {
|
||||
setSubmitLoading(false)
|
||||
setError("Request Failed! [ "+err+" ]")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return <Modal size="xl" title="Add a new service" opened={opened} onClose={close} closeOnClickOutside={false} centered>
|
||||
<form onSubmit={form.onSubmit(submitRequest)}>
|
||||
<TextInput
|
||||
label="Service name"
|
||||
placeholder="Challenge 01"
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<Space h="md" />
|
||||
|
||||
<NumberInput
|
||||
placeholder="8080"
|
||||
min={1}
|
||||
max={65535}
|
||||
label="Public Service port"
|
||||
{...form.getInputProps('port')}
|
||||
/>
|
||||
|
||||
{form.values.chosenInternalPort?<>
|
||||
<Space h="md" />
|
||||
<NumberInput
|
||||
placeholder="8080"
|
||||
min={1}
|
||||
max={65535}
|
||||
label="Internal Proxy Port"
|
||||
{...form.getInputProps('internalPort')}
|
||||
/>
|
||||
<Space h="sm" />
|
||||
</>:null}
|
||||
|
||||
<Space h="xl" />
|
||||
|
||||
<Switch
|
||||
label="Auto-Start Service"
|
||||
{...form.getInputProps('autostart', { type: 'checkbox' })}
|
||||
/>
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
<Switch
|
||||
label="Choose internal port"
|
||||
{...form.getInputProps('chosenInternalPort', { type: 'checkbox' })}
|
||||
/>
|
||||
|
||||
<Group position="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Add Service</Button>
|
||||
</Group>
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
{error?<>
|
||||
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
|
||||
Error: {error}
|
||||
</Notification><Space h="md" /></>:null}
|
||||
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
}
|
||||
|
||||
export default AddNewService;
|
||||
13
frontend/src/components/RegexProxy/RegexView/RegexView.module.scss
Executable file
13
frontend/src/components/RegexProxy/RegexView/RegexView.module.scss
Executable file
@@ -0,0 +1,13 @@
|
||||
|
||||
@use "../../vars" as *;
|
||||
|
||||
.box{
|
||||
padding:30px;
|
||||
margin:5px;
|
||||
}
|
||||
|
||||
.regex_text{
|
||||
padding: 10px;
|
||||
background-color: $third_color;
|
||||
border-radius: 15px;
|
||||
}
|
||||
114
frontend/src/components/RegexProxy/RegexView/index.tsx
Executable file
114
frontend/src/components/RegexProxy/RegexView/index.tsx
Executable file
@@ -0,0 +1,114 @@
|
||||
import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip } from '@mantine/core';
|
||||
import React, { useState } from 'react';
|
||||
import { RegexFilter } from '../../js/models';
|
||||
import { activateregex, b64decode, deactivateregex, deleteregex, errorNotify, fireUpdateRequest, okNotify } from '../../js/utils';
|
||||
import style from "./RegexView.module.scss";
|
||||
import { BsTrashFill } from "react-icons/bs"
|
||||
import YesNoModal from '../YesNoModal';
|
||||
import FilterTypeSelector from '../FilterTypeSelector';
|
||||
import { FaPause, FaPlay } from 'react-icons/fa';
|
||||
|
||||
|
||||
function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
|
||||
|
||||
const mode_string = regexInfo.mode === "C"? "C -> S":
|
||||
regexInfo.mode === "S"? "S -> C":
|
||||
regexInfo.mode === "B"? "S <-> C": "🤔"
|
||||
|
||||
let regex_expr = b64decode(regexInfo.regex);
|
||||
|
||||
const [deleteModal, setDeleteModal] = useState(false);
|
||||
const [deleteTooltipOpened, setDeleteTooltipOpened] = useState(false);
|
||||
const [statusTooltipOpened, setStatusTooltipOpened] = useState(false);
|
||||
|
||||
const deleteRegex = () => {
|
||||
deleteregex(regexInfo.id).then(res => {
|
||||
if(!res){
|
||||
okNotify(`Regex ${regex_expr} deleted successfully!`,`Regex '${regex_expr}' ID:${regexInfo.id} has been deleted!`)
|
||||
fireUpdateRequest()
|
||||
}else{
|
||||
errorNotify(`Regex ${regex_expr} deleation failed!`,`Error: ${res}`)
|
||||
}
|
||||
}).catch( err => errorNotify(`Regex ${regex_expr} deleation failed!`,`Error: ${err}`))
|
||||
}
|
||||
|
||||
const changeRegexStatus = () => {
|
||||
|
||||
(regexInfo.active?deactivateregex:activateregex)(regexInfo.id).then(res => {
|
||||
if(!res){
|
||||
okNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivated":"activated"} successfully!`,`Regex '${regex_expr}' ID:${regexInfo.id} has been ${regexInfo.active?"deactivated":"activated"}!`)
|
||||
fireUpdateRequest()
|
||||
}else{
|
||||
errorNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivation":"activation"} failed!`,`Error: ${res}`)
|
||||
}
|
||||
}).catch( err => errorNotify(`Regex ${regex_expr} ${regexInfo.active?"deactivation":"activation"} failed!`,`Error: ${err}`))
|
||||
|
||||
}
|
||||
|
||||
return <div className={style.box}>
|
||||
<Grid>
|
||||
<Grid.Col span={2}>
|
||||
<Title order={2} style={{color:"#FFF"}}>Regex:</Title>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<Text className={style.regex_text}> {regex_expr}</Text>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2} className='center-flex'>
|
||||
<Space w="xs" />
|
||||
<Tooltip label={regexInfo.active?"Deactivate":"Activate"} zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color={regexInfo.active?"orange":"teal"} opened={statusTooltipOpened}>
|
||||
<ActionIcon color={regexInfo.active?"orange":"teal"} onClick={changeRegexStatus} size="xl" radius="md" variant="filled"
|
||||
onFocus={() => setStatusTooltipOpened(false)} onBlur={() => setStatusTooltipOpened(false)}
|
||||
onMouseEnter={() => setStatusTooltipOpened(true)} onMouseLeave={() => setStatusTooltipOpened(false)}
|
||||
>{regexInfo.active?<FaPause size="20px" />:<FaPlay size="20px" />}</ActionIcon>
|
||||
</Tooltip>
|
||||
<Space w="xs" />
|
||||
<Tooltip label="Delete regex" zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="red" opened={deleteTooltipOpened} >
|
||||
<ActionIcon color="red" onClick={()=>setDeleteModal(true)} size="xl" radius="md" variant="filled"
|
||||
onFocus={() => setDeleteTooltipOpened(false)} onBlur={() => setDeleteTooltipOpened(false)}
|
||||
onMouseEnter={() => setDeleteTooltipOpened(true)} onMouseLeave={() => setDeleteTooltipOpened(false)}
|
||||
><BsTrashFill size={22} /></ActionIcon>
|
||||
</Tooltip>
|
||||
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2} />
|
||||
<Grid.Col className='center-flex-row' span={4}>
|
||||
<Space h="xs" />
|
||||
<FilterTypeSelector
|
||||
size="md"
|
||||
color="gray"
|
||||
disabled
|
||||
value={regexInfo.is_blacklist?"blacklist":"whitelist"}
|
||||
/>
|
||||
<Space h="md" />
|
||||
<div 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>
|
||||
</Grid.Col>
|
||||
<Grid.Col style={{width:"100%"}} span={6}>
|
||||
<Space h="xs" />
|
||||
<div 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>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<YesNoModal
|
||||
title='Are you sure to delete this regex?'
|
||||
description={`You are going to delete the regex '${regex_expr}'.`}
|
||||
onClose={()=>setDeleteModal(false)}
|
||||
action={deleteRegex}
|
||||
opened={deleteModal}
|
||||
/>
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
export default RegexView;
|
||||
107
frontend/src/components/RegexProxy/ServiceRow/ChangePortModal.tsx
Executable file
107
frontend/src/components/RegexProxy/ServiceRow/ChangePortModal.tsx
Executable file
@@ -0,0 +1,107 @@
|
||||
import { Button, Group, NumberInput, Space, Notification, Modal, Center, Title } from '@mantine/core';
|
||||
import { useForm } from '@mantine/hooks';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { changeports, fireUpdateRequest, okNotify } from '../../js/utils';
|
||||
import { ImCross } from "react-icons/im"
|
||||
import { Service } from '../../js/models';
|
||||
import { FaLongArrowAltDown } from 'react-icons/fa';
|
||||
|
||||
type InputForm = {
|
||||
internalPort:number,
|
||||
port:number
|
||||
}
|
||||
|
||||
function ChangePortModal({ service, opened, onClose }:{ service:Service, opened:boolean, onClose:()=>void }) {
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
internalPort: service.internal_port,
|
||||
port: service.public_port
|
||||
},
|
||||
validationRules:{
|
||||
internalPort: (value) => value>0 && value<65536,
|
||||
port: (value) => value>0 && value<65536
|
||||
}
|
||||
})
|
||||
|
||||
useEffect(()=>{
|
||||
form.setValues({internalPort: service.internal_port, port:service.public_port})
|
||||
},[opened])
|
||||
|
||||
const close = () =>{
|
||||
onClose()
|
||||
form.reset()
|
||||
setError(null)
|
||||
}
|
||||
|
||||
const [submitLoading, setSubmitLoading] = useState(false)
|
||||
const [error, setError] = useState<string|null>(null)
|
||||
|
||||
const submitRequest = (data:InputForm) =>{
|
||||
setSubmitLoading(true)
|
||||
changeports(service.id, data).then( res => {
|
||||
if (!res){
|
||||
setSubmitLoading(false)
|
||||
close();
|
||||
fireUpdateRequest();
|
||||
okNotify(`Internal port on ${service.name} service has changed in ${data.internalPort}`, `Successfully changed internal port of service with id ${service.id}`)
|
||||
}else{
|
||||
setSubmitLoading(false)
|
||||
setError("Invalid request! [ "+res+" ]")
|
||||
}
|
||||
}).catch( err => {
|
||||
setSubmitLoading(false)
|
||||
setError("Request Failed! [ "+err+" ]")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return <Modal size="xl" title="Change Ports" opened={opened} onClose={close} closeOnClickOutside={false} centered>
|
||||
<form onSubmit={form.onSubmit(submitRequest)}>
|
||||
|
||||
|
||||
|
||||
<NumberInput
|
||||
placeholder="30001"
|
||||
min={1}
|
||||
max={65535}
|
||||
label="Internal Proxy Port"
|
||||
{...form.getInputProps('internalPort')}
|
||||
/>
|
||||
|
||||
<Space h="xl" />
|
||||
<Center><FaLongArrowAltDown size={50}/></Center>
|
||||
|
||||
<NumberInput
|
||||
placeholder="8080"
|
||||
min={1}
|
||||
max={65535}
|
||||
label="Public Service Port"
|
||||
{...form.getInputProps('port')}
|
||||
/>
|
||||
|
||||
<Space h="xl" />
|
||||
|
||||
<Center><Title order={5}>The change of the ports will cause a temporarily shutdown of the service! ⚠️</Title></Center>
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
<Group position="right" mt="md">
|
||||
<Button loading={submitLoading} disabled={
|
||||
service.internal_port === form.values.internalPort && service.public_port === form.values.port
|
||||
} type="submit">Change Port</Button>
|
||||
</Group>
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
{error?<>
|
||||
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
|
||||
Error: {error}
|
||||
</Notification><Space h="md" /></>:null}
|
||||
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
}
|
||||
|
||||
export default ChangePortModal;
|
||||
69
frontend/src/components/RegexProxy/ServiceRow/RenameForm.tsx
Normal file
69
frontend/src/components/RegexProxy/ServiceRow/RenameForm.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Button, Group, Space, TextInput, Notification, Modal } from '@mantine/core';
|
||||
import { useForm } from '@mantine/hooks';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { fireUpdateRequest, okNotify, renameservice } from '../../js/utils';
|
||||
import { ImCross } from "react-icons/im"
|
||||
import { Service } from '../../js/models';
|
||||
|
||||
function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>void, service:Service }) {
|
||||
|
||||
const form = useForm({
|
||||
initialValues: { name:service.name },
|
||||
validationRules:{ name: (value) => value !== "" }
|
||||
})
|
||||
|
||||
const close = () =>{
|
||||
onClose()
|
||||
form.reset()
|
||||
setError(null)
|
||||
}
|
||||
|
||||
useEffect(()=> form.setFieldValue("name", service.name),[opened])
|
||||
|
||||
const [submitLoading, setSubmitLoading] = useState(false)
|
||||
const [error, setError] = useState<string|null>(null)
|
||||
|
||||
const submitRequest = ({ name }:{ name:string }) => {
|
||||
setSubmitLoading(true)
|
||||
renameservice(service.id, name).then( res => {
|
||||
if (!res){
|
||||
setSubmitLoading(false)
|
||||
close();
|
||||
fireUpdateRequest();
|
||||
okNotify(`Service ${service.name} has been renamed in ${ name }`, `Successfully renamed service on port ${service.public_port}`)
|
||||
}else{
|
||||
setSubmitLoading(false)
|
||||
setError("Error: [ "+res+" ]")
|
||||
}
|
||||
}).catch( err => {
|
||||
setSubmitLoading(false)
|
||||
setError("Request Failed! [ "+err+" ]")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
return <Modal size="xl" title={`Rename '${service.name}' service on port ${service.public_port}`} opened={opened} onClose={close} closeOnClickOutside={false} centered>
|
||||
<form onSubmit={form.onSubmit(submitRequest)}>
|
||||
<TextInput
|
||||
label="Service Name"
|
||||
placeholder="Awesome Service Name!"
|
||||
{...form.getInputProps('name')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Rename</Button>
|
||||
</Group>
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
{error?<>
|
||||
<Notification icon={<ImCross size={14} />} color="red" onClose={()=>{setError(null)}}>
|
||||
Error: {error}
|
||||
</Notification><Space h="md" /></>:null}
|
||||
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
}
|
||||
|
||||
export default RenameForm;
|
||||
18
frontend/src/components/RegexProxy/ServiceRow/ServiceRow.module.scss
Executable file
18
frontend/src/components/RegexProxy/ServiceRow/ServiceRow.module.scss
Executable file
@@ -0,0 +1,18 @@
|
||||
|
||||
@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;
|
||||
}
|
||||
234
frontend/src/components/RegexProxy/ServiceRow/index.tsx
Executable file
234
frontend/src/components/RegexProxy/ServiceRow/index.tsx
Executable file
@@ -0,0 +1,234 @@
|
||||
import { ActionIcon, Badge, Divider, Grid, MediaQuery, Menu, Space, Title, Tooltip } from '@mantine/core';
|
||||
import React, { useState } from 'react';
|
||||
import { FaPause, FaPlay, FaStop } from 'react-icons/fa';
|
||||
import { Service } from '../../js/models';
|
||||
import { MdOutlineArrowForwardIos } from "react-icons/md"
|
||||
import style from "./ServiceRow.module.scss";
|
||||
import YesNoModal from '../YesNoModal';
|
||||
import { deleteservice, errorNotify, fireUpdateRequest, okNotify, pauseservice, regenport, startservice, stopservice } from '../../js/utils';
|
||||
import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs';
|
||||
import { TbNumbers } from 'react-icons/tb';
|
||||
import { BiRename } from 'react-icons/bi'
|
||||
import ChangePortModal from './ChangePortModal';
|
||||
import RenameForm from './RenameForm';
|
||||
|
||||
//"status":"stop"/"wait"/"active"/"pause",
|
||||
function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) {
|
||||
|
||||
let status_color = "gray";
|
||||
switch(service.status){
|
||||
case "stop": status_color = "red"; break;
|
||||
case "wait": status_color = "yellow"; break;
|
||||
case "active": status_color = "teal"; break;
|
||||
case "pause": status_color = "cyan"; break;
|
||||
}
|
||||
|
||||
const [stopModal, setStopModal] = useState(false);
|
||||
const [buttonLoading, setButtonLoading] = useState(false)
|
||||
const [tooltipStopOpened, setTooltipStopOpened] = useState(false);
|
||||
const [deleteModal, setDeleteModal] = useState(false)
|
||||
const [changePortModal, setChangePortModal] = useState(false)
|
||||
const [choosePortModal, setChoosePortModal] = useState(false)
|
||||
const [renameModal, setRenameModal] = useState(false)
|
||||
|
||||
const stopService = async () => {
|
||||
setButtonLoading(true)
|
||||
await stopservice(service.id).then(res => {
|
||||
if(!res){
|
||||
okNotify(`Service ${service.id} stopped successfully!`,`The service ${service.name} has been stopped!`)
|
||||
fireUpdateRequest();
|
||||
}else{
|
||||
errorNotify(`An error as occurred during the stopping of the service ${service.id}`,`Error: ${res}`)
|
||||
}
|
||||
}).catch(err => {
|
||||
errorNotify(`An error as occurred during the stopping of the service ${service.id}`,`Error: ${err}`)
|
||||
})
|
||||
setButtonLoading(false);
|
||||
}
|
||||
|
||||
const startService = async () => {
|
||||
setButtonLoading(true)
|
||||
await startservice(service.id).then(res => {
|
||||
if(!res){
|
||||
okNotify(`Service ${service.id} started successfully!`,`The service ${service.name} has been started!`)
|
||||
fireUpdateRequest();
|
||||
}else{
|
||||
errorNotify(`An error as occurred during the starting of the service ${service.id}`,`Error: ${res}`)
|
||||
}
|
||||
}).catch(err => {
|
||||
errorNotify(`An error as occurred during the starting of the service ${service.id}`,`Error: ${err}`)
|
||||
})
|
||||
setButtonLoading(false)
|
||||
}
|
||||
|
||||
const pauseService = async () => {
|
||||
setButtonLoading(true)
|
||||
await pauseservice(service.id).then(res => {
|
||||
if(!res){
|
||||
okNotify(`Service ${service.id} paused successfully!`,`The service ${service.name} has been paused (Transparent mode)!`)
|
||||
fireUpdateRequest();
|
||||
}else{
|
||||
errorNotify(`An error as occurred during the pausing of the service ${service.id}`,`Error: ${res}`)
|
||||
}
|
||||
}).catch(err => {
|
||||
errorNotify(`An error as occurred during the pausing of the service ${service.id}`,`Error: ${err}`)
|
||||
})
|
||||
setButtonLoading(false)
|
||||
|
||||
}
|
||||
|
||||
const deleteService = () => {
|
||||
deleteservice(service.id).then(res => {
|
||||
if (!res){
|
||||
okNotify("Service delete complete!",`The service ${service.id} has been deleted!`)
|
||||
fireUpdateRequest();
|
||||
}else
|
||||
errorNotify("An error occurred while deleting a service",`Error: ${res}`)
|
||||
}).catch(err => {
|
||||
errorNotify("An error occurred while deleting a service",`Error: ${err}`)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
const changePort = () => {
|
||||
regenport(service.id).then(res => {
|
||||
if (!res){
|
||||
okNotify("Service port regeneration completed!",`The service ${service.id} has changed the internal port!`)
|
||||
fireUpdateRequest();
|
||||
}else
|
||||
errorNotify("An error occurred while changing the internal service port",`Error: ${res}`)
|
||||
}).catch(err => {
|
||||
errorNotify("An error occurred while changing the internal service port",`Error: ${err}`)
|
||||
})
|
||||
}
|
||||
|
||||
return <>
|
||||
<Grid className={style.row} justify="flex-end" style={{width:"100%"}}>
|
||||
<Grid.Col md={4} xs={12}>
|
||||
<MediaQuery smallerThan="md" styles={{ display: 'none' }}><div>
|
||||
<div className="center-flex-row">
|
||||
<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>
|
||||
<Badge color={status_color} size="xl" radius="md">{service.internal_port} {"->"} {service.public_port}</Badge>
|
||||
</div>
|
||||
</div></MediaQuery>
|
||||
<MediaQuery largerThan="md" styles={{ display: 'none' }}><div>
|
||||
<div className="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>
|
||||
<Space w="xl" />
|
||||
<Badge color={status_color} size="xl" radius="md">{service.internal_port} {"->"} {service.public_port}</Badge>
|
||||
</div>
|
||||
</div></MediaQuery>
|
||||
|
||||
<MediaQuery largerThan="md" styles={{ display: 'none' }}>
|
||||
<Space h="xl" />
|
||||
</MediaQuery>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col className="center-flex" md={8} xs={12}>
|
||||
<MediaQuery smallerThan="md" styles={{ display: 'none' }}>
|
||||
<div className='flex-spacer' />
|
||||
</MediaQuery>
|
||||
<MediaQuery largerThan="md" styles={{ display: 'none' }}>
|
||||
<><Space w="xl" /><Space w="xl" /></>
|
||||
</MediaQuery>
|
||||
|
||||
<div className="center-flex-row">
|
||||
<Badge style={{marginBottom:"20px"}} color={status_color} radius="sm" size="xl" variant="filled">Status: <u>{service.status}</u></Badge>
|
||||
<Badge style={{marginBottom:"8px"}}color="violet" radius="sm" size="lg" variant="filled">Regex: {service.n_regex}</Badge>
|
||||
<Badge color="yellow" radius="sm" size="lg" variant="filled">Connections Blocked: {service.n_packets}</Badge>
|
||||
</div>
|
||||
<MediaQuery largerThan="md" styles={{ display: 'none' }}>
|
||||
<div className='flex-spacer' />
|
||||
</MediaQuery>
|
||||
<MediaQuery smallerThan="md" styles={{ display: 'none' }}>
|
||||
<><Space w="xl" /><Space w="xl" /></>
|
||||
</MediaQuery>
|
||||
<div className="center-flex">
|
||||
<Menu>
|
||||
<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>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>
|
||||
<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>
|
||||
<Space w="md"/>
|
||||
{["pause","wait"].includes(service.status)?
|
||||
|
||||
<Tooltip label="Stop service" zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color="orange" opened={tooltipStopOpened} tooltipId="tooltip-stop-id">
|
||||
<ActionIcon color="yellow" loading={buttonLoading}
|
||||
onClick={()=>setStopModal(true)} 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>:
|
||||
<Tooltip label={service.status === "stop"?"Start in pause mode":"Pause service"} zIndex={0} transition="pop" transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" color={service.status === "stop"?"cyan":"red"}>
|
||||
<ActionIcon color={service.status === "stop"?"cyan":"red"} loading={buttonLoading}
|
||||
onClick={pauseService} size="xl" radius="md" variant="filled"
|
||||
/*disabled={service.status === "stop"}*/>
|
||||
<FaPause size="20px" />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
}
|
||||
|
||||
<Space w="md"/>
|
||||
<Tooltip label="Start service" transition="pop" zIndex={0} transitionDuration={200} /*openDelay={500}*/ transitionTimingFunction="ease" 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}
|
||||
<MediaQuery largerThan="md" styles={{ display: 'none' }}>
|
||||
<><Space w="xl" /><Space w="xl" /></>
|
||||
</MediaQuery>
|
||||
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<YesNoModal
|
||||
title='Are you sure to stop this service?'
|
||||
description={`You are going to delete the service '${service.id}', causing the firewall to stop. This will cause the shutdown of your service! ⚠️`}
|
||||
onClose={()=>{setStopModal(false);}}
|
||||
action={stopService}
|
||||
opened={stopModal}
|
||||
/>
|
||||
<hr style={{width:"100%"}}/>
|
||||
<YesNoModal
|
||||
title='Are you sure to delete this service?'
|
||||
description={`You are going to delete the service '${service.id}', causing the stopping of the firewall and deleting all the regex associated. This will cause the shutdown of your service! ⚠️`}
|
||||
onClose={()=>setDeleteModal(false) }
|
||||
action={deleteService}
|
||||
opened={deleteModal}
|
||||
/>
|
||||
<YesNoModal
|
||||
title='Are you sure to change the proxy internal port?'
|
||||
description={`You are going to change the proxy port '${service.internal_port}'. This will cause the shutdown of your service temporarily! ⚠️`}
|
||||
onClose={()=>setChangePortModal(false)}
|
||||
action={changePort}
|
||||
opened={changePortModal}
|
||||
/>
|
||||
<ChangePortModal
|
||||
service={service}
|
||||
onClose={()=> setChoosePortModal(false)}
|
||||
opened={choosePortModal}
|
||||
/>
|
||||
<RenameForm
|
||||
onClose={()=>setRenameModal(false)}
|
||||
opened={renameModal}
|
||||
service={service}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
||||
export default ServiceRow;
|
||||
116
frontend/src/components/RegexProxy/utils.ts
Normal file
116
frontend/src/components/RegexProxy/utils.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { ServerResponse } from "../../js/models"
|
||||
import { getapi, postapi } from "../../js/utils"
|
||||
|
||||
export type GeneralStats = {
|
||||
services:number,
|
||||
closed:number,
|
||||
regexes:number
|
||||
}
|
||||
|
||||
export type Service = {
|
||||
id:string,
|
||||
name:string,
|
||||
status:string,
|
||||
public_port:number,
|
||||
internal_port:number,
|
||||
n_packets:number,
|
||||
n_regex:number,
|
||||
}
|
||||
|
||||
export type ServiceAddForm = {
|
||||
name:string,
|
||||
port:number,
|
||||
internalPort?:number
|
||||
}
|
||||
|
||||
export type ServerResponseWithID = {
|
||||
status:string,
|
||||
id:string
|
||||
}
|
||||
|
||||
export type ChangePort = {
|
||||
port?: number,
|
||||
internalPort?: number
|
||||
}
|
||||
|
||||
export type RegexFilter = {
|
||||
id:number,
|
||||
service_id:string,
|
||||
regex:string
|
||||
is_blacklist:boolean,
|
||||
is_case_sensitive:boolean,
|
||||
mode:string //C S B => C->S S->C BOTH
|
||||
n_packets:number,
|
||||
active:boolean
|
||||
}
|
||||
|
||||
export type RegexAddForm = {
|
||||
service_id:string,
|
||||
regex:string,
|
||||
is_case_sensitive:boolean,
|
||||
is_blacklist:boolean,
|
||||
mode:string, // C->S S->C BOTH,
|
||||
active: boolean
|
||||
}
|
||||
|
||||
export const regexproxy = {
|
||||
stats: async () => {
|
||||
return await getapi("regexproxy/stats") as GeneralStats;
|
||||
},
|
||||
services: async() => {
|
||||
return await getapi("regexproxy/services") as Service[];
|
||||
},
|
||||
serviceinfo: async (service_id:string) => {
|
||||
return await getapi(`regexproxy/service/${service_id}`) as Service;
|
||||
},
|
||||
regexdelete: async (regex_id:number) => {
|
||||
const { status } = await getapi(`regexproxy/regex/${regex_id}/delete`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
regexenable: async (regex_id:number) => {
|
||||
const { status } = await getapi(`regexproxy/regex/${regex_id}/enable`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
regexdisable: async (regex_id:number) => {
|
||||
const { status } = await getapi(`regexproxy/regex/${regex_id}/disable`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
servicestart: async (service_id:string) => {
|
||||
const { status } = await getapi(`regexproxy/service/${service_id}/start`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
servicestop: async (service_id:string) => {
|
||||
const { status } = await getapi(`regexproxy/service/${service_id}/stop`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
servicepause: async (service_id:string) => {
|
||||
const { status } = await getapi(`regexproxy/service/${service_id}/pause`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
serviceregenport: async (service_id:string) => {
|
||||
const { status } = await getapi(`regexproxy/service/${service_id}/regen-port`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
servicechangeport: async (service_id:string, data:ChangePort) => {
|
||||
const { status } = await postapi(`regexproxy/service/${service_id}/change-ports`,data) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
servicesadd: async (data:ServiceAddForm) => {
|
||||
return await postapi("regexproxy/services/add",data) as ServerResponseWithID;
|
||||
},
|
||||
servicerename: async (service_id:string, name: string) => {
|
||||
const { status } = await postapi(`regexproxy/service/${service_id}/rename`,{ name }) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
servicedelete: async (service_id:string) => {
|
||||
const { status } = await getapi(`regexproxy/service/${service_id}/delete`) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
regexesadd: async (data:RegexAddForm) => {
|
||||
const { status } = await postapi("regexproxy/regexes/add",data) as ServerResponse;
|
||||
return status === "ok"?undefined:status
|
||||
},
|
||||
serviceregexes: async (service_id:string) => {
|
||||
return await getapi(`regexproxy/service/${service_id}/regexes`) as RegexFilter[];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user